3 # $Id: smbldap-usermod,v 1.13 2005/05/27 14:21:00 jtournier Exp $
5 # This code was developped by IDEALX (http://IDEALX.org/) and
6 # contributors (their names can be found in the CONTRIBUTORS file).
8 # Copyright (C) 2001-2002 IDEALX
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.
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.
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,
25 # Purpose of smbldap-usermod : user (posix,shadow,samba) modification
29 use FindBin qw($RealBin);
39 my $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);
40 if ( (!$ok) || (@ARGV < 1) || ($Options{'?'}) || ($Options{'h'}) ) {
42 print "Usage: $0 [-awmugdsckABCDEFGHIPSMT?h] username\n";
43 print "Available options are:\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";
50 print " -o uid can be non unique\n";
52 print " -G supplementary groups (comma separated)\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";
76 print "You must be root to modify an user\n";
79 # Read only first @ARGV
82 # Let's connect to the directory first
83 my $ldap_master=connect_ldap_master();
86 my $user_entry = read_user_entry($user);
87 if (!defined($user_entry)) {
88 print "$0: user $user doesn't exist\n";
93 if (grep ($_ =~ /^sambaSamAccount$/i, $user_entry->get_value('objectClass'))) {
97 # get the dn of the user
98 my $dn= $user_entry->dn();
103 if (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;
114 my $modify = $ldap_master->modify ( "$dn",
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"],
128 $modify->code && warn "failed to modify entry: ", $modify->error ;
135 if (defined($tmp = $Options{'u'})) {
136 if (defined($Options{'o'})) {
137 $nscd_status = system "/etc/init.d/nscd status >/dev/null 2>&1";
139 if ($nscd_status == 0) {
140 system "/etc/init.d/nscd stop > /dev/null 2>&1";
143 if (getpwuid($tmp)) {
144 if ($nscd_status == 0) {
145 system "/etc/init.d/nscd start > /dev/null 2>&1";
148 print "$0: uid number $tmp exists\n";
151 if ($nscd_status == 0) {
152 system "/etc/init.d/nscd start > /dev/null 2>&1";
156 push(@mods, 'uidNumber', $tmp);
157 $_userUidNumber = $tmp;
159 # as rid we use 2 * uid + 1000
160 my $_userRid = 2 * $_userUidNumber + 1000;
161 if (defined($Options{'x'})) {
162 $_userRid= sprint("%x", $_userRid);
164 push(@mods, 'sambaSID', $config{SID}.'-'.$_userRid);
172 if (defined($tmp = $Options{'g'})) {
173 $_userGidNumber = parse_group($tmp);
174 if ($_userGidNumber < 0) {
175 print "$0: group $tmp doesn't exist\n";
178 push(@mods, 'gidNumber', $_userGidNumber);
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";
187 push(@mods, 'sambaPrimaryGroupSid', $_userGroupSID);
192 if (defined($tmp = $Options{'s'})) {
193 push(@mods, 'loginShell' => $tmp);
197 if (defined($tmp = $Options{'c'})) {
198 push(@mods, 'gecos' => $tmp,
199 'description' => $tmp);
201 push(@mods, 'displayName' => $tmp);
205 if (defined($tmp = $Options{'d'})) {
206 push(@mods, 'homeDirectory' => $tmp);
209 if (defined($tmp = $Options{'N'})) {
210 push(@mods, 'cn' => $tmp);
213 if (defined($tmp = $Options{'S'})) {
214 push(@mods, 'sn' => $tmp);
218 if ($tmp= $Options{'M'}) {
219 # action si + or - for adding or deleting an entry
221 if ($tmp =~ s/^([+-])+\s*//) {
224 my @userMailLocal = &split_arg_comma($tmp);
226 foreach my $m (@userMailLocal) {
227 my $domain = $config{mailDomain};
228 if ($m =~ /^(.+)@/) {
230 # mailLocalAddress contains only the first part
233 push(@mail, $m.($domain ? '@'.$domain : ''));
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);
249 push(@mods, 'mailLocalAddress', [ @userMailLocal ]);
250 push(@mods, 'mail' => [ @mail ]);
254 if ($tmp= $Options{'T'}) {
257 # action si + or - for adding or deleting an entry
258 if ($tmp =~ s/^([+-])+\s*//) {
261 my @userMailTo = &split_arg_comma($tmp);
263 @old = $user_entry->get_value('mailRoutingAddress');
265 if ($action eq '+') {
266 @userMailTo = &list_union(\@old, \@userMailTo);
267 } elsif ($action eq '-') {
268 @userMailTo = &list_minus(\@old, \@userMailTo);
270 push(@mods, 'mailRoutingAddress', [ @userMailTo ]);
274 my @objectclass = $user_entry->get_value('objectClass');
275 if (! grep ($_ =~ /^inetLocalMailRecipient$/i, @objectclass)) {
276 push(@mods, 'objectClass' => [ @objectclass, 'inetLocalMailRecipient' ]);
281 if (defined($tmp = $Options{'G'})) {
283 if ($tmp =~ s/^([+-])+\s*//) {
286 if ($action eq '-') {
287 # remove user from specified groups
288 foreach my $gname (&split_arg_comma($tmp)) {
289 group_remove_member($gname, $user);
292 if ($action ne '+') {
293 my @old = &find_groups_of($user);
294 # remove user from old groups
295 foreach my $gname (@old) {
297 group_remove_member($gname, $user);
301 # add user to new groups
302 add_grouplist_user($tmp, $user);
307 # A : sambaPwdCanChange
308 # B : sambaPwdMustChange
311 # E : sambaLogonScript
312 # F : sambaProfilePath
316 my $winmagic = 2147483647;
318 $samba = is_samba_user($user);
320 if (defined($tmp = $Options{'e'})) {
322 my $kickoffTime=`date --date='$tmp' +%s`;
324 push(@mods, 'sambakickoffTime' => $kickoffTime);
326 print "User $user is not a samba user\n";
330 my $_sambaPwdCanChange;
331 if (defined($tmp = $Options{'A'})) {
333 $attr = "sambaPwdCanChange";
335 $_sambaPwdCanChange=0;
337 $_sambaPwdCanChange=$winmagic;
339 push(@mods, 'sambaPwdCanChange' => $_sambaPwdCanChange);
341 print "User $user is not a samba user\n";
345 my $_sambaPwdMustChange;
346 if (defined($tmp = $Options{'B'})) {
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
354 my $flags = $user_entry->get_value('sambaAcctFlags');
355 if ( defined $flags and $flags =~ /X/ ) {
357 if ($flags =~ /(\w+)/) {
361 $_sambaAcctFlags="\[$letters\]";
362 push(@mods, 'sambaAcctFlags' => $_sambaAcctFlags);
364 my $_sambaPwdLastSet = $user_entry->get_value('sambaPwdLastSet');
365 if ($_sambaPwdLastSet == 0) {
366 push(@mods, 'sambaPwdLastSet' => $winmagic);
369 $_sambaPwdMustChange=$winmagic;
371 push(@mods, 'sambaPwdMustChange' => $_sambaPwdMustChange);
373 print "User $user is not a samba user\n";
377 if (defined($tmp = $Options{'C'})) {
379 if ($tmp eq "" and defined $user_entry->get_value('sambaHomePath')) {
380 push(@dels, 'sambaHomePath' => []);
381 } elsif ($tmp ne "") {
382 push(@mods, 'sambaHomePath' => $tmp);
385 print "User $user is not a samba user\n";
390 if (defined($tmp = $Options{'D'})) {
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);
399 print "User $user is not a samba user\n";
403 if (defined($tmp = $Options{'E'})) {
405 if ($tmp eq "" and defined $user_entry->get_value('sambaLogonScript')) {
406 push(@dels, 'sambaLogonScript' => []);
407 } elsif ($tmp ne "") {
408 push(@mods, 'sambaLogonScript' => $tmp);
411 print "User $user is not a samba user\n";
415 if (defined($tmp = $Options{'F'})) {
417 if ($tmp eq "" and defined $user_entry->get_value('sambaProfilePath')) {
418 push(@dels, 'sambaProfilePath' => []);
419 } elsif ($tmp ne "") {
420 push(@mods, 'sambaProfilePath' => $tmp);
423 print "User $user is not a samba user\n";
427 if ($samba == 1 and (defined $Options{'H'} or defined $Options{'I'} or defined $Options{'J'})) {
429 if (defined($tmp = $Options{'H'})) {
430 #$tmp =~ s/\\/\\\\/g;
431 $_sambaAcctFlags=$tmp;
435 $flags = $user_entry->get_value('sambaAcctFlags');
437 if (defined($tmp = $Options{'I'})) {
438 if ( !($flags =~ /D/) ) {
440 if ($flags =~ /(\w+)/) {
443 $_sambaAcctFlags="\[D$letters\]";
445 } elsif (defined($tmp = $Options{'J'})) {
446 if ( $flags =~ /D/ ) {
448 if ($flags =~ /(\w+)/) {
452 $_sambaAcctFlags="\[$letters\]";
458 if ("$_sambaAcctFlags" ne '') {
459 push(@mods, 'sambaAcctFlags' => $_sambaAcctFlags);
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";
468 my $modify = $ldap_master->modify ( "$dn",
469 'replace' => { @mods }
471 $modify->code && warn "failed to modify entry: ", $modify->error ;
473 # we can delete only if @dels is not empty: we check the number of elements
474 my $nb_to_del=scalar(@dels);
475 if ($nb_to_del != 0) {
476 $modify = $ldap_master->modify ( "$dn",
477 'delete' => { @dels }
479 $modify->code && warn "failed to modify entry: ", $modify->error ;
482 $ldap_master->unbind;
484 if (defined(my $new_user= $Options{'r'})) {
485 my $ldap_master=connect_ldap_master();
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";
493 my $modify = $ldap_master->moddn (
494 "uid=$user,$config{usersdn}",
495 newrdn => "uid=$new_user",
497 newsuperior => "$config{usersdn}"
499 $modify->code && die "failed to change dn", $modify->error;
501 # change cn, sn attributes
502 my $user_entry = read_user_entry($new_user);
503 my $dn= $user_entry->dn();
505 push(@mods, 'sn' => $new_user);
506 push(@mods, 'cn' => $new_user);
507 $modify = $ldap_master->modify ("$dn",
509 'replace' => [ @mods ]
512 $modify->code && warn "failed to change cn and sn attributes: ", $modify->error;
514 # changing username in groups
515 my @groups = &find_groups_of($user);
516 foreach my $gname (@groups) {
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",
523 'delete' => [memberUid => $user],
524 'add' => [memberUid => $new_user]
526 $modify->code && warn "failed to change cn and sn attributes: ", $modify->error;
529 $ldap_master->unbind;
532 $nscd_status = system "/etc/init.d/nscd status >/dev/null 2>&1";
534 if ($nscd_status == 0) {
535 system "/etc/init.d/nscd restart > /dev/null 2>&1";
538 if (defined($Options{'P'})) {
539 exec "$RealBin/smbldap-passwd $user"
543 ############################################################
547 smbldap-usermod - Modify a user account
551 smbldap-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
555 The 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
558 Add the sambaSAMAccount objectclass to the specified user account. This allow the user to become a samba user.
561 The new value of the user's comment field (gecos).
564 The user's new login directory.
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.
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.
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
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.
579 The name of the user's new login shell. Setting this field to blank causes the system to select the default login shell.
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.
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.
589 Creates rid and primaryGroupID in hex instead of decimal (for Samba 2.2.2 unpatched only - higher versions always use decimal)
592 can change password ? 0 if no, 1 if yes
595 must change password ? 0 if no, 1 if yes
598 sambaHomePath (SMB home share, like '\\\\PDC-SRV\\homes')
601 sambaHomeDrive (letter associated with home share, like 'H:')
604 sambaLogonScript, relative to the [netlogon] share (DOS script to execute on login, like 'foo.bat')
607 sambaProfilePath (profile directory, like '\\\\PDC-SRV\\profiles\\foo')
610 sambaAcctFlags, spaces and trailing bracket are ignored (samba account control bits like '[NDHTUMWSLKI]')
613 disable user. Can't be used with -H or -J
616 enable user. Can't be used with -H or -I
619 set the canonical name (attribut cn)
625 End by invoking smbldap-passwd to change the user password (both unix and samba passwords)