3 # Copyright (c) 2007, Cameron Rich
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions are met:
10 # * Redistributions of source code must retain the above copyright notice,
11 # this list of conditions and the following disclaimer.
12 # * Redistributions in binary form must reproduce the above copyright
13 # notice, this list of conditions and the following disclaimer in the
14 # documentation and/or other materials provided with the distribution.
15 # * Neither the name of the axTLS project nor the names of its
16 # contributors may be used to endorse or promote products derived
17 # from this software without specific prior written permission.
19 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
23 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
25 # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
27 # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28 # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29 # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 # Demonstrate the use of the axTLS library in Perl with a set of
34 # command-line parameters similar to openssl. In fact, openssl clients
35 # should be able to communicate with axTLS servers and visa-versa.
37 # This code has various bits enabled depending on the configuration. To enable
38 # the most interesting version, compile with the 'full mode' enabled.
40 # To see what options you have, run the following:
41 # > [perl] axssl s_server -?
42 # > [perl] axssl s_client -?
44 # The axtls/axtlsp shared libraries must be in the same directory or be found
45 # by the OS. axtlsp.pm must be in this directory or be in @INC.
47 # Under Win32, ActivePerl was used (see
48 # http://www.activestate.com/Products/ActivePerl/?mp=1)
53 # To get access to Win32 file descriptor stuff
58 eval("use Win32API::File 0.08 qw( :ALL )");
65 # Win32 has some problems with socket handles
70 return $is_win32 ? FdGetOsFHandle($sock) : $sock;
74 if ($#ARGV == 0 && $ARGV[0] eq "version")
76 printf("axssl.pl ".axtlsp::ssl_version()."\n");
81 # Main entry point. Doesn't do much except works out whether we are a client
84 print_options($#ARGV > -1 ? $ARGV[0] : "")
85 if ($#ARGV < 0 || ($ARGV[0] ne "s_server" && $ARGV[0] ne "s_client"));
88 # Cygwin/Win32 issue - flush our output continuously
92 my $build_mode = axtlsp::ssl_get_config($axtlsp::SSL_BUILD_MODE);
93 $ARGV[0] eq "s_server" ? do_server($build_mode) : do_client($build_mode);
96 # Implement the SSL server logic.
100 my ($build_mode) = @_;
103 my $options = $axtlsp::SSL_DISPLAY_CERTS;
105 my $password = undef;
106 my $private_key_file = undef;
107 my $cert_size = axtlsp::ssl_get_config($axtlsp::SSL_MAX_CERT_CFG_OFFSET);
108 my $ca_cert_size = axtlsp::ssl_get_config(
109 $axtlsp::SSL_MAX_CA_CERT_CFG_OFFSET);
115 if ($ARGV[$i] eq "-accept")
117 print_server_options($build_mode, $ARGV[$i]) if $i >= $#ARGV;
120 elsif ($ARGV[$i] eq "-quiet")
123 $options &= ~$axtlsp::SSL_DISPLAY_CERTS;
125 elsif ($build_mode >= $axtlsp::SSL_BUILD_SERVER_ONLY)
127 if ($ARGV[$i] eq "-cert")
129 print_server_options($build_mode, $ARGV[$i])
130 if $i >= $#ARGV || $#cert >= $cert_size-1;
132 push @cert, $ARGV[++$i];
134 elsif ($ARGV[$i] eq "-key")
136 print_server_options($build_mode, $ARGV[$i]) if $i >= $#ARGV;
137 $private_key_file = $ARGV[++$i];
138 $options |= $axtlsp::SSL_NO_DEFAULT_KEY;
140 elsif ($ARGV[$i] eq "-pass")
142 print_server_options($build_mode, $ARGV[$i]) if $i >= $#ARGV;
143 $password = $ARGV[++$i];
145 elsif ($build_mode >= $axtlsp::SSL_BUILD_ENABLE_VERIFICATION)
147 if ($ARGV[$i] eq "-verify")
149 $options |= $axtlsp::SSL_CLIENT_AUTHENTICATION;
151 elsif ($ARGV[$i] eq "-CAfile")
153 print_server_options($build_mode, $ARGV[$i])
154 if $i >= $#ARGV || $#ca_cert >= $ca_cert_size-1;
155 push @ca_cert, $ARGV[++$i];
157 elsif ($build_mode == $axtlsp::SSL_BUILD_FULL_MODE)
159 if ($ARGV[$i] eq "-debug")
161 $options |= $axtlsp::SSL_DISPLAY_BYTES;
163 elsif ($ARGV[$i] eq "-state")
165 $options |= $axtlsp::SSL_DISPLAY_STATES;
167 elsif ($ARGV[$i] eq "-show-rsa")
169 $options |= $axtlsp::SSL_DISPLAY_RSA;
173 print_server_options($build_mode, $ARGV[$i]);
178 print_server_options($build_mode, $ARGV[$i]);
183 print_server_options($build_mode, $ARGV[$i]);
188 print_server_options($build_mode, $ARGV[$i]);
194 # Create socket for incoming connections
195 my $server_sock = IO::Socket::INET->new(Proto => 'tcp',
198 Reuse => 1) or die $!;
200 ###########################################################################
201 # This is where the interesting stuff happens. Up until now we've
202 # just been setting up sockets etc. Now we do the SSL handshake.
203 ###########################################################################
204 my $ssl_ctx = axtlsp::ssl_ctx_new($options, $axtlsp::SSL_DEFAULT_SVR_SESS);
205 die "Error: Server context is invalid" if not defined $ssl_ctx;
207 if (defined $private_key_file)
209 my $obj_type = $axtlsp::SSL_OBJ_RSA_KEY;
211 $obj_type = $axtlsp::SSL_OBJ_PKCS8 if $private_key_file =~ /.p8$/;
212 $obj_type = $axtlsp::SSL_OBJ_PKCS12 if $private_key_file =~ /.p12$/;
214 die "Private key '$private_key_file' is undefined." if
215 axtlsp::ssl_obj_load($ssl_ctx, $obj_type,
216 $private_key_file, $password);
221 die "Certificate '$_' is undefined."
222 if axtlsp::ssl_obj_load($ssl_ctx, $axtlsp::SSL_OBJ_X509_CERT,
223 $_, undef) != $axtlsp::SSL_OK;
228 die "Certificate '$_' is undefined."
229 if axtlsp::ssl_obj_load($ssl_ctx, $axtlsp::SSL_OBJ_X509_CACERT,
230 $_, undef) != $axtlsp::SSL_OK;
235 printf("ACCEPT\n") if not $quiet;
236 my $client_sock = $server_sock->accept;
237 my $native_sock = get_native_sock($client_sock->fileno);
239 # This doesn't work in Win32 - need to get file descriptor from socket.
240 my $ssl = axtlsp::ssl_server_new($ssl_ctx, $native_sock);
242 # do the actual SSL handshake
249 ($res, $buf) = axtlsp::ssl_read($ssl, undef);
250 last if $res < $axtlsp::SSL_OK;
252 if ($res == $axtlsp::SSL_OK) # connection established and ok
254 if (axtlsp::ssl_handshake_status($ssl) == $axtlsp::SSL_OK)
256 if (!$quiet && !$connected)
258 display_session_id($ssl);
259 display_cipher($ssl);
266 if ($res > $axtlsp::SSL_OK)
270 elsif ($res < $axtlsp::SSL_OK)
272 axtlsp::ssl_display_error($res) if not $quiet;
277 # client was disconnected or the handshake failed.
278 printf("CONNECTION CLOSED\n") if not $quiet;
279 axtlsp::ssl_free($ssl);
283 axtlsp::ssl_ctx_free($ssl_ctx);
287 # Implement the SSL client logic.
291 my ($build_mode) = @_;
294 my $options = $axtlsp::SSL_SERVER_VERIFY_LATER|$axtlsp::SSL_DISPLAY_CERTS;
295 my $private_key_file = undef;
298 my $password = undef;
300 my $host = "127.0.0.1";
303 my $cert_size = axtlsp::ssl_get_config(
304 $axtlsp::SSL_MAX_CERT_CFG_OFFSET);
305 my $ca_cert_size = axtlsp::ssl_get_config(
306 $axtlsp::SSL_MAX_CA_CERT_CFG_OFFSET);
310 if ($ARGV[$i] eq "-connect")
312 print_client_options($build_mode, $ARGV[$i]) if $i >= $#ARGV;
313 ($host, $port) = split(':', $ARGV[++$i]);
315 elsif ($ARGV[$i] eq "-cert")
317 print_client_options($build_mode, $ARGV[$i])
318 if $i >= $#ARGV || $#cert >= $cert_size-1;
320 push @cert, $ARGV[++$i];
322 elsif ($ARGV[$i] eq "-key")
324 print_client_options($build_mode, $ARGV[$i]) if $i >= $#ARGV;
325 $private_key_file = $ARGV[++$i];
326 $options |= $axtlsp::SSL_NO_DEFAULT_KEY;
328 elsif ($ARGV[$i] eq "-CAfile")
330 print_client_options($build_mode, $ARGV[$i])
331 if $i >= $#ARGV || $#ca_cert >= $ca_cert_size-1;
333 push @ca_cert, $ARGV[++$i];
335 elsif ($ARGV[$i] eq "-verify")
337 $options &= ~$axtlsp::SSL_SERVER_VERIFY_LATER;
339 elsif ($ARGV[$i] eq "-reconnect")
343 elsif ($ARGV[$i] eq "-quiet")
346 $options &= ~$axtlsp::SSL_DISPLAY_CERTS;
348 elsif ($ARGV[$i] eq "-pass")
350 print_server_options($build_mode, $ARGV[$i]) if $i >= $#ARGV;
351 $password = $ARGV[++$i];
353 elsif ($build_mode == $axtlsp::SSL_BUILD_FULL_MODE)
355 if ($ARGV[$i] eq "-debug")
357 $options |= $axtlsp::SSL_DISPLAY_BYTES;
359 elsif ($ARGV[$i] eq "-state")
361 $options |= $axtlsp::SSL_DISPLAY_STATES;
363 elsif ($ARGV[$i] eq "-show-rsa")
365 $options |= $axtlsp::SSL_DISPLAY_RSA;
367 else # don't know what this is
369 print_client_options($build_mode, $ARGV[$i]);
372 else # don't know what this is
374 print_client_options($build_mode, $ARGV[$i]);
380 my $client_sock = new IO::Socket::INET (
381 PeerAddr => $host, PeerPort => $port, Proto => 'tcp')
382 || die ("no socket: $!");
385 my $native_sock = get_native_sock($client_sock->fileno);
387 printf("CONNECTED\n") if not $quiet;
389 ###########################################################################
390 # This is where the interesting stuff happens. Up until now we've
391 # just been setting up sockets etc. Now we do the SSL handshake.
392 ###########################################################################
393 my $ssl_ctx = axtlsp::ssl_ctx_new($options, $axtlsp::SSL_DEFAULT_CLNT_SESS);
394 die "Error: Client context is invalid" if not defined $ssl_ctx;
396 if (defined $private_key_file)
398 my $obj_type = $axtlsp::SSL_OBJ_RSA_KEY;
400 $obj_type = $axtlsp::SSL_OBJ_PKCS8 if $private_key_file =~ /.p8$/;
401 $obj_type = $axtlsp::SSL_OBJ_PKCS12 if $private_key_file =~ /.p12$/;
403 die "Private key '$private_key_file' is undefined." if
404 axtlsp::ssl_obj_load($ssl_ctx, $obj_type,
405 $private_key_file, $password);
410 die "Certificate '$_' is undefined."
411 if axtlsp::ssl_obj_load($ssl_ctx, $axtlsp::SSL_OBJ_X509_CERT,
412 $_, undef) != $axtlsp::SSL_OK;
417 die "Certificate '$_' is undefined."
418 if axtlsp::ssl_obj_load($ssl_ctx, $axtlsp::SSL_OBJ_X509_CACERT,
419 $_, undef) != $axtlsp::SSL_OK;
422 # Try session resumption?
425 my $session_id = undef;
426 my $sess_id_size = 0;
430 $ssl = axtlsp::ssl_client_new($ssl_ctx, $native_sock,
431 $session_id, $sess_id_size);
433 $res = axtlsp::ssl_handshake_status($ssl);
434 if ($res != $axtlsp::SSL_OK)
436 axtlsp::ssl_display_error($res) if !$quiet;
437 axtlsp::ssl_free($ssl);
441 display_session_id($ssl);
442 $session_id = axtlsp::ssl_get_session_id($ssl);
446 axtlsp::ssl_free($ssl);
448 $client_sock = new IO::Socket::INET (
449 PeerAddr => $host, PeerPort => $port, Proto => 'tcp')
450 || die ("no socket: $!");
457 $ssl = axtlsp::ssl_client_new($ssl_ctx, $native_sock, undef, 0);
460 # check the return status
461 $res = axtlsp::ssl_handshake_status($ssl);
462 if ($res != $axtlsp::SSL_OK)
464 axtlsp::ssl_display_error($res) if not $quiet;
470 my $common_name = axtlsp::ssl_get_cert_dn($ssl,
471 $axtlsp::SSL_X509_CERT_COMMON_NAME);
473 printf("Common Name:\t\t\t%s\n", $common_name) if defined $common_name;
474 display_session_id($ssl);
475 display_cipher($ssl);
480 my $cstring = pack("a*x", $_); # add null terminator
481 $res = axtlsp::ssl_write($ssl, \$cstring, length($cstring));
482 if ($res < $axtlsp::SSL_OK)
484 axtlsp::ssl_display_error($res) if not $quiet;
489 axtlsp::ssl_ctx_free($ssl_ctx);
494 # We've had some sort of command-line error. Print out the basic options.
499 printf("axssl: Error: '%s' is an invalid command.\n", $option);
500 printf("usage: axssl [s_server|s_client|version] [args ...]\n");
505 # We've had some sort of command-line error. Print out the server options.
507 sub print_server_options
509 my ($build_mode, $option) = @_;
510 my $cert_size = axtlsp::ssl_get_config($axtlsp::SSL_MAX_CERT_CFG_OFFSET);
511 my $ca_cert_size = axtlsp::ssl_get_config(
512 $axtlsp::SSL_MAX_CA_CERT_CFG_OFFSET);
514 printf("unknown option %s\n", $option);
515 printf("usage: s_server [args ...]\n");
516 printf(" -accept arg\t- port to accept on (default is 4433)\n");
517 printf(" -quiet\t\t- No server output\n");
519 if ($build_mode >= $axtlsp::SSL_BUILD_SERVER_ONLY)
521 printf(" -cert arg\t- certificate file to add (in addition to default)".
523 "\t\t Can repeat up to %d times\n", $cert_size);
524 printf(" -key arg\t- Private key file to use - default DER format\n");
525 printf(" -pass\t\t- private key file pass phrase source\n");
528 if ($build_mode >= $axtlsp::SSL_BUILD_ENABLE_VERIFICATION)
530 printf(" -verify\t- turn on peer certificate verification\n");
531 printf(" -CAfile arg\t- Certificate authority - default DER format\n");
532 printf("\t\t Can repeat up to %d times\n", $ca_cert_size);
535 if ($build_mode == $axtlsp::SSL_BUILD_FULL_MODE)
537 printf(" -debug\t\t- Print more output\n");
538 printf(" -state\t\t- Show state messages\n");
539 printf(" -show-rsa\t- Show RSA state\n");
546 # We've had some sort of command-line error. Print out the client options.
548 sub print_client_options
550 my ($build_mode, $option) = @_;
551 my $cert_size = axtlsp::ssl_get_config($axtlsp::SSL_MAX_CERT_CFG_OFFSET);
552 my $ca_cert_size = axtlsp::ssl_get_config(
553 $axtlsp::SSL_MAX_CA_CERT_CFG_OFFSET);
555 printf("unknown option %s\n", $option);
557 if ($build_mode >= $axtlsp::SSL_BUILD_ENABLE_CLIENT)
559 printf("usage: s_client [args ...]\n");
560 printf(" -connect host:port - who to connect to (default ".
561 "is localhost:4433)\n");
562 printf(" -verify\t- turn on peer certificate verification\n");
563 printf(" -cert arg\t- certificate file to use - default DER format\n");
564 printf(" -key arg\t- Private key file to use - default DER format\n");
565 printf("\t\t Can repeat up to %d times\n", $cert_size);
566 printf(" -CAfile arg\t- Certificate authority - default DER format\n");
567 printf("\t\t Can repeat up to %d times\n", $ca_cert_size);
568 printf(" -quiet\t\t- No client output\n");
569 printf(" -pass\t\t- private key file pass phrase source\n");
570 printf(" -reconnect\t- Drop and re-make the connection ".
571 "with the same Session-ID\n");
573 if ($build_mode == $axtlsp::SSL_BUILD_FULL_MODE)
575 printf(" -debug\t\t- Print more output\n");
576 printf(" -state\t\t- Show state messages\n");
577 printf(" -show-rsa\t- Show RSA state\n");
582 printf("Change configuration to allow this feature\n");
589 # Display what cipher we are using
594 printf("CIPHER is ");
595 my $cipher_id = axtlsp::ssl_get_cipher_id($ssl);
597 if ($cipher_id == $axtlsp::SSL_AES128_SHA)
599 printf("AES128-SHA");
601 elsif ($cipher_id == $axtlsp::SSL_AES256_SHA)
603 printf("AES256-SHA");
605 elsif ($axtlsp::SSL_RC4_128_SHA)
609 elsif ($axtlsp::SSL_RC4_128_MD5)
615 printf("Unknown - %d", $cipher_id);
622 # Display what session id we have.
624 sub display_session_id
627 my $session_id = axtlsp::ssl_get_session_id($ssl);
628 if (length($$session_id) > 0)
630 printf("-----BEGIN SSL SESSION PARAMETERS-----\n");
631 printf(unpack("H*", $$session_id));
632 printf("\n-----END SSL SESSION PARAMETERS-----\n");