3
# $Id: smbldap-usermod,v 1.11 2005/01/08 12:04:45 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 ( $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)