#
#
# a class for implementing a IO::Socket::INET type interface
# to SSL-sockets (aspa@kronodoc.fi).
#
# this implementation draws from Crypt::SSLeay (Net::SSL)
# by Gisle Aas.
#
#
# $Id: SSL.pm,v 1.55 2002/03/18 06:46:27 aspa Exp $.
#
#
# prerequisites:
# - Net_SSLeay-1.03 (CPAN).
# - OpenSSL v0.9.1c (ftp://ftp.openssl.org/).
#
# Notes:
# ------
# * IO::Socket::INET interface used by LWP::Protocol::http (see
# LWP::Protocol::http::request (LWP v5.43)):
# * Net::SSL interface used by LWP::Protocol (see
# LWP::Protocol::https (LWP v5.43)):
# - $sock->get_peer_certificate, $sock->get_cipher,
# $cert->subject_name, $cert->issuer_name.
# * LWP::Protocol::https disables warnings.
#
# TODO:
# -----
# - error handling: a server side view.
#
#
package IO::Socket::SSL;
use 5.005;
use strict;
use Carp;
use English;
use POSIX qw(getcwd);
use Net::SSLeay 1.08;
use IO::Socket;
$IO::Socket::SSL::VERSION = '0.81';
@IO::Socket::SSL::ISA = qw(IO::Socket::INET);
Net::SSLeay::load_error_strings();
Net::SSLeay::SSLeay_add_ssl_algorithms();
Net::SSLeay::randomize();
$IO::Socket::SSL::SSL_Context_obj = 0;
$IO::Socket::SSL::DEBUG = 0;
if($IO::Socket::SSL::DEBUG) {
print STDERR "\nusing **SSL_NetSSLeay.pm: v$IO::Socket::SSL::VERSION\n";
}
#
# ***** set default values for key and cert files etc.
#
my $DEFAULT_SERVER_KEY_FILE = "certs/server-key.pem";
my $DEFAULT_SERVER_CERT_FILE = "certs/server-cert.pem";
my $DEFAULT_CLIENT_KEY_FILE = "certs/client-key.pem";
my $DEFAULT_CLIENT_CERT_FILE = "certs/client-cert.pem";
my $DEFAULT_CA_FILE = "certs/my-ca.pem";
my $DEFAULT_CA_PATH = getcwd() . "/certs";
my $DEFAULT_IS_SERVER = 0;
my $DEFAULT_USE_CERT = 0;
# &Net::SSLeay::VERIFY_NONE, &Net::SSLeay::VERIFY_PEER();
my $DEFAULT_VERIFY_MODE = &Net::SSLeay::VERIFY_PEER();
my $DEFAULT_CIPHER_LIST = "ALL:!LOW:!EXP";
my $DEFAULT_SSL_VERSION = undef;
#
# ******************** IO::Socket::SSL class ********************
#
# class attributes:
# -----------------
# SSL_Context_obj, DEBUG
#
# instance attributes:
# --------------------
# _fileno - call to overrided fileno() result in infinite recursion as
# our parent (IO::Handle) calls fileno() in fileno(). for this
# reason fileno() has to be implemented without using fileno().
# _opened - see _fileno.
# _SSL_SSL_obj - encapsulates the underlying SSL connection object
# _arguments - arguments used when creating the IO::Socket::SSL object
# _SSL_want_read - for non-blocking IO
# _SSL_want_write - for non-blocking IO
#
# private methods:
# ----------------
# _init_SSL, _unsupported, _unimplemented, _myerror, _get_SSL_err_str
#
sub context_init {
my $args = shift;
my ($ctx);
if ( ! defined ($ctx = SSL_Context->new($args)) ) {
return $ctx;
}
set_global_context($ctx);
return 1;
}
sub set_global_context {
my $ctx = shift;
if($ctx) { $ctx->{'_isGlobalCtx'} = 1; } # not when resetting
$IO::Socket::SSL::SSL_Context_obj = $ctx;
}
sub get_global_context {
my $ctx = $IO::Socket::SSL::SSL_Context_obj;
return $ctx;
}
# object creation call stack:
# - new IO::Socket::SSL
# -- new IO::Socket::INET
# --- new IO::Socket
# ---- new IO::Handle
# ---- IO::Socket::SSL::configure
# ----- IO::Socket::INET::configure
# ------ listen/connect
sub new {
my $class = shift || "IO::Socket::SSL";
my $self;
if( !($self = $class->SUPER::new(@_)) ) {
return undef;
}
_preBlessInitAttrs($self, fileno($self));
#bless $self, $class;
tie *{$self}, 'SSL_HANDLE', $self;
return $self;
}
# initialize instance attributes.
sub _preBlessInitAttrs {
my $self = shift;
my $fileno = shift;
# NB: do not set '_opened', '_SSL_SSL_obj', '_arguments'.
${*$self}{'_fileno'} = $fileno;
${*$self}{'_SSL_want_read'} = 0;
${*$self}{'_SSL_want_write'} = 0;
return 1;
}
# ***** configure
#
# return values: IO::Socket::SSL or undef.
#
sub configure {
my ($self, $args) = @_;
my ($r, $k, $v, $ctx_obj, $ctx_created, $ssl_obj);
# choose context object to use: parameter or global.
$ctx_obj = $args->{'SSL_CTX'} || get_global_context();
# SSL_Context::new sets up SSL context. it's run only once.
if(! $ctx_obj ) {
# implicitly create SSL context. argument logic:
# on an implicit context creation per connection arguments
# are used also as global SSL context arguments!
if( ! defined ($ctx_obj = SSL_Context->new($args)) ) {
# context initialization failed. fatal.
return undef;
} else {
# a valid context was returned. save it.
set_global_context($ctx_obj);
}
}
# save SSL configuration arguments from $args and save
# them in ${*$self} for connect and accept.
${*$self}{'_arguments'} = $args;
# call superclass's (IO::Socket::INET) configure to setup
# connection. superclass's configure calls connect and
# accept methods among others.
if( !($r = $self->SUPER::configure($args)) ) {
my $err_str = "\$fh->SUPER::configure() failed: $!.";
return $self->_myerror("configure: '$err_str'.");
}
return $self;
}
# ***** connect
#
# return values: IO::Socket::SSL or undef.
#
sub connect {
my $self = shift;
my ($s, $r, $ssl_obj);
my $args = ${*$self}{'_arguments'};
if( !($s = $self->SUPER::connect(@_)) ) {
return $s;
}
# create the SSL object.
if( ! ($ssl_obj = SSL_SSL->new($s, $args)) ) {
return undef;
}
${*$s}{'_SSL_SSL_obj'} = $ssl_obj;
my $ssl = $ssl_obj->get_ssl_handle();
if ( ($r = Net::SSLeay::connect($ssl)) <= 0 ) { # ssl/s23_clnt.c
my $err_str = $self->_get_SSL_err_str();
return $self->_myerror("SSL_connect: '$err_str'.");
}
${*$self}{'_opened'} = 1;
return $self;
}
# ***** accept
#
# return values: IO::Socket::SSL or undef.
#
sub accept {
my $self = shift;
my $class = shift || "IO::Socket::SSL";
my ($newsock, $r, $ssl_obj);
my $args = ${*$self}{'_arguments'};
if( ! ($newsock = IO::Socket::accept($self, 'IO::Socket::INET')) ) {
return $self->_myerror("accept failed: '$!'.\n");
}
my $fileno = fileno($newsock);
_preBlessInitAttrs($newsock, $fileno);
# create the SSL object.
if( ! ($ssl_obj = SSL_SSL->new($newsock, $args)) ) {
return undef;
}
${*$newsock}{'_SSL_SSL_obj'} = $ssl_obj;
my $ssl = $ssl_obj->get_ssl_handle();
if( ($r = Net::SSLeay::accept($ssl)) <= 0 ) { # ssl/s23_srvr.c
my $err_str = $self->_get_SSL_err_str();
return $self->_myerror("SSL_accept: '$err_str'.");
}
# make $newsock a IO::Socket::SSL object and tie it.
bless $newsock, $class;
tie *{$newsock}, 'SSL_HANDLE', $newsock;
print STDERR "accept: self: $self, newsock: $newsock, fileno: $fileno.\n"
if $IO::Socket::SSL::DEBUG;
${*$newsock}{'_opened'} = 1;
return $newsock;
}
# ***** alias sysread and syswrite.
*read = \&sysread;
*write = \&syswrite;
# ***** syswrite
sub syswrite {
if( (@_ < 2) || (@_ > 4) ) {
croak '$fh->syswrite(BUF [, LEN [, OFFSET]])';
}
my $self = shift;
my $buf = shift;
my $arg_len = shift || length $buf;
my $offset = shift || 0;
my $ssl_obj = ${*$self}{'_SSL_SSL_obj'};
my $ssl = $ssl_obj->get_ssl_handle();
my ($res, $len, $real_len, $wbufref, $ssl_err);
# obtain a buffer ref to write buffer.
$wbufref = \substr("$buf", $offset, $arg_len);
# argument length is not allowed to be greater than buffer length.
if( $arg_len > ($real_len = length($$wbufref)) ) {
$len = $real_len;
} else {
$len = $arg_len;
}
# previous operations don't count.
${*$self}{'_SSL_want_read'} = 0;
${*$self}{'_SSL_want_write'} = 0;
# see Net_SSLeay-1.03/SSLeay.xs,
# openssl-0.9.1c/ssl/ssl_lib.c and bio_ssl.c.
if( ($res = Net::SSLeay::write($ssl, $$wbufref)) < 0 ) {
if( ($ssl_err = Net::SSLeay::get_error($ssl, -1)) ) {
# '-1' safe as of openssl-0.9.6b/ssl/ssl_lib.c
if ($ssl_err == &Net::SSLeay::ERROR_WANT_READ) {
# possible if a renogotiation is taking place
${*$self}{'_SSL_want_read'} = 1;
} elsif ($ssl_err == &Net::SSLeay::ERROR_WANT_WRITE) {
${*$self}{'_SSL_want_write'} = 1;
} else {
my $err_str = $self->_get_SSL_err_str();
}
}
return undef;
}
return $res;
}
# ***** sysread
sub sysread {
if( (@_ != 3) && (@_ != 4) ) {
croak '$fh->sysread(BUF, LEN [, OFFSET])';
}
my $self = $_[0];
my $max_len = $_[2];
my $offset = $_[3] || 0;
my ($int_buf, $ssl_err);
my $ssl_obj = ${*$self}{'_SSL_SSL_obj'};
my $ssl = $ssl_obj->get_ssl_handle();
# previous operations don't count.
${*$self}{'_SSL_want_read'} = 0;
${*$self}{'_SSL_want_write'} = 0;
# see Net_SSLeay-1.03/SSLeay.xs,
# openssl-0.9.1c/ssl/ssl_lib.c and bio_ssl.c.
if( ! defined ($int_buf = Net::SSLeay::read($ssl, $max_len)) ) {
if( ($ssl_err = Net::SSLeay::get_error($ssl, -1)) ) {
# '-1' safe as of openssl-0.9.6b/ssl/ssl_lib.c
if ($ssl_err == &Net::SSLeay::ERROR_WANT_READ) {
${*$self}{'_SSL_want_read'} = 1;
} elsif ($ssl_err == &Net::SSLeay::ERROR_WANT_WRITE) {
# possible if a renogotiation is taking place
${*$self}{'_SSL_want_write'} = 1;
} else {
my $err_str = $self->_get_SSL_err_str();
}
}
return undef;
}
my $read_len = length($int_buf);
# EOF handling: we've had an EOF if Net::SSLeay::read() returns 0.
if( $read_len == 0 ) {
# N.B.: perl sysread() semantics seem to require that
# the buffer is set to "" when an EOF is encountered.
$_[1] = "";
return 0;
}
if(!defined($_[1])) { $_[1] = ""; } # initialize uninitialized buffer.
my $buffer_len = length($_[1]);
my $start = ($offset >= 0) ? $offset : $buffer_len + $offset;
my $elen = $buffer_len - $start;
# IO::Scalar might be handy with buffer handling.
if ( ($start >= 0) && ($start <= $buffer_len) ) {
substr($_[1], $start, $elen) = "$int_buf";
} else {
croak '$fh->sysread(): offset outside of buffer.' .
" ('$_[1]' : $start / $buffer_len / $read_len).";
}
return $read_len;
}
sub want_read {
my $self = shift;
my $v = $ {*$self}{'_SSL_want_read'};
return $v;
}
sub want_write {
my $self = shift;
my $v = $ {*$self}{'_SSL_want_write'};
return $v;
}
# ***** readline
sub readline {
my $self = shift;
my $ssl_obj = ${*$self}{'_SSL_SSL_obj'};
my $ssl = $ssl_obj->get_ssl_handle();
if (wantarray()) { # list context
my (@got, $got);
while ($got = Net::SSLeay::ssl_read_until($ssl)) { push @got, $got; }
return @got;
}
else { # scalar or void context
my $got = Net::SSLeay::ssl_read_until($ssl);
return ($got eq '')?undef:$got;
}
}
# ***** print
sub print {
if( ! @_ ) {
croak 'usage: $fh->print([ARGS])';
}
my $field_separator = (defined $,) ? $, : '';
my $record_separator = (defined $\) ? $\ : '';
my $self = shift;
my $str = join($field_separator, @_, $record_separator);
#print STDERR "print: str: '$str'\n" if $IO::Socket::SSL::DEBUG;
return $self->syswrite($str, length($str));
}
# ***** printf
sub printf {
if( (@_ < 2) ) {
croak 'usage: $fh->printf(FMT,[ARGS])';
}
my $self = shift;
my $fmt = shift;
my $str = sprintf($fmt, @_); # sprintf return values?
return $self->syswrite($str, length($str));
}
# ***** close
sub close {
my $self = shift;
print STDERR "close: $self.\n" if $IO::Socket::SSL::DEBUG;
untie(*$self);
${*$self}{'_opened'} = 0;
return $self->SUPER::close();
}
sub opened {
my $self = shift;
return ${*$self}{'_opened'};
}
# ***** socketToSSL
# support for startTLS.
sub socketToSSL {
my $sock = shift;
my $args = shift || {};
my $r;
if(!$sock) {
croak 'usage: IO::Socket::SSL::socketToSSL(socket)';
}
_preBlessInitAttrs($sock, fileno($sock));
# transform IO::Socket::INET to IO::Socket::SSL.
# create an SSL object.
my $ssl_obj;
if( ! ($ssl_obj = SSL_SSL->new($sock, $args)) ) {
my $err_str = IO::Socket::SSL::_get_SSL_err_str();
return IO::Socket::SSL::_myerror($sock, "socketToSSL(): " .
"unable to create SSL object");
}
${*$sock}{'_SSL_SSL_obj'} = $ssl_obj;
my $ssl = $ssl_obj->get_ssl_handle();
if ( ($r = Net::SSLeay::connect($ssl)) <= 0 ) { # ssl/s23_clnt.c
my $err_str = IO::Socket::SSL::_get_SSL_err_str();
return IO::Socket::SSL::_myerror($sock,"socketToSSL(): connect failed");
}
bless $sock, "IO::Socket::SSL";
tie *{$sock}, 'SSL_HANDLE', $sock;
return $sock;
}
# ***** get_verify_mode
sub get_verify_mode {
my $self = shift;
# get verify mode from SSL_SSL!
# get SSL context.
my $args = ${*$self}{'_arguments'};
my $ctx_obj = $args->{'SSL_CTX'} || get_global_context();
my $ctx = $ctx_obj->get_context_handle;
# Net::SSLeay does not implement this function, yet.
#my $mode = &Net::SSLeay::CTX_get_verify_mode($ctx);
#return $mode;
return undef;
}
# ***** get_cipher
sub get_cipher {
my $self = shift;
my $ssl_obj = ${*$self}{'_SSL_SSL_obj'};
my $ssl = $ssl_obj->get_ssl_handle();
my $cipher_str = Net::SSLeay::get_cipher($ssl);
return $cipher_str;
}
# ***** get_peer_certificate
sub get_peer_certificate {
my $self = shift;
my $ssl_obj = ${*$self}{'_SSL_SSL_obj'};
my $ssl = $ssl_obj->get_ssl_handle();
my ($cert, $cert_obj);
if(!($cert = Net::SSLeay::get_peer_certificate($ssl))) {
my $err_str = $self->_get_SSL_err_str();
return $self->_myerror("get_peer_certificate: '$err_str'.");
}
$cert_obj = X509_Certificate->new();
$cert_obj->{'_cert_handle'} = $cert;
return $cert_obj;
}
sub DESTROY {
my $self = shift;
print STDERR "IO::Socket::SSL::DESTROY: '$self'.\n"
if $IO::Socket::SSL::DEBUG;
}
# ***** unsupported methods.
sub getc { shift->_unsupported("getc"); }
sub eof { shift->_unsupported("eof"); }
sub truncate { shift->_unsupported("truncate"); }
sub stat { shift->_unsupported("stat"); }
sub ungetc { shift->_unsupported("ungetc"); }
sub setbuf { shift->_unsupported("setbuf"); }
sub setvbuf { shift->_unsupported("setvbuf"); }
# ***** unimplemented methods.
sub getline { shift->_unimplemented("getline"); }
sub getlines { shift->_unimplemented("getlines"); }
sub fdopen { shift->_unimplemented("fdopen"); }
sub untaint { shift->_unimplemented("untaint"); }
# ***** utility methods
sub _myerror {
my $fh = shift;
$fh = ref($fh) ? $fh : 0;
my $errstr = join("", "fh: '$fh'. error message: '", @_, "'");
carp $errstr if $IO::Socket::SSL::DEBUG;
if($fh && defined fileno($fh)) {
#$fh->close();
}
return undef;
}
sub _unsupported {
my($self, $meth) = @_;
die "'$meth' not supported by IO::Socket::SSL sockets";
}
sub _unimplemented {
my($self, $meth) = @_;
die "'$meth' not implemented for IO::Socket::SSL sockets";
}
sub _get_SSL_err_str {
my $err = Net::SSLeay::ERR_get_error();
my $err_str = Net::SSLeay::ERR_error_string($err);
return $err_str;
}
1;
#
# ******************** SSL_HANDLE ********************
#
package SSL_HANDLE;
# ***** define filehandle tying interface.
sub TIEHANDLE {
my $class = shift;
my $tie_handle = shift;
return bless \$tie_handle, $class;
}
sub PRINT {
my $tie_handle = shift;
return ${$tie_handle}->print(@_);
}
sub PRINTF {
my $tie_handle = shift;
return ${$tie_handle}->printf(@_);
}
sub WRITE {
my $tie_handle = shift;
return ${$tie_handle}->write(@_);
}
sub READLINE {
my $tie_handle = shift;
return ${$tie_handle}->readline(@_);
}
sub GETC {
my $tie_handle = shift;
return ${$tie_handle}->getc(@_);
}
sub READ {
my $tie_handle = shift;
return ${$tie_handle}->read(@_);
}
sub CLOSE {
my $tie_handle = shift;
return ${$tie_handle}->close(@_);
}
sub FILENO {
my $tie_handle = shift;
my $fileno = ${*${$tie_handle}}{'_fileno'};
return $fileno;
}
1;
#
# ******************** SSL_SSL class ********************
#
package SSL_SSL;
# instance attributes:
# --------------------
# _SSL_ssl_handle
#
@SSL_SSL::ISA = ();
# ***** new
#
# return values: SSL-ref or undef.
#
sub new {
my $class = shift;
my $s = shift;
my $args = shift;
my $self = {};
bless $self, $class;
my ($r, $ssl);
my $ctx_obj = $args->{'SSL_CTX'} || IO::Socket::SSL::get_global_context();
my $ctx = $ctx_obj->get_context_handle;
my $cipher_list = $args->{'SSL_cipher_list'} || $DEFAULT_CIPHER_LIST;
my $verify_mode = (defined $args->{'SSL_verify_mode'}) ?
$args->{'SSL_verify_mode'} : $DEFAULT_VERIFY_MODE;
# create a new SSL structure and attach it to the context.
if (!($ssl = Net::SSLeay::new($ctx)) ) {
my $err_str =IO::Socket::SSL::_get_SSL_err_str();
return IO::Socket::SSL::_myerror("SSL_new: '$err_str'.");
}
# set per connection options.
if (defined $verify_mode) {
&Net::SSLeay::set_verify($ssl, $verify_mode, 0);
}
# see: bin/openssl ciphers -v,
# http://www.modssl.org/docs/2.3/ssl_reference.html#ToC9
&Net::SSLeay::set_cipher_list($ssl, $cipher_list);
if( ! ($r = Net::SSLeay::set_fd($ssl, $s->fileno)) ) {
my $err_str = IO::Socket::SSL::_get_SSL_err_str();
return IO::Socket::SSL::_myerror("set_fd: '$err_str'.");
}
$self->{'_SSL_ssl_handle'} = $ssl;
return $self;
}
sub get_ssl_handle {
my $self = shift;
return $self->{'_SSL_ssl_handle'};
}
# ***** DESTROY
sub DESTROY {
my $self = shift;
my $ssl = $self->get_ssl_handle();
print STDERR "DESTROY: $self.\n" if $IO::Socket::SSL::DEBUG;
if($ssl) {
# should release all SSL-struct related resources.
Net::SSLeay::free($ssl);
$self->{'_SSL_ssl_handle'} = undef;
}
}
1;
#
# ******************** SSL_Context class ********************
#
package SSL_Context;
# instance attributes:
# --------------------
# _SSL_context
#
@SSL_Context::ISA = ();
#
# ***** SSL_Context::new
#
# return values: SSL context ref or undef.
#
sub new {
my ($class, $args) = @_;
my ($key_file, $cert_file, $ca_file, $ca_path,
$is_server, $use_cert, $verify_mode, $r, $s, $ctx);
my $self = {};
bless $self, $class;
# get SSL arguments.
$is_server = $args->{'SSL_server'} || $args->{'Listen'};
if ( $is_server ) {
# creating a server socket.
$key_file=$args->{'SSL_key_file'}||$DEFAULT_SERVER_KEY_FILE;
$cert_file=$args->{'SSL_cert_file'}||$DEFAULT_SERVER_CERT_FILE;
} else {
# creating a client socket.
$key_file=$args->{'SSL_key_file'}||$DEFAULT_CLIENT_KEY_FILE;
$cert_file=$args->{'SSL_cert_file'}||$DEFAULT_CLIENT_CERT_FILE;
}
$ca_file = (defined $args->{'SSL_ca_file'}) ?
$args->{'SSL_ca_file'} : $DEFAULT_CA_FILE;
$ca_path = $args->{'SSL_ca_path'} || $DEFAULT_CA_PATH;
$verify_mode = (defined $args->{'SSL_verify_mode'}) ?
$args->{'SSL_verify_mode'} : $DEFAULT_VERIFY_MODE;
$use_cert = $args->{'SSL_use_cert'} || $DEFAULT_USE_CERT;
# choose SSL protocol version to be used.
my $CTX_constructor = undef;
my $ssl_version = $args->{'SSL_version'} || $DEFAULT_SSL_VERSION;
if($ssl_version) {
if($ssl_version eq "sslv2" ) {
$CTX_constructor = \&Net::SSLeay::CTX_v2_new;
print STDERR "using SSLv2\n" if($IO::Socket::SSL::DEBUG);
} elsif ($ssl_version eq "sslv3" ) {
$CTX_constructor = \&Net::SSLeay::CTX_v3_new;
print STDERR "using SSLv3\n" if($IO::Socket::SSL::DEBUG);
} elsif ($ssl_version eq "tlsv1") {
$CTX_constructor = \&Net::SSLeay::CTX_tlsv1_new;
print STDERR "using TLSv1\n" if($IO::Socket::SSL::DEBUG);
} else { # SSL v23
;
}
}
if(!$ssl_version || !$CTX_constructor) { # default to SSL v23
print STDERR "using SSLv2/3\n" if($IO::Socket::SSL::DEBUG);
$CTX_constructor = \&Net::SSLeay::CTX_new;
}
# create SSL context;
if(! ($ctx = &{$CTX_constructor}() ) ) {
my $err_str = IO::Socket::SSL::_get_SSL_err_str();
return IO::Socket::SSL::_myerror("CTX_new(): '$err_str'.");
}
# set options for the context.
$r = Net::SSLeay::CTX_set_options($ctx, &Net::SSLeay::OP_ALL() );
if( !($verify_mode == &Net::SSLeay::VERIFY_NONE()) ) {
# set SSL certificate load paths.
if(!($r = Net::SSLeay::CTX_load_verify_locations($ctx,
$ca_file,
$ca_path))) {
my $err_str = IO::Socket::SSL::_get_SSL_err_str();
return IO::Socket::SSL::_myerror("CTX_load_verify_locations: " .
"'$err_str'.");
}
}
# NOTE: private key, certificate and certificate verification
# mode are associated only to the SSL context. this is
# because they are client/server specific attributes and
# it doesn't seem to make much sense to change them between
# requests (aspa@kronodoc.fi).
# load certificate and private key.
if( $is_server || $use_cert ) {
print STDERR "loading private key ($key_file).\n"
if ($IO::Socket::SSL::DEBUG);
if(!($r=Net::SSLeay::CTX_use_PrivateKey_file($ctx,
$key_file, &Net::SSLeay::FILETYPE_PEM() ))) {
my $err_str = IO::Socket::SSL::_get_SSL_err_str();
return IO::Socket::SSL::_myerror("CTX_use_RSAPrivateKey_file:" .
" '$err_str'.");
}
print STDERR "loading cert ($cert_file).\n"
if ($IO::Socket::SSL::DEBUG);
if(!($r=Net::SSLeay::CTX_use_certificate_file($ctx,
$cert_file, &Net::SSLeay::FILETYPE_PEM() ))) {
my $err_str = IO::Socket::SSL::_get_SSL_err_str();
return IO::Socket::SSL::_myerror("CTX_use_certificate_file:" .
" '$err_str'.");
}
}
$r = Net::SSLeay::CTX_set_verify($ctx, $verify_mode, 0);
$self->{'_SSL_context'} = $ctx;
return $self;
}
# b IO::Socket::SSL::configure
sub getConnection {
my $self = shift;
my $class = shift;
push @_, 'SSL_CTX', $self;
my $sock = $class->new(@_);
return $sock;
}
sub get_context_handle {
my $self = shift;
return $self->{'_SSL_context'};
}
sub DESTROY {
my $self = shift;
my $ctx = $self->get_context_handle;
print STDERR "SSL_Context::DESTROY: '$self', '$ctx'.\n"
if $IO::Socket::SSL::DEBUG;
# this is an example of a potential race condition.
if ($ctx && !$self->{'_CTX_freed'}) {
# should release all SSL_CTX-struct related resources.
Net::SSLeay::CTX_free($ctx);
$self->{'_CTX_freed'} = 1;
}
# IO::Socket::SSL specific.
my $gCtx = IO::Socket::SSL::get_global_context();
if($self->{'_isGlobalCtx'} && $gCtx && ($gCtx == $ctx)) {
IO::Socket::SSL::set_global_context(0);
}
return;
}
1;
#
# ******************** X509_Certificate class ********************
#
#
# a minimal class for providing certificate handling functionality
# needed by libwww-perl (LWP::Protocol::https).
#
package X509_Certificate;
# instance attributes:
# --------------------
# _cert_handle
#
@X509_Certificate::ISA = ();
sub new {
bless {};
};
sub subject_name {
my $self = shift;
my $cert = $self->{'_cert_handle'};
my ($name, $str_name);
if(!($name = Net::SSLeay::X509_get_subject_name($cert))) {
my $err_str = IO::Socket::SSL::_get_SSL_err_str();
return IO::Socket::SSL::_myerror("X509_get_subject_name: " .
"'$err_str'.");
}
$str_name = Net::SSLeay::X509_NAME_oneline($name);
return "$str_name";
}
sub issuer_name {
my $self = shift;
my $cert = $self->{'_cert_handle'};
my ($name, $str_name);
if(!($name = Net::SSLeay::X509_get_issuer_name($cert))) {
my $err_str = IO::Socket::SSL::_get_SSL_err_str();
return IO::Socket::SSL::_myerror("X509_get_issuer_name:" .
" '$err_str'.");
}
$str_name = Net::SSLeay::X509_NAME_oneline($name);
return "$str_name";
}
sub DESTROY {
my $self = shift;
my $cert = $self->{'_cert_handle'};
print STDERR "X509_Certificate::DESTROY: '$self', '$cert'.\n"
if $IO::Socket::SSL::DEBUG;
# here we should free resources held by the the certificate.
# include/openssl/x509.h: X509_free(X509 *a);
# NB: Net::SSLeay (v1.05) doesn't define this!
#Net::SSLeay::X509_free($cert);
}
1;
__END__
=head1 NAME
IO::Socket::SSL - a SSL socket interface class
=head1 SYNOPSIS
use IO::Socket::SSL;
=head1 DESCRIPTION
IO::Socket::SSL is a class implementing an object oriented
interface to SSL sockets. The class is a descendent of
IO::Socket::INET and provides a subset of the base class's
interface methods as well as SSL specific methods.
=head1 SUPPORTED INTERFACE
The following methods from the IO::Socket::INET interface are
supported, unimplemented and unsupported respectively:
=over 4
=item supported methods
IO::Socket::INET interface: new, close, fileno, opened, flush,
socket, socketpair, bind, listen, peername, sockname,
timeout, sockopt, sockdomain, socktype, protocol, sockaddr,
sockport, sockhost, peeraddr, peerport, peerhost, sysread,
syswrite, read, write, DESTROY, accept, connect, print, printf;
others: context_init, get_cipher, get_peer_certificate;
=item unimplemented methods
getline, getlines, fdopen, untaint, error, clearerr, send, recv;
=item unsupported methods
getc, eof, truncate, stat, ungetc, setbuf, setvbuf, <$fh>.
=back
=head1 CLASS VARIABLES
=over 4
=item IO::Socket::SSL::DEBUG
=back
=head1 METHODS
=head2 context_init ( [ARGS] )
This class method is used for initializing and setting
the global SSL settings. The following following arguments are
supported:
=over 4
=item SSL_server
This option must be used when a SSL_Context is explicitly created
for server contexts.
=item SSL_use_cert
With server sockets a server certificate is always used. For client
sockets certificate use is optional. This attribute is set to true
if a certificate is to be used.
=item SSL_verify_mode
Type of verification process which is to be performed upon a peer
certificate. This can be a combination of 0x00 (don't verify),
0x01 (verify peer), 0x02 (fail verification if there's no peer
certificate), and 0x04 (verify client once). Default: verify peer.
=item SSL_key_file
Filename of the PEM encoded private key file. Default:
"certs/server-key.pem" or "certs/client-key.pem".
=item SSL_cert_file
Filename of the PEM encoded certificate file. Default:
"certs/server-cert.pem" or "certs/client-cert.pem".
=item SSL_ca_path
Pathname to the Certicate Authority certificate directory. If server
or client certificates are to be verified the trusted CA certificates
have to reside in this directory. The CA certificate filename that is
used for finding the certificate is a hash value generated from the
certificate with a .<serial number> suffix appended to it. The hash
value can be obtained with the command line: ssleay x509 -hash
< ca-cert.pem.
=item SSL_ca_file
Filename of the CA certificate.
=back
=head2 new ( [ARGS] )
See IO::Socket::INET constructor's documentation for
details. The following additional per connection SSL options
are supported:
=over 4
=item SSL_verify_mode
See above.
=item SSL_cipher_list
A list of allowed ciphers. The list is in string form. See
http://www.modssl.org/docs/2.3/ssl_reference.html#ToC9.
=back
=head2 get_cipher
Get a string representation of the used cipher.
=head2 get_peer_certificate
Obtain a reference to the X509_Certificate object representing
peer's certificate.
=head1 RELATED CLASSES
These are internal classes with which the IO::Socket::SSL API
user usually doesn't have to be concerned with.
=head2 SSL_Context
Encapsulates global SSL options.
=head2 METHODS
=over 4
=item new ( [ARGS] )
See context_init arguments.
=item DESTROY
=back
=head2 SSL_SSL
Encapsulates per connection SSL options.
=head2 METHODS
=over 4
=item new ( [ARGS] )
=item DESTROY
=back
=head2 X509_Certificate
Encapsulates X509 certificate information.
=head2 METHODS
=over 4
=item subject_name
Returns a stringified representation of subject's name.
=item issuer_name
Returns a stringified representation of issuer's name.
=back
=head1 EXAMPLES
See demo and t directories.
=head1 RESTRICTIONS
Currently, the IO::Socket::INET interface as implemented by this
package is not quite complete. There can be only one SSL context at
a given time.
=head1 SEE ALSO
IO::Socket::INET.
=head1 ACKNOWLEDGEMENTS
This package has benefited from the work and help of
Gisle Aas and Sampo Kellomäki.
=head1 COPYRIGHT
Copyright 1999, Marko Asplund
This library is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.
=cut
# net resources:
# ==============
# http://www.linpro.no/lwp
# http://search.ietf.org/internet-drafts/draft-ietf-tls-https-02.txt
# http://www.ietf.org/rfc/rfc2246.txt
# http://www.rsa.com/rsalabs/pubs/PKCS
# ftp://ftp.bull.com/pub/OSIdirectory/ITUnov96/X.509
# http://www.ietf.org/rfc/rfc1945.txt
# http://www.ietf.org/rfc/rfc2068.txt
# http://www.fortify.net