Viewing file: batch_exec.cgi (17.21 KB) -rwxr-xr-x Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
#!/usr/bin/perl # batch_exec.cgi # Execute create/modify/delete commands in a batch file
require './user-lib.pl'; $access{'batch'} || &error($text{'batch_ecannot'}); if ($ENV{'REQUEST_METHOD'} eq 'GET') { &ReadParse(); } else { &ReadParseMime(); } if ($in{'source'} == 0) { $data = $in{'file'}; $data =~ /\S/ || &error($text{'batch_efile'}); } elsif ($in{'source'} == 1) { open(LOCAL, $in{'local'}) || &error($text{'batch_elocal'}); while(<LOCAL>) { $data .= $_; } close(LOCAL); } elsif ($in{'source'} == 2) { $data = $in{'text'}; $data =~ /\S/ || &error($text{'batch_etext'}); }
&ui_print_unbuffered_header(undef, $text{'batch_title'}, "");
# Force defaults for save options $in{'makehome'} = 1 if (!$access{'makehome'}); $in{'copy'} = 1 if (!$access{'copy'} && $config{'user_files'} =~ /\S/); $in{'movehome'} = 1 if (!$access{'movehome'}); $in{'chuid'} = 1 if (!$access{'chuid'}); $in{'chgid'} = 1 if (!$access{'chgid'});
# Work out a good base UID for new users &build_user_used(\%used, undef, \%taken); $newuid = int($config{'base_uid'} > $access{'lowuid'} ? $config{'base_uid'} : $access{'lowuid'});
# Work out a good base GID for new groups &build_group_used(\%gused, \%gtaken); if ($config{'new_user_gid'}) { %used = ( %used, %gused ); } $newgid = int($config{'base_gid'} > $access{'lowgid'} ? $config{'base_gid'} : $access{'lowgid'}); @glist = &list_groups();
# Process the file &batch_start() if ($in{'batch'}); &lock_user_files(); $lnum = $created = $modified = $deleted = 0; print "<pre>\n"; $pft = &passfiles_type(); foreach $line (split(/[\r\n]+/, $data)) { $lnum++; $line =~ s/^\s*#.*$//; next if ($line !~ /\S/); local @line = split(/:/, $line, -1); local %user; if ($line[0] eq 'create') { # Creating a new user if ($pft == 5) { # Openserver passwd and short shadow information if (@line != 10) { print &text('batch_elen', $lnum, 10),"\n"; next; } $user{'min'} = $line[8]; $user{'max'} = $line[9]; } elsif ($pft == 4) { # AIX passwd and security information if (@line != 12) { print &text('batch_elen', $lnum, 12),"\n"; next; } $user{'min'} = $line[8]; $user{'max'} = $line[9]; $user{'expire'} = $line[10]; map { $user{$_}++ } split(/\s+/, $line[11]); } elsif ($pft == 2) { # SYSV-style passwd and shadow information if (@line != 13) { print &text('batch_elen', $lnum, 13),"\n"; next; } $user{'min'} = $line[8]; $user{'max'} = $line[9]; $user{'warn'} = $line[10]; $user{'inactive'} = $line[11]; $user{'expire'} = $line[12]; $user{'change'} = $line[2] eq '' ? '' : int(time() / (60*60*24)); } elsif ($pft == 1 || $pft == 6) { # BSD master.passwd information if (@line != 11) { print &text('batch_elen', $lnum, 11),"\n"; next; } $user{'class'} = $line[8]; $user{'change'} = $line[9]; $user{'expire'} = $line[10]; } else { # Classic passwd file information (type 0 and 3) if (@line != 8) { print &text('batch_elen', $lnum, 8),"\n"; next; } }
# Make sure all min/max fields are numeric $err = &validate_batch_minmax(\%user, $lnum); if ($err) { print $err,"\n"; next; }
# Parse common fields if (!$line[1]) { print &text('batch_eline', $lnum),"\n"; next; } $user{'user'} = $line[1]; $err = &check_username_restrictions($user{'user'}); if ($err) { print &text('batch_echeck', $lnum, $err),"\n"; next; } if ($taken{$user{'user'}}) { print &text('batch_euser', $lnum, $user{'user'}),"\n"; next; } if ($line[3] !~ /^\d+$/) { # make up a UID while($used{$newuid}) { $newuid++; } $user{'uid'} = $newuid; } else { # use the given UID if ($used{$line[3]} && !$access{'umultiple'}) { print &text('batch_ecaccess', $lnum, $text{'usave_euidused2'}),"\n"; next; } $user{'uid'} = $line[3]; } $used{$user{'uid'}}++; if ($line[7] !~ /^\//) { print &text('batch_eshell', $lnum, $line[7]),"\n"; next; } $user{'shell'} = $line[7]; $user{'real'} = $line[5]; local @gids = split(/[ ,]+/, $line[4]); $user{'gid'} = $gids[0]; local $grp = &my_getgrgid($gids[0]);
$real_home = undef; if ($access{'autohome'}) { # Assign home dir automatically based on ACL $user{'home'} = &auto_home_dir($access{'home'}, $user{'user'}, $grp); if ($config{'real_base'}) { $real_home = &auto_home_dir( $config{'real_base'}, $user{'user'}, $grp); } } else { if ($line[6] eq '' && $config{'home_base'}) { # Choose home dir automatically based on # module config $user{'home'} = &auto_home_dir( $config{'home_base'}, $user{'user'}, $user{'gid'}); if ($config{'real_base'}) { $real_home = &auto_home_dir( $config{'real_base'}, $user{'user'}, $grp); } } elsif ($line[6] !~ /^\//) { print &text('batch_ehome', $lnum,$line[6]),"\n"; next; } else { # Use given home dir $user{'home'} = $line[6]; } } $real_home ||= $user{'home'};
# Check access control restrictions if (!$access{'ucreate'}) { print &text('batch_ecaccess', $lnum, $text{'usave_ecreate'}); next; } local $ch = &check_user(\%user); if ($ch) { print &text('batch_ecaccess', $lnum, $ch),"\n"; next; }
# Work out secondary group membership local @secs; if (@gids > 1) { local $i; for($i=1; $i<@gids; $i++) { local ($group) = grep { $_->{'gid'} eq $gids[$i] } @glist; push(@secs, $group) if ($group); } }
# Work out the password if ($in{'crypt'}) { $user{'pass'} = $line[2]; $user{'passmode'} = 2; } elsif ($line[2] eq 'x') { # No login allowed $user{'pass'} = $config{'lock_string'}; $user{'passmode'} = 1; } elsif ($line[2] eq '') { # No password needed $user{'pass'} = ''; $user{'passmode'} = 0; } else { # Normal password $user{'pass'} = &encrypt_password($line[2]); $user{'passmode'} = 3; $user{'plainpass'} = $line[2]; }
# Run the before command &set_user_envs(\%user, 'CREATE_USER', $user{'plainpass'}, [ map { $_->{'gid'} } @secs ]); $merr = &making_changes(); &error(&text('usave_emaking', "<tt>$merr</tt>")) if (defined($merr));
if ($user{'gid'} !~ /^\d+$/) { # Need to create a new group for the user if (!$access{'gcreate'}) { print &text('batch_ecaccess', $lnum, $text{'usave_egcreate'}),"\n"; next; } if ($gtaken{$user{'user'}}) { print &text('batch_egtaken', $lnum, $user{'user'}),"\n"; next; }
if ($config{'new_user_gid'}) { $newgid = $user{'uid'}; } else { while($gused{$newgid}) { $newgid++; } } local %group; $group{'group'} = $user{'user'}; $user{'gid'} = $group{'gid'} = $newgid; &create_group(\%group); $gused{$group{'gid'}}++; }
# Create the user! if ($in{'makehome'} && !-d $user{'home'}) { &create_home_directory(\%user, $real_home); } &create_user(\%user);
# Add user to some secondary groups local $group; foreach $group (@secs) { local @mems = split(/,/ , $group->{'members'}); push(@mems, $user{'user'}); $group->{'members'} = join(",", @mems); &modify_group($group, $group); }
# All done &made_changes();
# Call other modules, ignoring any failures $error_must_die = 1; eval { &other_modules("useradmin_create_user", \%user) if ($access{'cothers'} == 1 && $in{'others'} || $access{'cothers'} == 0); }; $other_err = $@; $error_must_die = 0;
if ($in{'copy'} && $in{'makehome'}) { # Copy files to user's home directory local $groupname = &my_getgrgid($user{'gid'}); local $uf = &get_skel_directory(\%user, $groupname); ©_skel_files($uf, $user{'home'}, $user{'uid'}, $user{'gid'}); }
print "<b>",&text('batch_created',$user{'user'}),"</b>\n"; print "<b><i>",&text('batch_eother', $other_err),"</i></b>\n" if ($other_err); $created++; } elsif ($line[0] eq 'delete') { # Deleting an existing user if (@line != 2) { print &text('batch_elen', $lnum, 2),"\n"; next; } local @ulist = &list_users(); local ($user) = grep { $_->{'user'} eq $line[1] } @ulist; if (!$user) { print &text('batch_enouser', $lnum, $line[1]),"\n"; next; } if (!&can_edit_user(\%access, $user)) { print &text('batch_edaccess', $lnum, $text{'udel_euser'}),"\n"; next; } if (!$config{'delete_root'} && $user->{'uid'} <= 10) { print &text('batch_edaccess', $lnum, $text{'udel_eroot'}),"\n"; next; }
# Run the before command &set_user_envs($user, 'DELETE_USER', undef, [ &secondary_groups($user->{'user'}) ]); $merr = &making_changes(); &error(&text('usave_emaking', "<tt>$merr</tt>")) if (defined($merr));
# Delete from other modules, ignoring errors $error_must_die = 1; eval { &other_modules("useradmin_delete_user", $user) if ($access{'dothers'} == 1 && $in{'others'} || $access{'dothers'} == 0); }; $other_err = $@; $error_must_die = 0;
# Delete the user entry &delete_user($user);
# Delete the user from groups foreach $g (&list_groups()) { @mems = split(/,/, $g->{'members'}); $idx = &indexof($user->{'user'}, @mems); if ($idx >= 0) { splice(@mems, $idx, 1); %newg = %$g; $newg{'members'} = join(',', @mems); &modify_group($g, \%newg); } $mygroup = $g if ($g->{'group'} eq $user->{'user'}); }
# Delete the user's group if ($mygroup && !$mygroup->{'members'}) { local $another; foreach $ou (&list_users()) { $another++ if ($ou->{'gid'} == $mygroup->{'gid'}); } if (!$another) { &delete_group($mygroup); } } &made_changes();
# Delete his home directory if ($in{'delhome'} && $user->{'home'} && $user->{'home'} !~ /^\/+$/) { &delete_home_directory($user); }
print "<b>",&text('batch_deleted',$user->{'user'}),"</b>\n"; print "<b><i>",&text('batch_eother', $other_err),"</i></b>\n" if ($other_err); $deleted++; } elsif ($line[0] eq 'modify') { # Modifying an existing user local $wlen = $pft == 5 ? 11 : $pft == 4 ? 13 : $pft == 2 ? 14 : $pft == 1 || $pft == 6 ? 12 : 9; if (@line != $wlen) { print &text('batch_elen', $lnum, $wlen),"\n"; next; } local @ulist = &list_users(); local ($user) = grep { $_->{'user'} eq $line[1] } @ulist; if (!$user) { print &text('batch_enouser', $lnum, $line[1]),"\n"; next; } %olduser = %user = %$user; $user{'olduser'} = $user->{'user'}; if (!&can_edit_user(\%access, \%user)) { print &text('batch_emaccess', $lnum, $text{'usave_eedit'}),"\n"; next; }
# Update supplied fields if ($line[2] ne '') { if (!$access{'urename'}) { print &text('batch_erename', $lnum, $line[1]),"\n"; } $user{'user'} = $line[2]; } if ($in{'crypt'} && $line[3] ne '') { # Changing to pre-encrypted password $user{'pass'} = $line[3]; $user{'passmode'} = 2; } elsif ($line[3] eq 'x') { # No login allowed $user{'pass'} = $config{'lock_string'}; $user{'passmode'} = 1; } elsif ($line[3] ne '') { # Normal password $user{'pass'} = &encrypt_password($line[3]); $user{'passmode'} = 3; $user{'plainpass'} = $line[3]; } else { # No change $user{'passmode'} = 4; } $user{'uid'} = $line[4] if ($line[4] ne ''); $user{'gid'} = $line[5] if ($line[5] ne ''); $user{'real'} = $line[6] if ($line[6] ne ''); $user{'home'} = $line[7] if ($line[7] ne ''); $user{'shell'} = $line[8] if ($line[8] ne ''); if ($access{'peopt'}) { if ($pft == 5) { # Openserver password and short shadow $user{'min'}=$line[9] if ($line[9] ne ''); $user{'max'}=$line[10] if ($line[10] ne ''); $user{'change'}=int(time() / (60*60*24)) if ($line[3] ne ''); } elsif ($pft == 4) { # AIX password and security information $user{'min'}=$line[9] if ($line[9] ne ''); $user{'max'}=$line[10] if ($line[10] ne ''); $user{'expire'}=$line[11] if ($line[11] ne ''); if ($line[12] ne '') { delete($user{'admin'}); delete($user{'admchg'}); delete($user{'nocheck'}); map { $user{$_}++ } split(/\s+/, $line[12]); } $user{'change'}=time() if ($line[3] ne ''); } elsif ($pft == 2) { # SYSV-style passwd and shadow information $user{'min'}=$line[9] if ($line[9] ne ''); $user{'max'}=$line[10] if ($line[10] ne ''); $user{'warn'}=$line[11] if ($line[11] ne ''); $user{'inactive'}=$line[12] if ($line[12] ne ''); $user{'expire'}=$line[13] if ($line[13] ne ''); $user{'change'}=int(time() / (60*60*24)) if ($line[3] ne ''); } elsif ($pft == 1 || $pft == 6) { # BSD master.passwd information $user{'class'}=$line[9] if ($line[9] ne ''); $user{'change'}=$line[10] if ($line[10] ne ''); $user{'expire'}=$line[11] if ($line[11] ne ''); } }
# Check access control restrictions local $ch = &check_user(\%user, \%olduser); if ($ch) { print &text('batch_emaccess', $lnum, $ch),"\n"; next; }
# Run the before command &set_user_envs(\%user, 'MODIFY_USER', $user{'plainpass'}, [ &secondary_groups($user{'user'}) ]); $merr = &making_changes(); &error(&text('usave_emaking', "<tt>$merr</tt>")) if (defined($merr));
# Move home directory if needed if ($olduser{'home'} ne $user{'home'} && $in{'movehome'} && $user{'home'} ne '/' && $olduser{'home'} ne '/') { if (-d $olduser{'home'} && !-e $user{'home'}) { local $out = &backquote_logged( "mv \"$olduser{'home'}\" ". "\"$user{'home'}\" 2>&1"); if ($?) { &error(&text('batch_emove', $lnum, $out)); } } }
# Change UIDs and GIDs if ($olduser{'gid'} != $user{'gid'} && $in{'chgid'}) { if ($in{'chgid'} == 1) { &recursive_change($user{'home'},$olduser{'uid'}, $olduser{'gid'}, -1, $user{'gid'}); } else { &recursive_change("/", $olduser{'uid'}, $olduser{'gid'}, -1, $user{'gid'}); } } if ($olduser{'uid'} != $user{'uid'} && $in{'chuid'}) { if ($in{'chuid'} == 1) { &recursive_change($user{'home'},$olduser{'uid'}, -1, $user{'uid'}, -1); } else { &recursive_change("/", $olduser{'uid'}, -1, $user{'uid'}, -1); } }
# Actually modify the user &modify_user(\%olduser, \%user);
# If the user has been renamed, update any secondary groups if ($olduser{'user'} ne $user{'user'}) { foreach $group (@glist) { local @mems = split(/,/, $group->{'members'}); local $idx = &indexof($olduser{'user'}, @mems); if ($idx >= 0) { $mems[$idx] = $user{'user'}; $group->{'members'} = join(",", @mems); &modify_group($group, $group); } } }
&made_changes();
# Modify in other modules, ignoring errors $error_must_die = 1; eval { &other_modules("useradmin_modify_user", \%user, \%olduser) if ($access{'mothers'} == 1 && $in{'others'} || $access{'mothers'} == 0); }; $error_must_die = 0; $other_err = $@;
print "<b>",&text('batch_modified',$olduser{'user'}),"</b>\n"; print "<b><i>",&text('batch_eother', $other_err),"</i></b>\n" if ($other_err); $modified++; } else { print &text('batch_eaction', $lnum, $line[0]),"\n"; next; } } print "</pre>\n"; &batch_end() if ($in{'batch'}); &unlock_user_files(); &webmin_log("batch", undef, $in{'source'} == 1 ? $in{'local'} : undef, { 'created' => $created, 'modified' => $modified, 'deleted' => $deleted, 'lnum' => $lnum } );
&ui_print_footer("batch_form.cgi", $text{'batch_return'}, "", $text{'index_return'});
# check_user(\%user, [\%olduser]) # Check access control restrictions for a user sub check_user { # check if uid is within range if ($access{'lowuid'} && $_[0]->{'uid'} < $access{'lowuid'}) { return &text('usave_elowuid', $access{'lowuid'}); } if ($access{'hiuid'} && $_[0]->{'uid'} > $access{'hiuid'}) { return &text('usave_ehiuid', $access{'hiuid'}); } if ($_[1] && !$access{'uuid'} && $_[1]->{'uid'} != $_[0]->{'uid'}) { return $text{'usave_euuid'}; }
# make sure home dir is under the allowed root if (!$access{'autohome'}) { $al = length($access{'home'}); if (length($_[0]->{'home'}) < $al || substr($_[0]->{'home'}, 0, $al) ne $access{'home'}) { return &text('usave_ehomepath', $_[0]->{'home'}); } }
# check for invalid shell if ($access{'shells'} ne '*' && &indexof($_[0]->{'shell'}, split(/\s+/, $access{'shells'})) < 0) { return &text('usave_eshell', $_[0]->{'shell'}); }
# check for invalid primary group (unless one is dynamically assigned) if ($user{'gid'} ne '') { local $ng = &my_getgrgid($_[0]->{'gid'}); local $ni = &can_use_group(\%access, $ng); if ($_[1]) { if ($_[1]->{'gid'} != $_[0]->{'gid'}) { local $og = &my_getgrgid($_[1]->{'gid'}); local $oi = &can_use_group(\%access, $og); if (!$ni) { return &text('usave_eprimary', $ng); } if (!$oi) { return &text('usave_eprimaryr', $og); } } } else { return &text('usave_eprimary', $ng) if (!$ni); } } return undef; }
sub secondary_groups { local @secs; foreach $g (@glist) { @mems = split(/,/, $g->{'members'}); if (&indexof($_[0], @mems) >= 0) { push(@secs, $g->{'gid'}); } } return @secs; }
sub validate_batch_minmax { local ($user, $lnum) = @_; foreach my $f ('min', 'max', 'warn', 'inactive', 'expire', 'change') { $user->{$f} =~ /^(\-|\+|)\d*$/ || return &text('batch_e'.$f, $lnum, $user->{$f}); } return undef; }
|