--- loncom/lond 2018/10/29 02:57:30 1.550 +++ loncom/lond 2019/04/26 20:22:10 1.558 @@ -2,7 +2,7 @@ # The LearningOnline Network # lond "LON Daemon" Server (port "LOND" 5663) # -# $Id: lond,v 1.550 2018/10/29 02:57:30 raeburn Exp $ +# $Id: lond,v 1.558 2019/04/26 20:22:10 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -65,7 +65,7 @@ my $DEBUG = 0; # Non zero to ena my $status=''; my $lastlog=''; -my $VERSION='$Revision: 1.550 $'; #' stupid emacs +my $VERSION='$Revision: 1.558 $'; #' stupid emacs my $remoteVERSION; my $currenthostid="default"; my $currentdomainid; @@ -80,11 +80,12 @@ my $clientsamedom; # LonCAP # and client. my $clientsameinst; # LonCAPA "internet domain" same for # this host and client. -my $clientremoteok; # Client allowed to host domain's users. - # (version constraints ignored), not set - # if this host and client share "internet domain". -my %clientprohibited; # Actions prohibited on client; - +my $clientremoteok; # Current domain permits hosting on client + # (not set if host and client share "internet domain"). + # Values are 0 or 1; 1 if allowed. +my %clientprohibited; # Commands from client prohibited for domain's + # users. + my $server; my $keymode; @@ -176,6 +177,7 @@ my @installerrors = ("ok", # shared ("Access to other domain's content by this domain") # enroll ("Enrollment in this domain's courses by others") # coaurem ("Co-author roles for this domain's users elsewhere") +# othcoau ("Co-author roles in this domain for others") # domroles ("Domain roles in this domain assignable to others") # catalog ("Course Catalog for this domain displayed elsewhere") # reqcrs ("Requests for creation of courses in this domain by others") @@ -224,6 +226,7 @@ my %trust = ( dcmaildump => {remote => 1, domroles => 1}, dcmailput => {remote => 1, domroles => 1}, del => {remote => 1, domroles => 1, enroll => 1, content => 1}, + delbalcookie => {institutiononly => 1}, deldom => {remote => 1, domroles => 1}, # not currently used devalidatecache => {institutiononly => 1}, domroleput => {remote => 1, enroll => 1}, @@ -234,7 +237,7 @@ my %trust = ( edit => {institutiononly => 1}, #not used currently eget => {remote => 1, domroles => 1, enroll => 1}, #not used currently egetdom => {remote => 1, domroles => 1, enroll => 1, }, - ekey => {}, #not used currently + ekey => {anywhere => 1}, exit => {anywhere => 1}, fetchuserfile => {remote => 1, enroll => 1}, get => {remote => 1, domroles => 1, enroll => 1}, @@ -299,9 +302,9 @@ my %trust = ( store => {remote => 1, enroll => 1, reqcrs => 1,}, studentphoto => {remote => 1, enroll => 1}, sub => {content => 1,}, - tmpdel => {anywhere => 1}, - tmpget => {anywhere => 1}, - tmpput => {anywhere => 1}, + tmpdel => {institutiononly => 1}, + tmpget => {institutiononly => 1}, + tmpput => {remote => 1, othcoau => 1}, tokenauthuserfile => {anywhere => 1}, unsub => {content => 1,}, update => {shared => 1}, @@ -831,8 +834,8 @@ sub PushFile { # hosts.tab ($filename eq host). # domain.tab ($filename eq domain). # dns_hosts.tab ($filename eq dns_host). - # dns_domain.tab ($filename eq dns_domain). - # loncapaCAcrl.pem ($filename eq loncapaCAcrl); + # dns_domain.tab ($filename eq dns_domain). + # loncapaCAcrl.pem ($filename eq loncapaCAcrl). # Construct the destination filename or reject the request. # # lonManage is supposed to ensure this, however this session could be @@ -2345,12 +2348,84 @@ sub change_password_handler { } if($validated) { my $realpasswd = &get_auth_type($udom, $uname); # Defined since authd. - my ($howpwd,$contentpwd)=split(/:/,$realpasswd); + my $notunique; if ($howpwd eq 'internal') { &Debug("internal auth"); my $ncpass = &hash_passwd($udom,$npass); - if(&rewrite_password_file($udom, $uname, "internal:$ncpass")) { + my (undef,$method,@rest) = split(/!/,$contentpwd); + if ($method eq 'bcrypt') { + my %passwdconf = &Apache::lonnet::get_passwdconf($udom); + if (($passwdconf{'numsaved'}) && ($passwdconf{'numsaved'} =~ /^\d+$/)) { + my @oldpasswds; + my $userpath = &propath($udom,$uname); + my $fullpath = $userpath.'/oldpasswds'; + if (-d $userpath) { + my @oldfiles; + if (-e $fullpath) { + if (opendir(my $dir,$fullpath)) { + (@oldfiles) = grep(/^\d+$/,readdir($dir)); + closedir($dir); + } + if (@oldfiles) { + @oldfiles = sort { $b <=> $a } (@oldfiles); + my $numremoved = 0; + for (my $i=0; $i<@oldfiles; $i++) { + if ($i>=$passwdconf{'numsaved'}) { + if (-f "$fullpath/$oldfiles[$i]") { + if (unlink("$fullpath/$oldfiles[$i]")) { + $numremoved ++; + } + } + } elsif (open(my $fh,'<',"$fullpath/$oldfiles[$i]")) { + while (my $line = <$fh>) { + push(@oldpasswds,$line); + } + close($fh); + } + } + if ($numremoved) { + &logthis("unlinked $numremoved old password files for $uname:$udom"); + } + } + } + push(@oldpasswds,$contentpwd); + foreach my $item (@oldpasswds) { + my (undef,$method,@rest) = split(/!/,$item); + if ($method eq 'bcrypt') { + my $result = &hash_passwd($udom,$npass,@rest); + if ($result eq $item) { + $notunique = 1; + last; + } + } + } + unless ($notunique) { + unless (-e $fullpath) { + if (&mkpath("$fullpath/")) { + chmod(0700,$fullpath); + } + } + if (-d $fullpath) { + my $now = time; + if (open(my $fh,'>',"$fullpath/$now")) { + print $fh $contentpwd; + close($fh); + chmod(0400,"$fullpath/$now"); + } + } + } + } + } + } + if ($notunique) { + my $msg="Result of password change for $uname:$udom - password matches one used before"; + if ($lonhost) { + $msg .= " - request originated from: $lonhost"; + } + &logthis($msg); + &Reply($client, "prioruse\n", $userinput); + } elsif (&rewrite_password_file($udom, $uname, "internal:$ncpass")) { my $msg="Result of password change for $uname: pwchange_success"; if ($lonhost) { $msg .= " - request originated from: $lonhost"; @@ -2378,7 +2453,6 @@ sub change_password_handler { # &Failure( $client, "auth_mode_error\n", $userinput); } - } else { if ($failure eq '') { $failure = 'non_authorized'; @@ -5521,6 +5595,58 @@ sub tmp_del_handler { ®ister_handler("tmpdel", \&tmp_del_handler, 0, 1, 0); # +# Process the delbalcookie command. This command deletes a balancer +# cookie in the lonBalancedir directory created by switchserver +# +# Parameters: +# $cmd - Command that got us here. +# $cookie - Cookie to be deleted. +# $client - socket open on the client process. +# +# Returns: +# 1 - Indicating processing should continue. +# Side Effects: +# A cookie file is deleted from the lonBalancedir directory +# A reply is sent to the client. +sub del_balcookie_handler { + my ($cmd, $cookie, $client) = @_; + + my $userinput= "$cmd:$cookie"; + + chomp($cookie); + my $deleted = ''; + if ($cookie =~ /^$LONCAPA::match_domain\_$LONCAPA::match_username\_[a-f0-9]{32}$/) { + my $execdir=$perlvar{'lonBalanceDir'}; + if (-e "$execdir/$cookie.id") { + if (open(my $fh,'<',"$execdir/$cookie.id")) { + my $dodelete; + while (my $line = <$fh>) { + chomp($line); + if ($line eq $clientname) { + $dodelete = 1; + last; + } + } + close($fh); + if ($dodelete) { + if (unlink("$execdir/$cookie.id")) { + $deleted = 1; + } + } + } + } + } + if ($deleted) { + &Reply($client, "ok\n", $userinput); + } else { + &Failure( $client, "error: ".($!+0)."Unlinking cookie file Failed ". + "while attempting delbalcookie\n", $userinput); + } + return 1; +} +®ister_handler("delbalcookie", \&del_balcookie_handler, 0, 1, 0); + +# # Processes the setannounce command. This command # creates a file named announce.txt in the top directory of # the documentn root and sets its contents. The announce.txt file is @@ -7045,7 +7171,7 @@ sub UpdateHosts { my %oldconf = %secureconf; my %connchange; - if (lonssl::Read_Connect_Config(\%secureconf,\%crlchecked,\%perlvar) eq 'ok') { + if (lonssl::Read_Connect_Config(\%secureconf,\%perlvar,\%crlchecked) eq 'ok') { logthis(' Reloaded SSL connection rules and cleared CRL checking history '); } else { logthis(' Failed to reload SSL connection rules and clear CRL checking history '); @@ -7327,7 +7453,7 @@ if ($arch eq 'unknown') { chomp($arch); } -unless (lonssl::Read_Connect_Config(\%secureconf,\%crlchecked,\%perlvar) eq 'ok') { +unless (lonssl::Read_Connect_Config(\%secureconf,\%perlvar,\%crlchecked) eq 'ok') { &logthis('No connectionrules table. Will fallback to loncapa.conf'); } @@ -7461,7 +7587,7 @@ sub make_new_child { $ConnectionType = "manager"; $clientname = $managers{$outsideip}; } - my ($clientok,$clientinfoset); + my $clientok; if ($clientrec || $ismanager) { &status("Waiting for init from $clientip $clientname"); @@ -7562,7 +7688,6 @@ sub make_new_child { } } else { - $clientinfoset = &set_client_info(); my $ok = InsecureConnection($client); if($ok) { $clientok = 1; @@ -7600,34 +7725,7 @@ sub make_new_child { # ------------------------------------------------------------ Process requests my $keep_going = 1; my $user_input; - unless ($clientinfoset) { - $clientinfoset = &set_client_info(); - } - $clientremoteok = 0; - unless ($clientsameinst) { - $clientremoteok = 1; - my $defdom = &Apache::lonnet::host_domain($perlvar{'lonHostID'}); - %clientprohibited = &get_prohibited($defdom); - if ($clientintdom) { - my $remsessconf = &get_usersession_config($defdom,'remotesession'); - if (ref($remsessconf) eq 'HASH') { - if (ref($remsessconf->{'remote'}) eq 'HASH') { - if (ref($remsessconf->{'remote'}->{'excludedomain'}) eq 'ARRAY') { - if (grep(/^\Q$clientintdom\E$/,@{$remsessconf->{'remote'}->{'excludedomain'}})) { - $clientremoteok = 0; - } - } - if (ref($remsessconf->{'remote'}->{'includedomain'}) eq 'ARRAY') { - if (grep(/^\Q$clientintdom\E$/,@{$remsessconf->{'remote'}->{'includedomain'}})) { - $clientremoteok = 1; - } else { - $clientremoteok = 0; - } - } - } - } - } - } + while(($user_input = get_request) && $keep_going) { alarm(120); Debug("Main: Got $user_input\n"); @@ -7660,22 +7758,30 @@ sub make_new_child { # # Used to determine if a particular client is from the same domain -# as the current server, or from the same internet domain. +# as the current server, or from the same internet domain, and +# also if the client can host sessions for the domain's users. +# A hash is populated with keys set to commands sent by the client +# which may not be executed for this domain. # # Optional input -- the client to check for domain and internet domain. # If not specified, defaults to the package variable: $clientname # # If called in array context will not set package variables, but will # instead return an array of two values - (a) true if client is in the -# same domain as the server, and (b) true if client is in the same internet -# domain. +# same domain as the server, and (b) true if client is in the same +# internet domain. # # If called in scalar context, sets package variables for current client: # -# $clienthomedom - LonCAPA domain of homeID for client. -# $clientsamedom - LonCAPA domain same for this host and client. -# $clientintdom - LonCAPA "internet domain" for client. -# $clientsameinst - LonCAPA "internet domain" same for this host & client. +# $clienthomedom - LonCAPA domain of homeID for client. +# $clientsamedom - LonCAPA domain same for this host and client. +# $clientintdom - LonCAPA "internet domain" for client. +# $clientsameinst - LonCAPA "internet domain" same for this host & client. +# $clientremoteok - If current domain permits hosting on this client: 1 +# %clientprohibited - Commands prohibited for domain's users for this client. +# +# if the host and client have the same "internet domain", then the value +# of $clientremoteok is not used, and no commands are prohibited. # # returns 1 to indicate package variables have been set for current client. # @@ -7687,7 +7793,7 @@ sub set_client_info { my $clientserverhomeID = &Apache::lonnet::get_server_homeID($clienthost); my $homedom = &Apache::lonnet::host_domain($clientserverhomeID); my $samedom = 0; - if ($perlvar{'lonDefDom'} eq $homedom) { + if ($perlvar{'lonDefDomain'} eq $homedom) { $samedom = 1; } my $intdom = &Apache::lonnet::internet_dom($clientserverhomeID); @@ -7707,6 +7813,13 @@ sub set_client_info { $clientsamedom = $samedom; $clientintdom = $intdom; $clientsameinst = $sameinst; + if ($clientsameinst) { + undef($clientremoteok); + undef(%clientprohibited); + } else { + $clientremoteok = &get_remote_hostable($currentdomainid); + %clientprohibited = &get_prohibited($currentdomainid); + } return 1; } } @@ -8454,6 +8567,7 @@ sub sethost { eq &Apache::lonnet::get_host_ip($hostid)) { $currenthostid =$hostid; $currentdomainid=&Apache::lonnet::host_domain($hostid); + &set_client_info(); # &logthis("Setting hostid to $hostid, and domain to $currentdomainid"); } else { &logthis("Requested host id $hostid not an alias of ". @@ -8530,6 +8644,32 @@ sub get_prohibited { return %prohibited; } +sub get_remote_hostable { + my ($dom) = @_; + my $result; + if ($clientintdom) { + $result = 1; + my $remsessconf = &get_usersession_config($dom,'remotesession'); + if (ref($remsessconf) eq 'HASH') { + if (ref($remsessconf->{'remote'}) eq 'HASH') { + if (ref($remsessconf->{'remote'}->{'excludedomain'}) eq 'ARRAY') { + if (grep(/^\Q$clientintdom\E$/,@{$remsessconf->{'remote'}->{'excludedomain'}})) { + $result = 0; + } + } + if (ref($remsessconf->{'remote'}->{'includedomain'}) eq 'ARRAY') { + if (grep(/^\Q$clientintdom\E$/,@{$remsessconf->{'remote'}->{'includedomain'}})) { + $result = 1; + } else { + $result = 0; + } + } + } + } + } + return $result; +} + sub distro_and_arch { return $dist.':'.$arch; }