[LON-CAPA-cvs] cvs: doc /loncapafiles loncapafiles.lpml loncom LondConnection.pm loncnew loncron lond lonssl.pm loncom/init.d loncontrol loncom/lonnet/perl lonnet.pm

raeburn raeburn at source.lon-capa.org
Tue Aug 7 13:12:33 EDT 2018


raeburn		Tue Aug  7 17:12:33 2018 EDT

  Modified files:              
    /loncom	lonssl.pm LondConnection.pm loncnew lond loncron 
    /loncom/init.d	loncontrol 
    /loncom/lonnet/perl	lonnet.pm 
    /doc/loncapafiles	loncapafiles.lpml 
  Log:
  - Support Certificate Revocation List checking when using SSL channel
    for key exchange during negotiation of connection to remote lond.
  
  
-------------- next part --------------
Index: loncom/lonssl.pm
diff -u loncom/lonssl.pm:1.16 loncom/lonssl.pm:1.17
--- loncom/lonssl.pm:1.16	Sun Jul 29 03:03:36 2018
+++ loncom/lonssl.pm	Tue Aug  7 17:12:08 2018
@@ -1,5 +1,5 @@
 #
-# $Id: lonssl.pm,v 1.16 2018/07/29 03:03:36 raeburn Exp $
+# $Id: lonssl.pm,v 1.17 2018/08/07 17:12:08 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -116,7 +116,11 @@
 #                                          issued to this host.
 #                KeyFile string    	   Full pathname to the host's private 
 #                                          key file for the certificate.
-#               peer    string             lonHostID of remote LON-CAPA server 
+#               peer    string             lonHostID of remote LON-CAPA server
+#               CRLFile                    Full path name to the certificate
+#                                          revocation list file for the cluster
+#                                          to which server belongs (optional)
+
 # Returns
 #	-	Reference to an SSL socket on success
 #       -	undef on failure.  Reason for failure can be interrogated from 
@@ -129,10 +133,11 @@
 	$CACert,
 	$MyCert,
 	$KeyFile,
-        $peer)          = @_;
+        $peer,
+        $CRLFile)          = @_;
     
     
-    Debug("Client promotion using key: $KeyFile, Cert: $MyCert, CA: $CACert, Remote Host: $peer\n");
+    Debug("Client promotion using key: $KeyFile, Cert: $MyCert, CA: $CACert, CRL: $CRLFile, Remote Host: $peer\n");
 
     # To create the ssl socket we need to duplicate the existing
     # socket.  Otherwise closing the ssl socket will close the plaintext socket
@@ -150,21 +155,26 @@
     # Starting with rev. 1.95, the default became SSL_VERIFY_PEER which
     # prevents an SSL connection to lond unless SSL_verifycn_name is set
     # to the lonHostID of the remote host, (and the remote certificate has
-    # the remote lonHostID as CN, and has been signed by the LON-CAPA CA. 
+    # the remote lonHostID as CN, and has been signed by the LON-CAPA CA.
     # Set SSL_verify_mode to Net::SSLeay::VERIFY_PEER() instead of to
     # SSL_VERIFY_PEER for compatibility with IO::Socket::SSL rev. 1.01
     # used by CentOS/RHEL/Scientific Linux 5).
     
-    my $client = IO::Socket::SSL->new_from_fd($dupfno,
-					      SSL_use_cert => 1,
-					      SSL_key_file  => $KeyFile,
-					      SSL_cert_file => $MyCert,
-					      SSL_ca_file   => $CACert,
-					      SSL_verifycn_name => $peer,
-					      SSL_verify_mode => Net::SSLeay::VERIFY_PEER());
-    
+    my %sslargs = (SSL_use_cert      => 1,
+                   SSL_key_file      => $KeyFile,
+                   SSL_cert_file     => $MyCert,
+                   SSL_ca_file       => $CACert,
+                   SSL_verifycn_name => $peer,
+                   SSL_verify_mode   => Net::SSLeay::VERIFY_PEER());
+    if (($CRLFile ne '') && (-e $CRLFile)) {
+        $sslargs{SSL_check_crl} = 1;
+        $sslargs{SSL_crl_file} = $CRLFile;
+    }
+    my $client = IO::Socket::SSL->new_from_fd($dupfno,%sslargs);
     if(!$client) {
-	$lasterror = IO::Socket::SSL::errstr();
+        if ($IO::Socket::SSL::SSL_ERROR == -1) {
+	    $lasterror = -1;
+        }
 	return undef;
     }
     return $client;		# Undef if the client negotiation fails.
@@ -182,7 +192,10 @@
 #                                          issued to this host.
 #                KeyFile string    	   Full pathname to the host's private 
 #                                          key file for the certificate.
-#                peer   string             lonHostID of remote LON-CAPA client
+#               peer   string              lonHostID of remote LON-CAPA client
+#               CRLFile                    Full path name to the certificate
+#                                          revocation list file for the cluster
+#                                          to which server belongs (optional)
 # Returns
 #	-	Reference to an SSL socket on success
 #       -	undef on failure.  Reason for failure can be interrogated from 
@@ -195,7 +208,8 @@
 	$CACert,
 	$MyCert,
 	$KeyFile,
-        $peer)          = @_;
+        $peer,
+        $CRLFile)          = @_;
 
 
 
@@ -211,16 +225,22 @@
 	Debug("dup failed: $!\n");
     }
     Debug(" Fileno = $dupfno\n");
