--- loncom/lond 2004/03/16 20:48:49 1.184 +++ loncom/lond 2004/07/29 10:50:54 1.218 @@ -2,7 +2,7 @@ # The LearningOnline Network # lond "LON Daemon" Server (port "LOND" 5663) # -# $Id: lond,v 1.184 2004/03/16 20:48:49 raeburn Exp $ +# $Id: lond,v 1.218 2004/07/29 10:50:54 foxr Exp $ # # Copyright Michigan State University Board of Trustees # @@ -45,25 +45,34 @@ use Authen::Krb4; use Authen::Krb5; use lib '/home/httpd/lib/perl/'; use localauth; +use localenroll; use File::Copy; use LONCAPA::ConfigFileEdit; +use LONCAPA::lonlocal; +use LONCAPA::lonssl; my $DEBUG = 0; # Non zero to enable debug log entries. my $status=''; my $lastlog=''; -my $VERSION='$Revision: 1.184 $'; #' stupid emacs +my $VERSION='$Revision: 1.218 $'; #' stupid emacs my $remoteVERSION; -my $currenthostid; +my $currenthostid="default"; my $currentdomainid; my $client; -my $clientip; -my $clientname; +my $clientip; # IP address of client. +my $clientdns; # DNS name of client. +my $clientname; # LonCAPA name of client. my $server; -my $thisserver; +my $thisserver; # DNS of us. + +my $keymode; + +my $cipher; # Cipher key negotiated with client +my $tmpsnum = 0; # Id of tmpputs. # # Connection type is: @@ -74,15 +83,30 @@ my $thisserver; my $ConnectionType; -my %hostid; -my %hostdom; -my %hostip; +my %hostid; # ID's for hosts in cluster by ip. +my %hostdom; # LonCAPA domain for hosts in cluster. +my %hostip; # IPs for hosts in cluster. +my %hostdns; # ID's of hosts looked up by DNS name. 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. @@ -120,6 +144,195 @@ my @adderrors = ("ok", "lcuseradd Password mismatch"); + +# +# Statistics that are maintained and dislayed in the status line. +# +my $Transactions = 0; # Number of attempted transactions. +my $Failures = 0; # Number of transcations failed. + +# ResetStatistics: +# Resets the statistics counters: +# +sub ResetStatistics { + $Transactions = 0; + $Failures = 0; +} + + + +#------------------------------------------------------------------------ +# +# LocalConnection +# Completes the formation of a locally authenticated connection. +# This function will ensure that the 'remote' client is really the +# local host. If not, the connection is closed, and the function fails. +# If so, initcmd is parsed for the name of a file containing the +# IDEA session key. The fie is opened, read, deleted and the session +# key returned to the caller. +# +# Parameters: +# $Socket - Socket open on client. +# $initcmd - The full text of the init command. +# +# Implicit inputs: +# $clientdns - The DNS name of the remote client. +# $thisserver - Our DNS name. +# +# Returns: +# IDEA session key on success. +# undef on failure. +# +sub LocalConnection { + my ($Socket, $initcmd) = @_; + Debug("Attempting local connection: $initcmd client: $clientdns me: $thisserver"); + if($clientdns ne $thisserver) { + &logthis(' LocalConnection rejecting non local: ' + ."$clientdns ne $thisserver "); + close $Socket; + return undef; + } + else { + chomp($initcmd); # Get rid of \n in filename. + my ($init, $type, $name) = split(/:/, $initcmd); + Debug(" Init command: $init $type $name "); + + # Require that $init = init, and $type = local: Otherwise + # the caller is insane: + + if(($init ne "init") && ($type ne "local")) { + &logthis(' LocalConnection: caller is insane! ' + ."init = $init, and type = $type "); + close($Socket);; + return undef; + + } + # Now get the key filename: + + my $IDEAKey = lonlocal::ReadKeyFile($name); + return $IDEAKey; + } +} +#------------------------------------------------------------------------------ +# +# SSLConnection +# Completes the formation of an ssh authenticated connection. The +# socket is promoted to an ssl socket. If this promotion and the associated +# certificate exchange are successful, the IDEA key is generated and sent +# to the remote peer via the SSL tunnel. The IDEA key is also returned to +# the caller after the SSL tunnel is torn down. +# +# Parameters: +# Name Type Purpose +# $Socket IO::Socket::INET Plaintext socket. +# +# Returns: +# IDEA key on success. +# undef on failure. +# +sub SSLConnection { + my $Socket = shift; + + Debug("SSLConnection: "); + my $KeyFile = lonssl::KeyFile(); + if(!$KeyFile) { + my $err = lonssl::LastError(); + &logthis(" CRITICAL" + ."Can't get key file $err "); + return undef; + } + my ($CACertificate, + $Certificate) = lonssl::CertificateFile(); + + + # If any of the key, certificate or certificate authority + # certificate filenames are not defined, this can't work. + + if((!$Certificate) || (!$CACertificate)) { + my $err = lonssl::LastError(); + &logthis(" CRITICAL" + ."Can't get certificates: $err "); + + return undef; + } + Debug("Key: $KeyFile CA: $CACertificate Cert: $Certificate"); + + # Indicate to our peer that we can procede with + # a transition to ssl authentication: + + print $Socket "ok:ssl\n"; + + Debug("Approving promotion -> ssl"); + # And do so: + + my $SSLSocket = lonssl::PromoteServerSocket($Socket, + $CACertificate, + $Certificate, + $KeyFile); + if(! ($SSLSocket) ) { # SSL socket promotion failed. + my $err = lonssl::LastError(); + &logthis(" CRITICAL " + ."SSL Socket promotion failed: $err "); + return undef; + } + Debug("SSL Promotion successful"); + + # + # The only thing we'll use the socket for is to send the IDEA key + # to the peer: + + my $Key = lonlocal::CreateCipherKey(); + print $SSLSocket "$Key\n"; + + lonssl::Close($SSLSocket); + + Debug("Key exchange complete: $Key"); + + return $Key; +} +# +# InsecureConnection: +# If insecure connections are allowd, +# exchange a challenge with the client to 'validate' the +# client (not really, but that's the protocol): +# We produce a challenge string that's sent to the client. +# The client must then echo the challenge verbatim to us. +# +# Parameter: +# Socket - Socket open on the client. +# Returns: +# 1 - success. +# 0 - failure (e.g.mismatch or insecure not allowed). +# +sub InsecureConnection { + my $Socket = shift; + + # Don't even start if insecure connections are not allowed. + + if(! $perlvar{londAllowInsecure}) { # Insecure connections not allowed. + return 0; + } + + # Fabricate a challenge string and send it.. + + my $challenge = "$$".time; # pid + time. + print $Socket "$challenge\n"; + &status("Waiting for challenge reply"); + + my $answer = <$Socket>; + $answer =~s/\W//g; + if($challenge eq $answer) { + return 1; + } + else { + logthis("WARNING client did not respond to challenge"); + &status("No challenge reqply"); + return 0; + } + + +} + # # GetCertificate: Given a transaction that requires a certificate, # this function will extract the certificate from the transaction @@ -175,7 +388,6 @@ sub ReadManagerTable { 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 @@ -225,8 +437,8 @@ sub ValidManager { # 1 - Success. # sub CopyFile { - my $oldfile = shift; - my $newfile = shift; + + my ($oldfile, $newfile) = @_; # The file must exist: @@ -326,8 +538,8 @@ sub AdjustHostContents { # 0 - failure and $! has an errno. # sub InstallFile { - my $Filename = shift; - my $Contents = shift; + + my ($Filename, $Contents) = @_; my $TempFile = $Filename.".tmp"; # Open the file for write: @@ -350,6 +562,8 @@ sub InstallFile { return 1; } + + # # ConfigFileFromSelector: converts a configuration file selector # (one of host or domain at this point) into a @@ -564,8 +778,8 @@ sub isValidEditCommand { # file being edited. # sub ApplyEdit { - my $directive = shift; - my $editor = shift; + + my ($directive, $editor) = @_; # Break the directive down into its command and its parameters # (at most two at this point. The meaning of the parameters, if in fact @@ -649,8 +863,8 @@ sub AdjustOurHost { # editor - Editor containing the file. # sub ReplaceConfigFile { - my $filename = shift; - my $editor = shift; + + my ($filename, $editor) = @_; CopyFile ($filename, $filename.".old"); @@ -719,6 +933,2112 @@ sub EditFile { return "ok\n"; } + +#--------------------------------------------------------------- +# +# Manipulation of hash based databases (factoring out common code +# for later use as we refactor. +# +# 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 tie_domain_hash { + my ($domain,$namespace,$how,$loghead,$logtail) = @_; + + # Filter out any whitespace in the domain name: + + $domain =~ s/\W//g; + + # We have enough to go on to tie the hash: + + my $user_top_dir = $perlvar{'lonUsersDir'}; + my $domain_dir = $user_top_dir."/$domain"; + my $resource_file = $domain_dir."/$namespace.db"; + my %hash; + if(tie(%hash, 'GDBM_File', $resource_file, $how, 0640)) { + if (defined($loghead)) { # Need to log the operation. + my $logFh = IO::File->new(">>$domain_dir/$namespace.hist"); + if($logFh) { + my $timestamp = time; + print $logFh "$loghead:$timestamp:$logtail\n"; + } + $logFh->close; + } + 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 tie_user_hash { + my ($domain,$user,$namespace,$how,$loghead,$what) = @_; + + $namespace=~s/\//\_/g; # / -> _ + $namespace=~s/\W//g; # whitespace eliminated. + my $proname = propath($domain, $user); + + # Tie the database. + + my %hash; + if(tie(%hash, 'GDBM_File', "$proname/$namespace.db", + $how, 0640)) { + # If this is a namespace for which a history is kept, + # make the history log entry: + if (($namespace =~/^nohist\_/) && (defined($loghead))) { + my $args = scalar @_; + Debug(" Opening history: $namespace $args"); + my $hfh = IO::File->new(">>$proname/$namespace.hist"); + if($hfh) { + my $now = time; + print $hfh "$loghead:$now:$what\n"; + } + $hfh->close; + } + return \%hash; + } else { + return undef; + } + +} + +#--------------------- 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 ping_handler { + my ($cmd, $tail, $client) = @_; + Debug("$cmd $tail $client .. $currenthostid:"); + + Reply( $client,"$currenthostid\n","$cmd:$tail"); + + return 1; +} +®ister_handler("ping", \&ping_handler, 0, 1, 1); # Ping unencoded, client or manager. + +#++ +# +# Handles pong requests. Pong replies with our current host id, and +# the results of a ping sent to us via our lonc. +# +# 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 pong_handler { + my ($cmd, $tail, $replyfd) = @_; + + my $reply=&reply("ping",$clientname); + &Reply( $replyfd, "$currenthostid:$reply\n", "$cmd:$tail"); + return 1; +} +®ister_handler("pong", \&pong_handler, 0, 1, 1); # Pong unencoded, client or manager + +#++ +# Called to establish an encrypted session key with the remote client. +# Note that with secure lond, in most cases this function is never +# invoked. Instead, the secure session key is established either +# via a local file that's locked down tight and only lives for a short +# time, or via an ssl tunnel...and is generated from a bunch-o-random +# bits from /dev/urandom, rather than the predictable pattern used by +# by this sub. This sub is only used in the old-style insecure +# key negotiation. +# 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 establish_key_handler { + my ($cmd, $tail, $replyfd) = @_; + + 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; + +} +®ister_handler("ekey", \&establish_key_handler, 0, 1,1); + + +# 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 load_handler { + my ($cmd, $tail, $replyfd) = @_; + + # 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; +} +register_handler("load", \&load_handler, 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 user_load_handler { + my ($cmd, $tail, $replyfd) = @_; + + my $userloadpercent=&userload(); + &Reply($replyfd, "$userloadpercent\n", "$cmd:$tail"); + + return 1; +} +register_handler("userload", \&user_load_handler, 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 user_authorization_type { + my ($cmd, $tail, $replyfd) = @_; + + 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; +} +®ister_handler("currentauth", \&user_authorization_type, 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 push_file_handler { + my ($cmd, $tail, $client) = @_; + + 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); + } +} +®ister_handler("pushfile", \&push_file_handler, 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 reinit_process_handler { + my ($cmd, $tail, $client) = @_; + + 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; +} + +®ister_handler("reinit", \&reinit_process_handler, 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 edit_table_handler { + my ($command, $tail, $client) = @_; + + 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; +} +register_handler("edit", \&edit_table_handler, 1, 0, 1); + + + + +#--------------------------------------------------------------- +# +# Getting, decoding and dispatching requests: +# + +# +# 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 get_request { + my $input = <$client>; + chomp($input); + + Debug("get_request: Request = $input\n"); + + &status('Processing '.$clientname.':'.$input); + + return $input; +} +#--------------------------------------------------------------- +# +# Process a request. This sub should shrink as each action +# gets farmed out into a separat sub that is registered +# with the dispatch hash. +# +# Parameters: +# user_input - The request received from the client (lonc). +# Returns: +# true to keep processing, false if caller should exit. +# +sub process_request { + my ($userinput) = @_; # Easier for now to break style than to + # fix all the userinput -> user_input. + my $wasenc = 0; # True if request was encrypted. +# ------------------------------------------------------------ See if encrypted + if ($userinput =~ /^enc/) { + $userinput = decipher($userinput); + $wasenc=1; + if(!$userinput) { # Cipher not defined. + &Failure($client, "error: Encrypted data without negotated key"); + return 0; + } + } + Debug("process_request: $userinput\n"); + + # + # The 'correct way' to add a command to lond is now to + # write a sub to execute it and Add it to the command dispatch + # hash via a call to register_handler.. The comments to that + # sub should give you enough to go on to show how to do this + # along with the examples that are building up as this code + # is getting refactored. Until all branches of the + # if/elseif monster below have been factored out into + # separate procesor subs, if the dispatch hash is missing + # the command keyword, we will fall through to the remainder + # of the if/else chain below in order to keep this thing in + # working order throughout the transmogrification. + + my ($command, $tail) = split(/:/, $userinput, 2); + chomp($command); + chomp($tail); + $tail =~ s/(\r)//; # This helps people debugging with e.g. telnet. + $command =~ s/(\r)//; # And this too for parameterless commands. + if(!$tail) { + $tail =""; # defined but blank. + } + + &Debug("Command received: $command, encoded = $wasenc"); + + if(defined $Dispatcher{$command}) { + + my $dispatch_info = $Dispatcher{$command}; + my $handler = $$dispatch_info[0]; + my $need_encode = $$dispatch_info[1]; + my $client_types = $$dispatch_info[2]; + Debug("Matched dispatch hash: mustencode: $need_encode " + ."ClientType $client_types"); + + # Validate the request: + + my $ok = 1; + my $requesterprivs = 0; + if(&isClient()) { + $requesterprivs |= $CLIENT_OK; + } + if(&isManager()) { + $requesterprivs |= $MANAGER_OK; + } + if($need_encode && (!$wasenc)) { + Debug("Must encode but wasn't: $need_encode $wasenc"); + $ok = 0; + } + if(($client_types & $requesterprivs) == 0) { + Debug("Client not privileged to do this operation"); + $ok = 0; + } + + if($ok) { + Debug("Dispatching to handler $command $tail"); + my $keep_going = &$handler($command, $tail, $client); + return $keep_going; + } else { + Debug("Refusing to dispatch because client did not match requirements"); + Failure($client, "refused\n", $userinput); + return 1; + } + + } + +#------------------- Commands not yet in spearate handlers. -------------- + + +# ------------------------------------------------------------------------ auth + if ($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.','. + &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); + + } +# ---------------------------------------------------------------------- update + } elsif ($userinput =~ /^update/) { # client clear or encoded. + if(isClient) { + my ($cmd,$fname)=split(/:/,$userinput); + my $ownership=ishome($fname); + if ($ownership eq 'not_owner') { + if (-e $fname) { + my ($dev,$ino,$mode,$nlink, + $uid,$gid,$rdev,$size, + $atime,$mtime,$ctime, + $blksize,$blocks)=stat($fname); + my $now=time; + my $since=$now-$atime; + if ($since>$perlvar{'lonExpire'}) { + my $reply= + &reply("unsub:$fname","$clientname"); + unlink("$fname"); + } else { + my $transname="$fname.in.transfer"; + my $remoteurl= + &reply("sub:$fname","$clientname"); + my $response; + { + my $ua=new LWP::UserAgent; + my $request=new HTTP::Request('GET',"$remoteurl"); + $response=$ua->request($request,$transname); + } + if ($response->is_error()) { + unlink($transname); + my $message=$response->status_line; + &logthis( + "LWP GET: $message for $fname ($remoteurl)"); + } else { + if ($remoteurl!~/\.meta$/) { + my $ua=new LWP::UserAgent; + my $mrequest= + new HTTP::Request('GET',$remoteurl.'.meta'); + my $mresponse= + $ua->request($mrequest,$fname.'.meta'); + if ($mresponse->is_error()) { + unlink($fname.'.meta'); + } + } + rename($transname,$fname); + } + } + print $client "ok\n"; + } else { + print $client "not_found\n"; + } + } else { + print $client "rejected\n"; + } + } else { + Reply($client, "refused\n", $userinput); + + } +# -------------------------------------- fetch a user file from a remote server + } elsif ($userinput =~ /^fetchuserfile/) { # Client clear or enc. + if(isClient) { + my ($cmd,$fname)=split(/:/,$userinput); + my ($udom,$uname,$ufile) = ($fname =~ m|^([^/]+)/([^/]+)/(.+)$|); + my $udir=propath($udom,$uname).'/userfiles'; + unless (-e $udir) { mkdir($udir,0770); } + if (-e $udir) { + $ufile=~s/^[\.\~]+//; + my $path = $udir; + if ($ufile =~m|(.+)/([^/]+)$|) { + my @parts=split('/',$1); + foreach my $part (@parts) { + $path .= '/'.$part; + if ((-e $path)!=1) { + mkdir($path,0770); + } + } + } + my $destname=$udir.'/'.$ufile; + my $transname=$udir.'/'.$ufile.'.in.transit'; + my $remoteurl='http://'.$clientip.'/userfiles/'.$fname; + my $response; + { + my $ua=new LWP::UserAgent; + my $request=new HTTP::Request('GET',"$remoteurl"); + $response=$ua->request($request,$transname); + } + if ($response->is_error()) { + unlink($transname); + my $message=$response->status_line; + &logthis("LWP GET: $message for $fname ($remoteurl)"); + print $client "failed\n"; + } else { + if (!rename($transname,$destname)) { + &logthis("Unable to move $transname to $destname"); + unlink($transname); + print $client "failed\n"; + } else { + print $client "ok\n"; + } + } + } else { + print $client "not_home\n"; + } + } else { + Reply($client, "refused\n", $userinput); + } +# --------------------------------------------------------- remove a user file + } elsif ($userinput =~ /^removeuserfile/) { # Client clear or enc. + if(isClient) { + my ($cmd,$fname)=split(/:/,$userinput); + my ($udom,$uname,$ufile) = ($fname =~ m|^([^/]+)/([^/]+)/(.+)$|); + &logthis("$udom - $uname - $ufile"); + if ($ufile =~m|/\.\./|) { + # any files paths with /../ in them refuse + # to deal with + print $client "refused\n"; + } else { + my $udir=propath($udom,$uname); + if (-e $udir) { + my $file=$udir.'/userfiles/'.$ufile; + if (-e $file) { + unlink($file); + if (-e $file) { + print $client "failed\n"; + } else { + print $client "ok\n"; + } + } else { + print $client "not_found\n"; + } + } else { + print $client "not_home\n"; + } + } + } else { + Reply($client, "refused\n", $userinput); + } +# ------------------------------------------ authenticate access to a user file + } elsif ($userinput =~ /^tokenauthuserfile/) { # Client only + if(isClient) { + my ($cmd,$fname,$session)=split(/:/,$userinput); + chomp($session); + my $reply='non_auth'; + if (open(ENVIN,$perlvar{'lonIDsDir'}.'/'. + $session.'.id')) { + while (my $line=) { + if ($line=~ m|userfile\.\Q$fname\E\=|) { $reply='ok'; } + } + close(ENVIN); + print $client $reply."\n"; + } else { + print $client "invalid_token\n"; + } + } else { + Reply($client, "refused\n", $userinput); + + } +# ----------------------------------------------------------------- unsubscribe + } elsif ($userinput =~ /^unsub/) { + if(isClient) { + my ($cmd,$fname)=split(/:/,$userinput); + if (-e $fname) { + print $client &unsub($fname,$clientip); + } else { + print $client "not_found\n"; + } + } else { + Reply($client, "refused\n", $userinput); + + } +# ------------------------------------------------------------------- subscribe + } elsif ($userinput =~ /^sub/) { + if(isClient) { + print $client &subscribe($userinput,$clientip); + } else { + Reply($client, "refused\n", $userinput); + + } +# ------------------------------------------------------------- current version + } elsif ($userinput =~ /^currentversion/) { + if(isClient) { + my ($cmd,$fname)=split(/:/,$userinput); + print $client ¤tversion($fname)."\n"; + } else { + Reply($client, "refused\n", $userinput); + + } +# ------------------------------------------------------------------------- log + } elsif ($userinput =~ /^log/) { + if(isClient) { + my ($cmd,$udom,$uname,$what)=split(/:/,$userinput); + chomp($what); + my $proname=propath($udom,$uname); + my $now=time; + { + my $hfh; + if ($hfh=IO::File->new(">>$proname/activity.log")) { + print $hfh "$now:$clientname:$what\n"; + print $client "ok\n"; + } else { + print $client "error: ".($!+0) + ." IO::File->new Failed " + ."while attempting log\n"; + } + } + } else { + Reply($client, "refused\n", $userinput); + + } +# ------------------------------------------------------------------------- put + } elsif ($userinput =~ /^put/) { + if(isClient) { + my ($cmd,$udom,$uname,$namespace,$what) + =split(/:/,$userinput,5); + $namespace=~s/\//\_/g; + $namespace=~s/\W//g; + if ($namespace ne 'roles') { + chomp($what); + my $proname=propath($udom,$uname); + my $now=time; + my @pairs=split(/\&/,$what); + my %hash; + if (tie(%hash,'GDBM_File', + "$proname/$namespace.db", + &GDBM_WRCREAT(),0640)) { + unless ($namespace=~/^nohist\_/) { + my $hfh; + if ($hfh=IO::File->new(">>$proname/$namespace.hist")) { print $hfh "P:$now:$what\n"; } + } + + foreach my $pair (@pairs) { + my ($key,$value)=split(/=/,$pair); + $hash{$key}=$value; + } + if (untie(%hash)) { + print $client "ok\n"; + } else { + print $client "error: ".($!+0) + ." untie(GDBM) failed ". + "while attempting put\n"; + } + } else { + print $client "error: ".($!) + ." tie(GDBM) Failed ". + "while attempting put\n"; + } + } else { + print $client "refused\n"; + } + } else { + Reply($client, "refused\n", $userinput); + + } +# ------------------------------------------------------------------- inc + } elsif ($userinput =~ /^inc:/) { + if(isClient) { + my ($cmd,$udom,$uname,$namespace,$what) + =split(/:/,$userinput); + $namespace=~s/\//\_/g; + $namespace=~s/\W//g; + if ($namespace ne 'roles') { + chomp($what); + my $proname=propath($udom,$uname); + my $now=time; + my @pairs=split(/\&/,$what); + my %hash; + if (tie(%hash,'GDBM_File', + "$proname/$namespace.db", + &GDBM_WRCREAT(),0640)) { + unless ($namespace=~/^nohist\_/) { + my $hfh; + if ($hfh=IO::File->new(">>$proname/$namespace.hist")) { print $hfh "P:$now:$what\n"; } + } + foreach my $pair (@pairs) { + my ($key,$value)=split(/=/,$pair); + # We could check that we have a number... + if (! defined($value) || $value eq '') { + $value = 1; + } + $hash{$key}+=$value; + } + if (untie(%hash)) { + print $client "ok\n"; + } else { + print $client "error: ".($!+0) + ." untie(GDBM) failed ". + "while attempting inc\n"; + } + } else { + print $client "error: ".($!) + ." tie(GDBM) Failed ". + "while attempting inc\n"; + } + } else { + print $client "refused\n"; + } + } else { + Reply($client, "refused\n", $userinput); + + } +# -------------------------------------------------------------------- rolesput + } elsif ($userinput =~ /^rolesput/) { + if(isClient) { + &Debug("rolesput"); + if ($wasenc==1) { + my ($cmd,$exedom,$exeuser,$udom,$uname,$what) + =split(/:/,$userinput); + &Debug("cmd = ".$cmd." exedom= ".$exedom. + "user = ".$exeuser." udom=".$udom. + "what = ".$what); + my $namespace='roles'; + chomp($what); + my $proname=propath($udom,$uname); + my $now=time; + my @pairs=split(/\&/,$what); + my %hash; + if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT(),0640)) { + { + my $hfh; + if ($hfh=IO::File->new(">>$proname/$namespace.hist")) { + print $hfh "P:$now:$exedom:$exeuser:$what\n"; + } + } + + foreach my $pair (@pairs) { + my ($key,$value)=split(/=/,$pair); + &ManagePermissions($key, $udom, $uname, + &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 @rolekeys=split(/\&/,$what); + my %hash; + if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT(),0640)) { + { + my $hfh; + if ($hfh=IO::File->new(">>$proname/$namespace.hist")) { + print $hfh "D:$now:$exedom:$exeuser:$what\n"; + } + } + foreach my $key (@rolekeys) { + delete $hash{$key}; + } + if (untie(%hash)) { + print $client "ok\n"; + } else { + print $client "error: ".($!+0) + ." untie(GDBM) Failed ". + "while attempting rolesdel\n"; + } + } else { + print $client "error: ".($!+0) + ." tie(GDBM) Failed ". + "while attempting rolesdel\n"; + } + } else { + print $client "refused\n"; + } + } else { + Reply($client, "refused\n", $userinput); + + } +# ------------------------------------------------------------------------- get + } elsif ($userinput =~ /^get/) { + if(isClient) { + my ($cmd,$udom,$uname,$namespace,$what) + =split(/:/,$userinput); + $namespace=~s/\//\_/g; + $namespace=~s/\W//g; + chomp($what); + my @queries=split(/\&/,$what); + my $proname=propath($udom,$uname); + my $qresult=''; + my %hash; + if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER(),0640)) { + for (my $i=0;$i<=$#queries;$i++) { + $qresult.="$hash{$queries[$i]}&"; + } + if (untie(%hash)) { + $qresult=~s/\&$//; + print $client "$qresult\n"; + } else { + print $client "error: ".($!+0) + ." untie(GDBM) Failed ". + "while attempting get\n"; + } + } else { + if ($!+0 == 2) { + print $client "error:No such file or ". + "GDBM reported bad block error\n"; + } else { + print $client "error: ".($!+0) + ." tie(GDBM) Failed ". + "while attempting get\n"; + } + } + } else { + Reply($client, "refused\n", $userinput); + + } +# ------------------------------------------------------------------------ eget + } elsif ($userinput =~ /^eget/) { + if (isClient) { + my ($cmd,$udom,$uname,$namespace,$what) + =split(/:/,$userinput); + $namespace=~s/\//\_/g; + $namespace=~s/\W//g; + chomp($what); + my @queries=split(/\&/,$what); + my $proname=propath($udom,$uname); + my $qresult=''; + my %hash; + if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER(),0640)) { + for (my $i=0;$i<=$#queries;$i++) { + $qresult.="$hash{$queries[$i]}&"; + } + if (untie(%hash)) { + $qresult=~s/\&$//; + if ($cipher) { + my $cmdlength=length($qresult); + $qresult.=" "; + my $encqresult=''; + for + (my $encidx=0;$encidx<=$cmdlength;$encidx+=8) { + $encqresult.= + unpack("H16", + $cipher->encrypt(substr($qresult,$encidx,8))); + } + print $client "enc:$cmdlength:$encqresult\n"; + } else { + print $client "error:no_key\n"; + } + } else { + print $client "error: ".($!+0) + ." untie(GDBM) Failed ". + "while attempting eget\n"; + } + } else { + print $client "error: ".($!+0) + ." tie(GDBM) Failed ". + "while attempting eget\n"; + } + } else { + Reply($client, "refused\n", $userinput); + + } +# ------------------------------------------------------------------------- del + } elsif ($userinput =~ /^del/) { + if(isClient) { + my ($cmd,$udom,$uname,$namespace,$what) + =split(/:/,$userinput); + $namespace=~s/\//\_/g; + $namespace=~s/\W//g; + chomp($what); + my $proname=propath($udom,$uname); + my $now=time; + my @keys=split(/\&/,$what); + my %hash; + if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT(),0640)) { + unless ($namespace=~/^nohist\_/) { + my $hfh; + if ($hfh=IO::File->new(">>$proname/$namespace.hist")) { print $hfh "D:$now:$what\n"; } + } + foreach my $key (@keys) { + delete($hash{$key}); + } + if (untie(%hash)) { + print $client "ok\n"; + } else { + print $client "error: ".($!+0) + ." untie(GDBM) Failed ". + "while attempting del\n"; + } + } else { + print $client "error: ".($!+0) + ." tie(GDBM) Failed ". + "while attempting del\n"; + } + } else { + Reply($client, "refused\n", $userinput); + + } +# ------------------------------------------------------------------------ keys + } elsif ($userinput =~ /^keys/) { + if(isClient) { + my ($cmd,$udom,$uname,$namespace) + =split(/:/,$userinput); + $namespace=~s/\//\_/g; + $namespace=~s/\W//g; + my $proname=propath($udom,$uname); + my $qresult=''; + my %hash; + if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER(),0640)) { + foreach my $key (keys %hash) { + $qresult.="$key&"; + } + if (untie(%hash)) { + $qresult=~s/\&$//; + print $client "$qresult\n"; + } else { + print $client "error: ".($!+0) + ." untie(GDBM) Failed ". + "while attempting keys\n"; + } + } else { + print $client "error: ".($!+0) + ." tie(GDBM) Failed ". + "while attempting keys\n"; + } + } else { + Reply($client, "refused\n", $userinput); + + } +# ----------------------------------------------------------------- dumpcurrent + } elsif ($userinput =~ /^currentdump/) { + if (isClient) { + my ($cmd,$udom,$uname,$namespace) + =split(/:/,$userinput); + $namespace=~s/\//\_/g; + $namespace=~s/\W//g; + my $qresult=''; + my $proname=propath($udom,$uname); + my %hash; + if (tie(%hash,'GDBM_File', + "$proname/$namespace.db", + &GDBM_READER(),0640)) { + # Structure of %data: + # $data{$symb}->{$parameter}=$value; + # $data{$symb}->{'v.'.$parameter}=$version; + # since $parameter will be unescaped, we do not + # have to worry about silly parameter names... + my %data = (); + while (my ($key,$value) = each(%hash)) { + my ($v,$symb,$param) = split(/:/,$key); + next if ($v eq 'version' || $symb eq 'keys'); + next if (exists($data{$symb}) && + exists($data{$symb}->{$param}) && + $data{$symb}->{'v.'.$param} > $v); + $data{$symb}->{$param}=$value; + $data{$symb}->{'v.'.$param}=$v; + } + if (untie(%hash)) { + while (my ($symb,$param_hash) = each(%data)) { + while(my ($param,$value) = each (%$param_hash)){ + next if ($param =~ /^v\./); + $qresult.=$symb.':'.$param.'='.$value.'&'; + } + } + chop($qresult); + print $client "$qresult\n"; + } else { + print $client "error: ".($!+0) + ." untie(GDBM) Failed ". + "while attempting currentdump\n"; + } + } else { + print $client "error: ".($!+0) + ." tie(GDBM) Failed ". + "while attempting currentdump\n"; + } + } else { + Reply($client, "refused\n", $userinput); + } +# ------------------------------------------------------------------------ dump + } elsif ($userinput =~ /^dump/) { + if(isClient) { + my ($cmd,$udom,$uname,$namespace,$regexp) + =split(/:/,$userinput); + $namespace=~s/\//\_/g; + $namespace=~s/\W//g; + if (defined($regexp)) { + $regexp=&unescape($regexp); + } else { + $regexp='.'; + } + my $qresult=''; + my $proname=propath($udom,$uname); + my %hash; + if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER(),0640)) { + while (my ($key,$value) = each(%hash)) { + if ($regexp eq '.') { + $qresult.=$key.'='.$value.'&'; + } else { + my $unescapeKey = &unescape($key); + if (eval('$unescapeKey=~/$regexp/')) { + $qresult.="$key=$value&"; + } + } + } + if (untie(%hash)) { + chop($qresult); + print $client "$qresult\n"; + } else { + print $client "error: ".($!+0) + ." untie(GDBM) Failed ". + "while attempting dump\n"; + } + } else { + print $client "error: ".($!+0) + ." tie(GDBM) Failed ". + "while attempting dump\n"; + } + } else { + Reply($client, "refused\n", $userinput); + + } +# ----------------------------------------------------------------------- store + } elsif ($userinput =~ /^store/) { + if(isClient) { + my ($cmd,$udom,$uname,$namespace,$rid,$what) + =split(/:/,$userinput); + $namespace=~s/\//\_/g; + $namespace=~s/\W//g; + if ($namespace ne 'roles') { + chomp($what); + my $proname=propath($udom,$uname); + my $now=time; + my @pairs=split(/\&/,$what); + my %hash; + if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT(),0640)) { + unless ($namespace=~/^nohist\_/) { + my $hfh; + if ($hfh=IO::File->new(">>$proname/$namespace.hist")) { + print $hfh "P:$now:$rid:$what\n"; + } + } + my @previouskeys=split(/&/,$hash{"keys:$rid"}); + my $key; + $hash{"version:$rid"}++; + my $version=$hash{"version:$rid"}; + my $allkeys=''; + foreach my $pair (@pairs) { + my ($key,$value)=split(/=/,$pair); + $allkeys.=$key.':'; + $hash{"$version:$rid:$key"}=$value; + } + $hash{"$version:$rid:timestamp"}=$now; + $allkeys.='timestamp'; + $hash{"$version:keys:$rid"}=$allkeys; + if (untie(%hash)) { + print $client "ok\n"; + } else { + print $client "error: ".($!+0) + ." untie(GDBM) Failed ". + "while attempting store\n"; + } + } else { + print $client "error: ".($!+0) + ." tie(GDBM) Failed ". + "while attempting store\n"; + } + } else { + print $client "refused\n"; + } + } else { + Reply($client, "refused\n", $userinput); + + } +# --------------------------------------------------------------------- restore + } elsif ($userinput =~ /^restore/) { + if(isClient) { + my ($cmd,$udom,$uname,$namespace,$rid) + =split(/:/,$userinput); + $namespace=~s/\//\_/g; + $namespace=~s/\W//g; + chomp($rid); + my $proname=propath($udom,$uname); + my $qresult=''; + my %hash; + if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER(),0640)) { + my $version=$hash{"version:$rid"}; + $qresult.="version=$version&"; + my $scope; + for ($scope=1;$scope<=$version;$scope++) { + my $vkeys=$hash{"$scope:keys:$rid"}; + my @keys=split(/:/,$vkeys); + my $key; + $qresult.="$scope:keys=$vkeys&"; + foreach $key (@keys) { + $qresult.="$scope:$key=".$hash{"$scope:$rid:$key"}."&"; + } + } + if (untie(%hash)) { + $qresult=~s/\&$//; + print $client "$qresult\n"; + } else { + print $client "error: ".($!+0) + ." untie(GDBM) Failed ". + "while attempting restore\n"; + } + } else { + print $client "error: ".($!+0) + ." tie(GDBM) Failed ". + "while attempting restore\n"; + } + } else { + Reply($client, "refused\n", $userinput); + + } +# -------------------------------------------------------------------- chatsend + } elsif ($userinput =~ /^chatsend/) { + if(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,$descr,$inst_code)=split(/=/,$pair); + $hash{$key}=$descr.':'.$inst_code.':'.$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,$inst_code); + if ($value =~ m/^([^\:]*):([^\:]*):(\d+)$/) { + ($descr,$inst_code,$lasttime)=($1,$2,$3); + } else { + ($descr,$lasttime) = split(/\:/,$value); + } + if ($lasttime<$since) { next; } + if ($description eq '.') { + $qresult.=$key.'='.$descr.':'.$inst_code.'&'; + } else { + my $unescapeVal = &unescape($descr); + if (eval('$unescapeVal=~/\Q$description\E/i')) { + $qresult.=$key.'='.$descr.':'.$inst_code.'&'; + } + } + } + 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 @pairs=split(/\&/,$what); + my %hash; + if (tie(%hash,'GDBM_File',"$proname.db",&GDBM_WRCREAT(),0640)) { + { + my $hfh; + if ($hfh=IO::File->new(">>$proname.hist")) { + print $hfh "P:$now:$what\n"; + } + } + foreach my $pair (@pairs) { + my ($key,$value)=split(/=/,$pair); + $hash{$key}=$value; + } + if (untie(%hash)) { + print $client "ok\n"; + } else { + print $client "error: ".($!+0) + ." untie(GDBM) Failed ". + "while attempting 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); + + } +# ----------------------------------------- portfolio directory list (portls) + } elsif ($userinput =~ /^portls/) { + if(isClient) { + my ($cmd,$uname,$udom)=split(/:/,$userinput); + my $udir=propath($udom,$uname).'/userfiles/portfolio'; + my $dirLine=''; + my $dirContents=''; + if (opendir(LSDIR,$udir.'/')){ + while ($dirLine = readdir(LSDIR)){ + $dirContents = $dirContents.$dirLine.'
'; + } + } else { + $dirContents = "No directory found\n"; + } + print $client $dirContents."\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(); + return 0; + +# ---------------------------------- 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"; + } +#------------------------------- is auto-enrollment enabled? + } elsif ($userinput =~/^autorun:/) { + if (isClient) { + my ($cmd,$cdom) = split(/:/,$userinput); + my $outcome = &localenroll::run($cdom); + print $client "$outcome\n"; + } else { + print $client "0\n"; + } +#------------------------------- get official sections (for auto-enrollment). + } elsif ($userinput =~/^autogetsections:/) { + if (isClient) { + my ($cmd,$coursecode,$cdom)=split(/:/,$userinput); + my @secs = &localenroll::get_sections($coursecode,$cdom); + my $seclist = &escape(join(':',@secs)); + print $client "$seclist\n"; + } else { + print $client "refused\n"; + } +#----------------------- validate owner of new course section (for auto-enrollment). + } elsif ($userinput =~/^autonewcourse:/) { + if (isClient) { + my ($cmd,$inst_course_id,$owner,$cdom)=split(/:/,$userinput); + my $outcome = &localenroll::new_course($inst_course_id,$owner,$cdom); + print $client "$outcome\n"; + } else { + print $client "refused\n"; + } +#-------------- validate course section in schedule of classes (for auto-enrollment). + } elsif ($userinput =~/^autovalidatecourse:/) { + if (isClient) { + my ($cmd,$inst_course_id,$cdom)=split(/:/,$userinput); + my $outcome=&localenroll::validate_courseID($inst_course_id,$cdom); + print $client "$outcome\n"; + } else { + print $client "refused\n"; + } +#--------------------------- create password for new user (for auto-enrollment). + } elsif ($userinput =~/^autocreatepassword:/) { + if (isClient) { + my ($cmd,$authparam,$cdom)=split(/:/,$userinput); + my ($create_passwd,$authchk); + ($authparam,$create_passwd,$authchk) = &localenroll::create_password($authparam,$cdom); + print $client &escape($authparam.':'.$create_passwd.':'.$authchk)."\n"; + } else { + print $client "refused\n"; + } +#--------------------------- read and remove temporary files (for auto-enrollment). + } elsif ($userinput =~/^autoretrieve:/) { + if (isClient) { + my ($cmd,$filename) = split(/:/,$userinput); + my $source = $perlvar{'lonDaemons'}.'/tmp/'.$filename; + if ( (-e $source) && ($filename ne '') ) { + my $reply = ''; + if (open(my $fh,$source)) { + while (<$fh>) { + chomp($_); + $_ =~ s/^\s+//g; + $_ =~ s/\s+$//g; + $reply .= $_; + } + close($fh); + print $client &escape($reply)."\n"; +# unlink($source); + } else { + print $client "error\n"; + } + } else { + print $client "error\n"; + } + } else { + print $client "refused\n"; + } +#--------------------- read and retrieve institutional code format (for support form). + } elsif ($userinput =~/^autoinstcodeformat:/) { + if (isClient) { + my $reply; + my($cmd,$cdom,$course) = split(/:/,$userinput); + my @pairs = split/\&/,$course; + my %instcodes = (); + my %codes = (); + my @codetitles = (); + my %cat_titles = (); + my %cat_order = (); + foreach (@pairs) { + my ($key,$value) = split/=/,$_; + $instcodes{&unescape($key)} = &unescape($value); + } + my $formatreply = &localenroll::instcode_format($cdom,\%instcodes,\%codes,\@codetitles,\%cat_titles,\%cat_order); + if ($formatreply eq 'ok') { + my $codes_str = &hash2str(%codes); + my $codetitles_str = &array2str(@codetitles); + my $cat_titles_str = &hash2str(%cat_titles); + my $cat_order_str = &hash2str(%cat_order); + print $client $codes_str.':'.$codetitles_str.':'.$cat_titles_str.':'.$cat_order_str."\n"; + } + } else { + print $client "refused\n"; + } +# ------------------------------------------------------------- unknown command + + } else { + # unknown command + print $client "unknown_cmd\n"; + } +# -------------------------------------------------------------------- complete + Debug("process_request - returning 1"); + return 1; +} +# +# 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) = @_; + 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: +# $request_name - 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 +# $must_encode - True if the request must be encoded to be good. +# $client_ok - True if it's ok for a client to request this. +# $manager_ok - 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 register_handler { + my ($request_name,$procedure,$must_encode, $client_ok,$manager_ok) = @_; + + # Don't allow duplication# + + if (defined $Dispatcher{$request_name}) { + die "Attempting to define a duplicate request handler for $request_name\n"; + } + # Build the client type mask: + + my $client_type_mask = 0; + if($client_ok) { + $client_type_mask |= $CLIENT_OK; + } + if($manager_ok) { + $client_type_mask |= $MANAGER_OK; + } + + # Enter the hash: + + my @entry = ($procedure, $must_encode, $client_type_mask); + + $Dispatcher{$request_name} = \@entry; + + +} + + +#------------------------------------------------------------------ + + + + # # Convert an error return code from lcpasswd to a string value. # @@ -749,7 +3069,7 @@ sub catchexception { $SIG{'QUIT'}='DEFAULT'; $SIG{__DIE__}='DEFAULT'; &status("Catching exception"); - &logthis("CRITICAL: " + &logthis("CRITICAL: " ."ABNORMAL EXIT. Child $$ for server $thisserver died through " ."a crash with this error msg->[$error]"); &logthis('Famous last words: '.$status.' - '.$lastlog); @@ -760,7 +3080,7 @@ sub catchexception { sub timeout { &status("Handling Timeout"); - &logthis("CRITICAL: TIME OUT ".$$.""); + &logthis("CRITICAL: TIME OUT ".$$.""); &catchexception('Timeout'); } # -------------------------------- Set signal handlers to record abnormal exits @@ -843,7 +3163,7 @@ sub HUNTSMAN { # si &logthis("Free socket: ".shutdown($server,2)); # free up socket my $execdir=$perlvar{'lonDaemons'}; unlink("$execdir/logs/lond.pid"); - &logthis("CRITICAL: Shutting down"); + &logthis("CRITICAL: Shutting down"); &status("Done killing children"); exit; # clean up with dignity } @@ -853,7 +3173,7 @@ sub HUPSMAN { # sig &status("Killing children for restart (HUP)"); kill 'INT' => keys %children; &logthis("Free socket: ".shutdown($server,2)); # free up socket - &logthis("CRITICAL: Restarting"); + &logthis("CRITICAL: Restarting"); my $execdir=$perlvar{'lonDaemons'}; unlink("$execdir/logs/lond.pid"); &status("Restarting self (HUP)"); @@ -863,7 +3183,7 @@ sub HUPSMAN { # sig # # Kill off hashes that describe the host table prior to re-reading it. # Hashes affected are: -# %hostid, %hostdom %hostip +# %hostid, %hostdom %hostip %hostdns. # sub KillHostHashes { foreach my $key (keys %hostid) { @@ -875,6 +3195,9 @@ sub KillHostHashes { foreach my $key (keys %hostip) { delete $hostip{$key}; } + foreach my $key (keys %hostdns) { + delete $hostdns{$key}; + } } # # Read in the host table from file and distribute it into the various hashes: @@ -885,15 +3208,21 @@ sub KillHostHashes { sub ReadHostTable { open (CONFIG,"$perlvar{'lonTabDir'}/hosts.tab") || die "Can't read host file"; - + my $myloncapaname = $perlvar{'lonHostID'}; + Debug("My loncapa name is : $myloncapaname"); while (my $configline=) { 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; } + $hostid{$ip}=$id; # LonCAPA name of host by IP. + $hostdom{$id}=$domain; # LonCAPA domain name of host. + $hostip{$id}=$ip; # IP address of host. + $hostdns{$name} = $id; # LonCAPA name of host by DNS. + + if ($id eq $perlvar{'lonHostID'}) { + Debug("Found me in the host table: $name"); + $thisserver=$name; + } } } close(CONFIG); @@ -963,7 +3292,8 @@ sub checkchildren { } } sleep 5; - $SIG{ALRM} = sub { die "timeout" }; + $SIG{ALRM} = sub { Debug("timeout"); + die "timeout"; }; $SIG{__DIE__} = 'DEFAULT'; &status("Checking on the children (waiting for reports)"); foreach (sort keys %children) { @@ -1015,13 +3345,38 @@ sub Debug { # request - Original request from client. # sub Reply { - my $fd = shift; - my $reply = shift; - my $request = shift; - + my ($fd, $reply, $request) = @_; print $fd $reply; Debug("Request was $request Reply was $reply"); + $Transactions++; + + +} + + +# +# 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 @@ -1030,13 +3385,14 @@ sub logstatus { my $docdir=$perlvar{'lonDocRoot'}; { my $fh=IO::File->new(">>$docdir/lon-status/londstatus.txt"); - print $fh $$."\t".$clientname."\t".$currenthostid."\t".$status."\t".$lastlog."\n"; + print $fh $$."\t".$clientname."\t".$currenthostid."\t" + .$status."\t".$lastlog."\t $keymode\n"; $fh->close(); } &status("Finished londstatus.txt"); { my $fh=IO::File->new(">$docdir/lon-status/londchld/$$.txt"); - print $fh $status."\n".$lastlog."\n".time; + print $fh $status."\n".$lastlog."\n".time."\n$keymode"; $fh->close(); } &status("Finished logging"); @@ -1095,11 +3451,11 @@ sub reconlonc { kill USR1 => $loncpid; } else { &logthis( - "CRITICAL: " + "CRITICAL: " ."lonc at pid $loncpid not responding, giving up"); } } else { - &logthis('CRITICAL: lonc not running, giving up'); + &logthis('CRITICAL: lonc not running, giving up'); } } @@ -1203,7 +3559,7 @@ my $execdir=$perlvar{'lonDaemons'}; open (PIDSAVE,">$execdir/logs/lond.pid"); print PIDSAVE "$$\n"; close(PIDSAVE); -&logthis("CRITICAL: ---------- Starting ----------"); +&logthis("CRITICAL: ---------- Starting ----------"); &status('Starting'); @@ -1236,7 +3592,7 @@ while (1) { sub make_new_child { my $pid; - my $cipher; +# my $cipher; # Now global my $sigset; $client = shift; @@ -1265,9 +3621,12 @@ sub make_new_child { &logthis("Unable to determine who caller was, getpeername returned nothing"); } if (defined($iaddr)) { - $clientip=inet_ntoa($iaddr); + $clientip = inet_ntoa($iaddr); + Debug("Connected with $clientip"); + $clientdns = gethostbyaddr($iaddr, AF_INET); + Debug("Connected with $clientdns by name"); } else { - &logthis("Unable to determine clinetip"); + &logthis("Unable to determine clientip"); $clientip='Unavailable'; } @@ -1292,7 +3651,7 @@ sub make_new_child { sigprocmask(SIG_UNBLOCK, $sigset) or die "Can't unblock SIGINT for fork: $!\n"; - my $tmpsnum=0; +# my $tmpsnum=0; # Now global #---------------------------------------------------- kerberos 5 initialization &Authen::Krb5::init_context(); &Authen::Krb5::init_ets(); @@ -1301,7 +3660,7 @@ sub make_new_child { # ============================================================================= # do something with the connection # ----------------------------------------------------------------------------- - # see if we know client and check for spoof IP by challenge + # see if we know client and 'check' for spoof IP by ineffective challenge ReadManagerTable; # May also be a manager!! @@ -1319,6 +3678,7 @@ sub make_new_child { $clientname = $managers{$clientip}; } my $clientok; + if ($clientrec || $ismanager) { &status("Waiting for init from $clientip $clientname"); &logthis('INFO: Connection, '. @@ -1326,34 +3686,95 @@ sub make_new_child { " ($clientname) connection type = $ConnectionType " ); &status("Connecting $clientip ($clientname))"); my $remotereq=<$client>; - $remotereq=~s/[^\w:]//g; + chomp($remotereq); + Debug("Got init: $remotereq"); + my $inikeyword = split(/:/, $remotereq); if ($remotereq =~ /^init/) { &sethost("sethost:$perlvar{'lonHostID'}"); - my $challenge="$$".time; - print $client "$challenge\n"; - &status( - "Waiting for challenge reply from $clientip ($clientname)"); - $remotereq=<$client>; - $remotereq=~s/\W//g; - if ($challenge eq $remotereq) { - $clientok=1; - print $client "ok\n"; + # + # If the remote is attempting a local init... give that a try: + # + my ($i, $inittype) = split(/:/, $remotereq); + + # If the connection type is ssl, but I didn't get my + # certificate files yet, then I'll drop back to + # insecure (if allowed). + + if($inittype eq "ssl") { + my ($ca, $cert) = lonssl::CertificateFile; + my $kfile = lonssl::KeyFile; + if((!$ca) || + (!$cert) || + (!$kfile)) { + $inittype = ""; # This forces insecure attempt. + &logthis(" Certificates not " + ."installed -- trying insecure auth"); + } + else { # SSL certificates are in place so + } # Leave the inittype alone. + } + + if($inittype eq "local") { + my $key = LocalConnection($client, $remotereq); + if($key) { + Debug("Got local key $key"); + $clientok = 1; + my $cipherkey = pack("H32", $key); + $cipher = new IDEA($cipherkey); + print $client "ok:local\n"; + &logthis('"); + $keymode = "local" + } else { + Debug("Failed to get local key"); + $clientok = 0; + shutdown($client, 3); + close $client; + } + } elsif ($inittype eq "ssl") { + my $key = SSLConnection($client); + if ($key) { + $clientok = 1; + my $cipherkey = pack("H32", $key); + $cipher = new IDEA($cipherkey); + &logthis('' + ."Successfull ssl authentication with $clientname "); + $keymode = "ssl"; + + } else { + $clientok = 0; + close $client; + } + } else { - &logthis( - "WARNING: $clientip did not reply challenge"); - &status('No challenge reply '.$clientip); + my $ok = InsecureConnection($client); + if($ok) { + $clientok = 1; + &logthis('' + ."Successful insecure authentication with $clientname "); + print $client "ok\n"; + $keymode = "insecure"; + } else { + &logthis('' + ."Attempted insecure connection disallowed "); + close $client; + $clientok = 0; + + } } } else { &logthis( - "WARNING: " + "WARNING: " ."$clientip failed to initialize: >$remotereq< "); &status('No init '.$clientip); } + } else { &logthis( - "WARNING: Unknown client $clientip"); + "WARNING: Unknown client $clientip"); &status('Hung up on '.$clientip); } + if ($clientok) { # ---------------- New known client connecting, could mean machine online again @@ -1365,1496 +3786,31 @@ sub make_new_child { } &reconlonc("$perlvar{'lonSockDir'}/$id"); } - &logthis("Established connection: $clientname"); + &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); - - } -# ---------------------------------------------------------------------- update - } elsif ($userinput =~ /^update/) { # client clear or encoded. - if(isClient) { - my ($cmd,$fname)=split(/:/,$userinput); - my $ownership=ishome($fname); - if ($ownership eq 'not_owner') { - if (-e $fname) { - my ($dev,$ino,$mode,$nlink, - $uid,$gid,$rdev,$size, - $atime,$mtime,$ctime, - $blksize,$blocks)=stat($fname); - my $now=time; - my $since=$now-$atime; - if ($since>$perlvar{'lonExpire'}) { - my $reply= - &reply("unsub:$fname","$clientname"); - unlink("$fname"); - } else { - my $transname="$fname.in.transfer"; - my $remoteurl= - &reply("sub:$fname","$clientname"); - my $response; - { - my $ua=new LWP::UserAgent; - my $request=new HTTP::Request('GET',"$remoteurl"); - $response=$ua->request($request,$transname); - } - if ($response->is_error()) { - unlink($transname); - my $message=$response->status_line; - &logthis( - "LWP GET: $message for $fname ($remoteurl)"); - } else { - if ($remoteurl!~/\.meta$/) { - my $ua=new LWP::UserAgent; - my $mrequest= - new HTTP::Request('GET',$remoteurl.'.meta'); - my $mresponse= - $ua->request($mrequest,$fname.'.meta'); - if ($mresponse->is_error()) { - unlink($fname.'.meta'); - } - } - rename($transname,$fname); - } - } - print $client "ok\n"; - } else { - print $client "not_found\n"; - } - } else { - print $client "rejected\n"; - } - } else { - Reply($client, "refused\n", $userinput); - - } -# -------------------------------------- fetch a user file from a remote server - } elsif ($userinput =~ /^fetchuserfile/) { # Client clear or enc. - if(isClient) { - my ($cmd,$fname)=split(/:/,$userinput); - my ($udom,$uname,$ufile) = ($fname =~ /^([^\/]+)\/([^\/]+)\/(.+)$/); - my $udir=propath($udom,$uname).'/userfiles'; - unless (-e $udir) { mkdir($udir,0770); } - if (-e $udir) { - $ufile=~s/^[\.\~]+//; - my $path = $udir; - if ($ufile =~/(.+)\/([^\/]+)$/) { - my @parts=split(/\//,$1); - foreach my $part (@parts) { - $path .= '/'.$part; - if ((-e $path)!=1) { - mkdir($path,0770); - } - } - } - my $destname=$udir.'/'.$ufile; - my $transname=$udir.'/'.$ufile.'.in.transit'; - my $remoteurl='http://'.$clientip.'/userfiles/'.$fname; - my $response; - { - my $ua=new LWP::UserAgent; - my $request=new HTTP::Request('GET',"$remoteurl"); - $response=$ua->request($request,$transname); - } - if ($response->is_error()) { - unlink($transname); - my $message=$response->status_line; - &logthis("LWP GET: $message for $fname ($remoteurl)"); - print $client "failed\n"; - } else { - if (!rename($transname,$destname)) { - &logthis("Unable to move $transname to $destname"); - unlink($transname); - print $client "failed\n"; - } else { - print $client "ok\n"; - } - } - } else { - print $client "not_home\n"; - } - } else { - Reply($client, "refused\n", $userinput); - } -# ------------------------------------------ authenticate access to a user file - } elsif ($userinput =~ /^tokenauthuserfile/) { # Client only - if(isClient) { - my ($cmd,$fname,$session)=split(/:/,$userinput); - chomp($session); - my $reply='non_auth'; - if (open(ENVIN,$perlvar{'lonIDsDir'}.'/'. - $session.'.id')) { - while (my $line=) { - if ($line=~ m|userfile\.$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 inc\n"; - } - } else { - print $client "error: ".($!) - ." tie(GDBM) Failed ". - "while attempting inc\n"; - } - } else { - print $client "refused\n"; - } - } else { - Reply($client, "refused\n", $userinput); - - } -# -------------------------------------------------------------------- rolesput - } elsif ($userinput =~ /^rolesput/) { - if(isClient) { - &Debug("rolesput"); - if ($wasenc==1) { - my ($cmd,$exedom,$exeuser,$udom,$uname,$what) - =split(/:/,$userinput); - &Debug("cmd = ".$cmd." exedom= ".$exedom. - "user = ".$exeuser." udom=".$udom. - "what = ".$what); - my $namespace='roles'; - chomp($what); - my $proname=propath($udom,$uname); - my $now=time; - { - my $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 + my $keep_going = 1; + my $user_input; + while(($user_input = get_request) && $keep_going) { + alarm(120); + Debug("Main: Got $user_input\n"); + $keep_going = &process_request($user_input); alarm(0); - &status('Listening to '.$clientname); + &status('Listening to '.$clientname." ($keymode)"); } + # --------------------------------------------- client unknown or fishy, refuse - } else { + } else { print $client "refused\n"; $client->close(); - &logthis("WARNING: " + &logthis("WARNING: " ."Rejected client $clientip, closing connection"); } - } + } # ============================================================================= - &logthis("CRITICAL: " + &logthis("CRITICAL: " ."Disconnect from $clientip ($clientname)"); @@ -2879,10 +3835,8 @@ sub make_new_child { # sub ManagePermissions { - my $request = shift; - my $domain = shift; - my $user = shift; - my $authtype= shift; + + my ($request, $domain, $user, $authtype) = @_; # See if the request is of the form /$domain/_au if($request =~ /^(\/$domain\/_au)$/) { # It's an author rolesput... @@ -2899,8 +3853,8 @@ sub ManagePermissions # sub GetAuthType { - my $domain = shift; - my $user = shift; + + my ($domain, $user) = @_; Debug("GetAuthType( $domain, $user ) \n"); my $proname = &propath($domain, $user); @@ -3009,17 +3963,36 @@ 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")) { - $result="ok\n"; - } else { - $result="not_subscribed\n"; - } + $unsubs++; # Successful unsub via marker file. + } + + # The more modern way to do it is to have a subscription list + # file: + if (-e "$fname.subscription") { my $found=&addline($fname,$clientname,$clientip,''); - if ($found) { $result="ok\n"; } + if ($found) { + $unsubs++; + } + } + + # If either or both of these mechanisms succeeded in unsubscribing a + # resource we can return ok: + + if($unsubs) { + $result = "ok\n"; } else { - if ($result != "ok\n") { $result="not_subscribed\n"; } + $result = "not_subscribed\n"; } + return $result; } @@ -3141,6 +4114,16 @@ sub make_passwd_file { } } 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 to create privilged account blocked"); + return "no_priv_account_error\n"; + } + my $execpath="$perlvar{'lonDaemons'}/"."lcuseradd"; { &Debug("Executing external: ".$execpath); @@ -3173,7 +4156,7 @@ sub sethost { my (undef,$hostid)=split(/:/,$remotereq); if (!defined($hostid)) { $hostid=$perlvar{'lonHostID'}; } if ($hostip{$perlvar{'lonHostID'}} eq $hostip{$hostid}) { - $currenthostid=$hostid; + $currenthostid =$hostid; $currentdomainid=$hostdom{$hostid}; &logthis("Setting hostid to $hostid, and domain to $currentdomainid"); } else { @@ -3213,6 +4196,74 @@ sub userload { return $userloadpercent; } +# Routines for serializing arrays and hashes (copies from lonnet) + +sub array2str { + my (@array) = @_; + my $result=&arrayref2str(\@array); + $result=~s/^__ARRAY_REF__//; + $result=~s/__END_ARRAY_REF__$//; + return $result; +} + +sub arrayref2str { + my ($arrayref) = @_; + my $result='__ARRAY_REF__'; + foreach my $elem (@$arrayref) { + if(ref($elem) eq 'ARRAY') { + $result.=&arrayref2str($elem).'&'; + } elsif(ref($elem) eq 'HASH') { + $result.=&hashref2str($elem).'&'; + } elsif(ref($elem)) { + #print("Got a ref of ".(ref($elem))." skipping."); + } else { + $result.=&escape($elem).'&'; + } + } + $result=~s/\&$//; + $result .= '__END_ARRAY_REF__'; + return $result; +} + +sub hash2str { + my (%hash) = @_; + my $result=&hashref2str(\%hash); + $result=~s/^__HASH_REF__//; + $result=~s/__END_HASH_REF__$//; + return $result; +} + +sub hashref2str { + my ($hashref)=@_; + my $result='__HASH_REF__'; + foreach (sort(keys(%$hashref))) { + if (ref($_) eq 'ARRAY') { + $result.=&arrayref2str($_).'='; + } elsif (ref($_) eq 'HASH') { + $result.=&hashref2str($_).'='; + } elsif (ref($_)) { + $result.='='; + #print("Got a ref of ".(ref($_))." skipping."); + } else { + if ($_) {$result.=&escape($_).'=';} else { last; } + } + + if(ref($hashref->{$_}) eq 'ARRAY') { + $result.=&arrayref2str($hashref->{$_}).'&'; + } elsif(ref($hashref->{$_}) eq 'HASH') { + $result.=&hashref2str($hashref->{$_}).'&'; + } elsif(ref($hashref->{$_})) { + $result.='&'; + #print("Got a ref of ".(ref($hashref->{$_}))." skipping."); + } else { + $result.=&escape($hashref->{$_}).'&'; + } + } + $result=~s/\&$//; + $result .= '__END_HASH_REF__'; + return $result; +} + # ----------------------------------- POD (plain old documentation, CPAN style) =head1 NAME