[LON-CAPA-cvs] cvs: loncom / LWPReq.pm Lond.pm loncapa_apache.conf lond lontrans.pm /cgi lonauthcgi.pm loncertstatus.pl /configuration SSL.pm /interface domainprefs.pm domainstatus.pm lonconfigsettings.pm /lonnet/perl lonnet.pm

raeburn raeburn at source.lon-capa.org
Mon Jul 25 15:50:46 EDT 2016


raeburn		Mon Jul 25 19:50:46 2016 EDT

  Added files:                 
    /loncom/cgi	loncertstatus.pl 
    /loncom/configuration	SSL.pm 

  Modified files:              
    /loncom	LWPReq.pm Lond.pm lond lontrans.pm loncapa_apache.conf 
    /loncom/cgi	lonauthcgi.pm 
    /loncom/interface	domainprefs.pm domainstatus.pm 
                     	lonconfigsettings.pm 
    /loncom/lonnet/perl	lonnet.pm 
  Log:
  - Use Server Name Indication (SNI) and SSL when replicating content from
    /raw/.
  - Domain status screen has link to show status of LON-CAPA SSL certificates.
  - "SSL" domain config for (a) "internal" LON-CAPA SSL connection to servers/VMs
    in other domain, (b) Replication of domain's resources to other domains.
  - Replication can use name-based virtual hosts with SSL, with verification of 
    client certificate (cert: /home/httpd/lonCerts/lonhostnamecert.pem, signed
    by LON-CAPA CA, with Common Name of internal-<server hostname>, same IP address 
    as server hostname).
  
  
-------------- next part --------------
Index: loncom/LWPReq.pm
diff -u loncom/LWPReq.pm:1.1 loncom/LWPReq.pm:1.2
--- loncom/LWPReq.pm:1.1	Sat Jul  2 17:55:57 2016
+++ loncom/LWPReq.pm	Mon Jul 25 19:49:45 2016
@@ -1,8 +1,8 @@
 # The LearningOnline Network with CAPA
-# LON-CAPA wrapper for LWP UserAgent to accommodate certificate
+# LON-CAPA wrapper for LWP UserAgent to accommodate certification
 # verification for SSL.
 #
-# $Id: LWPReq.pm,v 1.1 2016/07/02 17:55:57 raeburn Exp $
+# $Id: LWPReq.pm,v 1.2 2016/07/25 19:49:45 raeburn Exp $
 #
 # The LearningOnline Network with CAPA
 #
@@ -36,28 +36,63 @@
 use LONCAPA::Configuration;
 use IO::Socket::SSL();
 use LWP::UserAgent();
