source: branches/samba-3.0/examples/LDAP/smbldap-tools-0.9.2/smbldap-usermod

Last change on this file was 1, checked in by Paul Smedley, 18 years ago

Initial code import

File size: 18.5 KB
Line 
1#!/usr/bin/perl -w
2
3# $Id: smbldap-usermod,v 1.14 2005/07/05 09:05:16 jtournier Exp $
4#
5# This code was developped by IDEALX (http://IDEALX.org/) and
6# contributors (their names can be found in the CONTRIBUTORS file).
7#
8# Copyright (C) 2001-2002 IDEALX
9#
10# This program is free software; you can redistribute it and/or
11# modify it under the terms of the GNU General Public License
12# as published by the Free Software Foundation; either version 2
13# of the License, or (at your option) any later version.
14#
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18# GNU General Public License for more details.
19#
20# You should have received a copy of the GNU General Public License
21# along with this program; if not, write to the Free Software
22# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
23# USA.
24
25# Purpose of smbldap-usermod : user (posix,shadow,samba) modification
26
27use strict;
28use FindBin;
29use FindBin qw($RealBin);
30use lib "$RealBin/";
31use smbldap_tools;
32
33#####################
34
35use Getopt::Std;
36my %Options;
37my $nscd_status;
38
39my $ok = getopts('A:B:C:D:E:F:H:IJM:N:S:PT:ame:f:u:g:G:d:l:r:s:c:ok:?h', \%Options);
40if ( (!$ok) || (@ARGV < 1) || ($Options{'?'}) || ($Options{'h'}) ) {
41 print_banner;
42 print "Usage: $0 [-awmugdsckABCDEFGHIPSMT?h] username\n";
43 print "Available options are:\n";
44 print " -c gecos\n";
45 print " -d home directory\n";
46 #print " -m move home directory\n";
47 #print " -f inactive days\n";
48 print " -r new username (cn, sn and dn are updated)\n";
49 print " -u uid\n";
50 print " -o uid can be non unique\n";
51 print " -g gid\n";
52 print " -G supplementary groups (comma separated)\n";
53 print " -s shell\n";
54 print " -N canonical name\n";
55 print " -S surname\n";
56 print " -P ends by invoking smbldap-passwd\n";
57 print " For samba users:\n";
58 print " -a add sambaSAMAccount objectclass\n";
59 print " -e expire date (\"YYYY-MM-DD HH:MM:SS\")\n";
60 print " -A can change password ? 0 if no, 1 if yes\n";
61 print " -B must change password ? 0 if no, 1 if yes\n";
62 print " -C sambaHomePath (SMB home share, like '\\\\PDC-SRV\\homes')\n";
63 print " -D sambaHomeDrive (letter associated with home share, like 'H:')\n";
64 print " -E sambaLogonScript (DOS script to execute on login)\n";
65 print " -F sambaProfilePath (profile directory, like '\\\\PDC-SRV\\profiles\\foo')\n";
66 print " -H sambaAcctFlags (samba account control bits like '[NDHTUMWSLKI]')\n";
67 print " -I disable an user. Can't be used with -H or -J\n";
68 print " -J enable an user. Can't be used with -H or -I\n";
69 print " -M mailAddresses (comma seperated)\n";
70 print " -T mailToAddress (forward address) (comma seperated)\n";
71 print " -?|-h show this help message\n";
72 exit (1);
73}
74
75if ($< != 0) {
76 print "You must be root to modify an user\n";
77 exit (1);
78}
79# Read only first @ARGV
80my $user = $ARGV[0];
81
82# Let's connect to the directory first
83my $ldap_master=connect_ldap_master();
84
85# Read user data
86my $user_entry = read_user_entry($user);
87if (!defined($user_entry)) {
88 print "$0: user $user doesn't exist\n";
89 exit (1);
90}
91
92my $samba = 0;
93if (grep ($_ =~ /^sambaSamAccount$/i, $user_entry->get_value('objectClass'))) {
94 $samba = 1;
95}
96
97# get the dn of the user
98my $dn= $user_entry->dn();
99
100my $tmp;
101my @mods;
102my @dels;
103if (defined($tmp = $Options{'a'})) {
104 # Let's connect to the directory first
105 my $winmagic = 2147483647;
106 my $valpwdcanchange = 0;
107 my $valpwdmustchange = $winmagic;
108 my $valpwdlastset = 0;
109 my $valacctflags = "[UX]";
110 my $user_entry=read_user_entry($user);
111 my $uidNumber = $user_entry->get_value('uidNumber');
112 my $userRid = 2 * $uidNumber + 1000;
113 # apply changes
114 my $modify = $ldap_master->modify ( "$dn",
115 changes => [
116 add => [objectClass => 'sambaSAMAccount'],
117 add => [sambaPwdLastSet => "$valpwdlastset"],
118 add => [sambaLogonTime => '0'],
119 add => [sambaLogoffTime => '2147483647'],
120 add => [sambaKickoffTime => '2147483647'],
121 add => [sambaPwdCanChange => "$valpwdcanchange"],
122 add => [sambaPwdMustChange => "$valpwdmustchange"],
123 add => [displayName => "$config{userGecos}"],
124 add => [sambaSID=> "$config{SID}-$userRid"],
125 add => [sambaAcctFlags => "$valacctflags"],
126 ]
127 );
128 $modify->code && warn "failed to modify entry: ", $modify->error ;
129}
130
131# Process options
132my $changed_uid;
133my $_userUidNumber;
134my $_userRid;
135if (defined($tmp = $Options{'u'})) {
136 if (defined($Options{'o'})) {
137 $nscd_status = system "/etc/init.d/nscd status >/dev/null 2>&1";
138
139 if ($nscd_status == 0) {
140 system "/etc/init.d/nscd stop > /dev/null 2>&1";
141 }
142
143 if (getpwuid($tmp)) {
144 if ($nscd_status == 0) {
145 system "/etc/init.d/nscd start > /dev/null 2>&1";
146 }
147
148 print "$0: uid number $tmp exists\n";
149 exit (6);
150 }
151 if ($nscd_status == 0) {
152 system "/etc/init.d/nscd start > /dev/null 2>&1";
153 }
154
155 }
156 push(@mods, 'uidNumber', $tmp);
157 $_userUidNumber = $tmp;
158 if ($samba) {
159 # as rid we use 2 * uid + 1000
160 my $_userRid = 2 * $_userUidNumber + 1000;
161 if (defined($Options{'x'})) {
162 $_userRid= sprint("%x", $_userRid);
163 }
164 push(@mods, 'sambaSID', $config{SID}.'-'.$_userRid);
165 }
166 $changed_uid = 1;
167}
168
169my $changed_gid;
170my $_userGidNumber;
171my $_userGroupSID;
172if (defined($tmp = $Options{'g'})) {
173 $_userGidNumber = parse_group($tmp);
174 if ($_userGidNumber < 0) {
175 print "$0: group $tmp doesn't exist\n";
176 exit (6);
177 }
178 push(@mods, 'gidNumber', $_userGidNumber);
179 if ($samba) {
180 # as grouprid we use the sambaSID attribute's value of the group
181 my $group_entry = read_group_entry_gid($_userGidNumber);
182 my $_userGroupSID = $group_entry->get_value('sambaSID');
183 unless ($_userGroupSID) {
184 print "Error: sambaPrimaryGroupSid could not be set (sambaSID for group $_userGidNumber does not exist\n";
185 exit (7);
186 }
187 push(@mods, 'sambaPrimaryGroupSid', $_userGroupSID);
188 }
189 $changed_gid = 1;
190}
191
192if (defined($tmp = $Options{'s'})) {
193 push(@mods, 'loginShell' => $tmp);
194}
195
196
197if (defined($tmp = $Options{'c'})) {
198 push(@mods, 'gecos' => $tmp,
199 'description' => $tmp);
200 if ($samba == 1) {
201 push(@mods, 'displayName' => $tmp);
202 }
203}
204
205if (defined($tmp = $Options{'d'})) {
206 push(@mods, 'homeDirectory' => $tmp);
207}
208
209if (defined($tmp = $Options{'N'})) {
210 push(@mods, 'cn' => $tmp);
211}
212
213if (defined($tmp = $Options{'S'})) {
214 push(@mods, 'sn' => $tmp);
215}
216
217my $mailobj = 0;
218if ($tmp= $Options{'M'}) {
219 # action si + or - for adding or deleting an entry
220 my $action= '';
221 if ($tmp =~ s/^([+-])+\s*//) {
222 $action= $1;
223 }
224 my @userMailLocal = &split_arg_comma($tmp);
225 my @mail;
226 foreach my $m (@userMailLocal) {
227 my $domain = $config{mailDomain};
228 if ($m =~ /^(.+)@/) {
229 push (@mail, $m);
230 # mailLocalAddress contains only the first part
231 $m= $1;
232 } else {
233 push(@mail, $m.($domain ? '@'.$domain : ''));
234 }
235 }
236 if ($action) {
237 my @old_MailLocal;
238 my @old_mail;
239 @old_mail = $user_entry->get_value('mail');
240 @old_MailLocal = $user_entry->get_value('mailLocalAddress');
241 if ($action eq '+') {
242 @userMailLocal = &list_union(\@old_MailLocal, \@userMailLocal);
243 @mail = &list_union(\@old_mail, \@mail);
244 } elsif ($action eq '-') {
245 @userMailLocal = &list_minus(\@old_MailLocal, \@userMailLocal);
246 @mail = &list_minus(\@old_mail, \@mail);
247 }
248 }
249 push(@mods, 'mailLocalAddress', [ @userMailLocal ]);
250 push(@mods, 'mail' => [ @mail ]);
251 $mailobj = 1;
252}
253
254if ($tmp= $Options{'T'}) {
255 my $action= '';
256 my @old;
257 # action si + or - for adding or deleting an entry
258 if ($tmp =~ s/^([+-])+\s*//) {
259 $action= $1;
260 }
261 my @userMailTo = &split_arg_comma($tmp);
262 if ($action) {
263 @old = $user_entry->get_value('mailRoutingAddress');
264 }
265 if ($action eq '+') {
266 @userMailTo = &list_union(\@old, \@userMailTo);
267 } elsif ($action eq '-') {
268 @userMailTo = &list_minus(\@old, \@userMailTo);
269 }
270 push(@mods, 'mailRoutingAddress', [ @userMailTo ]);
271 $mailobj = 1;
272}
273if ($mailobj) {
274 my @objectclass = $user_entry->get_value('objectClass');
275 if (! grep ($_ =~ /^inetLocalMailRecipient$/i, @objectclass)) {
276 push(@mods, 'objectClass' => [ @objectclass, 'inetLocalMailRecipient' ]);
277 }
278}
279
280
281if (defined($tmp = $Options{'G'})) {
282 my $action= '';
283 if ($tmp =~ s/^([+-])+\s*//) {
284 $action= $1;
285 }
286 if ($action eq '-') {
287 # remove user from specified groups
288 foreach my $gname (&split_arg_comma($tmp)) {
289 group_remove_member($gname, $user);
290 }
291 } else {
292 if ($action ne '+') {
293 my @old = &find_groups_of($user);
294 # remove user from old groups
295 foreach my $gname (@old) {
296 if ($gname ne "") {
297 group_remove_member($gname, $user);
298 }
299 }
300 }
301 # add user to new groups
302 add_grouplist_user($tmp, $user);
303 }
304}
305
306#
307# A : sambaPwdCanChange
308# B : sambaPwdMustChange
309# C : sambaHomePath
310# D : sambaHomeDrive
311# E : sambaLogonScript
312# F : sambaProfilePath
313# H : sambaAcctFlags
314
315my $attr;
316my $winmagic = 2147483647;
317
318$samba = is_samba_user($user);
319
320if (defined($tmp = $Options{'e'})) {
321 if ($samba == 1) {
322 my $kickoffTime=`date --date='$tmp' +%s`;
323 chomp($kickoffTime);
324 push(@mods, 'sambakickoffTime' => $kickoffTime);
325 } else {
326 print "User $user is not a samba user\n";
327 }
328}
329
330my $_sambaPwdCanChange;
331if (defined($tmp = $Options{'A'})) {
332 if ($samba == 1) {
333 $attr = "sambaPwdCanChange";
334 if ($tmp != 0) {
335 $_sambaPwdCanChange=0;
336 } else {
337 $_sambaPwdCanChange=$winmagic;
338 }
339 push(@mods, 'sambaPwdCanChange' => $_sambaPwdCanChange);
340 } else {
341 print "User $user is not a samba user\n";
342 }
343}
344
345my $_sambaPwdMustChange;
346if (defined($tmp = $Options{'B'})) {
347 if ($samba == 1) {
348 if ($tmp != 0) {
349 $_sambaPwdMustChange=0;
350 # To force a user to change his password:
351 # . the attribut sambaPwdLastSet must be != 0
352 # . the attribut sambaAcctFlags must not match the 'X' flag
353 my $_sambaAcctFlags;
354 my $flags = $user_entry->get_value('sambaAcctFlags');
355 if ( defined $flags and $flags =~ /X/ ) {
356 my $letters;
357 if ($flags =~ /(\w+)/) {
358 $letters = $1;
359 }
360 $letters =~ s/X//;
361 $_sambaAcctFlags="\[$letters\]";
362 push(@mods, 'sambaAcctFlags' => $_sambaAcctFlags);
363 }
364 my $_sambaPwdLastSet = $user_entry->get_value('sambaPwdLastSet');
365 if ($_sambaPwdLastSet == 0) {
366 push(@mods, 'sambaPwdLastSet' => $winmagic);
367 }
368 } else {
369 $_sambaPwdMustChange=$winmagic;
370 }
371 push(@mods, 'sambaPwdMustChange' => $_sambaPwdMustChange);
372 } else {
373 print "User $user is not a samba user\n";
374 }
375}
376
377if (defined($tmp = $Options{'C'})) {
378 if ($samba == 1) {
379 if ($tmp eq "" and defined $user_entry->get_value('sambaHomePath')) {
380 push(@dels, 'sambaHomePath' => []);
381 } elsif ($tmp ne "") {
382 push(@mods, 'sambaHomePath' => $tmp);
383 }
384 } else {
385 print "User $user is not a samba user\n";
386 }
387}
388
389my $_sambaHomeDrive;
390if (defined($tmp = $Options{'D'})) {
391 if ($samba == 1) {
392 if ($tmp eq "" and defined $user_entry->get_value('sambaHomeDrive')) {
393 push(@dels, 'sambaHomeDrive' => []);
394 } elsif ($tmp ne "") {
395 $tmp = $tmp.":" unless ($tmp =~ /:/);
396 push(@mods, 'sambaHomeDrive' => $tmp);
397 }
398 } else {
399 print "User $user is not a samba user\n";
400 }
401}
402
403if (defined($tmp = $Options{'E'})) {
404 if ($samba == 1) {
405 if ($tmp eq "" and defined $user_entry->get_value('sambaLogonScript')) {
406 push(@dels, 'sambaLogonScript' => []);
407 } elsif ($tmp ne "") {
408 push(@mods, 'sambaLogonScript' => $tmp);
409 }
410 } else {
411 print "User $user is not a samba user\n";
412 }
413}
414
415if (defined($tmp = $Options{'F'})) {
416 if ($samba == 1) {
417 if ($tmp eq "" and defined $user_entry->get_value('sambaProfilePath')) {
418 push(@dels, 'sambaProfilePath' => []);
419 } elsif ($tmp ne "") {
420 push(@mods, 'sambaProfilePath' => $tmp);
421 }
422 } else {
423 print "User $user is not a samba user\n";
424 }
425}
426
427if ($samba == 1 and (defined $Options{'H'} or defined $Options{'I'} or defined $Options{'J'})) {
428 my $_sambaAcctFlags;
429 if (defined($tmp = $Options{'H'})) {
430 #$tmp =~ s/\\/\\\\/g;
431 $_sambaAcctFlags=$tmp;
432 } else {
433 # I or J
434 my $flags;
435 $flags = $user_entry->get_value('sambaAcctFlags');
436
437 if (defined($tmp = $Options{'I'})) {
438 if ( !($flags =~ /D/) ) {
439 my $letters;
440 if ($flags =~ /(\w+)/) {
441 $letters = $1;
442 }
443 $_sambaAcctFlags="\[D$letters\]";
444 }
445 } elsif (defined($tmp = $Options{'J'})) {
446 if ( $flags =~ /D/ ) {
447 my $letters;
448 if ($flags =~ /(\w+)/) {
449 $letters = $1;
450 }
451 $letters =~ s/D//;
452 $_sambaAcctFlags="\[$letters\]";
453 }
454 }
455 }
456
457
458 if ($_sambaAcctFlags and "$_sambaAcctFlags" ne '') {
459 push(@mods, 'sambaAcctFlags' => $_sambaAcctFlags);
460 }
461
462} elsif (!$samba == 1 and (defined $Options{'H'} or defined $Options{'I'} or defined $Options{'J'})) {
463 print "User $user is not a samba user\n";
464}
465
466
467# apply changes
468my $modify = $ldap_master->modify ( "$dn",
469 'replace' => { @mods }
470 );
471$modify->code && warn "failed to modify entry: ", $modify->error ;
472
473# we can delete only if @dels is not empty: we check the number of elements
474my $nb_to_del=scalar(@dels);
475if ($nb_to_del != 0) {
476 $modify = $ldap_master->modify ( "$dn",
477 'delete' => { @dels }
478 );
479 $modify->code && warn "failed to modify entry: ", $modify->error ;
480}
481# take down session
482$ldap_master->unbind;
483
484if (defined(my $new_user= $Options{'r'})) {
485 my $ldap_master=connect_ldap_master();
486 chomp($new_user);
487 # read eventual new user entry
488 my $new_user_entry = read_user_entry($new_user);
489 if (defined($new_user_entry)) {
490 print "$0: user $new_user already exists, cannot rename\n";
491 exit (1);
492 }
493 my $modify = $ldap_master->moddn (
494 "uid=$user,$config{usersdn}",
495 newrdn => "uid=$new_user",
496 deleteoldrdn => "1",
497 newsuperior => "$config{usersdn}"
498 );
499 $modify->code && die "failed to change dn", $modify->error;
500
501 # change cn, sn attributes
502 my $user_entry = read_user_entry($new_user);
503 my $dn= $user_entry->dn();
504 my @mods;
505 push(@mods, 'sn' => $new_user);
506 push(@mods, 'cn' => $new_user);
507 $modify = $ldap_master->modify ("$dn",
508 changes => [
509 'replace' => [ @mods ]
510 ]
511 );
512 $modify->code && warn "failed to change cn and sn attributes: ", $modify->error;
513
514 # changing username in groups
515 my @groups = &find_groups_of($user);
516 foreach my $gname (@groups) {
517 if ($gname ne "") {
518 my $dn_line = get_group_dn($gname);
519 my $dn = get_dn_from_line("$dn_line");
520 print "updating group $gname\n";
521 $modify = $ldap_master->modify("$dn",
522 changes => [
523 'delete' => [memberUid => $user],
524 'add' => [memberUid => $new_user]
525 ]);
526 $modify->code && warn "failed to change cn and sn attributes: ", $modify->error;
527 }
528 }
529 $ldap_master->unbind;
530}
531
532$nscd_status = system "/etc/init.d/nscd status >/dev/null 2>&1";
533
534if ($nscd_status == 0) {
535 system "/etc/init.d/nscd restart > /dev/null 2>&1";
536}
537
538if (defined($Options{'P'})) {
539 exec "$RealBin/smbldap-passwd $user"
540}
541
542
543############################################################
544
545=head1 NAME
546
547smbldap-usermod - Modify a user account
548
549=head1 SYNOPSIS
550
551smbldap-usermod [-a] [-c comment] [-d home_dir] [-e expiration_date] [-g initial_group] [-l login_name] [-p passwd] [-s shell] [-u uid [ -o]] [-x] [-A canchange] [-B mustchange] [-C smbhome] [-D homedrive] [-E scriptpath] [-F profilepath] [-G group[,...]] [-H acctflags] [-N canonical_name] [-S surname] [-P] login
552
553=head1 DESCRIPTION
554
555The smbldap-usermod command modifies the system account files to reflect the changes that are specified on the command line. The options which apply to the usermod command are
556
557-a
558 Add the sambaSAMAccount objectclass to the specified user account. This allow the user to become a samba user.
559
560-c comment
561 The new value of the user's comment field (gecos).
562
563-d home_dir
564 The user's new login directory.
565
566-e expiration_date
567 Set the expiration date for the user account. This only affect samba account. The date must be in the following format : YYYY-MM-DD HH:MM:SS. This option call the external 'date' command to set calculate the number of seconds from Junary 1 1970 to the specified date.
568
569-g initial_group
570 The group name or number of the user's new initial login group. The group name must exist. A group number must refer to an already existing group. The default group number is 1.
571
572-G group,[...]
573 A list of supplementary groups which the user is also a member of. Each group is separated from the next by a comma, with no intervening whitespace. The groups are subject to the same restrictions as the group given with the -g option. If the user is currently a member of a group which is not listed, the user will be removed from the group
574
575-l login_name
576 The name of the user will be changed from login to login_name. Nothing else is changed. In particular, the user's home directory name should probably be changed to reflect the new login name.
577
578-s shell
579 The name of the user's new login shell. Setting this field to blank causes the system to select the default login shell.
580
581-u uid
582 The numerical value of the user's ID. This value must be unique, unless the -o option is used. The value must be non negative. Any files which the user owns and which are located in the directory tree rooted at the user's home directory will have the file user ID changed automatically. Files outside of the user's home directory must be altered manually.
583
584-r new_user
585 Allow to rename a user. This option will update the cn, sn and dn attribute for the user. You can
586 also update others attributes using the corresponding script options.
587
588-x
589 Creates rid and primaryGroupID in hex instead of decimal (for Samba 2.2.2 unpatched only - higher versions always use decimal)
590
591-A
592 can change password ? 0 if no, 1 if yes
593
594-B
595 must change password ? 0 if no, 1 if yes
596
597-C
598 sambaHomePath (SMB home share, like '\\\\PDC-SRV\\homes')
599
600-D
601 sambaHomeDrive (letter associated with home share, like 'H:')
602
603-E
604 sambaLogonScript, relative to the [netlogon] share (DOS script to execute on login, like 'foo.bat')
605
606-F
607 sambaProfilePath (profile directory, like '\\\\PDC-SRV\\profiles\\foo')
608
609-H
610 sambaAcctFlags, spaces and trailing bracket are ignored (samba account control bits like '[NDHTUMWSLKI]')
611
612-I
613 disable user. Can't be used with -H or -J
614
615-J
616 enable user. Can't be used with -H or -I
617
618-N
619 set the canonical name (attribut cn)
620
621-S
622 Set the surname
623
624-P
625 End by invoking smbldap-passwd to change the user password (both unix and samba passwords)
626
627=head1 SEE ALSO
628
629 usermod(1)
630
631=cut
632
633#'
Note: See TracBrowser for help on using the repository browser.