3 # Execute create/modify/delete commands in a batch file
5 require './ldap-useradmin-lib.pl';
6 $access{'batch'} || &error($text{'batch_ecannot'});
7 if ($ENV{'REQUEST_METHOD'} eq 'GET') {
13 if ($in{'source'} == 0) {
15 $data =~ /\S/ || &error($text{'batch_efile'});
17 elsif ($in{'source'} == 1) {
18 open(LOCAL, $in{'local'}) || &error($text{'batch_elocal'});
24 elsif ($in{'source'} == 2) {
26 $data =~ /\S/ || &error($text{'batch_etext'});
29 &ui_print_unbuffered_header(undef, $text{'batch_title'}, "");
31 $ldap = &ldap_connect();
32 $schema = $ldap->schema();
33 $pft = $schema->attribute("shadowLastChange") ? 2 : 0;
36 # Work out a good base UID for new users
37 $newuid = $mconfig{'base_uid'};
38 $newgid = $mconfig{'base_gid'};
39 @glist = &list_groups();
42 $lnum = $created = $modified = $deleted = 0;
44 LINE: foreach $line (split(/[\r\n]+/, $data)) {
46 $line =~ s/^\s*#.*$//;
47 next if ($line !~ /\S/);
48 local @line = split(/:/, $line, -1);
50 if ($line[0] eq 'create') {
54 # SYSV-style passwd and shadow information
56 print &text('batch_elen', $lnum, 13),"\n";
59 $user{'min'} = $line[8];
60 $user{'max'} = $line[9];
61 $user{'warn'} = $line[10];
62 $user{'inactive'} = $line[11];
63 $user{'expire'} = $line[12];
64 if ($in{'forcechange'} == 1){
67 $user{'change'} = $line[2] eq '' ? '' :
68 int(time() / (60*60*24));
70 @attrs = @line[13 .. $#line];
73 # Classic passwd file information
75 print &text('batch_elen', $lnum, 8),"\n";
78 @attrs = @line[9 .. $#line];
83 print &text('batch_eline', $lnum),"\n";
86 $user{'user'} = $line[1];
87 $err = &useradmin::check_username_restrictions($user{'user'});
89 print &text('batch_echeck', $lnum, $err),"\n";
92 if (&check_user_used($ldap, $user{'user'})) {
93 print &text('batch_euser', $lnum, $user{'user'}),"\n";
96 if ($line[3] !~ /^\d+$/) {
98 while(&check_uid_used($ldap, $newuid) ||
99 $mconfig{'new_user_gid'} &&
100 &check_gid_used($ldap, $newuid)) {
103 $user{'uid'} = $newuid;
107 if (&check_uid_used($ldap, $line[3])) {
108 print &text('batch_ecaccess', $lnum,
109 $text{'usave_euidused2'}),"\n";
112 $user{'uid'} = $line[3];
115 print &text('batch_eshell', $lnum, $line[7]),"\n";
118 $user{'shell'} = $line[7];
119 $user{'real'} = $line[5];
120 local @gids = split(/[ ,]+/, $line[4]);
121 $user{'gid'} = $gids[0];
122 local $grp = &all_getgrgid($gids[0]);
124 if ($line[6] eq '' && $mconfig{'home_base'}) {
125 # Choose home dir automatically
126 $user{'home'} = &auto_home_dir(
127 $mconfig{'home_base'}, $user{'user'}, $user{'gid'});
129 elsif ($line[6] !~ /^\//) {
130 print &text('batch_ehome', $lnum,$line[6]),"\n";
135 $user{'home'} = $line[6];
138 # Work out secondary group membership
142 for($i=1; $i<@gids; $i++) {
144 grep { $_->{'gid'} eq $gids[$i] } @glist;
145 push(@secs, $group) if ($group);
151 $user{'pass'} = $line[2];
152 $user{'passmode'} = 2;
154 elsif ($line[2] eq 'x') {
156 $user{'pass'} = $mconfig{'lock_string'};
157 $user{'passmode'} = 1;
159 elsif ($line[2] eq '') {
162 $user{'passmode'} = 0;
166 $user{'pass'} = &encrypt_password($line[2]);
167 $user{'passmode'} = 3;
168 $user{'plainpass'} = $line[2];
171 $user{'ldap_attrs'} ||= [ ];
173 # Add Samba-specific properties
174 push(@{$user{'ldap_class'}}, $config{'samba_class'});
175 &samba_properties(1, \%user, $user{'passmode'},
176 $user{'plainpass'}, $schema,
177 $user{'ldap_attrs'}, $ldap);
180 # Add extra LDAP attrs
181 foreach $a (@attrs) {
183 if ($a =~ /^([^=]+)=(.*)/) {
184 push(@{$user{'ldap_attrs'}}, $1, $2);
187 print &text('batch_eattr', $lnum, $a),"\n";
192 # Run the before command
193 &set_user_envs(\%user, 'CREATE_USER', $user{'plainpass'},
194 [ map { $_->{'gid'} } @secs ]);
195 $merr = &making_changes();
196 &error(&text('usave_emaking', "<tt>$merr</tt>"))
199 if ($user{'gid'} !~ /^\d+$/) {
200 # Need to create a new group for the user
201 if (&check_group_used($ldap, $user{'user'})) {
202 print &text('batch_egtaken', $lnum,
207 if ($mconfig{'new_user_gid'}) {
208 $newgid = $user{'uid'};
211 while(&check_gid_used($ldap, $newgid)) {
216 $group{'group'} = $user{'user'};
217 $user{'gid'} = $group{'gid'} = $newgid;
218 &create_group(\%group);
221 # Create home directory
222 if ($in{'makehome'} && !-d $user{'home'}) {
223 &lock_file($user{'home'});
224 if (!mkdir($user{'home'}, oct($mconfig{'homedir_perms'}))) {
225 print &text('batch_emkdir', $user{'home'}, $!),"\n";
227 chmod(oct($mconfig{'homedir_perms'}), $user{'home'});
228 chown($user{'uid'}, $user{'gid'}, $user{'home'});
229 &unlock_file($user{'home'});
233 &create_user(\%user);
235 # Add user to some secondary groups
237 foreach $group (@secs) {
238 local @mems = split(/,/ , $group->{'members'});
239 push(@mems, $user{'user'});
240 $group->{'members'} = join(",", @mems);
241 &modify_group($group, $group);
244 # Re-get the new user object
245 $base = &get_user_base();
246 $newdn = "uid=$user{'user'},$base";
247 $rv = $ldap->search(base => $newdn,
249 filter => &user_filter());
250 ($uinfo) = $rv->all_entries;
251 %user = &dn_to_hash($uinfo);
253 # Call the post command
254 &set_user_envs(\%user, 'CREATE_USER', $user{'plainpass'},
255 [ map { $_->{'gid'} } @secs ]);
258 # Call other modules, ignoring any failures
261 &other_modules("useradmin_create_user", \%user)
267 if ($in{'copy'} && $in{'makehome'}) {
268 # Copy files to user's home directory
269 local $uf = $mconfig{'user_files'};
270 local $shell = $user{'shell'}; $shell =~ s/^(.*)\///g;
271 if ($group = &all_getgrgid($user{'gid'})) {
272 $uf =~ s/\$group/$group/g;
274 $uf =~ s/\$gid/$user{'gid'}/g;
275 $uf =~ s/\$shell/$shell/g;
276 &useradmin::copy_skel_files($uf, $user{'home'},
277 $user{'uid'}, $user{'gid'});
280 print "<b>",&text('batch_created',$user{'user'}),"</b>\n";
281 print "<b><i>",&text('batch_eother', $other_err),"</i></b>\n"
285 elsif ($line[0] eq 'delete') {
286 # Deleting an existing user
288 print &text('batch_elen', $lnum, 2),"\n";
291 local @ulist = &list_users();
292 local ($user) = grep { $_->{'user'} eq $line[1] } @ulist;
294 print &text('batch_enouser', $lnum, $line[1]),"\n";
297 if (!$mconfig{'delete_root'} && $user->{'uid'} <= 10) {
298 print &text('batch_edaccess', $lnum,
299 $text{'udel_eroot'}),"\n";
303 # Run the before command
304 &set_user_envs($user, 'DELETE_USER', undef,
305 [ &secondary_groups($user->{'user'}) ]);
306 $merr = &making_changes();
307 &error(&text('usave_emaking', "<tt>$merr</tt>"))
310 # Delete from other modules, ignoring errors
313 &other_modules("useradmin_delete_user", $user)
319 # Delete the user entry
322 # Delete the user from groups
323 foreach $g (&list_groups()) {
324 @mems = split(/,/, $g->{'members'});
325 $idx = &indexof($user->{'user'}, @mems);
327 splice(@mems, $idx, 1);
329 $newg{'members'} = join(',', @mems);
330 &modify_group($g, \%newg);
332 $mygroup = $g if ($g->{'group'} eq $user->{'user'});
335 # Delete the user's group
336 if ($mygroup && !$mygroup->{'members'}) {
338 foreach $ou (&list_users()) {
340 if ($ou->{'gid'} == $mygroup->{'gid'});
343 &delete_group($mygroup);
348 # Delete his addressbook entry
349 if ($config{'addressbook'}) {
350 &delete_ldap_subtree($ldap,
351 "ou=$user->{'user'}, $config{'addressbook'}");
354 # Delete his home directory
355 if ($in{'delhome'} && $user->{'home'} !~ /^\/+$/) {
356 if ($mconfig{'delete_only'}) {
357 &lock_file($user->{'home'});
358 &system_logged("find \"$user->{'home'}\" ! -type d -user $user->{'uid'} | xargs rm -f >/dev/null 2>&1");
359 &system_logged("find \"$user->{'home'}\" -type d -user $user->{'uid'} | xargs rmdir >/dev/null 2>&1");
360 rmdir($user->{'home'});
361 &unlock_file($user->{'home'});
364 &system_logged("rm -rf \"$user->{'home'}\" >/dev/null 2>&1");
368 print "<b>",&text('batch_deleted',$user->{'user'}),"</b>\n";
369 print "<b><i>",&text('batch_eother', $other_err),"</i></b>\n"
373 elsif ($line[0] eq 'modify') {
374 # Modifying an existing user
375 local $wlen = $pft == 5 ? 11 :
378 $pft == 1 || $pft == 6 ? 12 : 9;
380 print &text('batch_elen', $lnum, $wlen),"\n";
383 local @attrs = @line[$wlen .. $#line];
384 local @ulist = &list_users();
385 local ($user) = grep { $_->{'user'} eq $line[1] } @ulist;
387 print &text('batch_enouser', $lnum, $line[1]),"\n";
390 %olduser = %user = %$user;
391 $user{'olduser'} = $user->{'user'};
393 # Update supplied fields
394 $user{'user'} = $line[2] if ($line[2] ne '');
395 if ($in{'crypt'} && $line[3] ne '') {
396 # Changing to pre-encrypted password
397 $user{'pass'} = $line[3];
398 $user{'passmode'} = 2;
400 elsif ($line[3] eq 'x') {
402 $user{'pass'} = $mconfig{'lock_string'};
403 $user{'passmode'} = 1;
405 elsif ($line[3] ne '') {
407 $user{'pass'} = &encrypt_password($line[3]);
408 $user{'passmode'} = 3;
409 $user{'plainpass'} = $line[3];
413 $user{'passmode'} = 4;
415 $user{'uid'} = $line[4] if ($line[4] ne '');
416 $user{'gid'} = $line[5] if ($line[5] ne '');
417 $user{'real'} = $line[6] if ($line[6] ne '');
418 $user{'home'} = $line[7] if ($line[7] ne '');
419 $user{'shell'} = $line[8] if ($line[8] ne '');
422 # SYSV-style passwd and shadow information
423 $user{'min'}=$line[9] if ($line[9] ne '');
424 $user{'max'}=$line[10] if ($line[10] ne '');
425 $user{'warn'}=$line[11] if ($line[11] ne '');
426 $user{'inactive'}=$line[12]
427 if ($line[12] ne '');
428 $user{'expire'}=$line[13] if ($line[13] ne '');
429 if ($in{'forcechange'} == 1){
431 } elsif ($line[3] ne ''){
432 $user{'change'}= int(time() / (60*60*24));
436 # Work out Samba properties
437 $wassamba = &indexof($config{'samba_class'},
438 @{$user{'ldap_class'}}) >= 0;
439 $user{'ldap_attrs'} ||= [ ];
441 # Need to update Samba attributes
442 &samba_properties(0, \%user, $user{'passmode'},
443 $user{'plainpass'}, $schema,
444 $user{'ldap_attrs'});
447 # Set extra LDAP attrs
448 foreach $a (@attrs) {
450 if ($a =~ /^([^=]+)=(.*)/) {
451 push(@{$user{'ldap_attrs'}}, $1, $2);
454 print &text('batch_eattr', $lnum, $a),"\n";
459 # Run the before command
460 &set_user_envs(\%user, 'MODIFY_USER', $user{'plainpass'},
461 [ &secondary_groups($user{'user'}) ]);
462 $merr = &making_changes();
463 &error(&text('usave_emaking', "<tt>$merr</tt>"))
466 # Move home directory if needed
467 if ($olduser{'home'} ne $user{'home'} && $in{'movehome'} &&
468 $user{'home'} ne '/' && $olduser{'home'} ne '/') {
469 if (-d $olduser{'home'} && !-e $user{'home'}) {
470 local $out = &backquote_logged(
471 "mv \"$olduser{'home'}\" ".
472 "\"$user{'home'}\" 2>&1");
473 if ($?) { &error(&text('batch_emove',
478 # Change UIDs and GIDs
479 if ($olduser{'gid'} != $user{'gid'} && $in{'chgid'}) {
480 if ($in{'chgid'} == 1) {
481 &useradmin::recursive_change(
482 $user{'home'}, $olduser{'uid'},
483 $olduser{'gid'}, -1, $user{'gid'});
486 &useradmin::recursive_change(
487 "/", $olduser{'uid'},
488 $olduser{'gid'}, -1, $user{'gid'});
491 if ($olduser{'uid'} != $user{'uid'} && $in{'chuid'}) {
492 if ($in{'chuid'} == 1) {
493 &useradmin::recursive_change(
494 $user{'home'}, $olduser{'uid'},
495 -1, $user{'uid'}, -1);
498 &useradmin::recursive_change(
499 "/", $olduser{'uid'},
500 -1, $user{'uid'}, -1);
504 # Actually modify the user
505 &modify_user(\%olduser, \%user);
507 # If the user has been renamed, update any secondary groups
508 if ($olduser{'user'} ne $user{'user'}) {
509 foreach $group (@glist) {
510 local @mems = split(/,/, $group->{'members'});
511 local $idx = &indexof($olduser{'user'}, @mems);
513 $mems[$idx] = $user{'user'};
514 $group->{'members'} = join(",", @mems);
515 &modify_group($group, $group);
522 # Modify in other modules, ignoring errors
525 &other_modules("useradmin_modify_user",
532 print "<b>",&text('batch_modified',$olduser{'user'}),"</b>\n";
533 print "<b><i>",&text('batch_eother', $other_err),"</i></b>\n"
538 print &text('batch_eaction', $lnum, $line[0]),"\n";
543 &unlock_user_files();
544 &webmin_log("batch", undef, $in{'source'} == 1 ? $in{'local'} : undef,
545 { 'created' => $created, 'modified' => $modified,
546 'deleted' => $deleted, 'lnum' => $lnum } );
548 &ui_print_footer("batch_form.cgi", $text{'batch_return'},
549 "", $text{'index_return'});
551 # check_user(\%user, [\%olduser])
552 # Check access control restrictions for a user
555 # check if uid is within range
556 if ($access{'lowuid'} && $_[0]->{'uid'} < $access{'lowuid'}) {
557 return &text('usave_elowuid', $access{'lowuid'});
559 if ($access{'hiuid'} && $_[0]->{'uid'} > $access{'hiuid'}) {
560 return &text('usave_ehiuid', $access{'hiuid'});
562 if ($_[1] && !$access{'uuid'} && $_[1]->{'uid'} != $_[0]->{'uid'}) {
563 return $text{'usave_euuid'};
566 # make sure home dir is under the allowed root
567 if (!$access{'autohome'}) {
568 $al = length($access{'home'});
569 if (length($_[0]->{'home'}) < $al ||
570 substr($_[0]->{'home'}, 0, $al) ne $access{'home'}) {
571 return &text('usave_ehomepath', $_[0]->{'home'});
575 # check for invalid shell
576 if ($access{'shells'} ne '*' &&
577 &indexof($_[0]->{'shell'}, split(/\s+/, $access{'shells'})) < 0) {
578 return &text('usave_eshell', $_[0]->{'shell'});
581 # check for invalid primary group (unless one is dynamically assigned)
582 if ($user{'gid'} ne '') {
583 local $ng = &all_getgrgid($_[0]->{'gid'});
584 local $ni = &can_use_group(\%access, $ng);
586 if ($_[1]->{'gid'} != $_[0]->{'gid'}) {
587 local $og = &all_getgrgid($_[1]->{'gid'});
588 local $oi = &can_use_group(\%access, $og);
589 if (!$ni) { return &text('usave_eprimary', $ng); }
590 if (!$oi) { return &text('usave_eprimaryr', $og); }
594 return &text('usave_eprimary', $ng) if (!$ni);
603 foreach $g (@glist) {
604 @mems = split(/,/, $g->{'members'});
605 if (&indexof($_[0], @mems) >= 0) {
606 push(@secs, $g->{'gid'});