--- loncom/lond 2021/02/10 15:05:51 1.489.2.40 +++ loncom/lond 2022/02/20 20:58:26 1.489.2.43.2.3 @@ -2,7 +2,7 @@ # The LearningOnline Network # lond "LON Daemon" Server (port "LOND" 5663) # -# $Id: lond,v 1.489.2.40 2021/02/10 15:05:51 raeburn Exp $ +# $Id: lond,v 1.489.2.43.2.3 2022/02/20 20:58:26 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -63,7 +63,7 @@ my $DEBUG = 0; # Non zero to ena my $status=''; my $lastlog=''; -my $VERSION='$Revision: 1.489.2.40 $'; #' stupid emacs +my $VERSION='$Revision: 1.489.2.43.2.3 $'; #' stupid emacs my $remoteVERSION; my $currenthostid="default"; my $currentdomainid; @@ -1795,7 +1795,7 @@ sub read_lonnet_global { } if ($what eq 'perlvar') { if (!exists($packagevars{$what}{'lonBalancer'})) { - if ($dist =~ /^(centos|rhes|fedora|scientific|oracle)/) { + if ($dist =~ /^(centos|rhes|fedora|scientific|oracle|rocky|alma)/) { my $othervarref=LONCAPA::Configuration::read_conf('httpd.conf'); if (ref($othervarref) eq 'HASH') { $items->{'lonBalancer'} = $othervarref->{'lonBalancer'}; @@ -3536,6 +3536,47 @@ sub dump_with_regexp { } ®ister_handler("dump", \&dump_with_regexp, 0, 1, 0); +# +# Process the encrypted dump request. Original call should +# be from lonnet::dump() with seventh arg ($encrypt) set to +# 1, to ensure that both request and response are encrypted. +# +# Parameters: +# $cmd - Command keyword of request (edump). +# $tail - Tail of the command. +# See &dump_with_regexp for more +# information about this. +# $client - File open on the client. +# Returns: +# 1 - Continue processing +# 0 - server should exit. +# + +sub encrypted_dump_with_regexp { + my ($cmd, $tail, $client) = @_; + my $res = LONCAPA::Lond::dump_with_regexp($tail, $clientversion); + + if ($res =~ /^error:/) { + Failure($client, \$res, "$cmd:$tail"); + } else { + if ($cipher) { + my $cmdlength=length($res); + $res.=" "; + my $encres=''; + for (my $encidx=0;$encidx<=$cmdlength;$encidx+=8) { + $encres.= unpack("H16", + $cipher->encrypt(substr($res, + $encidx, + 8))); + } + &Reply( $client,"enc:$cmdlength:$encres\n","$cmd:$tail"); + } else { + &Failure( $client, "error:no_key\n","$cmd:$tail"); + } + } +} +®ister_handler("edump", \&encrypted_dump_with_regexp, 0, 1, 0); + # Store a set of key=value pairs associated with a versioned name. # # Parameters: @@ -4682,17 +4723,119 @@ sub get_domain_handler { my $userinput = "$cmd:$tail"; my ($udom,$namespace,$what)=split(/:/,$tail,3); - my $res = LONCAPA::Lond::get_dom($userinput); - if ($res =~ /^error:/) { - &Failure($client, \$res, $userinput); + if (($namespace =~ /^enc/) || ($namespace eq 'private')) { + &Failure( $client, "refused\n", $userinput); } else { - &Reply($client, \$res, $userinput); + my $res = LONCAPA::Lond::get_dom($userinput); + if ($res =~ /^error:/) { + &Failure($client, \$res, $userinput); + } else { + &Reply($client, \$res, $userinput); + } } return 1; } ®ister_handler("getdom", \&get_domain_handler, 0, 1, 0); +sub encrypted_get_domain_handler { + my ($cmd, $tail, $client) = @_; + + my $userinput = "$cmd:$tail"; + + my ($udom,$namespace,$what) = split(/:/,$tail,3); + if ($namespace eq 'private') { + &Failure( $client, "refused\n", $userinput); + } else { + my $res = LONCAPA::Lond::get_dom($userinput); + if ($res =~ /^error:/) { + &Failure($client, \$res, $userinput); + } else { + if ($cipher) { + my $cmdlength=length($res); + $res.=" "; + my $encres=''; + for (my $encidx=0;$encidx<=$cmdlength;$encidx+=8) { + $encres.= unpack("H16", + $cipher->encrypt(substr($res, + $encidx, + 8))); + } + &Reply( $client,"enc:$cmdlength:$encres\n",$userinput); + } else { + &Failure( $client, "error:no_key\n",$userinput); + } + } + } + return 1; +} +®ister_handler("egetdom", \&encrypted_get_domain_handler, 1, 1, 0); + +# +# Encrypted get from the namespace database file at the domain level. +# This function retrieves a keyed item from a specific named database in the +# domain directory. +# +# Parameters: +# $cmd - Command request keyword (lti). +# $tail - Tail of the command. This is a colon-separated list +# consisting of the domain, coursenum, if for LTI- +# enabled deep-linking to course content using +# link protection configured within a course, +# context (=deeplink) if for LTI-enabled deep-linking +# to course content using LTI Provider settings +# configured within a course's domain, the (escaped) +# launch URL, the (escaped) method (typically POST), +# and a frozen hash of the LTI launch parameters +# from the LTI payload. +# $client - File descriptor open on the client. +# Returns: +# 1 - Continue processing. +# 0 - Exit. +# Side effects: +# The reply will contain an LTI itemID, if the signed LTI payload +# could be verified using the consumer key and the shared secret +# available for that key (for the itemID) for either the course or domain, +# depending on values for cnum and context. The reply is encrypted before +# being written to $client. +# +sub lti_handler { + my ($cmd, $tail, $client) = @_; + + my $userinput = "$cmd:$tail"; + + my ($cdom,$cnum,$context,$escurl,$escmethod,$items) = split(/:/,$tail); + my $url = &unescape($escurl); + my $method = &unescape($escmethod); + my $params = &Apache::lonnet::thaw_unescape($items); + my $res; + if ($cnum ne '') { + $res = &LONCAPA::Lond::crslti_itemid($cdom,$cnum,$url,$method,$params,$perlvar{'lonVersion'}); + } else { + $res = &LONCAPA::Lond::domlti_itemid($cdom,$context,$url,$method,$params,$perlvar{'lonVersion'}); + } + if ($res =~ /^error:/) { + &Failure($client, \$res, $userinput); + } else { + if ($cipher) { + my $cmdlength=length($res); + $res.=" "; + my $encres=''; + for (my $encidx=0;$encidx<=$cmdlength;$encidx+=8) { + $encres.= unpack("H16", + $cipher->encrypt(substr($res, + $encidx, + 8))); + } + &Reply( $client,"enc:$cmdlength:$encres\n",$userinput); + } else { + &Failure( $client, "error:no_key\n",$userinput); + } + } + return 1; +} +®ister_handler("lti", \<i_handler, 1, 1, 0); + # # Puts an id to a domains id database. # @@ -5118,15 +5261,23 @@ sub tmp_put_handler { } my ($id,$store); $tmpsnum++; - if (($context eq 'resetpw') || ($context eq 'createaccount')) { - $id = &md5_hex(&md5_hex(time.{}.rand().$$)); + my $numtries = 0; + my $execdir=$perlvar{'lonDaemons'}; + if (($context eq 'resetpw') || ($context eq 'createaccount') || + ($context eq 'sso') || ($context eq 'link') || ($context eq 'retry')) { + $id = &md5_hex(&md5_hex(time.{}.rand().$$.$tmpsnum)); + while ((-e "$execdir/tmp/$id.tmp") && ($numtries <10)) { + undef($id); + $id = &md5_hex(&md5_hex(time.{}.rand().$$.$tmpsnum)); + $numtries ++; + } } else { $id = $$.'_'.$clientip.'_'.$tmpsnum; } $id=~s/\W/\_/g; $record=~s/\n//g; - my $execdir=$perlvar{'lonDaemons'}; - if ($store=IO::File->new(">$execdir/tmp/$id.tmp")) { + if (($id ne '') && + ($store=IO::File->new(">$execdir/tmp/$id.tmp"))) { print $store $record; close $store; &Reply($client, \$id, $userinput); @@ -5501,6 +5652,39 @@ sub validate_instcode_handler { } ®ister_handler("autovalidateinstcode", \&validate_instcode_handler, 0, 1, 0); +# +# Validate co-owner for cross-listed institutional code and +# institutional course code itself used for a LON-CAPA course. +# +# Formal Parameters: +# $cmd - The command request that got us dispatched. +# $tail - The tail of the command. In this case, +# this is a colon separated string containing: +# $dom - Course's LON-CAPA domain +# $instcode - Institutional course code for the course +# $inst_xlist - Institutional course Id for the crosslisting +# $coowner - Username of co-owner +# (values for all but $dom have been escaped). +# +# $client - Socket open on the client. +# Returns: +# 1 - Indicating processing should continue. +# +sub validate_instcrosslist_handler { + my ($cmd, $tail, $client) = @_; + my $userinput = "$cmd:$tail"; + my ($dom,$instcode,$inst_xlist,$coowner) = split(/:/,$tail); + $instcode = &unescape($instcode); + $inst_xlist = &unescape($inst_xlist); + $coowner = &unescape($coowner); + my $outcome = &localenroll::validate_crosslist_access($dom,$instcode, + $inst_xlist,$coowner); + &Reply($client, \$outcome, $userinput); + + return 1; +} +®ister_handler("autovalidateinstcrosslist", \&validate_instcrosslist_handler, 0, 1, 0); + # Get the official sections for which auto-enrollment is possible. # Since the admin people won't know about 'unofficial sections' # we cannot auto-enroll on them. @@ -5625,6 +5809,62 @@ sub validate_class_access_handler { ®ister_handler("autovalidateclass_sec", \&validate_class_access_handler, 0, 1, 0); # +# Modify institutional sections (using customized &instsec_reformat() +# routine in localenroll.pm), to either clutter or declutter, for +# purposes of ensuring an institutional course section (string) can +# be unambiguously separated into institutional course and section. +# +# Formal Parameters: +# $cmd - The command request that got us dispatched. +# $tail - The tail of the command. In this case this is a colon separated +# set of values that will be split into: +# $cdom - The LON-CAPA domain of the course. +# $action - Either: clutter or declutter +# clutter adds character(s) to eliminate ambiguity +# declutter removes the added characters (e.g., for +# display of the institutional course section string. +# $info - A frozen hash in which keys are: +# LON-CAPA course number:Institutional course code +# and values are a reference to an array of the +# items to modify -- either institutional sections, +# or institutional course sections (for crosslistings). +# $client - The socket open on the client. +# Returns: +# 1 - continue processing. +# + +sub instsec_reformat_handler { + my ($cmd, $tail, $client) = @_; + my $userinput = "$cmd:$tail"; + my ($cdom,$action,$info) = split(/:/,$tail); + my $instsecref = &Apache::lonnet::thaw_unescape($info); + my ($outcome,$result); + eval { + local($SIG{__DIE__})='DEFAULT'; + $outcome=&localenroll::instsec_reformat($cdom,$action,$instsecref); + if ($outcome eq 'ok') { + if (ref($instsecref) eq 'HASH') { + foreach my $key (keys(%{$instsecref})) { + $result .= &escape($key).'='.&Apache::lonnet::freeze_escape($instsecref->{$key}).'&'; + } + $result =~ s/\&$//; + } + } + }; + if (!$@) { + if ($outcome eq 'ok') { + &Reply( $client, \$result, $userinput); + } else { + &Reply($client,\$outcome, $userinput); + } + } else { + &Failure($client,"unknown_cmd\n",$userinput); + } + return 1; +} +®ister_handler("autoinstsecreformat",\&instsec_reformat_handler, 0, 1, 0); + +# # Validate course owner or co-owners(s) access to enrollment data for all sections # and crosslistings for a particular course. # @@ -7103,7 +7343,7 @@ sub make_new_child { &Authen::Krb5::init_context(); my $no_ets; - if ($dist =~ /^(?:centos|rhes|scientific|oracle)(\d+)$/) { + if ($dist =~ /^(?:centos|rhes|scientific|oracle|rocky|alma)(\d+)/) { if ($1 >= 7) { $no_ets = 1; } @@ -7269,7 +7509,7 @@ sub make_new_child { Debug("Main: Got $user_input\n"); $keep_going = &process_request($user_input); alarm(0); - &status('Listening to '.$clientname." ($keymode)"); + &status('Listening to '.$clientname." ($keymode)"); } # --------------------------------------------- client unknown or fishy, refuse @@ -7285,8 +7525,8 @@ sub make_new_child { &logthis("CRITICAL: " ."Disconnect from $clientip ($clientname)"); - - + + # this exit is VERY important, otherwise the child will become # a producer of more and more children, forking yourself into # process death. 500 Internal Server Error

Internal Server Error

The server encountered an internal error or misconfiguration and was unable to complete your request.

Please contact the server administrator at root@localhost to inform them of the time this error occurred, and the actions you performed just before this error.

More information about this error may be available in the server error log.