-    my $client = IO::Socket::SSL->new_from_fd($dupfno,
-					      SSL_server    => 1, # Server role.
-					      SSL_use_cert  => 1,
-					      SSL_key_file  => $KeyFile,
-					      SSL_cert_file => $MyCert,
-					      SSL_ca_file   => $CACert,
-					      SSL_verifycn_name => $peer,
-					      SSL_verify_mode => Net::SSLeay::VERIFY_PEER());
+    my %sslargs = (SSL_server        => 1, # Server role.
+                   SSL_use_cert      => 1,
+                   SSL_key_file      => $KeyFile,
+                   SSL_cert_file     => $MyCert,
+                   SSL_ca_file       => $CACert,
+                   SSL_verifycn_name => $peer,
+                   SSL_verify_mode   => Net::SSLeay::VERIFY_PEER()); 
+    if (($CRLFile ne '') && (-e $CRLFile)) {
+        $sslargs{SSL_check_crl} = 1;
+        $sslargs{SSL_crl_file} = $CRLFile; 
+    }
+    my $client = IO::Socket::SSL->new_from_fd($dupfno,%sslargs);
     if(!$client) {
-	$lasterror = IO::Socket::SSL::errstr();
+        if ($IO::Socket::SSL::SSL_ERROR == -1) {
+            $lasterror = -1;
+        }
 	return undef;
     }
     return $client;
@@ -342,14 +362,61 @@
     return $KeyFilename;
 }
 
+sub CRLFile {
+
+    # I need some perl variables from the configuration file for this:
+
+    my $CertificateDir   = $perlvar->{lonCertificateDirectory};
+    my $CRLFilename      = $perlvar->{lonnetCertRevocationList};
+
+    # Ensure the variables exist:
+
+    if((!$CertificateDir) || (!$CRLFilename)) {
+        $lasterror = "Missing parameter dir: $CertificateDir "
+                    ."CRL file: $CRLFilename";
+        return undef;
+    }
+
+    # Build the actual filename and ensure that it not only exists but
+    # is also readable:
+
+    $CRLFilename    = $CertificateDir.$pathsep.$CRLFilename;
+    if(! (-r $CRLFilename)) {
+        $lasterror = "Unreadable key file $CRLFilename";
+        return undef;
+    }
+
+    return $CRLFilename;
+}
+
+sub BadCertDir {
+    my $SocketDir = $perlvar->{lonSockDir};
+    if (-d "$SocketDir/nosslverify/") {
+        return "$SocketDir/nosslverify"
+    }
+}
+
+sub has_badcert_file {
+    my ($client) = @_;
+    my $SocketDir = $perlvar->{lonSockDir};
+    if (-e "$SocketDir/nosslverify/$client") {
+        return 1;
+    }
+    return;
+}
+
 sub Read_Connect_Config {
-    my ($secureconf,$perlvarref) = @_;
-    return unless (ref($secureconf) eq 'HASH');
+    my ($secureconf,$checkedcrl,$perlvarref) = @_;
+    return unless ((ref($secureconf) eq 'HASH') && (ref($checkedcrl) eq 'HASH'));
 
     unless (ref($perlvarref) eq 'HASH') {
         $perlvarref = $perlvar;
     }
-    
+
+    # Clear hash of clients for which Certificate Revocation List checked 
+    foreach my $key (keys(%{$checkedcrl})) {
+        delete($checkedcrl->{$key});
+    }
     # Clean out the old table first.
     foreach my $key (keys(%{$secureconf})) {
         delete($secureconf->{$key});
Index: loncom/LondConnection.pm
diff -u loncom/LondConnection.pm:1.56 loncom/LondConnection.pm:1.57
--- loncom/LondConnection.pm:1.56	Sun Jul 29 03:03:36 2018
+++ loncom/LondConnection.pm	Tue Aug  7 17:12:09 2018
@@ -1,7 +1,7 @@
 #   This module defines and implements a class that represents
 #   a connection to a lond daemon.
 #
-# $Id: LondConnection.pm,v 1.56 2018/07/29 03:03:36 raeburn Exp $
+# $Id: LondConnection.pm,v 1.57 2018/08/07 17:12:09 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -44,7 +44,8 @@
 my %perlvar;
 my %secureconf;
 my %badcerts;
-my %hosttypes; 
+my %hosttypes;
+my %crlchecked;
 my $InsecureOk;
 
 #
@@ -80,7 +81,8 @@
     unless (lonssl::Read_Host_Types(\%hosttypes,\%perlvar) eq 'ok') {
         Debug(1,"Failed to retrieve hosttypes hash.\n");
     }
-    undef(%badcerts);
+    %badcerts = ();
+    %crlchecked = ();
 }
 
 sub ResetReadConfig {
@@ -204,7 +206,7 @@
                      Port               => $Port,
                      State              => "Initialized",
 		     AuthenticationMode => "",
-		     InsecureOK         => $allowinsecure,                     
+		     InsecureOK         => $allowinsecure,
                      TransactionRequest => "",
                      TransactionReply   => "",
                      NextRequest        => "",
@@ -276,9 +278,10 @@
 
 	my ($ca, $cert) = lonssl::CertificateFile;
 	my $sslkeyfile  = lonssl::KeyFile;
+        my $badcertfile = lonssl::has_badcert_file($self->{LoncapaHim});
 
 	if (($conntype ne 'no') && (defined($ca)) && (defined($cert)) && (defined($sslkeyfile)) &&
-            (!exists($badcerts{$self->{LoncapaHim}}))) {
+            (!exists($badcerts{$self->{LoncapaHim}})) && !$badcertfile) {
 	    $self->{AuthenticationMode} = "ssl";
 	    $self->{TransactionRequest} = "init:ssl:$perlvar{'lonVersion'}\n";
 	} elsif ($self->{InsecureOK}) {
@@ -443,16 +446,26 @@
 			fcntl($socket, F_SETFL, $flags | O_NONBLOCK);
 			$self->ToVersionRequest();
 			return 0;
-		    }
-		    else {	         # Failed in ssl exchange.
-		        if (($sslresult == -1) && ($self->{InsecureOK})) {
+		    } 
+		    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!");
 			$self->Transition("Disconnected");
 			$socket->close;
 			return -1;
-		    }
+                    }
 		} 
 		elsif ($Response =~ /^[0-9]+/) { # Old style lond.
 		    return $self->CompleteInsecure();
@@ -1051,14 +1064,19 @@
     my ($SSLCACertificate,
 	$SSLCertificate) = lonssl::CertificateFile();
     my $SSLKey             = lonssl::KeyFile();
-
+    my $CRLFile;
+    unless ($crlchecked{$peer}) {
+        $CRLFile = lonssl::CRLFile();
+        $crlchecked{$peer} = 1;
+    }
     #  Promote our connection to ssl and read the key from lond.
 
     my $SSLSocket = lonssl::PromoteClientSocket($socket,
 						$SSLCACertificate,
 						$SSLCertificate,
 						$SSLKey,
-                                                $peer);
+                                                $peer,
+                                                $CRLFile);
     if(defined $SSLSocket) {
 	my $key  = <$SSLSocket>;
 	lonssl::Close($SSLSocket);
@@ -1211,6 +1229,15 @@
     return $self->{LoncapaHim};
 }
 
