package BecomeUser; require Exporter; @ISA = qw(Exporter); @EXPORT_OK = qw(become_user restore_id); use Reporting qw(initial_report failed_report debug_report); use strict; # This module changes the process identity BEGIN { # Do we reset our UID to that of users? if( $> == 0 ) { # Do setting if our effective UID is root # Remember the incoming effective ids, we restore # to these after every action. Both real and effective # are set to the original effective so that we look # look like a root process even from setuid scripts. # CODE < = Real UID, > = Eff UID, ( = Real GID, ) = Eff GID $BecomeUser::resting_uid = $>; $BecomeUser::resting_gid = $); # Test for support for setre*id, if unavailable # fall back on set*id eval { ($(,$)) = ($BecomeUser::resting_gid,-1); ($<,$>) = ($BecomeUser::resting_uid,-1); }; if($@) { # setre*id does not seem to be available, try set*id $( = $BecomeUser::resting_gid; $< = $BecomeUser::resting_uid; initial_report("Using set*id."); $BecomeUser::got_setre = 0; } else { initial_report("Using setre*id."); $BecomeUser::got_setre = 1; } initial_report("Running privileged [$< : $(], will execute commands as the Xlogin"); $BecomeUser::setting_uids = 1; } else { initial_report("Running unprivileged, will execute all commands as [$< : $(]"); $BecomeUser::setting_uids = 0; } } # Change the process' identity (real and effective) to a user's (if process was started # with sufficient privileges to allow this, does nothing otherwise) # # arg 1 = Name of the user # arg 2 = Name of the project (group) # # Returns 1 if successful, 0 otherwise # # Side effects: modifies the ENV array, setting values for USER, LOGNAME and HOME # sub become_user { if($BecomeUser::setting_uids) { my $new_name = shift; my $new_group = shift; my $new_uid = $BecomeUser::uids{$new_name}; if(!$new_uid) { my ($l_name, $l_passwd, $l_uid, $l_gid, $l_quota, $l_comment, $l_gcos, $l_dir, $l_shell); # Not seen this user name yet, ask the system for a mapping # and put it into our cached copy ($l_name, $l_passwd, $l_uid, $l_gid, $l_quota, $l_comment, $l_gcos, $l_dir, $l_shell) = getpwnam($new_name); if(!$l_uid) { failed_report("Attempt to run a task for an unknown user $new_name"); return 0; } debug_report("New user information obtained for $new_name ($l_name $l_uid $l_gid) "); # Cache this user $BecomeUser::uids{$new_name} = $l_uid; $BecomeUser::gids{$new_name} = $l_gid; $BecomeUser::homes{$new_name} = $l_dir; $new_uid = $l_uid; } # Do not allow changes to root if($new_uid == 0) { failed_report("Attempt to run a command as root $new_name"); return 0; } # Do project(group) mapping my $new_gid; if($new_group eq "NONE") { # None selected by user, set default from password file $new_gid = $BecomeUser::gids{$new_name}; } else { # User has selected one, check OK and set $new_gid = $BecomeUser::groups{$new_group}; # map name to gid if(!$new_gid) { # Need a new one? my($g_name, $g_passwd, $g_gid, $g_members) = getgrnam($new_group); if(!$g_gid) { failed_report("Unknown group name requested $new_group for TSI user mapping."); return 0; } debug_report("New group information obtained for $new_group ($g_name $g_gid)"); $BecomeUser::groups{$new_group} = $g_gid; $BecomeUser::members{$new_group} = $g_members; $new_gid = $g_gid; } if($new_gid != $BecomeUser::gids{$new_name}) { # is requested the primary group? # Check that this user is a member of the requested group my $mem_list = $BecomeUser::members{$new_group}; if($mem_list =~ m/$new_name/) { } else { failed_report("The user $new_name is not a member of the group $new_group."); return 0; } } } # Change identity # # Do this in a pair to try to force a call to setreuid # rather than calls to setruid and seteuid - not supported by AIX # The -1s force a single value to be changed, preserving the saved UID # CODE < = Real UID, > = Eff UID, ( = Real GID, ) = Eff GID # Go carefully, do groups while effective is root, then real # and last effective (permission to set uids is based on effective) `cat /opt/beingrid/.secret/.$new_name/.secret | /usr/bin/klog $new_name`; if($BecomeUser::got_setre) { ($(,$)) = ($new_gid,-1); ($(,$)) = (-1,"$new_gid $new_gid"); ($<,$>) = ($new_uid,-1); ($<,$>) = (-1,$new_uid); } else { $( = $new_gid; $) = "$new_gid $new_gid"; $< = $new_uid; $> = $new_uid; } if($< != $new_uid) { failed_report("Could not set TSI identity (real) to $new_name $new_uid."); return 0; } if($> != $new_uid) { failed_report("Could not set TSI identity (effective) to $new_name $new_uid."); return 0; } if($( != $new_gid) { failed_report("Could not set TSI identity (group real) to $new_group $new_gid."); return 0; } if($) != $new_gid) { failed_report("Could not set TSI identity (group effective) to $new_group $new_gid."); return 0; } debug_report("Executing command as $new_name [$< : $(] [$>: $)]"); # Set some sort of user environment $ENV{USER} = $new_name; $ENV{LOGNAME} = $new_name; $ENV{HOME} = $BecomeUser::homes{$new_name}; } return 1; } # Change the process identity back to the initial (probably root). # # No return # # Side effects: modifies the ENV array, setting dummy values for USER, LOGNAME and HOME sub restore_id { if($BecomeUser::setting_uids) { # Go carefully back to resting effective first - # this goes because of saved? - then real - so that # ps gives resting, then groups OK coz effective is root if($BecomeUser::got_setre) { ($<,$>) = (-1,$BecomeUser::resting_uid); ($<,$>) = ($BecomeUser::resting_uid,-1); ($(,$)) = (-1,$BecomeUser::resting_gid); ($(,$)) = ($BecomeUser::resting_gid,-1); } else { $> = $BecomeUser::resting_uid; $< = $BecomeUser::resting_uid; $) = $BecomeUser::resting_gid; $( = $BecomeUser::resting_gid; } `/usr/local/bin/unlog`; $ENV{USER} = "nobody"; $ENV{LOGNAME} = "nobody"; $ENV{HOME} = "/tmp"; debug_report("Restored IDs to [$< : $(] [$> : $)]"); } } # # Copyright (c) Fujitsu Ltd 2000 - 2004 # # Use and distribution is subject a License. # A copy was supplied with the distribution (see documentation or the jar file). # # This product includes software developed by Fujitsu Limited (http://www.fujitsu.com).