Diff for /loncom/LondConnection.pm between versions 1.46 and 1.60

version 1.46, 2007/04/10 23:15:20 version 1.60, 2018/12/11 12:24:56
Line 40  use LONCAPA::lonlocal; Line 40  use LONCAPA::lonlocal;
 use LONCAPA::lonssl;  use LONCAPA::lonssl;
   
   
   
   
 my $DebugLevel=0;  my $DebugLevel=0;
 my %perlvar;  my %perlvar;
   my %secureconf;
   my %badcerts;
   my %hosttypes;
   my %crlchecked;
 my $InsecureOk;  my $InsecureOk;
   
 #  #
Line 70  sub ReadConfig { Line 72  sub ReadConfig {
     my $perlvarref = read_conf('loncapa.conf');      my $perlvarref = read_conf('loncapa.conf');
     %perlvar    = %{$perlvarref};      %perlvar    = %{$perlvarref};
     $ConfigRead = 1;      $ConfigRead = 1;
       
     $InsecureOk = $perlvar{loncAllowInsecure};      $InsecureOk = $perlvar{loncAllowInsecure};
   
       unless (lonssl::Read_Connect_Config(\%secureconf,\%perlvar) eq 'ok') {
           Debug(1,"Failed to retrieve secureconf hash.\n");
       }
       unless (lonssl::Read_Host_Types(\%hosttypes,\%perlvar) eq 'ok') {
           Debug(1,"Failed to retrieve hosttypes hash.\n");
       }
       %badcerts = ();
       %crlchecked = ();
   }
   
   sub ResetReadConfig {
       $ConfigRead = 0;
 }  }
   
 sub Debug {  sub Debug {
Line 150  host the remote lond is on. This host is Line 165  host the remote lond is on. This host is
   
  port number the remote lond is listening on.   port number the remote lond is listening on.
   
   =item lonid
   
    lonid of the remote lond is listening on.
   
   =item deflonid
   
    default lonhostID of the remote lond is listening on.
   
 =cut  =cut
   
 sub new {  sub new {
     my ($class, $DnsName, $Port, $lonid) = @_;      my ($class, $DnsName, $Port, $lonid, $deflonid) = @_;
   
     if (!$ConfigRead) {      if (!$ConfigRead) {
  ReadConfig();   ReadConfig();
  $ConfigRead = 1;   $ConfigRead = 1;
     }      }
     &Debug(4,$class."::new( ".$DnsName.",".$Port.",".$lonid.")\n");      &Debug(4,$class."::new( ".$DnsName.",".$Port.",".$lonid.",".$deflonid.")\n");
   
       my ($conntype,$gotconninfo,$allowinsecure);
       if ((ref($secureconf{'connto'}) eq 'HASH') &&
           (exists($hosttypes{$lonid}))) {
           $conntype = $secureconf{'connto'}{$hosttypes{$lonid}};
           if ($conntype ne '') {
               if ($conntype ne 'req') {
                   $allowinsecure = 1;
               }
               $gotconninfo = 1;
           }
       }
       unless ($gotconninfo) {
           $allowinsecure = $InsecureOk;
       }
   
     # The host must map to an entry in the hosts table:      # The host must map to an entry in the hosts table:
     #  We connect to the dns host that corresponds to that      #  We connect to the dns host that corresponds to that
Line 173  sub new { Line 211  sub new {
     # Now create the object...      # Now create the object...
     my $self     = { Host               => $DnsName,      my $self     = { Host               => $DnsName,
                      LoncapaHim         => $lonid,                       LoncapaHim         => $lonid,
                        LoncapaDefid       => $deflonid,
                      Port               => $Port,                       Port               => $Port,
                      State              => "Initialized",                       State              => "Initialized",
      AuthenticationMode => "",       AuthenticationMode => "",
        InsecureOK         => $allowinsecure,
                      TransactionRequest => "",                       TransactionRequest => "",
                      TransactionReply   => "",                       TransactionReply   => "",
                      NextRequest        => "",                       NextRequest        => "",
Line 189  sub new { Line 229  sub new {
      LocalKeyFile       => "",       LocalKeyFile       => "",
                      CipherKey          => "",                       CipherKey          => "",
                      LondVersion        => "Unknown",                       LondVersion        => "Unknown",
                      Cipher             => undef};                       Cipher             => undef,
        ClientData         => undef};
     bless($self, $class);      bless($self, $class);
     unless ($self->{Socket} = IO::Socket::INET->new(PeerHost => $self->{Host},      unless ($self->{Socket} = IO::Socket::INET->new(PeerHost => $self->{Host},
        PeerPort => $self->{Port},         PeerPort => $self->{Port},
Line 200  sub new { Line 241  sub new {
  return undef; # Inidicates the socket could not be made.   return undef; # Inidicates the socket could not be made.
     }      }
     my $socket = $self->{Socket}; # For local use only.      my $socket = $self->{Socket}; # For local use only.
       $socket->sockopt(SO_KEEPALIVE, 1); # Turn on keepalive probes when idle.
     #  If we are local, we'll first try local auth mode, otherwise, we'll try      #  If we are local, we'll first try local auth mode, otherwise, we'll try
     #  the ssl auth mode:      #  the ssl auth mode:
   
Line 214  sub new { Line 256  sub new {
  #  allowed...else give up right away.   #  allowed...else give up right away.
   
  if(!(defined $key) || !(defined $keyfile)) {   if(!(defined $key) || !(defined $keyfile)) {
     if($InsecureOk) {              my $canconnect = 0;
               if (ref($secureconf{'connto'}) eq 'HASH') {
                   unless ($secureconf{'connto'}->{'dom'} eq 'req') {
                       $canconnect = 1;
                   }
               } else {
                   $canconnect = $InsecureOk;
               }
       if ($canconnect) {
  $self->{AuthenticationMode} = "insecure";   $self->{AuthenticationMode} = "insecure";
  $self->{TransactionRequest} = "init\n";   $self->{TransactionRequest} = "init\n";
     }       } 
Line 237  sub new { Line 287  sub new {
   
  my ($ca, $cert) = lonssl::CertificateFile;   my ($ca, $cert) = lonssl::CertificateFile;
  my $sslkeyfile  = lonssl::KeyFile;   my $sslkeyfile  = lonssl::KeyFile;
           my $badcertfile = lonssl::has_badcert_file($self->{LoncapaHim});
   
  if((defined $ca)  && (defined $cert) && (defined $sslkeyfile)) {   if (($conntype ne 'no') && (defined($ca)) && (defined($cert)) && (defined($sslkeyfile)) &&
               (!exists($badcerts{$self->{LoncapaHim}})) && !$badcertfile) {
     $self->{AuthenticationMode} = "ssl";      $self->{AuthenticationMode} = "ssl";
     $self->{TransactionRequest} = "init:ssl\n";      $self->{TransactionRequest} = "init:ssl:$perlvar{'lonVersion'}\n";
    } elsif ($self->{InsecureOK}) {
       # Allowed to do insecure:
       $self->{AuthenticationMode} = "insecure";
       $self->{TransactionRequest} = "init::$perlvar{'lonVersion'}\n";
  } else {   } else {
     if($InsecureOk) { # Allowed to do insecure:      # Not allowed to do insecure...
  $self->{AuthenticationMode} = "insecure";      $socket->close;
  $self->{TransactionRequest} = "init\n";      return undef;
     }  
     else { # Not allowed to do insecure...  
  $socket->close;  
  return undef;  
     }  
  }   }
     }      }
   
Line 340  sub Readable { Line 390  sub Readable {
  $self->Transition("Disconnected");   $self->Transition("Disconnected");
  return -1;   return -1;
     }      }
       # If we actually got data, reset the timeout.
   
       if (length $data) {
    $self->{TimeoutRemaining}   = $self->{TimeoutValue}; # getting data resets the timeout period.
       }
     #  Append the data to the buffer.  And figure out if the read is done:      #  Append the data to the buffer.  And figure out if the read is done:
   
     &Debug(9,"Received from host: ".$data);      &Debug(9,"Received from host: ".$data);
Line 392  sub Readable { Line 447  sub Readable {
     }      }
     elsif ($ConnectionMode eq "ssl") {      elsif ($ConnectionMode eq "ssl") {
  if($Response =~ /^ok:ssl/) {     # Good ssl...   if($Response =~ /^ok:ssl/) {     # Good ssl...
     if($self->ExchangeKeysViaSSL()) { # Success skip to vsn stuff      my $sslresult = $self->ExchangeKeysViaSSL();
                       if ($sslresult == 1) { # Success skip to vsn stuff
  # Need to reset to non blocking:   # Need to reset to non blocking:
   
  my $flags = fcntl($socket, F_GETFL, 0);   my $flags = fcntl($socket, F_GETFL, 0);
  fcntl($socket, F_SETFL, $flags | O_NONBLOCK);   fcntl($socket, F_SETFL, $flags | O_NONBLOCK);
  $self->ToVersionRequest();   $self->ToVersionRequest();
  return 0;   return 0;
     }      } 
     else {         # Failed in ssl exchange.      else { # Failed in ssl exchange.
           if (($sslresult == -1) && (lonssl::LastError == -1) && ($self->{InsecureOK})) {
                               my $badcertdir = &lonssl::BadCertDir();
                               if (($badcertdir) && $self->{LoncapaHim}) {
                                   if (open(my $fh,'>',"$badcertdir/".$self->{LoncapaHim})) {
                                       close($fh);
                                   }
                               }
       $badcerts{$self->{LoncapaHim}} = 1;
                               &Debug(3,"SSL verification failed: close socket and initiate insecure connection");
                               $self->Transition("ReInitNoSSL");
                               $socket->close;
                               return -1;
    }
  &Debug(3,"init:ssl failed key negotiation!");   &Debug(3,"init:ssl failed key negotiation!");
  $self->Transition("Disconnected");   $self->Transition("Disconnected");
  $socket->close;   $socket->close;
  return -1;   return -1;
     }                      }
  }    } 
  elsif ($Response =~ /^[0-9]+/) { # Old style lond.   elsif ($Response =~ /^[0-9]+/) { # Old style lond.
     return $self->CompleteInsecure();      return $self->CompleteInsecure();
Line 470  sub Readable { Line 539  sub Readable {
     }      }
  } elsif ($self->{State}  eq "ReceivingKey") {   } elsif ($self->{State}  eq "ReceivingKey") {
     my $buildkey = $self->{TransactionReply};      my $buildkey = $self->{TransactionReply};
               chomp($buildkey);
     my $key = $self->{LoncapaHim}.$perlvar{'lonHostID'};      my $key = $self->{LoncapaHim}.$perlvar{'lonHostID'};
     $key=~tr/a-z/A-Z/;      $key=~tr/a-z/A-Z/;
     $key=~tr/G-P/0-9/;      $key=~tr/G-P/0-9/;
Line 501  sub Readable { Line 571  sub Readable {
  $self->{InformWritable}     = 1;   $self->{InformWritable}     = 1;
  $self->{InformReadable}     = 0;   $self->{InformReadable}     = 0;
  $self->{Timeoutable}        = 1;   $self->{Timeoutable}        = 1;
  $self->{TimeoutRemaining}   = $self->{TimeoutValue};  
  $self->Transition("SendingRequest");   $self->Transition("SendingRequest");
  return 0;   return 0;
     } else {      } else {
Line 563  sub Writable { Line 632  sub Writable {
  ($errno == POSIX::EAGAIN)         ||   ($errno == POSIX::EAGAIN)         ||
  ($errno == POSIX::EINTR)          ||   ($errno == POSIX::EINTR)          ||
  ($errno ==  0)) {   ($errno ==  0)) {
    $self->{TimeoutRemaining} = $self->{TimeoutValue};
  substr($self->{TransactionRequest}, 0, $nwritten) = ""; # rmv written part   substr($self->{TransactionRequest}, 0, $nwritten) = ""; # rmv written part
       if(length $self->{TransactionRequest} == 0) {   if(length $self->{TransactionRequest} == 0) {
          $self->{InformWritable} = 0;      $self->{InformWritable} = 0;
          $self->{InformReadable} = 1;      $self->{InformReadable} = 1;
          $self->{TransactionReply} = '';      $self->{TransactionReply} = '';
          #      #
          # Figure out the next state:      # Figure out the next state:
          #      #
          if($self->{State} eq "Connected") {      if($self->{State} eq "Connected") {
             $self->Transition("Initialized");   $self->Transition("Initialized");
          } elsif($self->{State} eq "ChallengeReceived") {      } elsif($self->{State} eq "ChallengeReceived") {
             $self->Transition("ChallengeReplied");   $self->Transition("ChallengeReplied");
          } elsif($self->{State} eq "RequestingVersion") {      } elsif($self->{State} eq "RequestingVersion") {
             $self->Transition("ReadingVersionString");   $self->Transition("ReadingVersionString");
          } elsif ($self->{State} eq "SetHost") {      } elsif ($self->{State} eq "SetHost") {
             $self->Transition("HostSet");   $self->Transition("HostSet");
          } elsif($self->{State} eq "RequestingKey") {      } elsif($self->{State} eq "RequestingKey") {
             $self->Transition("ReceivingKey");   $self->Transition("ReceivingKey");
 #            $self->{InformWritable} = 0;  #            $self->{InformWritable} = 0;
 #            $self->{InformReadable} = 1;  #            $self->{InformReadable} = 1;
 #            $self->{TransactionReply} = '';  #            $self->{TransactionReply} = '';
          } elsif ($self->{State} eq "SendingRequest") {      } elsif ($self->{State} eq "SendingRequest") {
             $self->Transition("ReceivingReply");   $self->Transition("ReceivingReply");
             $self->{TimeoutRemaining} = $self->{TimeoutValue};   $self->{TimeoutRemaining} = $self->{TimeoutValue};
          } elsif ($self->{State} eq "Disconnected") {      } elsif ($self->{State} eq "Disconnected") {
             return -1;   return -1;
          }      }
          return 0;      return 0;
       }   }
    } else { # The write failed (e.g. partner disconnected).      } else { # The write failed (e.g. partner disconnected).
       $self->Transition("Disconnected");   $self->Transition("Disconnected");
       $socket->close();   $socket->close();
       return -1;   return -1;
    }      }
       
 }  }
 =pod  =pod
   
Line 744  sub Shutdown { Line 814  sub Shutdown {
     $socket->shutdown(2);      $socket->shutdown(2);
  }   }
     }      }
       $self->{Timeoutable}   = 0; # Shutdown sockets can't timeout.
 }  }
   
 =pod  =pod
Line 994  sub CreateCipher { Line 1065  sub CreateCipher {
 sub ExchangeKeysViaSSL {  sub ExchangeKeysViaSSL {
     my $self   = shift;      my $self   = shift;
     my $socket = $self->{Socket};      my $socket = $self->{Socket};
       my $peer = $self->{LoncapaHim};
       my $peerdef = $self->{LoncapaDefid};
   
     #  Get our signed certificate, the certificate authority's       #  Get our signed certificate, the certificate authority's 
     #  certificate and our private key file.  All of these      #  certificate and our private key file.  All of these
Line 1002  sub ExchangeKeysViaSSL { Line 1075  sub ExchangeKeysViaSSL {
     my ($SSLCACertificate,      my ($SSLCACertificate,
  $SSLCertificate) = lonssl::CertificateFile();   $SSLCertificate) = lonssl::CertificateFile();
     my $SSLKey             = lonssl::KeyFile();      my $SSLKey             = lonssl::KeyFile();
       my $CRLFile;
       unless ($crlchecked{$peerdef}) {
           $CRLFile = lonssl::CRLFile();
           $crlchecked{$peerdef} = 1;
       }
     #  Promote our connection to ssl and read the key from lond.      #  Promote our connection to ssl and read the key from lond.
   
     my $SSLSocket = lonssl::PromoteClientSocket($socket,      my $SSLSocket = lonssl::PromoteClientSocket($socket,
  $SSLCACertificate,   $SSLCACertificate,
  $SSLCertificate,   $SSLCertificate,
  $SSLKey);   $SSLKey,
                                                   $peer,
                                                   $peerdef,
                                                   $CRLFile);
     if(defined $SSLSocket) {      if(defined $SSLSocket) {
  my $key  = <$SSLSocket>;   my $key  = <$SSLSocket>;
  lonssl::Close($SSLSocket);   lonssl::Close($SSLSocket);
Line 1024  sub ExchangeKeysViaSSL { Line 1104  sub ExchangeKeysViaSSL {
     else {      else {
  # Failed!!   # Failed!!
  Debug(3, "Failed to negotiate SSL connection!");   Debug(3, "Failed to negotiate SSL connection!");
  return 0;   return -1;
     }      }
     # should not get here      # should not get here
     return 0;      return 0;
Line 1049  sub ExchangeKeysViaSSL { Line 1129  sub ExchangeKeysViaSSL {
 #  #
 sub CompleteInsecure {  sub CompleteInsecure {
     my $self = shift;      my $self = shift;
     if($InsecureOk) {      if ($self->{InsecureOK}) {
  $self->{AuthenticationMode} = "insecure";   $self->{AuthenticationMode} = "insecure";
  &Debug(8," Transition out of Initialized:insecure");   &Debug(8," Transition out of Initialized:insecure");
  $self->{TransactionRequest} = $self->{TransactionReply};   $self->{TransactionRequest} = $self->{TransactionReply};
Line 1137  sub PeerVersion { Line 1217  sub PeerVersion {
    return $version;     return $version;
 }  }
   
   #
   #  Manipulate the client data field
   #
   sub SetClientData {
       my ($self, $newData) = @_;
       $self->{ClientData} = $newData;
   }
   #
   #  Get the current client data field.
   #
   sub GetClientData {
       my $self = shift;
       return $self->{ClientData};
   }
   
   #
   # Get the HostID of our peer 
   #
   
   sub PeerLoncapaHim {
       my $self = shift;
       return $self->{LoncapaHim};
   }
   
   #
   # Get the Authentication mode
   #
   
   sub GetKeyMode {
       my $self = shift;
       return $self->{AuthenticationMode};
   }
   
 1;  1;
   
 =pod  =pod

Removed from v.1.46  
changed lines
  Added in v.1.60


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>