+#
+# Get the Authentication mode
+#
+
+sub GetKeyMode {
+    my $self = shift;
+    return $self->{AuthenticationMode};
+}
+
 1;
 
 =pod
Index: loncom/loncnew
diff -u loncom/loncnew:1.104 loncom/loncnew:1.105
--- loncom/loncnew:1.104	Sun Jun 24 18:45:18 2018
+++ loncom/loncnew	Tue Aug  7 17:12:09 2018
@@ -2,7 +2,7 @@
 # The LearningOnline Network with CAPA
 # lonc maintains the connections to remote computers
 #
-# $Id: loncnew,v 1.104 2018/06/24 18:45:18 raeburn Exp $
+# $Id: loncnew,v 1.105 2018/08/07 17:12:09 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -74,6 +74,7 @@
 
 my %ChildPid;			# by pid -> host.
 my %ChildHost;			# by host.
+my %ChildKeyMode;               # by pid -> keymode
 my %listening_to;		# Socket->host table for who the parent
                                 # is listening to.
 my %parent_dispatchers;         # host-> listener watcher events. 
@@ -741,7 +742,7 @@
  
   The socket to kill off.
 
-=item Restart
+=item restart
 
 non-zero if we are allowed to create a new connection.
 
@@ -749,6 +750,7 @@
 
 sub KillSocket {
     my $Socket = shift;
+    my $restart = shift;
 
     Log("WARNING", "Shutting down a socket");
     $Socket->Shutdown();
@@ -765,16 +767,24 @@
     if(exists($ActiveConnections{$Socket})) {
 	$ActiveConnections{$Socket}->cancel;
 	delete($ActiveConnections{$Socket});
-	$ConnectionCount--;
+        # Decrement ConnectionCount unless we will immediately
+        # re-connect (i.e., $restart is true), because this was
+        # a connection where the SSL channel for exchange of the
+        # shared key failed, and we may use an insecure channel.
+        unless ($restart) {
+	    $ConnectionCount--;
+        }
 	if ($ConnectionCount < 0) { $ConnectionCount = 0; }
     }
     #  If the connection count has gone to zero and there is work in the
     #  work queue, the work all gets failed with con_lost.
     #
+  
     if($ConnectionCount == 0) {
 	$LondConnecting = 0;	# No connections so also not connecting.
 	EmptyQueue();
-	CloseAllLondConnections; # Should all already be closed but...
+	CloseAllLondConnections(); # Should all already be closed but...
+        &clear_childpid($$);
     }
     UpdateStatus();
 }
@@ -871,17 +881,33 @@
 
 	Log("WARNING",
 	    "Lond connection lost.");
+        my $state_on_exit = $Socket->GetState();
 	if(exists($ActiveTransactions{$Socket})) {
 	    FailTransaction($ActiveTransactions{$Socket});
 	} else {
 	    #  Socket is connecting and failed... need to mark
 	    #  no longer connecting.
-	   
 	    $LondConnecting = 0;
 	}
 	$Watcher->cancel();
-	KillSocket($Socket);
-	$ConnectionRetriesLeft--;       # Counts as connection failure
+        if ($state_on_exit eq 'ReInitNoSSL') {
+            # SSL certificate verification failed, and insecure connection
+            # allowed. Send restart arg to KillSocket(), so EmptyQueue() 
+            # is not called, as we still hope to process queued request.
+
+            KillSocket($Socket,1);
+
+            # Re-initiate creation of Lond Connection for use with queued
+            # request.
+
+            ShowStatus("Connected to ".$RemoteHost);
+            Log("WARNING","No SSL channel (verification failed), will try with insecure channel");
+            &MakeLondConnection(1);
+
+        } else {
+	    KillSocket($Socket);
+	    $ConnectionRetriesLeft--;       # Counts as connection failure         
+        }
 	return;
     }
     SocketDump(6,$Socket);