+use LWP::UserAgent::DNS::Hosts();
+use Apache::lonnet;
 
 sub makerequest {
-    my ($request,$content,$perlvar,$timeout,$verifycert,$use_lc_ca,$debug) = @_;
+    my ($remotehostid,$request,$content,$perlvar,$timeout,$verifycert,$use_lc_ca,$debug) = @_;
     unless (ref($perlvar) eq' HASH') {
         $perlvar = LONCAPA::Configuration::read_conf('loncapa.conf');
     }
-    my ($certf,$keyf,$caf, at opts);
+    my ($certf,$keyf,$caf, at opts,$dns_set,$lonhost);
     if (ref($perlvar) eq 'HASH') {
-        $certf = $perlvar->{'lonCertificateDirectory'}.'/'.$perlvar->{'lonnetHostnameCertificate'};
-        $keyf = $perlvar->{'lonCertificateDirectory'}.'/'.$perlvar->{'lonnetPrivateKey'};
-        $caf = $perlvar->{'lonCertificateDirectory'}.'/'.$perlvar->{'lonnetCertificateAuthority'};
+        $lonhost = $perlvar->{'lonHostID'};
+        if ($perlvar->{'lonCertificateDirectory'}) {
+            if ($perlvar->{'lonnetHostnameCertificate'}) {
+                if (-e $perlvar->{'lonCertificateDirectory'}.'/'.$perlvar->{'lonnetHostnameCertificate'}) {
+                    $certf = $perlvar->{'lonCertificateDirectory'}.'/'.$perlvar->{'lonnetHostnameCertificate'};
+                }
+            }
+            if ($perlvar->{'lonnetPrivateKey'}) {
+                if (-e $perlvar->{'lonCertificateDirectory'}.'/'.$perlvar->{'lonnetPrivateKey'}) {
+                    $keyf = $perlvar->{'lonCertificateDirectory'}.'/'.$perlvar->{'lonnetPrivateKey'};
+                }
+            }
+            if ($perlvar->{'lonnetCertificateAuthority'}) {
+                if (-e $perlvar->{'lonCertificateDirectory'}.'/'.$perlvar->{'lonnetCertificateAuthority'}) {
+                    $caf = $perlvar->{'lonCertificateDirectory'}.'/'.$perlvar->{'lonnetCertificateAuthority'};
+                }
+            }
+        }
     }
     if ($debug) {
         $IO::Socket::SSL::DEBUG=$debug;
     }
-    my $response;
+    my ($response,$stdhostname,$remotehostname,$fn);
+    if ($request->uri =~ m{^https?://((?:internal\-|)([^/]+))(/raw/.+)$}) {
+        $remotehostname = $1;
+        $stdhostname = $2;
+        $fn = $3;
+        $dns_set = &setdns($remotehostid,$remotehostname);
+        unless ($remotehostname =~ /^internal\-/) {
+            if (($use_lc_ca && $certf && $keyf) &&
+                (&raw_redirected($remotehostid,$lonhost))) {
+                $remotehostname = 'internal-'.$stdhostname;
+                $request->uri('https://'.$remotehostname.$fn);
+            }
+        }
+    }
     if (LWP::UserAgent->VERSION >= 6.00) {
         my $ssl_opts;
         if ($use_lc_ca && $certf && $keyf) {
             $ssl_opts->{'SSL_use_cert'} = 1;
             $ssl_opts->{'SSL_cert_file'} = $certf;
             $ssl_opts->{'SSL_key_file'} = $keyf;
+            if ($dns_set && $remotehostname) {
+                if ($remotehostname =~ /^internal\-/) {
+                    $ssl_opts->{'SSL_hostname'} = $remotehostname;
+                }
+            }
         } else {
             $ssl_opts->{'SSL_use_cert'} = 0;
         }
@@ -65,7 +100,7 @@
             $ssl_opts->{'verify_hostname'} = 1;
             $ssl_opts->{'SSL_verify_mode'} = IO::Socket::SSL::SSL_VERIFY_PEER;
             $ssl_opts->{'SSL_version'} = 'SSLv23:!SSLv3:!SSLv2';
-            if ($use_lc_ca) {   
+            if ($use_lc_ca) {
                 $ssl_opts->{'SSL_ca_file'} = $caf;
             }
         } else {
@@ -77,11 +112,30 @@
         if ($timeout) {
             $ua->timeout($timeout);
         }
+        if ($use_lc_ca && $remotehostname && $fn) {
+            $ua->requests_redirectable(undef);
+        }
         if ($content ne '') {
             $response = $ua->request($request,$content);
         } else {
             $response = $ua->request($request);
         }
+        if (($response->code eq '302') && ($fn) && ($remotehostname) &&
+            ($response->header('Location') eq 'https://internal-'.$stdhostname.$fn)) {
+            my $newurl = $response->header('Location');
+            unless ($dns_set) {
+                $dns_set = &setdns($remotehostid,$remotehostname);
+            }
+            if ($use_lc_ca && $certf && $keyf) {
+                $ssl_opts->{'SSL_hostname'} = 'internal-'.$stdhostname;
+            }
+            $request->uri($newurl);
+            if ($content ne '') {
+                $response = $ua->request($request,$content);
+            } else {
+                $response = $ua->request($request);
+            }
+        }
     } else {
         {
             require Net::SSLGlue::LWP;
@@ -90,6 +144,11 @@
                 $Net::SSLGlue::LWP::SSLopts{'SSL_use_cert'} = 1;
                 $Net::SSLGlue::LWP::SSLopts{'SSL_cert_file'} = $certf;
                 $Net::SSLGlue::LWP::SSLopts{'SSL_key_file'} = $keyf;
+                if ($dns_set && $remotehostname) {
+                    if ($remotehostname =~ /^internal\-/) {
+                        $Net::SSLGlue::LWP::SSLopts{'SSL_hostname'} = $remotehostname;
+                    }
+                }
             } else {
                 $Net::SSLGlue::LWP::SSLopts{'SSL_use_cert'} = 0;
             }
@@ -106,14 +165,94 @@
             if ($timeout) {
                 $ua->timeout($timeout);
             }
+            if ($use_lc_ca && $remotehostname && $fn) {
+                $ua->requests_redirectable(undef);
+            }
             if ($content ne '') {
                 $response = $ua->request($request,$content);
             } else {
                 $response = $ua->request($request);
             }
+            if (($response->code eq '302') && ($fn) && ($remotehostname) &&
+                ($response->header('Location') eq 'https://internal-'.$stdhostname.$fn)) {
+                my $newurl = $response->header('Location');
+                unless ($dns_set) {
+                    $dns_set = &setdns($remotehostid,$remotehostname);
+                }
+                $Net::SSLGlue::LWP::SSLopts{'SSL_hostname'} = 'internal-'.$stdhostname;
+                $request->uri($newurl);
+                if ($content ne '') {
+                    $response = $ua->request($request,$content);
+                } else {
+                    $response = $ua->request($request);
+                }
+            }
         }
    }
+   if ($dns_set) {
+       $dns_set = &unsetdns();
+   }
    return $response;
 }
 
+sub setdns {
+    my ($remotehostid,$remotehostname) = @_;
+    my $ip = &Apache::lonnet::get_host_ip($remotehostid);
+    if ($remotehostname =~ /^internal\-/) {
+        LWP::UserAgent::DNS::Hosts->register_host(
+            $remotehostname => $ip,
+        );
+    } else {
+        LWP::UserAgent::DNS::Hosts->register_host(
+            'internal-'.$remotehostname => $ip,
+        );
+    }
+    LWP::UserAgent::DNS::Hosts->enable_override;
+    return 1;
+}
+
+sub unsetdns {
+    LWP::UserAgent::DNS::Hosts->clear_hosts();
+    return 0;
+}
+
+sub raw_redirected {
+    my ($remotehostid,$lonhost) = @_;
+    my $remhostname = &Apache::lonnet::hostname($remotehostid);
+    my $redirect;
+    if ($remhostname) {
+        my $remoterev = &Apache::lonnet::get_server_loncaparev(undef,$remotehostid);
+        my ($remmajor,$remminor) = ($remoterev =~ /^(\d+)\.(\d+)/);
+        if (($remmajor > 2) || (($remmajor == 2) && $remminor >= 12)) {
+            my $internet_names = &Apache::lonnet::get_internet_names($remotehostid);
+            if (ref($internet_names) eq 'ARRAY') {
+                my $intdom = &Apache::lonnet::internet_dom($lonhost);
+                unless (grep(/^\Q$intdom\E$/,@{$internet_names})) {
+                    my $remhomeID = &Apache::lonnet::get_server_homeID($remhostname);
+                    my $remhomedom = &Apache::lonnet::host_domain($remhomeID);
+                    my %domdefaults = &Apache::lonnet::get_domain_defaults($remhomedom);
+                    my $replication = $domdefaults{'replication'};
+                    if (ref($replication) eq 'HASH') {
+                        if (ref($replication->{'reqcerts'}) eq 'ARRAY') {
+                            if (grep(/^\Q$intdom\E$/,@{$replication->{'reqcerts'}})) {
+                                $redirect = 1;
+                            } else {
+                                $redirect = 0;
+                            }
+                        }
+                        if (ref($replication->{'noreqcerts'}) eq 'ARRAY') {
+                            if (grep(/^\Q$intdom\E$/,@{$replication->{'noreqcerts'}})) {
+                                $redirect = 0;
+                            } else {
+                                $redirect = 1;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+    return $redirect;
+}
+
 1;
Index: loncom/Lond.pm
diff -u loncom/Lond.pm:1.8 loncom/Lond.pm:1.9
--- loncom/Lond.pm:1.8	Fri May 22 21:14:59 2015
+++ loncom/Lond.pm	Mon Jul 25 19:49:45 2016
@@ -1,6 +1,6 @@
 # The LearningOnline Network
 #
-# $Id: Lond.pm,v 1.8 2015/05/22 21:14:59 raeburn Exp $
+# $Id: Lond.pm,v 1.9 2016/07/25 19:49:45 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -37,6 +37,7 @@
 use LONCAPA;
 use Apache::lonnet;
 use GDBM_File;
+use Crypt::OpenSSL::X509;
 
 
 sub dump_with_regexp {
@@ -782,6 +783,85 @@
     return $qresult;
 }
 
+sub server_certs {
+    my ($perlvar) = @_;
+    my %pemfiles = (
+                     key      => 'lonnetPrivateKey',
+                     host     => 'lonnetCertificate',
+                     hostname => 'lonnetHostnameCertificate',
+                     ca       => 'lonnetCertificateAuthority',
+                   );
+    my (%md5hash,%info);
+    if (ref($perlvar) eq 'HASH') {
+        my $certsdir = $perlvar->{'lonCertificateDirectory'};
+        if (-d $certsdir) {
+            foreach my $key (keys(%pemfiles)) {
+                if ($perlvar->{$pemfiles{$key}}) {
+                    my $file = $certsdir.'/'.$perlvar->{$pemfiles{$key}};
+                    if (-e $file) {
+                        if ($key eq 'key') {
+                            if (open(PIPE,"openssl rsa -noout -in $file -check |")) {
+                                my $check = <PIPE>;
+                                close(PIPE);
+                                chomp($check);
+                                $info{$key}{'status'} = $check;
+                            }
+                            if (open(PIPE,"openssl rsa -noout -modulus -in $file | openssl md5 |")) {
+                                $md5hash{$key} = <PIPE>;
+                                close(PIPE);
+                            }
+                        } else {
+                            if ($key eq 'ca') {
+                                if (open(PIPE,"openssl verify -CAfile $file $file |")) {
+                                    my $check = <PIPE>;
+                                    close(PIPE);
+                                    chomp($check);
+                                    if ($check eq "$file: OK") {
+                                        $info{$key}{'status'} = 'ok';
+                                    } else {
+                                        $check =~ s/^\Q$file\E\:?\s*//;
+                                        $info{$key}{'status'} = $check;
+                                    }
+                                }
+                            } else {
+                                if (open(PIPE,"openssl x509 -noout -modulus -in $file | openssl md5 |")) {
+                                    $md5hash{$key} = <PIPE>;
+                                    close(PIPE);
+                                }
+                            }
+                            my $x509 = Crypt::OpenSSL::X509->new_from_file($file);
+                            my @items = split(/,\s+/,$x509->subject());
+                            foreach my $item (@items) {
+                                my ($name,$value) = split(/=/,$item);
+                                if ($name eq 'CN') {
+                                    $info{$key}{'cn'} = $value;
+                                }
+                            }
+                            $info{$key}{'start'} = $x509->notBefore();
+                            $info{$key}{'end'} = $x509->notAfter();
+                            $info{$key}{'alg'} = $x509->sig_alg_name();
+                            $info{$key}{'size'} = $x509->bit_length();
+                            $info{$key}{'email'} = $x509->email();
+                        }
+                    }
+                }
+            }
+        }
+    }
+    foreach my $key ('host','hostname') {
+        if ($md5hash{$key}) {
+            if ($md5hash{$key} eq $md5hash{'key'}) {
+                $info{$key}{'status'} = 'ok';
+            }
+        }
+    }
+    my $result;
+    foreach my $key (keys(%info)) {
+        $result .= &escape($key).'='.&Apache::lonnet::freeze_escape($info{$key}).'&';
+    }
+    $result =~ s/\&$//;
+    return $result;
+}
 
 1;
 
Index: loncom/lond
diff -u loncom/lond:1.522 loncom/lond:1.523
--- loncom/lond:1.522	Mon May 30 03:16:38 2016
+++ loncom/lond	Mon Jul 25 19:49:45 2016
@@ -2,7 +2,7 @@
 # The LearningOnline Network
 # lond "LON Daemon" Server (port "LOND" 5663)
 #
-# $Id: lond,v 1.522 2016/05/30 03:16:38 raeburn Exp $
+# $Id: lond,v 1.523 2016/07/25 19:49:45 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -41,7 +41,7 @@
 #use Apache::File;
 use POSIX;
 use Crypt::IDEA;
-use LWP::UserAgent();
+use HTTP::Request;
 use Digest::MD5 qw(md5_hex);
 use GDBM_File;
 use Authen::Krb5;
@@ -58,13 +58,14 @@
 use Crypt::Eksblowfish::Bcrypt;
 use Digest::SHA;
 use Encode;
+use LONCAPA::LWPReq;
 
 my $DEBUG = 0;		       # Non zero to enable debug log entries.
 
 my $status='';
 my $lastlog='';
 
-my $VERSION='$Revision: 1.522 $'; #' stupid emacs
+my $VERSION='$Revision: 1.523 $'; #' stupid emacs
 my $remoteVERSION;
 my $currenthostid="default";
 my $currentdomainid;
@@ -667,10 +668,8 @@
                 $clientprotocol = 'http' if ($clientprotocol ne 'https');
                 my $url = '/adm/'.$filename;
                 $url =~ s{_}{/};
-                my $ua=new LWP::UserAgent;
-                $ua->timeout(60);
                 my $request=new HTTP::Request('GET',"$clientprotocol://$clienthost$url");
-                my $response=$ua->request($request);
+                my $response = LONCAPA::LWPReq::makerequest($clientname,$request,'',\%perlvar,60,0);
                 if ($response->is_error()) {
                     &logthis('<font color="red"> Pushfile: unable to install '
                             .$tablefile." - error attempting to pull data. </font>");
@@ -1797,6 +1796,16 @@
 }
 &register_handler("serverdistarch", \&server_distarch_handler, 0, 1, 0);
 
+sub server_certs_handler {
+    my ($cmd,$tail,$client) = @_;
+    my $userinput = "$cmd:$tail";
+    my $result;
+    my $result = &LONCAPA::Lond::server_certs(\%perlvar);
+    &Reply($client,\$result,$userinput);
+    return;
+}
+&register_handler("servercerts", \&server_certs_handler, 0, 1, 0);
+
 #   Process a reinit request.  Reinit requests that either
 #   lonc or lond be reinitialized so that an updated 
 #   host.tab or domain.tab can be processed.
@@ -2331,9 +2340,8 @@
 # FIXME: this should use the LWP mechanism, not internal alarms.
                 alarm(1200);
 		{
-		    my $ua=new LWP::UserAgent;
 		    my $request=new HTTP::Request('GET',"$remoteurl");
-		    $response=$ua->request($request,$transname);
+                    $response=&LONCAPA::LWPReq::makerequest($clientname,$request,$transname,\%perlvar,1200,0,1);
 		}
 		alarm(0);
 		if ($response->is_error()) {
@@ -2346,9 +2354,8 @@
 # FIXME: isn't there an internal LWP mechanism for this?
 			alarm(120);
 			{
-			    my $ua=new LWP::UserAgent;
 			    my $mrequest=new HTTP::Request('GET',$remoteurl.'.meta');
-			    my $mresponse=$ua->request($mrequest,$fname.'.meta');
+                            my $mresponse = &LONCAPA::LWPReq::makerequest($clientname,$mrequest,$fname.'.meta',\%perlvar,120,0,1);
 			    if ($mresponse->is_error()) {
 				unlink($fname.'.meta');
 			    }
@@ -2423,11 +2430,15 @@
 	my $remoteurl=$clientprotocol.'://'.$clienthost.'/userfiles/'.$fname;
 	my $response;
 	Debug("Remote URL : $remoteurl Transfername $transname Destname: $destname");
-	alarm(120);
+	alarm(1200);
 	{
-	    my $ua=new LWP::UserAgent;
 	    my $request=new HTTP::Request('GET',"$remoteurl");
-	    $response=$ua->request($request,$transname);
+            my $verifycert = 1;
+            my @machine_ids = &Apache::lonnet::current_machine_ids();
+            if (grep(/^\Q$clientname\E$/, at machine_ids)) {
+                $verifycert = 0;
+            }
+            $response = &LONCAPA::LWPReq::makerequest($clientname,$request,$transname,\%perlvar,1200,$verifycert);
 	}
 	alarm(0);
 	if ($response->is_error()) {
Index: loncom/lontrans.pm
diff -u loncom/lontrans.pm:1.14 loncom/lontrans.pm:1.15
--- loncom/lontrans.pm:1.14	Fri Oct 21 16:03:06 2011
+++ loncom/lontrans.pm	Mon Jul 25 19:49:45 2016
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # URL translation for User Files
 #
-# $Id: lontrans.pm,v 1.14 2011/10/21 16:03:06 www Exp $
+# $Id: lontrans.pm,v 1.15 2016/07/25 19:49:45 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -29,16 +29,32 @@
 package Apache::lontrans;
 
 use strict;
-use Apache::Constants qw(:common :remotehost);
+use Apache::Constants qw(:common :remotehost REDIRECT);
 use Apache::lonnet();
 use Apache::File();
-use LONCAPA;
-
+use LONCAPA qw(:DEFAULT :match);
 
 sub handler {
     my $r = shift;
     # FIXME line remove when mod_perl fixes BUG#4948 
     $r->notes->set('error-notes' => '');
+    if ($r->uri=~m{^/raw/}) {
+        my $host = $r->headers_in->get('Host');
+        if ($host) {
+            unless ($host =~ /^internal\-/) {
+                my $c = $r->connection;
+                if (ref($c)) {
+                    my $remote_ip = $c->remote_ip;
+                    my $lonhost = $r->dir_config('lonHostID');
+                    if (&redirect_raw($remote_ip,$lonhost)) {
+                        my $location = 'https://internal-'.$host.$r->uri;
+                        $r->headers_out->set(Location => $location);
+                        return REDIRECT;
+                    }
+                }
+            }
+        }
+    }
     if ($r->uri=~m|^(/raw)?/uploaded/|) {
         my $fn = $r->uri();
         $fn=~s/^\/raw//;
@@ -52,8 +68,59 @@
 	    $r->filename(&propath($udom,$uname).
 			 '/userfiles/'.(join('/', at ufile)));
         }
-    } else { return DECLINED; }
-    return OK;
+        return OK;
+    } else { 
+        return DECLINED;
+    }
+}
+
+sub redirect_raw {
+    my ($remote_ip,$lonhost) = @_;
+    my @remhostids = &Apache::lonnet::get_hosts_from_ip($remote_ip);
+    my $redirect;
+    while (@remhostids) {
+        my $try_server = pop(@remhostids);
+        my $remhostname = &Apache::lonnet::hostname($try_server);
+        if ($remhostname) {
+            my $remoterev = &Apache::lonnet::get_server_loncaparev(undef,$try_server);
+            my ($remmajor,$remminor) = ($remoterev =~ /^(\d+)\.(\d+)/);
+            if (($remmajor > 2) || (($remmajor == 2) && $remminor >= 12)) {
+                my $internet_names = &Apache::lonnet::get_internet_names($try_server);
+                if (ref($internet_names) eq 'ARRAY') {
+                    my $intdom = &Apache::lonnet::internet_dom($lonhost);
+                    unless (grep(/^\Q$intdom\E$/,@{$internet_names})) {
+                        my $lonhostname = &Apache::lonnet::hostname($lonhost);
+                        my $serverhomeID = &Apache::lonnet::get_server_homeID($lonhostname);
+                        my $serverhomedom = &Apache::lonnet::host_domain($serverhomeID);
+                        my %domdefaults = &Apache::lonnet::get_domain_defaults($serverhomedom);
+                        my $replication = $domdefaults{'replication'};
+                        if (ref($replication) eq 'HASH') {
+                            my $remhomeID = &Apache::lonnet::get_server_homeID($remhostname);
+                            my $remhomedom = &Apache::lonnet::host_domain($remhomeID);
+                            my $remprimary = &Apache::lonnet::domain($remhomedom,'primary');
+                            my $remintdom = &Apache::lonnet::internet_dom($remprimary);
+                            if (ref($replication->{'certreq'}) eq 'ARRAY') {
+                                if (grep(/^\Q$remintdom\E$/,@{$replication->{'certreq'}})) {
+                                    $redirect = 1;
+                                } else {
+                                    $redirect = 0;
+                                }
+                            }
+                            if (ref($replication->{'nocertreq'}) eq 'ARRAY') {
+                                if (grep(/^\Q$remintdom\E$/,@{$replication->{'nocertreq'}})) {
+                                    $redirect = 0;
+                                } else {
+                                    $redirect = 1;
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+            last;
+        }
+    }
+    return $redirect;  
 }
 
 1;
Index: loncom/loncapa_apache.conf
diff -u loncom/loncapa_apache.conf:1.247 loncom/loncapa_apache.conf:1.248
--- loncom/loncapa_apache.conf:1.247	Fri Jul  1 20:00:07 2016
+++ loncom/loncapa_apache.conf	Mon Jul 25 19:49:45 2016
@@ -2,7 +2,7 @@
 ## loncapa_apache.conf -- Apache HTTP LON-CAPA configuration file
 ##
 
-# $Id: loncapa_apache.conf,v 1.247 2016/07/01 20:00:07 raeburn Exp $
+# $Id: loncapa_apache.conf,v 1.248 2016/07/25 19:49:45 raeburn Exp $
 
 #
 # LON-CAPA Section (extensions to httpd.conf daemon configuration)
@@ -1763,6 +1763,7 @@
 
 PerlSetVar lonnetCertificateAuthority loncapaCA.pem
 PerlSetVar lonnetCertificate          lonhostcert.pem
+PerlSetVar lonnetHostnameCertificate  lonhostnamecert.pem
 
 #
 #  To generate the request for a certificate, and to negotiate the
Index: loncom/cgi/lonauthcgi.pm
diff -u loncom/cgi/lonauthcgi.pm:1.14 loncom/cgi/lonauthcgi.pm:1.15
--- loncom/cgi/lonauthcgi.pm:1.14	Mon May  4 15:43:57 2015
+++ loncom/cgi/lonauthcgi.pm	Mon Jul 25 19:50:01 2016
@@ -1,7 +1,7 @@
 #
 # LON-CAPA authorization for cgi-bin scripts
 #
-# $Id: lonauthcgi.pm,v 1.14 2015/05/04 15:43:57 raeburn Exp $
+# $Id: lonauthcgi.pm,v 1.15 2016/07/25 19:50:01 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -383,6 +383,7 @@
                    'checksums'         => 'LON-CAPA Module Checking',
                    'diskusage'         => 'Course/Community Disk Usage',
                    'clusterstatus'     => 'Domain status',
+                   'certstatus'        => 'LON-CAPA SSL Certificates Status',
                    'metadata_keywords' => 'Display Metadata Keywords',
                    'metadata_harvest'  => 'Harvest Metadata Searches',
                    'takeoffline'       => 'Offline - replace Log-in page',
Index: loncom/interface/domainprefs.pm
diff -u loncom/interface/domainprefs.pm:1.274 loncom/interface/domainprefs.pm:1.275
--- loncom/interface/domainprefs.pm:1.274	Sun Jul 24 14:34:59 2016
+++ loncom/interface/domainprefs.pm	Mon Jul 25 19:50:30 2016
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # Handler to set domain-wide configuration settings
 #
-# $Id: domainprefs.pm,v 1.274 2016/07/24 14:34:59 raeburn Exp $
+# $Id: domainprefs.pm,v 1.275 2016/07/25 19:50:30 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -169,6 +169,7 @@
 use LONCAPA qw(:DEFAULT :match);
 use LONCAPA::Enrollment;
 use LONCAPA::lonauthcgi();
+use LONCAPA::SSL;
 use File::Copy;
 use Locale::Language;
 use DateTime::TimeZone;
@@ -217,13 +218,13 @@
                 'serverstatuses','requestcourses','helpsettings',
                 'coursedefaults','usersessions','loadbalancing',
                 'requestauthor','selfenrollment','inststatus',
-                'ltitools'],$dom);
+                'ltitools','ssl'],$dom);
     my @prefs_order = ('rolecolors','login','defaults','quotas','autoenroll',
                        'autoupdate','autocreate','directorysrch','contacts',
                        'usercreation','selfcreation','usermodification','scantron',
                        'requestcourses','requestauthor','coursecategories',
                        'serverstatuses','helpsettings','coursedefaults',
-                       'ltitools','selfenrollment','usersessions');
+                       'ltitools','selfenrollment','usersessions','ssl');
     my %existing;
     if (ref($domconfig{'loadbalancing'}) eq 'HASH') {
         %existing = %{$domconfig{'loadbalancing'}};
@@ -482,7 +483,18 @@
                   print => \&print_ltitools,
                   modify => \&modify_ltitools,
                  },
- 
+          'ssl' =>
+                 {text  => 'LON-CAPA Network (SSL)',
+                  help  => 'Domain_Configuration_Network_SSL',
+                  header => [{col1 => 'Server',
+                              col2 => 'Certificate Status'},
+                             {col1 => 'Connections to other servers',
+                              col2 => 'Rules'},
+                             {col1 => "Replicating domain's published content",
+                              col2 => 'Rules'}],
+                  print => \&print_ssl,
+                  modify => \&modify_ssl,
+                 },
     );
     if (keys(%servers) > 1) {
         $prefs{'login'}  = { text   => 'Log-in page options',
@@ -658,6 +670,8 @@
         $output = &modify_loadbalancing($dom,%domconfig);
     } elsif ($action eq 'ltitools') {
         $output = &modify_ltitools($r,$dom,$action,$lastactref,%domconfig);
+    } elsif ($action eq 'ssl') {
+        $output = &modify_ssl($dom,$lastactref,%domconfig);
     }
     return $output;
 }
@@ -704,7 +718,7 @@
         $rowtotal ++;
         if (($action eq 'autoupdate') || ($action eq 'usercreation') || ($action eq 'selfcreation') ||
             ($action eq 'usermodification') || ($action eq 'defaults') || ($action eq 'coursedefaults') ||
-            ($action eq 'selfenrollment') || ($action eq 'usersessions')) {
+            ($action eq 'selfenrollment') || ($action eq 'usersessions') || ($action eq 'ssl')) {
             $output .= $item->{'print'}->('top',$dom,$settings,\$rowtotal);
         } elsif ($action eq 'coursecategories') {
             $output .= $item->{'print'}->('top',$dom,$item,$settings,\$rowtotal);
@@ -734,7 +748,8 @@
             $rowtotal ++;
         if (($action eq 'autoupdate') || ($action eq 'usercreation') ||
             ($action eq 'selfcreation') || ($action eq 'selfenrollment') ||
-            ($action eq 'usersessions') || ($action eq 'coursecategories')) {
+            ($action eq 'usersessions') || ($action eq 'coursecategories') || 
+            ($action eq 'ssl')) {
             if ($action eq 'coursecategories') {
                 $output .= &print_coursecategories('middle',$dom,$item,$settings,\$rowtotal);
                 $colspan = ' colspan="2"';
@@ -3795,8 +3810,8 @@
 sub print_usersessions {
     my ($position,$dom,$settings,$rowtotal) = @_;
     my ($css_class,$datatable,%checked,%choices);
-    my (%by_ip,%by_location, at intdoms);
-    &build_location_hashes(\@intdoms,\%by_ip,\%by_location);
+    my (%by_ip,%by_location, at intdoms, at instdoms);
+    &build_location_hashes(\@intdoms,\%by_ip,\%by_location,\@instdoms);
 
     my @alldoms = &Apache::lonnet::all_domains();
     my %serverhomes = %Apache::lonnet::serverhomeIDs;
@@ -3934,10 +3949,178 @@
     return $datatable;
 }
 
+sub print_ssl {
+    my ($position,$dom,$settings,$rowtotal) = @_;
+    my ($css_class,$datatable);
+    my $lonhost = '';
+    my $itemcount = 1;
+    if ($position eq 'top') {
+        my %domservers = &Apache::lonnet::get_servers($dom);
+        $css_class = $itemcount%2?' class="LC_odd_row"':'';
+        $datatable .= '<tr'.$css_class.'><td colspan="2">'.
+                      &LONCAPA::SSL::print_certstatus(\%domservers,'web','domprefs').
+                      '</td></tr>';
+        $itemcount ++;
+    } else {
+        my %titles = &ssl_titles();
+        my (%by_ip,%by_location, at intdoms, at instdoms);
+        &build_location_hashes(\@intdoms,\%by_ip,\%by_location,\@instdoms);
+        my @alldoms = &Apache::lonnet::all_domains();
+        my %serverhomes = %Apache::lonnet::serverhomeIDs;
+        my @domservers = &Apache::lonnet::get_servers($dom);
+        my %servers = &Apache::lonnet::internet_dom_servers($dom);
+        my %altids = &id_for_thisdom(%servers);
+        if ($position eq 'middle') {
+            foreach my $type ('dom','intdom','other') {
+                my %checked;
+                $css_class = $itemcount%2?' class="LC_odd_row"':'';
+                $datatable .= '<tr'.$css_class.'><td>'.$titles{$type}.'</td>'.
+                              '<td class="LC_right_item">';
+                my $skip; 
+                if ($type eq 'dom') {
+                    unless (keys(%servers) > 1) {
+                        $datatable .= &mt('Nothing to set here, as there are no other servers/VMs');    
+                        $skip = 1;
+                    }
+                }
+                if ($type eq 'intdom') {
+                    unless (@instdoms > 1) {
+                        $datatable .= &mt('Nothing to set here, as there are no other domains for this institution');
+                        $skip = 1;
+                    } 
+                } elsif ($type eq 'other') {
+                    if (keys(%by_location) == 0) {
+                        $datatable .= &mt('Nothing to set here, as there are no other institutions');
+                        $skip = 1;
+                    }
+                }
+                unless ($skip) {
+                    $checked{'yes'} = ' checked="checked"'; 
+                    if (ref($settings) eq 'HASH') {
+                        if (ref($settings->{'connect'}) eq 'HASH') {
+                            if ($settings->{'connect'}->{$type} =~ /^(no|req)$/) {
+                                $checked{$1} = $checked{'yes'};
+                                delete($checked{'yes'}); 
+                            }
+                        }
+                    }
+                    foreach my $option ('no','yes','req') {
+                        $datatable .= '<span class="LC_nobreak"><label>'.
+                                      '<input type="radio" name="connect_'.$type.'" '.
+                                      'value="'.$option.'"'.$checked{$option}.' />'.$titles{$option}.
+                                      '</label></span>'.(' 'x2);
+                    }
+                }
+                $datatable .= '</td></tr>';
+                $itemcount ++; 
+            }
+        } else {
+            my $numinrow = 5;
+            my $prefix = 'replication';
+            my @types = ('certreq','nocertreq');
+            my (%current,%checkedon,%checkedoff);
+            my @locations = sort(keys(%by_location));
+            foreach my $type (@types) {
+                $checkedon{$type} = '';
+                $checkedoff{$type} = ' checked="checked"';
+            }
+            if (ref($settings) eq 'HASH') {
+                if (ref($settings->{$prefix}) eq 'HASH') {
+                    foreach my $key (keys(%{$settings->{$prefix}})) {
+                        $current{$key} = $settings->{$prefix}{$key};
+                        if (ref($current{$key}) eq 'ARRAY') {
+                            $checkedon{$key} = ' checked="checked"';
+                            $checkedoff{$key} = '';
+                        }
+                    }
+                }
+            }
+            if (@locations > 0) {
+                foreach my $type (@types) {
+                    $css_class = $itemcount%2?' class="LC_odd_row"':'';
+                    $datatable .= '<tr'.$css_class.'>
+                                   <td><span class="LC_nobreak LC_right_item">'.$titles{$type}.'</span><br />
+                                   <span class="LC_nobreak"> 
+                                   <label><input type="radio" name="'.$prefix.'_'.$type.'_inuse" '.$checkedoff{$type}.' value="0" />'.&mt('Not in use').'</label> 
+                                   <label><input type="radio" name="'.$prefix.'_'.$type.'_inuse" '.$checkedon{$type}.' value="1" />'.&mt('In use').'</label></span></td><td>
+                                   <div><input type="button" value="'.&mt('check all').'" '.
+                                   'onclick="javascript:checkAll(document.display.'.$prefix.'_'.$type.')"'.
+                                   ' />'.(' 'x2)."\n".
+                                   '<input type="button" value="'.&mt('uncheck all').'" '.
+                                   'onclick="javascript:uncheckAll(document.display.'.$prefix.'_'.$type.')" />'.
+                                   "\n".
+                                   '</div><div><table>';
+                    my $rem;
+                    for (my $i=0; $i<@locations; $i++) {
+                        my ($showloc,$value,$checkedtype);
+                        if (ref($by_location{$locations[$i]}) eq 'ARRAY') {
+                            my $ip = $by_location{$locations[$i]}->[0];
+                            if (ref($by_ip{$ip}) eq 'ARRAY') {
+                                $value = join(':',@{$by_ip{$ip}});
+                                $showloc = join(', ',@{$by_ip{$ip}});
+                                if (ref($current{$type}) eq 'ARRAY') {
+                                    foreach my $loc (@{$by_ip{$ip}}) {
+                                        if (grep(/^\Q$loc\E$/,@{$current{$type}})) {
+                                            $checkedtype = ' checked="checked"';
+                                            last;
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                        $rem = $i%($numinrow);
+                        if ($rem == 0) {
+                            if ($i > 0) {
+                               $datatable .= '</tr>';
+                            }
+                            $datatable .= '<tr>';
+                        }
+                        $datatable .= '<td class="LC_left_item">'.
+                                      '<span class="LC_nobreak"><label>'.
+                                      '<input type="checkbox" name="'.$prefix.'_'.$type.
+                                      '" value="'.$value.'"'.$checkedtype.' />'.$showloc.
+                                      '</label></span></td>';
+                    }
+                    $rem = @locations%($numinrow);
+                    my $colsleft = $numinrow - $rem;
+                    if ($colsleft > 1 ) {
+                        $datatable .= '<td colspan="'.$colsleft.'" class="LC_left_item">'.
+                                      ' </td>';
+                    } elsif ($colsleft == 1) {
+                        $datatable .= '<td class="LC_left_item"> </td>';
+                    }
+                    $datatable .= '</tr></table></td></tr>';
+                    $itemcount ++;
+                }
+            } else {
+                $datatable .= '<tr'.$css_class.'><td>'.&mt('Nothing to set here, as there are no other institutions').'</td></tr>';
+                $itemcount ++;
+            }
+        }
+    }
+    $$rowtotal += $itemcount;
+    return $datatable;
+}
+
+sub ssl_titles {
+    return &Apache::lonlocal::texthash (
+               dom           => 'LON-CAPA servers/VMs from same domain',
+               intdom        => 'LON-CAPA servers/VMs from same "internet" domain',
+               other         => 'External LON-CAPA servers/VMs',
+               connect       => 'Connections to other servers',
+               replication   => 'Replicating content to other institutions',
+               certreq       => 'Client certificate required, but specific domains exempt',
+               nocertreq     => 'No client certificate required, except for specific domains',
+               no            => 'SSL not used',
+               yes           => 'SSL Optional (used if available)',
+               req           => 'SSL Required',
+    );
+} 
+
 sub build_location_hashes {
-    my ($intdoms,$by_ip,$by_location) = @_;
+    my ($intdoms,$by_ip,$by_location,$instdoms) = @_;
     return unless((ref($intdoms) eq 'ARRAY') && (ref($by_ip) eq 'HASH') &&
-                  (ref($by_location) eq 'HASH')); 
+                  (ref($by_location) eq 'HASH') && (ref($instdoms) eq 'ARRAY'));
     my %iphost = &Apache::lonnet::get_iphost();
     my $primary_id = &Apache::lonnet::domain($env{'request.role.domain'},'primary');
     my $primary_ip = &Apache::lonnet::get_host_ip($primary_id);
@@ -3954,7 +4137,13 @@
             foreach my $id (@{$iphost{$ip}}) {
                 my $location = &Apache::lonnet::internet_dom($id);
                 if ($location) {
-                    next if (grep(/^\Q$location\E$/,@{$intdoms}));
+                    if (grep(/^\Q$location\E$/,@{$intdoms})) {
+                        my $dom = &Apache::lonnet::host_domain($id);
+                        unless (grep(/^\Q$dom\E/,@{$instdoms})) {
+                            push(@{$instdoms},$dom);
+                        }
+                        next;
+                    }
                     if (ref($by_ip->{$ip}) eq 'ARRAY') {
                         unless(grep(/^\Q$location\E$/,@{$by_ip->{$ip}})) {
                             push(@{$by_ip->{$ip}},$location);
@@ -5872,9 +6061,9 @@
 
 sub serverstatus_pages {
     return ('userstatus','lonstatus','loncron','server-status','codeversions',
-            'checksums','clusterstatus','metadata_keywords','metadata_harvest',
-            'takeoffline','takeonline','showenv','toggledebug','ping','domconf',
-            'uniquecodes','diskusage','coursecatalog');
+            'checksums','clusterstatus','certstatus','metadata_keywords',
+            'metadata_harvest','takeoffline','takeonline','showenv','toggledebug',
+            'ping','domconf','uniquecodes','diskusage','coursecatalog');
 }
 
 sub defaults_javascript {
@@ -12040,8 +12229,8 @@
                 );
     my @prefixes = ('remote','hosted','spares');
     my @lcversions = &Apache::lonnet::all_loncaparevs();
-    my (%by_ip,%by_location, at intdoms);
-    &build_location_hashes(\@intdoms,\%by_ip,\%by_location);
+    my (%by_ip,%by_location, at intdoms, at instdoms);
+    &build_location_hashes(\@intdoms,\%by_ip,\%by_location,\@instdoms);
     my @locations = sort(keys(%by_location));
     my (%defaultshash,%changes);
     foreach my $prefix (@prefixes) {
@@ -12355,6 +12544,171 @@
     return $resulttext;
 }
 
+sub modify_ssl {
+    my ($dom,$lastactref,%domconfig) = @_;
+    my (%by_ip,%by_location, at intdoms, at instdoms);
+    &build_location_hashes(\@intdoms,\%by_ip,\%by_location,\@instdoms);
+    my @locations = sort(keys(%by_location));
+    my %servers = &Apache::lonnet::internet_dom_servers($dom);
+    my (%defaultshash,%changes);
+    my $action = 'ssl';
+    my @prefixes = ('connect','replication');
+    foreach my $prefix (@prefixes) {
+        $defaultshash{$action}{$prefix} = {};
+    }
+    my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1);
+    my $resulttext;
+    my %iphost = &Apache::lonnet::get_iphost();
+    my @reptypes = ('certreq','nocertreq');
+    my @connecttypes = ('dom','intdom','other');
+    my %types = (
+                  connect      => \@connecttypes,
+                  replication  => \@reptypes,
+                );
+    my $action = 'ssl';
+    foreach my $prefix (sort(keys(%types))) {
+        foreach my $type (@{$types{$prefix}}) {
+            if ($prefix eq 'connect') {
+                my $value = 'yes';
+                if ($env{'form.'.$prefix.'_'.$type} =~ /^(no|req)$/) {
+                    $value = $env{'form.'.$prefix.'_'.$type};
+                }
+                if (ref($domconfig{$action}{$prefix}) eq 'HASH') {
+                    if ($domconfig{$action}{$prefix}{$type} ne '') {
+                        if ($value ne $domconfig{$action}{$prefix}{$type}) {
+                            $changes{$prefix}{$type} = 1;
+                        }
+                        $defaultshash{$action}{$prefix}{$type} = $value;
+                    } else {
+                        $defaultshash{$action}{$prefix}{$type} = $value;
+                        $changes{$prefix}{$type} = 1;
+                    }
+                } else {
+                    $defaultshash{$action}{$prefix}{$type} = $value;
+                    $changes{$prefix}{$type} = 1;
+                }
+                if (($type eq 'dom') && (keys(%servers) == 1)) {
+                    delete($changes{$prefix}{$type});
+                } elsif (($type eq 'intdom') && (@instdoms == 1)) {
+                    delete($changes{$prefix}{$type});
+                } elsif (($type eq 'other') && (keys(%by_location) == 0)) { 
+                    delete($changes{$prefix}{$type});
+                }
+            } elsif ($prefix eq 'replication') {
+                if (@locations > 0) {
+                    my $inuse = $env{'form.'.$prefix.'_'.$type.'_inuse'};
+                    my @vals = &Apache::loncommon::get_env_multiple('form.'.$prefix.'_'.$type);
+                    my @okvals;
+                    foreach my $val (@vals) {
+                        if ($val =~ /:/) {
+                            my @items = split(/:/,$val);
+                            foreach my $item (@items) {
+                                if (ref($by_location{$item}) eq 'ARRAY') {
+                                    push(@okvals,$item);
+                                }
+                            }
+                        } else {
+                            if (ref($by_location{$val}) eq 'ARRAY') {
+                                push(@okvals,$val);
+                            }
+                        }
+                    }
+                    @okvals = sort(@okvals);
+                    if (ref($domconfig{$action}) eq 'HASH') {
+                        if (ref($domconfig{$action}{$prefix}) eq 'HASH') {
+                            if (ref($domconfig{$action}{$prefix}{$type}) eq 'ARRAY') {
+                                if ($inuse == 0) {
+                                    $changes{$prefix}{$type} = 1;
+                                } else {
+                                    $defaultshash{$action}{$prefix}{$type} = \@okvals;
+                                    my @changed = &Apache::loncommon::compare_arrays($domconfig{$action}{$prefix}{$type},$defaultshash{$action}{$prefix}{$type});
+                                    if (@changed > 0) {
+                                        $changes{$prefix}{$type} = 1;
+                                    }
+                                }
+                            } else {
+                                if ($inuse == 1) {
+                                    $defaultshash{$action}{$prefix}{$type} = \@okvals;
+                                    $changes{$prefix}{$type} = 1;
+                                }
+                            }
+                        } else {
+                            if ($inuse == 1) {
+                                $defaultshash{$action}{$prefix}{$type} = \@okvals;
+                                $changes{$prefix}{$type} = 1;
+                            }
+                        }
+                    } else {
+                        if ($inuse == 1) {
+                            $defaultshash{$action}{$prefix}{$type} = \@okvals;
+                            $changes{$prefix}{$type} = 1;
+                        }
+                    }
+                }
+            }
+        }
+    }
+    my $nochgmsg = &mt('No changes made to LON-CAPA SSL settings');
+    if (keys(%changes) > 0) {
+        my $putresult = &Apache::lonnet::put_dom('configuration',\%defaultshash,
+                                                 $dom);
+        if ($putresult eq 'ok') {
+            if (ref($defaultshash{$action}) eq 'HASH') {
+                if (ref($defaultshash{$action}{'replication'}) eq 'HASH') {
+                    $domdefaults{'replication'} = $defaultshash{$action}{'replication'};
+                }
+                if (ref($defaultshash{$action}{'connect'}) eq 'HASH') {
+                    $domdefaults{'connect'} = $domconfig{$action}{'connect'};
+                }
+            }
+            my $cachetime = 24*60*60;
+            &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime);
+            if (ref($lastactref) eq 'HASH') {
+                $lastactref->{'domdefaults'} = 1;
+            }
+            if (keys(%changes) > 0) {
+                my %titles = &ssl_titles();
+                $resulttext = &mt('Changes made:').'<ul>';
+                foreach my $prefix (@prefixes) {
+                    if (ref($changes{$prefix}) eq 'HASH') {
+                        $resulttext .= '<li>'.$titles{$prefix}.'<ul>';
+                        foreach my $type (@{$types{$prefix}}) {
+                            if (defined($changes{$prefix}{$type})) {
+                                my $newvalue;
+                                if (ref($defaultshash{$action}) eq 'HASH') {
+                                    if (ref($defaultshash{$action}{$prefix})) {
+                                        if ($prefix eq 'connect') {
+                                            $newvalue = $titles{$defaultshash{$action}{$prefix}{$type}};
+                                        } elsif (ref($defaultshash{$action}{$prefix}{$type}) eq 'ARRAY') {
+                                            if (@{$defaultshash{$action}{$prefix}{$type}} > 0) {
+                                                $newvalue = join(', ',@{$defaultshash{$action}{$prefix}{$type}});
+                                            }
+                                        }
+                                    }
+                                    if ($newvalue eq '') {
+                                        $resulttext .= '<li>'.&mt('[_1] set to: none',$titles{$type}).'</li>';
+                                    } else {
+                                        $resulttext .= '<li>'.&mt('[_1] set to: [_2].',$titles{$type},$newvalue).'</li>';
+                                    }
+                                }
+                            }
+                        }
+                        $resulttext .= '</ul>';
+                    }
+                }
+            } else {
+                $resulttext = $nochgmsg;
+            }
+        } else {
+            $resulttext = '<span class="LC_error">'.
+                          &mt('An error occurred: [_1]',$putresult).'</span>';
+        }
+    } else {
+        $resulttext = $nochgmsg;
+    }
+    return $resulttext;
+}
+
 sub modify_loadbalancing {
     my ($dom,%domconfig) = @_;
     my $primary_id = &Apache::lonnet::domain($dom,'primary');
Index: loncom/interface/domainstatus.pm
diff -u loncom/interface/domainstatus.pm:1.7 loncom/interface/domainstatus.pm:1.8
--- loncom/interface/domainstatus.pm:1.7	Mon Mar 17 02:36:56 2014
+++ loncom/interface/domainstatus.pm	Mon Jul 25 19:50:30 2016
@@ -2,7 +2,7 @@
 # Generate a menu page containing links to server status pages accessible
 # to user. 
 #
-# $Id: domainstatus.pm,v 1.7 2014/03/17 02:36:56 raeburn Exp $
+# $Id: domainstatus.pm,v 1.8 2016/07/25 19:50:30 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -145,6 +145,15 @@
                  linktitle => $titles->{'clusterstatus'},
              },
              {
+                 linktext => $linknames->{'certstatus'},
+                 icon => '',
+                 alttext => '',
+                 #help => 'Domain_Coordination_Apache_Status',
+                 url => '/cgi-bin/loncertstatus.pl',
+                 permission => $candisplay->{'certstatus'},
+                 linktitle => $titles->{'certstatus'},
+             },
+             {
                  linktext => $linknames->{'codeversions'},
                  icon => '',
                  alttext => '',
@@ -249,6 +258,7 @@
                     'lonstatus' => 'Connection Status',
                     'server-status' => 'Apache Server Status',
                     'clusterstatus' => 'Domain Status',
+                    'certstatus' => 'LON-CAPA SSL Certificates Status',   
                     'codeversions' => 'LON-CAPA Modules',
                     'checksums'    => 'Check for LON-CAPA Module changes',   
                     'diskusage'    => 'Display quotas and usage for Course/Community Content',
Index: loncom/interface/lonconfigsettings.pm
diff -u loncom/interface/lonconfigsettings.pm:1.31 loncom/interface/lonconfigsettings.pm:1.32
--- loncom/interface/lonconfigsettings.pm:1.31	Tue Jun  9 21:22:56 2015
+++ loncom/interface/lonconfigsettings.pm	Mon Jul 25 19:50:30 2016
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # Handler to set domain-wide configuration settings
 #
-# $Id: lonconfigsettings.pm,v 1.31 2015/06/09 21:22:56 damieng Exp $
+# $Id: lonconfigsettings.pm,v 1.32 2016/07/25 19:50:30 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -338,7 +338,7 @@
     if ((ref($prefs_order) eq 'ARRAY') && (ref($prefs) eq 'HASH') && (ref($values) eq 'HASH')) { 
         if (@actions > 0) {
             my $rowsum = 0;
-            my (%output,%rowtotal, at items);
+            my (%output,%rowtotal, at items,$got_check_uncheck);
             foreach my $item (@{$prefs_order}) {
                 if (grep(/^\Q$item\E$/, at actions)) {
                     push(@items,$item);
@@ -347,12 +347,15 @@
                         if (ref($values) eq 'HASH') { 
                             $settings = $values->{$item};
                         }
-                        if ($item eq 'usersessions') {
-                            $r->print('<script type="text/javascript">'."\n".
-                                      '// <![CDATA['."\n".
-                                      &Apache::loncommon::check_uncheck_jscript()."\n".
-                                      '// ]]>'."\n".
-                                      '</script>'."\n");
+                        if (($item eq 'usersessions') || ($item eq 'ssl')) {
+                            unless ($got_check_uncheck) {
+                                $r->print('<script type="text/javascript">'."\n".
+                                          '// <![CDATA['."\n".
+                                          &Apache::loncommon::check_uncheck_jscript()."\n".
+                                          '// ]]>'."\n".
+                                          '</script>'."\n");
+                                $got_check_uncheck = 1;
+                            }
                         } elsif ($item eq 'selfcreation') {
                             if (ref($values) eq 'HASH') {
                                 $settings = $values->{'usercreation'};
Index: loncom/lonnet/perl/lonnet.pm
diff -u loncom/lonnet/perl/lonnet.pm:1.1314 loncom/lonnet/perl/lonnet.pm:1.1315
--- loncom/lonnet/perl/lonnet.pm:1.1314	Sun Jul 24 14:35:29 2016
+++ loncom/lonnet/perl/lonnet.pm	Mon Jul 25 19:50:44 2016
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # TCP networking package
 #
-# $Id: lonnet.pm,v 1.1314 2016/07/24 14:35:29 raeburn Exp $
+# $Id: lonnet.pm,v 1.1315 2016/07/25 19:50:44 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -229,6 +229,46 @@
     return;
 }
 
+sub get_servercerts_info {
+    my ($lonhost,$context) = @_;
+    my ($rep,$uselocal);
+    if (grep { $_ eq $lonhost } &current_machine_ids()) {
+        $uselocal = 1;
+    }
+    if (($context ne 'cgi') || $uselocal) {
+        my $distro = (split(/\:/,&get_server_distarch($lonhost)))[0];
+        if ($distro =~ /^(?:centos|redhat|scientific)(\d+)$/) {
+            if ($1 < 6) {
+                $uselocal = 0;
+            }
+        }
+    }
+    if ($uselocal) {
+        $rep = LONCAPA::Lond::server_certs(\%perlvar);
+    } else {
+        $rep=&reply('servercerts',$lonhost);
+    }
+    my ($result,%returnhash);
+    if (defined($lonhost)) {
+        if (!defined(&hostname($lonhost))) {
+            return;
+        }
+    }
+    if (($rep=~/^(refused|rejected|error)/) || ($rep eq 'con_lost') ||
+        ($rep eq 'unknown_cmd')) {
+        $result = $rep;
+    } else {
+        $result = 'ok';
+        my @pairs=split(/\&/,$rep);
+        foreach my $item (@pairs) {
+            my ($key,$value)=split(/=/,$item,2);
+            my $what = &unescape($key);
+            $returnhash{$what}=&thaw_unescape($value);
+        }
+    }
+    return ($result,\%returnhash);
+}
+
 sub get_server_loncaparev {
     my ($dom,$lonhost,$ignore_cache,$caller) = @_;
     if (defined($lonhost)) {
@@ -2202,7 +2242,7 @@
                                   'requestcourses','inststatus',
                                   'coursedefaults','usersessions',
                                   'requestauthor','selfenrollment',
-                                  'coursecategories','autoenroll'],$domain);
+                                  'coursecategories','ssl','autoenroll'],$domain);
     my @coursetypes = ('official','unofficial','community','textbook','placement');
     if (ref($domconfig{'defaults'}) eq 'HASH') {
         $domdefaults{'lang_def'} = $domconfig{'defaults'}{'lang_def'}; 
@@ -2328,6 +2368,14 @@
             $domdefaults{'catunauth'} = $domconfig{'coursecategories'}{'unauth'};
         }
     }
+    if (ref($domconfig{'ssl'}) eq 'HASH') {
+        if (ref($domconfig{'ssl'}{'replication'}) eq 'HASH') {
+            $domdefaults{'replication'} = $domconfig{'ssl'}{'replication'};
+        }
+        if (ref($domconfig{'ssl'}{'connect'}) eq 'HASH') {
+            $domdefaults{'connect'} = $domconfig{'ssl'}{'connect'};
+        }
+    }
     if (ref($domconfig{'autoenroll'}) eq 'HASH') {
         $domdefaults{'autofailsafe'} = $domconfig{'autoenroll'}{'autofailsafe'};
     }

Index: loncom/cgi/loncertstatus.pl
+++ loncom/cgi/loncertstatus.pl
#!/usr/bin/perl
$|=1;
# Displays status of LON-CAPA SSL certificates in /home/httpd/lonCerts
# on domain's servers.
#
# $Id: loncertstatus.pl,v 1.1 2016/07/25 19:50:01 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
# This file is part of the LearningOnline Network with CAPA (LON-CAPA).
#
# LON-CAPA is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# LON-CAPA is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with LON-CAPA; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# /home/httpd/html/adm/gpl.txt
#
# http://www.lon-capa.org/
#

use strict;

use lib '/home/httpd/lib/perl/';
use Apache::lonlocal();
use Apache::lonhtmlcommon;
use LONCAPA::Configuration();
use LONCAPA::loncgi();
use LONCAPA::lonauthcgi();
use LONCAPA::SSL();

my $perlvar=&LONCAPA::Configuration::read_conf('loncapa.conf');
my $lonhost;
if (ref($perlvar) eq 'HASH') {
    my @reqd = qw(lonnetPrivateKey lonnetCertificate lonnetHostnameCertificate 
                  lonnetCertificateAuthority lonCertificateDirectory);
    $lonhost = $perlvar->{'lonHostID'};
    foreach my $key (keys(%{$perlvar})) {
        unless (grep(/^\Q$key\E$/, at reqd)) {
            delete($perlvar->{$key});
        }
    }
}

print &LONCAPA::loncgi::cgi_header('text/html',1);
&main($lonhost);

sub main {
    my ($lonhost) = @_;
    my $machine_dom = &Apache::lonnet::host_domain($lonhost);
    if (&LONCAPA::lonauthcgi::check_ipbased_access('certstatus')) {
        &LONCAPA::loncgi::check_cookie_and_load_env();
    } else {
        if (!&LONCAPA::loncgi::check_cookie_and_load_env()) {
            &Apache::lonlocal::get_language_handle();
            print(&LONCAPA::loncgi::missing_cookie_msg());
            return;
        }
        if (!&LONCAPA::lonauthcgi::can_view('certstatus')) {
            &Apache::lonlocal::get_language_handle();
            print(&LONCAPA::lonauthcgi::unauthorized_msg('certstatus'));
            return;
        }
    }
    my %domservers = &Apache::lonnet::get_servers($machine_dom);
    &Apache::lonlocal::get_language_handle();
    &Apache::lonhtmlcommon::add_breadcrumb(
        {href=>"/cgi-bin/loncertstatus.pl",
         text=>"LON-CAPA Certificate Status"});
    print &Apache::loncommon::start_page('LON-CAPA SSL Certificates Status').
          &Apache::lonhtmlcommon::breadcrumbs('SSL Certificates');
    print &LONCAPA::SSL::print_certstatus(\%domservers,'web','cgi');
    print &Apache::loncommon::end_page();
    return;
}


Index: loncom/configuration/SSL.pm
+++ loncom/configuration/SSL.pm
# The LearningOnline Network with CAPA
# Checksum installed LON-CAPA modules and some configuration files
#
# $Id: SSL.pm,v 1.1 2016/07/25 19:50:16 raeburn Exp $
#
# The LearningOnline Network with CAPA
#
# Copyright Michigan State University Board of Trustees
#
# This file is part of the LearningOnline Network with CAPA (LON-CAPA).
#
# LON-CAPA is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# LON-CAPA is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with LON-CAPA; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# /home/httpd/html/adm/gpl.txt
#
# http://www.lon-capa.org/
#

package LONCAPA::SSL;
use strict;
use lib '/home/httpd/lib/perl/';
use Apache::lonlocal();
use Apache::lonnet();
use Apache::loncommon();
use Apache::lonhtmlcommon();
use LONCAPA; 

sub print_certstatus {
    my ($servers,$target,$context) = @_;
    return unless (ref($servers) eq 'HASH');
    my $message;
    my %lt = &Apache::lonlocal::texthash (
                 'file'     => 'File',
                 'avai'     => 'Available',
                 'yes'      => 'Yes',
                 'no'       => 'No',
                 'cn'       => 'Common Name',
                 'start'    => 'Valid From',
                 'end'      => 'Valid To',
                 'alg'      => 'Signature Algorithm',
                 'size'     => 'Public Key Size',
                 'status'   => 'Status',
                 'email'    => 'E-mail',
                 'key'      => 'Private Key',
                 'host'     => 'Connections Certificate',
                 'hostname' => 'Replication Certificate',
                 'ca'       => 'LON-CAPA CA Certificate',
    );
    my @files = qw(key host hostname ca);
    my @fields = qw(status cn start end alg size email);
    foreach my $server (sort(keys(%{$servers}))) {
        my $hostname = &Apache::lonnet::hostname($server);
        my ($result,$hashref) = &Apache::lonnet::get_servercerts_info($server,$context);
        if ($result eq 'ok' && ref($hashref) eq 'HASH') {
            if ($target eq 'web') {
                $message .= "<fieldset><legend>$hostname ($server)</legend>".
                            &Apache::loncommon::start_data_table().
                            &Apache::loncommon::start_data_table_header_row()."\n";
                foreach my $item ('file','avai', at fields) {
                    $message .= '<th>'.$lt{$item}.'</th>';
                }
                $message .= &Apache::loncommon::end_data_table_header_row()."\n";
            } else {
                $message .= $server.':';
            }
            foreach my $file (@files) {
                if ($target eq 'web') {
                    $message .= &Apache::loncommon::start_data_table_row()."\n".
                                '<td>'.$lt{$file}.'</td>';
                } else {
                    $message .= $file.'=';
                }
                if (ref($hashref->{$file}) eq 'HASH') {
                    if ($target eq 'web') {
                        $message .= '<td>'.$lt{'yes'}.'</td>';
                    } else {
                        $message .= $lt{'yes'}.',';
                    }
                    foreach my $item (@fields) {
                        my $display = $hashref->{$file}->{$item};
                        if ($target eq 'web') {
                            if ($item eq 'status') {
                                $display = &Apache::lonhtmlcommon::confirm_success($display);
                            }
                            $message .= "<td>$display</td>";
                        } else {
                            $message .= "$display,";
                        }
                    }
                } else {
                    if ($target eq 'web') {
                        $message .= '<td>'.$lt{'no'}.'<td>';
                    } else {
                        $message .= $lt{'no'}.',';
                    }
                    foreach my $item (@fields) {
                        if ($target eq 'web') {
                            $message .= '<td> </td>';
                        } else {
                            $message .= ',';
                        }
                    }
                    if ($target eq 'web') {
                        $message .= &Apache::loncommon::end_data_table_row()."\n";
                    } else {
                        $message =~ s/,$//;
                        $message .= '&';
                    }
                }
            }
            if ($target eq 'web') {
                $message .= &Apache::loncommon::end_data_table().'</fieldset>';
            } else {
                $message =~ s/\&$//;
            }
            $message .= "\n";
        } else {
            if ($target eq 'web') {
                $message .= "$server error\n";
            } else {
                $message .= "$server error\n";
            }
        }
    }
    return $message;
}

1;



More information about the LON-CAPA-cvs mailing list