--- loncom/lonssl.pm 2004/05/26 11:12:58 1.2 +++ loncom/lonssl.pm 2004/06/17 09:27:38 1.8 @@ -1,5 +1,5 @@ # -# $Id: lonssl.pm,v 1.2 2004/05/26 11:12:58 foxr Exp $ +# $Id: lonssl.pm,v 1.8 2004/06/17 09:27:38 foxr Exp $ # # Copyright Michigan State University Board of Trustees # @@ -23,7 +23,7 @@ # # http://www.lon-capa.org/ # - +package lonssl; # lonssl.pm # This file contains common functions used by lond and lonc when # negotiating the exchange of the session encryption key via an @@ -32,9 +32,65 @@ # use strict; + +# CPAN/Standard modules: + use IO::Socket::INET; use IO::Socket::SSL; +use Fcntl; +use POSIX; + +# Loncapa modules: + +use LONCAPA::Configuration; + +# Global storage: + +my $perlvar; # this refers to the apache perlsetvar + # variable hash. + +my $pathsep = "/"; # We're on unix after all. + + +# Initialization code: + +$perlvar = LONCAPA::Configuration::read_conf('loncapa.conf'); + + +my $lasterror=""; + + +sub LastError { + return $lasterror; +} + +#------------------------------------------------------------------------- +# Name SetFdBlocking - +# Turn blocking mode on on the file handle. This is required for +# SSL key negotiation. +# +# Parameters: +# Handle - Reference to the handle to modify. +# Returns: +# prior flag settings. +# +sub SetFdBlocking { + print STDERR "SetFdBlocking called \n"; + my $Handle = shift; + + + + my $flags = fcntl($Handle, F_GETFL, 0); + if(!$flags) { + print STDERR "SetBLocking fcntl get faild $!\n"; + } + my $newflags = $flags & (~ O_NONBLOCK); # Turn off O_NONBLOCK... + if(!fcntl($Handle, F_SETFL, $newflags)) { + print STDERR "Can't set non block mode $!\n"; + } + return $flags; +} #-------------------------------------------------------------------------- # @@ -53,26 +109,40 @@ use IO::Socket::SSL; # - Reference to an SSL socket on success # - undef on failure. Reason for failure can be interrogated from # IO::Socket::SSL +# Side effects: socket is left in blocking mode!! +# sub PromoteClientSocket { - my $PlaintextSocket = shift; - my $CACert = shift; - my $MyCert = shift; - my $KeyFile = shift; - - # To create the ssl socket we need to duplicate the existing - # socket. Otherwise closing the ssl socket will close the plaintext socket - # too: - - open (DUPLICATE, "+>$PlaintextSocket"); - - my $client = IO::Socket::SSL->new_from_fd(fileno(DUPLICATE), - SSL_user_cert => 1, - SSL_key_file => $KeyFile, - SSL_cert_file => $MyCert, - SSL_ca_fie => $$CACert); - - return $client; # Undef if the client negotiation fails. + my ($PlaintextSocket, + $CACert, + $MyCert, + $KeyFile) = @_; + + + print STDERR "Client promotion using key: $KeyFile, Cert: $MyCert, CA: $CACert\n"; + + # To create the ssl socket we need to duplicate the existing + # socket. Otherwise closing the ssl socket will close the plaintext socket + # too. We also must flip into blocking mode for the duration of the + # ssl negotiation phase.. the caller will have to flip to non block if + # that's what they want + + my $oldflags = SetFdBlocking($PlaintextSocket); + my $dupfno = fcntl($PlaintextSocket, F_DUPFD, 0); + print STDERR "Client promotion got dup = $dupfno\n"; + + + my $client = IO::Socket::SSL->new_from_fd($dupfno, + SSL_user_cert => 1, + SSL_key_file => $KeyFile, + SSL_cert_file => $MyCert, + SSL_ca_fie => $CACert); + + if(!$client) { + $lasterror = IO::Socket::SSL::errstr(); + return undef; + } + return $client; # Undef if the client negotiation fails. } #---------------------------------------------------------------------- @@ -91,27 +161,40 @@ sub PromoteClientSocket { # - Reference to an SSL socket on success # - undef on failure. Reason for failure can be interrogated from # IO::Socket::SSL -sub PromoteServerSocket -{ - my $PlaintextSocket = shift; - my $CACert = shift; - my $MyCert = shift; - my $KeyFile = shift; - - - # To create the ssl socket we need to duplicate the existing - # socket. Otherwise closing the ssl socket will close the plaintext socket - # too: - - open (DUPLICATE, "+>$PlaintextSocket"); - - my $client = IO::Socket::SSL->new_from_fd(fileno(DUPLICATE), - SSL_server => 1, # Server role. - SSL_user_cert => 1, - SSL_key_file => $KeyFile, - SSL_cert_file => $MyCert, - SSL_ca_fie => $$CACert); - return $client; +# Side Effects: +# Socket is left in blocking mode!!! +# +sub PromoteServerSocket { + my ($PlaintextSocket, + $CACert, + $MyCert, + $KeyFile) = @_; + + + + # To create the ssl socket we need to duplicate the existing + # socket. Otherwise closing the ssl socket will close the plaintext socket + # too: + + print STDERR "Server promotion: Key = $KeyFile, Cert $MyCert CA $CACert\n"; + + my $oldflags = SetFdBlocking($PlaintextSocket); + my $dupfno = fcntl($PlaintextSocket, F_DUPFD, 0); + if (!$dupfno) { + print STDERR "dup failed: $!\n"; + } + print STDERR " Fileno = $dupfno\n"; + my $client = IO::Socket::SSL->new_from_fd($dupfno, + SSL_server => 1, # Server role. + SSL_user_cert => 1, + SSL_key_file => $KeyFile, + SSL_cert_file => $MyCert, + SSL_ca_fie => $CACert); + if(!$client) { + $lasterror = IO::Socket::SSL::errstr(); + return undef; + } + return $client; } #------------------------------------------------------------------------- @@ -127,9 +210,107 @@ sub PromoteServerSocket # NONE # sub Close { - my $Socket = shift; + my $Socket = shift; + + $Socket->close(SSL_no_shutdown =>1); # Otherwise the parent socket + # gets torn down. +} +#--------------------------------------------------------------------------- +# +# Name GetPeerCertificate +# Description Inquires about the certificate of the peer of a connection. +# Parameters Name Type Description +# SSLSocket IO::Socket::SSL SSL tunnel socket open on +# the peer. +# Returns +# A two element list. The first element of the list is the name of +# the certificate authority. The second element of the list is the name +# of the owner of the certificate. +sub GetPeerCertificate { + my $SSLSocket = shift; + + my $CertOwner = $SSLSocket->peer_certificate("owner"); + my $CertCA = $SSLSocket->peer_certificate("authority"); + + return ($CertCA, $CertOwner); +} +#---------------------------------------------------------------------------- +# +# Name CertificateFile +# Description Locate the certificate files for this host. +# Returns +# Returns a two element array. The first element contains the name of +# the certificate file for this host. The second element contains the name +# of the certificate file for the CA that granted the certificate. If +# either file cannot be located, returns undef. +# +sub CertificateFile { + + # I need some perl variables from the configuration file for this: + + my $CertificateDir = $perlvar->{lonCertificateDirectory}; + my $CaFilename = $perlvar->{lonnetCertificateAuthority}; + my $CertFilename = $perlvar->{lonnetCertificate}; + + # Ensure the existence of these variables: + + if((!$CertificateDir) || (!$CaFilename) || (!$CertFilename)) { + $lasterror = "Missing info: dir: $CertificateDir CA: $CaFilename " + ."Cert: $CertFilename"; + return undef; + } + + # Build the actual filenames and check for their existence and + # readability. + + my $CaFilename = $CertificateDir.$pathsep.$CaFilename; + my $CertFilename = $CertificateDir.$pathsep.$CertFilename; + + if((! -r $CaFilename) || (! -r $CertFilename)) { + $lasterror = "CA file $CaFilename or Cert File: $CertFilename " + ."not readable"; + return undef; + } + + # Everything works fine!! + + return ($CaFilename, $CertFilename); + +} +#------------------------------------------------------------------------ +# +# Name KeyFile +# Description +# Returns the name of the private key file of the current host. +# Returns +# Returns the name of the key file or undef if the file cannot +# be found. +# +sub KeyFile { - $Socket->close(SSL_no_shutdown =>1); # Otherwise the parent socket - # gets torn down. + # I need some perl variables from the configuration file for this: + + my $CertificateDir = $perlvar->{lonCertificateDirectory}; + my $KeyFilename = $perlvar->{lonnetPrivateKey}; + + # Ensure the variables exist: + + if((!$CertificateDir) || (!$KeyFilename)) { + $lasterror = "Missing parameter dir: $CertificateDir " + ."key: $KeyFilename"; + return undef; + } + + # Build the actual filename and ensure that it not only exists but + # is also readable: + + my $KeyFilename = $CertificateDir.$pathsep.$KeyFilename; + if(! (-r $KeyFilename)) { + $lasterror = "Unreadable key file $KeyFilename"; + return undef; + } + + return $KeyFilename; } +1;