--- loncom/lond 2004/05/07 17:57:18 1.178.2.23 +++ loncom/lond 2004/02/24 16:53:16 1.179 @@ -2,7 +2,7 @@ # The LearningOnline Network # lond "LON Daemon" Server (port "LOND" 5663) # -# $Id: lond,v 1.178.2.23 2004/05/07 17:57:18 www Exp $ +# $Id: lond,v 1.179 2004/02/24 16:53:16 albertel Exp $ # # Copyright Michigan State University Board of Trustees # @@ -20,7 +20,7 @@ # # You should have received a copy of the GNU General Public License # along with LON-CAPA; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # /home/httpd/html/adm/gpl.txt # @@ -48,12 +48,12 @@ use localauth; use File::Copy; use LONCAPA::ConfigFileEdit; -my $DEBUG = 1; # Non zero to enable debug log entries. +my $DEBUG = 0; # Non zero to enable debug log entries. my $status=''; my $lastlog=''; -my $VERSION='$Revision: 1.178.2.23 $'; #' stupid emacs +my $VERSION='$Revision: 1.179 $'; #' stupid emacs my $remoteVERSION; my $currenthostid; my $currentdomainid; @@ -62,9 +62,6 @@ my $client; my $clientip; my $clientname; -my $cipher; # Cipher key negotiated with client. -my $tmpsnum = 0;; # Id of tmpputs. - my $server; my $thisserver; @@ -86,19 +83,6 @@ my %managers; # Ip -> manager names my %perlvar; # Will have the apache conf defined perl vars. # -# The hash below is used for command dispatching, and is therefore keyed on the request keyword. -# Each element of the hash contains a reference to an array that contains: -# A reference to a sub that executes the request corresponding to the keyword. -# A flag that is true if the request must be encoded to be acceptable. -# A mask with bits as follows: -# CLIENT_OK - Set when the function is allowed by ordinary clients -# MANAGER_OK - Set when the function is allowed to manager clients. -# -my $CLIENT_OK = 1; -my $MANAGER_OK = 2; -my %Dispatcher; - -# # The array below are password error strings." # my $lastpwderror = 13; # Largest error number from lcpasswd. @@ -135,2632 +119,6 @@ my @adderrors = ("ok", "lcuseradd Could not add user.", "lcuseradd Password mismatch"); -# -# Statistics that are maintained and dislayed in the status line. -# -my $Transactions; # Number of attempted transactions. -my $Failures; # Number of transcations failed. - -# ResetStatistics: -# Resets the statistics counters: -# -sub ResetStatistics { - $Transactions = 0; - $Failures = 0; -} - -# -# Return true if client is a manager. -# -sub isManager { - return (($ConnectionType eq "manager") || ($ConnectionType eq "both")); -} -# -# Return tru if client can do client functions -# -sub isClient { - return (($ConnectionType eq "client") || ($ConnectionType eq "both")); -} -# -# Ties a domain level resource file to a hash. -# If requested a history entry is created in the associated hist file. -# -# Parameters: -# domain - Name of the domain in which the resource file lives. -# namespace - Name of the hash within that domain. -# how - How to tie the hash (e.g. GDBM_WRCREAT()). -# loghead - Optional parameter, if present a log entry is created -# in the associated history file and this is the first part -# of that entry. -# logtail - Goes along with loghead, The actual logentry is of the -# form $loghead::logtail. -# Returns: -# Reference to a hash bound to the db file or alternatively undef -# if the tie failed. -# -sub TieDomainHash { - my $domain = shift; - my $namespace = shift; - my $how = shift; - - # Filter out any whitespace in the domain name: - - $domain =~ s/\W//g; - - # We have enough to go on to tie the hash: - - my $UserTopDir = $perlvar{'lonUsersDir'}; - my $DomainDir = $UserTopDir."/$domain"; - my $ResourceFile = $DomainDir."/$namespace.db"; - my %hash; - if(tie(%hash, 'GDBM_File', $ResourceFile, $how, 0640)) { - if (scalar @_) { # Need to log the operation. - my $logFh = IO::File->new(">>$DomainDir/$namespace.hist"); - if($logFh) { - my $TimeStamp = time; - my ($loghead, $logtail) = @_; - print $logFh "$loghead:$TimeStamp:$logtail\n"; - } - } - return \%hash; # Return the tied hash. - } - else { - return undef; # Tie failed. - } -} - -# -# Ties a user's resource file to a hash. -# If necessary, an appropriate history -# log file entry is made as well. -# This sub factors out common code from the subs that manipulate -# the various gdbm files that keep keyword value pairs. -# Parameters: -# domain - Name of the domain the user is in. -# user - Name of the 'current user'. -# namespace - Namespace representing the file to tie. -# how - What the tie is done to (e.g. GDBM_WRCREAT(). -# loghead - Optional first part of log entry if there may be a -# history file. -# what - Optional tail of log entry if there may be a history -# file. -# Returns: -# hash to which the database is tied. It's up to the caller to untie. -# undef if the has could not be tied. -# -sub TieUserHash { - my $domain = shift; - my $user = shift; - my $namespace = shift; - my $how = shift; - - $namespace=~s/\//\_/g; # / -> _ - $namespace=~s/\W//g; # whitespace eliminated. - my $proname = propath($domain, $user); - - # If this is a namespace for which a history is kept, - # make the history log entry: - - - if (($namespace =~/^nohist\_/) && (scalar @_ > 0)) { - my $args = scalar @_; - Debug(" Opening history: $namespace $args"); - my $hfh = IO::File->new(">>$proname/$namespace.hist"); - if($hfh) { - my $now = time; - my $loghead = shift; - my $what = shift; - print $hfh "$loghead:$now:$what\n"; - } - } - # Tie the database. - - my %hash; - if(tie(%hash, 'GDBM_File', "$proname/$namespace.db", - $how, 0640)) { - return \%hash; - } - else { - return undef; - } - -} - -# -# Get a Request: -# 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 GetRequest { - my $input = <$client>; - chomp($input); - - Debug("Request = $input\n"); - - &status('Processing '.$clientname.':'.$input); - - return $input; -} -# -# Decipher encoded traffic -# Parameters: -# input - Encoded data. -# Returns: -# Decoded data or undef if encryption key was not yet negotiated. -# Implicit input: -# cipher - This global holds the negotiated encryption key. -# -sub Decipher { - my $input = shift; - my $output = ''; - - - if($cipher) { - my($enc, $enclength, $encinput) = split(/:/, $input); - for(my $encidx = 0; $encidx < length($encinput); $encidx += 16) { - $output .= - $cipher->decrypt(pack("H16", substr($encinput, $encidx, 16))); - } - return substr($output, 0, $enclength); - } else { - return undef; - } -} - -# -# Register a command processor. This function is invoked to register a sub -# to process a request. Once registered, the ProcessRequest sub can automatically -# dispatch requests to an appropriate sub, and do the top level validity checking -# as well: -# - Is the keyword recognized. -# - Is the proper client type attempting the request. -# - Is the request encrypted if it has to be. -# Parameters: -# $RequestName - Name of the request being registered. -# This is the command request that will match -# against the hash keywords to lookup the information -# associated with the dispatch information. -# $Procedure - Reference to a sub to call to process the request. -# All subs get called as follows: -# Procedure($cmd, $tail, $replyfd, $key) -# $cmd - the actual keyword that invoked us. -# $tail - the tail of the request that invoked us. -# $replyfd- File descriptor connected to the client -# $MustEncode - True if the request must be encoded to be good. -# $ClientOk - True if it's ok for a client to request this. -# $ManagerOk - True if it's ok for a manager to request this. -# Side effects: -# - On success, the Dispatcher hash has an entry added for the key $RequestName -# - On failure, the program will die as it's a bad internal bug to try to -# register a duplicate command handler. -# -sub RegisterHandler { - my $RequestName = shift; - my $Procedure = shift; - my $MustEncode = shift; - my $ClientOk = shift; - my $ManagerOk = shift; - - # Don't allow duplication# - - if (defined $Dispatcher{$RequestName}) { - die "Attempting to define a duplicate request handler for $RequestName\n"; - } - # Build the client type mask: - - my $ClientTypeMask = 0; - if($ClientOk) { - $ClientTypeMask |= $CLIENT_OK; - } - if($ManagerOk) { - $ClientTypeMask |= $MANAGER_OK; - } - - # Enter the hash: - - my @entry = ($Procedure, $MustEncode, $ClientTypeMask); - - $Dispatcher{$RequestName} = \@entry; - - -} - -#--------------------- Request Handlers -------------------------------------------- -# -# By convention each request handler registers itself prior to the sub declaration: -# - -# Handles ping requests. -# Parameters: -# $cmd - the actual keyword that invoked us. -# $tail - the tail of the request that invoked us. -# $replyfd- File descriptor connected to the client -# Implicit Inputs: -# $currenthostid - Global variable that carries the name of the host we are -# known as. -# Returns: -# 1 - Ok to continue processing. -# 0 - Program should exit. -# Side effects: -# Reply information is sent to the client. - -sub PingHandler { - my $cmd = shift; - my $tail = shift; - my $client = shift; - - Reply( $client,"$currenthostid\n","$cmd:$tail"); - - return 1; -} -RegisterHandler("ping", \&PingHandler, 0, 1, 1); # Ping unencoded, client or manager. -# -# Handles pong reequests: -# Parameters: -# $cmd - the actual keyword that invoked us. -# $tail - the tail of the request that invoked us. -# $replyfd- File descriptor connected to the client -# Implicit Inputs: -# $currenthostid - Global variable that carries the name of the host we are -# connected to. -# Returns: -# 1 - Ok to continue processing. -# 0 - Program should exit. -# Side effects: -# Reply information is sent to the client. - -sub PongHandler { - my $cmd = shift; - my $tail = shift; - my $replyfd = shift; - - my $reply=&reply("ping",$clientname); - Reply( $replyfd, "$currenthostid:$reply\n", "$cmd:$tail"); - return 1; -} -RegisterHandler("pong", \&PongHandler, 0, 1, 1); # Pong unencoded, client or manager - -# -# EstablishKeyHandler: -# Called to establish an encrypted session key with the remote client. -# -# Parameters: -# $cmd - the actual keyword that invoked us. -# $tail - the tail of the request that invoked us. -# $replyfd- File descriptor connected to the client -# Implicit Inputs: -# $currenthostid - Global variable that carries the name of the host -# known as. -# $clientname - Global variable that carries the name of the hsot we're connected to. -# Returns: -# 1 - Ok to continue processing. -# 0 - Program should exit. -# Implicit Outputs: -# Reply information is sent to the client. -# $cipher is set with a reference to a new IDEA encryption object. -# -sub EstablishKeyHandler { - my $cmd = shift; - my $tail = shift; - my $replyfd = shift; - - my $buildkey=time.$$.int(rand 100000); - $buildkey=~tr/1-6/A-F/; - $buildkey=int(rand 100000).$buildkey.int(rand 100000); - my $key=$currenthostid.$clientname; - $key=~tr/a-z/A-Z/; - $key=~tr/G-P/0-9/; - $key=~tr/Q-Z/0-9/; - $key=$key.$buildkey.$key.$buildkey.$key.$buildkey; - $key=substr($key,0,32); - my $cipherkey=pack("H32",$key); - $cipher=new IDEA $cipherkey; - Reply($replyfd, "$buildkey\n", "$cmd:$tail"); - - return 1; - -} -RegisterHandler("ekey", \&EstablishKeyHandler, 0, 1,1); - -# LoadHandler: -# Handler for the load command. Returns the current system load average -# to the requestor. -# -# Parameters: -# $cmd - the actual keyword that invoked us. -# $tail - the tail of the request that invoked us. -# $replyfd- File descriptor connected to the client -# Implicit Inputs: -# $currenthostid - Global variable that carries the name of the host -# known as. -# $clientname - Global variable that carries the name of the hsot we're connected to. -# Returns: -# 1 - Ok to continue processing. -# 0 - Program should exit. -# Side effects: -# Reply information is sent to the client. -sub LoadHandler { - my $cmd = shift; - my $tail = shift; - my $replyfd = shift; - - # Get the load average from /proc/loadavg and calculate it as a percentage of - # the allowed load limit as set by the perl global variable lonLoadLim - - my $loadavg; - my $loadfile=IO::File->new('/proc/loadavg'); - - $loadavg=<$loadfile>; - $loadavg =~ s/\s.*//g; # Extract the first field only. - - my $loadpercent=100*$loadavg/$perlvar{'lonLoadLim'}; - - Reply( $replyfd, "$loadpercent\n", "$cmd:$tail"); - - return 1; -} -RegisterHandler("load", \&LoadHandler, 0, 1, 0); - - -# -# Process the userload request. This sub returns to the client the current -# user load average. It can be invoked either by clients or managers. -# -# Parameters: -# $cmd - the actual keyword that invoked us. -# $tail - the tail of the request that invoked us. -# $replyfd- File descriptor connected to the client -# Implicit Inputs: -# $currenthostid - Global variable that carries the name of the host -# known as. -# $clientname - Global variable that carries the name of the hsot we're connected to. -# Returns: -# 1 - Ok to continue processing. -# 0 - Program should exit -# Implicit inputs: -# whatever the userload() function requires. -# Implicit outputs: -# the reply is written to the client. -# -sub UserLoadHandler { - my $cmd = shift; - my $tail = shift; - my $replyfd = shift; - - my $userloadpercent=&userload(); - Reply($replyfd, "$userloadpercent\n", "$cmd:$tail"); - - return 1; -} -RegisterHandler("userload", \&UserLoadHandler, 0, 1, 0); - -# Process a request for the authorization type of a user: -# (userauth). -# -# Parameters: -# $cmd - the actual keyword that invoked us. -# $tail - the tail of the request that invoked us. -# $replyfd- File descriptor connected to the client -# Returns: -# 1 - Ok to continue processing. -# 0 - Program should exit -# Implicit outputs: -# The user authorization type is written to the client. -# -sub UserAuthorizationType { - my $cmd = shift; - my $tail = shift; - my $replyfd = shift; - - my $userinput = "$cmd:$tail"; - - # Pull the domain and username out of the command tail. - # and call GetAuthType to determine the authentication type. - - my ($udom,$uname)=split(/:/,$tail); - my $result = GetAuthType($udom, $uname); - if($result eq "nouser") { - Failure( $replyfd, "unknown_user\n", $userinput); - } else { - # - # We only want to pass the second field from GetAuthType - # for ^krb.. otherwise we'll be handing out the encrypted - # password for internals e.g. - # - my ($type,$otherinfo) = split(/:/,$result); - if($type =~ /^krb/) { - $type = $result; - } - Reply( $replyfd, "$type\n", $userinput); - } - - return 1; -} -RegisterHandler("currentauth", \&UserAuthorizationType, 1, 1, 0); -# -# Process a request by a manager to push a hosts or domain table -# to us. We pick apart the command and pass it on to the subs -# that already exist to do this. -# -# Parameters: -# $cmd - the actual keyword that invoked us. -# $tail - the tail of the request that invoked us. -# $client - File descriptor connected to the client -# Returns: -# 1 - Ok to continue processing. -# 0 - Program should exit -# Implicit Output: -# a reply is written to the client. - -sub PushFileHandler { - my $cmd = shift; - my $tail = shift; - my $client = shift; - - my $userinput = "$cmd:$tail"; - - # At this time we only know that the IP of our partner is a valid manager - # the code below is a hook to do further authentication (e.g. to resolve - # spoofing). - - my $cert = GetCertificate($userinput); - if(ValidManager($cert)) { - - # Now presumably we have the bona fides of both the peer host and the - # process making the request. - - my $reply = PushFile($userinput); - Reply($client, "$reply\n", $userinput); - - } else { - Failure( $client, "refused\n", $userinput); - } -} -RegisterHandler("pushfile", \&PushFileHandler, 1, 0, 1); - - - -# 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. -# -# Parameters: -# $cmd - the actual keyword that invoked us. -# $tail - the tail of the request that invoked us. -# $client - File descriptor connected to the client -# Returns: -# 1 - Ok to continue processing. -# 0 - Program should exit -# Implicit output: -# a reply is sent to the client. -# -sub ReinitProcessHandler { - my $cmd = shift; - my $tail = shift; - my $client = shift; - - my $userinput = "$cmd:$tail"; - - my $cert = GetCertificate($userinput); - if(ValidManager($cert)) { - chomp($userinput); - my $reply = ReinitProcess($userinput); - Reply( $client, "$reply\n", $userinput); - } else { - Failure( $client, "refused\n", $userinput); - } - return 1; -} - -RegisterHandler("reinit", \&ReinitProcessHandler, 1, 0, 1); - -# Process the editing script for a table edit operation. -# the editing operation must be encrypted and requested by -# a manager host. -# -# Parameters: -# $cmd - the actual keyword that invoked us. -# $tail - the tail of the request that invoked us. -# $client - File descriptor connected to the client -# Returns: -# 1 - Ok to continue processing. -# 0 - Program should exit -# Implicit output: -# a reply is sent to the client. -# -sub EditTableHandler { - my $command = shift; - my $tail = shift; - my $client = shift; - - my $userinput = "$command:$tail"; - - my $cert = GetCertificate($userinput); - if(ValidManager($cert)) { - my($filetype, $script) = split(/:/, $tail); - if (($filetype eq "hosts") || - ($filetype eq "domain")) { - if($script ne "") { - Reply($client, # BUGBUG - EditFile - EditFile($userinput), # could fail. - $userinput); - } else { - Failure($client,"refused\n",$userinput); - } - } else { - Failure($client,"refused\n",$userinput); - } - } else { - Failure($client,"refused\n",$userinput); - } - return 1; -} -RegisterHandler("edit", \&EditTableHandler, 1, 0, 1); - - -# -# Authenticate a user against the LonCAPA authentication -# database. Note that there are several authentication -# possibilities: -# - unix - The user can be authenticated against the unix -# password file. -# - internal - The user can be authenticated against a purely -# internal per user password file. -# - kerberos - The user can be authenticated against either a kerb4 or kerb5 -# ticket granting authority. -# - user - The person tailoring LonCAPA can supply a user authentication -# mechanism that is per system. -# -# 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 AuthenticateHandler { - my $cmd = shift; - my $tail = shift; - my $client = shift; - - # Regenerate the full input line - - my $userinput = $cmd.":".$tail; - - # udom - User's domain. - # uname - Username. - # upass - User's password. - - my ($udom,$uname,$upass)=split(/:/,$tail); - Debug(" Authenticate domain = $udom, user = $uname, password = $upass"); - chomp($upass); - $upass=unescape($upass); - - my $pwdcorrect = ValidateUser($udom, $uname, $upass); - if($pwdcorrect) { - Reply( $client, "authorized\n", $userinput); - # - # Bad credentials: Failed to authorize - # - } else { - Failure( $client, "non_authorized\n", $userinput); - } - - return 1; -} -RegisterHandler("auth", \&AuthenticateHandler, 1, 1, 0); - -# -# Change a user's password. Note that this function is complicated by -# the fact that a user may be authenticated in more than one way: -# At present, we are not able to change the password for all types of -# authentication methods. Only for: -# unix - unix password or shadow passoword style authentication. -# local - Locally written authentication mechanism. -# For now, kerb4 and kerb5 password changes are not supported and result -# in an error. -# FUTURE WORK: -# Support kerberos passwd changes? -# 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 ChangePasswordHandler { - my $cmd = shift; - my $tail = shift; - my $client = shift; - - my $userinput = $cmd.":".$tail; # Reconstruct client's string. - - # - # udom - user's domain. - # uname - Username. - # upass - Current password. - # npass - New password. - - my ($udom,$uname,$upass,$npass)=split(/:/,$tail); - - $upass=&unescape($upass); - $npass=&unescape($npass); - &Debug("Trying to change password for $uname"); - - # First require that the user can be authenticated with their - # old password: - - my $validated = ValidateUser($udom, $uname, $upass); - if($validated) { - my $realpasswd = GetAuthType($udom, $uname); # Defined since authd. - - my ($howpwd,$contentpwd)=split(/:/,$realpasswd); - if ($howpwd eq 'internal') { - &Debug("internal auth"); - my $salt=time; - $salt=substr($salt,6,2); - my $ncpass=crypt($npass,$salt); - if(RewritePwFile($udom, $uname, "internal:$ncpass")) { - &logthis("Result of password change for " - ."$uname: pwchange_success"); - Reply($client, "ok\n", $userinput); - } else { - &logthis("Unable to open $uname passwd " - ."to change password"); - Failure( $client, "non_authorized\n",$userinput); - } - } elsif ($howpwd eq 'unix') { - # Unix means we have to access /etc/password - &Debug("auth is unix"); - my $execdir=$perlvar{'lonDaemons'}; - &Debug("Opening lcpasswd pipeline"); - my $pf = IO::File->new("|$execdir/lcpasswd > " - ."$perlvar{'lonDaemons'}" - ."/logs/lcpasswd.log"); - print $pf "$uname\n$npass\n$npass\n"; - close $pf; - my $err = $?; - my $result = ($err>0 ? 'pwchange_failure' : 'ok'); - &logthis("Result of password change for $uname: ". - &lcpasswdstrerror($?)); - Reply($client, "$result\n", $userinput); - } else { - # this just means that the current password mode is not - # one we know how to change (e.g the kerberos auth modes or - # locally written auth handler). - # - Reply( $client, "auth_mode_error\n", $userinput); - } - - } - else { - Reply( $client, "non_authorized\n", $userinput); - } - - return 1; -} -RegisterHandler("passwd", \&ChangePasswordHandler, 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 AddUserHandler { - my $cmd = shift; - my $tail = shift; - my $client = shift; - - 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 = PasswordPath($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; - -} -RegisterHandler("makeuser", \&AddUserHandler, 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 ChangeAuthenticationHandler { - my $cmd = shift; - my $tail = shift; - my $client = shift; - - 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 = PasswordPath($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; -} -RegisterHandler("changeuserauth", \&ChangeAuthenticationHandler, 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 IsHomeHandler { - my $cmd = shift; - my $tail = shift; - my $client = shift; - - my $userinput = "$cmd:$tail"; - - my ($udom,$uname)=split(/:/,$tail); - chomp($uname); - my $passfile = PasswordFilename($udom, $uname); - if($passfile) { - Reply( $client, "found\n", $userinput); - } else { - Failure($client, "not_found\n", $userinput); - } - return 1; -} -RegisterHandler("home", \&IsHomeHandler, 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 UpdateResourceHandler { - my $cmd = shift; - my $tail = shift; - my $client = shift; - - 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; -} -RegisterHandler("update", \&UpdateResourceHandler, 0 ,1, 0); - -# -# Fetch a user file from a remote server: -# 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 FetchUserFileHandler { - my $cmd = shift; - my $tail = shift; - my $client = shift; - - my $userinput = "$cmd:$tail"; - my $fname = $tail; - my ($udom,$uname,$ufile)=split(/\//,$fname); - my $udir=propath($udom,$uname).'/userfiles'; - unless (-e $udir) { - mkdir($udir,0770); - } - if (-e $udir) { - $ufile=~s/^[\.\~]+//; - $ufile=~s/\///g; - my $destname=$udir.'/'.$ufile; - my $transname=$udir.'/'.$ufile.'.in.transit'; - my $remoteurl='http://'.$clientip.'/userfiles/'.$fname; - 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)"); - Failure($client, "failed\n", $userinput); - } else { - 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; -} -RegisterHandler("fetchuserfile", \&FetchUserFileHandler, 0, 1, 0); -# -# Authenticate access to a user file. -# -# 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 AuthenticateUserFileAccess { - my $cmd = shift; - my $tail = shift; - my $client = shift; - my $userinput = "$cmd:$tail"; - - my ($fname,$session)=split(/:/,$tail); - chomp($session); - my $reply='non_auth'; - if (open(ENVIN,$perlvar{'lonIDsDir'}.'/'.$session.'.id')) { - while (my $line=) { - if ($line=~/userfile\.$fname\=/) { - $reply='ok'; - } - } - close(ENVIN); - Reply($client, $reply."\n", $userinput); - } else { - Failure($client, "invalid_token\n", $userinput); - } - return 1; - -} -RegisterHandler("tokenauthuserfile", \&AuthenticateUserFileAccess, 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 UnsubscribeHandler { - my $cmd = shift; - my $tail = shift; - my $client = shift; - my $userinput= "$cmd:$tail"; - - my ($fname) = $tail; - - Debug("Unsubscribing $fname"); - if (-e $fname) { - Debug("Exists"); - Reply($client, &unsub($fname,$clientip), $userinput); - } else { - Failure($client, "not_found\n", $userinput); - } - return 1; -} -RegisterHandler("unsub", \&UnsubscribeHandler, 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 SubscribeHandler { - my $cmd = shift; - my $tail = shift; - my $client = shift; - my $userinput = "$cmd:$tail"; - - Reply( $client, &subscribe($userinput,$clientip), $userinput); - - return 1; -} -RegisterHandler("sub", \&SubscribeHandler, 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 CurrentVersionHandler { - my $cmd = shift; - my $tail = shift; - my $client = shift; - my $userinput= "$cmd:$tail"; - - my $fname = $tail; - Reply( $client, ¤tversion($fname)."\n", $userinput); - return 1; - -} -RegisterHandler("currentversion", \&CurrentVersionHandler, 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 ActivityLogEntryHandler { - my $cmd = shift; - my $tail = shift; - my $client = shift; - 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; -} -RegisterHandler("log", \&ActivityLogEntryHandler, 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 PutUserProfileEntry { - my $cmd = shift; - my $tail = shift; - my $client = shift; - my $userinput = "$cmd:$tail"; - - my ($udom,$uname,$namespace,$what) =split(/:/,$tail); - if ($namespace ne 'roles') { - chomp($what); - my $hashref = TieUserHash($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; -} -RegisterHandler("put", \&PutUserProfileEntry, 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 IncrementUserValueHandler { - my $cmd = shift; - my $tail = shift; - my $client = shift; - my $userinput = "$cmd:$tail"; - - my ($udom,$uname,$namespace,$what) =split(/:/,$tail); - if ($namespace ne 'roles') { - chomp($what); - my $hashref = TieUserHash($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; -} -RegisterHandler("inc", \&IncrementUserValueHandler, 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 RolesPutHandler { - my $cmd = shift; - my $tail = shift; - my $client = shift; - my $userinput = "$cmd:$tail"; - - my ( $exedom, $exeuser, $udom, $uname, $what) = split(/:/,$tail); - - - my $namespace='roles'; - chomp($what); - my $hashref = TieUserHash($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); - &ManagePermissions($key, $udom, $uname, - &GetAuthType( $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; -} -RegisterHandler("rolesput", \&RolesPutHandler, 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 RolesDeleteHandler { - my $cmd = shift; - my $tail = shift; - my $client = shift; - 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 = TieUserHash($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; -} -RegisterHandler("rolesdel", \&RolesDeleteHandler, 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 GetProfileEntry { - my $cmd = shift; - my $tail = shift; - my $client = shift; - my $userinput= "$cmd:$tail"; - - my ($udom,$uname,$namespace,$what) = split(/:/,$tail); - chomp($what); - my $hashref = TieUserHash($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; -} -RegisterHandler("get", \&GetProfileEntry, 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 GetProfileEntryEncrypted { - my $cmd = shift; - my $tail = shift; - my $client = shift; - my $userinput = "$cmd:$tail"; - - my ($cmd,$udom,$uname,$namespace,$what) = split(/:/,$userinput); - chomp($what); - my $hashref = TieUserHash($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; -} -RegisterHandler("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 DeleteProfileEntry { - my $cmd = shift; - my $tail = shift; - my $client = shift; - my $userinput = "cmd:$tail"; - - my ($udom,$uname,$namespace,$what) = split(/:/,$tail); - chomp($what); - my $hashref = TieUserHash($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; -} -RegisterHandler("del", \&DeleteProfileEntry, 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 GetProfileKeys { - my $cmd = shift; - my $tail = shift; - my $client = shift; - my $userinput = "$cmd:$tail"; - - my ($udom,$uname,$namespace)=split(/:/,$tail); - my $qresult=''; - my $hashref = TieUserHash($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; -} -RegisterHandler("keys", \&GetProfileKeys, 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 DumpProfileDatabase { - my $cmd = shift; - my $tail = shift; - my $client = shift; - my $userinput = "$cmd:$tail"; - - my ($udom,$uname,$namespace) = split(/:/,$tail); - my $hashref = TieUserHash($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; -} -RegisterHandler("currentdump", \&DumpProfileDatabase, 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 DumpWithRegexp { - my $cmd = shift; - my $tail = shift; - my $client = shift; - - my $userinput = "$cmd:$tail"; - - my ($udom,$uname,$namespace,$regexp)=split(/:/,$tail); - if (defined($regexp)) { - $regexp=&unescape($regexp); - } else { - $regexp='.'; - } - my $hashref =TieUserHash($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; -} -RegisterHandler("dump", \&DumpWithRegexp, 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 StoreHandler { - my $cmd = shift; - my $tail = shift; - my $client = shift; - - my $userinput = "$cmd:$tail"; - - my ($udom,$uname,$namespace,$rid,$what) =split(/:/,$tail); - if ($namespace ne 'roles') { - - chomp($what); - my @pairs=split(/\&/,$what); - my $hashref = TieUserHash($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; -} -RegisterHandler("store", \&StoreHandler, 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 RestoreHandler { - my $cmd = shift; - my $tail = shift; - my $client = shift; - - 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; - - -} -RegisterHandler("restore", \&RestoreHandler, 0,1,0); - -# -# Add a chat message to to a discussion board. -# -# Parameters: -# $cmd - Request keyword. -# $tail - Tail of the command. A colon separated list -# containing: -# cdom - Domain on which the chat board lives -# cnum - Identifier of the discussion group. -# post - Body of the posting. -# $client - Socket open on the client. -# Returns: -# 1 - Indicating caller should keep on processing. -# -# Side-effects: -# writes a reply to the client. -# -# -sub SendChatHandler { - my $cmd = shift; - my $tail = shift; - my $client = shift; - - my $userinput = "$cmd:$tail"; - - my ($cdom,$cnum,$newpost)=split(/\:/,$tail); - &chatadd($cdom,$cnum,$newpost); - Reply($client, "ok\n", $userinput); - - return 1; -} -RegisterHandler("chatsend", \&SendChatHandler, 0, 1, 0); -# -# Retrieve the set of chat messagss from a discussion board. -# -# Parameters: -# $cmd - Command keyword that initiated the request. -# $tail - Remainder of the request after the command -# keyword. In this case a colon separated list of -# chat domain - Which discussion board. -# chat id - Discussion thread(?) -# domain/user - Authentication domain and username -# of the requesting person. -# $client - Socket open on the client program. -# Returns: -# 1 - continue processing -# Side effects: -# Response is written to the client. -# -sub RetrieveChatHandler { - my $cmd = shift; - my $tail = shift; - my $client = shift; - - my $userinput = "$cmd:$tail"; - - my ($cdom,$cnum,$udom,$uname)=split(/\:/,$tail); - my $reply=''; - foreach (&getchat($cdom,$cnum,$udom,$uname)) { - $reply.=&escape($_).':'; - } - $reply=~s/\:$//; - Reply($client, $reply."\n", $userinput); - - - return 1; -} -RegisterHandler("chatretr", \&RetrieveChatHandler, 0, 1, 0); -# -# Initiate a query of an sql database. SQL query repsonses get put in -# a file for later retrieval. This prevents sql query results from -# bottlenecking the system. Note that with loncnew, perhaps this is -# less of an issue since multiple outstanding requests can be concurrently -# serviced. -# -# Parameters: -# $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 -# in the original arg1, arg2, arg3. -# $client - Socket open on the client. -# Return: -# 1 - Indicating processing should continue. -# Side-effects: -# a reply is written to $client. -# -sub SendQueryHandler { - my $cmd = shift; - my $tail = shift; - my $client = shift; - - my $userinput = "$cmd:$tail"; - - my ($query,$arg1,$arg2,$arg3)=split(/\:/,$tail); - $query=~s/\n*$//g; - Reply($client, "". sqlreply("$clientname\&$query". - "\&$arg1"."\&$arg2"."\&$arg3")."\n", - $userinput); - - return 1; -} -RegisterHandler("querysend", \&SendQueryHandler, 0, 1, 0); - -# -# Add a reply to an sql query. SQL queries are done asyncrhonously. -# The query is submitted via a "querysend" transaction. -# There it is passed on to the lonsql daemon, queued and issued to -# mysql. -# This transaction is invoked when the sql transaction is complete -# it stores the query results in flie and indicates query completion. -# presumably local software then fetches this response... I'm guessing -# the sequence is: lonc does a querysend, we ask lonsql to do it. -# lonsql on completion of the query interacts with the lond of our -# client to do a query reply storing two files: -# - id - The results of the query. -# - id.end - Indicating the transaction completed. -# NOTE: id is a unique id assigned to the query and querysend time. -# Parameters: -# $cmd - Command keyword that initiated this request. -# $tail - Remainder of the tail. In this case that's a colon -# separated list containing the query Id and the -# results of the query. -# $client - Socket open on the client. -# Return: -# 1 - Indicating that we should continue processing. -# Side effects: -# ok written to the client. -# -sub ReplyQueryHandler { - my $cmd = shift; - my $tail = shift; - my $client = shift; - - my $userinput = "$cmd:$tail"; - - my ($cmd,$id,$reply)=split(/:/,$userinput); - my $store; - my $execdir=$perlvar{'lonDaemons'}; - if ($store=IO::File->new(">$execdir/tmp/$id")) { - $reply=~s/\&/\n/g; - print $store $reply; - close $store; - my $store2=IO::File->new(">$execdir/tmp/$id.end"); - print $store2 "done\n"; - close $store2; - Reply($client, "ok\n", $userinput); - } else { - Failure($client, "error: ".($!+0) - ." IO::File->new Failed ". - "while attempting queryreply\n", $userinput); - } - - - return 1; -} -RegisterHandler("queryreply", \&ReplyQueryHandler, 0, 1, 0); -# -# Process the courseidput query. Not quite sure what this means -# at the system level sense. It appears a gdbm file in the -# /home/httpd/lonUsers/$domain/nohist_courseids is tied and -# a set of entries made in that database. -# -# Parameters: -# $cmd - The command keyword that initiated this request. -# $tail - Tail of the command. In this case consists of a colon -# separated list contaning the domain to apply this to and -# an ampersand separated list of keyword=value pairs. -# $client - Socket open on the client. -# Returns: -# 1 - indicating that processing should continue -# -# Side effects: -# reply is written to the client. -# -sub PutCourseIdHandler { - my $cmd = shift; - my $tail = shift; - my $client = shift; - - my $userinput = "$cmd:$tail"; - - my ($udom, $what) = split(/:/, $tail); - chomp($what); - my $now=time; - my @pairs=split(/\&/,$what); - - my $hashref = TieDomainHash($udom, "nohist_courseids", &GDBM_WRCREAT()); - if ($hashref) { - foreach my $pair (@pairs) { - my ($key,$value)=split(/=/,$pair); - $hashref->{$key}=$value.':'.$now; - } - if (untie(%$hashref)) { - Reply($client, "ok\n", $userinput); - } else { - Failure( $client, "error: ".($!+0) - ." untie(GDBM) Failed ". - "while attempting courseidput\n", $userinput); - } - } else { - Failure( $client, "error: ".($!+0) - ." tie(GDBM) Failed ". - "while attempting courseidput\n", $userinput); - } - - return 1; -} -RegisterHandler("courseidput", \&PutCourseIdHandler, 0, 1, 0); - -# Retrieves the value of a course id resource keyword pattern -# defined since a starting date. Both the starting date and the -# keyword pattern are optional. If the starting date is not supplied it -# is treated as the beginning of time. If the pattern is not found, -# it is treatred as "." matching everything. -# -# Parameters: -# $cmd - Command keyword that resulted in us being dispatched. -# $tail - The remainder of the command that, in this case, consists -# of a colon separated list of: -# domain - The domain in which the course database is -# defined. -# since - Optional parameter describing the minimum -# time of definition(?) of the resources that -# will match the dump. -# description - regular expression that is used to filter -# the dump. Only keywords matching this regexp -# will be used. -# $client - The socket open on the client. -# Returns: -# 1 - Continue processing. -# Side Effects: -# a reply is written to $client. -sub DumpCourseIdHandler { - my $cmd = shift; - my $tail = shift; - my $client = shift; - - my $userinput = "$cmd:$tail"; - - my ($udom,$since,$description) =split(/:/,$tail); - if (defined($description)) { - $description=&unescape($description); - } else { - $description='.'; - } - unless (defined($since)) { $since=0; } - my $qresult=''; - logthis(" Looking for $description since $since"); - my $hashref = TieDomainHash($udom, "nohist_courseids", &GDBM_WRCREAT()); - if ($hashref) { - while (my ($key,$value) = each(%$hashref)) { - my ($descr,$lasttime)=split(/\:/,$value); - logthis("Got: key = $key descr = $descr time: $lasttime"); - if ($lasttime<$since) { - logthis("Skipping .. too early"); - next; - } - if ($description eq '.') { - logthis("Adding wildcard match"); - $qresult.=$key.'='.$descr.'&'; - } else { - my $unescapeVal = &unescape($descr); - logthis("Matching with $unescapeVal"); - if (eval('$unescapeVal=~/\Q$description\E/i')) { - logthis("Adding on match"); - $qresult.="$key=$descr&"; - } - } - } - if (untie(%$hashref)) { - chop($qresult); - Reply($client, "$qresult\n", $userinput); - } else { - Failure($client, "error: ".($!+0)." untie(GDBM) Failed ". - "while attempting courseiddump\n", $userinput); - } - } else { - Failure($client, "error: ".($!+0)." tie(GDBM) Failed ". - "while attempting courseiddump\n", $userinput); - } - - - return 1; -} -RegisterHandler("courseiddump", \&DumpCourseIdHandler, 0, 1, 0); -# -# Puts an id to a domains id database. -# -# Parameters: -# $cmd - The command that triggered us. -# $tail - Remainder of the request other than the command. This is a -# colon separated list containing: -# $domain - The domain for which we are writing the id. -# $pairs - The id info to write... this is and & separated list -# of keyword=value. -# $client - Socket open on the client. -# Returns: -# 1 - Continue processing. -# Side effects: -# reply is written to $client. -# -sub PutIdHandler { - my $cmd = shift; - my $tail = shift; - my $client = shift; - - my $userinput = "$cmd:$tail"; - - my ($udom,$what)=split(/:/,$tail); - chomp($what); - my @pairs=split(/\&/,$what); - my $hashref = TieDomainHash($udom, "ids", &GDBM_WRCREAT(), - "P", $what); - if ($hashref) { - 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 idput\n", $userinput); - } - } else { - Failure( $client, "error: ".($!+0)." tie(GDBM) Failed ". - "while attempting idput\n", $userinput); - } - - return 1; -} - -RegisterHandler("idput", \&PutIdHandler, 0, 1, 0); -# -# Retrieves a set of id values from the id database. -# Returns an & separated list of results, one for each requested id to the -# client. -# -# Parameters: -# $cmd - Command keyword that caused us to be dispatched. -# $tail - Tail of the command. Consists of a colon separated: -# domain - the domain whose id table we dump -# ids Consists of an & separated list of -# id keywords whose values will be fetched. -# nonexisting keywords will have an empty value. -# $client - Socket open on the client. -# -# Returns: -# 1 - indicating processing should continue. -# Side effects: -# An & separated list of results is written to $client. -# -sub GetIdHandler { - my $cmd = shift; - my $tail = shift; - my $client = shift; - - my $userinput = "$client:$tail"; - - my ($udom,$what)=split(/:/,$tail); - chomp($what); - my @queries=split(/\&/,$what); - my $qresult=''; - my $hashref = TieDomainHash($udom, "ids", &GDBM_READER()); - if ($hashref) { - for (my $i=0;$i<=$#queries;$i++) { - $qresult.="$hashref->{$queries[$i]}&"; - } - if (untie(%$hashref)) { - $qresult=~s/\&$//; - Reply($client, "$qresult\n", $userinput); - } else { - Failure( $client, "error: ".($!+0)." untie(GDBM) Failed ". - "while attempting idget\n",$userinput); - } - } else { - Failure($client, "error: ".($!+0)." tie(GDBM) Failed ". - "while attempting idget\n",$userinput); - } - - return 1; -} - -RegisterHandler("idget", \&GetIdHandler, 0, 1, 0); -# -# Process the tmpput command I'm not sure what this does.. Seems to -# create a file in the lonDaemons/tmp directory of the form $id.tmp -# where Id is the client's ip concatenated with a sequence number. -# The file will contain some value that is passed in. Is this e.g. -# a login token? -# -# Parameters: -# $cmd - The command that got us dispatched. -# $tail - The remainder of the request following $cmd: -# In this case this will be the contents of the file. -# $client - Socket connected to the client. -# Returns: -# 1 indicating processing can continue. -# Side effects: -# A file is created in the local filesystem. -# A reply is sent to the client. -sub TmpPutHandler { - my $cmd = shift; - my $what = shift; - my $client = shift; - - my $userinput = "$cmd:$what"; # Reconstruct for logging. - - - my $store; - $tmpsnum++; - my $id=$$.'_'.$clientip.'_'.$tmpsnum; - $id=~s/\W/\_/g; - $what=~s/\n//g; - my $execdir=$perlvar{'lonDaemons'}; - if ($store=IO::File->new(">$execdir/tmp/$id.tmp")) { - print $store $what; - close $store; - Reply($client, "$id\n", $userinput); - } else { - Failure( $client, "error: ".($!+0)."IO::File->new Failed ". - "while attempting tmpput\n", $userinput); - } - return 1; - -} -RegisterHandler("tmpput", \&TmpPutHandler, 0, 1, 0); - -# Processes the tmpget command. This command returns the contents -# of a temporary resource file(?) created via tmpput. -# -# Paramters: -# $cmd - Command that got us dispatched. -# $id - Tail of the command, contain the id of the resource -# we want to fetch. -# $client - socket open on the client. -# Return: -# 1 - Inidcating processing can continue. -# Side effects: -# A reply is sent to the client. - -# -sub TmpGetHandler { - my $cmd = shift; - my $id = shift; - my $client = shift; - my $userinput = "$cmd:$id"; - - - $id=~s/\W/\_/g; - my $store; - my $execdir=$perlvar{'lonDaemons'}; - if ($store=IO::File->new("$execdir/tmp/$id.tmp")) { - my $reply=<$store>; - Reply( $client, "$reply\n", $userinput); - close $store; - } else { - Failure( $client, "error: ".($!+0)."IO::File->new Failed ". - "while attempting tmpget\n", $userinput); - } - - return 1; -} -RegisterHandler("tmpget", \&TmpGetHandler, 0, 1, 0); -# -# Process the tmpdel command. This command deletes a temp resource -# created by the tmpput command. -# -# Parameters: -# $cmd - Command that got us here. -# $id - Id of the temporary resource created. -# $client - socket open on the client process. -# -# Returns: -# 1 - Indicating processing should continue. -# Side Effects: -# A file is deleted -# A reply is sent to the client. -sub TmpDelHandler { - my $cmd = shift; - my $id = shift; - my $client = shift; - - my $userinput= "$cmd:$id"; - - chomp($id); - $id=~s/\W/\_/g; - my $execdir=$perlvar{'lonDaemons'}; - if (unlink("$execdir/tmp/$id.tmp")) { - Reply($client, "ok\n", $userinput); - } else { - Failure( $client, "error: ".($!+0)."Unlink tmp Failed ". - "while attempting tmpdel\n", $userinput); - } - - return 1; - -} -RegisterHandler("tmpdel", \&TmpDelHandler, 0, 1, 0); -# -# ls - list the contents of a directory. For each file in the -# 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. -# Parameters: -# $cmd - The command that dispatched us (ls). -# $ulsdir - The directory path to list... I'm not sure what this -# is relative as things like ls:. return e.g. -# no_such_dir. -# $client - Socket open on the client. -# Returns: -# 1 - indicating that the daemon should not disconnect. -# Side Effects: -# The reply is written to $client. -# -sub LsHandler { - my $cmd = shift; - my $ulsdir = shift; - my $client = shift; - - my $userinput = "$cmd:$ulsdir"; - - chomp($ulsdir); - - my $ulsout=''; - my $ulsfn; - logthis("ls for '$ulsdir'"); - if (-e $ulsdir) { - logthis("ls - directory exists"); - if(-d $ulsdir) { - logthis("ls $ulsdir is a file"); - if (opendir(LSDIR,$ulsdir)) { - while ($ulsfn=readdir(LSDIR)) { - my @ulsstats=stat($ulsdir.'/'.$ulsfn); - $ulsout.=$ulsfn.'&'. - join('&',@ulsstats).':'; - } - closedir(LSDIR); - } - } else { - my @ulsstats=stat($ulsdir); - $ulsout.=$ulsfn.'&'.join('&',@ulsstats).':'; - } - } else { - $ulsout='no_such_dir'; - } - if ($ulsout eq '') { $ulsout='empty'; } - Reply($client, "$ulsout\n", $userinput); - - - return 1; -} -RegisterHandler("ls", \&LsHandler, 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 -# printed in its entirety at the LonCAPA login page. Note: -# once the announcement.txt fileis created it cannot be deleted. -# However, setting the contents of the file to empty removes the -# announcement from the login page of loncapa so who cares. -# -# Parameters: -# $cmd - The command that got us dispatched. -# $announcement - The text of the announcement. -# $client - Socket open on the client process. -# Retunrns: -# 1 - Indicating request processing should continue -# Side Effects: -# The file {DocRoot}/announcement.txt is created. -# A reply is sent to $client. -# -sub SetAnnounceHandler { - my $cmd = shift; - my $announcement = shift; - my $client = shift; - - my $userinput = "$cmd:$announcement"; - - chomp($announcement); - $announcement=&unescape($announcement); - if (my $store=IO::File->new('>'.$perlvar{'lonDocRoot'}. - '/announcement.txt')) { - print $store $announcement; - close $store; - Reply($client, "ok\n", $userinput); - } else { - Failure($client, "error: ".($!+0)."\n", $userinput); - } - - return 1; -} -RegisterHandler("setannounce", \&SetAnnounceHandler, 0, 1, 0); - -# -# Return the version of the daemon. This can be used to determine -# the compatibility of cross version installations or, alternatively to -# simply know who's out of date and who isn't. Note that the version -# is returned concatenated with the tail. -# Parameters: -# $cmd - the request that dispatched to us. -# $tail - Tail of the request (client's version?). -# $client - Socket open on the client. -#Returns: -# 1 - continue processing requests. -# Side Effects: -# Replies with version to $client. -sub GetVersionHandler { - my $client = shift; - my $tail = shift; - my $client = shift; - my $userinput = $client; - - Reply($client, &version($userinput)."\n", $userinput); - - - return 1; -} -RegisterHandler("version", \&GetVersionHandler, 0, 1, 0); - -# Set the current host and domain. This is used to support -# multihomed systems. Each IP of the system, or even separate daemons -# on the same IP can be treated as handling a separate lonCAPA virtual -# machine. This command selects the virtual lonCAPA. The client always -# knows the right one since it is lonc and it is selecting the domain/system -# from the hosts.tab file. -# Parameters: -# $cmd - Command that dispatched us. -# $tail - Tail of the command (domain/host requested). -# $socket - Socket open on the client. -# -# Returns: -# 1 - Indicates the program should continue to process requests. -# Side-effects: -# The default domain/system context is modified for this daemon. -# a reply is sent to the client. -# -sub SelectHostHandler { - my $cmd = shift; - my $tail = shift; - my $socket = shift; - - my $userinput ="$cmd:$tail"; - - Reply($client, &sethost($userinput)."\n", $userinput); - - - return 1; -} -RegisterHandler("sethost", \&SelectHostHandler, 0, 1, 0); - -# Process a request to exit: -# - "bye" is sent to the client. -# - The client socket is shutdown and closed. -# - We indicate to the caller that we should exit. -# Formal Parameters: -# $cmd - The command that got us here. -# $tail - Tail of the command (empty). -# $client - Socket open on the tail. -# Returns: -# 0 - Indicating the program should exit!! -# -sub ExitHandler { - my $cmd = shift; - my $tail = shift; - my $client = shift; - - my $userinput = "$cmd:$tail"; - - &logthis("Client $clientip ($clientname) hanging up: $userinput"); - Reply($client, "bye\n", $userinput); - $client->shutdown(2); # shutdown the socket forcibly. - $client->close(); - - return 0; -} -RegisterHandler("exit", \&ExitHandler, 0, 1,1); -RegisterHandler("init", \&ExitHandler, 0, 1,1); # RE-init is like exit. -RegisterHandler("quit", \&ExitHandler, 0, 1,1); # I like this too! -#------------------------------------------------------------------------------------ -# -# Process a Request. Takes a request from the client validates -# it and performs the operation requested by it. Returns -# a response to the client. -# -# Parameters: -# request - A string containing the user's request. -# Returns: -# 0 - Requested to exit, caller should shut down. -# 1 - Accept additional requests from the client. -# -sub ProcessRequest { - my $Request = shift; - my $KeepGoing = 1; # Assume we're not asked to stop. - - my $wasenc=0; - my $userinput = $Request; # for compatibility with oldcode - - -# ------------------------------------------------------------ See if encrypted - - if($userinput =~ /^enc/) { - $wasenc = 1; - $userinput = Decipher($userinput); - if(! $userinput) { - Failure($client,"error:Encrypted data without negotiating key"); - return 0; # Break off with this imposter. - } - } - # Split off the request keyword from the rest of the stuff. - - my ($command, $tail) = split(/:/, $userinput, 2); - chomp($command); - chomp($tail); - $tail =~ s/(\r)//; # This helps people debugging with e.g. telnet. - - Debug("Command received: $command, encoded = $wasenc"); - - -# ------------------------------------------------------------- Normal commands - - # - # If the command is in the hash, then execute it via the hash dispatch: - # - if(defined $Dispatcher{$command}) { - - my $DispatchInfo = $Dispatcher{$command}; - my $Handler = $$DispatchInfo[0]; - my $NeedEncode = $$DispatchInfo[1]; - my $ClientTypes = $$DispatchInfo[2]; - Debug("Matched dispatch hash: mustencode: $NeedEncode ClientType $ClientTypes"); - - # Validate the request: - - my $ok = 1; - my $requesterprivs = 0; - if(isClient()) { - $requesterprivs |= $CLIENT_OK; - } - if(isManager()) { - $requesterprivs |= $MANAGER_OK; - } - if($NeedEncode && (!$wasenc)) { - Debug("Must encode but wasn't: $NeedEncode $wasenc"); - $ok = 0; - } - if(($ClientTypes & $requesterprivs) == 0) { - Debug("Client not privileged to do this operation"); - $ok = 0; - } - - if($ok) { - Debug("Dispatching to handler $command $tail"); - $KeepGoing = &$Handler($command, $tail, $client); - } else { - Debug("Refusing to dispatch because ok is false"); - Failure($client, "refused\n", $userinput); - } - - -# ------------------------------------------------------------- unknown command - - } else { - # unknown command - Failure($client, "unknown_cmd\n", $userinput); - } - - return $KeepGoing; -} - # # GetCertificate: Given a transaction that requires a certificate, @@ -2779,6 +137,18 @@ sub GetCertificate { return $clientip; } +# +# Return true if client is a manager. +# +sub isManager { + return (($ConnectionType eq "manager") || ($ConnectionType eq "both")); +} +# +# Return tru if client can do client functions +# +sub isClient { + return (($ConnectionType eq "client") || ($ConnectionType eq "both")); +} # @@ -2793,42 +163,42 @@ sub ReadManagerTable { # Clean out the old table first.. - foreach my $key (keys %managers) { - delete $managers{$key}; - } - - my $tablename = $perlvar{'lonTabDir'}."/managers.tab"; - if (!open (MANAGERS, $tablename)) { - logthis('No manager table. Nobody can manage!!'); - return; - } - while(my $host = ) { - chomp($host); - if ($host =~ "^#") { # Comment line. - logthis(' Skipping line: '. "$host\n"); - next; - } - if (!defined $hostip{$host}) { # This is a non cluster member + foreach my $key (keys %managers) { + delete $managers{$key}; + } + + my $tablename = $perlvar{'lonTabDir'}."/managers.tab"; + if (!open (MANAGERS, $tablename)) { + logthis('No manager table. Nobody can manage!!'); + return; + } + while(my $host = ) { + chomp($host); + if ($host =~ "^#") { # Comment line. + logthis(' Skipping line: '. "$host\n"); + next; + } + if (!defined $hostip{$host}) { # This is a non cluster member # The entry is of the form: # cluname:hostname # cluname - A 'cluster hostname' is needed in order to negotiate # the host key. # hostname- The dns name of the host. # - my($cluname, $dnsname) = split(/:/, $host); - - my $ip = gethostbyname($dnsname); - if(defined($ip)) { # bad names don't deserve entry. - my $hostip = inet_ntoa($ip); - $managers{$hostip} = $cluname; - logthis(' registering manager '. - "$dnsname as $cluname with $hostip \n"); - } - } else { - logthis(' existing host'." $host\n"); - $managers{$hostip{$host}} = $host; # Use info from cluster tab if clumemeber - } - } + my($cluname, $dnsname) = split(/:/, $host); + + my $ip = gethostbyname($dnsname); + if(defined($ip)) { # bad names don't deserve entry. + my $hostip = inet_ntoa($ip); + $managers{$hostip} = $cluname; + logthis(' registering manager '. + "$dnsname as $cluname with $hostip \n"); + } + } else { + logthis(' existing host'." $host\n"); + $managers{$hostip{$host}} = $host; # Use info from cluster tab if clumemeber + } + } } # @@ -2862,7 +232,7 @@ sub CopyFile { if(-e $oldfile) { - # Read the old file. + # Read the old file. my $oldfh = IO::File->new("< $oldfile"); if(!$oldfh) { @@ -2915,30 +285,30 @@ sub AdjustHostContents { my $adjusted; my $me = $perlvar{'lonHostID'}; - foreach my $line (split(/\n/,$contents)) { + foreach my $line (split(/\n/,$contents)) { if(!(($line eq "") || ($line =~ /^ *\#/) || ($line =~ /^ *$/))) { chomp($line); my ($id,$domain,$role,$name,$ip,$maxcon,$idleto,$mincon)=split(/:/,$line); if ($id eq $me) { - my $ip = gethostbyname($name); - my $ipnew = inet_ntoa($ip); - $ip = $ipnew; + my $ip = gethostbyname($name); + my $ipnew = inet_ntoa($ip); + $ip = $ipnew; # Reconstruct the host line and append to adjusted: - my $newline = "$id:$domain:$role:$name:$ip"; - if($maxcon ne "") { # Not all hosts have loncnew tuning params - $newline .= ":$maxcon:$idleto:$mincon"; - } - $adjusted .= $newline."\n"; + my $newline = "$id:$domain:$role:$name:$ip"; + if($maxcon ne "") { # Not all hosts have loncnew tuning params + $newline .= ":$maxcon:$idleto:$mincon"; + } + $adjusted .= $newline."\n"; - } else { # Not me, pass unmodified. - $adjusted .= $line."\n"; - } + } else { # Not me, pass unmodified. + $adjusted .= $line."\n"; + } } else { # Blank or comment never re-written. $adjusted .= $line."\n"; # Pass blanks and comments as is. } - } - return $adjusted; + } + return $adjusted; } # # InstallFile: Called to install an administrative file: @@ -2961,14 +331,14 @@ sub InstallFile { my $TempFile = $Filename.".tmp"; # Open the file for write: - + my $fh = IO::File->new("> $TempFile"); # Write to temp. if(!(defined $fh)) { &logthis(' Unable to create '.$TempFile.""); return 0; } # write the contents of the file: - + print $fh ($Contents); $fh->close; # In case we ever have a filesystem w. locking @@ -3051,7 +421,7 @@ sub PushFile { return "error:$!"; } &logthis(' Pushfile: backed up ' - .$tablefile." to $backupfile"); + .$tablefile." to $backupfile"); # If the file being pushed is the host file, we adjust the entry for ourself so that the # IP will be our current IP as looked up in dns. Note this is only 99% good as it's possible @@ -3067,12 +437,13 @@ sub PushFile { if(!InstallFile($tablefile, $contents)) { &logthis(' Pushfile: unable to install ' - .$tablefile." $! "); + .$tablefile." $! "); return "error:$!"; - } else { + } + else { &logthis(' Installed new '.$tablefile .""); - + } @@ -3209,7 +580,7 @@ sub ApplyEdit { } elsif ($command eq "delete") { $editor->DeleteLine($p1); # p1 - key p2 null. } else { # Should not get here!!! - die "Invalid command given to ApplyEdit $command"; + die "Invalid command given to ApplyEdit $command" } } # @@ -3379,8 +750,8 @@ sub catchexception { $SIG{__DIE__}='DEFAULT'; &status("Catching exception"); &logthis("CRITICAL: " - ."ABNORMAL EXIT. Child $$ for server $thisserver died through " - ."a crash with this error msg->[$error]"); + ."ABNORMAL EXIT. Child $$ for server $thisserver died through " + ."a crash with this error msg->[$error]"); &logthis('Famous last words: '.$status.' - '.$lastlog); if ($client) { print $client "error: $error\n"; } $server->close(); @@ -3406,11 +777,11 @@ undef $perlvarref; # ----------------------------- Make sure this process is running from user=www 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.' |\ + 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"); - exit 1; + exit 1; } # --------------------------------------------- Check if other instance running @@ -3418,10 +789,10 @@ if ($wwwid!=$<) { my $pidfile="$perlvar{'lonDaemons'}/logs/lond.pid"; if (-e $pidfile) { - my $lfh=IO::File->new("$pidfile"); - my $pide=<$lfh>; - chomp($pide); - if (kill 0 => $pide) { die "already running"; } + my $lfh=IO::File->new("$pidfile"); + my $pide=<$lfh>; + chomp($pide); + if (kill 0 => $pide) { die "already running"; } } # ------------------------------------------------------------- Read hosts file @@ -3434,25 +805,33 @@ $server = IO::Socket::INET->new(LocalPor Proto => 'tcp', Reuse => 1, Listen => 10 ) - or die "making socket: $@\n"; + or die "making socket: $@\n"; # --------------------------------------------------------- Do global variables # global variables my %children = (); # keys are current child process IDs -my $children = 0; # current number of children sub REAPER { # takes care of dead children $SIG{CHLD} = \&REAPER; &status("Handling child death"); - my $pid = wait; - if (defined($children{$pid})) { - &logthis("Child $pid died"); - $children --; - delete $children{$pid}; - } else { - &logthis("Unknown Child $pid died"); + my $pid; + do { + $pid = waitpid(-1,&WNOHANG()); + if (defined($children{$pid})) { + &logthis("Child $pid died"); + delete($children{$pid}); + } else { + &logthis("Unknown Child $pid died"); + } + } while ( $pid > 0 ); + foreach my $child (keys(%children)) { + $pid = waitpid($child,&WNOHANG()); + if ($pid > 0) { + &logthis("Child $child - $pid looks like we missed it's death"); + delete($children{$pid}); + } } &status("Finished Handling child death"); } @@ -3508,12 +887,14 @@ sub ReadHostTable { open (CONFIG,"$perlvar{'lonTabDir'}/hosts.tab") || die "Can't read host file"; while (my $configline=) { - my ($id,$domain,$role,$name,$ip)=split(/:/,$configline); - chomp($ip); $ip=~s/\D+$//; - $hostid{$ip}=$id; - $hostdom{$id}=$domain; - $hostip{$id}=$ip; - if ($id eq $perlvar{'lonHostID'}) { $thisserver=$name; } + if (!($configline =~ /^\s*\#/)) { + my ($id,$domain,$role,$name,$ip)=split(/:/,$configline); + chomp($ip); $ip=~s/\D+$//; + $hostid{$ip}=$id; + $hostdom{$id}=$domain; + $hostip{$id}=$ip; + if ($id eq $perlvar{'lonHostID'}) { $thisserver=$name; } + } } close(CONFIG); } @@ -3587,17 +968,17 @@ sub checkchildren { &status("Checking on the children (waiting for reports)"); foreach (sort keys %children) { unless (-e "$docdir/lon-status/londchld/$_.txt") { - eval { - alarm(300); - &logthis('Child '.$_.' did not respond'); - kill 9 => $_; - #$emailto="$perlvar{'lonAdmEMail'},$perlvar{'lonSysEMail'}"; - #$subj="LON: $currenthostid killed lond process $_"; - #my $result=`echo 'Killed lond process $_.' | mailto $emailto -s '$subj' > /dev/null`; - #$execdir=$perlvar{'lonDaemons'}; - #$result=`/bin/cp $execdir/logs/lond.log $execdir/logs/lond.log.$_`; - alarm(0); - } + eval { + alarm(300); + &logthis('Child '.$_.' did not respond'); + kill 9 => $_; + #$emailto="$perlvar{'lonAdmEMail'},$perlvar{'lonSysEMail'}"; + #$subj="LON: $currenthostid killed lond process $_"; + #my $result=`echo 'Killed lond process $_.' | mailto $emailto -s '$subj' > /dev/null`; + #$execdir=$perlvar{'lonDaemons'}; + #$result=`/bin/cp $execdir/logs/lond.log $execdir/logs/lond.log.$_`; + alarm(0); + } } } $SIG{ALRM} = 'DEFAULT'; @@ -3633,10 +1014,7 @@ sub Debug { # reply - Text to send to client. # request - Original request from client. # -# Note: This increments Transactions -# sub Reply { - alarm(120); my $fd = shift; my $reply = shift; my $request = shift; @@ -3644,33 +1022,6 @@ sub Reply { print $fd $reply; Debug("Request was $request Reply was $reply"); - $Transactions++; - alarm(0); - - -} -# -# Sub to report a failure. -# This function: -# - Increments the failure statistic counters. -# - Invokes Reply to send the error message to the client. -# Parameters: -# fd - File descriptor open on the client -# reply - Reply text to emit. -# request - The original request message (used by Reply -# to debug if that's enabled. -# Implicit outputs: -# $Failures- The number of failures is incremented. -# Reply (invoked here) sends a message to the -# client: -# -sub Failure { - my $fd = shift; - my $reply = shift; - my $request = shift; - - $Failures++; - Reply($fd, $reply, $request); # That's simple eh? } # ------------------------------------------------------------------ Log status @@ -3678,19 +1029,17 @@ sub logstatus { &status("Doing logging"); my $docdir=$perlvar{'lonDocRoot'}; { - my $fh=IO::File->new(">>$docdir/lon-status/londstatus.txt"); - print $fh $$."\t".$currenthostid."\t".$status."\t".$lastlog."\n"; - $fh->close(); + my $fh=IO::File->new(">>$docdir/lon-status/londstatus.txt"); + print $fh $$."\t".$clientname."\t".$currenthostid."\t".$status."\t".$lastlog."\n"; + $fh->close(); } &status("Finished londstatus.txt"); { my $fh=IO::File->new(">$docdir/lon-status/londchld/$$.txt"); - print $fh $status."\n".$lastlog."\n".time; - $fh->close(); + print $fh $status."\n".$lastlog."\n".time; + $fh->close(); } - ResetStatistics; &status("Finished logging"); - } sub initnewstatus { @@ -3712,11 +1061,8 @@ sub status { my $what=shift; my $now=time; my $local=localtime($now); - my $status = "lond: $what $local "; - if($Transactions) { - $status .= " Transactions: $Transactions Failed; $Failures"; - } - $0=$status; + $status=$local.': '.$what; + $0='lond: '.$what.' '.$local; } # -------------------------------------------------------- Escape Special Chars @@ -3748,11 +1094,12 @@ sub reconlonc { &logthis("lonc at pid $loncpid responding, sending USR1"); kill USR1 => $loncpid; } else { - &logthis("CRITICAL: " - ."lonc at pid $loncpid not responding, giving up"); + &logthis( + "CRITICAL: " + ."lonc at pid $loncpid not responding, giving up"); } } else { - &logthis('CRITICAL: lonc not running, giving up'); + &logthis('CRITICAL: lonc not running, giving up'); } } @@ -3764,7 +1111,7 @@ sub subreply { my $sclient=IO::Socket::UNIX->new(Peer =>"$peerfile", Type => SOCK_STREAM, Timeout => 10) - or return "con_lost"; + or return "con_lost"; print $sclient "$cmd\n"; my $answer=<$sclient>; chomp($answer); @@ -3773,22 +1120,22 @@ sub subreply { } sub reply { - my ($cmd,$server)=@_; - my $answer; - if ($server ne $currenthostid) { - $answer=subreply($cmd,$server); - if ($answer eq 'con_lost') { - $answer=subreply("ping",$server); - if ($answer ne $server) { - &logthis("sub reply: answer != server answer is $answer, server is $server"); - &reconlonc("$perlvar{'lonSockDir'}/$server"); - } - $answer=subreply($cmd,$server); - } - } else { - $answer='self_reply'; - } - return $answer; + my ($cmd,$server)=@_; + my $answer; + if ($server ne $currenthostid) { + $answer=subreply($cmd,$server); + if ($answer eq 'con_lost') { + $answer=subreply("ping",$server); + if ($answer ne $server) { + &logthis("sub reply: answer != server answer is $answer, server is $server"); + &reconlonc("$perlvar{'lonSockDir'}/$server"); + } + $answer=subreply($cmd,$server); + } + } else { + $answer='self_reply'; + } + return $answer; } # -------------------------------------------------------------- Talk to lonsql @@ -3807,7 +1154,7 @@ sub subsqlreply { my $sclient=IO::Socket::UNIX->new(Peer =>"$peerfile", Type => SOCK_STREAM, Timeout => 10) - or return "con_lost"; + or return "con_lost"; print $sclient "$cmd\n"; my $answer=<$sclient>; chomp($answer); @@ -3819,14 +1166,11 @@ sub subsqlreply { sub propath { my ($udom,$uname)=@_; - Debug("Propath:$udom:$uname"); $udom=~s/\W//g; $uname=~s/\W//g; - Debug("Propath2:$udom:$uname"); my $subdir=$uname.'__'; $subdir =~ s/(.)(.)(.).*/$1\/$2\/$3/; my $proname="$perlvar{'lonUsersDir'}/$udom/$subdir/$uname"; - Debug("Propath returning $proname"); return $proname; } @@ -3834,13 +1178,9 @@ sub propath { sub ishome { my $author=shift; - Debug("ishome: $author"); $author=~s/\/home\/httpd\/html\/res\/([^\/]*)\/([^\/]*).*/$1\/$2/; - Debug(" after big regsub: $author"); my ($udom,$uname)=split(/\//,$author); - Debug(" domain: $udom user: $uname"); my $proname=propath($udom,$uname); - Debug(" path = $proname"); if (-e $proname) { return 'owner'; } else { @@ -3881,7 +1221,6 @@ $SIG{USR2} = \&UpdateHosts; ReadHostTable; - # -------------------------------------------------------------- # Accept connections. When a connection comes in, it is validated # and if good, a child process is created to process transactions @@ -3897,6 +1236,7 @@ while (1) { sub make_new_child { my $pid; + my $cipher; my $sigset; $client = shift; @@ -3907,7 +1247,7 @@ sub make_new_child { $sigset = POSIX::SigSet->new(SIGINT); sigprocmask(SIG_BLOCK, $sigset) or die "Can't block SIGINT for fork: $!\n"; - + die "fork: $!" unless defined ($pid = fork); $client->sockopt(SO_KEEPALIVE, 1); # Enable monitoring of @@ -3926,7 +1266,6 @@ sub make_new_child { sigprocmask(SIG_UNBLOCK, $sigset) or die "Can't unblock SIGINT for fork: $!\n"; $children{$pid} = $clientip; - $children++; &status('Started child '.$pid); return; } else { @@ -3943,11 +1282,11 @@ sub make_new_child { sigprocmask(SIG_UNBLOCK, $sigset) or die "Can't unblock SIGINT for fork: $!\n"; - - + my $tmpsnum=0; +#---------------------------------------------------- kerberos 5 initialization &Authen::Krb5::init_context(); &Authen::Krb5::init_ets(); - + &status('Accepted connection'); # ============================================================================= # do something with the connection @@ -3974,7 +1313,7 @@ sub make_new_child { &status("Waiting for init from $clientip $clientname"); &logthis('INFO: Connection, '. $clientip. - " ($clientname) connection type = $ConnectionType " ); + " ($clientname) connection type = $ConnectionType " ); &status("Connecting $clientip ($clientname))"); my $remotereq=<$client>; $remotereq=~s/[^\w:]//g; @@ -3982,23 +1321,27 @@ sub make_new_child { &sethost("sethost:$perlvar{'lonHostID'}"); my $challenge="$$".time; print $client "$challenge\n"; - &status("Waiting for challenge reply from $clientip ($clientname)"); + &status( + "Waiting for challenge reply from $clientip ($clientname)"); $remotereq=<$client>; $remotereq=~s/\W//g; if ($challenge eq $remotereq) { $clientok=1; print $client "ok\n"; } else { - &logthis("WARNING: $clientip did not reply challenge"); + &logthis( + "WARNING: $clientip did not reply challenge"); &status('No challenge reply '.$clientip); } } else { - &logthis("WARNING: " + &logthis( + "WARNING: " ."$clientip failed to initialize: >$remotereq< "); &status('No init '.$clientip); } } else { - &logthis("WARNING: Unknown client $clientip"); + &logthis( + "WARNING: Unknown client $clientip"); &status('Hung up on '.$clientip); } if ($clientok) { @@ -4014,15 +1357,1472 @@ sub make_new_child { } &logthis("Established connection: $clientname"); &status('Will listen to '.$clientname); +# ------------------------------------------------------------ Process requests + while (my $userinput=<$client>) { + chomp($userinput); + Debug("Request = $userinput\n"); + &status('Processing '.$clientname.': '.$userinput); + my $wasenc=0; + alarm(120); +# ------------------------------------------------------------ See if encrypted + if ($userinput =~ /^enc/) { + if ($cipher) { + my ($cmd,$cmdlength,$encinput)=split(/:/,$userinput); + $userinput=''; + for (my $encidx=0;$encidxdecrypt( + pack("H16",substr($encinput,$encidx,16)) + ); + } + $userinput=substr($userinput,0,$cmdlength); + $wasenc=1; + } + } + +# ------------------------------------------------------------- Normal commands +# ------------------------------------------------------------------------ ping + if ($userinput =~ /^ping/) { # client only + if(isClient) { + print $client "$currenthostid\n"; + } else { + Reply($client, "refused\n", $userinput); + } +# ------------------------------------------------------------------------ pong + }elsif ($userinput =~ /^pong/) { # client only + if(isClient) { + my $reply=&reply("ping",$clientname); + print $client "$currenthostid:$reply\n"; + } else { + Reply($client, "refused\n", $userinput); + } +# ------------------------------------------------------------------------ ekey + } elsif ($userinput =~ /^ekey/) { # ok for both clients & mgrs + my $buildkey=time.$$.int(rand 100000); + $buildkey=~tr/1-6/A-F/; + $buildkey=int(rand 100000).$buildkey.int(rand 100000); + my $key=$currenthostid.$clientname; + $key=~tr/a-z/A-Z/; + $key=~tr/G-P/0-9/; + $key=~tr/Q-Z/0-9/; + $key=$key.$buildkey.$key.$buildkey.$key.$buildkey; + $key=substr($key,0,32); + my $cipherkey=pack("H32",$key); + $cipher=new IDEA $cipherkey; + print $client "$buildkey\n"; +# ------------------------------------------------------------------------ load + } elsif ($userinput =~ /^load/) { # client only + if (isClient) { + my $loadavg; + { + my $loadfile=IO::File->new('/proc/loadavg'); + $loadavg=<$loadfile>; + } + $loadavg =~ s/\s.*//g; + my $loadpercent=100*$loadavg/$perlvar{'lonLoadLim'}; + print $client "$loadpercent\n"; + } else { + Reply($client, "refused\n", $userinput); + + } +# -------------------------------------------------------------------- userload + } elsif ($userinput =~ /^userload/) { # client only + if(isClient) { + my $userloadpercent=&userload(); + print $client "$userloadpercent\n"; + } else { + Reply($client, "refused\n", $userinput); + + } +# +# Transactions requiring encryption: +# +# ----------------------------------------------------------------- currentauth + } elsif ($userinput =~ /^currentauth/) { + if (($wasenc==1) && isClient) { # Encoded & client only. + my ($cmd,$udom,$uname)=split(/:/,$userinput); + my $result = GetAuthType($udom, $uname); + if($result eq "nouser") { + print $client "unknown_user\n"; + } + else { + print $client "$result\n" + } + } else { + Reply($client, "refused\n", $userinput); + + } +#--------------------------------------------------------------------- pushfile + } elsif($userinput =~ /^pushfile/) { # encoded & manager. + if(($wasenc == 1) && isManager) { + my $cert = GetCertificate($userinput); + if(ValidManager($cert)) { + my $reply = PushFile($userinput); + print $client "$reply\n"; + } else { + print $client "refused\n"; + } + } else { + Reply($client, "refused\n", $userinput); + + } +#--------------------------------------------------------------------- reinit + } elsif($userinput =~ /^reinit/) { # Encoded and manager + if (($wasenc == 1) && isManager) { + my $cert = GetCertificate($userinput); + if(ValidManager($cert)) { + chomp($userinput); + my $reply = ReinitProcess($userinput); + print $client "$reply\n"; + } else { + print $client "refused\n"; + } + } else { + Reply($client, "refused\n", $userinput); + } +#------------------------------------------------------------------------- edit + } elsif ($userinput =~ /^edit/) { # encoded and manager: + if(($wasenc ==1) && (isManager)) { + my $cert = GetCertificate($userinput); + if(ValidManager($cert)) { + my($command, $filetype, $script) = split(/:/, $userinput); + if (($filetype eq "hosts") || ($filetype eq "domain")) { + if($script ne "") { + Reply($client, EditFile($userinput)); + } else { + Reply($client,"refused\n",$userinput); + } + } else { + Reply($client,"refused\n",$userinput); + } + } else { + Reply($client,"refused\n",$userinput); + } + } else { + Reply($client,"refused\n",$userinput); + } +# ------------------------------------------------------------------------ auth + } elsif ($userinput =~ /^auth/) { # Encoded and client only. + if (($wasenc==1) && isClient) { + my ($cmd,$udom,$uname,$upass)=split(/:/,$userinput); + chomp($upass); + $upass=unescape($upass); + my $proname=propath($udom,$uname); + my $passfilename="$proname/passwd"; + if (-e $passfilename) { + my $pf = IO::File->new($passfilename); + my $realpasswd=<$pf>; + chomp($realpasswd); + my ($howpwd,$contentpwd)=split(/:/,$realpasswd); + my $pwdcorrect=0; + if ($howpwd eq 'internal') { + &Debug("Internal auth"); + $pwdcorrect= + (crypt($upass,$contentpwd) eq $contentpwd); + } elsif ($howpwd eq 'unix') { + &Debug("Unix auth"); + if((getpwnam($uname))[1] eq "") { #no such user! + $pwdcorrect = 0; + } else { + $contentpwd=(getpwnam($uname))[1]; + my $pwauth_path="/usr/local/sbin/pwauth"; + unless ($contentpwd eq 'x') { + $pwdcorrect= + (crypt($upass,$contentpwd) eq + $contentpwd); + } + + elsif (-e $pwauth_path) { + open PWAUTH, "|$pwauth_path" or + die "Cannot invoke authentication"; + print PWAUTH "$uname\n$upass\n"; + close PWAUTH; + $pwdcorrect=!$?; + } + } + } elsif ($howpwd eq 'krb4') { + my $null=pack("C",0); + unless ($upass=~/$null/) { + my $krb4_error = &Authen::Krb4::get_pw_in_tkt + ($uname,"",$contentpwd,'krbtgt', + $contentpwd,1,$upass); + if (!$krb4_error) { + $pwdcorrect = 1; + } else { + $pwdcorrect=0; + # log error if it is not a bad password + if ($krb4_error != 62) { + &logthis('krb4:'.$uname.','.$contentpwd.','. + &Authen::Krb4::get_err_txt($Authen::Krb4::error)); + } + } + } + } elsif ($howpwd eq 'krb5') { + my $null=pack("C",0); + unless ($upass=~/$null/) { + my $krbclient=&Authen::Krb5::parse_name($uname.'@'.$contentpwd); + my $krbservice="krbtgt/".$contentpwd."\@".$contentpwd; + my $krbserver=&Authen::Krb5::parse_name($krbservice); + my $credentials=&Authen::Krb5::cc_default(); + $credentials->initialize($krbclient); + my $krbreturn = + &Authen::Krb5::get_in_tkt_with_password( + $krbclient,$krbserver,$upass,$credentials); +# unless ($krbreturn) { +# &logthis("Krb5 Error: ". +# &Authen::Krb5::error()); +# } + $pwdcorrect = ($krbreturn == 1); + } else { $pwdcorrect=0; } + } elsif ($howpwd eq 'localauth') { + $pwdcorrect=&localauth::localauth($uname,$upass, + $contentpwd); + } + if ($pwdcorrect) { + print $client "authorized\n"; + } else { + print $client "non_authorized\n"; + } + } else { + print $client "unknown_user\n"; + } + } else { + Reply($client, "refused\n", $userinput); + + } +# ---------------------------------------------------------------------- passwd + } elsif ($userinput =~ /^passwd/) { # encoded and client + if (($wasenc==1) && isClient) { + my + ($cmd,$udom,$uname,$upass,$npass)=split(/:/,$userinput); + chomp($npass); + $upass=&unescape($upass); + $npass=&unescape($npass); + &Debug("Trying to change password for $uname"); + my $proname=propath($udom,$uname); + my $passfilename="$proname/passwd"; + if (-e $passfilename) { + my $realpasswd; + { my $pf = IO::File->new($passfilename); + $realpasswd=<$pf>; } + chomp($realpasswd); + my ($howpwd,$contentpwd)=split(/:/,$realpasswd); + if ($howpwd eq 'internal') { + &Debug("internal auth"); + if (crypt($upass,$contentpwd) eq $contentpwd) { + my $salt=time; + $salt=substr($salt,6,2); + my $ncpass=crypt($npass,$salt); + { + my $pf; + if ($pf = IO::File->new(">$passfilename")) { + print $pf "internal:$ncpass\n"; + &logthis("Result of password change for $uname: pwchange_success"); + print $client "ok\n"; + } else { + &logthis("Unable to open $uname passwd to change password"); + print $client "non_authorized\n"; + } + } + + } else { + print $client "non_authorized\n"; + } + } elsif ($howpwd eq 'unix') { + # Unix means we have to access /etc/password + # one way or another. + # First: Make sure the current password is + # correct + &Debug("auth is unix"); + $contentpwd=(getpwnam($uname))[1]; + my $pwdcorrect = "0"; + my $pwauth_path="/usr/local/sbin/pwauth"; + unless ($contentpwd eq 'x') { + $pwdcorrect= + (crypt($upass,$contentpwd) eq $contentpwd); + } elsif (-e $pwauth_path) { + open PWAUTH, "|$pwauth_path" or + die "Cannot invoke authentication"; + print PWAUTH "$uname\n$upass\n"; + close PWAUTH; + &Debug("exited pwauth with $? ($uname,$upass) "); + $pwdcorrect=($? == 0); + } + if ($pwdcorrect) { + my $execdir=$perlvar{'lonDaemons'}; + &Debug("Opening lcpasswd pipeline"); + my $pf = IO::File->new("|$execdir/lcpasswd > $perlvar{'lonDaemons'}/logs/lcpasswd.log"); + print $pf "$uname\n$npass\n$npass\n"; + close $pf; + my $err = $?; + my $result = ($err>0 ? 'pwchange_failure' + : 'ok'); + &logthis("Result of password change for $uname: ". + &lcpasswdstrerror($?)); + print $client "$result\n"; + } else { + print $client "non_authorized\n"; + } + } else { + print $client "auth_mode_error\n"; + } + } else { + print $client "unknown_user\n"; + } + } else { + Reply($client, "refused\n", $userinput); + + } +# -------------------------------------------------------------------- makeuser + } elsif ($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); + print $client $result; + } + } 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); - ResetStatistics(); + } +# ---------------------------------------------------------------------- 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); -# ------------------------------------------------------------ Process requests - my $KeepGoing = 1; - while ((my $userinput=GetRequest) && $KeepGoing) { - $KeepGoing = ProcessRequest($userinput); -# -------------------------------------------------------------------- complete + } +# -------------------------------------- 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)=split(/\//,$fname); + my $udir=propath($udom,$uname).'/userfiles'; + unless (-e $udir) { mkdir($udir,0770); } + if (-e $udir) { + $ufile=~s/^[\.\~]+//; + $ufile=~s/\///g; + 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); + + } +# ------------------------------------------ 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=~/userfile\.$fname\=/) { $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($client,$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); + $namespace=~s/\//\_/g; + $namespace=~s/\W//g; + if ($namespace ne 'roles') { + chomp($what); + my $proname=propath($udom,$uname); + my $now=time; + unless ($namespace=~/^nohist\_/) { + my $hfh; + if ( + $hfh=IO::File->new(">>$proname/$namespace.hist") + ) { print $hfh "P:$now:$what\n"; } + } + my @pairs=split(/\&/,$what); + my %hash; + if (tie(%hash,'GDBM_File', + "$proname/$namespace.db", + &GDBM_WRCREAT(),0640)) { + 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; + unless ($namespace=~/^nohist\_/) { + my $hfh; + if ( + $hfh=IO::File->new(">>$proname/$namespace.hist") + ) { print $hfh "P:$now:$what\n"; } + } + my @pairs=split(/\&/,$what); + my %hash; + if (tie(%hash,'GDBM_File', + "$proname/$namespace.db", + &GDBM_WRCREAT(),0640)) { + 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 put\n"; + } + } else { + print $client "error: ".($!) + ." tie(GDBM) Failed ". + "while attempting put\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 $hfh; + if ( + $hfh=IO::File->new(">>$proname/$namespace.hist") + ) { + print $hfh "P:$now:$exedom:$exeuser:$what\n"; + } + } + my @pairs=split(/\&/,$what); + my %hash; + if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT(),0640)) { + foreach my $pair (@pairs) { + my ($key,$value)=split(/=/,$pair); + &ManagePermissions($key, $udom, $uname, + &GetAuthType( $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 $hfh; + if ( + $hfh=IO::File->new(">>$proname/$namespace.hist") + ) { + print $hfh "D:$now:$exedom:$exeuser:$what\n"; + } + } + my @rolekeys=split(/\&/,$what); + my %hash; + if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT(),0640)) { + 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; + unless ($namespace=~/^nohist\_/) { + my $hfh; + if ( + $hfh=IO::File->new(">>$proname/$namespace.hist") + ) { print $hfh "D:$now:$what\n"; } + } + my @keys=split(/\&/,$what); + my %hash; + if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT(),0640)) { + 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; + unless ($namespace=~/^nohist\_/) { + my $hfh; + if ( + $hfh=IO::File->new(">>$proname/$namespace.hist") + ) { print $hfh "P:$now:$rid:$what\n"; } + } + my @pairs=split(/\&/,$what); + my %hash; + if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT(),0640)) { + 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(isClient) { + my ($cmd,$cdom,$cnum,$newpost)=split(/\:/,$userinput); + &chatadd($cdom,$cnum,$newpost); + print $client "ok\n"; + } else { + Reply($client, "refused\n", $userinput); + + } +# -------------------------------------------------------------------- chatretr + } elsif ($userinput =~ /^chatretr/) { + if(isClient) { + my + ($cmd,$cdom,$cnum,$udom,$uname)=split(/\:/,$userinput); + my $reply=''; + foreach (&getchat($cdom,$cnum,$udom,$uname)) { + $reply.=&escape($_).':'; + } + $reply=~s/\:$//; + print $client $reply."\n"; + } else { + Reply($client, "refused\n", $userinput); + + } +# ------------------------------------------------------------------- querysend + } elsif ($userinput =~ /^querysend/) { + if(isClient) { + my ($cmd,$query, + $arg1,$arg2,$arg3)=split(/\:/,$userinput); + $query=~s/\n*$//g; + print $client "". + sqlreply("$clientname\&$query". + "\&$arg1"."\&$arg2"."\&$arg3")."\n"; + } else { + Reply($client, "refused\n", $userinput); + + } +# ------------------------------------------------------------------ queryreply + } elsif ($userinput =~ /^queryreply/) { + if(isClient) { + my ($cmd,$id,$reply)=split(/:/,$userinput); + my $store; + my $execdir=$perlvar{'lonDaemons'}; + if ($store=IO::File->new(">$execdir/tmp/$id")) { + $reply=~s/\&/\n/g; + print $store $reply; + close $store; + my $store2=IO::File->new(">$execdir/tmp/$id.end"); + print $store2 "done\n"; + close $store2; + print $client "ok\n"; + } + else { + print $client "error: ".($!+0) + ." IO::File->new Failed ". + "while attempting queryreply\n"; + } + } else { + Reply($client, "refused\n", $userinput); + + } +# ----------------------------------------------------------------- courseidput + } elsif ($userinput =~ /^courseidput/) { + if(isClient) { + my ($cmd,$udom,$what)=split(/:/,$userinput); + chomp($what); + $udom=~s/\W//g; + my $proname= + "$perlvar{'lonUsersDir'}/$udom/nohist_courseids"; + my $now=time; + my @pairs=split(/\&/,$what); + my %hash; + if (tie(%hash,'GDBM_File',"$proname.db",&GDBM_WRCREAT(),0640)) { + foreach my $pair (@pairs) { + my ($key,$value)=split(/=/,$pair); + $hash{$key}=$value.':'.$now; + } + if (untie(%hash)) { + print $client "ok\n"; + } else { + print $client "error: ".($!+0) + ." untie(GDBM) Failed ". + "while attempting courseidput\n"; + } + } else { + print $client "error: ".($!+0) + ." tie(GDBM) Failed ". + "while attempting courseidput\n"; + } + } else { + Reply($client, "refused\n", $userinput); + + } +# ---------------------------------------------------------------- courseiddump + } elsif ($userinput =~ /^courseiddump/) { + if(isClient) { + my ($cmd,$udom,$since,$description) + =split(/:/,$userinput); + if (defined($description)) { + $description=&unescape($description); + } else { + $description='.'; + } + unless (defined($since)) { $since=0; } + my $qresult=''; + my $proname= + "$perlvar{'lonUsersDir'}/$udom/nohist_courseids"; + my %hash; + if (tie(%hash,'GDBM_File',"$proname.db",&GDBM_READER(),0640)) { + while (my ($key,$value) = each(%hash)) { + my ($descr,$lasttime)=split(/\:/,$value); + if ($lasttime<$since) { next; } + if ($description eq '.') { + $qresult.=$key.'='.$descr.'&'; + } else { + my $unescapeVal = &unescape($descr); + if (eval('$unescapeVal=~/$description/i')) { + $qresult.="$key=$descr&"; + } + } + } + if (untie(%hash)) { + chop($qresult); + print $client "$qresult\n"; + } else { + print $client "error: ".($!+0) + ." untie(GDBM) Failed ". + "while attempting courseiddump\n"; + } + } else { + print $client "error: ".($!+0) + ." tie(GDBM) Failed ". + "while attempting courseiddump\n"; + } + } else { + Reply($client, "refused\n", $userinput); + + } +# ----------------------------------------------------------------------- idput + } elsif ($userinput =~ /^idput/) { + if(isClient) { + my ($cmd,$udom,$what)=split(/:/,$userinput); + chomp($what); + $udom=~s/\W//g; + my $proname="$perlvar{'lonUsersDir'}/$udom/ids"; + my $now=time; + { + my $hfh; + if ( + $hfh=IO::File->new(">>$proname.hist") + ) { print $hfh "P:$now:$what\n"; } + } + my @pairs=split(/\&/,$what); + my %hash; + if (tie(%hash,'GDBM_File',"$proname.db",&GDBM_WRCREAT(),0640)) { + 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 idput\n"; + } + } else { + print $client "error: ".($!+0) + ." tie(GDBM) Failed ". + "while attempting idput\n"; + } + } else { + Reply($client, "refused\n", $userinput); + + } +# ----------------------------------------------------------------------- idget + } elsif ($userinput =~ /^idget/) { + if(isClient) { + my ($cmd,$udom,$what)=split(/:/,$userinput); + chomp($what); + $udom=~s/\W//g; + my $proname="$perlvar{'lonUsersDir'}/$udom/ids"; + my @queries=split(/\&/,$what); + my $qresult=''; + my %hash; + if (tie(%hash,'GDBM_File',"$proname.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 idget\n"; + } + } else { + print $client "error: ".($!+0) + ." tie(GDBM) Failed ". + "while attempting idget\n"; + } + } else { + Reply($client, "refused\n", $userinput); + + } +# ---------------------------------------------------------------------- tmpput + } elsif ($userinput =~ /^tmpput/) { + if(isClient) { + my ($cmd,$what)=split(/:/,$userinput); + my $store; + $tmpsnum++; + my $id=$$.'_'.$clientip.'_'.$tmpsnum; + $id=~s/\W/\_/g; + $what=~s/\n//g; + my $execdir=$perlvar{'lonDaemons'}; + if ($store=IO::File->new(">$execdir/tmp/$id.tmp")) { + print $store $what; + close $store; + print $client "$id\n"; + } + else { + print $client "error: ".($!+0) + ."IO::File->new Failed ". + "while attempting tmpput\n"; + } + } else { + Reply($client, "refused\n", $userinput); + + } + +# ---------------------------------------------------------------------- tmpget + } elsif ($userinput =~ /^tmpget/) { + if(isClient) { + my ($cmd,$id)=split(/:/,$userinput); + chomp($id); + $id=~s/\W/\_/g; + my $store; + my $execdir=$perlvar{'lonDaemons'}; + if ($store=IO::File->new("$execdir/tmp/$id.tmp")) { + my $reply=<$store>; + print $client "$reply\n"; + close $store; + } + else { + print $client "error: ".($!+0) + ."IO::File->new Failed ". + "while attempting tmpget\n"; + } + } else { + Reply($client, "refused\n", $userinput); + + } +# ---------------------------------------------------------------------- tmpdel + } elsif ($userinput =~ /^tmpdel/) { + if(isClient) { + my ($cmd,$id)=split(/:/,$userinput); + chomp($id); + $id=~s/\W/\_/g; + my $execdir=$perlvar{'lonDaemons'}; + if (unlink("$execdir/tmp/$id.tmp")) { + print $client "ok\n"; + } else { + print $client "error: ".($!+0) + ."Unlink tmp Failed ". + "while attempting tmpdel\n"; + } + } else { + Reply($client, "refused\n", $userinput); + + } +# -------------------------------------------------------------------------- ls + } elsif ($userinput =~ /^ls/) { + if(isClient) { + my $obs; + my $rights; + my ($cmd,$ulsdir)=split(/:/,$userinput); + my $ulsout=''; + my $ulsfn; + if (-e $ulsdir) { + if(-d $ulsdir) { + if (opendir(LSDIR,$ulsdir)) { + while ($ulsfn=readdir(LSDIR)) { + undef $obs, $rights; + my @ulsstats=stat($ulsdir.'/'.$ulsfn); + #We do some obsolete checking here + if(-e $ulsdir.'/'.$ulsfn.".meta") { + open(FILE, $ulsdir.'/'.$ulsfn.".meta"); + my @obsolete=; + foreach my $obsolete (@obsolete) { + if($obsolete =~ m|()(on)|) { $obs = 1; } + if($obsolete =~ m|()(default)|) { $rights = 1; } + } + } + $ulsout.=$ulsfn.'&'.join('&',@ulsstats); + if($obs eq '1') { $ulsout.="&1"; } + else { $ulsout.="&0"; } + if($rights eq '1') { $ulsout.="&1:"; } + else { $ulsout.="&0:"; } + } + closedir(LSDIR); + } + } else { + my @ulsstats=stat($ulsdir); + $ulsout.=$ulsfn.'&'.join('&',@ulsstats).':'; + } + } else { + $ulsout='no_such_dir'; + } + if ($ulsout eq '') { $ulsout='empty'; } + print $client "$ulsout\n"; + } else { + Reply($client, "refused\n", $userinput); + + } +# ----------------------------------------------------------------- setannounce + } elsif ($userinput =~ /^setannounce/) { + if (isClient) { + my ($cmd,$announcement)=split(/:/,$userinput); + chomp($announcement); + $announcement=&unescape($announcement); + if (my $store=IO::File->new('>'.$perlvar{'lonDocRoot'}. + '/announcement.txt')) { + print $store $announcement; + close $store; + print $client "ok\n"; + } else { + print $client "error: ".($!+0)."\n"; + } + } else { + Reply($client, "refused\n", $userinput); + + } +# ------------------------------------------------------------------ Hanging up + } elsif (($userinput =~ /^exit/) || + ($userinput =~ /^init/)) { # no restrictions. + &logthis( + "Client $clientip ($clientname) hanging up: $userinput"); + print $client "bye\n"; + $client->shutdown(2); # shutdown the socket forcibly. + $client->close(); + last; + +# ---------------------------------- set current host/domain + } elsif ($userinput =~ /^sethost:/) { + if (isClient) { + print $client &sethost($userinput)."\n"; + } else { + print $client "refused\n"; + } +#---------------------------------- request file (?) version. + } elsif ($userinput =~/^version:/) { + if (isClient) { + print $client &version($userinput)."\n"; + } else { + print $client "refused\n"; + } +# ------------------------------------------------------------- unknown command + + } else { + # unknown command + print $client "unknown_cmd\n"; + } +# -------------------------------------------------------------------- complete + alarm(0); &status('Listening to '.$clientname); } # --------------------------------------------- client unknown or fishy, refuse @@ -4059,14 +2859,14 @@ 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 ManagePermissions +{ my $request = shift; my $domain = shift; my $user = shift; my $authtype= shift; # See if the request is of the form /$domain/_au - &logthis("request is $request"); if($request =~ /^(\/$domain\/_au)$/) { # It's an author rolesput... my $execdir = $perlvar{'lonDaemons'}; my $userhome= "/home/$user" ; @@ -4074,231 +2874,39 @@ sub ManagePermissions { system("$execdir/lchtmldir $userhome $user $authtype"); } } - -# -# Return the full path of a user password file, whether it exists or not. -# Parameters: -# domain - Domain in which the password file lives. -# user - name of the user. -# Returns: -# Full passwd path: -# -sub PasswordPath { - my $domain = shift; - my $user = shift; - - my $path = &propath($domain, $user); - $path .= "/passwd"; - - return $path; -} - -# Password Filename -# Returns the path to a passwd file given domain and user... only if -# it exists. -# Parameters: -# domain - Domain in which to search. -# user - username. -# Returns: -# - If the password file exists returns its path. -# - If the password file does not exist, returns undefined. -# -sub PasswordFilename { - my $domain = shift; - my $user = shift; - - Debug ("PasswordFilename called: dom = $domain user = $user"); - - my $path = PasswordPath($domain, $user); - Debug("PasswordFilename got path: $path"); - if(-e $path) { - return $path; - } else { - return undef; - } -} - -# -# Rewrite the contents of the user's passwd file. -# Parameters: -# domain - domain of the user. -# name - User's name. -# contents - New contents of the file. -# Returns: -# 0 - Failed. -# 1 - Success. -# -sub RewritePwFile { - my $domain = shift; - my $user = shift; - my $contents = shift; - - my $file = PasswordFilename($domain, $user); - if (defined $file) { - my $pf = IO::File->new(">$file"); - if($pf) { - print $pf "$contents\n"; - return 1; - } else { - return 0; - } - } else { - return 0; - } - -} # # GetAuthType - Determines the authorization type of a user in a domain. # Returns the authorization type or nouser if there is no such user. # -sub GetAuthType { +sub GetAuthType +{ my $domain = shift; my $user = shift; Debug("GetAuthType( $domain, $user ) \n"); - my $passwdfile = PasswordFilename($domain, $user); - if( defined $passwdfile ) { + my $proname = &propath($domain, $user); + my $passwdfile = "$proname/passwd"; + if( -e $passwdfile ) { my $pf = IO::File->new($passwdfile); my $realpassword = <$pf>; chomp($realpassword); Debug("Password info = $realpassword\n"); - return $realpassword; - } else { - Debug("Returning nouser"); - return "nouser"; - } -} - -# -# Validate a user given their domain, name and password. This utility -# function is used by both AuthenticateHandler and ChangePasswordHandler -# to validate the login credentials of a user. -# Parameters: -# $domain - The domain being logged into (this is required due to -# the capability for multihomed systems. -# $user - The name of the user being validated. -# $password - The user's propoposed password. -# -# Returns: -# 1 - The domain,user,pasword triplet corresponds to a valid -# user. -# 0 - The domain,user,password triplet is not a valid user. -# -sub ValidateUser { - my $domain = shift; - my $user = shift; - my $password= shift; - - # Why negative ~pi you may well ask? Well this function is about - # authentication, and therefore very important to get right. - # I've initialized the flag that determines whether or not I've - # validated correctly to a value it's not supposed to get. - # At the end of this function. I'll ensure that it's not still that - # value so we don't just wind up returning some accidental value - # as a result of executing an unforseen code path that - # did not set $validated. - - my $validated = -3.14159; - - # How we authenticate is determined by the type of authentication - # the user has been assigned. If the authentication type is - # "nouser", the user does not exist so we will return 0. - - my $contents = GetAuthType($domain, $user); - my ($howpwd, $contentpwd) = split(/:/, $contents); - - my $null = pack("C",0); # Used by kerberos auth types. - - if ($howpwd ne 'nouser') { - - if($howpwd eq "internal") { # Encrypted is in local password file. - $validated = (crypt($password, $contentpwd) eq $contentpwd); - } - elsif ($howpwd eq "unix") { # User is a normal unix user. - $contentpwd = (getpwnam($user))[1]; - if($contentpwd) { - if($contentpwd eq 'x') { # Shadow password file... - my $pwauth_path = "/usr/local/sbin/pwauth"; - open PWAUTH, "|$pwauth_path" or - die "Cannot invoke authentication"; - print PWAUTH "$user\n$password\n"; - close PWAUTH; - $validated = ! $?; - - } else { # Passwords in /etc/passwd. - $validated = (crypt($password, - $contentpwd) eq $contentpwd); - } - } else { - $validated = 0; - } - } - elsif ($howpwd eq "krb4") { # user is in kerberos 4 auth. domain. - if(! ($password =~ /$null/) ) { - my $k4error = &Authen::Krb4::get_pw_in_tkt($user, - "", - $contentpwd,, - 'krbtgt', - $contentpwd, - 1, - $password); - if(!$k4error) { - $validated = 1; - } - else { - $validated = 0; - &logthis('krb4: '.$user.', '.$contentpwd.', '. - &Authen::Krb4::get_err_txt($Authen::Krb4::error)); - } - } - else { - $validated = 0; # Password has a match with null. - } + my ($authtype, $contentpwd) = split(/:/, $realpassword); + Debug("Authtype = $authtype, content = $contentpwd\n"); + my $availinfo = ''; + if($authtype eq 'krb4' or $authtype eq 'krb5') { + $availinfo = $contentpwd; } - 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); - my $krbservice = "krbtgt/".$contentpwd."\@".$contentpwd; - my $krbserver = &Authen::Krb5::parse_name($krbservice); - my $credentials= &Authen::Krb5::cc_default(); - $credentials->initialize($krbclient); - my $krbreturn = &Authen::KRb5::get_in_tkt_with_password($krbclient, - $krbserver, - $password, - $credentials); - $validated = ($krbreturn == 1); - } - else { - $validated = 0; - } - } - elsif ($howpwd eq "localauth") { - # Authenticate via installation specific authentcation method: - $validated = &localauth::localauth($user, - $password, - $contentpwd); - } - else { # Unrecognized auth is also bad. - $validated = 0; - } - } else { - $validated = 0; - } - # - # $validated has the correct stat of the authentication: - # - unless ($validated != -3.14159) { - die "ValidateUser - failed to set the value of validated"; + return "$authtype:$availinfo"; + } + else { + Debug("Returning nouser"); + return "nouser"; } - return $validated; } -# -# Add a line to the subscription list? -# sub addline { my ($fname,$hostid,$ip,$newline)=@_; my $contents; @@ -4306,31 +2914,19 @@ sub addline { my $expr='^'.$hostid.':'.$ip.':'; $expr =~ s/\./\\\./g; my $sh; - Debug("Looking for $expr"); if ($sh=IO::File->new("$fname.subscription")) { while (my $subline=<$sh>) { - Debug("addline: line: $subline"); - if ($subline !~ /$expr/) { - $contents.= $subline; - } else { - Debug("Found $subline"); - $found=1; - } + if ($subline !~ /$expr/) {$contents.= $subline;} else {$found=1;} } $sh->close(); } $sh=IO::File->new(">$fname.subscription"); if ($contents) { print $sh $contents; } - if ($newline) { - Debug("Appending $newline"); - print $sh $newline; - } + if ($newline) { print $sh $newline; } $sh->close(); return $found; } -# -# Get chat messages. -# + sub getchat { my ($cdom,$cname,$udom,$uname)=@_; my %hash; @@ -4355,9 +2951,7 @@ sub getchat { } return (@participants,@entries); } -# -# Add a chat message -# + sub chatadd { my ($cdom,$cname,$newchat)=@_; my %hash; @@ -4397,36 +2991,17 @@ sub chatadd { sub unsub { my ($fname,$clientip)=@_; my $result; - my $unsubs = 0; # Number of successful unsubscribes: - - - # An old way subscriptions were handled was to have a - # subscription marker file: - - Debug("Attempting unlink of $fname.$clientname"); if (unlink("$fname.$clientname")) { - $unsubs++; # Successful unsub via marker file. - } - - # The more modern way to do it is to have a subscription list - # file: - + $result="ok\n"; + } else { + $result="not_subscribed\n"; + } if (-e "$fname.subscription") { my $found=&addline($fname,$clientname,$clientip,''); - if ($found) { - $unsubs++; - } - } - - # If either or both of these mechanisms succeeded in unsubscribing a - # resource we can return ok: - - if($unsubs) { - $result = "ok\n"; + if ($found) { $result="ok\n"; } } else { - $result = "not_subscribed\n"; + if ($result != "ok\n") { $result="not_subscribed\n"; } } - return $result; } @@ -4435,7 +3010,7 @@ sub currentversion { my $version=-1; my $ulsdir=''; if ($fname=~/^(.+)\/[^\/]+$/) { - $ulsdir=$1; + $ulsdir=$1; } my ($fnamere1,$fnamere2); # remove version if already specified @@ -4478,15 +3053,12 @@ sub thisversion { sub subscribe { my ($userinput,$clientip)=@_; - chomp($userinput); my $result; my ($cmd,$fname)=split(/:/,$userinput); my $ownership=&ishome($fname); - Debug("subscribe: Owner = $ownership file: '$fname'"); if ($ownership eq 'owner') { # explitly asking for the current version? unless (-e $fname) { - Debug("subscribe - does not exist"); my $currentversion=¤tversion($fname); if (&thisversion($fname)==$currentversion) { if ($fname=~/^(.+)\.\d+\.(\w+(?:\.meta)*)$/) { @@ -4495,14 +3067,13 @@ sub subscribe { symlink($root.'.'.$extension, $root.'.'.$currentversion.'.'.$extension); unless ($extension=~/\.meta$/) { - symlink($root.'.'.$extension.'.meta', - $root.'.'.$currentversion.'.'.$extension.'.meta'); + symlink($root.'.'.$extension.'.meta', + $root.'.'.$currentversion.'.'.$extension.'.meta'); } } } } if (-e $fname) { - Debug("subscribe - exists"); if (-d $fname) { $result="directory\n"; } else { @@ -4551,35 +3122,23 @@ sub make_passwd_file { print $pf "localauth:$npass\n"; } } elsif ($umode eq 'unix') { - # - # Don't allow the creation of privileged accounts!!! that would - # be real bad!!! - # - my $uid = getpwnam($uname); - if((defined $uid) && ($uid == 0)) { - &logthis(">>>Attempted add of privileged account blocked<<<"); - return "no_priv_account_error\n"; - } - - # - my $execpath="$perlvar{'lonDaemons'}/"."lcuseradd"; - - &Debug("Executing external: ".$execpath); - &Debug("user = ".$uname.", Password =". $npass); - my $se = IO::File->new("|$execpath > $perlvar{'lonDaemons'}/logs/lcuseradd.log"); - print $se "$uname\n"; - print $se "$npass\n"; - print $se "$npass\n"; - - my $useraddok = $?; - if($useraddok > 0) { - my $lcstring = lcuseraddstrerror($useraddok); - &logthis("Failed lcuseradd: $lcstring"); - return "error: lcuseradd failed: $lcstring\n"; + { + my $execpath="$perlvar{'lonDaemons'}/"."lcuseradd"; + { + &Debug("Executing external: ".$execpath); + &Debug("user = ".$uname.", Password =". $npass); + my $se = IO::File->new("|$execpath > $perlvar{'lonDaemons'}/logs/lcuseradd.log"); + print $se "$uname\n"; + print $se "$npass\n"; + print $se "$npass\n"; + } + my $useraddok = $?; + if($useraddok > 0) { + &logthis("Failed lcuseradd: ".&lcuseraddstrerror($useraddok)); + } + my $pf = IO::File->new(">$passfilename"); + print $pf "unix:\n"; } - my $pf = IO::File->new(">$passfilename"); - print $pf "unix:\n"; - } elsif ($umode eq 'none') { { my $pf = IO::File->new(">$passfilename"); @@ -4593,10 +3152,8 @@ sub make_passwd_file { sub sethost { my ($remotereq) = @_; - Debug("sethost got $remotereq"); my (undef,$hostid)=split(/:/,$remotereq); if (!defined($hostid)) { $hostid=$perlvar{'lonHostID'}; } - Debug("sethost attempting to set host $hostid"); if ($hostip{$perlvar{'lonHostID'}} eq $hostip{$hostid}) { $currenthostid=$hostid; $currentdomainid=$hostdom{$hostid}; @@ -4614,10 +3171,8 @@ sub version { $remoteVERSION=(split(/:/,$userinput))[1]; return "version:$VERSION"; } -############## >>>>>>>>>>>>>>>>>>>>>>>>>> FUTUREWORK <<<<<<<<<<<<<<<<<<<<<<<<<<<< + #There is a copy of this in lonnet.pm -# Can we hoist these lil' things out into common places? -# sub userload { my $numusers=0; {