--- loncom/lond 2004/08/05 11:37:05 1.223 +++ loncom/lond 2004/08/18 11:31:50 1.232 @@ -2,7 +2,7 @@ # The LearningOnline Network # lond "LON Daemon" Server (port "LOND" 5663) # -# $Id: lond,v 1.223 2004/08/05 11:37:05 foxr Exp $ +# $Id: lond,v 1.232 2004/08/18 11:31:50 foxr Exp $ # # Copyright Michigan State University Board of Trustees # @@ -57,7 +57,7 @@ my $DEBUG = 1; # Non zero to ena my $status=''; my $lastlog=''; -my $VERSION='$Revision: 1.223 $'; #' stupid emacs +my $VERSION='$Revision: 1.232 $'; #' stupid emacs my $remoteVERSION; my $currenthostid="default"; my $currentdomainid; @@ -162,8 +162,6 @@ sub ResetStatistics { $Failures = 0; } - - #------------------------------------------------------------------------ # # LocalConnection @@ -194,8 +192,7 @@ sub LocalConnection { ."$clientdns ne $thisserver "); close $Socket; return undef; - } - else { + } else { chomp($initcmd); # Get rid of \n in filename. my ($init, $type, $name) = split(/:/, $initcmd); Debug(" Init command: $init $type $name "); @@ -326,8 +323,7 @@ sub InsecureConnection { $answer =~s/\W//g; if($challenge eq $answer) { return 1; - } - else { + } else { logthis("WARNING client did not respond to challenge"); &status("No challenge reqply"); return 0; @@ -374,7 +370,6 @@ sub isClient { # - This allows dynamic changes to the manager table # without the need to signal to the lond. # - sub ReadManagerTable { # Clean out the old table first.. @@ -656,8 +651,7 @@ sub PushFile { &logthis(' Pushfile: unable to install ' .$tablefile." $! "); return "error:$!"; - } - else { + } else { &logthis(' Installed new '.$tablefile .""); @@ -1503,8 +1497,7 @@ sub change_password_handler { &Failure( $client, "auth_mode_error\n", $userinput); } - } - else { + } else { &Failure( $client, "non_authorized\n", $userinput); } @@ -1513,6 +1506,1198 @@ sub change_password_handler { register_handler("passwd", \&change_password_handler, 1, 1, 0); +# +# Create a new user. User in this case means a lon-capa user. +# The user must either already exist in some authentication realm +# like kerberos or the /etc/passwd. If not, a user completely local to +# this loncapa system is created. +# +# Parameters: +# $cmd - The command that got us here. +# $tail - Tail of the command (remaining parameters). +# $client - File descriptor connected to client. +# Returns +# 0 - Requested to exit, caller should shut down. +# 1 - Continue processing. +# Implicit inputs: +# The authentication systems describe above have their own forms of implicit +# input into the authentication process that are described above. +sub add_user_handler { + + my ($cmd, $tail, $client) = @_; + + + my ($udom,$uname,$umode,$npass)=split(/:/,$tail); + my $userinput = $cmd.":".$tail; # Reconstruct the full request line. + + &Debug("cmd =".$cmd." $udom =".$udom." uname=".$uname); + + + if($udom eq $currentdomainid) { # Reject new users for other domains... + + my $oldumask=umask(0077); + chomp($npass); + $npass=&unescape($npass); + my $passfilename = &password_path($udom, $uname); + &Debug("Password file created will be:".$passfilename); + if (-e $passfilename) { + &Failure( $client, "already_exists\n", $userinput); + } else { + my @fpparts=split(/\//,$passfilename); + my $fpnow=$fpparts[0].'/'.$fpparts[1].'/'.$fpparts[2]; + my $fperror=''; + for (my $i=3;$i<= ($#fpparts-1);$i++) { + $fpnow.='/'.$fpparts[$i]; + unless (-e $fpnow) { + &logthis("mkdir $fpnow"); + unless (mkdir($fpnow,0777)) { + $fperror="error: ".($!+0)." mkdir failed while attempting " + ."makeuser"; + } + } + } + unless ($fperror) { + my $result=&make_passwd_file($uname, $umode,$npass, $passfilename); + &Reply($client, $result, $userinput); #BUGBUG - could be fail + } else { + &Failure($client, "$fperror\n", $userinput); + } + } + umask($oldumask); + } else { + &Failure($client, "not_right_domain\n", + $userinput); # Even if we are multihomed. + + } + return 1; + +} +®ister_handler("makeuser", \&add_user_handler, 1, 1, 0); + +# +# Change the authentication method of a user. Note that this may +# also implicitly change the user's password if, for example, the user is +# joining an existing authentication realm. Known authentication realms at +# this time are: +# internal - Purely internal password file (only loncapa knows this user) +# local - Institutionally written authentication module. +# unix - Unix user (/etc/passwd with or without /etc/shadow). +# kerb4 - kerberos version 4 +# kerb5 - kerberos version 5 +# +# Parameters: +# $cmd - The command that got us here. +# $tail - Tail of the command (remaining parameters). +# $client - File descriptor connected to client. +# Returns +# 0 - Requested to exit, caller should shut down. +# 1 - Continue processing. +# Implicit inputs: +# The authentication systems describe above have their own forms of implicit +# input into the authentication process that are described above. +# +sub change_authentication_handler { + + my ($cmd, $tail, $client) = @_; + + my $userinput = "$cmd:$tail"; # Reconstruct user input. + + my ($udom,$uname,$umode,$npass)=split(/:/,$tail); + &Debug("cmd = ".$cmd." domain= ".$udom."uname =".$uname." umode= ".$umode); + if ($udom ne $currentdomainid) { + &Failure( $client, "not_right_domain\n", $client); + } else { + + chomp($npass); + + $npass=&unescape($npass); + my $passfilename = &password_path($udom, $uname); + if ($passfilename) { # Not allowed to create a new user!! + my $result=&make_passwd_file($uname, $umode,$npass,$passfilename); + &Reply($client, $result, $userinput); + } else { + &Failure($client, "non_authorized", $userinput); # Fail the user now. + } + } + return 1; +} +®ister_handler("changeuserauth", \&change_authentication_handler, 1,1, 0); + +# +# Determines if this is the home server for a user. The home server +# for a user will have his/her lon-capa passwd file. Therefore all we need +# to do is determine if this file exists. +# +# Parameters: +# $cmd - The command that got us here. +# $tail - Tail of the command (remaining parameters). +# $client - File descriptor connected to client. +# Returns +# 0 - Requested to exit, caller should shut down. +# 1 - Continue processing. +# Implicit inputs: +# The authentication systems describe above have their own forms of implicit +# input into the authentication process that are described above. +# +sub is_home_handler { + my ($cmd, $tail, $client) = @_; + + my $userinput = "$cmd:$tail"; + + my ($udom,$uname)=split(/:/,$tail); + chomp($uname); + my $passfile = &password_filename($udom, $uname); + if($passfile) { + &Reply( $client, "found\n", $userinput); + } else { + &Failure($client, "not_found\n", $userinput); + } + return 1; +} +®ister_handler("home", \&is_home_handler, 0,1,0); + +# +# Process an update request for a resource?? I think what's going on here is +# that a resource has been modified that we hold a subscription to. +# If the resource is not local, then we must update, or at least invalidate our +# cached copy of the resource. +# FUTURE WORK: +# I need to look at this logic carefully. My druthers would be to follow +# typical caching logic, and simple invalidate the cache, drop any subscription +# an let the next fetch start the ball rolling again... however that may +# actually be more difficult than it looks given the complex web of +# proxy servers. +# Parameters: +# $cmd - The command that got us here. +# $tail - Tail of the command (remaining parameters). +# $client - File descriptor connected to client. +# Returns +# 0 - Requested to exit, caller should shut down. +# 1 - Continue processing. +# Implicit inputs: +# The authentication systems describe above have their own forms of implicit +# input into the authentication process that are described above. +# +sub update_resource_handler { + + my ($cmd, $tail, $client) = @_; + + my $userinput = "$cmd:$tail"; + + my $fname= $tail; # This allows interactive testing + + + my $ownership=ishome($fname); + if ($ownership eq 'not_owner') { + if (-e $fname) { + my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, + $atime,$mtime,$ctime,$blksize,$blocks)=stat($fname); + my $now=time; + my $since=$now-$atime; + if ($since>$perlvar{'lonExpire'}) { + my $reply=&reply("unsub:$fname","$clientname"); + unlink("$fname"); + } else { + my $transname="$fname.in.transfer"; + my $remoteurl=&reply("sub:$fname","$clientname"); + my $response; + alarm(120); + { + my $ua=new LWP::UserAgent; + my $request=new HTTP::Request('GET',"$remoteurl"); + $response=$ua->request($request,$transname); + } + alarm(0); + if ($response->is_error()) { + unlink($transname); + my $message=$response->status_line; + &logthis("LWP GET: $message for $fname ($remoteurl)"); + } else { + if ($remoteurl!~/\.meta$/) { + 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'); + } + } + alarm(0); + } + rename($transname,$fname); + } + } + &Reply( $client, "ok\n", $userinput); + } else { + &Failure($client, "not_found\n", $userinput); + } + } else { + &Failure($client, "rejected\n", $userinput); + } + return 1; +} +®ister_handler("update", \&update_resource_handler, 0 ,1, 0); + +# +# Fetch a user file from a remote server to the user's home directory +# userfiles subdir. +# Parameters: +# $cmd - The command that got us here. +# $tail - Tail of the command (remaining parameters). +# $client - File descriptor connected to client. +# Returns +# 0 - Requested to exit, caller should shut down. +# 1 - Continue processing. +# +sub fetch_user_file_handler { + + my ($cmd, $tail, $client) = @_; + + my $userinput = "$cmd:$tail"; + my $fname = $tail; + my ($udom,$uname,$ufile) = ($fname =~ m|^([^/]+)/([^/]+)/(.+)$|); + my $udir=&propath($udom,$uname).'/userfiles'; + unless (-e $udir) { + mkdir($udir,0770); + } + Debug("fetch user file for $fname"); + if (-e $udir) { + $ufile=~s/^[\.\~]+//; + + # IF necessary, create the path right down to the file. + # Note that any regular files in the way of this path are + # wiped out to deal with some earlier folly of mine. + + my $path = $udir; + if ($ufile =~m|(.+)/([^/]+)$|) { + my @parts=split('/',$1); + foreach my $part (@parts) { + $path .= '/'.$part; + if( -f $path) { + unlink($path); + } + if ((-e $path)!=1) { + mkdir($path,0770); + } + } + } + + + my $destname=$udir.'/'.$ufile; + my $transname=$udir.'/'.$ufile.'.in.transit'; + my $remoteurl='http://'.$clientip.'/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); + if ($response->is_error()) { + unlink($transname); + my $message=$response->status_line; + &logthis("LWP GET: $message for $fname ($remoteurl)"); + &Failure($client, "failed\n", $userinput); + } else { + Debug("Renaming $transname to $destname"); + if (!rename($transname,$destname)) { + &logthis("Unable to move $transname to $destname"); + unlink($transname); + &Failure($client, "failed\n", $userinput); + } else { + &Reply($client, "ok\n", $userinput); + } + } + } else { + &Failure($client, "not_home\n", $userinput); + } + return 1; +} +®ister_handler("fetchuserfile", \&fetch_user_file_handler, 0, 1, 0); + +# +# Remove a file from a user's home directory userfiles subdirectory. +# Parameters: +# cmd - the Lond request keyword that got us here. +# tail - the part of the command past the keyword. +# client- File descriptor connected with the client. +# +# Returns: +# 1 - Continue processing. + +sub remove_user_file_handler { + my ($cmd, $tail, $client) = @_; + + my ($fname) = split(/:/, $tail); # Get rid of any tailing :'s lonc may have sent. + + my ($udom,$uname,$ufile) = ($fname =~ m|^([^/]+)/([^/]+)/(.+)$|); + if ($ufile =~m|/\.\./|) { + # any files paths with /../ in them refuse + # to deal with + &Failure($client, "refused\n", "$cmd:$tail"); + } else { + my $udir = &propath($udom,$uname); + if (-e $udir) { + my $file=$udir.'/userfiles/'.$ufile; + if (-e $file) { + unlink($file); + if (-e $file) { + &Failure($client, "failed\n", "$cmd:$tail"); + } else { + &Reply($client, "ok\n", "$cmd:$tail"); + } + } else { + &Failure($client, "not_found\n", "$cmd:$tail"); + } + } else { + &Failure($client, "not_home\n", "$cmd:$tail"); + } + } + return 1; +} +®ister_handler("removeuserfile", \&remove_user_file_handler, 0,1,0); + + +# +# Authenticate access to a user file by checking the user's +# session token(?) +# +# Parameters: +# cmd - The request keyword that dispatched to tus. +# tail - The tail of the request (colon separated parameters). +# client - Filehandle open on the client. +# Return: +# 1. + +sub token_auth_user_file_handler { + my ($cmd, $tail, $client) = @_; + + my ($fname, $session) = split(/:/, $tail); + + chomp($session); + my $reply='non_auth'; + if (open(ENVIN,$perlvar{'lonIDsDir'}.'/'. + $session.'.id')) { + while (my $line=) { + if ($line=~ m|userfile\.\Q$fname\E\=|) { $reply='ok'; } + } + close(ENVIN); + &Reply($client, $reply); + } else { + &Failure($client, "invalid_token\n", "$cmd:$tail"); + } + return 1; + +} + +®ister_handler("tokenauthuserfile", \&token_auth_user_file_handler, 0,1,0); + + +# +# Unsubscribe from a resource. +# +# Parameters: +# $cmd - The command that got us here. +# $tail - Tail of the command (remaining parameters). +# $client - File descriptor connected to client. +# Returns +# 0 - Requested to exit, caller should shut down. +# 1 - Continue processing. +# +sub unsubscribe_handler { + my ($cmd, $tail, $client) = @_; + + my $userinput= "$cmd:$tail"; + + my ($fname) = split(/:/,$tail); # Split in case there's extrs. + + &Debug("Unsubscribing $fname"); + if (-e $fname) { + &Debug("Exists"); + &Reply($client, &unsub($fname,$clientip), $userinput); + } else { + &Failure($client, "not_found\n", $userinput); + } + return 1; +} +®ister_handler("unsub", \&unsubscribe_handler, 0, 1, 0); +# Subscribe to a resource +# +# Parameters: +# $cmd - The command that got us here. +# $tail - Tail of the command (remaining parameters). +# $client - File descriptor connected to client. +# Returns +# 0 - Requested to exit, caller should shut down. +# 1 - Continue processing. +# +sub subscribe_handler { + my ($cmd, $tail, $client)= @_; + + my $userinput = "$cmd:$tail"; + + &Reply( $client, &subscribe($userinput,$clientip), $userinput); + + return 1; +} +®ister_handler("sub", \&subscribe_handler, 0, 1, 0); + +# +# Determine the version of a resource (?) Or is it return +# the top version of the resource? Not yet clear from the +# code in currentversion. +# +# Parameters: +# $cmd - The command that got us here. +# $tail - Tail of the command (remaining parameters). +# $client - File descriptor connected to client. +# Returns +# 0 - Requested to exit, caller should shut down. +# 1 - Continue processing. +# +sub current_version_handler { + my ($cmd, $tail, $client) = @_; + + my $userinput= "$cmd:$tail"; + + my $fname = $tail; + &Reply( $client, ¤tversion($fname)."\n", $userinput); + return 1; + +} +®ister_handler("currentversion", \¤t_version_handler, 0, 1, 0); + +# Make an entry in a user's activity log. +# +# Parameters: +# $cmd - The command that got us here. +# $tail - Tail of the command (remaining parameters). +# $client - File descriptor connected to client. +# Returns +# 0 - Requested to exit, caller should shut down. +# 1 - Continue processing. +# +sub activity_log_handler { + my ($cmd, $tail, $client) = @_; + + + my $userinput= "$cmd:$tail"; + + my ($udom,$uname,$what)=split(/:/,$tail); + chomp($what); + my $proname=&propath($udom,$uname); + my $now=time; + my $hfh; + if ($hfh=IO::File->new(">>$proname/activity.log")) { + print $hfh "$now:$clientname:$what\n"; + &Reply( $client, "ok\n", $userinput); + } else { + &Failure($client, "error: ".($!+0)." IO::File->new Failed " + ."while attempting log\n", + $userinput); + } + + return 1; +} +register_handler("log", \&activity_log_handler, 0, 1, 0); + +# +# Put a namespace entry in a user profile hash. +# My druthers would be for this to be an encrypted interaction too. +# anything that might be an inadvertent covert channel about either +# user authentication or user personal information.... +# +# Parameters: +# $cmd - The command that got us here. +# $tail - Tail of the command (remaining parameters). +# $client - File descriptor connected to client. +# Returns +# 0 - Requested to exit, caller should shut down. +# 1 - Continue processing. +# +sub put_user_profile_entry { + my ($cmd, $tail, $client) = @_; + + my $userinput = "$cmd:$tail"; + + my ($udom,$uname,$namespace,$what) =split(/:/,$tail); + if ($namespace ne 'roles') { + chomp($what); + my $hashref = &tie_user_hash($udom, $uname, $namespace, + &GDBM_WRCREAT(),"P",$what); + if($hashref) { + my @pairs=split(/\&/,$what); + foreach my $pair (@pairs) { + my ($key,$value)=split(/=/,$pair); + $hashref->{$key}=$value; + } + if (untie(%$hashref)) { + &Reply( $client, "ok\n", $userinput); + } else { + &Failure($client, "error: ".($!+0)." untie(GDBM) failed ". + "while attempting put\n", + $userinput); + } + } else { + &Failure( $client, "error: ".($!)." tie(GDBM) Failed ". + "while attempting put\n", $userinput); + } + } else { + &Failure( $client, "refused\n", $userinput); + } + + return 1; +} +®ister_handler("put", \&put_user_profile_entry, 0, 1, 0); + +# +# Increment a profile entry in the user history file. +# The history contains keyword value pairs. In this case, +# The value itself is a pair of numbers. The first, the current value +# the second an increment that this function applies to the current +# value. +# +# Parameters: +# $cmd - The command that got us here. +# $tail - Tail of the command (remaining parameters). +# $client - File descriptor connected to client. +# Returns +# 0 - Requested to exit, caller should shut down. +# 1 - Continue processing. +# +sub increment_user_value_handler { + my ($cmd, $tail, $client) = @_; + + my $userinput = "$cmd:$tail"; + + my ($udom,$uname,$namespace,$what) =split(/:/,$tail); + if ($namespace ne 'roles') { + chomp($what); + my $hashref = &tie_user_hash($udom, $uname, + $namespace, &GDBM_WRCREAT(), + "P",$what); + if ($hashref) { + my @pairs=split(/\&/,$what); + foreach my $pair (@pairs) { + my ($key,$value)=split(/=/,$pair); + # We could check that we have a number... + if (! defined($value) || $value eq '') { + $value = 1; + } + $hashref->{$key}+=$value; + } + if (untie(%$hashref)) { + &Reply( $client, "ok\n", $userinput); + } else { + &Failure($client, "error: ".($!+0)." untie(GDBM) failed ". + "while attempting inc\n", $userinput); + } + } else { + &Failure($client, "error: ".($!+0)." tie(GDBM) Failed ". + "while attempting inc\n", $userinput); + } + } else { + &Failure($client, "refused\n", $userinput); + } + + return 1; +} +®ister_handler("inc", \&increment_user_value_handler, 0, 1, 0); + + +# +# Put a new role for a user. Roles are LonCAPA's packaging of permissions. +# Each 'role' a user has implies a set of permissions. Adding a new role +# for a person grants the permissions packaged with that role +# to that user when the role is selected. +# +# Parameters: +# $cmd - The command string (rolesput). +# $tail - The remainder of the request line. For rolesput this +# consists of a colon separated list that contains: +# The domain and user that is granting the role (logged). +# The domain and user that is getting the role. +# The roles being granted as a set of & separated pairs. +# each pair a key value pair. +# $client - File descriptor connected to the client. +# Returns: +# 0 - If the daemon should exit +# 1 - To continue processing. +# +# +sub roles_put_handler { + my ($cmd, $tail, $client) = @_; + + my $userinput = "$cmd:$tail"; + + my ( $exedom, $exeuser, $udom, $uname, $what) = split(/:/,$tail); + + + my $namespace='roles'; + chomp($what); + my $hashref = &tie_user_hash($udom, $uname, $namespace, + &GDBM_WRCREAT(), "P", + "$exedom:$exeuser:$what"); + # + # Log the attempt to set a role. The {}'s here ensure that the file + # handle is open for the minimal amount of time. Since the flush + # is done on close this improves the chances the log will be an un- + # corrupted ordered thing. + if ($hashref) { + my @pairs=split(/\&/,$what); + foreach my $pair (@pairs) { + my ($key,$value)=split(/=/,$pair); + &manage_permissions($key, $udom, $uname, + &get_auth_type( $udom, $uname)); + $hashref->{$key}=$value; + } + if (untie($hashref)) { + &Reply($client, "ok\n", $userinput); + } else { + &Failure( $client, "error: ".($!+0)." untie(GDBM) Failed ". + "while attempting rolesput\n", $userinput); + } + } else { + &Failure( $client, "error: ".($!+0)." tie(GDBM) Failed ". + "while attempting rolesput\n", $userinput); + } + return 1; +} +®ister_handler("rolesput", \&roles_put_handler, 1,1,0); # Encoded client only. + +# +# Deletes (removes) a role for a user. This is equivalent to removing +# a permissions package associated with the role from the user's profile. +# +# Parameters: +# $cmd - The command (rolesdel) +# $tail - The remainder of the request line. This consists +# of: +# The domain and user requesting the change (logged) +# The domain and user being changed. +# The roles being revoked. These are shipped to us +# as a bunch of & separated role name keywords. +# $client - The file handle open on the client. +# Returns: +# 1 - Continue processing +# 0 - Exit. +# +sub roles_delete_handler { + my ($cmd, $tail, $client) = @_; + + my $userinput = "$cmd:$tail"; + + my ($exedom,$exeuser,$udom,$uname,$what)=split(/:/,$tail); + &Debug("cmd = ".$cmd." exedom= ".$exedom."user = ".$exeuser." udom=".$udom. + "what = ".$what); + my $namespace='roles'; + chomp($what); + my $hashref = &tie_user_hash($udom, $uname, $namespace, + &GDBM_WRCREAT(), "D", + "$exedom:$exeuser:$what"); + + if ($hashref) { + my @rolekeys=split(/\&/,$what); + + foreach my $key (@rolekeys) { + delete $hashref->{$key}; + } + if (untie(%$hashref)) { + &Reply($client, "ok\n", $userinput); + } else { + &Failure( $client, "error: ".($!+0)." untie(GDBM) Failed ". + "while attempting rolesdel\n", $userinput); + } + } else { + &Failure( $client, "error: ".($!+0)." tie(GDBM) Failed ". + "while attempting rolesdel\n", $userinput); + } + + return 1; +} +®ister_handler("rolesdel", \&roles_delete_handler, 1,1, 0); # Encoded client only + +# Unencrypted get from a user's profile database. See +# GetProfileEntryEncrypted for a version that does end-to-end encryption. +# This function retrieves a keyed item from a specific named database in the +# user's directory. +# +# Parameters: +# $cmd - Command request keyword (get). +# $tail - Tail of the command. This is a colon separated list +# consisting of the domain and username that uniquely +# identifies the profile, +# The 'namespace' which selects the gdbm file to +# do the lookup in, +# & separated list of keys to lookup. Note that +# the values are returned as an & separated list too. +# $client - File descriptor open on the client. +# Returns: +# 1 - Continue processing. +# 0 - Exit. +# +sub get_profile_entry { + my ($cmd, $tail, $client) = @_; + + my $userinput= "$cmd:$tail"; + + my ($udom,$uname,$namespace,$what) = split(/:/,$tail); + chomp($what); + my $hashref = &tie_user_hash($udom, $uname, $namespace, + &GDBM_READER()); + if ($hashref) { + my @queries=split(/\&/,$what); + my $qresult=''; + + for (my $i=0;$i<=$#queries;$i++) { + $qresult.="$hashref->{$queries[$i]}&"; # Presumably failure gives empty string. + } + $qresult=~s/\&$//; # Remove trailing & from last lookup. + if (untie(%$hashref)) { + &Reply($client, "$qresult\n", $userinput); + } else { + &Failure($client, "error: ".($!+0)." untie(GDBM) Failed ". + "while attempting get\n", $userinput); + } + } else { + if ($!+0 == 2) { # +0 coerces errno -> number 2 is ENOENT + &Failure($client, "error:No such file or ". + "GDBM reported bad block error\n", $userinput); + } else { # Some other undifferentiated err. + &Failure($client, "error: ".($!+0)." tie(GDBM) Failed ". + "while attempting get\n", $userinput); + } + } + return 1; +} +®ister_handler("get", \&get_profile_entry, 0,1,0); + +# +# Process the encrypted get request. Note that the request is sent +# in clear, but the reply is encrypted. This is a small covert channel: +# information about the sensitive keys is given to the snooper. Just not +# information about the values of the sensitive key. Hmm if I wanted to +# know these I'd snoop for the egets. Get the profile item names from them +# and then issue a get for them since there's no enforcement of the +# requirement of an encrypted get for particular profile items. If I +# were re-doing this, I'd force the request to be encrypted as well as the +# reply. I'd also just enforce encrypted transactions for all gets since +# that would prevent any covert channel snooping. +# +# Parameters: +# $cmd - Command keyword of request (eget). +# $tail - Tail of the command. See GetProfileEntry # for more information about this. +# $client - File open on the client. +# Returns: +# 1 - Continue processing +# 0 - server should exit. +sub get_profile_entry_encrypted { + my ($cmd, $tail, $client) = @_; + + my $userinput = "$cmd:$tail"; + + my ($cmd,$udom,$uname,$namespace,$what) = split(/:/,$userinput); + chomp($what); + my $hashref = &tie_user_hash($udom, $uname, $namespace, + &GDBM_READER()); + if ($hashref) { + my @queries=split(/\&/,$what); + my $qresult=''; + for (my $i=0;$i<=$#queries;$i++) { + $qresult.="$hashref->{$queries[$i]}&"; + } + if (untie(%$hashref)) { + $qresult=~s/\&$//; + 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 eget\n", $userinput); + } + } else { + &Failure($client, "error: ".($!+0)." tie(GDBM) Failed ". + "while attempting eget\n", $userinput); + } + + return 1; +} +®ister_handler("eget", \&GetProfileEntryEncrypted, 0, 1, 0); +# +# Deletes a key in a user profile database. +# +# Parameters: +# $cmd - Command keyword (del). +# $tail - Command tail. IN this case a colon +# separated list containing: +# The domain and user that identifies uniquely +# the identity of the user. +# The profile namespace (name of the profile +# database file). +# & separated list of keywords to delete. +# $client - File open on client socket. +# Returns: +# 1 - Continue processing +# 0 - Exit server. +# +# + +sub delete_profile_entry { + my ($cmd, $tail, $client) = @_; + + my $userinput = "cmd:$tail"; + + my ($udom,$uname,$namespace,$what) = split(/:/,$tail); + chomp($what); + my $hashref = &tie_user_hash($udom, $uname, $namespace, + &GDBM_WRCREAT(), + "D",$what); + if ($hashref) { + my @keys=split(/\&/,$what); + foreach my $key (@keys) { + delete($hashref->{$key}); + } + if (untie(%$hashref)) { + &Reply($client, "ok\n", $userinput); + } else { + &Failure($client, "error: ".($!+0)." untie(GDBM) Failed ". + "while attempting del\n", $userinput); + } + } else { + &Failure( $client, "error: ".($!+0)." tie(GDBM) Failed ". + "while attempting del\n", $userinput); + } + return 1; +} +®ister_handler("del", \&delete_profile_entry, 0, 1, 0); +# +# List the set of keys that are defined in a profile database file. +# A successful reply from this will contain an & separated list of +# the keys. +# Parameters: +# $cmd - Command request (keys). +# $tail - Remainder of the request, a colon separated +# list containing domain/user that identifies the +# user being queried, and the database namespace +# (database filename essentially). +# $client - File open on the client. +# Returns: +# 1 - Continue processing. +# 0 - Exit the server. +# +sub get_profile_keys { + my ($cmd, $tail, $client) = @_; + + my $userinput = "$cmd:$tail"; + + my ($udom,$uname,$namespace)=split(/:/,$tail); + my $qresult=''; + my $hashref = &tie_user_hash($udom, $uname, $namespace, + &GDBM_READER()); + if ($hashref) { + foreach my $key (keys %$hashref) { + $qresult.="$key&"; + } + if (untie(%$hashref)) { + $qresult=~s/\&$//; + &Reply($client, "$qresult\n", $userinput); + } else { + &Failure($client, "error: ".($!+0)." untie(GDBM) Failed ". + "while attempting keys\n", $userinput); + } + } else { + &Failure( $client, "error: ".($!+0)." tie(GDBM) Failed ". + "while attempting keys\n", $userinput); + } + + return 1; +} +®ister_handler("keys", \&get_profile_keys, 0, 1, 0); + +# +# Dump the contents of a user profile database. +# Note that this constitutes a very large covert channel too since +# the dump will return sensitive information that is not encrypted. +# The naive security assumption is that the session negotiation ensures +# our client is trusted and I don't believe that's assured at present. +# Sure want badly to go to ssl or tls. Of course if my peer isn't really +# a LonCAPA node they could have negotiated an encryption key too so >sigh<. +# +# Parameters: +# $cmd - The command request keyword (currentdump). +# $tail - Remainder of the request, consisting of a colon +# separated list that has the domain/username and +# the namespace to dump (database file). +# $client - file open on the remote client. +# Returns: +# 1 - Continue processing. +# 0 - Exit the server. +# +sub dump_profile_database { + my ($cmd, $tail, $client) = @_; + + my $userinput = "$cmd:$tail"; + + my ($udom,$uname,$namespace) = split(/:/,$tail); + my $hashref = &tie_user_hash($udom, $uname, $namespace, + &GDBM_READER()); + if ($hashref) { + # Structure of %data: + # $data{$symb}->{$parameter}=$value; + # $data{$symb}->{'v.'.$parameter}=$version; + # since $parameter will be unescaped, we do not + # have to worry about silly parameter names... + + my $qresult=''; + my %data = (); # A hash of anonymous hashes.. + while (my ($key,$value) = each(%$hashref)) { + my ($v,$symb,$param) = split(/:/,$key); + next if ($v eq 'version' || $symb eq 'keys'); + next if (exists($data{$symb}) && + exists($data{$symb}->{$param}) && + $data{$symb}->{'v.'.$param} > $v); + $data{$symb}->{$param}=$value; + $data{$symb}->{'v.'.$param}=$v; + } + if (untie(%$hashref)) { + while (my ($symb,$param_hash) = each(%data)) { + while(my ($param,$value) = each (%$param_hash)){ + next if ($param =~ /^v\./); # Ignore versions... + # + # Just dump the symb=value pairs separated by & + # + $qresult.=$symb.':'.$param.'='.$value.'&'; + } + } + chop($qresult); + &Reply($client , "$qresult\n", $userinput); + } else { + &Failure( $client, "error: ".($!+0)." untie(GDBM) Failed ". + "while attempting currentdump\n", $userinput); + } + } else { + &Failure($client, "error: ".($!+0)." tie(GDBM) Failed ". + "while attempting currentdump\n", $userinput); + } + + return 1; +} +®ister_handler("currentdump", \&dump_profile_database, 0, 1, 0); + +# +# Dump a profile database with an optional regular expression +# to match against the keys. In this dump, no effort is made +# to separate symb from version information. Presumably the +# databases that are dumped by this command are of a different +# structure. Need to look at this and improve the documentation of +# both this and the currentdump handler. +# Parameters: +# $cmd - The command keyword. +# $tail - All of the characters after the $cmd: +# These are expected to be a colon +# separated list containing: +# domain/user - identifying the user. +# namespace - identifying the database. +# regexp - optional regular expression +# that is matched against +# database keywords to do +# selective dumps. +# $client - Channel open on the client. +# Returns: +# 1 - Continue processing. +# Side effects: +# response is written to $client. +# +sub dump_with_regexp { + my ($cmd, $tail, $client) = @_; + + + my $userinput = "$cmd:$tail"; + + my ($udom,$uname,$namespace,$regexp)=split(/:/,$tail); + if (defined($regexp)) { + $regexp=&unescape($regexp); + } else { + $regexp='.'; + } + my $hashref = &tie_user_hash($udom, $uname, $namespace, + &GDBM_READER()); + if ($hashref) { + my $qresult=''; + while (my ($key,$value) = each(%$hashref)) { + if ($regexp eq '.') { + $qresult.=$key.'='.$value.'&'; + } else { + my $unescapeKey = &unescape($key); + if (eval('$unescapeKey=~/$regexp/')) { + $qresult.="$key=$value&"; + } + } + } + if (untie(%$hashref)) { + chop($qresult); + &Reply($client, "$qresult\n", $userinput); + } else { + &Failure( $client, "error: ".($!+0)." untie(GDBM) Failed ". + "while attempting dump\n", $userinput); + } + } else { + &Failure($client, "error: ".($!+0)." tie(GDBM) Failed ". + "while attempting dump\n", $userinput); + } + + return 1; +} + +®ister_handler("dump", \&dump_with_regexp, 0, 1, 0); + +# Store a set of key=value pairs associated with a versioned name. +# +# Parameters: +# $cmd - Request command keyword. +# $tail - Tail of the request. This is a colon +# separated list containing: +# domain/user - User and authentication domain. +# namespace - Name of the database being modified +# rid - Resource keyword to modify. +# what - new value associated with rid. +# +# $client - Socket open on the client. +# +# +# Returns: +# 1 (keep on processing). +# Side-Effects: +# Writes to the client +sub store_handler { + my ($cmd, $tail, $client) = @_; + + my $userinput = "$cmd:$tail"; + + my ($udom,$uname,$namespace,$rid,$what) =split(/:/,$tail); + if ($namespace ne 'roles') { + + chomp($what); + my @pairs=split(/\&/,$what); + my $hashref = &tie_user_hash($udom, $uname, $namespace, + &GDBM_WRCREAT(), "P", + "$rid:$what"); + if ($hashref) { + my $now = time; + my @previouskeys=split(/&/,$hashref->{"keys:$rid"}); + my $key; + $hashref->{"version:$rid"}++; + my $version=$hashref->{"version:$rid"}; + my $allkeys=''; + foreach my $pair (@pairs) { + my ($key,$value)=split(/=/,$pair); + $allkeys.=$key.':'; + $hashref->{"$version:$rid:$key"}=$value; + } + $hashref->{"$version:$rid:timestamp"}=$now; + $allkeys.='timestamp'; + $hashref->{"$version:keys:$rid"}=$allkeys; + if (untie($hashref)) { + &Reply($client, "ok\n", $userinput); + } else { + &Failure($client, "error: ".($!+0)." untie(GDBM) Failed ". + "while attempting store\n", $userinput); + } + } else { + &Failure( $client, "error: ".($!+0)." tie(GDBM) Failed ". + "while attempting store\n", $userinput); + } + } else { + &Failure($client, "refused\n", $userinput); + } + + return 1; +} +®ister_handler("store", \&store_handler, 0, 1, 0); +# +# Dump out all versions of a resource that has key=value pairs associated +# with it for each version. These resources are built up via the store +# command. +# +# Parameters: +# $cmd - Command keyword. +# $tail - Remainder of the request which consists of: +# domain/user - User and auth. domain. +# namespace - name of resource database. +# rid - Resource id. +# $client - socket open on the client. +# +# Returns: +# 1 indicating the caller should not yet exit. +# Side-effects: +# Writes a reply to the client. +# The reply is a string of the following shape: +# version=current&version:keys=k1:k2...&1:k1=v1&1:k2=v2... +# Where the 1 above represents version 1. +# this continues for all pairs of keys in all versions. +# +# +# +# +sub restore_handler { + my ($cmd, $tail, $client) = @_; + + my $userinput = "$cmd:$tail"; # Only used for logging purposes. + + my ($cmd,$udom,$uname,$namespace,$rid) = split(/:/,$userinput); + $namespace=~s/\//\_/g; + $namespace=~s/\W//g; + chomp($rid); + my $proname=&propath($udom,$uname); + my $qresult=''; + my %hash; + if (tie(%hash,'GDBM_File',"$proname/$namespace.db", + &GDBM_READER(),0640)) { + my $version=$hash{"version:$rid"}; + $qresult.="version=$version&"; + my $scope; + for ($scope=1;$scope<=$version;$scope++) { + my $vkeys=$hash{"$scope:keys:$rid"}; + my @keys=split(/:/,$vkeys); + my $key; + $qresult.="$scope:keys=$vkeys&"; + foreach $key (@keys) { + $qresult.="$scope:$key=".$hash{"$scope:$rid:$key"}."&"; + } + } + if (untie(%hash)) { + $qresult=~s/\&$//; + &Reply( $client, "$qresult\n", $userinput); + } else { + &Failure($client, "error: ".($!+0)." untie(GDBM) Failed ". + "while attempting restore\n", $userinput); + } + } else { + &Failure($client, "error: ".($!+0)." tie(GDBM) Failed ". + "while attempting restore\n", $userinput); + } + + return 1; + + +} +®ister_handler("restore", \&restore_handler, 0,1,0); +# +# #--------------------------------------------------------------- # # Getting, decoding and dispatching requests: @@ -1523,7 +2708,7 @@ register_handler("passwd", \&change_pass # Gets a Request message from the client. The transaction # is defined as a 'line' of text. We remove the new line # from the text line. -# +# sub get_request { my $input = <$client>; chomp($input); @@ -1628,823 +2813,8 @@ sub process_request { -# -------------------------------------------------------------------- makeuser - if ($userinput =~ /^makeuser/) { # encoded and client. - &Debug("Make user received"); - my $oldumask=umask(0077); - if (($wasenc==1) && isClient) { - my - ($cmd,$udom,$uname,$umode,$npass)=split(/:/,$userinput); - &Debug("cmd =".$cmd." $udom =".$udom. - " uname=".$uname); - chomp($npass); - $npass=&unescape($npass); - my $proname=propath($udom,$uname); - my $passfilename="$proname/passwd"; - &Debug("Password file created will be:". - $passfilename); - if (-e $passfilename) { - print $client "already_exists\n"; - } elsif ($udom ne $currentdomainid) { - print $client "not_right_domain\n"; - } else { - my @fpparts=split(/\//,$proname); - my $fpnow=$fpparts[0].'/'.$fpparts[1].'/'.$fpparts[2]; - my $fperror=''; - for (my $i=3;$i<=$#fpparts;$i++) { - $fpnow.='/'.$fpparts[$i]; - unless (-e $fpnow) { - unless (mkdir($fpnow,0777)) { - $fperror="error: ".($!+0) - ." mkdir failed while attempting " - ."makeuser"; - } - } - } - unless ($fperror) { - my $result=&make_passwd_file($uname, $umode,$npass, - $passfilename); - print $client $result; - } else { - print $client "$fperror\n"; - } - } - } else { - Reply($client, "refused\n", $userinput); - - } - umask($oldumask); -# -------------------------------------------------------------- changeuserauth - } elsif ($userinput =~ /^changeuserauth/) { # encoded & client - &Debug("Changing authorization"); - if (($wasenc==1) && isClient) { - my - ($cmd,$udom,$uname,$umode,$npass)=split(/:/,$userinput); - chomp($npass); - &Debug("cmd = ".$cmd." domain= ".$udom. - "uname =".$uname." umode= ".$umode); - $npass=&unescape($npass); - my $proname=&propath($udom,$uname); - my $passfilename="$proname/passwd"; - if ($udom ne $currentdomainid) { - print $client "not_right_domain\n"; - } else { - my $result=&make_passwd_file($uname, $umode,$npass, - $passfilename); - Reply($client, $result, $userinput); - } - } else { - Reply($client, "refused\n", $userinput); - - } -# ------------------------------------------------------------------------ home - } elsif ($userinput =~ /^home/) { # client clear or encoded - if(isClient) { - my ($cmd,$udom,$uname)=split(/:/,$userinput); - chomp($uname); - my $proname=propath($udom,$uname); - if (-e $proname) { - print $client "found\n"; - } else { - print $client "not_found\n"; - } - } else { - Reply($client, "refused\n", $userinput); - - } -# ---------------------------------------------------------------------- update - } elsif ($userinput =~ /^update/) { # client clear or encoded. - if(isClient) { - my ($cmd,$fname)=split(/:/,$userinput); - my $ownership=ishome($fname); - if ($ownership eq 'not_owner') { - if (-e $fname) { - my ($dev,$ino,$mode,$nlink, - $uid,$gid,$rdev,$size, - $atime,$mtime,$ctime, - $blksize,$blocks)=stat($fname); - my $now=time; - my $since=$now-$atime; - if ($since>$perlvar{'lonExpire'}) { - my $reply= - &reply("unsub:$fname","$clientname"); - unlink("$fname"); - } else { - my $transname="$fname.in.transfer"; - my $remoteurl= - &reply("sub:$fname","$clientname"); - my $response; - { - my $ua=new LWP::UserAgent; - my $request=new HTTP::Request('GET',"$remoteurl"); - $response=$ua->request($request,$transname); - } - if ($response->is_error()) { - unlink($transname); - my $message=$response->status_line; - &logthis( - "LWP GET: $message for $fname ($remoteurl)"); - } else { - if ($remoteurl!~/\.meta$/) { - 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'); - } - } - rename($transname,$fname); - } - } - print $client "ok\n"; - } else { - print $client "not_found\n"; - } - } else { - print $client "rejected\n"; - } - } else { - Reply($client, "refused\n", $userinput); - - } -# -------------------------------------- fetch a user file from a remote server - } elsif ($userinput =~ /^fetchuserfile/) { # Client clear or enc. - if(isClient) { - my ($cmd,$fname)=split(/:/,$userinput); - my ($udom,$uname,$ufile) = ($fname =~ m|^([^/]+)/([^/]+)/(.+)$|); - my $udir=propath($udom,$uname).'/userfiles'; - unless (-e $udir) { mkdir($udir,0770); } - if (-e $udir) { - $ufile=~s/^[\.\~]+//; - my $path = $udir; - if ($ufile =~m|(.+)/([^/]+)$|) { - my @parts=split('/',$1); - foreach my $part (@parts) { - $path .= '/'.$part; - if ((-e $path)!=1) { - mkdir($path,0770); - } - } - } - my $destname=$udir.'/'.$ufile; - my $transname=$udir.'/'.$ufile.'.in.transit'; - my $remoteurl='http://'.$clientip.'/userfiles/'.$fname; - my $response; - { - my $ua=new LWP::UserAgent; - my $request=new HTTP::Request('GET',"$remoteurl"); - $response=$ua->request($request,$transname); - } - if ($response->is_error()) { - unlink($transname); - my $message=$response->status_line; - &logthis("LWP GET: $message for $fname ($remoteurl)"); - print $client "failed\n"; - } else { - if (!rename($transname,$destname)) { - &logthis("Unable to move $transname to $destname"); - unlink($transname); - print $client "failed\n"; - } else { - print $client "ok\n"; - } - } - } else { - print $client "not_home\n"; - } - } else { - Reply($client, "refused\n", $userinput); - } -# --------------------------------------------------------- remove a user file - } elsif ($userinput =~ /^removeuserfile/) { # Client clear or enc. - if(isClient) { - my ($cmd,$fname)=split(/:/,$userinput); - my ($udom,$uname,$ufile) = ($fname =~ m|^([^/]+)/([^/]+)/(.+)$|); - &logthis("$udom - $uname - $ufile"); - if ($ufile =~m|/\.\./|) { - # any files paths with /../ in them refuse - # to deal with - print $client "refused\n"; - } else { - my $udir=propath($udom,$uname); - if (-e $udir) { - my $file=$udir.'/userfiles/'.$ufile; - if (-e $file) { - unlink($file); - if (-e $file) { - print $client "failed\n"; - } else { - print $client "ok\n"; - } - } else { - print $client "not_found\n"; - } - } else { - print $client "not_home\n"; - } - } - } else { - Reply($client, "refused\n", $userinput); - } -# ------------------------------------------ authenticate access to a user file - } elsif ($userinput =~ /^tokenauthuserfile/) { # Client only - if(isClient) { - my ($cmd,$fname,$session)=split(/:/,$userinput); - chomp($session); - my $reply='non_auth'; - if (open(ENVIN,$perlvar{'lonIDsDir'}.'/'. - $session.'.id')) { - while (my $line=) { - if ($line=~ m|userfile\.\Q$fname\E\=|) { $reply='ok'; } - } - close(ENVIN); - print $client $reply."\n"; - } else { - print $client "invalid_token\n"; - } - } else { - Reply($client, "refused\n", $userinput); - - } -# ----------------------------------------------------------------- unsubscribe - } elsif ($userinput =~ /^unsub/) { - if(isClient) { - my ($cmd,$fname)=split(/:/,$userinput); - if (-e $fname) { - print $client &unsub($fname,$clientip); - } else { - print $client "not_found\n"; - } - } else { - Reply($client, "refused\n", $userinput); - - } -# ------------------------------------------------------------------- subscribe - } elsif ($userinput =~ /^sub/) { - if(isClient) { - print $client &subscribe($userinput,$clientip); - } else { - Reply($client, "refused\n", $userinput); - - } -# ------------------------------------------------------------- current version - } elsif ($userinput =~ /^currentversion/) { - if(isClient) { - my ($cmd,$fname)=split(/:/,$userinput); - print $client ¤tversion($fname)."\n"; - } else { - Reply($client, "refused\n", $userinput); - - } -# ------------------------------------------------------------------------- log - } elsif ($userinput =~ /^log/) { - if(isClient) { - my ($cmd,$udom,$uname,$what)=split(/:/,$userinput); - chomp($what); - my $proname=propath($udom,$uname); - my $now=time; - { - my $hfh; - if ($hfh=IO::File->new(">>$proname/activity.log")) { - print $hfh "$now:$clientname:$what\n"; - print $client "ok\n"; - } else { - print $client "error: ".($!+0) - ." IO::File->new Failed " - ."while attempting log\n"; - } - } - } else { - Reply($client, "refused\n", $userinput); - - } -# ------------------------------------------------------------------------- put - } elsif ($userinput =~ /^put/) { - if(isClient) { - my ($cmd,$udom,$uname,$namespace,$what) - =split(/:/,$userinput,5); - $namespace=~s/\//\_/g; - $namespace=~s/\W//g; - if ($namespace ne 'roles') { - chomp($what); - my $proname=propath($udom,$uname); - my $now=time; - my @pairs=split(/\&/,$what); - my %hash; - if (tie(%hash,'GDBM_File', - "$proname/$namespace.db", - &GDBM_WRCREAT(),0640)) { - unless ($namespace=~/^nohist\_/) { - my $hfh; - if ($hfh=IO::File->new(">>$proname/$namespace.hist")) { print $hfh "P:$now:$what\n"; } - } - - foreach my $pair (@pairs) { - my ($key,$value)=split(/=/,$pair); - $hash{$key}=$value; - } - if (untie(%hash)) { - print $client "ok\n"; - } else { - print $client "error: ".($!+0) - ." untie(GDBM) failed ". - "while attempting put\n"; - } - } else { - print $client "error: ".($!) - ." tie(GDBM) Failed ". - "while attempting put\n"; - } - } else { - print $client "refused\n"; - } - } else { - Reply($client, "refused\n", $userinput); - - } -# ------------------------------------------------------------------- inc - } elsif ($userinput =~ /^inc:/) { - if(isClient) { - my ($cmd,$udom,$uname,$namespace,$what) - =split(/:/,$userinput); - $namespace=~s/\//\_/g; - $namespace=~s/\W//g; - if ($namespace ne 'roles') { - chomp($what); - my $proname=propath($udom,$uname); - my $now=time; - my @pairs=split(/\&/,$what); - my %hash; - if (tie(%hash,'GDBM_File', - "$proname/$namespace.db", - &GDBM_WRCREAT(),0640)) { - unless ($namespace=~/^nohist\_/) { - my $hfh; - if ($hfh=IO::File->new(">>$proname/$namespace.hist")) { print $hfh "P:$now:$what\n"; } - } - foreach my $pair (@pairs) { - my ($key,$value)=split(/=/,$pair); - # We could check that we have a number... - if (! defined($value) || $value eq '') { - $value = 1; - } - $hash{$key}+=$value; - } - if (untie(%hash)) { - print $client "ok\n"; - } else { - print $client "error: ".($!+0) - ." untie(GDBM) failed ". - "while attempting inc\n"; - } - } else { - print $client "error: ".($!) - ." tie(GDBM) Failed ". - "while attempting inc\n"; - } - } else { - print $client "refused\n"; - } - } else { - Reply($client, "refused\n", $userinput); - - } -# -------------------------------------------------------------------- rolesput - } elsif ($userinput =~ /^rolesput/) { - if(isClient) { - &Debug("rolesput"); - if ($wasenc==1) { - my ($cmd,$exedom,$exeuser,$udom,$uname,$what) - =split(/:/,$userinput); - &Debug("cmd = ".$cmd." exedom= ".$exedom. - "user = ".$exeuser." udom=".$udom. - "what = ".$what); - my $namespace='roles'; - chomp($what); - my $proname=propath($udom,$uname); - my $now=time; - my @pairs=split(/\&/,$what); - my %hash; - if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT(),0640)) { - { - my $hfh; - if ($hfh=IO::File->new(">>$proname/$namespace.hist")) { - print $hfh "P:$now:$exedom:$exeuser:$what\n"; - } - } - - foreach my $pair (@pairs) { - my ($key,$value)=split(/=/,$pair); - &ManagePermissions($key, $udom, $uname, - &get_auth_type( $udom, - $uname)); - $hash{$key}=$value; - } - if (untie(%hash)) { - print $client "ok\n"; - } else { - print $client "error: ".($!+0) - ." untie(GDBM) Failed ". - "while attempting rolesput\n"; - } - } else { - print $client "error: ".($!+0) - ." tie(GDBM) Failed ". - "while attempting rolesput\n"; - } - } else { - print $client "refused\n"; - } - } else { - Reply($client, "refused\n", $userinput); - - } -# -------------------------------------------------------------------- rolesdel - } elsif ($userinput =~ /^rolesdel/) { - if(isClient) { - &Debug("rolesdel"); - if ($wasenc==1) { - my ($cmd,$exedom,$exeuser,$udom,$uname,$what) - =split(/:/,$userinput); - &Debug("cmd = ".$cmd." exedom= ".$exedom. - "user = ".$exeuser." udom=".$udom. - "what = ".$what); - my $namespace='roles'; - chomp($what); - my $proname=propath($udom,$uname); - my $now=time; - my @rolekeys=split(/\&/,$what); - my %hash; - if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT(),0640)) { - { - my $hfh; - if ($hfh=IO::File->new(">>$proname/$namespace.hist")) { - print $hfh "D:$now:$exedom:$exeuser:$what\n"; - } - } - foreach my $key (@rolekeys) { - delete $hash{$key}; - } - if (untie(%hash)) { - print $client "ok\n"; - } else { - print $client "error: ".($!+0) - ." untie(GDBM) Failed ". - "while attempting rolesdel\n"; - } - } else { - print $client "error: ".($!+0) - ." tie(GDBM) Failed ". - "while attempting rolesdel\n"; - } - } else { - print $client "refused\n"; - } - } else { - Reply($client, "refused\n", $userinput); - - } -# ------------------------------------------------------------------------- get - } elsif ($userinput =~ /^get/) { - if(isClient) { - my ($cmd,$udom,$uname,$namespace,$what) - =split(/:/,$userinput); - $namespace=~s/\//\_/g; - $namespace=~s/\W//g; - chomp($what); - my @queries=split(/\&/,$what); - my $proname=propath($udom,$uname); - my $qresult=''; - my %hash; - if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER(),0640)) { - for (my $i=0;$i<=$#queries;$i++) { - $qresult.="$hash{$queries[$i]}&"; - } - if (untie(%hash)) { - $qresult=~s/\&$//; - print $client "$qresult\n"; - } else { - print $client "error: ".($!+0) - ." untie(GDBM) Failed ". - "while attempting get\n"; - } - } else { - if ($!+0 == 2) { - print $client "error:No such file or ". - "GDBM reported bad block error\n"; - } else { - print $client "error: ".($!+0) - ." tie(GDBM) Failed ". - "while attempting get\n"; - } - } - } else { - Reply($client, "refused\n", $userinput); - - } -# ------------------------------------------------------------------------ eget - } elsif ($userinput =~ /^eget/) { - if (isClient) { - my ($cmd,$udom,$uname,$namespace,$what) - =split(/:/,$userinput); - $namespace=~s/\//\_/g; - $namespace=~s/\W//g; - chomp($what); - my @queries=split(/\&/,$what); - my $proname=propath($udom,$uname); - my $qresult=''; - my %hash; - if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER(),0640)) { - for (my $i=0;$i<=$#queries;$i++) { - $qresult.="$hash{$queries[$i]}&"; - } - if (untie(%hash)) { - $qresult=~s/\&$//; - 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))); - } - print $client "enc:$cmdlength:$encqresult\n"; - } else { - print $client "error:no_key\n"; - } - } else { - print $client "error: ".($!+0) - ." untie(GDBM) Failed ". - "while attempting eget\n"; - } - } else { - print $client "error: ".($!+0) - ." tie(GDBM) Failed ". - "while attempting eget\n"; - } - } else { - Reply($client, "refused\n", $userinput); - - } -# ------------------------------------------------------------------------- del - } elsif ($userinput =~ /^del/) { - if(isClient) { - my ($cmd,$udom,$uname,$namespace,$what) - =split(/:/,$userinput); - $namespace=~s/\//\_/g; - $namespace=~s/\W//g; - chomp($what); - my $proname=propath($udom,$uname); - my $now=time; - my @keys=split(/\&/,$what); - my %hash; - if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT(),0640)) { - unless ($namespace=~/^nohist\_/) { - my $hfh; - if ($hfh=IO::File->new(">>$proname/$namespace.hist")) { print $hfh "D:$now:$what\n"; } - } - foreach my $key (@keys) { - delete($hash{$key}); - } - if (untie(%hash)) { - print $client "ok\n"; - } else { - print $client "error: ".($!+0) - ." untie(GDBM) Failed ". - "while attempting del\n"; - } - } else { - print $client "error: ".($!+0) - ." tie(GDBM) Failed ". - "while attempting del\n"; - } - } else { - Reply($client, "refused\n", $userinput); - - } -# ------------------------------------------------------------------------ keys - } elsif ($userinput =~ /^keys/) { - if(isClient) { - my ($cmd,$udom,$uname,$namespace) - =split(/:/,$userinput); - $namespace=~s/\//\_/g; - $namespace=~s/\W//g; - my $proname=propath($udom,$uname); - my $qresult=''; - my %hash; - if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER(),0640)) { - foreach my $key (keys %hash) { - $qresult.="$key&"; - } - if (untie(%hash)) { - $qresult=~s/\&$//; - print $client "$qresult\n"; - } else { - print $client "error: ".($!+0) - ." untie(GDBM) Failed ". - "while attempting keys\n"; - } - } else { - print $client "error: ".($!+0) - ." tie(GDBM) Failed ". - "while attempting keys\n"; - } - } else { - Reply($client, "refused\n", $userinput); - - } -# ----------------------------------------------------------------- dumpcurrent - } elsif ($userinput =~ /^currentdump/) { - if (isClient) { - my ($cmd,$udom,$uname,$namespace) - =split(/:/,$userinput); - $namespace=~s/\//\_/g; - $namespace=~s/\W//g; - my $qresult=''; - my $proname=propath($udom,$uname); - my %hash; - if (tie(%hash,'GDBM_File', - "$proname/$namespace.db", - &GDBM_READER(),0640)) { - # Structure of %data: - # $data{$symb}->{$parameter}=$value; - # $data{$symb}->{'v.'.$parameter}=$version; - # since $parameter will be unescaped, we do not - # have to worry about silly parameter names... - my %data = (); - while (my ($key,$value) = each(%hash)) { - my ($v,$symb,$param) = split(/:/,$key); - next if ($v eq 'version' || $symb eq 'keys'); - next if (exists($data{$symb}) && - exists($data{$symb}->{$param}) && - $data{$symb}->{'v.'.$param} > $v); - $data{$symb}->{$param}=$value; - $data{$symb}->{'v.'.$param}=$v; - } - if (untie(%hash)) { - while (my ($symb,$param_hash) = each(%data)) { - while(my ($param,$value) = each (%$param_hash)){ - next if ($param =~ /^v\./); - $qresult.=$symb.':'.$param.'='.$value.'&'; - } - } - chop($qresult); - print $client "$qresult\n"; - } else { - print $client "error: ".($!+0) - ." untie(GDBM) Failed ". - "while attempting currentdump\n"; - } - } else { - print $client "error: ".($!+0) - ." tie(GDBM) Failed ". - "while attempting currentdump\n"; - } - } else { - Reply($client, "refused\n", $userinput); - } -# ------------------------------------------------------------------------ dump - } elsif ($userinput =~ /^dump/) { - if(isClient) { - my ($cmd,$udom,$uname,$namespace,$regexp) - =split(/:/,$userinput); - $namespace=~s/\//\_/g; - $namespace=~s/\W//g; - if (defined($regexp)) { - $regexp=&unescape($regexp); - } else { - $regexp='.'; - } - my $qresult=''; - my $proname=propath($udom,$uname); - my %hash; - if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER(),0640)) { - while (my ($key,$value) = each(%hash)) { - if ($regexp eq '.') { - $qresult.=$key.'='.$value.'&'; - } else { - my $unescapeKey = &unescape($key); - if (eval('$unescapeKey=~/$regexp/')) { - $qresult.="$key=$value&"; - } - } - } - if (untie(%hash)) { - chop($qresult); - print $client "$qresult\n"; - } else { - print $client "error: ".($!+0) - ." untie(GDBM) Failed ". - "while attempting dump\n"; - } - } else { - print $client "error: ".($!+0) - ." tie(GDBM) Failed ". - "while attempting dump\n"; - } - } else { - Reply($client, "refused\n", $userinput); - - } -# ----------------------------------------------------------------------- store - } elsif ($userinput =~ /^store/) { - if(isClient) { - my ($cmd,$udom,$uname,$namespace,$rid,$what) - =split(/:/,$userinput); - $namespace=~s/\//\_/g; - $namespace=~s/\W//g; - if ($namespace ne 'roles') { - chomp($what); - my $proname=propath($udom,$uname); - my $now=time; - my @pairs=split(/\&/,$what); - my %hash; - if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT(),0640)) { - unless ($namespace=~/^nohist\_/) { - my $hfh; - if ($hfh=IO::File->new(">>$proname/$namespace.hist")) { - print $hfh "P:$now:$rid:$what\n"; - } - } - my @previouskeys=split(/&/,$hash{"keys:$rid"}); - my $key; - $hash{"version:$rid"}++; - my $version=$hash{"version:$rid"}; - my $allkeys=''; - foreach my $pair (@pairs) { - my ($key,$value)=split(/=/,$pair); - $allkeys.=$key.':'; - $hash{"$version:$rid:$key"}=$value; - } - $hash{"$version:$rid:timestamp"}=$now; - $allkeys.='timestamp'; - $hash{"$version:keys:$rid"}=$allkeys; - if (untie(%hash)) { - print $client "ok\n"; - } else { - print $client "error: ".($!+0) - ." untie(GDBM) Failed ". - "while attempting store\n"; - } - } else { - print $client "error: ".($!+0) - ." tie(GDBM) Failed ". - "while attempting store\n"; - } - } else { - print $client "refused\n"; - } - } else { - Reply($client, "refused\n", $userinput); - - } -# --------------------------------------------------------------------- restore - } elsif ($userinput =~ /^restore/) { - if(isClient) { - my ($cmd,$udom,$uname,$namespace,$rid) - =split(/:/,$userinput); - $namespace=~s/\//\_/g; - $namespace=~s/\W//g; - chomp($rid); - my $proname=propath($udom,$uname); - my $qresult=''; - my %hash; - if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER(),0640)) { - my $version=$hash{"version:$rid"}; - $qresult.="version=$version&"; - my $scope; - for ($scope=1;$scope<=$version;$scope++) { - my $vkeys=$hash{"$scope:keys:$rid"}; - my @keys=split(/:/,$vkeys); - my $key; - $qresult.="$scope:keys=$vkeys&"; - foreach $key (@keys) { - $qresult.="$scope:$key=".$hash{"$scope:$rid:$key"}."&"; - } - } - if (untie(%hash)) { - $qresult=~s/\&$//; - print $client "$qresult\n"; - } else { - print $client "error: ".($!+0) - ." untie(GDBM) Failed ". - "while attempting restore\n"; - } - } else { - print $client "error: ".($!+0) - ." tie(GDBM) Failed ". - "while attempting restore\n"; - } - } else { - Reply($client, "refused\n", $userinput); - - } # -------------------------------------------------------------------- chatsend - } elsif ($userinput =~ /^chatsend/) { + if ($userinput =~ /^chatsend/) { if(isClient) { my ($cmd,$cdom,$cnum,$newpost)=split(/\:/,$userinput); &chatadd($cdom,$cnum,$newpost); @@ -2495,8 +2865,7 @@ sub process_request { print $store2 "done\n"; close $store2; print $client "ok\n"; - } - else { + } else { print $client "error: ".($!+0) ." IO::File->new Failed ". "while attempting queryreply\n"; @@ -2811,21 +3180,21 @@ sub process_request { return 0; # ---------------------------------- set current host/domain - } elsif ($userinput =~ /^sethost:/) { + } elsif ($userinput =~ /^sethost/) { if (isClient) { print $client &sethost($userinput)."\n"; } else { print $client "refused\n"; } #---------------------------------- request file (?) version. - } elsif ($userinput =~/^version:/) { + } elsif ($userinput =~/^version/) { if (isClient) { print $client &version($userinput)."\n"; } else { print $client "refused\n"; } #------------------------------- is auto-enrollment enabled? - } elsif ($userinput =~/^autorun:/) { + } elsif ($userinput =~/^autorun/) { if (isClient) { my ($cmd,$cdom) = split(/:/,$userinput); my $outcome = &localenroll::run($cdom); @@ -2834,7 +3203,7 @@ sub process_request { print $client "0\n"; } #------------------------------- get official sections (for auto-enrollment). - } elsif ($userinput =~/^autogetsections:/) { + } elsif ($userinput =~/^autogetsections/) { if (isClient) { my ($cmd,$coursecode,$cdom)=split(/:/,$userinput); my @secs = &localenroll::get_sections($coursecode,$cdom); @@ -2844,7 +3213,7 @@ sub process_request { print $client "refused\n"; } #----------------------- validate owner of new course section (for auto-enrollment). - } elsif ($userinput =~/^autonewcourse:/) { + } elsif ($userinput =~/^autonewcourse/) { if (isClient) { my ($cmd,$inst_course_id,$owner,$cdom)=split(/:/,$userinput); my $outcome = &localenroll::new_course($inst_course_id,$owner,$cdom); @@ -2853,7 +3222,7 @@ sub process_request { print $client "refused\n"; } #-------------- validate course section in schedule of classes (for auto-enrollment). - } elsif ($userinput =~/^autovalidatecourse:/) { + } elsif ($userinput =~/^autovalidatecourse/) { if (isClient) { my ($cmd,$inst_course_id,$cdom)=split(/:/,$userinput); my $outcome=&localenroll::validate_courseID($inst_course_id,$cdom); @@ -2862,7 +3231,7 @@ sub process_request { print $client "refused\n"; } #--------------------------- create password for new user (for auto-enrollment). - } elsif ($userinput =~/^autocreatepassword:/) { + } elsif ($userinput =~/^autocreatepassword/) { if (isClient) { my ($cmd,$authparam,$cdom)=split(/:/,$userinput); my ($create_passwd,$authchk); @@ -2872,7 +3241,7 @@ sub process_request { print $client "refused\n"; } #--------------------------- read and remove temporary files (for auto-enrollment). - } elsif ($userinput =~/^autoretrieve:/) { + } elsif ($userinput =~/^autoretrieve/) { if (isClient) { my ($cmd,$filename) = split(/:/,$userinput); my $source = $perlvar{'lonDaemons'}.'/tmp/'.$filename; @@ -2898,7 +3267,7 @@ sub process_request { print $client "refused\n"; } #--------------------- read and retrieve institutional code format (for support form). - } elsif ($userinput =~/^autoinstcodeformat:/) { + } elsif ($userinput =~/^autoinstcodeformat/) { if (isClient) { my $reply; my($cmd,$cdom,$course) = split(/:/,$userinput); @@ -3010,7 +3379,6 @@ sub register_handler { $Dispatcher{$request_name} = \@entry; - } @@ -3057,7 +3425,6 @@ sub catchexception { $server->close(); die($error); } - sub timeout { &status("Handling Timeout"); &logthis("CRITICAL: TIME OUT ".$$.""); @@ -3065,6 +3432,7 @@ sub timeout { } # -------------------------------- Set signal handlers to record abnormal exits + $SIG{'QUIT'}=\&catchexception; $SIG{__DIE__}=\&catchexception; @@ -3694,8 +4062,7 @@ sub make_new_child { $inittype = ""; # This forces insecure attempt. &logthis(" Certificates not " ."installed -- trying insecure auth"); - } - else { # SSL certificates are in place so + } else { # SSL certificates are in place so } # Leave the inittype alone. } @@ -3818,7 +4185,7 @@ sub make_new_child { # user - Name of the user for which the role is being put. # authtype - The authentication type associated with the user. # -sub ManagePermissions +sub manage_permissions { my ($request, $domain, $user, $authtype) = @_; @@ -3929,8 +4296,7 @@ sub get_auth_type } return "$authtype:$availinfo"; - } - else { + } else { Debug("Returning nouser"); return "nouser"; } @@ -4010,18 +4376,15 @@ sub validate_user { $password); if(!$k4error) { $validated = 1; - } - else { + } else { $validated = 0; &logthis('krb4: '.$user.', '.$contentpwd.', '. &Authen::Krb4::get_err_txt($Authen::Krb4::error)); } - } - else { + } else { $validated = 0; # Password has a match with null. } - } - elsif ($howpwd eq "krb5") { # User is in kerberos 5 auth. domain. + } elsif ($howpwd eq "krb5") { # User is in kerberos 5 auth. domain. if(!($password =~ /$null/)) { # Null password not allowed. my $krbclient = &Authen::Krb5::parse_name($user.'@' .$contentpwd); @@ -4034,18 +4397,15 @@ sub validate_user { $password, $credentials); $validated = ($krbreturn == 1); - } - else { + } else { $validated = 0; } - } - elsif ($howpwd eq "localauth") { + } elsif ($howpwd eq "localauth") { # Authenticate via installation specific authentcation method: $validated = &localauth::localauth($user, $password, $contentpwd); - } - else { # Unrecognized auth is also bad. + } else { # Unrecognized auth is also bad. $validated = 0; } } else { @@ -4308,7 +4668,8 @@ sub make_passwd_file { } my $execpath ="$perlvar{'lonDaemons'}/"."lcuseradd"; - my $lc_error_file = "/tmp/lcuseradd".$$.".status"; + + my $lc_error_file = $execdir."/tmp/lcuseradd".$$.".status"; { &Debug("Executing external: ".$execpath); &Debug("user = ".$uname.", Password =". $npass); @@ -4329,8 +4690,7 @@ sub make_passwd_file { my $error_text = &lcuseraddstrerror($useraddok); &logthis("Failed lcuseradd: $error_text"); $result = "lcuseradd_failed:$error_text\n"; - } - else { + } else { my $pf = IO::File->new(">$passfilename"); print $pf "unix:\n"; } @@ -4666,7 +5026,7 @@ Place in B stores hash in namespace -=item rolesput +=item rolesputy put a role into a user's environment