[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