@@ -892,6 +918,8 @@
     if($State eq "Initialized") {
 
 
+    } elsif ($State eq "ReInitNoSSL") {
+
     } elsif ($State eq "ChallengeReceived") {
 	#  The challenge must be echoed back;  The state machine
 	# in the connection takes care of setting that up.  Just
@@ -928,7 +956,14 @@
     } elsif ($State eq "ReceivingKey") {
 
     } elsif ($State eq "Idle") {
-   
+
+        if ($ConnectionCount == 1) { 
+            # Write child Pid file to keep track of ssl and insecure
+            # connections
+
+            &record_childpid($Socket);
+        }
+
 	# This is as good a spot as any to get the peer version
 	# string:
    
@@ -1093,7 +1128,9 @@
 
 	$Watcher->cb(\&LondReadable);
 	$Watcher->poll("r");
-	
+
+    } elsif ($State eq "ReInitNoSSL") {
+
     } elsif ($State eq "ChallengeReceived") {
 	# We received the challenge, now we 
 	# are echoing it back. This is a no-op,
@@ -1204,7 +1241,8 @@
 
 =cut
 
-sub MakeLondConnection {     
+sub MakeLondConnection {
+    my ($restart) = @_;
     Debug(4,"MakeLondConnection to ".GetServerHost()." on port "
 	  .GetServerPort());
 
@@ -1212,12 +1250,11 @@
 					 &GetServerPort(),
 					 &GetHostId());
 
-    if($Connection eq undef) {	
+    if($Connection eq undef) {
 	Log("CRITICAL","Failed to make a connection with lond.");
 	$ConnectionRetriesLeft--;
 	return 0;		# Failure.
     }  else {
-
 	$LondConnecting = 1;	# Connection in progress.
 	# The connection needs to have writability 
 	# monitored in order to send the init sequence
@@ -1242,7 +1279,9 @@
 	if ($ConnectionCount == 0) {
 	    &SetupTimer;	# Need to handle timeouts with connections...
 	}
-	$ConnectionCount++;
+        unless ($restart) {
+	    $ConnectionCount++;
+        }
 	$Connection->SetClientData($ConnectionCount);
 	Debug(4, "Connection count = ".$ConnectionCount);
 	if($ConnectionCount == 1) { # First Connection:
@@ -1652,6 +1691,7 @@
 	."died through "."\"$signal\"");
     #LogPerm("F:lonc: $$ on $RemoteHost signalled to death: "
 #	    ."\"$signal\"");
+    &clear_childpid($$);
     exit 0;
 
 }
@@ -1972,6 +2012,7 @@
 	my $host = $ChildPid{$pid};
 	if($host) {		# It's for real...
 	    &Debug(9, "Caught sigchild for $host");
+            &clear_childpid($pid);
 	    delete($ChildPid{$pid});
 	    delete($ChildHost{$host});
 	    &parent_clean_up($host);
@@ -2143,6 +2184,7 @@
 
     &KillThemAll();
     LondConnection->ResetReadConfig();
+    ShowStatus('Parent keeping the flock');
 }
 
 
@@ -2181,12 +2223,14 @@
     # Our children >will< die.
     # but we need to catch their death and cleanup after them in case this is 
     # a restart set of kills
+    my $execdir = $perlvar{'lonDaemons'};
     my @allpids = keys(%ChildPid);
     foreach my $pid (@allpids) {
 	my $serving = $ChildPid{$pid};
 	ShowStatus("Nicely Killing lonc for $serving pid = $pid");
 	Log("CRITICAL", "Nicely Killing lonc for $serving pid = $pid");
 	kill 'QUIT' => $pid;
+        &clear_childpid($pid);
     }
     ShowStatus("Finished killing child processes off.");
 }
@@ -2206,6 +2250,7 @@
 	Log("CRITICAL", "Nastily killing lonc for $serving pid = $pid");
 	kill 'KILL' => $pid;
 	delete($ChildPid{$pid});
+        delete($ChildKeyMode{$pid});
 	my $execdir = $perlvar{'lonDaemons'};
 	unlink("$execdir/logs/lonc.pid");
     }
@@ -2234,6 +2279,10 @@
 
 }
 
+=pod
+
+=cut
+
 sub my_hostname {
     use Sys::Hostname::FQDN();
     my $name = Sys::Hostname::FQDN::fqdn();
@@ -2241,6 +2290,49 @@
     return $name;
 }
 
