--- loncom/Attic/lchtmldir 2004/10/19 11:11:34 1.10 +++ loncom/Attic/lchtmldir 2008/05/30 13:37:48 1.21 @@ -2,6 +2,8 @@ # The Learning Online Network with CAPA # +# $Id: lchtmldir,v 1.21 2008/05/30 13:37:48 raeburn Exp $ +# # Copyright Michigan State University Board of Trustees # # This file is part of the LearningOnline Network with CAPA (LON-CAPA). @@ -41,7 +43,7 @@ # NSCL # Michigan State University8 # East Lansing, MI 48824-1321 - +# # General flow of control: # 1. Validate process state (must be run as www). # 2. Validate parameters: Need two parameters: @@ -61,7 +63,7 @@ # - internal - www:www/2775 # - local - www:www/2775 # - +# # # Take a few precautions to be sure that we're not vulnerable to trojan # horses and other fine issues: @@ -69,12 +71,14 @@ use strict; use Fcntl qw(:mode); use DirHandle; - +use POSIX; +use lib '/home/httpd/lib/perl/'; +use LONCAPA qw(:match); $ENV{'PATH'} = '/bin:/usr/bin:/usr/local/sbin:/home/httpd/perl'; delete @ENV{qw{IFS CDPATH ENV BASH_ENV}}; -my $DEBUG = 0; # .nonzero -> Debug printing enabled. +my $DEBUG = 1; # .nonzero -> Debug printing enabled. my $path_sep = "/"; # Unix like operating systems. @@ -125,14 +129,14 @@ if( $authentication ne "unix:" && $authentication ne "localauth:") { if($DEBUG) { print("Invalid authentication parameter: ".$authentication."\n"); - print("Should be one of: unix, internal, krb4, localauth\n"); + print("Should be one of-- unix: internal: krb4: krb5: localauth:\n"); } exit 3; } # Untaint the username. -my $match = $username =~ /^(\w+)$/; +my $match = $username =~ /^($match_username)$/; my $patt = $1; if($DEBUG) { @@ -144,7 +148,7 @@ my $safeuser = $patt; if($DEBUG) { print("Save username = $safeuser \n"); } -if(($username ne $safeuser) or ($safeuser!~/^[A-za-z]/)) { +if($username ne $safeuser) { if($DEBUG) { print("User name $username had illegal characters\n"); } @@ -154,39 +158,57 @@ if(($username ne $safeuser) or ($safeuse #untaint the base directory require that the dir contain only # alphas, / numbers or underscores, and end in /$safeuser -$dir =~ /(^([\w\/]+))/; -my $dirtry1 = $1; -$dir =~ /$\/$safeuser/; -my $dirtry2 = $1; +my ($allowed_dir) = ($dir =~ m{(^([/]|$match_username)+)}); -if(($dirtry1 ne $dir) or ($dirtry2 ne $dir)) { +my $has_correct_end = ($dir =~ m{/\Q$safeuser\E$}); + +if(($allowed_dir ne $dir) or (!$has_correct_end)) { if ($DEBUG) { print("Directory $dir is not a valid home for $safeuser\n"); } exit 5; } - # As root, create the directory. -my $homedir = $dirtry1; +my $homedir = $allowed_dir; my $fulldir = $homedir."/public_html"; if($DEBUG) { print("Full directory path is: $fulldir \n"); } -if(!( -e $dirtry1)) { +if(!( -e $homedir)) { if($DEBUG) { - print("User's home directory $dirtry1 does not exist\n"); + print("User's home directory $homedir does not exist\n"); } if ($authentication eq "unix:") { exit 6; } } +if ($authentication eq "unix:") { + # check whether group $safeuser exists. + my $usergroups = `id -nG $safeuser`; + if (! grep /^$safeuser$/, split(/\s+/,$usergroups)) { + if($DEBUG) { + print("Group \"$safeuser\" does not exist or $safeuser is not a member of that group.\n"); + } + exit 7; + } +} + + + &EnableRoot; +# If authentication is internal and the top level directory exists +# give it the right permissions (in case this is a modification. + +if ($authentication eq "internal:") { + chmod(0711, $homedir); # so www can enter ~/public_html. +} + &System("/bin/mkdir -p $fulldir") unless (-e $fulldir); unless(-e $fulldir."/index.html") { open OUT,">".$fulldir."/index.html"; @@ -214,14 +236,15 @@ END close OUT; } -&System("/bin/chmod 02775 $fulldir"); -&System("/bin/chmod 0775 $fulldir"."/index.html"); +&System("/bin/chmod 02770 $fulldir"); +&System("/bin/chmod 0770 $fulldir"."/index.html"); # Based on the authentiation mode, set the ownership of the directory. if($authentication eq "unix:") { # Unix mode authentication... - &System("/bin/chown -R $safeuser".":".$safeuser." ".$fulldir); + print "Unix auth\n"; + &System("/bin/chown -R $safeuser:$safeuser"." ".$fulldir); &JoinGroup($safeuser); } else { # Internal, Kerberos, and Local authentication are for users @@ -232,9 +255,33 @@ if($authentication eq "unix:") { # Unix # is that a file system user is being demoted to internal user... if($authentication eq "internal:") { - &System("/bin/chown -R root:root ".$homedir); + # In case the user was a unix/filesystem authenticated user, + # we'll take a bit of time here to write a script in the + # user's home directory that can reset ownerships and permissions + # back the way the used to be. + + # This can take long enough for lond to time out, so we'll do it + # in a separate process that we'll not wait for. + # + my $fpid = fork; + if($fpid) { + &DisableRoot; + exit 0; + } else { + print "Forked\n"; + POSIX::setsid(); # Disassociate from parent. + print "Separate session\n"; + &write_restore_script($homedir); + print "Restore script written\n"; + &System("/bin/chown -R root:root ".$homedir); + &System("/bin/chown -R www:www ".$fulldir); + print "Exiting\n"; + exit 0; + } + } else { + &System("/bin/chown -R www:www ".$fulldir); } - &System("/bin/chown -R www:www ".$fulldir); + } &DisableRoot; @@ -276,7 +323,10 @@ sub DisableRoot { print("Disable root: id = ".$>."\n"); } } - +# +# Join the www user to the user's group. +# we must be running with euid as root at this time. +# sub JoinGroup { my $usergroup = shift; @@ -295,7 +345,16 @@ sub JoinGroup { } exit 6; } - + if (-e '/var/run/httpd.pid') { + open(PID,'/var/run/httpd.pid'); + my $pid=; + close(PID); + my ($safepid) = $pid=~ /(\d+)/; + $pid = $safepid; + if ($pid) { + my $status = system("kill -USR1 $safepid"); + } + } } @@ -371,7 +430,13 @@ sub process_tree { } # -# Simple test of process_tree: +# Callback from process_tree to write the script lines +# requried to restore files to current ownership and permission. +# Parameters: +# dir - Name of the directory the file lives in. +# name - Name of the file itself. +# statinfo - Array from lstat called on the file. +# # sub write_script { my ($dir, $name, $statinfo) = @_; @@ -392,6 +457,46 @@ sub write_script { } +# +# Write a script in the user's home directory that can restore +# the permissions and ownerhips of all the files in the directory +# tree to their current ownerships and permissions. This is done +# prior to making the user into an internally authenticated user +# in case they were previously file system authenticated and +# need to go back. +# The file we will create will be of the form +# restore_n.sh Where n is a number that we will keep +# incrementing as needed until there isn't a file by that name. +# +# Parameters: +# dir - Path to the user's home directory. +# +sub write_restore_script { + my ($dir) = @_; + + # Create a unique file: + + my $version_number = 0; + my $filename = 'restore_'.$version_number.'.sh'; + my $full_name = $dir.$path_sep.$filename; + + while(-e $full_name) { + $version_number++; + $filename = 'restore_'.$version_number.'.sh'; + $full_name = $dir.$path_sep.$filename; + } + # $full_name is the full path of a file that does not yet exist + # of the form we want: + + open(CHMODSCRIPT, "> $full_name"); + + &process_tree(\&write_script, $dir); + + close(CHMODSCRIPT); + + chmod(0750, $full_name); + +}