Viewing file: batch_exec.cgi (15.71 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 './ldap-useradmin-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'}, "");
$ldap = &ldap_connect(); $schema = $ldap->schema(); $pft = $schema->attribute("shadowLastChange") ? 2 : 0; &lock_user_files();
# Work out a good base UID for new users $newuid = $mconfig{'base_uid'}; $newgid = $mconfig{'base_gid'}; @glist = &list_groups();
# Process the file $lnum = $created = $modified = $deleted = 0; print "<pre>\n"; LINE: 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 local @attrs; if ($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]; if ($in{'forcechange'} == 1){ $user{'change'} = 0; } else { $user{'change'} = $line[2] eq '' ? '' : int(time() / (60*60*24)); } @attrs = @line[13 .. $#line]; } else { # Classic passwd file information if (@line < 8) { print &text('batch_elen', $lnum, 8),"\n"; next; } @attrs = @line[9 .. $#line]; }
# Parse common fields if (!$line[1]) { print &text('batch_eline', $lnum),"\n"; next; } $user{'user'} = $line[1]; $err = &useradmin::check_username_restrictions($user{'user'}); if ($err) { print &text('batch_echeck', $lnum, $err),"\n"; next; } if (&check_user_used($ldap, $user{'user'})) { print &text('batch_euser', $lnum, $user{'user'}),"\n"; next; } if ($line[3] !~ /^\d+$/) { # make up a UID while(&check_uid_used($ldap, $newuid) || $mconfig{'new_user_gid'} && &check_gid_used($ldap, $newuid)) { $newuid++; } $user{'uid'} = $newuid; } else { # use the given UID if (&check_uid_used($ldap, $line[3])) { print &text('batch_ecaccess', $lnum, $text{'usave_euidused2'}),"\n"; next; } $user{'uid'} = $line[3]; } if (!-r $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 = &all_getgrgid($gids[0]);
if ($line[6] eq '' && $mconfig{'home_base'}) { # Choose home dir automatically $user{'home'} = &auto_home_dir( $mconfig{'home_base'}, $user{'user'}, $user{'gid'}); } elsif ($line[6] !~ /^\//) { print &text('batch_ehome', $lnum,$line[6]),"\n"; next; } else { # Use given home dir $user{'home'} = $line[6]; }
# 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 password if ($in{'crypt'}) { $user{'pass'} = $line[2]; $user{'passmode'} = 2; } elsif ($line[2] eq 'x') { # No login allowed $user{'pass'} = $mconfig{'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]; }
$user{'ldap_attrs'} ||= [ ]; if ($in{'samba'}) { # Add Samba-specific properties push(@{$user{'ldap_class'}}, $config{'samba_class'}); &samba_properties(1, \%user, $user{'passmode'}, $user{'plainpass'}, $schema, $user{'ldap_attrs'}, $ldap); }
# Add extra LDAP attrs foreach $a (@attrs) { next if (!$a); if ($a =~ /^([^=]+)=(.*)/) { push(@{$user{'ldap_attrs'}}, $1, $2); } else { print &text('batch_eattr', $lnum, $a),"\n"; next LINE; } }
# 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 (&check_group_used($ldap, $user{'user'})) { print &text('batch_egtaken', $lnum, $user{'user'}),"\n"; next; }
if ($mconfig{'new_user_gid'}) { $newgid = $user{'uid'}; } else { while(&check_gid_used($ldap, $newgid)) { $newgid++; } } local %group; $group{'group'} = $user{'user'}; $user{'gid'} = $group{'gid'} = $newgid; &create_group(\%group); }
# Create home directory if ($in{'makehome'} && !-d $user{'home'}) { &lock_file($user{'home'}); if (!mkdir($user{'home'}, oct($mconfig{'homedir_perms'}))) { print &text('batch_emkdir', $user{'home'}, $!),"\n"; } chmod(oct($mconfig{'homedir_perms'}), $user{'home'}); chown($user{'uid'}, $user{'gid'}, $user{'home'}); &unlock_file($user{'home'}); }
# Create the user! &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); }
# Re-get the new user object $base = &get_user_base(); $newdn = "uid=$user{'user'},$base"; $rv = $ldap->search(base => $newdn, scope => 'base', filter => &user_filter()); ($uinfo) = $rv->all_entries; %user = &dn_to_hash($uinfo);
# Call the post command &set_user_envs(\%user, 'CREATE_USER', $user{'plainpass'}, [ map { $_->{'gid'} } @secs ]); &made_changes();
# Call other modules, ignoring any failures $error_must_die = 1; eval { &other_modules("useradmin_create_user", \%user) if ($in{'others'}); }; $other_err = $@; $error_must_die = 0;
if ($in{'copy'} && $in{'makehome'}) { # Copy files to user's home directory local $uf = $mconfig{'user_files'}; local $shell = $user{'shell'}; $shell =~ s/^(.*)\///g; if ($group = &all_getgrgid($user{'gid'})) { $uf =~ s/\$group/$group/g; } $uf =~ s/\$gid/$user{'gid'}/g; $uf =~ s/\$shell/$shell/g; &useradmin::copy_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 (!$mconfig{'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 ($in{'others'}); }; $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 addressbook entry if ($config{'addressbook'}) { &delete_ldap_subtree($ldap, "ou=$user->{'user'}, $config{'addressbook'}"); }
# Delete his home directory if ($in{'delhome'} && $user->{'home'} !~ /^\/+$/) { if ($mconfig{'delete_only'}) { &lock_file($user->{'home'}); &system_logged("find \"$user->{'home'}\" ! -type d -user $user->{'uid'} | xargs rm -f >/dev/null 2>&1"); &system_logged("find \"$user->{'home'}\" -type d -user $user->{'uid'} | xargs rmdir >/dev/null 2>&1"); rmdir($user->{'home'}); &unlock_file($user->{'home'}); } else { &system_logged("rm -rf \"$user->{'home'}\" >/dev/null 2>&1"); } }
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 @attrs = @line[$wlen .. $#line]; 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'};
# Update supplied fields $user{'user'} = $line[2] if ($line[2] ne ''); 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'} = $mconfig{'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 ($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 ''); if ($in{'forcechange'} == 1){ $user{'change'} = 0; } elsif ($line[3] ne ''){ $user{'change'}= int(time() / (60*60*24)); } }
# Work out Samba properties $wassamba = &indexof($config{'samba_class'}, @{$user{'ldap_class'}}) >= 0; $user{'ldap_attrs'} ||= [ ]; if ($wassamba) { # Need to update Samba attributes &samba_properties(0, \%user, $user{'passmode'}, $user{'plainpass'}, $schema, $user{'ldap_attrs'}); }
# Set extra LDAP attrs foreach $a (@attrs) { next if (!$a); if ($a =~ /^([^=]+)=(.*)/) { push(@{$user{'ldap_attrs'}}, $1, $2); } else { print &text('batch_eattr', $lnum, $a),"\n"; next LINE; } }
# 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) { &useradmin::recursive_change( $user{'home'}, $olduser{'uid'}, $olduser{'gid'}, -1, $user{'gid'}); } else { &useradmin::recursive_change( "/", $olduser{'uid'}, $olduser{'gid'}, -1, $user{'gid'}); } } if ($olduser{'uid'} != $user{'uid'} && $in{'chuid'}) { if ($in{'chuid'} == 1) { &useradmin::recursive_change( $user{'home'}, $olduser{'uid'}, -1, $user{'uid'}, -1); } else { &useradmin::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 ($in{'others'}); }; $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"; &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 = &all_getgrgid($_[0]->{'gid'}); local $ni = &can_use_group(\%access, $ng); if ($_[1]) { if ($_[1]->{'gid'} != $_[0]->{'gid'}) { local $og = &all_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; }
|