--- loncom/lond 2016/05/08 19:05:10 1.520
+++ loncom/lond 2018/12/03 13:20:21 1.553
@@ -2,7 +2,7 @@
# The LearningOnline Network
# lond "LON Daemon" Server (port "LOND" 5663)
#
-# $Id: lond,v 1.520 2016/05/08 19:05:10 raeburn Exp $
+# $Id: lond,v 1.553 2018/12/03 13:20:21 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -15,7 +15,6 @@
#
# 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.
#
@@ -36,12 +35,13 @@ use LONCAPA;
use LONCAPA::Configuration;
use LONCAPA::Lond;
+use Socket;
use IO::Socket;
use IO::File;
#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 Mail::Send;
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.520 $'; #' stupid emacs
+my $VERSION='$Revision: 1.553 $'; #' stupid emacs
my $remoteVERSION;
my $currenthostid="default";
my $currentdomainid;
@@ -74,8 +75,16 @@ my $clientip; # IP address of client.
my $clientname; # LonCAPA name of client.
my $clientversion; # LonCAPA version running on client.
my $clienthomedom; # LonCAPA domain of homeID for client.
- # primary library server.
-
+my $clientintdom; # LonCAPA "internet domain" for client.
+my $clientsamedom; # LonCAPA domain same for this host
+ # 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 $server;
my $keymode;
@@ -96,6 +105,13 @@ my %managers; # Ip -> manager names
my %perlvar; # Will have the apache conf defined perl vars.
+my %secureconf; # Will have requirements for security
+ # of lond connections
+
+my %crlchecked; # Will contain clients for which the client's SSL
+ # has been checked against the cluster's Certificate
+ # Revocation List.
+
my $dist;
#
@@ -145,6 +161,159 @@ my @installerrors = ("ok",
);
#
+# The %trust hash classifies commands according to type of trust
+# required for execution of the command.
+#
+# When clients from a different institution request execution of a
+# particular command, the trust settings for that institution set
+# for this domain (or default domain for a multi-domain server) will
+# be checked to see if running the command is allowed.
+#
+# Trust types which depend on the "Trust" domain configuration
+# for the machine's default domain are:
+#
+# content ("Access to this domain's content by others")
+# 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")
+# msg ("Users in other domains can send messages to this domain")
+#
+# Trust type which depends on the User Session Hosting (remote)
+# domain configuration for machine's default domain is: "remote".
+#
+# Trust types which depend on contents of manager.tab in
+# /home/httpd/lonTabs is: "manageronly".
+#
+# Trust type which requires client to share the same LON-CAPA
+# "internet domain" (i.e., same institution as this server) is:
+# "institutiononly".
+#
+
+my %trust = (
+ auth => {remote => 1},
+ autocreatepassword => {remote => 1},
+ autocrsreqchecks => {remote => 1, reqcrs => 1},
+ autocrsrequpdate => {remote => 1},
+ autocrsreqvalidation => {remote => 1},
+ autogetsections => {remote => 1},
+ autoinstcodedefaults => {remote => 1, catalog => 1},
+ autoinstcodeformat => {remote => 1, catalog => 1},
+ autonewcourse => {remote => 1, reqcrs => 1},
+ autophotocheck => {remote => 1, enroll => 1},
+ autophotochoice => {remote => 1},
+ autophotopermission => {remote => 1, enroll => 1},
+ autopossibleinstcodes => {remote => 1, reqcrs => 1},
+ autoretrieve => {remote => 1, enroll => 1, catalog => 1},
+ autorun => {remote => 1, enroll => 1, reqcrs => 1},
+ autovalidateclass_sec => {catalog => 1},
+ autovalidatecourse => {remote => 1, enroll => 1},
+ autovalidateinstcode => {domroles => 1, remote => 1, enroll => 1},
+ changeuserauth => {remote => 1, domroles => 1},
+ chatretr => {remote => 1, enroll => 1},
+ chatsend => {remote => 1, enroll => 1},
+ courseiddump => {remote => 1, domroles => 1, enroll => 1},
+ courseidput => {remote => 1, domroles => 1, enroll => 1},
+ courseidputhash => {remote => 1, domroles => 1, enroll => 1},
+ courselastaccess => {remote => 1, domroles => 1, enroll => 1},
+ currentauth => {remote => 1, domroles => 1, enroll => 1},
+ currentdump => {remote => 1, enroll => 1},
+ currentversion => {remote=> 1, content => 1},
+ 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},
+ domrolesdump => {remote => 1, catalog => 1},
+ du => {remote => 1, enroll => 1},
+ du2 => {remote => 1, enroll => 1},
+ dump => {remote => 1, enroll => 1, domroles => 1},
+ edit => {institutiononly => 1}, #not used currently
+ eget => {remote => 1, domroles => 1, enroll => 1}, #not used currently
+ egetdom => {remote => 1, domroles => 1, enroll => 1, },
+ ekey => {anywhere => 1},
+ exit => {anywhere => 1},
+ fetchuserfile => {remote => 1, enroll => 1},
+ get => {remote => 1, domroles => 1, enroll => 1},
+ getdom => {anywhere => 1},
+ home => {anywhere => 1},
+ iddel => {remote => 1, enroll => 1},
+ idget => {remote => 1, enroll => 1},
+ idput => {remote => 1, domroles => 1, enroll => 1},
+ inc => {remote => 1, enroll => 1},
+ init => {anywhere => 1},
+ inst_usertypes => {remote => 1, domroles => 1, enroll => 1},
+ instemailrules => {remote => 1, domroles => 1},
+ instidrulecheck => {remote => 1, domroles => 1,},
+ instidrules => {remote => 1, domroles => 1,},
+ instrulecheck => {remote => 1, enroll => 1, reqcrs => 1, domroles => 1},
+ instselfcreatecheck => {institutiononly => 1},
+ instuserrules => {remote => 1, enroll => 1, reqcrs => 1, domroles => 1},
+ keys => {remote => 1,},
+ load => {anywhere => 1},
+ log => {anywhere => 1},
+ ls => {remote => 1, enroll => 1, content => 1,},
+ ls2 => {remote => 1, enroll => 1, content => 1,},
+ ls3 => {remote => 1, enroll => 1, content => 1,},
+ makeuser => {remote => 1, enroll => 1, domroles => 1,},
+ mkdiruserfile => {remote => 1, enroll => 1,},
+ newput => {remote => 1, enroll => 1, reqcrs => 1, domroles => 1,},
+ passwd => {remote => 1},
+ ping => {anywhere => 1},
+ pong => {anywhere => 1},
+ pushfile => {manageronly => 1},
+ put => {remote => 1, enroll => 1, domroles => 1, msg => 1, content => 1, shared => 1},
+ putdom => {remote => 1, domroles => 1,},
+ putstore => {remote => 1, enroll => 1},
+ queryreply => {anywhere => 1},
+ querysend => {anywhere => 1},
+ querysend_activitylog => {remote => 1},
+ querysend_allusers => {remote => 1, domroles => 1},
+ querysend_courselog => {remote => 1},
+ querysend_fetchenrollment => {remote => 1},
+ querysend_getinstuser => {remote => 1},
+ querysend_getmultinstusers => {remote => 1},
+ querysend_instdirsearch => {remote => 1, domroles => 1, coaurem => 1},
+ querysend_institutionalphotos => {remote => 1},
+ querysend_portfolio_metadata => {remote => 1, content => 1},
+ querysend_userlog => {remote => 1, domroles => 1},
+ querysend_usersearch => {remote => 1, enroll => 1, coaurem => 1},
+ quit => {anywhere => 1},
+ readlonnetglobal => {institutiononly => 1},
+ reinit => {manageronly => 1}, #not used currently
+ removeuserfile => {remote => 1, enroll => 1},
+ renameuserfile => {remote => 1,},
+ restore => {remote => 1, enroll => 1, reqcrs => 1,},
+ rolesdel => {remote => 1, enroll => 1, domroles => 1, coaurem => 1},
+ rolesput => {remote => 1, enroll => 1, domroles => 1, coaurem => 1},
+ servercerts => {institutiononly => 1},
+ serverdistarch => {anywhere => 1},
+ serverhomeID => {anywhere => 1},
+ serverloncaparev => {anywhere => 1},
+ servertimezone => {remote => 1, enroll => 1},
+ setannounce => {remote => 1, domroles => 1},
+ sethost => {anywhere => 1},
+ store => {remote => 1, enroll => 1, reqcrs => 1,},
+ studentphoto => {remote => 1, enroll => 1},
+ sub => {content => 1,},
+ tmpdel => {institutiononly => 1},
+ tmpget => {institutiononly => 1},
+ tmpput => {remote => 1, othcoau => 1},
+ tokenauthuserfile => {anywhere => 1},
+ unsub => {content => 1,},
+ update => {shared => 1},
+ updateclickers => {remote => 1},
+ userhassession => {anywhere => 1},
+ userload => {anywhere => 1},
+ version => {anywhere => 1}, #not used
+ );
+
+#
# Statistics that are maintained and dislayed in the status line.
#
my $Transactions = 0; # Number of attempted transactions.
@@ -257,10 +426,19 @@ sub SSLConnection {
Debug("Approving promotion -> ssl");
# And do so:
+ my $CRLFile;
+ unless ($crlchecked{$clientname}) {
+ $CRLFile = lonssl::CRLFile();
+ $crlchecked{$clientname} = 1;
+ }
+
my $SSLSocket = lonssl::PromoteServerSocket($Socket,
$CACertificate,
$Certificate,
- $KeyFile);
+ $KeyFile,
+ $clientname,
+ $CRLFile,
+ $clientversion);
if(! ($SSLSocket) ) { # SSL socket promotion failed.
my $err = lonssl::LastError();
&logthis(" CRITICAL "
@@ -300,8 +478,23 @@ sub InsecureConnection {
my $Socket = shift;
# Don't even start if insecure connections are not allowed.
-
- if(! $perlvar{londAllowInsecure}) { # Insecure connections not allowed.
+ # return 0 if Insecure connections not allowed.
+ #
+ if (ref($secureconf{'connfrom'}) eq 'HASH') {
+ if ($clientsamedom) {
+ if ($secureconf{'connfrom'}{'dom'} eq 'req') {
+ return 0;
+ }
+ } elsif ($clientsameinst) {
+ if ($secureconf{'connfrom'}{'intdom'} eq 'req') {
+ return 0;
+ }
+ } else {
+ if ($secureconf{'connfrom'}{'other'} eq 'req') {
+ return 0;
+ }
+ }
+ } elsif (!$perlvar{londAllowInsecure}) {
return 0;
}
@@ -601,10 +794,17 @@ sub ConfigFileFromSelector {
my $selector = shift;
my $tablefile;
- my $tabledir = $perlvar{'lonTabDir'}.'/';
- if (($selector eq "hosts") || ($selector eq "domain") ||
- ($selector eq "dns_hosts") || ($selector eq "dns_domain")) {
- $tablefile = $tabledir.$selector.'.tab';
+ if ($selector eq 'loncapaCAcrl') {
+ my $tabledir = $perlvar{'lonCertificateDirectory'};
+ if (-d $tabledir) {
+ $tablefile = $tabledir.'/'.$selector.'.pem';
+ }
+ } else {
+ my $tabledir = $perlvar{'lonTabDir'}.'/';
+ if (($selector eq "hosts") || ($selector eq "domain") ||
+ ($selector eq "dns_hosts") || ($selector eq "dns_domain")) {
+ $tablefile = $tabledir.$selector.'.tab';
+ }
}
return $tablefile;
}
@@ -628,12 +828,13 @@ sub PushFile {
my ($command, $filename, $contents) = split(":", $request, 3);
&Debug("PushFile");
- # At this point in time, pushes for only the following tables are
- # supported:
+ # At this point in time, pushes for only the following tables and
+ # CRL file are supported:
# 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);
# Construct the destination filename or reject the request.
#
# lonManage is supposed to ensure this, however this session could be
@@ -654,7 +855,8 @@ sub PushFile {
if($filename eq "host") {
$contents = AdjustHostContents($contents);
- } elsif ($filename eq 'dns_host' || $filename eq 'dns_domain') {
+ } elsif (($filename eq 'dns_host') || ($filename eq 'dns_domain') ||
+ ($filename eq 'loncapaCAcrl')) {
if ($contents eq '') {
&logthis(' Pushfile: unable to install '
.$tablefile." - no data received from push. ");
@@ -665,12 +867,15 @@ sub PushFile {
if ($managers{$clientip} eq $clientname) {
my $clientprotocol = $Apache::lonnet::protocol{$clientname};
$clientprotocol = 'http' if ($clientprotocol ne 'https');
- my $url = '/adm/'.$filename;
- $url =~ s{_}{/};
- my $ua=new LWP::UserAgent;
- $ua->timeout(60);
+ my $url;
+ if ($filename eq 'loncapaCAcrl') {
+ $url = '/adm/dns/loncapaCRL';
+ } else {
+ $url = '/adm/'.$filename;
+ $url =~ s{_}{/};
+ }
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(' Pushfile: unable to install '
.$tablefile." - error attempting to pull data. ");
@@ -1425,6 +1630,22 @@ sub du2_handler {
# selected directory the filename followed by the full output of
# the stat function is returned. The returned info for each
# file are separated by ':'. The stat fields are separated by &'s.
+#
+# If the requested path contains /../ or is:
+#
+# 1. for a directory, and the path does not begin with one of:
+# (a) /home/httpd/html/res/
+# (b) /home/httpd/html/userfiles/
+# (c) /home/httpd/lonUsers//<1>/<2>/<3>//userfiles
+# or is:
+#
+# 2. for a file, and the path (after prepending) does not begin with one of:
+# (a) /home/httpd/lonUsers//<1>/<2>/<3>//
+# (b) /home/httpd/html/res///
+# (c) /home/httpd/html/userfiles///
+#
+# the response will be "refused".
+#
# Parameters:
# $cmd - The command that dispatched us (ls).
# $ulsdir - The directory path to list... I'm not sure what this
@@ -1446,8 +1667,17 @@ sub ls_handler {
my $rights;
my $ulsout='';
my $ulsfn;
+ if ($ulsdir =~m{/\.\./}) {
+ &Failure($client,"refused\n",$userinput);
+ return 1;
+ }
if (-e $ulsdir) {
if(-d $ulsdir) {
+ unless (($ulsdir =~ m{^/home/httpd/html/(res/$LONCAPA::match_domain|userfiles/)}) ||
+ ($ulsdir =~ m{^/home/httpd/lonUsers/$LONCAPA::match_domain(?:/[\w\-.@]){3}/$LONCAPA::match_name/userfiles})) {
+ &Failure($client,"refused\n",$userinput);
+ return 1;
+ }
if (opendir(LSDIR,$ulsdir)) {
while ($ulsfn=readdir(LSDIR)) {
undef($obs);
@@ -1471,6 +1701,11 @@ sub ls_handler {
closedir(LSDIR);
}
} else {
+ unless (($ulsdir =~ m{^/home/httpd/lonUsers/$LONCAPA::match_domain(?:/[\w\-.@]){3}/$LONCAPA::match_name/}) ||
+ ($ulsdir =~ m{^/home/httpd/html/(?:res|userfiles)/$LONCAPA::match_domain/$LONCAPA::match_name/})) {
+ &Failure($client,"refused\n",$userinput);
+ return 1;
+ }
my @ulsstats=stat($ulsdir);
$ulsout.=$ulsfn.'&'.join('&',@ulsstats).':';
}
@@ -1495,6 +1730,22 @@ sub ls_handler {
# selected directory the filename followed by the full output of
# the stat function is returned. The returned info for each
# file are separated by ':'. The stat fields are separated by &'s.
+#
+# If the requested path contains /../ or is:
+#
+# 1. for a directory, and the path does not begin with one of:
+# (a) /home/httpd/html/res/
+# (b) /home/httpd/html/userfiles/
+# (c) /home/httpd/lonUsers//<1>/<2>/<3>//userfiles
+# or is:
+#
+# 2. for a file, and the path (after prepending) does not begin with one of:
+# (a) /home/httpd/lonUsers//<1>/<2>/<3>//
+# (b) /home/httpd/html/res///
+# (c) /home/httpd/html/userfiles///
+#
+# the response will be "refused".
+#
# Parameters:
# $cmd - The command that dispatched us (ls).
# $ulsdir - The directory path to list... I'm not sure what this
@@ -1515,8 +1766,17 @@ sub ls2_handler {
my $rights;
my $ulsout='';
my $ulsfn;
+ if ($ulsdir =~m{/\.\./}) {
+ &Failure($client,"refused\n",$userinput);
+ return 1;
+ }
if (-e $ulsdir) {
if(-d $ulsdir) {
+ unless (($ulsdir =~ m{^/home/httpd/html/(res/$LONCAPA::match_domain|userfiles/)}) ||
+ ($ulsdir =~ m{^/home/httpd/lonUsers/$LONCAPA::match_domain(?:/[\w\-.@]){3}/$LONCAPA::match_name/userfiles})) {
+ &Failure($client,"refused\n","$userinput");
+ return 1;
+ }
if (opendir(LSDIR,$ulsdir)) {
while ($ulsfn=readdir(LSDIR)) {
undef($obs);
@@ -1541,6 +1801,11 @@ sub ls2_handler {
closedir(LSDIR);
}
} else {
+ unless (($ulsdir =~ m{^/home/httpd/lonUsers/$LONCAPA::match_domain(?:/[\w\-.@]){3}/$LONCAPA::match_name/}) ||
+ ($ulsdir =~ m{^/home/httpd/html/(?:res|userfiles)/$LONCAPA::match_domain/$LONCAPA::match_name/})) {
+ &Failure($client,"refused\n",$userinput);
+ return 1;
+ }
my @ulsstats=stat($ulsdir);
$ulsout.=$ulsfn.'&'.join('&',@ulsstats).':';
}
@@ -1557,6 +1822,25 @@ sub ls2_handler {
# selected directory the filename followed by the full output of
# the stat function is returned. The returned info for each
# file are separated by ':'. The stat fields are separated by &'s.
+#
+# If the requested path (after prepending) contains /../ or is:
+#
+# 1. for a directory, and the path does not begin with one of:
+# (a) /home/httpd/html/res/
+# (b) /home/httpd/html/userfiles/
+# (c) /home/httpd/lonUsers//<1>/<2>/<3>//userfiles
+# (d) /home/httpd/html/priv/ and client is the homeserver
+#
+# or is:
+#
+# 2. for a file, and the path (after prepending) does not begin with one of:
+# (a) /home/httpd/lonUsers//<1>/<2>/<3>//
+# (b) /home/httpd/html/res///
+# (c) /home/httpd/html/userfiles///
+# (d) /home/httpd/html/priv/// and client is the homeserver
+#
+# the response will be "refused".
+#
# Parameters:
# $cmd - The command that dispatched us (ls).
# $tail - The tail of the request that invoked us.
@@ -1596,22 +1880,12 @@ sub ls3_handler {
}
my $dir_root = $perlvar{'lonDocRoot'};
- if ($getpropath) {
+ if (($getpropath) || ($getuserdir)) {
if (($uname =~ /^$LONCAPA::match_name$/) && ($udom =~ /^$LONCAPA::match_domain$/)) {
$dir_root = &propath($udom,$uname);
$dir_root =~ s/\/$//;
} else {
- &Failure($client,"refused\n","$cmd:$tail");
- return 1;
- }
- } elsif ($getuserdir) {
- if (($uname =~ /^$LONCAPA::match_name$/) && ($udom =~ /^$LONCAPA::match_domain$/)) {
- my $subdir=$uname.'__';
- $subdir =~ s/(.)(.)(.).*/$1\/$2\/$3/;
- $dir_root = $Apache::lonnet::perlvar{'lonUsersDir'}
- ."/$udom/$subdir/$uname";
- } else {
- &Failure($client,"refused\n","$cmd:$tail");
+ &Failure($client,"refused\n",$userinput);
return 1;
}
} elsif ($alternate_root ne '') {
@@ -1624,14 +1898,56 @@ sub ls3_handler {
$ulsdir = $dir_root.'/'.$ulsdir;
}
}
+ if ($ulsdir =~m{/\.\./}) {
+ &Failure($client,"refused\n",$userinput);
+ return 1;
+ }
+ my $islocal;
+ my @machine_ids = &Apache::lonnet::current_machine_ids();
+ if (grep(/^\Q$clientname\E$/,@machine_ids)) {
+ $islocal = 1;
+ }
my $obs;
my $rights;
my $ulsout='';
my $ulsfn;
+
+ my ($crscheck,$toplevel,$currdom,$currnum,$skip);
+ unless ($islocal) {
+ my ($major,$minor) = split(/\./,$clientversion);
+ if (($major < 2) || ($major == 2 && $minor < 12)) {
+ $crscheck = 1;
+ }
+ }
if (-e $ulsdir) {
if(-d $ulsdir) {
- if (opendir(LSDIR,$ulsdir)) {
+ unless (($getpropath) || ($getuserdir) ||
+ ($ulsdir =~ m{^/home/httpd/html/(res/$LONCAPA::match_domain|userfiles/)}) ||
+ ($ulsdir =~ m{^/home/httpd/lonUsers/$LONCAPA::match_domain(?:/[\w\-.@]){3}/$LONCAPA::match_name/userfiles}) ||
+ (($ulsdir =~ m{^/home/httpd/html/priv/$LONCAPA::match_domain}) && ($islocal))) {
+ &Failure($client,"refused\n",$userinput);
+ return 1;
+ }
+ if (($crscheck) &&
+ ($ulsdir =~ m{^/home/httpd/html/res/($LONCAPA::match_domain)(/?$|/$LONCAPA::match_courseid)})) {
+ ($currdom,my $posscnum) = ($1,$2);
+ if (($posscnum eq '') || ($posscnum eq '/')) {
+ $toplevel = 1;
+ } else {
+ $posscnum =~ s{^/+}{};
+ if (&LONCAPA::Lond::is_course($currdom,$posscnum)) {
+ $skip = 1;
+ }
+ }
+ }
+ if ((!$skip) && (opendir(LSDIR,$ulsdir))) {
while ($ulsfn=readdir(LSDIR)) {
+ if (($crscheck) && ($toplevel) && ($currdom ne '') &&
+ ($ulsfn =~ /^$LONCAPA::match_courseid$/) && (-d "$ulsdir/$ulsfn")) {
+ if (&LONCAPA::Lond::is_course($currdom,$ulsfn)) {
+ next;
+ }
+ }
undef($obs);
undef($rights);
my @ulsstats=stat($ulsdir.'/'.$ulsfn);
@@ -1654,6 +1970,13 @@ sub ls3_handler {
closedir(LSDIR);
}
} else {
+ unless (($getpropath) || ($getuserdir) ||
+ ($ulsdir =~ m{^/home/httpd/lonUsers/$LONCAPA::match_domain(?:/[\w\-.@]){3}/$LONCAPA::match_name/}) ||
+ ($ulsdir =~ m{^/home/httpd/html/(?:res|userfiles)/$LONCAPA::match_domain/$LONCAPA::match_name/}) ||
+ (($ulsdir =~ m{^/home/httpd/html/priv/$LONCAPA::match_domain/$LONCAPA::match_name/}) && ($islocal))) {
+ &Failure($client,"refused\n",$userinput);
+ return 1;
+ }
my @ulsstats=stat($ulsdir);
$ulsout.=$ulsfn.'&'.join('&',@ulsstats).':';
}
@@ -1677,15 +2000,17 @@ sub read_lonnet_global {
);
my %limit_to = (
perlvar => {
- lonOtherAuthen => 1,
- lonBalancer => 1,
- lonVersion => 1,
- lonSysEMail => 1,
- lonHostID => 1,
- lonRole => 1,
- lonDefDomain => 1,
- lonLoadLim => 1,
- lonUserLoadLim => 1,
+ lonOtherAuthen => 1,
+ lonBalancer => 1,
+ lonVersion => 1,
+ lonAdmEMail => 1,
+ lonSupportEMail => 1,
+ lonSysEMail => 1,
+ lonHostID => 1,
+ lonRole => 1,
+ lonDefDomain => 1,
+ lonLoadLim => 1,
+ lonUserLoadLim => 1,
}
);
if (ref($requested) eq 'HASH') {
@@ -1797,6 +2122,16 @@ sub server_distarch_handler {
}
®ister_handler("serverdistarch", \&server_distarch_handler, 0, 1, 0);
+sub server_certs_handler {
+ my ($cmd,$tail,$client) = @_;
+ my $userinput = "$cmd:$tail";
+ my $hostname = &Apache::lonnet::hostname($perlvar{'lonHostID'});
+ my $result = &LONCAPA::Lond::server_certs(\%perlvar,$perlvar{'lonHostID'},$hostname);
+ &Reply($client,\$result,$userinput);
+ return;
+}
+®ister_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.
@@ -1929,7 +2264,7 @@ sub authenticate_handler {
my ($remote,$hosted);
my $remotesession = &get_usersession_config($udom,'remotesession');
if (ref($remotesession) eq 'HASH') {
- $remote = $remotesession->{'remote'}
+ $remote = $remotesession->{'remote'};
}
my $hostedsession = &get_usersession_config($clienthomedom,'hostedsession');
if (ref($hostedsession) eq 'HASH') {
@@ -2066,12 +2401,8 @@ sub hash_passwd {
my $plainsalt = substr($rest[1],0,22);
$salt = Crypt::Eksblowfish::Bcrypt::de_base64($plainsalt);
} else {
- my $defaultcost;
- my %domconfig =
- &Apache::lonnet::get_dom('configuration',['password'],$domain);
- if (ref($domconfig{'password'}) eq 'HASH') {
- $defaultcost = $domconfig{'password'}{'cost'};
- }
+ my %domdefaults = &Apache::lonnet::get_domain_defaults($domain);
+ my $defaultcost = $domdefaults{'intauth_cost'};
if (($defaultcost eq '') || ($defaultcost =~ /D/)) {
$cost = 10;
} else {
@@ -2326,34 +2657,26 @@ sub update_resource_handler {
my $transname="$fname.in.transfer";
my $remoteurl=&Apache::lonnet::reply("sub:$fname","$clientname");
my $response;
-# FIXME: cannot replicate files that take more than two minutes to transfer?
-# alarm(120);
-# 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);
- }
- alarm(0);
+# FIXME: cannot replicate files that take more than two minutes to transfer -- needs checking now 1200s timeout used
+# for LWP request.
+ my $request=new HTTP::Request('GET',"$remoteurl");
+ $response=&LONCAPA::LWPReq::makerequest($clientname,$request,$transname,\%perlvar,1200,0,1);
if ($response->is_error()) {
-# FIXME: we should probably clean up here instead of just whine
- unlink($transname);
+ my $reply=&Apache::lonnet::reply("unsub:$fname","$clientname");
+ &devalidate_meta_cache($fname);
+ if (-e $transname) {
+ unlink($transname);
+ }
+ unlink($fname);
my $message=$response->status_line;
&logthis("LWP GET: $message for $fname ($remoteurl)");
} else {
if ($remoteurl!~/\.meta$/) {
-# 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');
- if ($mresponse->is_error()) {
- unlink($fname.'.meta');
- }
+ my $mrequest=new HTTP::Request('GET',$remoteurl.'.meta');
+ my $mresponse = &LONCAPA::LWPReq::makerequest($clientname,$mrequest,$fname.'.meta',\%perlvar,120,0,1);
+ if ($mresponse->is_error()) {
+ unlink($fname.'.meta');
}
- alarm(0);
}
# we successfully transfered, copy file over to real name
rename($transname,$fname);
@@ -2423,13 +2746,13 @@ sub fetch_user_file_handler {
my $remoteurl=$clientprotocol.'://'.$clienthost.'/userfiles/'.$fname;
my $response;
Debug("Remote URL : $remoteurl Transfername $transname Destname: $destname");
- alarm(120);
- {
- my $ua=new LWP::UserAgent;
- my $request=new HTTP::Request('GET',"$remoteurl");
- $response=$ua->request($request,$transname);
- }
- alarm(0);
+ my $request=new HTTP::Request('GET',"$remoteurl");
+ my $verifycert = 1;
+ my @machine_ids = &Apache::lonnet::current_machine_ids();
+ if (grep(/^\Q$clientname\E$/,@machine_ids)) {
+ $verifycert = 0;
+ }
+ $response = &LONCAPA::LWPReq::makerequest($clientname,$request,$transname,\%perlvar,1200,$verifycert);
if ($response->is_error()) {
unlink($transname);
my $message=$response->status_line;
@@ -3127,7 +3450,8 @@ sub get_profile_entry {
#
# Parameters:
# $cmd - Command keyword of request (eget).
-# $tail - Tail of the command. See GetProfileEntry
# for more information about this.
+# $tail - Tail of the command. See GetProfileEntry
+# for more information about this.
# $client - File open on the client.
# Returns:
# 1 - Continue processing
@@ -3699,7 +4023,7 @@ sub retrieve_chat_handler {
# serviced.
#
# Parameters:
-# $cmd - COmmand keyword that initiated the request.
+# $cmd - Command keyword that initiated the request.
# $tail - Remainder of the command after the keyword.
# For this function, this consists of a query and
# 3 arguments that are self-documentingly labelled
@@ -3713,11 +4037,41 @@ sub retrieve_chat_handler {
sub send_query_handler {
my ($cmd, $tail, $client) = @_;
-
my $userinput = "$cmd:$tail";
my ($query,$arg1,$arg2,$arg3)=split(/\:/,$tail);
$query=~s/\n*$//g;
+ if (($query eq 'usersearch') || ($query eq 'instdirsearch')) {
+ my $usersearchconf = &get_usersearch_config($currentdomainid,'directorysrch');
+ my $earlyout;
+ if (ref($usersearchconf) eq 'HASH') {
+ if ($currentdomainid eq $clienthomedom) {
+ if ($query eq 'usersearch') {
+ if ($usersearchconf->{'lcavailable'} eq '0') {
+ $earlyout = 1;
+ }
+ } else {
+ if ($usersearchconf->{'available'} eq '0') {
+ $earlyout = 1;
+ }
+ }
+ } else {
+ if ($query eq 'usersearch') {
+ if ($usersearchconf->{'lclocalonly'}) {
+ $earlyout = 1;
+ }
+ } else {
+ if ($usersearchconf->{'localonly'}) {
+ $earlyout = 1;
+ }
+ }
+ }
+ }
+ if ($earlyout) {
+ &Reply($client, "query_not_authorized\n");
+ return 1;
+ }
+ }
&Reply($client, "". &sql_reply("$clientname\&$query".
"\&$arg1"."\&$arg2"."\&$arg3")."\n",
$userinput);
@@ -4580,7 +4934,41 @@ sub get_domain_handler {
my ($cmd, $tail, $client) = @_;
- my $userinput = "$client:$tail";
+ my $userinput = "$cmd:$tail";
+
+ my ($udom,$namespace,$what)=split(/:/,$tail,3);
+ chomp($what);
+ if ($namespace =~ /^enc/) {
+ &Failure( $client, "refused\n", $userinput);
+ } else {
+ my @queries=split(/\&/,$what);
+ my $qresult='';
+ my $hashref = &tie_domain_hash($udom, "$namespace", &GDBM_READER());
+ if ($hashref) {
+ for (my $i=0;$i<=$#queries;$i++) {
+ $qresult.="$hashref->{$queries[$i]}&";
+ }
+ if (&untie_domain_hash($hashref)) {
+ $qresult=~s/\&$//;
+ &Reply($client, \$qresult, $userinput);
+ } else {
+ &Failure( $client, "error: ".($!+0)." untie(GDBM) Failed ".
+ "while attempting getdom\n",$userinput);
+ }
+ } else {
+ &Failure($client, "error: ".($!+0)." tie(GDBM) Failed ".
+ "while attempting getdom\n",$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);
chomp($what);
@@ -4593,19 +4981,31 @@ sub get_domain_handler {
}
if (&untie_domain_hash($hashref)) {
$qresult=~s/\&$//;
- &Reply($client, \$qresult, $userinput);
+ if ($cipher) {
+ my $cmdlength=length($qresult);
+ $qresult.=" ";
+ my $encqresult='';
+ for (my $encidx=0;$encidx<=$cmdlength;$encidx+=8) {
+ $encqresult.= unpack("H16",
+ $cipher->encrypt(substr($qresult,
+ $encidx,
+ 8)));
+ }
+ &Reply( $client, "enc:$cmdlength:$encqresult\n", $userinput);
+ } else {
+ &Failure( $client, "error:no_key\n", $userinput);
+ }
} else {
&Failure( $client, "error: ".($!+0)." untie(GDBM) Failed ".
- "while attempting getdom\n",$userinput);
+ "while attempting egetdom\n",$userinput);
}
} else {
&Failure($client, "error: ".($!+0)." tie(GDBM) Failed ".
- "while attempting getdom\n",$userinput);
+ "while attempting egetdom\n",$userinput);
}
-
return 1;
}
-®ister_handler("getdom", \&get_domain_handler, 0, 1, 0);
+®ister_handler("egetdom", \&encrypted_get_domain_handler, 1, 1, 0);
#
# Puts an id to a domains id database.
@@ -5123,6 +5523,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
@@ -5401,9 +5853,10 @@ sub validate_course_section_handler {
# 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 words that will be split into:
+# set of values that will be split into:
# $inst_class - Institutional code for the specific class section
-# $courseowner - The escaped username:domain of the course owner
+# $ownerlist - An escaped comma-separated list of username:domain
+# of the course owner, and co-owner(s).
# $cdom - The domain of the course from the institution's
# point of view.
# $client - The socket open on the client.
@@ -5428,6 +5881,56 @@ sub validate_class_access_handler {
®ister_handler("autovalidateclass_sec", \&validate_class_access_handler, 0, 1, 0);
#
+# Validate course owner or co-owners(s) access to enrollment data for all sections
+# and crosslistings for a particular 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
+# set of values that will be split into:
+# $ownerlist - An escaped comma-separated list of username:domain
+# of the course owner, and co-owner(s).
+# $cdom - The domain of the course from the institution's
+# point of view.
+# $classes - Frozen hash of institutional course sections and
+# crosslistings.
+# $client - The socket open on the client.
+# Returns:
+# 1 - continue processing.
+#
+
+sub validate_classes_handler {
+ my ($cmd, $tail, $client) = @_;
+ my $userinput = "$cmd:$tail";
+ my ($ownerlist,$cdom,$classes) = split(/:/, $tail);
+ my $classesref = &Apache::lonnet::thaw_unescape($classes);
+ my $owners = &unescape($ownerlist);
+ my $result;
+ eval {
+ local($SIG{__DIE__})='DEFAULT';
+ my %validations;
+ my $response = &localenroll::check_instclasses($owners,$cdom,$classesref,
+ \%validations);
+ if ($response eq 'ok') {
+ foreach my $key (keys(%validations)) {
+ $result .= &escape($key).'='.&Apache::lonnet::freeze_escape($validations{$key}).'&';
+ }
+ $result =~ s/\&$//;
+ } else {
+ $result = 'error';
+ }
+ };
+ if (!$@) {
+ &Reply($client, \$result, $userinput);
+ } else {
+ &Failure($client,"unknown_cmd\n",$userinput);
+ }
+ return 1;
+}
+®ister_handler("autovalidateinstclasses", \&validate_classes_handler, 0, 1, 0);
+
+#
# Create a password for a new LON-CAPA user added by auto-enrollment.
# Only used for case where authentication method for new user is localauth
#
@@ -5462,13 +5965,58 @@ sub create_auto_enroll_password_handler
®ister_handler("autocreatepassword", \&create_auto_enroll_password_handler,
0, 1, 0);
+sub auto_export_grades_handler {
+ my ($cmd, $tail, $client) = @_;
+ my $userinput = "$cmd:$tail";
+ my ($cdom,$cnum,$info,$data) = split(/:/,$tail);
+ my $inforef = &Apache::lonnet::thaw_unescape($info);
+ my $dataref = &Apache::lonnet::thaw_unescape($data);
+ my ($outcome,$result);;
+ eval {
+ local($SIG{__DIE__})='DEFAULT';
+ my %rtnhash;
+ $outcome=&localenroll::export_grades($cdom,$cnum,$inforef,$dataref,\%rtnhash);
+ if ($outcome eq 'ok') {
+ foreach my $key (keys(%rtnhash)) {
+ $result .= &escape($key).'='.&Apache::lonnet::freeze_escape($rtnhash{$key}).'&';
+ }
+ $result =~ s/\&$//;
+ }
+ };
+ if (!$@) {
+ if ($outcome eq 'ok') {
+ if ($cipher) {
+ my $cmdlength=length($result);
+ $result.=" ";
+ my $encresult='';
+ for (my $encidx=0;$encidx<=$cmdlength;$encidx+=8) {
+ $encresult.= unpack("H16",
+ $cipher->encrypt(substr($result,
+ $encidx,
+ 8)));
+ }
+ &Reply( $client, "enc:$cmdlength:$encresult\n", $userinput);
+ } else {
+ &Failure( $client, "error:no_key\n", $userinput);
+ }
+ } else {
+ &Reply($client, "$outcome\n", $userinput);
+ }
+ } else {
+ &Failure($client,"export_error\n",$userinput);
+ }
+ return 1;
+}
+®ister_handler("autoexportgrades", \&auto_export_grades_handler,
+ 1, 1, 0);
+
# Retrieve and remove temporary files created by/during autoenrollment.
#
# Formal Parameters:
# $cmd - The command that got us dispatched.
# $tail - The tail of the command. In our case this is a colon
# separated list that will be split into:
-# $filename - The name of the file to remove.
+# $filename - The name of the file to retrieve.
# The filename is given as a path relative to
# the LonCAPA temp file directory.
# $client - Socket open on the client.
@@ -5482,7 +6030,12 @@ sub retrieve_auto_file_handler {
my ($filename) = split(/:/, $tail);
my $source = $perlvar{'lonDaemons'}.'/tmp/'.$filename;
- if ( (-e $source) && ($filename ne '') ) {
+
+ if ($filename =~m{/\.\./}) {
+ &Failure($client, "refused\n", $userinput);
+ } elsif ($filename !~ /^$LONCAPA::match_domain\_$LONCAPA::match_courseid\_.+_classlist\.xml$/) {
+ &Failure($client, "refused\n", $userinput);
+ } elsif ( (-e $source) && ($filename ne '') ) {
my $reply = '';
if (open(my $fh,$source)) {
while (<$fh>) {
@@ -6119,12 +6672,13 @@ sub get_request {
#
# Parameters:
# user_input - The request received from the client (lonc).
+#
# Returns:
# true to keep processing, false if caller should exit.
#
sub process_request {
- my ($userinput) = @_; # Easier for now to break style than to
- # fix all the userinput -> user_input.
+ my ($userinput) = @_; # Easier for now to break style than to
+ # fix all the userinput -> user_input.
my $wasenc = 0; # True if request was encrypted.
# ------------------------------------------------------------ See if encrypted
# for command
@@ -6204,6 +6758,62 @@ sub process_request {
Debug("Client not privileged to do this operation");
$ok = 0;
}
+ if ($ok) {
+ my $realcommand = $command;
+ if ($command eq 'querysend') {
+ my ($query,$rest)=split(/\:/,$tail,2);
+ $query=~s/\n*$//g;
+ my @possqueries =
+ qw(userlog courselog fetchenrollment institutionalphotos usersearch instdirsearch getinstuser getmultinstusers);
+ if (grep(/^\Q$query\E$/,@possqueries)) {
+ $command .= '_'.$query;
+ } elsif ($query eq 'prepare activity log') {
+ $command .= '_activitylog';
+ }
+ }
+ if (ref($trust{$command}) eq 'HASH') {
+ my $donechecks;
+ if ($trust{$command}{'anywhere'}) {
+ $donechecks = 1;
+ } elsif ($trust{$command}{'manageronly'}) {
+ unless (&isManager()) {
+ $ok = 0;
+ }
+ $donechecks = 1;
+ } elsif ($trust{$command}{'institutiononly'}) {
+ unless ($clientsameinst) {
+ $ok = 0;
+ }
+ $donechecks = 1;
+ } elsif ($clientsameinst) {
+ $donechecks = 1;
+ }
+ unless ($donechecks) {
+ foreach my $rule (keys(%{$trust{$command}})) {
+ next if ($rule eq 'remote');
+ if ($trust{$command}{$rule}) {
+ if ($clientprohibited{$rule}) {
+ $ok = 0;
+ } else {
+ $ok = 1;
+ $donechecks = 1;
+ last;
+ }
+ }
+ }
+ }
+ unless ($donechecks) {
+ if ($trust{$command}{'remote'}) {
+ if ($clientremoteok) {
+ $ok = 1;
+ } else {
+ $ok = 0;
+ }
+ }
+ }
+ }
+ $command = $realcommand;
+ }
if($ok) {
Debug("Dispatching to handler $command $tail");
@@ -6214,8 +6824,7 @@ sub process_request {
Failure($client, "refused\n", $userinput);
return 1;
}
-
- }
+ }
print $client "unknown_cmd\n";
# -------------------------------------------------------------------- complete
@@ -6355,8 +6964,8 @@ my $wwwid=getpwnam('www');
if ($wwwid!=$<) {
my $emailto="$perlvar{'lonAdmEMail'},$perlvar{'lonSysEMail'}";
my $subj="LON: $currenthostid User ID mismatch";
- system("echo 'User ID mismatch. lond must be run as user www.' |\
- mailto $emailto -s '$subj' > /dev/null");
+ system("echo 'User ID mismatch. lond must be run as user www.' |".
+ " mail -s '$subj' $emailto > /dev/null");
exit 1;
}
@@ -6472,6 +7081,7 @@ sub UpdateHosts {
# will take care of new and changed hosts as connections come into being.
&Apache::lonnet::reset_hosts_info();
+ my %active;
foreach my $child (keys(%children)) {
my $childip = $children{$child};
@@ -6481,15 +7091,62 @@ sub UpdateHosts {
." $child for ip $childip ");
kill('INT', $child);
} else {
+ $active{$child} = $childip;
logthis(' keeping child for ip '
." $childip (pid=$child) ");
}
}
+
+ my %oldconf = %secureconf;
+ my %connchange;
+ if (lonssl::Read_Connect_Config(\%secureconf,\%crlchecked,\%perlvar) 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 ');
+ }
+ if ((ref($oldconf{'connfrom'}) eq 'HASH') && (ref($secureconf{'connfrom'}) eq 'HASH')) {
+ foreach my $type ('dom','intdom','other') {
+ if ((($oldconf{'connfrom'}{$type} eq 'no') && ($secureconf{'connfrom'}{$type} eq 'req')) ||
+ (($oldconf{'connfrom'}{$type} eq 'req') && ($secureconf{'connfrom'}{$type} eq 'no'))) {
+ $connchange{$type} = 1;
+ }
+ }
+ }
+ if (keys(%connchange)) {
+ foreach my $child (keys(%active)) {
+ my $childip = $active{$child};
+ if ($childip ne '127.0.0.1') {
+ my $childhostname = gethostbyaddr(Socket::inet_aton($childip),AF_INET);
+ if ($childhostname ne '') {
+ my $childlonhost = &Apache::lonnet::get_server_homeID($childhostname);
+ my ($samedom,$sameinst) = &set_client_info($childlonhost);
+ if ($samedom) {
+ if ($connchange{'dom'}) {
+ logthis(' UpdateHosts killing child '
+ ." $child for ip $childip ");
+ kill('INT', $child);
+ }
+ } elsif ($sameinst) {
+ if ($connchange{'intdom'}) {
+ logthis(' UpdateHosts killing child '
+ ." $child for ip $childip ");
+ kill('INT', $child);
+ }
+ } else {
+ if ($connchange{'other'}) {
+ logthis(' UpdateHosts killing child '
+ ." $child for ip $childip ");
+ kill('INT', $child);
+ }
+ }
+ }
+ }
+ }
+ }
ReloadApache;
&status("Finished reloading hosts.tab");
}
-
sub checkchildren {
&status("Checking on the children (sending signals)");
&initnewstatus();
@@ -6724,6 +7381,10 @@ if ($arch eq 'unknown') {
chomp($arch);
}
+unless (lonssl::Read_Connect_Config(\%secureconf,\%crlchecked,\%perlvar) eq 'ok') {
+ &logthis('No connectionrules table. Will fallback to loncapa.conf');
+}
+
# --------------------------------------------------------------
# Accept connections. When a connection comes in, it is validated
# and if good, a child process is created to process transactions
@@ -6854,7 +7515,7 @@ sub make_new_child {
$ConnectionType = "manager";
$clientname = $managers{$outsideip};
}
- my $clientok;
+ my ($clientok,$clientinfoset);
if ($clientrec || $ismanager) {
&status("Waiting for init from $clientip $clientname");
@@ -6882,7 +7543,32 @@ sub make_new_child {
# If the connection type is ssl, but I didn't get my
# certificate files yet, then I'll drop back to
# insecure (if allowed).
-
+
+ if ($inittype eq "ssl") {
+ my $context;
+ if ($clientsamedom) {
+ $context = 'dom';
+ if ($secureconf{'connfrom'}{'dom'} eq 'no') {
+ $inittype = "";
+ }
+ } elsif ($clientsameinst) {
+ $context = 'intdom';
+ if ($secureconf{'connfrom'}{'intdom'} eq 'no') {
+ $inittype = "";
+ }
+ } else {
+ $context = 'other';
+ if ($secureconf{'connfrom'}{'other'} eq 'no') {
+ $inittype = "";
+ }
+ }
+ if ($inittype eq '') {
+ &logthis(" Domain config set "
+ ."to no ssl for $clientname (context: $context)"
+ ." -- trying insecure auth");
+ }
+ }
+
if($inittype eq "ssl") {
my ($ca, $cert) = lonssl::CertificateFile;
my $kfile = lonssl::KeyFile;
@@ -6915,7 +7601,7 @@ sub make_new_child {
close $client;
}
} elsif ($inittype eq "ssl") {
- my $key = SSLConnection($client);
+ my $key = SSLConnection($client,$clientname);
if ($key) {
$clientok = 1;
my $cipherkey = pack("H32", $key);
@@ -6930,6 +7616,7 @@ sub make_new_child {
}
} else {
+ $clientinfoset = &set_client_info();
my $ok = InsecureConnection($client);
if($ok) {
$clientok = 1;
@@ -6942,7 +7629,6 @@ sub make_new_child {
."Attempted insecure connection disallowed ");
close $client;
$clientok = 0;
-
}
}
} else {
@@ -6951,7 +7637,6 @@ sub make_new_child {
."$clientip failed to initialize: >$remotereq< ");
&status('No init '.$clientip);
}
-
} else {
&logthis(
"WARNING: Unknown client $clientip");
@@ -6969,9 +7654,34 @@ sub make_new_child {
# ------------------------------------------------------------ Process requests
my $keep_going = 1;
my $user_input;
- my $clienthost = &Apache::lonnet::hostname($clientname);
- my $clientserverhomeID = &Apache::lonnet::get_server_homeID($clienthost);
- $clienthomedom = &Apache::lonnet::host_domain($clientserverhomeID);
+ 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");
@@ -6987,7 +7697,7 @@ sub make_new_child {
&logthis("WARNING: "
."Rejected client $clientip, closing connection");
}
- }
+ }
# =============================================================================
@@ -7001,6 +7711,60 @@ sub make_new_child {
exit;
}
+
+#
+# Used to determine if a particular client is from the same domain
+# as the current server, or from the same internet 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.
+#
+# 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.
+#
+# returns 1 to indicate package variables have been set for current client.
+#
+
+sub set_client_info {
+ my ($lonhost) = @_;
+ $lonhost ||= $clientname;
+ my $clienthost = &Apache::lonnet::hostname($lonhost);
+ my $clientserverhomeID = &Apache::lonnet::get_server_homeID($clienthost);
+ my $homedom = &Apache::lonnet::host_domain($clientserverhomeID);
+ my $samedom = 0;
+ if ($perlvar{'lonDefDom'} eq $homedom) {
+ $samedom = 1;
+ }
+ my $intdom = &Apache::lonnet::internet_dom($clientserverhomeID);
+ my $sameinst = 0;
+ if ($intdom ne '') {
+ my $internet_names = &Apache::lonnet::get_internet_names($currenthostid);
+ if (ref($internet_names) eq 'ARRAY') {
+ if (grep(/^\Q$intdom\E$/,@{$internet_names})) {
+ $sameinst = 1;
+ }
+ }
+ }
+ if (wantarray) {
+ return ($samedom,$sameinst);
+ } else {
+ $clienthomedom = $homedom;
+ $clientsamedom = $samedom;
+ $clientintdom = $intdom;
+ $clientsameinst = $sameinst;
+ return 1;
+ }
+}
+
#
# Determine if a user is an author for the indicated domain.
#
@@ -7109,15 +7873,25 @@ sub password_filename {
# domain - domain of the user.
# name - User's name.
# contents - New contents of the file.
+# saveold - (optional). If true save old file in a passwd.bak file.
# Returns:
# 0 - Failed.
# 1 - Success.
#
sub rewrite_password_file {
- my ($domain, $user, $contents) = @_;
+ my ($domain, $user, $contents, $saveold) = @_;
my $file = &password_filename($domain, $user);
if (defined $file) {
+ if ($saveold) {
+ my $bakfile = $file.'.bak';
+ if (CopyFile($file,$bakfile)) {
+ chmod(0400,$bakfile);
+ &logthis("Old password saved in passwd.bak for internally authenticated user: $user:$domain");
+ } else {
+ &logthis("Failed to save old password in passwd.bak for internally authenticated user: $user:$domain");
+ }
+ }
my $pf = IO::File->new(">$file");
if($pf) {
print $pf "$contents\n";
@@ -7208,20 +7982,27 @@ sub validate_user {
$contentpwd = $domdefaults{'auth_arg_def'};
}
}
- }
+ }
if ($howpwd ne 'nouser') {
if($howpwd eq "internal") { # Encrypted is in local password file.
if (length($contentpwd) == 13) {
$validated = (crypt($password,$contentpwd) eq $contentpwd);
if ($validated) {
- my $ncpass = &hash_passwd($domain,$password);
- if (&rewrite_password_file($domain,$user,"$howpwd:$ncpass")) {
- &update_passwd_history($user,$domain,$howpwd,'conversion');
- &logthis("Validated password hashed with bcrypt for $user:$domain");
+ my %domdefaults = &Apache::lonnet::get_domain_defaults($domain);
+ if ($domdefaults{'intauth_switch'}) {
+ my $ncpass = &hash_passwd($domain,$password);
+ my $saveold;
+ if ($domdefaults{'intauth_switch'} == 2) {
+ $saveold = 1;
+ }
+ if (&rewrite_password_file($domain,$user,"$howpwd:$ncpass",$saveold)) {
+ &update_passwd_history($user,$domain,$howpwd,'conversion');
+ &logthis("Validated password hashed with bcrypt for $user:$domain");
+ }
}
}
} else {
- $validated = &check_internal_passwd($password,$contentpwd,$domain);
+ $validated = &check_internal_passwd($password,$contentpwd,$domain,$user);
}
}
elsif ($howpwd eq "unix") { # User is a normal unix user.
@@ -7291,24 +8072,35 @@ sub validate_user {
}
sub check_internal_passwd {
- my ($plainpass,$stored,$domain) = @_;
+ my ($plainpass,$stored,$domain,$user) = @_;
my (undef,$method,@rest) = split(/!/,$stored);
- if ($method eq "bcrypt") {
+ if ($method eq 'bcrypt') {
my $result = &hash_passwd($domain,$plainpass,@rest);
if ($result ne $stored) {
return 0;
}
- # Upgrade to a larger number of rounds if necessary
- my $defaultcost;
- my %domconfig =
- &Apache::lonnet::get_dom('configuration',['password'],$domain);
- if (ref($domconfig{'password'}) eq 'HASH') {
- $defaultcost = $domconfig{'password'}{'cost'};
- }
- if (($defaultcost eq '') || ($defaultcost =~ /D/)) {
- $defaultcost = 10;
+ my %domdefaults = &Apache::lonnet::get_domain_defaults($domain);
+ if ($domdefaults{'intauth_check'}) {
+ # Upgrade to a larger number of rounds if necessary
+ my $defaultcost = $domdefaults{'intauth_cost'};
+ if (($defaultcost eq '') || ($defaultcost =~ /D/)) {
+ $defaultcost = 10;
+ }
+ if (int($rest[0])new(">$passfilename");
if($pf) {
print $pf "localauth:$npass\n";
+ &update_passwd_history($uname,$udom,$umode,$action);
} else {
$result = "pass_file_failed_error";
}
@@ -7683,6 +8476,14 @@ sub make_passwd_file {
$result = "pass_file_failed_error";
}
}
+ } elsif ($umode eq 'lti') {
+ my $pf = IO::File->new(">$passfilename");
+ if($pf) {
+ print $pf "lti:\n";
+ &update_passwd_history($uname,$udom,$umode,$action);
+ } else {
+ $result = "pass_file_failed_error";
+ }
} else {
$result="auth_mode_error";
}
@@ -7729,16 +8530,59 @@ sub get_usersession_config {
return $usersessionconf;
} else {
my %domconfig = &Apache::lonnet::get_dom('configuration',['usersessions'],$dom);
- if (ref($domconfig{'usersessions'}) eq 'HASH') {
- &Apache::lonnet::do_cache_new($name,$dom,$domconfig{'usersessions'},3600);
- return $domconfig{'usersessions'};
- }
+ &Apache::lonnet::do_cache_new($name,$dom,$domconfig{'usersessions'},3600);
+ return $domconfig{'usersessions'};
}
return;
}
+sub get_usersearch_config {
+ my ($dom,$name) = @_;
+ my ($usersearchconf,$cached)=&Apache::lonnet::is_cached_new($name,$dom);
+ if (defined($cached)) {
+ return $usersearchconf;
+ } else {
+ my %domconfig = &Apache::lonnet::get_dom('configuration',['directorysrch'],$dom);
+ &Apache::lonnet::do_cache_new($name,$dom,$domconfig{'directorysrch'},600);
+ return $domconfig{'directorysrch'};
+ }
+ return;
+}
-
+sub get_prohibited {
+ my ($dom) = @_;
+ my $name = 'trust';
+ my ($trustconfig,$cached)=&Apache::lonnet::is_cached_new($name,$dom);
+ unless (defined($cached)) {
+ my %domconfig = &Apache::lonnet::get_dom('configuration',['trust'],$dom);
+ &Apache::lonnet::do_cache_new($name,$dom,$domconfig{'trust'},3600);
+ $trustconfig = $domconfig{'trust'};
+ }
+ my %prohibited;
+ if (ref($trustconfig)) {
+ foreach my $prefix (keys(%{$trustconfig})) {
+ if (ref($trustconfig->{$prefix}) eq 'HASH') {
+ my $reject;
+ if (ref($trustconfig->{$prefix}->{'exc'}) eq 'ARRAY') {
+ if (grep(/^\Q$clientintdom\E$/,@{$trustconfig->{$prefix}->{'exc'}})) {
+ $reject = 1;
+ }
+ }
+ if (ref($trustconfig->{$prefix}->{'inc'}) eq 'ARRAY') {
+ if (grep(/^\Q$clientintdom\E$/,@{$trustconfig->{$prefix}->{'inc'}})) {
+ $reject = 0;
+ } else {
+ $reject = 1;
+ }
+ }
+ if ($reject) {
+ $prohibited{$prefix} = 1;
+ }
+ }
+ }
+ }
+ return %prohibited;
+}
sub distro_and_arch {
return $dist.':'.$arch;
@@ -8065,7 +8909,6 @@ IO::File
Apache::File
POSIX
Crypt::IDEA
-LWP::UserAgent()
GDBM_File
Authen::Krb4
Authen::Krb5
@@ -8147,7 +8990,7 @@ is closed and the child exits.
=item Red CRITICAL Can't get key file
SSL key negotiation is being attempted but the call to
-lonssl::KeyFile failed. This usually means that the
+lonssl::KeyFile failed. This usually means that the
configuration file is not correctly defining or protecting
the directories/files lonCertificateDirectory or
lonnetPrivateKey