123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634 |
- #!/usr/bin/perl -w
- #
- # Copyright (c) 2007, Cameron Rich
- #
- # All rights reserved.
- #
- # Redistribution and use in source and binary forms, with or without
- # modification, are permitted provided that the following conditions are met:
- #
- # * Redistributions of source code must retain the above copyright notice,
- # this list of conditions and the following disclaimer.
- # * Redistributions in binary form must reproduce the above copyright
- # notice, this list of conditions and the following disclaimer in the
- # documentation and/or other materials provided with the distribution.
- # * Neither the name of the axTLS project nor the names of its
- # contributors may be used to endorse or promote products derived
- # from this software without specific prior written permission.
- #
- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- #
- #
- # Demonstrate the use of the axTLS library in Perl with a set of
- # command-line parameters similar to openssl. In fact, openssl clients
- # should be able to communicate with axTLS servers and visa-versa.
- #
- # This code has various bits enabled depending on the configuration. To enable
- # the most interesting version, compile with the 'full mode' enabled.
- #
- # To see what options you have, run the following:
- # > [perl] axssl s_server -?
- # > [perl] axssl s_client -?
- #
- # The axtls/axtlsp shared libraries must be in the same directory or be found
- # by the OS. axtlsp.pm must be in this directory or be in @INC.
- #
- # Under Win32, ActivePerl was used (see
- # http://www.activestate.com/Products/ActivePerl/?mp=1)
- #
- use axtlsp;
- use IO::Socket;
- # To get access to Win32 file descriptor stuff
- my $is_win32 = 0;
- if ($^O eq "MSWin32")
- {
- eval("use Win32API::File 0.08 qw( :ALL )");
- $is_win32 = 1;
- }
- use strict;
- #
- # Win32 has some problems with socket handles
- #
- sub get_native_sock
- {
- my ($sock) = @_;
- return $is_win32 ? FdGetOsFHandle($sock) : $sock;
- }
- # print version?
- if ($#ARGV == 0 && $ARGV[0] eq "version")
- {
- printf("axssl.pl ".axtlsp::ssl_version()."\n");
- exit 0;
- }
- #
- # Main entry point. Doesn't do much except works out whether we are a client
- # or a server.
- #
- print_options($#ARGV > -1 ? $ARGV[0] : "")
- if ($#ARGV < 0 || ($ARGV[0] ne "s_server" && $ARGV[0] ne "s_client"));
- # Cygwin/Win32 issue - flush our output continuously
- select STDOUT;
- local $|=1;
- my $build_mode = axtlsp::ssl_get_config($axtlsp::SSL_BUILD_MODE);
- $ARGV[0] eq "s_server" ? do_server($build_mode) : do_client($build_mode);
- #
- # Implement the SSL server logic.
- #
- sub do_server
- {
- my ($build_mode) = @_;
- my $i = 1;
- my $port = 4433;
- my $options = $axtlsp::SSL_DISPLAY_CERTS;
- my $quiet = 0;
- my $password = undef;
- my $private_key_file = undef;
- my $cert_size = axtlsp::ssl_get_config($axtlsp::SSL_MAX_CERT_CFG_OFFSET);
- my $ca_cert_size = axtlsp::ssl_get_config(
- $axtlsp::SSL_MAX_CA_CERT_CFG_OFFSET);
- my @cert;
- my @ca_cert;
- while ($i <= $#ARGV)
- {
- if ($ARGV[$i] eq "-accept")
- {
- print_server_options($build_mode, $ARGV[$i]) if $i >= $#ARGV;
- $port = $ARGV[++$i];
- }
- elsif ($ARGV[$i] eq "-quiet")
- {
- $quiet = 1;
- $options &= ~$axtlsp::SSL_DISPLAY_CERTS;
- }
- elsif ($build_mode >= $axtlsp::SSL_BUILD_SERVER_ONLY)
- {
- if ($ARGV[$i] eq "-cert")
- {
- print_server_options($build_mode, $ARGV[$i])
- if $i >= $#ARGV || $#cert >= $cert_size-1;
- push @cert, $ARGV[++$i];
- }
- elsif ($ARGV[$i] eq "-key")
- {
- print_server_options($build_mode, $ARGV[$i]) if $i >= $#ARGV;
- $private_key_file = $ARGV[++$i];
- $options |= $axtlsp::SSL_NO_DEFAULT_KEY;
- }
- elsif ($ARGV[$i] eq "-pass")
- {
- print_server_options($build_mode, $ARGV[$i]) if $i >= $#ARGV;
- $password = $ARGV[++$i];
- }
- elsif ($build_mode >= $axtlsp::SSL_BUILD_ENABLE_VERIFICATION)
- {
- if ($ARGV[$i] eq "-verify")
- {
- $options |= $axtlsp::SSL_CLIENT_AUTHENTICATION;
- }
- elsif ($ARGV[$i] eq "-CAfile")
- {
- print_server_options($build_mode, $ARGV[$i])
- if $i >= $#ARGV || $#ca_cert >= $ca_cert_size-1;
- push @ca_cert, $ARGV[++$i];
- }
- elsif ($build_mode == $axtlsp::SSL_BUILD_FULL_MODE)
- {
- if ($ARGV[$i] eq "-debug")
- {
- $options |= $axtlsp::SSL_DISPLAY_BYTES;
- }
- elsif ($ARGV[$i] eq "-state")
- {
- $options |= $axtlsp::SSL_DISPLAY_STATES;
- }
- elsif ($ARGV[$i] eq "-show-rsa")
- {
- $options |= $axtlsp::SSL_DISPLAY_RSA;
- }
- else
- {
- print_server_options($build_mode, $ARGV[$i]);
- }
- }
- else
- {
- print_server_options($build_mode, $ARGV[$i]);
- }
- }
- else
- {
- print_server_options($build_mode, $ARGV[$i]);
- }
- }
- else
- {
- print_server_options($build_mode, $ARGV[$i]);
- }
- $i++;
- }
- # Create socket for incoming connections
- my $server_sock = IO::Socket::INET->new(Proto => 'tcp',
- LocalPort => $port,
- Listen => 1,
- Reuse => 1) or die $!;
- ###########################################################################
- # This is where the interesting stuff happens. Up until now we've
- # just been setting up sockets etc. Now we do the SSL handshake.
- ###########################################################################
- my $ssl_ctx = axtlsp::ssl_ctx_new($options, $axtlsp::SSL_DEFAULT_SVR_SESS);
- die "Error: Server context is invalid" if not defined $ssl_ctx;
- if (defined $private_key_file)
- {
- my $obj_type = $axtlsp::SSL_OBJ_RSA_KEY;
- $obj_type = $axtlsp::SSL_OBJ_PKCS8 if $private_key_file =~ /.p8$/;
- $obj_type = $axtlsp::SSL_OBJ_PKCS12 if $private_key_file =~ /.p12$/;
- die "Private key '$private_key_file' is undefined." if
- axtlsp::ssl_obj_load($ssl_ctx, $obj_type,
- $private_key_file, $password);
- }
- foreach (@cert)
- {
- die "Certificate '$_' is undefined."
- if axtlsp::ssl_obj_load($ssl_ctx, $axtlsp::SSL_OBJ_X509_CERT,
- $_, undef) != $axtlsp::SSL_OK;
- }
- foreach (@ca_cert)
- {
- die "Certificate '$_' is undefined."
- if axtlsp::ssl_obj_load($ssl_ctx, $axtlsp::SSL_OBJ_X509_CACERT,
- $_, undef) != $axtlsp::SSL_OK;
- }
- for (;;)
- {
- printf("ACCEPT\n") if not $quiet;
- my $client_sock = $server_sock->accept;
- my $native_sock = get_native_sock($client_sock->fileno);
- # This doesn't work in Win32 - need to get file descriptor from socket.
- my $ssl = axtlsp::ssl_server_new($ssl_ctx, $native_sock);
- # do the actual SSL handshake
- my $res;
- my $buf;
- my $connected = 0;
- while (1)
- {
- ($res, $buf) = axtlsp::ssl_read($ssl, undef);
- last if $res < $axtlsp::SSL_OK;
- if ($res == $axtlsp::SSL_OK) # connection established and ok
- {
- if (axtlsp::ssl_handshake_status($ssl) == $axtlsp::SSL_OK)
- {
- if (!$quiet && !$connected)
- {
- display_session_id($ssl);
- display_cipher($ssl);
- }
- $connected = 1;
- }
- }
- if ($res > $axtlsp::SSL_OK)
- {
- printf($$buf);
- }
- elsif ($res < $axtlsp::SSL_OK)
- {
- axtlsp::ssl_display_error($res) if not $quiet;
- last;
- }
- }
- # client was disconnected or the handshake failed.
- printf("CONNECTION CLOSED\n") if not $quiet;
- axtlsp::ssl_free($ssl);
- $client_sock->close;
- }
- axtlsp::ssl_ctx_free($ssl_ctx);
- }
- #
- # Implement the SSL client logic.
- #
- sub do_client
- {
- my ($build_mode) = @_;
- my $i = 1;
- my $port = 4433;
- my $options = $axtlsp::SSL_SERVER_VERIFY_LATER|$axtlsp::SSL_DISPLAY_CERTS;
- my $private_key_file = undef;
- my $reconnect = 0;
- my $quiet = 0;
- my $password = undef;
- my @session_id;
- my $host = "127.0.0.1";
- my @cert;
- my @ca_cert;
- my $cert_size = axtlsp::ssl_get_config(
- $axtlsp::SSL_MAX_CERT_CFG_OFFSET);
- my $ca_cert_size = axtlsp::ssl_get_config(
- $axtlsp::SSL_MAX_CA_CERT_CFG_OFFSET);
- while ($i <= $#ARGV)
- {
- if ($ARGV[$i] eq "-connect")
- {
- print_client_options($build_mode, $ARGV[$i]) if $i >= $#ARGV;
- ($host, $port) = split(':', $ARGV[++$i]);
- }
- elsif ($ARGV[$i] eq "-cert")
- {
- print_client_options($build_mode, $ARGV[$i])
- if $i >= $#ARGV || $#cert >= $cert_size-1;
- push @cert, $ARGV[++$i];
- }
- elsif ($ARGV[$i] eq "-key")
- {
- print_client_options($build_mode, $ARGV[$i]) if $i >= $#ARGV;
- $private_key_file = $ARGV[++$i];
- $options |= $axtlsp::SSL_NO_DEFAULT_KEY;
- }
- elsif ($ARGV[$i] eq "-CAfile")
- {
- print_client_options($build_mode, $ARGV[$i])
- if $i >= $#ARGV || $#ca_cert >= $ca_cert_size-1;
- push @ca_cert, $ARGV[++$i];
- }
- elsif ($ARGV[$i] eq "-verify")
- {
- $options &= ~$axtlsp::SSL_SERVER_VERIFY_LATER;
- }
- elsif ($ARGV[$i] eq "-reconnect")
- {
- $reconnect = 4;
- }
- elsif ($ARGV[$i] eq "-quiet")
- {
- $quiet = 1;
- $options &= ~$axtlsp::SSL_DISPLAY_CERTS;
- }
- elsif ($ARGV[$i] eq "-pass")
- {
- print_server_options($build_mode, $ARGV[$i]) if $i >= $#ARGV;
- $password = $ARGV[++$i];
- }
- elsif ($build_mode == $axtlsp::SSL_BUILD_FULL_MODE)
- {
- if ($ARGV[$i] eq "-debug")
- {
- $options |= $axtlsp::SSL_DISPLAY_BYTES;
- }
- elsif ($ARGV[$i] eq "-state")
- {
- $options |= $axtlsp::SSL_DISPLAY_STATES;
- }
- elsif ($ARGV[$i] eq "-show-rsa")
- {
- $options |= $axtlsp::SSL_DISPLAY_RSA;
- }
- else # don't know what this is
- {
- print_client_options($build_mode, $ARGV[$i]);
- }
- }
- else # don't know what this is
- {
- print_client_options($build_mode, $ARGV[$i]);
- }
- $i++;
- }
- my $client_sock = new IO::Socket::INET (
- PeerAddr => $host, PeerPort => $port, Proto => 'tcp')
- || die ("no socket: $!");
- my $ssl;
- my $res;
- my $native_sock = get_native_sock($client_sock->fileno);
- printf("CONNECTED\n") if not $quiet;
- ###########################################################################
- # This is where the interesting stuff happens. Up until now we've
- # just been setting up sockets etc. Now we do the SSL handshake.
- ###########################################################################
- my $ssl_ctx = axtlsp::ssl_ctx_new($options, $axtlsp::SSL_DEFAULT_CLNT_SESS);
- die "Error: Client context is invalid" if not defined $ssl_ctx;
- if (defined $private_key_file)
- {
- my $obj_type = $axtlsp::SSL_OBJ_RSA_KEY;
- $obj_type = $axtlsp::SSL_OBJ_PKCS8 if $private_key_file =~ /.p8$/;
- $obj_type = $axtlsp::SSL_OBJ_PKCS12 if $private_key_file =~ /.p12$/;
- die "Private key '$private_key_file' is undefined." if
- axtlsp::ssl_obj_load($ssl_ctx, $obj_type,
- $private_key_file, $password);
- }
- foreach (@cert)
- {
- die "Certificate '$_' is undefined."
- if axtlsp::ssl_obj_load($ssl_ctx, $axtlsp::SSL_OBJ_X509_CERT,
- $_, undef) != $axtlsp::SSL_OK;
- }
- foreach (@ca_cert)
- {
- die "Certificate '$_' is undefined."
- if axtlsp::ssl_obj_load($ssl_ctx, $axtlsp::SSL_OBJ_X509_CACERT,
- $_, undef) != $axtlsp::SSL_OK;
- }
- # Try session resumption?
- if ($reconnect)
- {
- my $session_id = undef;
- my $sess_id_size = 0;
- while ($reconnect--)
- {
- $ssl = axtlsp::ssl_client_new($ssl_ctx, $native_sock,
- $session_id, $sess_id_size);
- $res = axtlsp::ssl_handshake_status($ssl);
- if ($res != $axtlsp::SSL_OK)
- {
- axtlsp::ssl_display_error($res) if !$quiet;
- axtlsp::ssl_free($ssl);
- exit 1;
- }
- display_session_id($ssl);
- $session_id = axtlsp::ssl_get_session_id($ssl);
- if ($reconnect)
- {
- axtlsp::ssl_free($ssl);
- $client_sock->close;
- $client_sock = new IO::Socket::INET (
- PeerAddr => $host, PeerPort => $port, Proto => 'tcp')
- || die ("no socket: $!");
- }
- }
- }
- else
- {
- $ssl = axtlsp::ssl_client_new($ssl_ctx, $native_sock, undef, 0);
- }
- # check the return status
- $res = axtlsp::ssl_handshake_status($ssl);
- if ($res != $axtlsp::SSL_OK)
- {
- axtlsp::ssl_display_error($res) if not $quiet;
- exit 1;
- }
- if (!$quiet)
- {
- my $common_name = axtlsp::ssl_get_cert_dn($ssl,
- $axtlsp::SSL_X509_CERT_COMMON_NAME);
- printf("Common Name:\t\t\t%s\n", $common_name) if defined $common_name;
- display_session_id($ssl);
- display_cipher($ssl);
- }
- while (<STDIN>)
- {
- my $cstring = pack("a*x", $_); # add null terminator
- $res = axtlsp::ssl_write($ssl, \$cstring, length($cstring));
- if ($res < $axtlsp::SSL_OK)
- {
- axtlsp::ssl_display_error($res) if not $quiet;
- last;
- }
- }
- axtlsp::ssl_ctx_free($ssl_ctx);
- $client_sock->close;
- }
- #
- # We've had some sort of command-line error. Print out the basic options.
- #
- sub print_options
- {
- my ($option) = @_;
- printf("axssl: Error: '%s' is an invalid command.\n", $option);
- printf("usage: axssl [s_server|s_client|version] [args ...]\n");
- exit 1;
- }
- #
- # We've had some sort of command-line error. Print out the server options.
- #
- sub print_server_options
- {
- my ($build_mode, $option) = @_;
- my $cert_size = axtlsp::ssl_get_config($axtlsp::SSL_MAX_CERT_CFG_OFFSET);
- my $ca_cert_size = axtlsp::ssl_get_config(
- $axtlsp::SSL_MAX_CA_CERT_CFG_OFFSET);
- printf("unknown option %s\n", $option);
- printf("usage: s_server [args ...]\n");
- printf(" -accept arg\t- port to accept on (default is 4433)\n");
- printf(" -quiet\t\t- No server output\n");
- if ($build_mode >= $axtlsp::SSL_BUILD_SERVER_ONLY)
- {
- printf(" -cert arg\t- certificate file to add (in addition to default)".
- " to chain -\n".
- "\t\t Can repeat up to %d times\n", $cert_size);
- printf(" -key arg\t- Private key file to use - default DER format\n");
- printf(" -pass\t\t- private key file pass phrase source\n");
- }
- if ($build_mode >= $axtlsp::SSL_BUILD_ENABLE_VERIFICATION)
- {
- printf(" -verify\t- turn on peer certificate verification\n");
- printf(" -CAfile arg\t- Certificate authority - default DER format\n");
- printf("\t\t Can repeat up to %d times\n", $ca_cert_size);
- }
- if ($build_mode == $axtlsp::SSL_BUILD_FULL_MODE)
- {
- printf(" -debug\t\t- Print more output\n");
- printf(" -state\t\t- Show state messages\n");
- printf(" -show-rsa\t- Show RSA state\n");
- }
- exit 1;
- }
- #
- # We've had some sort of command-line error. Print out the client options.
- #
- sub print_client_options
- {
- my ($build_mode, $option) = @_;
- my $cert_size = axtlsp::ssl_get_config($axtlsp::SSL_MAX_CERT_CFG_OFFSET);
- my $ca_cert_size = axtlsp::ssl_get_config(
- $axtlsp::SSL_MAX_CA_CERT_CFG_OFFSET);
- printf("unknown option %s\n", $option);
- if ($build_mode >= $axtlsp::SSL_BUILD_ENABLE_CLIENT)
- {
- printf("usage: s_client [args ...]\n");
- printf(" -connect host:port - who to connect to (default ".
- "is localhost:4433)\n");
- printf(" -verify\t- turn on peer certificate verification\n");
- printf(" -cert arg\t- certificate file to use - default DER format\n");
- printf(" -key arg\t- Private key file to use - default DER format\n");
- printf("\t\t Can repeat up to %d times\n", $cert_size);
- printf(" -CAfile arg\t- Certificate authority - default DER format\n");
- printf("\t\t Can repeat up to %d times\n", $ca_cert_size);
- printf(" -quiet\t\t- No client output\n");
- printf(" -pass\t\t- private key file pass phrase source\n");
- printf(" -reconnect\t- Drop and re-make the connection ".
- "with the same Session-ID\n");
- if ($build_mode == $axtlsp::SSL_BUILD_FULL_MODE)
- {
- printf(" -debug\t\t- Print more output\n");
- printf(" -state\t\t- Show state messages\n");
- printf(" -show-rsa\t- Show RSA state\n");
- }
- }
- else
- {
- printf("Change configuration to allow this feature\n");
- }
- exit 1;
- }
- #
- # Display what cipher we are using
- #
- sub display_cipher
- {
- my ($ssl) = @_;
- printf("CIPHER is ");
- my $cipher_id = axtlsp::ssl_get_cipher_id($ssl);
- if ($cipher_id == $axtlsp::SSL_AES128_SHA)
- {
- printf("AES128-SHA");
- }
- elsif ($cipher_id == $axtlsp::SSL_AES256_SHA)
- {
- printf("AES256-SHA");
- }
- elsif ($axtlsp::SSL_RC4_128_SHA)
- {
- printf("RC4-SHA");
- }
- elsif ($axtlsp::SSL_RC4_128_MD5)
- {
- printf("RC4-MD5");
- }
- else
- {
- printf("Unknown - %d", $cipher_id);
- }
- printf("\n");
- }
- #
- # Display what session id we have.
- #
- sub display_session_id
- {
- my ($ssl) = @_;
- my $session_id = axtlsp::ssl_get_session_id($ssl);
- if (length($$session_id) > 0)
- {
- printf("-----BEGIN SSL SESSION PARAMETERS-----\n");
- printf(unpack("H*", $$session_id));
- printf("\n-----END SSL SESSION PARAMETERS-----\n");
- }
- }
|