Annotation of loncom/lonssl.pm, revision 1.24
1.2 foxr 1: #
1.24 ! raeburn 2: # $Id: lonssl.pm,v 1.23 2018/12/11 15:15:26 raeburn Exp $
1.2 foxr 3: #
4: # Copyright Michigan State University Board of Trustees
5: #
6: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
7: #
8: # LON-CAPA is free software; you can redistribute it and/or modify
9: # it under the terms of the GNU General Public License as published by
10: # the Free Software Foundation; either version 2 of the License, or
11: # (at your option) any later version.
12: #
13: # LON-CAPA is distributed in the hope that it will be useful,
14: # but WITHOUT ANY WARRANTY; without even the implied warranty of
15: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16: # GNU General Public License for more details.
17: #
18: # You should have received a copy of the GNU General Public License
19: # along with LON-CAPA; if not, write to the Free Software
20: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21: #
22: # /home/httpd/html/adm/gpl.txt
23: #
24: # http://www.lon-capa.org/
25: #
1.6 foxr 26: package lonssl;
1.2 foxr 27: # lonssl.pm
28: # This file contains common functions used by lond and lonc when
29: # negotiating the exchange of the session encryption key via an
30: # SSL tunnel.
31: # See the POD sections and function documentation for more information.
32: #
33:
34: use strict;
1.4 foxr 35:
1.6 foxr 36: # CPAN/Standard modules:
1.4 foxr 37:
1.2 foxr 38: use IO::Socket::INET;
39: use IO::Socket::SSL;
1.14 raeburn 40: use Net::SSLeay;
1.2 foxr 41:
1.8 foxr 42: use Fcntl;
43: use POSIX;
44:
1.4 foxr 45: # Loncapa modules:
46:
47: use LONCAPA::Configuration;
48:
49: # Global storage:
50:
1.5 foxr 51: my $perlvar; # this refers to the apache perlsetvar
52: # variable hash.
1.4 foxr 53:
54: my $pathsep = "/"; # We're on unix after all.
55:
1.9 foxr 56: my $DEBUG = 0; # Set to non zero to enable debug output.
57:
1.4 foxr 58:
59: # Initialization code:
60:
61: $perlvar = LONCAPA::Configuration::read_conf('loncapa.conf');
62:
63:
1.8 foxr 64: my $lasterror="";
65:
66:
1.9 foxr 67:
1.8 foxr 68: sub LastError {
69: return $lasterror;
70: }
71:
1.9 foxr 72: sub Debug {
73: my $msg = shift;
74: if ($DEBUG) {
75: print STDERR $msg;
76: }
77: }
78:
1.8 foxr 79: #-------------------------------------------------------------------------
80: # Name SetFdBlocking -
81: # Turn blocking mode on on the file handle. This is required for
82: # SSL key negotiation.
83: #
84: # Parameters:
85: # Handle - Reference to the handle to modify.
86: # Returns:
87: # prior flag settings.
88: #
89: sub SetFdBlocking {
1.9 foxr 90: Debug("SetFdBlocking called \n");
1.8 foxr 91: my $Handle = shift;
92:
93:
94:
95: my $flags = fcntl($Handle, F_GETFL, 0);
96: if(!$flags) {
1.9 foxr 97: Debug("SetBLocking fcntl get faild $!\n");
1.8 foxr 98: }
99: my $newflags = $flags & (~ O_NONBLOCK); # Turn off O_NONBLOCK...
100: if(!fcntl($Handle, F_SETFL, $newflags)) {
1.9 foxr 101: Debug("Can't set non block mode $!\n");
1.8 foxr 102: }
103: return $flags;
104: }
1.2 foxr 105:
106: #--------------------------------------------------------------------------
107: #
108: # Name PromoteClientSocket
109: # Description Given an ordinary IO::Socket::INET Creates an SSL socket
110: # for a client that is connected to the same server.
111: # Parameters Name Type Description
112: # Socket IO::Socket::INET Original ordinary socket.
113: # CACert string Full path name to the certificate
114: # authority certificate file.
1.21 raeburn 115: # MyCert string Full path name to the certificate
1.2 foxr 116: # issued to this host.
1.21 raeburn 117: # KeyFile string Full pathname to the host's private
1.2 foxr 118: # key file for the certificate.
1.21 raeburn 119: # peer string lonid of remote LON-CAPA server
120: # peerdef string default lonHostID of remote server
1.17 raeburn 121: # CRLFile Full path name to the certificate
122: # revocation list file for the cluster
123: # to which server belongs (optional)
1.24 ! raeburn 124: # serverversion LON-CAPA version running on remote
! 125: # server.
1.17 raeburn 126:
1.2 foxr 127: # Returns
128: # - Reference to an SSL socket on success
129: # - undef on failure. Reason for failure can be interrogated from
130: # IO::Socket::SSL
1.8 foxr 131: # Side effects: socket is left in blocking mode!!
132: #
1.2 foxr 133:
134: sub PromoteClientSocket {
1.6 foxr 135: my ($PlaintextSocket,
136: $CACert,
137: $MyCert,
1.16 raeburn 138: $KeyFile,
1.17 raeburn 139: $peer,
1.21 raeburn 140: $peerdef,
1.24 ! raeburn 141: $CRLFile,
! 142: $serverversion) = @_;
1.18 raeburn 143:
1.24 ! raeburn 144: Debug("Client promotion using key: $KeyFile, Cert: $MyCert, CA: $CACert, CRL: $CRLFile, Remote Host: $peer, RemoteDefHost: $peerdef, RemoteLCVersion: $serverversion\n");
1.8 foxr 145:
1.3 albertel 146: # To create the ssl socket we need to duplicate the existing
147: # socket. Otherwise closing the ssl socket will close the plaintext socket
1.8 foxr 148: # too. We also must flip into blocking mode for the duration of the
149: # ssl negotiation phase.. the caller will have to flip to non block if
150: # that's what they want
151:
152: my $oldflags = SetFdBlocking($PlaintextSocket);
153: my $dupfno = fcntl($PlaintextSocket, F_DUPFD, 0);
1.9 foxr 154: Debug("Client promotion got dup = $dupfno\n");
1.8 foxr 155:
1.14 raeburn 156: # Starting with IO::Socket::SSL rev. 1.79, carp warns that a verify
157: # mode of SSL_VERIFY_NONE should be explicitly set for client, if
158: # verification is not to be used, and SSL_verify_mode is not set.
159: # Starting with rev. 1.95, the default became SSL_VERIFY_PEER which
1.16 raeburn 160: # prevents an SSL connection to lond unless SSL_verifycn_name is set
161: # to the lonHostID of the remote host, (and the remote certificate has
1.17 raeburn 162: # the remote lonHostID as CN, and has been signed by the LON-CAPA CA.
1.16 raeburn 163: # Set SSL_verify_mode to Net::SSLeay::VERIFY_PEER() instead of to
164: # SSL_VERIFY_PEER for compatibility with IO::Socket::SSL rev. 1.01
1.14 raeburn 165: # used by CentOS/RHEL/Scientific Linux 5).
1.21 raeburn 166:
167: my $verify_cn = $peerdef;
168: if ($verify_cn eq '') {
169: $verify_cn = $peer;
170: }
171:
1.17 raeburn 172: my %sslargs = (SSL_use_cert => 1,
173: SSL_key_file => $KeyFile,
174: SSL_cert_file => $MyCert,
1.24 ! raeburn 175: SSL_ca_file => $CACert);
! 176: my ($major,$minor) = split(/\./,$serverversion);
! 177: if (($major < 2) || ($major == 2 && $minor < 12)) {
! 178: $sslargs{SSL_verify_mode} = Net::SSLeay::VERIFY_NONE();
! 179: } else {
! 180: $sslargs{SSL_verifycn_scheme} = 'http',
! 181: $sslargs{SSL_verifycn_name} = $verify_cn,
! 182: $sslargs{SSL_verify_mode} = Net::SSLeay::VERIFY_PEER();
! 183: if (($CRLFile ne '') && (-e $CRLFile)) {
! 184: $sslargs{SSL_check_crl} = 1;
! 185: $sslargs{SSL_crl_file} = $CRLFile;
! 186: }
1.17 raeburn 187: }
1.24 ! raeburn 188: # Uncomment next two $IO::Socket::SSL::DEBUG lines, for debugging
! 189: # $IO::Socket::SSL::DEBUG = 0; # Set to integer >0 and <4
! 190: # # to write debugging to lonc_errors
1.17 raeburn 191: my $client = IO::Socket::SSL->new_from_fd($dupfno,%sslargs);
1.24 ! raeburn 192: # $IO::Socket::SSL::DEBUG = 0; # Do not change
1.8 foxr 193: if(!$client) {
1.17 raeburn 194: if ($IO::Socket::SSL::SSL_ERROR == -1) {
195: $lasterror = -1;
196: }
1.8 foxr 197: return undef;
198: }
1.3 albertel 199: return $client; # Undef if the client negotiation fails.
1.2 foxr 200: }
201:
202: #----------------------------------------------------------------------
203: # Name PromoteServerSocket
204: # Description Given an ordinary IO::Socket::INET Creates an SSL socket
1.16 raeburn 205: # for a server that is connected to the same client.
1.2 foxr 206: # Parameters Name Type Description
207: # Socket IO::Socket::INET Original ordinary socket.
208: # CACert string Full path name to the certificate
209: # authority certificate file.
210: # MyCert string Full path name to the certificate
211: # issued to this host.
212: # KeyFile string Full pathname to the host's private
213: # key file for the certificate.
1.17 raeburn 214: # peer string lonHostID of remote LON-CAPA client
215: # CRLFile Full path name to the certificate
216: # revocation list file for the cluster
217: # to which server belongs (optional)
1.18 raeburn 218: # clientversion LON-CAPA version running on remote
219: # client
1.2 foxr 220: # Returns
221: # - Reference to an SSL socket on success
222: # - undef on failure. Reason for failure can be interrogated from
223: # IO::Socket::SSL
1.8 foxr 224: # Side Effects:
225: # Socket is left in blocking mode!!!
226: #
1.3 albertel 227: sub PromoteServerSocket {
1.6 foxr 228: my ($PlaintextSocket,
229: $CACert,
230: $MyCert,
1.16 raeburn 231: $KeyFile,
1.17 raeburn 232: $peer,
1.18 raeburn 233: $CRLFile,
234: $clientversion) = @_;
1.3 albertel 235:
236: # To create the ssl socket we need to duplicate the existing
237: # socket. Otherwise closing the ssl socket will close the plaintext socket
238: # too:
239:
1.9 foxr 240: Debug("Server promotion: Key = $KeyFile, Cert $MyCert CA $CACert\n");
1.8 foxr 241:
242: my $oldflags = SetFdBlocking($PlaintextSocket);
243: my $dupfno = fcntl($PlaintextSocket, F_DUPFD, 0);
244: if (!$dupfno) {
1.9 foxr 245: Debug("dup failed: $!\n");
1.8 foxr 246: }
1.9 foxr 247: Debug(" Fileno = $dupfno\n");
1.17 raeburn 248: my %sslargs = (SSL_server => 1, # Server role.
249: SSL_use_cert => 1,
250: SSL_key_file => $KeyFile,
251: SSL_cert_file => $MyCert,
1.18 raeburn 252: SSL_ca_file => $CACert);
253: my ($major,$minor) = split(/\./,$clientversion);
254: if (($major < 2) || ($major == 2 && $minor < 12)) {
255: $sslargs{SSL_verify_mode} = Net::SSLeay::VERIFY_NONE();
256: } else {
1.24 ! raeburn 257: $sslargs{SSL_verifycn_scheme} = 'http';
1.18 raeburn 258: $sslargs{SSL_verifycn_name} = $peer;
259: $sslargs{SSL_verify_mode} = Net::SSLeay::VERIFY_PEER();
260: if (($CRLFile ne '') && (-e $CRLFile)) {
261: $sslargs{SSL_check_crl} = 1;
1.20 raeburn 262: $sslargs{SSL_crl_file} = $CRLFile;
1.18 raeburn 263: }
1.17 raeburn 264: }
1.24 ! raeburn 265: # Uncomment next two $IO::Socket::SSL::DEBUG lines, for debugging
! 266: # $IO::Socket::SSL::DEBUG = 0; # Set to integer >0 and <4
! 267: # # to write debugging to lond_errors
1.17 raeburn 268: my $client = IO::Socket::SSL->new_from_fd($dupfno,%sslargs);
1.24 ! raeburn 269: # $IO::Socket::SSL::DEBUG = 0; # Do not change
1.8 foxr 270: if(!$client) {
1.17 raeburn 271: if ($IO::Socket::SSL::SSL_ERROR == -1) {
272: $lasterror = -1;
273: }
1.8 foxr 274: return undef;
275: }
1.3 albertel 276: return $client;
1.2 foxr 277: }
278:
279: #-------------------------------------------------------------------------
280: #
281: # Name: Close
282: # Description: Properly closes an ssl client or ssl server socket in
283: # a way that keeps the parent socket open.
284: # Parameters: Name Type Description
285: # Socket IO::Socket::SSL SSL Socket gotten from either
286: # PromoteClientSocket or
287: # PromoteServerSocket
288: # Returns:
289: # NONE
290: #
291: sub Close {
1.3 albertel 292: my $Socket = shift;
1.4 foxr 293:
1.3 albertel 294: $Socket->close(SSL_no_shutdown =>1); # Otherwise the parent socket
295: # gets torn down.
1.2 foxr 296: }
1.4 foxr 297: #---------------------------------------------------------------------------
298: #
299: # Name GetPeerCertificate
300: # Description Inquires about the certificate of the peer of a connection.
301: # Parameters Name Type Description
302: # SSLSocket IO::Socket::SSL SSL tunnel socket open on
303: # the peer.
304: # Returns
305: # A two element list. The first element of the list is the name of
306: # the certificate authority. The second element of the list is the name
307: # of the owner of the certificate.
308: sub GetPeerCertificate {
1.6 foxr 309: my $SSLSocket = shift;
310:
311: my $CertOwner = $SSLSocket->peer_certificate("owner");
312: my $CertCA = $SSLSocket->peer_certificate("authority");
313:
1.8 foxr 314: return ($CertCA, $CertOwner);
1.4 foxr 315: }
316: #----------------------------------------------------------------------------
317: #
318: # Name CertificateFile
319: # Description Locate the certificate files for this host.
320: # Returns
321: # Returns a two element array. The first element contains the name of
322: # the certificate file for this host. The second element contains the name
323: # of the certificate file for the CA that granted the certificate. If
324: # either file cannot be located, returns undef.
325: #
326: sub CertificateFile {
327:
1.6 foxr 328: # I need some perl variables from the configuration file for this:
329:
330: my $CertificateDir = $perlvar->{lonCertificateDirectory};
331: my $CaFilename = $perlvar->{lonnetCertificateAuthority};
332: my $CertFilename = $perlvar->{lonnetCertificate};
333:
334: # Ensure the existence of these variables:
335:
336: if((!$CertificateDir) || (!$CaFilename) || (!$CertFilename)) {
1.8 foxr 337: $lasterror = "Missing info: dir: $CertificateDir CA: $CaFilename "
338: ."Cert: $CertFilename";
1.6 foxr 339: return undef;
340: }
341:
342: # Build the actual filenames and check for their existence and
343: # readability.
344:
1.10 albertel 345: $CaFilename = $CertificateDir.$pathsep.$CaFilename;
346: $CertFilename = $CertificateDir.$pathsep.$CertFilename;
1.6 foxr 347:
348: if((! -r $CaFilename) || (! -r $CertFilename)) {
1.8 foxr 349: $lasterror = "CA file $CaFilename or Cert File: $CertFilename "
350: ."not readable";
1.6 foxr 351: return undef;
352: }
353:
354: # Everything works fine!!
355:
1.8 foxr 356: return ($CaFilename, $CertFilename);
1.4 foxr 357:
358: }
359: #------------------------------------------------------------------------
360: #
361: # Name KeyFile
362: # Description
363: # Returns the name of the private key file of the current host.
364: # Returns
365: # Returns the name of the key file or undef if the file cannot
366: # be found.
367: #
368: sub KeyFile {
369:
1.6 foxr 370: # I need some perl variables from the configuration file for this:
371:
372: my $CertificateDir = $perlvar->{lonCertificateDirectory};
373: my $KeyFilename = $perlvar->{lonnetPrivateKey};
374:
375: # Ensure the variables exist:
376:
377: if((!$CertificateDir) || (!$KeyFilename)) {
1.8 foxr 378: $lasterror = "Missing parameter dir: $CertificateDir "
379: ."key: $KeyFilename";
1.6 foxr 380: return undef;
381: }
382:
383: # Build the actual filename and ensure that it not only exists but
384: # is also readable:
385:
1.10 albertel 386: $KeyFilename = $CertificateDir.$pathsep.$KeyFilename;
1.6 foxr 387: if(! (-r $KeyFilename)) {
1.8 foxr 388: $lasterror = "Unreadable key file $KeyFilename";
1.6 foxr 389: return undef;
390: }
391:
392: return $KeyFilename;
1.4 foxr 393: }
1.2 foxr 394:
1.17 raeburn 395: sub CRLFile {
396:
397: # I need some perl variables from the configuration file for this:
398:
399: my $CertificateDir = $perlvar->{lonCertificateDirectory};
400: my $CRLFilename = $perlvar->{lonnetCertRevocationList};
401:
402: # Ensure the variables exist:
403:
404: if((!$CertificateDir) || (!$CRLFilename)) {
405: $lasterror = "Missing parameter dir: $CertificateDir "
406: ."CRL file: $CRLFilename";
407: return undef;
408: }
409:
410: # Build the actual filename and ensure that it not only exists but
411: # is also readable:
412:
413: $CRLFilename = $CertificateDir.$pathsep.$CRLFilename;
414: if(! (-r $CRLFilename)) {
415: $lasterror = "Unreadable key file $CRLFilename";
416: return undef;
417: }
418:
419: return $CRLFilename;
420: }
421:
422: sub BadCertDir {
423: my $SocketDir = $perlvar->{lonSockDir};
424: if (-d "$SocketDir/nosslverify/") {
425: return "$SocketDir/nosslverify"
426: }
427: }
428:
429: sub has_badcert_file {
430: my ($client) = @_;
431: my $SocketDir = $perlvar->{lonSockDir};
432: if (-e "$SocketDir/nosslverify/$client") {
433: return 1;
434: }
435: return;
436: }
437:
1.15 raeburn 438: sub Read_Connect_Config {
1.23 raeburn 439: my ($secureconf,$perlvarref,$crlcheckedref) = @_;
1.19 raeburn 440: return unless (ref($secureconf) eq 'HASH');
1.15 raeburn 441:
442: unless (ref($perlvarref) eq 'HASH') {
443: $perlvarref = $perlvar;
444: }
1.17 raeburn 445:
1.22 raeburn 446: # Clear hash of clients in lond for which Certificate Revocation List checked
447: if (ref($crlcheckedref) eq 'HASH') {
448: foreach my $key (keys(%{$crlcheckedref})) {
449: delete($crlcheckedref->{$key});
450: }
451: }
1.15 raeburn 452: # Clean out the old table first.
453: foreach my $key (keys(%{$secureconf})) {
454: delete($secureconf->{$key});
455: }
456:
457: my $result;
458: my $tablename = $perlvarref->{'lonTabDir'}."/connectionrules.tab";
1.20 raeburn 459: if (open(my $fh,'<',$tablename)) {
1.15 raeburn 460: while (my $line = <$fh>) {
461: chomp($line);
462: my ($name,$value) = split(/=/,$line);
463: if ($value =~ /^(?:no|yes|req)$/) {
464: if ($name =~ /^conn(to|from)_(dom|intdom|other)$/) {
465: $secureconf->{'conn'.$1}{$2} = $value;
466: }
467: }
468: }
469: close($fh);
470: return 'ok';
471: }
472: return;
473: }
474:
475: sub Read_Host_Types {
476: my ($hosttypes,$perlvarref) = @_;
477: return unless (ref($hosttypes) eq 'HASH');
478:
479: unless (ref($perlvarref) eq 'HASH') {
480: $perlvarref = $perlvar;
481: }
1.20 raeburn 482:
1.15 raeburn 483: # Clean out the old table first.
484: foreach my $key (keys(%{$hosttypes})) {
485: delete($hosttypes->{$key});
486: }
487:
488: my $result;
489: my $tablename = $perlvarref->{'lonTabDir'}."/hosttypes.tab";
1.20 raeburn 490: if (open(my $fh,'<',$tablename)) {
1.15 raeburn 491: while (my $line = <$fh>) {
492: chomp($line);
493: my ($name,$value) = split(/:/,$line);
494: if (($name ne '') && ($value =~ /^(dom|intdom|other)$/)) {
495: $hosttypes->{$name} = $value;
496: }
497: }
498: close($fh);
499: return 'ok';
500: }
501: return;
502: }
503:
1.4 foxr 504: 1;
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>