+sub record_childpid {
+    my ($Socket) = @_;
+    my $docdir = $perlvar{'lonDocRoot'};
+    my $authmode = $Socket->GetKeyMode();
+    my $peer = $Socket->PeerLoncapaHim();
+    if (($authmode eq 'ssl') || ($authmode eq 'insecure')) {
+        my $childpid = $$;
+        if ($childpid) {
+            unless (exists($ChildKeyMode{$childpid})) {
+                $ChildKeyMode{$childpid} = $authmode;
+            }
+            if (-d "$docdir/lon-status/loncchld") {
+                unless (-e "$docdir/lon-status/loncchld/$childpid") {
+                    if (open (my $pidfh,'>',"$docdir/lon-status/loncchld/$childpid")) {
+                        print $pidfh "$peer:$authmode\n";
+                        close($pidfh);
+                    }
+                }
+            }
+        }
+    }
+    return;
+}
+
+sub clear_childpid {
+    my ($childpid) = @_; 
+    my $docdir = $perlvar{'lonDocRoot'};
+    if (-d "$docdir/lon-status/loncchld") {
+        if ($childpid =~ /^\d+$/) {
+            if (($ChildKeyMode{$childpid} eq 'insecure') ||
+                ($ChildKeyMode{$childpid} eq 'ssl')) {
+                if (-e "$docdir/lon-status/loncchld/$childpid") {
+                    unlink("$docdir/lon-status/loncchld/$childpid");
+                }
+            }
+        }
+    }
+    if (exists($ChildKeyMode{$childpid})) {
+        delete($ChildKeyMode{$childpid});
+    }
+    return;
+}
+
 =pod
 
 =head1 Theory
@@ -2345,6 +2437,12 @@
 
  "WARNING Failing transaction..." msgs for each in-flight or queued transaction.
 
+=item WARNING No SSL channel (verification failed), will try with insecure channel.
+
+Called when promotion of a socket to SSL failed because SSL certificate verification failed.
+Domain configuration must also permit insecure channel use for key exchange. Connection
+negotiation will start again from the beginning, but with Authentication Mode not set to ssl.
+
 =item INFO Connected to lond version:  <version> 
 
 When connection negotiation is complete, the lond version is requested and logged here.
Index: loncom/lond
diff -u loncom/lond:1.544 loncom/lond:1.545
--- loncom/lond:1.544	Sun Jul 29 03:03:36 2018
+++ loncom/lond	Tue Aug  7 17:12:09 2018
@@ -2,7 +2,7 @@
 # The LearningOnline Network
 # lond "LON Daemon" Server (port "LOND" 5663)
 #
-# $Id: lond,v 1.544 2018/07/29 03:03:36 raeburn Exp $
+# $Id: lond,v 1.545 2018/08/07 17:12:09 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -65,7 +65,7 @@
 my $status='';
 my $lastlog='';
 
-my $VERSION='$Revision: 1.544 $'; #' stupid emacs
+my $VERSION='$Revision: 1.545 $'; #' stupid emacs
 my $remoteVERSION;
 my $currenthostid="default";
 my $currentdomainid;
@@ -108,6 +108,10 @@
 my %secureconf;                 # Will have requirements for security 
                                 # of lond connections
 
+my %crlchecked;                 # Will contain clients for which the client's SSL
+                                # has been checked against the cluster's Certificate
+                                # Revocation List.
+
 my $dist;
 
 #
@@ -420,11 +424,18 @@
     Debug("Approving promotion -> ssl");
     #  And do so:
 
