--- loncom/Attic/lchtmldir 2002/05/03 03:21:25 1.2 +++ loncom/Attic/lchtmldir 2004/12/06 12:07:59 1.12 @@ -67,11 +67,15 @@ # horses and other fine issues: # use strict; +use Fcntl qw(:mode); +use DirHandle; +use POSIX; $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. # If the UID of the running process is not www exit with error. @@ -117,7 +121,7 @@ if($DEBUG) { if( $authentication ne "unix:" && $authentication ne "internal:" && - $authentication ne "krb4:" && + $authentication !~ /^krb(4|5):(.*)/ && $authentication ne "localauth:") { if($DEBUG) { print("Invalid authentication parameter: ".$authentication."\n"); @@ -167,7 +171,9 @@ if(($dirtry1 ne $dir) or ($dirtry2 ne $d # As root, create the directory. -my $fulldir = $dirtry1."/public_html"; +my $homedir = $dirtry1; +my $fulldir = $homedir."/public_html"; + if($DEBUG) { print("Full directory path is: $fulldir \n"); } @@ -175,11 +181,13 @@ if(!( -e $dirtry1)) { if($DEBUG) { print("User's home directory $dirtry1 does not exist\n"); } - exit 6; + if ($authentication eq "unix:") { + exit 6; + } } &EnableRoot; -&System("/bin/mkdir $fulldir") unless (-e $fulldir); +&System("/bin/mkdir -p $fulldir") unless (-e $fulldir); unless(-e $fulldir."/index.html") { open OUT,">".$fulldir."/index.html"; print OUT< $safeuser - -

$safeuser

+ +

$safeuser Construction Space

+

+ The LearningOnline Network with Computer-Assisted Personalized Approach +

- Learning Online Network +This is your construction space within LON-CAPA, where you would construct resources which are meant to be +used across courses and institutions.

- This area provides for: +Material within this area can only be seen and edited by $safeuser and designated co-authors. To make +it available to students and other instructors, the material needs to be published.

- END close OUT; } + &System("/bin/chmod 02775 $fulldir"); &System("/bin/chmod 0775 $fulldir"."/index.html"); @@ -212,30 +221,43 @@ END # Based on the authentiation mode, set the ownership of the directory. if($authentication eq "unix:") { # Unix mode authentication... - - - &System("/bin/chown -R $username".":".$username." ".$fulldir); - &JoinGroup($username); - - -} -elsif ($authentication eq "internal:") { # Internal authentication. - - &System("/bin/chown -R www:www $fulldir"); -} -elsif ($authentication eq "krb4:") { # Kerberos version 4 authentication - &System("/bin/chown -R $username".':'.$username." ".$fulldir); - &JoinGroup($username); -} -elsif ($authentication eq "localauth:") { # Local authentiation - &System("/bin/chown -R $username".':'.$username." $fulldir"); -} -else { - if($DEBUG) { - print("Authentication not legal".$authentication); + &System("/bin/chown -R $safeuser".":".$safeuser." ".$fulldir); + &JoinGroup($safeuser); +} else { + # Internal, Kerberos, and Local authentication are for users + # who do not have unix accounts on the system. Therefore we + # will give ownership of their public_html directories to www:www + # If the user is an internal auth user, the rest of the directory tree + # gets owned by root. This chown is needed in case what's really happening + # is that a file system user is being demoted to internal user... + + if($authentication eq "internal:") { + # 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); } - &DisableRoot; - exit 5; } &DisableRoot; @@ -261,7 +283,7 @@ sub EnableRoot { # root capability is already enabled } if($DEBUG) { - print("Enable Root - id = $> \n"); + print("Enable Root - id = $> $<\n"); } return $>; } @@ -283,6 +305,9 @@ sub JoinGroup { my $usergroup = shift; my $groups = `/usr/bin/groups www`; + # untaint + my ($safegroups)=($groups=~/:\s+([\s\w]+)/); + $groups=$safegroups; chomp $groups; $groups=~s/^\S+\s+\:\s+//; my @grouplist=split(/\s+/,$groups); my @ugrouplist=grep {!/www|$usergroup/} @grouplist; @@ -300,11 +325,142 @@ sub JoinGroup { sub System { - my $command = shift; + my ($command,@args) = @_; if($DEBUG) { - print("system: $command \n"); + print("system: $command with args ".join(' ',@args)."\n"); } - system($command); + system($command,@args); +} + + + + + +# +# This file contains code to recursively process +# a Directory. This is a bit more powerful +# than File::Find in that we pass the full +# stat info to the processing function. +# For each file in the specified directory subtree, +# The user's Code reference is invoked for all files, regular and otherwise +# except: +# ., .. +# +# Parameters: +# code_ref - Code reference, invoked for each file in the tree. +# as follows: CodeRef(directory, name, statinfo) +# directory the path to the directory holding the file. +# name the name of the file within Directory. +# statinfo a reference to the stat of the file. +# start_dir - The starting point of the directory walk. +# +# NOTE: +# Yes, we could have just used File::Find, but since we have to get the +# stat anyway, this is actually simpler, as File::Find would have gotten +# the stat to figure out the file type and then we would have gotten it +# again. +# + +sub process_tree { + my ($code_ref, $start_dir) = @_; + + my $dir = new DirHandle $start_dir; + if (!defined($dir)) { + print "Failed to open dirhandle: $start_dir\n"; + } + + # Now iterate through this level of the tree: + + while (defined (my $name = $dir->read)) { + next if $name =~/^\.\.?$/; # Skip ., .. (see cookbook pg 319) + + my $full_name = $start_dir.$path_sep.$name; # Full filename path. + my @stat_info = lstat($full_name); + my $mode = $stat_info[2]; + my $type = $mode & 0170000; # File type. + + # Unless the file type is a symlink, call the user code: + + unless ($type == S_IFLNK) { + &$code_ref($start_dir, $name, \@stat_info); + } + + # If the entry is a directory, we need to recurse: + + + if (($type == S_IFDIR) != 0) { + &process_tree($code_ref, $full_name); + } + } + +} +# +# 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) = @_; + + my $fullname = $dir.$path_sep.$name; + + # We're going to '' the name, but we need to deal with embedded + # ' characters. Using " is much worse as we'd then have to + # escape all the shell escapes too. This way all we need + # to do is replace ' with '\'' + + $fullname =~ s/\'/\'\\\'\'/g; + + my $perms = $statinfo->[2] & 0777; # Just permissions. + printf CHMODSCRIPT "chmod 0%o '%s'\n", $perms, $fullname; + printf CHMODSCRIPT "chown %d:%d '%s'\n", $statinfo->[4], $statinfo->[5], + $fullname + + +} +# +# 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); + }