+    my $CRLFile;
+    unless ($crlchecked{$clientname}) {
+        $CRLFile = lonssl::CRLFile();
+        $crlchecked{$clientname} = 1;
+    }
+
     my $SSLSocket = lonssl::PromoteServerSocket($Socket,
 						$CACertificate,
 						$Certificate,
 						$KeyFile,
-						$clientname);
+						$clientname,
+                                                $CRLFile);
     if(! ($SSLSocket) ) {	# SSL socket promotion failed.
 	my $err = lonssl::LastError();
 	&logthis("<font color=\"red\"> CRITICAL "
@@ -6993,10 +7004,10 @@
 
     my %oldconf = %secureconf;
     my %connchange;
-    if (lonssl::Read_Connect_Config(\%secureconf,\%perlvar) eq 'ok') {
-        logthis('<font color="blue"> Reloaded SSL connection rules </font>');
+    if (lonssl::Read_Connect_Config(\%secureconf,\%crlchecked,\%perlvar) eq 'ok') {
+        logthis('<font color="blue"> Reloaded SSL connection rules and cleared CRL checking history </font>');
     } else {
-        logthis('<font color="yellow"> Failed to reload SSL connection rules </font>');
+        logthis('<font color="yellow"> Failed to reload SSL connection rules and clear CRL checking history </font>');
     }
     if ((ref($oldconf{'connfrom'}) eq 'HASH') && (ref($secureconf{'connfrom'}) eq 'HASH')) {
         foreach my $type ('dom','intdom','other') {
@@ -7275,7 +7286,7 @@
     chomp($arch);
 }
 
-unless (lonssl::Read_Connect_Config(\%secureconf,\%perlvar) eq 'ok') {
+unless (lonssl::Read_Connect_Config(\%secureconf,\%crlchecked,\%perlvar) eq 'ok') {
     &logthis('<font color="blue">No connectionrules table. Will fallback to loncapa.conf</font>');
 }
 
Index: loncom/loncron
diff -u loncom/loncron:1.106 loncom/loncron:1.107
--- loncom/loncron:1.106	Wed Jul 18 13:44:55 2018
+++ loncom/loncron	Tue Aug  7 17:12:09 2018
@@ -2,7 +2,7 @@
 
 # Housekeeping program, started by cron, loncontrol and loncron.pl
 #
-# $Id: loncron,v 1.106 2018/07/18 13:44:55 raeburn Exp $
+# $Id: loncron,v 1.107 2018/08/07 17:12:09 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -165,6 +165,9 @@
 	&log($fh,unlink($pidfile).' - '.
 	    `killall -9 $kadaemon 2>&1`.
 	    '</font><br />');
+        if ($kadaemon eq 'loncnew') {
+            &clean_lonc_childpids();
+        }
 	&log($fh,"<h3>$daemon not running, trying to start</h3>");
 	
 	if (&start_daemon($fh,$daemon,$pidfile,$args)) {
@@ -821,8 +824,39 @@
     return;
 }
 
+sub clean_nosslverify {
+    my ($fh) = @_;
+    my %unlinked; 
+    if (-d "$perlvar{'lonSockDir'}/nosslverify") {
+        if (opendir(my $dh,"$perlvar{'lonSockDir'}/nosslverify")) {
+            while (my $fname=readdir($dh)) {
+                next if ($fname =~ /^\.+$/);
+                if (unlink("/home/httpd/sockets/nosslverify/$fname")) {
+                    &log($fh,"Unlinking $fname<br />");
+                    $unlinked{$fname} = 1;
+                }
+            }
+            closedir($dh);
+        }
+    }
+    &log($fh,"<p>Removed ".scalar(keys(%unlinked))." nosslverify clients</p>");
+    return %unlinked;
+}
+sub clean_lonc_childpids {
+    my $childpiddir = "$perlvar{'lonDocRoot'}/lon-status/loncchld";
+    if (-d $childpiddir) {
+        if (opendir(my $dh,$childpiddir)) {
+            while (my $fname=readdir($dh)) {
+                next if ($fname =~ /^\.+$/);
+                unlink("$childpiddir/$fname");
+            }
+            closedir($dh);
+        }
+    }
+}
+
 sub write_connection_config {
-    my ($isprimary,$domconf,$url,%connectssl);
+    my ($isprimary,$domconf,$url,%connectssl,%changes);
     my $primaryLibServer = &Apache::lonnet::domain($perlvar{'lonDefDomain'},'primary');
     if ($primaryLibServer eq $perlvar{'lonHostID'}) {
         $isprimary = 1;
@@ -849,10 +883,30 @@
             }
         }
         if (keys(%connectssl)) {
-            if (open(my $fh,">$perlvar{'lonTabDir'}/connectionrules.tab")) {
+            my %currconf; 
+            if (open(my $fh,'<',"$perlvar{'lonTabDir'}/connectionrules.tab")) {
+                while (my $line = <$fh>) {
+                    chomp($line);
+                    my ($name,$value) = split(/=/,$line);
+                    if ($value =~ /^(?:no|yes|req)$/) {
+                        if ($name =~ /^conn(to|from)_(dom|intdom|other)$/) {
+                            $currconf{$name} = $value;
+                        }
+                    }
+                }
+                close($fh);
+            }
+            if (open(my $fh,'>',"$perlvar{'lonTabDir'}/connectionrules.tab")) {
                 my $count = 0;
                 foreach my $key (sort(keys(%connectssl))) { 
                     print $fh "$key=$connectssl{$key}\n";
+                    if (exists($currconf{$key})) {
+                        unless ($currconf{$key} eq $connectssl{$key}) {
+                            $changes{$key} = 1;
+                        }
+                    } else {
+                        $changes{$key} = 1;
+                    }
                     $count ++;
                 }
                 close($fh);
@@ -864,6 +918,7 @@
     } else {
         print "Retrieval of SSL options for lonc/lond skipped - no configuration data available for domain.\n";
     }
+    return %changes;
 }
 
 sub get_domain_config {
@@ -917,9 +972,21 @@
     my %hostdom = &Apache::lonnet::all_host_domain();
     my $dom = $hostdom{$perlvar{'lonHostID'}};
     my $internetdom = $intdom{$perlvar{'lonHostID'}};
+    my %changes;
     if (($dom ne '') && ($internetdom ne '')) {
         if (keys(%hostdom)) {
-            if (open(my $fh,">$perlvar{'lonTabDir'}/hosttypes.tab")) {
+            my %currhosttypes;
+            if (open(my $fh,'<',"$perlvar{'lonTabDir'}/hosttypes.tab")) {
+                while (my $line = <$fh>) {
+                    chomp($line);
+                    my ($name,$value) = split(/:/,$line);
+                    if (($name ne '') && ($value =~ /^(dom|intdom|other)$/)) {
+                        $currhosttypes{$name} = $value;
+                    }
+                }
+                close($fh);
+            }
+            if (open(my $fh,'>',"$perlvar{'lonTabDir'}/hosttypes.tab")) {
                 my $count = 0;
                 foreach my $lonid (sort(keys(%hostdom))) {
                     my $type = 'other';
@@ -929,6 +996,13 @@
                         $type = 'intdom';
                     }
                     print $fh "$lonid:$type\n";
+                    if (exists($currhosttypes{$lonid})) {
+                        if ($type ne $currhosttypes{$lonid}) {
+                            $changes{$lonid} = 1;
+                        }
+                    } else {
+                        $changes{$lonid} = 1;
+                    }
                     $count ++;
                 }
                 close($fh);
@@ -940,14 +1014,72 @@
     } else {
         print "Writing of host types skipped - could not determine this host's LON-CAPA domain or 'internet' domain.\n";
     }
+    return %changes;
 }
 
 sub update_revocation_list {
-    if (&Apache::lonnet::fetch_crl_pemfile() eq 'ok') {
+    my ($result,$changed) = &Apache::lonnet::fetch_crl_pemfile();
+    if ($result eq 'ok') {
         print "Certificate Revocation List (from CA) updated.\n";
     } else {
         print "Certificate Revocation List from (CA) not updated.\n";
     }
+    return $changed;
+}
+
+sub reset_nosslverify_pids {
+    my (%sslrem) = @_;
+    &checkon_daemon($fh,'lond',40000,'USR2');
+    my $loncpidfile="$perlvar{'lonDaemons'}/logs/lonc.pid";
+    my $loncppid;
+    if ((-e $loncpidfile) && (open(my $pfh,'<',$loncpidfile))) {
+        $loncppid=<$pfh>;
+        chomp($loncppid);
+        close($pfh);
+        if ($loncppid =~ /^\d+$/) {
+            my %pids_by_host;
+            my $docdir = $perlvar{'lonDocRoot'};
+            if (-d "$docdir/lon-status/loncchld") {
+                if (opendir(my $dh,"$docdir/lon-status/loncchld")) {
+                    while (my $file = readdir($dh)) {
+                        next if ($file =~ /^\./);
+                        if (open(my $fh,'<',"$docdir/lon-status/loncchld/$file")) {
+                            my $record = <$fh>;
+                            chomp($record);
+                            close($fh);
+                            my ($remotehost,$authmode) = split(/:/,$record);
+                            $pids_by_host{$remotehost}{$authmode}{$file} = 1;
+                        }
+                    }
+                    closedir($dh);
+                    if (keys(%pids_by_host)) {
+                        foreach my $host (keys(%pids_by_host)) {
+                            if ($sslrem{$host}) {
+                                if (ref($pids_by_host{$host}) eq 'HASH') {
+                                    if (ref($pids_by_host{$host}{'insecure'}) eq 'HASH') {
+                                        if (keys($pids_by_host{$host}{'insecure'})) {
+                                            foreach my $pid (keys($pids_by_host{$host}{'insecure'})) {
+                                                if (open(PIPE,"ps -o ppid= -p $pid |")) {
+                                                    my $ppid = <PIPE>;
+                                                    chomp($ppid);
+                                                    close(PIPE);
+                                                    $ppid =~ s/(^\s+|\s+$)//g;
+                                                    if (($ppid == $loncppid) && (kill 0 => $pid)) {
+                                                        kill QUIT => $pid;
+                                                    }
+                                                }
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+    return;
 }
 
 sub send_mail {
@@ -1125,8 +1257,10 @@
         &checkon_daemon($fh,'lonr',40000);
     }
     if ($justreload) {
+        &clean_nosslverify($fh);
         &write_connection_config();
         &write_hosttypes();
+        &update_revocation_list(); 
 	&checkon_daemon($fh,'lond',40000,'USR2');
 	&checkon_daemon($fh,'lonc',40000,'USR2');
     }
@@ -1135,14 +1269,20 @@
     }
     if (!$justcheckdaemons && !$justcheckconnections && !$justreload) {
 	&check_delayed_msg($fh);
-	&finish_logging($fh);
 	&log_simplestatus();
         &write_loncaparevs();
         &write_serverhomeIDs();
 	&write_checksums();
-        &write_connection_config();
-        &write_hosttypes();
-        &update_revocation_list();
+        my %sslrem = &clean_nosslverify($fh);
+        &finish_logging($fh);
+        my %conchgs = &write_connection_config();
+        my %hosttypechgs = &write_hosttypes();
+        my $hadcrlchg = &update_revocation_list();
+        if ((keys(%{$conchgs}) > 0) || (keys(%hosttypechgs) > 0) ||
+            $hadcrlchg || (keys(%sslrem) > 0)) {
+            &checkon_daemon($fh,'lond',40000,'USR2');
+            &reset_nosslverify_pids(%sslrem);
+        }
 	if ($totalcount>200 && !$noemail) { &send_mail(); }
     }
 }
Index: loncom/init.d/loncontrol
diff -u loncom/init.d/loncontrol:1.44 loncom/init.d/loncontrol:1.45
--- loncom/init.d/loncontrol:1.44	Sun Sep 20 18:31:33 2015
+++ loncom/init.d/loncontrol	Tue Aug  7 17:12:17 2018
@@ -1,6 +1,6 @@
 #!/usr/bin/perl
 #
-# $Id: loncontrol,v 1.44 2015/09/20 18:31:33 raeburn Exp $
+# $Id: loncontrol,v 1.45 2018/08/07 17:12:17 raeburn Exp $
 #
 # The LearningOnline Network with CAPA
 #
@@ -93,6 +93,9 @@
 	print(", killed off extraneous processes");
     }
     unlink($pidfile);
+    if ($killallname eq 'loncnew') {
+        &clean_nossl_loncpids();
+    }
     print("\n");
 }
 
@@ -107,6 +110,22 @@
     }
 }
 
+sub clean_nossl_loncpids {
+    my $childpiddir = '/home/httpd/perl/logs/loncpids';
+    my $nossldir = '/home/httpd/sockets/nosslverify';
+    foreach my $dir ($childpiddir,$nossldir) {
+        if (-d $dir) {
+            if (opendir(my $dh,$dir)) {
+                while (my $fname=readdir($dh)) {
+                    next if ($fname =~ /^\./);
+                    unlink("$dir/$fname");
+                }
+                closedir($dh);
+            }
+        }
+    }
+}
+
 if (($command eq "restart") || ($command eq 'stop') || 
     ($command eq 'start') || ($command eq "status")) {
     my $iptables = &LONCAPA::Firewall::get_pathto_iptables();
Index: loncom/lonnet/perl/lonnet.pm
diff -u loncom/lonnet/perl/lonnet.pm:1.1379 loncom/lonnet/perl/lonnet.pm:1.1380
--- loncom/lonnet/perl/lonnet.pm:1.1379	Wed Jul 18 13:45:03 2018
+++ loncom/lonnet/perl/lonnet.pm	Tue Aug  7 17:12:25 2018
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # TCP networking package
 #
-# $Id: lonnet.pm,v 1.1379 2018/07/18 13:45:03 raeburn Exp $
+# $Id: lonnet.pm,v 1.1380 2018/08/07 17:12:25 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -13629,7 +13629,7 @@
 
 sub save_crl_pem {
     my ($response) = @_;
-    my $msg;
+    my ($msg,$hadchanges);
     if (ref($response)) {
         my $now = time;
         my $lonca = $perlvar{'lonCertificateDirectory'}.'/'.$perlvar{'lonnetCertificateAuthority'};
@@ -13644,11 +13644,43 @@
                     chomp($check);
                     if ($check eq 'verify OK') {
                         my $dest = "$perlvar{'lonCertificateDirectory'}/$perlvar{'lonnetCertRevocationList'}";
+                        my $backup;
                         if (-e $dest) {
-                            &File::Copy::move($dest,"$dest.bak");
+                            if (&File::Copy::move($dest,"$dest.bak")) {
+                                $backup = 'ok';
+                            }
                         }
                         if (&File::Copy::move($tmpcrl,$dest)) {
                             $msg = 'ok';
+                            if ($backup) {
+                                my (%oldnums,%newnums);
+                                if (open(PIPE, "openssl crl -inform PEM -text -noout -in $dest.bak |grep 'Serial Number' |")) {
+                                    while (<PIPE>) {
+                                        $oldnums{(split(/:/))[1]} = 1;
+                                    }
+                                    close(PIPE);
+                                }
+                                if (open(PIPE, "openssl crl -inform PEM -text -noout -in $dest |grep 'Serial Number' |")) {
+                                    while(<PIPE>) {
+                                        $newnums{(split(/:/))[1]} = 1;
+                                    }
+                                    close(PIPE);
+                                }
+                                foreach my $key (sort {$b <=> $a } (keys(%newnums))) {
+                                    unless (exists($oldnums{$key})) {
+                                        $hadchanges = 1;
+                                        last;
+                                    }
+                                }
+                                unless ($hadchanges) {
+                                    foreach my $key (sort {$b <=> $a } (keys(%oldnums))) {
+                                        unless (exists($newnums{$key})) {
+                                            $hadchanges = 1;
+                                            last;
+                                        }
+                                    }
+                                }
+                            }
                         }
                     } else {
                         unlink($tmpcrl);
@@ -13661,7 +13693,7 @@
             }
         }
     }
-    return $msg;
+    return ($msg,$hadchanges);
 }
 
 # ------------------------------------------------------------ Read domain file
Index: doc/loncapafiles/loncapafiles.lpml
diff -u doc/loncapafiles/loncapafiles.lpml:1.967 doc/loncapafiles/loncapafiles.lpml:1.968
--- doc/loncapafiles/loncapafiles.lpml:1.967	Tue Jun 19 12:26:32 2018
+++ doc/loncapafiles/loncapafiles.lpml	Tue Aug  7 17:12:32 2018
@@ -2,7 +2,7 @@
  "http://lpml.sourceforge.net/DTD/lpml.dtd">
 <!-- loncapafiles.lpml -->
 
-<!-- $Id: loncapafiles.lpml,v 1.967 2018/06/19 12:26:32 raeburn Exp $ -->
+<!-- $Id: loncapafiles.lpml,v 1.968 2018/08/07 17:12:32 raeburn Exp $ -->
 
 <!--
 
@@ -384,6 +384,16 @@
 </directory>
 <directory dist='default'>
   <protectionlevel>modest_delete</protectionlevel>
+  <targetdir dist='default'>home/httpd/html/lon-status/loncchld</targetdir>
+  <categoryname>server standard</categoryname>
+  <description>
+Used by a loncnew child which has negotiated a lond connection, to store its
+pid so loncron can send a QUIT signal to any created with insecure key exchange
+following SSL certificate verification failure, to accommodate installation of
+of updated certificate(s) on remote hosts.
+  </description>
+<directory dist='default'>
+  <protectionlevel>modest_delete</protectionlevel>
   <targetdir dist='default'>home/httpd/perl/tmp</targetdir>
   <categoryname>server standard</categoryname>
   <description>location where temporary files (for caching) are stored
@@ -504,6 +514,12 @@
   <description>lonc's sockets (delayed)</description>
 </directory>
 <directory dist='default'>
+  <protectionlevel>modest_delete</protectionlevel>
+  <targetdir dist='default'>home/httpd/sockets/nosslverify</targetdir>
+  <categoryname>server standard</categoryname>
+  <description>Remote hosts where SSL certificate verification failed</description>
+</directory>
+<directory dist='default'>
   <protectionlevel>never_delete</protectionlevel>
   <targetdir dist='default'>home/httpd/cgi-bin</targetdir>
   <categoryname>standard</categoryname>


More information about the LON-CAPA-cvs mailing list