~ubuntu-branches/ubuntu/raring/smbldap-tools/raring-proposed

« back to all changes in this revision

Viewing changes to smbldap-usermod

  • Committer: Package Import Robot
  • Author(s): Leo Iannacone
  • Date: 2011-09-27 18:05:13 UTC
  • mfrom: (6.1.2 sid)
  • Revision ID: package-import@ubuntu.com-20110927180513-2b9g9u4wkqeh4kxn
Tags: 0.9.7-1ubuntu1
* Merge from debian unstable (LP: #889308).  Remaining changes:
  - Apply patch from rdratlos to resolve being unable to join a Windows
    7 or Windows 2008 machine to a Samba domain due to the use of cached
    nss credentials. (LP: #814898)
  - 0020_original_doc_html_index.patch: Add index html file.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#!/usr/bin/perl -w
2
 
 
3
 
#  This code was developped by Jerome Tournier (jtournier@gmail.com) and
4
 
#  contributors (their names can be found in the CONTRIBUTORS file).
5
 
 
6
 
#  This was first contributed by IDEALX (http://www.opentrust.com/)
7
 
 
8
 
#  This program is free software; you can redistribute it and/or
9
 
#  modify it under the terms of the GNU General Public License
10
 
#  as published by the Free Software Foundation; either version 2
11
 
#  of the License, or (at your option) any later version.
12
 
#
13
 
#  This program is distributed in the hope that it will be useful,
14
 
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 
#  GNU General Public License for more details.
17
 
#
18
 
#  You should have received a copy of the GNU General Public License
19
 
#  along with this program; if not, write to the Free Software
20
 
#  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
21
 
#  USA.
22
 
 
23
 
# Purpose of smbldap-usermod : user (posix,shadow,samba) modification
24
 
 
25
 
use strict;
26
 
use FindBin;
27
 
use FindBin qw($RealBin);
28
 
use lib "$RealBin/";
29
 
use smbldap_tools;
30
 
use Time::Local;
31
 
#####################
32
 
 
33
 
use Getopt::Std;
34
 
use Getopt::Long;
35
 
my %Options;
36
 
my $nscd_status;
37
 
 
38
 
Getopt::Long::Configure('bundling');
39
 
my $ok = GetOptions(
40
 
    "A|sambaPwdCanChange=s"  => \$Options{A},
41
 
    "B|sambaPwdMustChange=s" => \$Options{B},
42
 
    "C|sambaHomePath=s"      => \$Options{C},
43
 
    "D|sambaHomeDrive=s"     => \$Options{D},
44
 
    "E|sambaLogonScript=s"   => \$Options{E},
45
 
    "F|sambaProfilePath=s"   => \$Options{F},
46
 
    "G|group=s"              => \$Options{G},
47
 
    "H|sambaAcctFlags=s"     => \$Options{H},
48
 
    "I|sambaDisable"         => \$Options{I},
49
 
    "J|sambaEnable"          => \$Options{J},
50
 
    "L|shadowLock"           => \$Options{L},
51
 
    "M|mailAddresses=s"      => \$Options{M},
52
 
    "N|givenName=s"          => \$Options{N},
53
 
    "P=s"                    => \$Options{P},
54
 
    "U|shadowUnlock"         => \$Options{U},
55
 
    "S|surname=s"            => \$Options{S},
56
 
    "T|mailToAddress=s"      => \$Options{T},
57
 
    "Z|attr=s"               => \$Options{Z},
58
 
    "a|addsambaSAMAccount"   => \$Options{a},
59
 
    "c|gecos=s"              => \$Options{c},
60
 
    "d|homedir=s"            => \$Options{d},
61
 
    "e|expire=s"             => \$Options{e},
62
 
    "sambaExpire=s"          => \$Options{sambaExpire},
63
 
    "g|gid=s"                => \$Options{g},
64
 
    "h|help"                 => \$Options{h},
65
 
    "o|canBeNotUnique"       => \$Options{o},
66
 
    "r|rename=s"             => \$Options{r},
67
 
    "s|shell=s"              => \$Options{s},
68
 
    "shadowExpire=s"         => \$Options{shadowExpire},
69
 
    "shadowMax=s"            => \$Options{shadowMax},
70
 
    "shadowMin=s"            => \$Options{shadowMin},
71
 
    "shadowInactive=s"       => \$Options{shadowInactive},
72
 
    "shadowWarning=s"        => \$Options{shadowWarning},
73
 
    "u|uid=s"                => \$Options{u}
74
 
);
75
 
 
76
 
#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);
77
 
 
78
 
if ( ( !$ok ) || ( @ARGV < 1 ) || ( $Options{'h'} ) ) {
79
 
    print_banner;
80
 
    print "Usage: $0 [options] username\n\n";
81
 
 
82
 
    print "Available UNIX options are:\n";
83
 
    print "  -c|--gecos <gecos>           gecos\n";
84
 
    print "  -d|--homedir <dir>           home directory\n";
85
 
    print "  -r|--rename <username>       username\n";
86
 
    print "  -u|--uid <uidNumber>         uid\n";
87
 
    print "  -o|--canBeNotUnique          uid can be non unique\n";
88
 
    print "  -g|--gid <gidNumber          gid\n";
89
 
    print
90
 
      "  -G|--group [+-]<grp1,...>    supplementary groups (comma separated)\n";
91
 
    print "  -s|--shell <shell>           shell\n";
92
 
    print "  -N|--givenName <name>        given name (first name)\n";
93
 
    print "  -S|--surname <suname>        surname (family name)\n";
94
 
    print "  -P                           ends by invoking smbldap-passwd\n";
95
 
    print "  -M|--mailAddresses <mail,>   mailAddresses (comma seperated)\n";
96
 
    print
97
 
"  -T|--mailToAddress <mail,>   mailToAddress (forward address) (comma seperated)\n";
98
 
    print
99
 
"  -e|--expire <date>           Sets both shadow and samba expiration date: like \"YYYY-MM-DD(HH:MM:SS)\", or \"yYmMdD\" to extand y year,m months and d days\n";
100
 
    print
101
 
"  --shadowExpire <date/n>      Shadow expiration date (like \"YYYY-MM-DD\") or 'n' days from today\n";
102
 
    print
103
 
"  --shadowMax <n>              User must change the password, at least, every 'n' days\n";
104
 
    print
105
 
"  --shadowMin <n>              user must wait 'n' days once the password has changed before changing it again\n";
106
 
    print
107
 
"  --shadowInactive <n>         number of days of inactivity allowed for the specified user\n";
108
 
    print
109
 
"  --shadowWarning <n>          User is warned that the password must be changed four days before the password expires\n";
110
 
    print "  -L|--shadowLock              lock unix user's password\n";
111
 
    print "  -U|--shadowUnlock            unlock unix user's password\n";
112
 
    print
113
 
"  -Z                           add custom attributes, as name=value pairs comma separated\n";
114
 
    print "\n";
115
 
    print "Available SAMBA options are:\n";
116
 
    print "  -a|--addsambaSAMAccount        add sambaSAMAccount objectclass\n";
117
 
    print
118
 
"  --sambaExpire <date>           expire date (\"YYYY-MM-DD HH:MM:SS\")\n";
119
 
    print
120
 
"  -A|--sambaPwdCanChange         can change password ? 0 if no, 1 if yes\n";
121
 
    print
122
 
"  -B|--sambaPwdMustChange        must change password ? 0 if no, 1 if yes\n";
123
 
    print
124
 
"  -C|--sambaHomePath <dir>       sambaHomePath (SMB home share, like '\\\\PDC-SRV\\homes')\n";
125
 
    print
126
 
"  -D|--sambaHomeDrive <drive>    sambaHomeDrive (letter associated with home share, like 'H:')\n";
127
 
    print
128
 
"  -E|--sambaLogonScript <script> sambaLogonScript (DOS script to execute on login)\n";
129
 
    print
130
 
"  -F|--sambaProfilePath <path>   sambaProfilePath (profile directory, like '\\\\PDC-SRV\\profiles\\foo')\n";
131
 
    print
132
 
"  -H|--sambaAcctFlags <flags>    sambaAcctFlags (samba account control bits like '[NDHTUMWSLKI]')\n";
133
 
    print
134
 
"  -I|--sambaDisable              disable an user. Can't be used with -H or -J\n";
135
 
    print
136
 
"  -J|--sambaEnable               enable an user. Can't be used with -H or -I\n";
137
 
    print "  -h|--help                      show this help message\n";
138
 
    exit(1);
139
 
}
140
 
 
141
 
if ( $< != 0 ) {
142
 
    print "You must be root to modify an user\n";
143
 
    exit(1);
144
 
}
145
 
 
146
 
# Read only first @ARGV
147
 
my $user = $ARGV[0];
148
 
 
149
 
# Let's connect to the directory first
150
 
my $ldap_master = connect_ldap_master();
151
 
 
152
 
# Read user data
153
 
my $user_entry = read_user_entry($user);
154
 
if ( !defined($user_entry) ) {
155
 
    print "$0: user $user doesn't exist\n";
156
 
    exit(1);
157
 
}
158
 
 
159
 
my $samba = is_samba_user($user);
160
 
 
161
 
# get the dn of the user
162
 
my $dn = $user_entry->dn();
163
 
 
164
 
my $tmp;
165
 
my @mods;
166
 
my @dels;
167
 
if ( defined( $tmp = $Options{'a'} ) ) {
168
 
    if ($samba) {
169
 
        print "Error: Account for user $user already _is_ a Samba account!\n",
170
 
          "Omit option -a!\n";
171
 
        exit(1);
172
 
    }
173
 
 
174
 
    # Let's connect to the directory first
175
 
    my $winmagic         = 2147483647;
176
 
    my $valpwdcanchange  = 0;
177
 
    my $valpwdmustchange = $winmagic;
178
 
    my $valpwdlastset    = 0;
179
 
    my $valacctflags     = "[UX]";
180
 
    my $user_entry       = read_user_entry($user);
181
 
    my $uidNumber        = $user_entry->get_value('uidNumber');
182
 
    my $userRid          = 2 * $uidNumber + 1000;
183
 
 
184
 
    # apply changes
185
 
    my $modify = $ldap_master->modify(
186
 
        "$dn",
187
 
        changes => [
188
 
            add => [ objectClass        => 'sambaSAMAccount' ],
189
 
            add => [ sambaPwdLastSet    => "$valpwdlastset" ],
190
 
            add => [ sambaLogonTime     => '0' ],
191
 
            add => [ sambaLogoffTime    => '2147483647' ],
192
 
            add => [ sambaKickoffTime   => '2147483647' ],
193
 
            add => [ sambaPwdCanChange  => "$valpwdcanchange" ],
194
 
            add => [ sambaPwdMustChange => "$valpwdmustchange" ],
195
 
            add => [ sambaSID           => "$config{SID}-$userRid" ],
196
 
            add => [ sambaAcctFlags     => "$valacctflags" ],
197
 
        ]
198
 
    );
199
 
    if ( $modify->code ) {
200
 
        warn "failed to modify entry: ", $modify->error;
201
 
        exit 1;
202
 
    }
203
 
 
204
 
    # when adding samba attributes, try to set samba primary group as well.
205
 
    my $group_entry =
206
 
      read_group_entry_gid( $user_entry->get_value('gidNumber') );
207
 
 
208
 
    # override group if new group id sould be set with this call as well
209
 
    $group_entry = read_group_entry_gid( $Options{'g'} ) if $Options{'g'};
210
 
    my $userGroupSID = $group_entry->get_value('sambaSID');
211
 
    if ($userGroupSID) {
212
 
        my $modify_grpSID = $ldap_master->modify( "$dn",
213
 
            changes => [ add => [ sambaPrimaryGroupSID => "$userGroupSID" ], ]
214
 
        );
215
 
 
216
 
        if ( $modify_grpSID->code ) {
217
 
            warn "failed to modify entry: ", $modify_grpSID->error;
218
 
            exit 1;
219
 
        }
220
 
    }
221
 
    else {    # no reason to abort imho
222
 
        print
223
 
"Warning: sambaPrimaryGroupSID could not be set beacuse group of user $user is not a mapped Domain group!\n",
224
 
"To get a list of groups mapped to Domain groups, use \"net groupmap list\" on a Domain member machine.\n";
225
 
    }
226
 
 
227
 
# now it is a samba account. this flag is needed here if someone uses i. e. "-a -g newgroupid".
228
 
# if not set here, the sambaPrimaryGroupSID value would not be updated
229
 
    $samba = 1;
230
 
 
231
 
}
232
 
 
233
 
# Process options
234
 
my $changed_uid;
235
 
my $_userUidNumber;
236
 
my $_userRid;
237
 
 
238
 
if ( defined( $tmp = $Options{'u'} ) ) {
239
 
    if ( !defined( $Options{'o'} ) ) {
240
 
        $nscd_status = system "/etc/init.d/nscd status >/dev/null 2>&1";
241
 
        if ( $nscd_status == 0 ) {
242
 
            system "/etc/init.d/nscd stop > /dev/null 2>&1";
243
 
        }
244
 
 
245
 
        if ( getpwuid($tmp) ) {
246
 
            if ( $nscd_status == 0 ) {
247
 
                system "/etc/init.d/nscd start > /dev/null 2>&1";
248
 
            }
249
 
 
250
 
            print "$0: uid number $tmp exists\n";
251
 
            exit(6);
252
 
        }
253
 
        if ( $nscd_status == 0 ) {
254
 
            system "/etc/init.d/nscd start > /dev/null 2>&1";
255
 
        }
256
 
    }
257
 
 
258
 
    push( @mods, 'uidNumber', $tmp );
259
 
    $_userUidNumber = $tmp;
260
 
    if ($samba) {
261
 
 
262
 
        # as rid we use 2 * uid + 1000
263
 
        my $_userRid = 2 * $_userUidNumber + 1000;
264
 
        if ( defined( $Options{'x'} ) ) {
265
 
            $_userRid = sprint( "%x", $_userRid );
266
 
        }
267
 
        push( @mods, 'sambaSID', $config{SID} . '-' . $_userRid );
268
 
    }
269
 
    $changed_uid = 1;
270
 
}
271
 
 
272
 
my $changed_gid;
273
 
my $_userGidNumber;
274
 
my $_userGroupSID;
275
 
if ( defined( $tmp = $Options{'g'} ) ) {
276
 
    $_userGidNumber = parse_group($tmp);
277
 
    if ( $_userGidNumber < 0 ) {
278
 
        print "$0: group $tmp doesn't exist\n";
279
 
        exit(6);
280
 
    }
281
 
    push( @mods, 'gidNumber', $_userGidNumber );
282
 
    if ($samba) {
283
 
 
284
 
        # as grouprid we use the sambaSID attribute's value of the group
285
 
        my $group_entry   = read_group_entry_gid($_userGidNumber);
286
 
        my $_userGroupSID = $group_entry->get_value('sambaSID');
287
 
        unless ($_userGroupSID) {
288
 
            print
289
 
"Error: sambaPrimaryGroupSid could not be set (sambaSID for group $_userGidNumber does not exist)\n";
290
 
            exit(7);
291
 
        }
292
 
        push( @mods, 'sambaPrimaryGroupSid', $_userGroupSID );
293
 
    }
294
 
    $changed_gid = 1;
295
 
}
296
 
 
297
 
if ( defined( $tmp = $Options{'s'} ) ) {
298
 
    push( @mods, 'loginShell' => $tmp );
299
 
}
300
 
 
301
 
if ( defined( $tmp = $Options{'c'} ) ) {
302
 
    push(
303
 
        @mods,
304
 
        'gecos'       => $tmp,
305
 
        'description' => $tmp
306
 
    );
307
 
}
308
 
 
309
 
if ( defined( $tmp = $Options{'d'} ) ) {
310
 
    push( @mods, 'homeDirectory' => $tmp );
311
 
}
312
 
 
313
 
# RFC 2256 & RFC 2798
314
 
# sn: family name (option S)             # RFC 2256: family name of a person.
315
 
# givenName: prenom (option N)           # RFC 2256: part of a person's name which is not their surname nor middle name.
316
 
# cn: person's full name                 # RFC 2256: person's full name.
317
 
# displayName: perferably displayed name # RFC 2798: preferred name of a person to be used when displaying entries.
318
 
 
319
 
#givenname is the forename of a person (not famiy name) => http://en.wikipedia.org/wiki/Given_name
320
 
#surname (or sn) is the familiy name => http://en.wikipedia.org/wiki/Surname
321
 
# my surname (or sn): Tournier
322
 
# my givenname: Jerome
323
 
 
324
 
if ( defined( $tmp = $Options{'N'} ) ) {
325
 
    push( @mods, 'givenName' => utf8Encode($tmp) );
326
 
}
327
 
 
328
 
if ( defined( $tmp = $Options{'S'} ) ) {
329
 
    push( @mods, 'sn' => utf8Encode($tmp) );
330
 
}
331
 
 
332
 
my $cn;
333
 
if ( $Options{'N'} or $Options{'S'} or $Options{'a'} ) {
334
 
    $Options{'N'} = $user_entry->get_value('givenName') unless $Options{'N'};
335
 
    $Options{'S'} = $user_entry->get_value('sn')        unless $Options{'S'};
336
 
 
337
 
# if givenName eq sn eq username (default of smbldap-useradd), cn and displayName would
338
 
# be "username username". So we just append surname if its not default
339
 
# (there may be the very very special case of an user where those three values _are_ equal)
340
 
    $cn = "$Options{'N'}";
341
 
    $cn .= " " . $Options{'S'}
342
 
      unless ( $Options{'S'} eq $Options{'N'} and $Options{'N'} eq $user );
343
 
    my $push_val = utf8Encode($cn);
344
 
    push( @mods, 'cn' => $push_val );
345
 
 
346
 
    # set displayName for Samba account
347
 
    if ($samba) {
348
 
        push( @mods, 'displayName' => $push_val );
349
 
    }
350
 
}
351
 
 
352
 
if ( defined $Options{'e'} ) {
353
 
    if ( !defined $Options{'shadowExpire'} ) {
354
 
        $Options{'shadowExpire'} = $Options{'e'};
355
 
    }
356
 
    if ( !defined $Options{'sambaExpire'} ) {
357
 
        $Options{'sambaExpire'} = $Options{'e'};
358
 
    }
359
 
}
360
 
 
361
 
sub parse_date_to_unix {
362
 
    my ($date) = @_;
363
 
 
364
 
    if ( $date =~ /(\d\d\d\d)-(\d?\d)-(\d?\d)(\s+(\d?\d):(\d\d):(\d\d))?/ ) {
365
 
        my $localtime;
366
 
        if ( defined $5 and defined $6 and defined $7 ) {
367
 
            $localtime = timelocal( $7, $6, $5, $3, $2 - 1, $1 );
368
 
        }
369
 
        else {
370
 
            $localtime = timelocal( 0, 0, 2, $3, $2 - 1, $1 );
371
 
        }
372
 
        my $shadowExpire = int($localtime);
373
 
        $shadowExpire = sprintf( "%d", $shadowExpire );
374
 
        return $shadowExpire;
375
 
    }
376
 
    else {
377
 
        my $daysAdd = 0;
378
 
 
379
 
        if ( $date =~ /(\d+)y/ ) {
380
 
            $daysAdd += 365.3 * $1;
381
 
        }
382
 
 
383
 
        if ( $date =~ /(\d+)m/ ) {
384
 
            $daysAdd += 30.4 * $1;
385
 
        }
386
 
 
387
 
        if ( $date =~ /(\d+)([^ym]{1}|$)/ ) {
388
 
            $daysAdd += $1;
389
 
        }
390
 
 
391
 
        if ( $daysAdd > 0 ) {
392
 
            return int( time + ( $daysAdd * 24 * 3600 ) );
393
 
        }
394
 
        else {
395
 
            return -1;
396
 
        }
397
 
    }
398
 
 
399
 
    return -1;
400
 
}
401
 
 
402
 
sub parse_date_to_unix_days {
403
 
    my ($arg) = @_;
404
 
    return int( parse_date_to_unix($arg) / 86400 );
405
 
}
406
 
 
407
 
# Shadow password parameters
408
 
my $localtime = time() / 86400;
409
 
if ( defined $Options{'shadowExpire'} ) {
410
 
 
411
 
    # Unix expiration password
412
 
    my $tmp = $Options{'shadowExpire'};
413
 
    chomp($tmp);
414
 
 
415
 
    #    my $expire=`date --date='$tmp' +%s`;
416
 
    #    chomp($expire);
417
 
    #    my $shadowExpire=int($expire/86400)+1;
418
 
    # date syntax asked: YYYY-MM-DD
419
 
 
420
 
    $tmp = parse_date_to_unix_days($tmp);
421
 
    if ( $tmp != -1 ) {
422
 
        push( @mods, 'shadowExpire', $tmp );
423
 
    }
424
 
    else {
425
 
        print "Invalid format for '--shadowExpire' option.\n";
426
 
    }
427
 
}
428
 
 
429
 
if ( defined $Options{'shadowWarning'} ) {
430
 
    push( @mods, 'shadowWarning', $Options{shadowWarning} );
431
 
}
432
 
 
433
 
if ( defined $Options{'shadowMax'} ) {
434
 
    push( @mods, 'shadowMax', $Options{shadowMax} );
435
 
}
436
 
 
437
 
if ( defined $Options{'shadowMin'} ) {
438
 
    push( @mods, 'shadowMin', $Options{shadowMin} );
439
 
}
440
 
 
441
 
if ( defined $Options{'shadowInactive'} ) {
442
 
    push( @mods, 'shadowInactive', $Options{shadowInactive} );
443
 
}
444
 
 
445
 
if ( defined $Options{'L'} ) {
446
 
 
447
 
    # lock shadow account
448
 
    $tmp = $user_entry->get_value('userPassword');
449
 
    if ( !( $tmp =~ /!/ ) ) {
450
 
        $tmp =~ s/}/}!/;
451
 
    }
452
 
    push( @mods, 'userPassword' => $tmp );
453
 
}
454
 
 
455
 
if ( defined $Options{'U'} ) {
456
 
 
457
 
    # unlock shadow account
458
 
    $tmp = $user_entry->get_value('userPassword');
459
 
    if ( $tmp =~ /!/ ) {
460
 
        $tmp =~ s/}!/}/;
461
 
    }
462
 
    push( @mods, 'userPassword' => $tmp );
463
 
}
464
 
 
465
 
my $mailobj = 0;
466
 
if ( $tmp = $Options{'M'} ) {
467
 
 
468
 
    # action si + or - for adding or deleting an entry
469
 
    my $action = '';
470
 
    if ( $tmp =~ s/^([+-])+\s*// ) {
471
 
        $action = $1;
472
 
    }
473
 
    my @userMailLocal = &split_arg_comma($tmp);
474
 
    my @mail;
475
 
    foreach my $m (@userMailLocal) {
476
 
        my $domain = $config{mailDomain};
477
 
        if ( $m =~ /^(.+)@/ ) {
478
 
            push( @mail, $m );
479
 
 
480
 
            # mailLocalAddress contains only the first part
481
 
            $m = $1;
482
 
        }
483
 
        else {
484
 
            push( @mail, $m . ( $domain ? '@' . $domain : '' ) );
485
 
        }
486
 
    }
487
 
    if ($action) {
488
 
        my @old_MailLocal;
489
 
        my @old_mail;
490
 
        @old_mail      = $user_entry->get_value('mail');
491
 
        @old_MailLocal = $user_entry->get_value('mailLocalAddress');
492
 
        if ( $action eq '+' ) {
493
 
            @userMailLocal = &list_union( \@old_MailLocal, \@userMailLocal );
494
 
            @mail          = &list_union( \@old_mail,      \@mail );
495
 
        }
496
 
        elsif ( $action eq '-' ) {
497
 
            @userMailLocal = &list_minus( \@old_MailLocal, \@userMailLocal );
498
 
            @mail          = &list_minus( \@old_mail,      \@mail );
499
 
        }
500
 
    }
501
 
    push( @mods, 'mailLocalAddress', [@userMailLocal] );
502
 
    push( @mods, 'mail' => [@mail] );
503
 
    $mailobj = 1;
504
 
}
505
 
 
506
 
if ( $tmp = $Options{'T'} ) {
507
 
    my $action = '';
508
 
    my @old;
509
 
 
510
 
    # action si + or - for adding or deleting an entry
511
 
    if ( $tmp =~ s/^([+-])+\s*// ) {
512
 
        $action = $1;
513
 
    }
514
 
    my @userMailTo = &split_arg_comma($tmp);
515
 
    if ($action) {
516
 
        @old = $user_entry->get_value('mailRoutingAddress');
517
 
    }
518
 
    if ( $action eq '+' ) {
519
 
        @userMailTo = &list_union( \@old, \@userMailTo );
520
 
    }
521
 
    elsif ( $action eq '-' ) {
522
 
        @userMailTo = &list_minus( \@old, \@userMailTo );
523
 
    }
524
 
    push( @mods, 'mailRoutingAddress', [@userMailTo] );
525
 
    $mailobj = 1;
526
 
}
527
 
if ($mailobj) {
528
 
    my @objectclass = $user_entry->get_value('objectClass');
529
 
    if ( !grep ( $_ =~ /^inetLocalMailRecipient$/i, @objectclass ) ) {
530
 
        push( @mods,
531
 
            'objectClass' => [ @objectclass, 'inetLocalMailRecipient' ] );
532
 
    }
533
 
}
534
 
 
535
 
if ( defined( $tmp = $Options{'G'} ) ) {
536
 
    my $action = '';
537
 
    if ( $tmp =~ s/^([+-])+\s*// ) {
538
 
        $action = $1;
539
 
    }
540
 
    if ( $action eq '-' ) {
541
 
 
542
 
        # remove user from specified groups
543
 
        foreach my $gname ( &split_arg_comma($tmp) ) {
544
 
            group_remove_member( $gname, $user );
545
 
        }
546
 
    }
547
 
    else {
548
 
        if ( $action ne '+' ) {
549
 
            my @old = &find_groups_of($user);
550
 
 
551
 
            # remove user from old groups
552
 
            foreach my $gname (@old) {
553
 
                if ( $gname ne "" ) {
554
 
                    group_remove_member( $gname, $user );
555
 
                }
556
 
            }
557
 
        }
558
 
 
559
 
        # add user to new groups
560
 
        add_grouplist_user( $tmp, $user );
561
 
    }
562
 
}
563
 
 
564
 
#
565
 
# A : sambaPwdCanChange
566
 
# B : sambaPwdMustChange
567
 
# C : sambaHomePath
568
 
# D : sambaHomeDrive
569
 
# E : sambaLogonScript
570
 
# F : sambaProfilePath
571
 
# H : sambaAcctFlags
572
 
 
573
 
my $attr;
574
 
my $winmagic = 2147483647;
575
 
 
576
 
$samba = is_samba_user($user);
577
 
 
578
 
if ( defined( $tmp = $Options{'sambaExpire'} ) ) {
579
 
    if ( $samba == 1 ) {
580
 
        my $kickoffTime = parse_date_to_unix($tmp);
581
 
        if ( $kickoffTime != -1 ) {
582
 
            push( @mods, 'sambakickoffTime' => $kickoffTime );
583
 
        }
584
 
        else {
585
 
            print "Invalid format for '--sambaExpire' option (" . $tmp . ").\n";
586
 
        }
587
 
    }
588
 
    else {
589
 
        print "User $user is not a samba user\n";
590
 
    }
591
 
}
592
 
 
593
 
my $_sambaPwdCanChange;
594
 
if ( defined( $tmp = $Options{'A'} ) ) {
595
 
    if ( $samba == 1 ) {
596
 
        $attr = "sambaPwdCanChange";
597
 
        if ( $tmp != 0 ) {
598
 
            $_sambaPwdCanChange = 0;
599
 
        }
600
 
        else {
601
 
            $_sambaPwdCanChange = $winmagic;
602
 
        }
603
 
        push( @mods, 'sambaPwdCanChange' => $_sambaPwdCanChange );
604
 
    }
605
 
    else {
606
 
        print "User $user is not a samba user\n";
607
 
    }
608
 
}
609
 
 
610
 
my $_sambaPwdMustChange;
611
 
if ( defined( $tmp = $Options{'B'} ) ) {
612
 
    if ( $samba == 1 ) {
613
 
        if ( $tmp != 0 ) {
614
 
            $_sambaPwdMustChange = 0;
615
 
 
616
 
            # To force a user to change his password:
617
 
            # . the attribut sambaAcctFlags must not match the 'X' flag
618
 
            my $_sambaAcctFlags;
619
 
            my $flags = $user_entry->get_value('sambaAcctFlags');
620
 
            if ( defined $flags and $flags =~ /X/ ) {
621
 
                my $letters;
622
 
                if ( $flags =~ /(\w+)/ ) {
623
 
                    $letters = $1;
624
 
                }
625
 
                $letters =~ s/X//;
626
 
                $_sambaAcctFlags = "\[$letters\]";
627
 
                push( @mods, 'sambaAcctFlags' => $_sambaAcctFlags );
628
 
            }
629
 
            push( @mods, 'sambaPwdLastSet' => '0' );
630
 
        }
631
 
        else {
632
 
            $_sambaPwdMustChange = $winmagic;
633
 
        }
634
 
        push( @mods, 'sambaPwdMustChange' => $_sambaPwdMustChange );
635
 
    }
636
 
    else {
637
 
        print "User $user is not a samba user\n";
638
 
    }
639
 
}
640
 
 
641
 
if ( defined( $tmp = $Options{'C'} ) ) {
642
 
    if ( $samba == 1 ) {
643
 
        if ( $tmp eq "" and defined $user_entry->get_value('sambaHomePath') ) {
644
 
            push( @dels, 'sambaHomePath' => [] );
645
 
        }
646
 
        elsif ( $tmp ne "" ) {
647
 
            push( @mods, 'sambaHomePath' => $tmp );
648
 
        }
649
 
    }
650
 
    else {
651
 
        print "User $user is not a samba user\n";
652
 
    }
653
 
}
654
 
 
655
 
if ( defined( $tmp = $Options{'D'} ) ) {
656
 
    if ( $samba == 1 ) {
657
 
        if ( $tmp eq "" and defined $user_entry->get_value('sambaHomeDrive') ) {
658
 
            push( @dels, 'sambaHomeDrive' => [] );
659
 
        }
660
 
        elsif ( $tmp ne "" ) {
661
 
            $tmp = $tmp . ":" unless ( $tmp =~ /:/ );
662
 
            push( @mods, 'sambaHomeDrive' => $tmp );
663
 
        }
664
 
    }
665
 
    else {
666
 
        print "User $user is not a samba user\n";
667
 
    }
668
 
}
669
 
 
670
 
if ( defined( $tmp = $Options{'E'} ) ) {
671
 
    if ( $samba == 1 ) {
672
 
        if ( $tmp eq "" and defined $user_entry->get_value('sambaLogonScript') )
673
 
        {
674
 
            push( @dels, 'sambaLogonScript' => [] );
675
 
        }
676
 
        elsif ( $tmp ne "" ) {
677
 
            push( @mods, 'sambaLogonScript' => $tmp );
678
 
        }
679
 
    }
680
 
    else {
681
 
        print "User $user is not a samba user\n";
682
 
    }
683
 
}
684
 
 
685
 
if ( defined( $tmp = $Options{'F'} ) ) {
686
 
    if ( $samba == 1 ) {
687
 
        if ( $tmp eq "" and defined $user_entry->get_value('sambaProfilePath') )
688
 
        {
689
 
            push( @dels, 'sambaProfilePath' => [] );
690
 
        }
691
 
        elsif ( $tmp ne "" ) {
692
 
            push( @mods, 'sambaProfilePath' => $tmp );
693
 
        }
694
 
    }
695
 
    else {
696
 
        print "User $user is not a samba user\n";
697
 
    }
698
 
}
699
 
 
700
 
if ( $samba == 1
701
 
    and
702
 
    ( defined $Options{'H'} or defined $Options{'I'} or defined $Options{'J'} )
703
 
  )
704
 
{
705
 
    my $_sambaAcctFlags;
706
 
    if ( defined( $tmp = $Options{'H'} ) ) {
707
 
 
708
 
        #$tmp =~ s/\\/\\\\/g;
709
 
        $_sambaAcctFlags = $tmp;
710
 
    }
711
 
    else {
712
 
 
713
 
        # I or J
714
 
        my $flags;
715
 
        $flags = $user_entry->get_value('sambaAcctFlags');
716
 
 
717
 
        if ( defined( $tmp = $Options{'I'} ) ) {
718
 
            if ( !( $flags =~ /D/ ) ) {
719
 
                my $letters;
720
 
                if ( $flags =~ /(\w+)/ ) {
721
 
                    $letters = $1;
722
 
                }
723
 
                $_sambaAcctFlags = "\[D$letters\]";
724
 
            }
725
 
        }
726
 
        elsif ( defined( $tmp = $Options{'J'} ) ) {
727
 
            if ( $flags =~ /D/ ) {
728
 
                my $letters;
729
 
                if ( $flags =~ /(\w+)/ ) {
730
 
                    $letters = $1;
731
 
                }
732
 
                $letters =~ s/D//;
733
 
                $_sambaAcctFlags = "\[$letters\]";
734
 
            }
735
 
        }
736
 
    }
737
 
 
738
 
    if ( $_sambaAcctFlags and "$_sambaAcctFlags" ne '' ) {
739
 
        push( @mods, 'sambaAcctFlags' => $_sambaAcctFlags );
740
 
        my $date = time;
741
 
        push( @mods, 'sambaPwdLastSet' => $date );
742
 
    }
743
 
 
744
 
}
745
 
elsif ( !$samba == 1
746
 
    and
747
 
    ( defined $Options{'H'} or defined $Options{'I'} or defined $Options{'J'} )
748
 
  )
749
 
{
750
 
    print "User $user is not a samba user\n";
751
 
}
752
 
 
753
 
if ( defined( $tmp = $Options{'Z'} ) ) {
754
 
    my @namval = split /,/, $tmp;
755
 
    if (@namval) {
756
 
        foreach my $pair (@namval) {
757
 
            my ( $name, $value ) = split /=/, $pair;
758
 
            next if ( !$name or !$value );
759
 
            push( @mods, $name => $value );
760
 
        }
761
 
    }
762
 
 
763
 
}
764
 
 
765
 
# apply changes
766
 
my $modify = $ldap_master->modify( "$dn", 'replace' => {@mods} );
767
 
$modify->code && warn "failed to modify entry: ", $modify->error;
768
 
 
769
 
# we can delete only if @dels is not empty: we check the number of elements
770
 
my $nb_to_del = scalar(@dels);
771
 
if ( $nb_to_del != 0 ) {
772
 
    $modify = $ldap_master->modify( "$dn", 'delete' => {@dels} );
773
 
 
774
 
    # don't proceed on error
775
 
    $modify->code && die "failed to modify entry: ", $modify->error;
776
 
}
777
 
 
778
 
# take down session
779
 
$ldap_master->unbind;
780
 
 
781
 
# asked to rename the account. only do that if new rdn doesn't equal old one
782
 
if ( defined( my $new_user = $Options{'r'} ) and $Options{'r'} ne $user ) {
783
 
    my $ldap_master = connect_ldap_master();
784
 
    chomp($new_user);
785
 
 
786
 
    # read eventual new user entry
787
 
    my $new_user_entry = read_user_entry($new_user);
788
 
    if ( defined($new_user_entry) ) {
789
 
        print "$0: user $new_user already exists, cannot rename\n";
790
 
        exit(8);
791
 
    }
792
 
 
793
 
    # we check the real dn as it could be a user or computer account
794
 
    $dn = $user_entry->dn;
795
 
    my $computer;
796
 
    my ( $account, $rdn ) = ( $dn =~ m/([^,])*,(.*)$/ );
797
 
    if ( $account =~ m/(.*)\$/ ) {
798
 
 
799
 
        # it's a computer account
800
 
        $computer = 1;
801
 
        my $modify = $ldap_master->moddn(
802
 
            "uid=$user,$rdn",
803
 
            newrdn       => "uid=$new_user",
804
 
            deleteoldrdn => "1",
805
 
            newsuperior  => "$rdn"
806
 
        );
807
 
 
808
 
    }
809
 
    else {
810
 
        my $modify = $ldap_master->moddn(
811
 
            "uid=$user,$rdn",
812
 
            newrdn       => "uid=$new_user",
813
 
            deleteoldrdn => "1",
814
 
            newsuperior  => "$rdn"
815
 
        );
816
 
    }
817
 
    $modify->code && die "failed to change dn: ", $modify->error;
818
 
 
819
 
    # change cn, sn, displayName, givenName attributes
820
 
    my $user_entry = read_user_entry($new_user);
821
 
    my $dn         = $user_entry->dn();
822
 
    my @mods;
823
 
 
824
 
# If old sn is not old username (which would be the default) we assume that there is a surename that is correctly set
825
 
# so no change is required here. If they equal, we use the new username for new sn unless
826
 
# -S option is used (the sn value would have been set/updated before in that case).
827
 
    push( @mods, 'sn' => $new_user )
828
 
      unless ( $user_entry->get_value('sn')
829
 
        and $user_entry->get_value('sn') ne $user or $Options{'S'} );
830
 
 
831
 
# If old givenName is not old username (which would be the default) we assume that there is a
832
 
# givenName correctly set so no change is required here. If they equal, we set givenName to the new username unless
833
 
# the -N option is used (the givenName would have been set/updated before in that case).
834
 
    push( @mods, 'givenName' => $new_user )
835
 
      unless ( $user_entry->get_value('givenName')
836
 
        and $user_entry->get_value('givenName') ne $new_user or $Options{'N'} );
837
 
 
838
 
# common name, should be "fistname lastname". Same for displayName but only if $samba.
839
 
# Uses utf8Encode like processing of options -S and -N above. If old cn is old username
840
 
# (which would be the default) this field should be updated, unless -N _and_ -S is given.
841
 
# Then assume it has been set correctly with -N and -S before.
842
 
    push( @mods, "cn" => $new_user )
843
 
      unless ( $user_entry->get_value("cn")
844
 
        and $user_entry->get_value("cn") ne utf8Encode($user)
845
 
        or $Options{'N'} and $Options{'S'} );
846
 
    push( @mods, "displayName" => $new_user )
847
 
      unless ( not $samba
848
 
        or $user_entry->get_value("displayName")
849
 
        and $user_entry->get_value("displayName") ne utf8Encode($user)
850
 
        or $Options{'N'} and $Options{'S'} );
851
 
 
852
 
    if ( @mods > 0 ) {    # only change if there is something to change
853
 
        $modify =
854
 
          $ldap_master->modify( "$dn", changes => [ 'replace' => [@mods] ] );
855
 
        $modify->code && warn "failed to rename the user: ", $modify->error;
856
 
    }
857
 
 
858
 
    # changing username in groups
859
 
    my @groups = &find_groups_of($user);
860
 
    foreach my $gname (@groups) {
861
 
        if ( $gname ne "" ) {
862
 
            my $dn_line = get_group_dn($gname);
863
 
            my $dn      = get_dn_from_line("$dn_line");
864
 
            print "updating group $gname\n";
865
 
            $modify = $ldap_master->modify(
866
 
                "$dn",
867
 
                changes => [
868
 
                    'delete' => [ memberUid => $user ],
869
 
                    'add'    => [ memberUid => $new_user ]
870
 
                ]
871
 
            );
872
 
            $modify->code
873
 
              && warn "failed to update user's supplementary group $gname: ",
874
 
              $modify->error;
875
 
        }
876
 
    }
877
 
 
878
 
    $ldap_master->unbind;
879
 
}
880
 
 
881
 
$nscd_status = system "/etc/init.d/nscd status >/dev/null 2>&1";
882
 
 
883
 
if ( $nscd_status == 0 ) {
884
 
    system "/etc/init.d/nscd restart > /dev/null 2>&1";
885
 
}
886
 
 
887
 
if ( defined( $Options{'P'} ) ) {
888
 
    exec "$RealBin/smbldap-passwd $user";
889
 
}
890
 
 
891
 
############################################################
892
 
 
893
 
=head1 NAME
894
 
 
895
 
smbldap-usermod - Modify a user account
896
 
 
897
 
=head1 SYNOPSIS
898
 
 
899
 
smbldap-usermod [-c gecos] [-d home_dir] [-r login_name] [-u uid] [-g gid] [-o] [-G group[,...]] [-s shell] [-N first_name] [-S surname] [-P] [-M mail[,...]] [-T mail,[..]] [--shadowExpire date/n] [--shadowMax n] [--shadowMin n] [--shadowInactive n] [--shadowWarning n] [-L] [-U] [-a] [-e expiration_date/n] [--sambaExpire date/n] [-A canchange] [-B mustchange] [-C smbhome] [-D homedrive] [-E scriptpath] [-F profilepath] [-H acctflags] [-I] [-J] [-h] login
900
 
 
901
 
=head1 DESCRIPTION
902
 
 
903
 
The smbldap-usermod  command  modifies the system account files to reflect the changes that are specified
904
 
on the  command  line.
905
 
 
906
 
=head2 UNIX options
907
 
 
908
 
-c, --gecos gecos
909
 
    The new value of the user's comment field (gecos). (Don't use this to modify displayName or cn. Use -N and -S options combined instead).
910
 
 
911
 
-d, --homedir home_dir
912
 
    The user's new login directory.
913
 
 
914
 
-r, --rename new_user
915
 
    Allow to rename a user. This option will update the dn attribute for the user. You can also update others attributes using the corresponding script options.
916
 
 
917
 
-u, --uid uid
918
 
    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.
919
 
 
920
 
-o, --canBeNotUnique
921
 
    uidNumber can be non unique
922
 
 
923
 
-g, --gid initial_group
924
 
    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.
925
 
 
926
 
-G, --group [+-]group,[...]
927
 
    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, unless the '+' or '-' caracter is used to add or remove groups to inital ones.
928
 
 
929
 
-s, --shel shell
930
 
    The name of the user's new login shell.  Setting this  field  to blank causes the system to select the default
931
 
    login shell.
932
 
 
933
 
-N, --givenName
934
 
    set the user's given name (attribute givenName). Additionally used to set the first name in displayName and cn.
935
 
 
936
 
-S, --surname
937
 
    Set the user's surname (attribute sn). Additionally used to set the last name in displayName and cn.
938
 
 
939
 
-P
940
 
    End by invoking smbldap-passwd to change the user password (both unix and samba passwords)
941
 
 
942
 
-M, --mailAddresses  mail,[...]
943
 
    mailAddresses (comma seperated)
944
 
 
945
 
-T, --mailToAddress  mail,[...]
946
 
    mailToAddress (forward address) (comma seperated)
947
 
 
948
 
--shadowExpire <YYYY-MM-DD/n>
949
 
    Set the expiration date for the user password. This only affect unix account. The date may be specified as either YYYY-MM-DD or 'n' days from day. The 'n' syntax also supports the extended format (#y)(#m)(#d) for years, months, and days from today. One need not specify all three, so all of the following are examples of valid input: '5y4m2d' (5 years, 4 months, and 2 days), '5y' (5 years), '5y2d' (5 years and 2 days), and '3' (3 days). This option calls the internal 'timelocal' command to set calculate the number of seconds from Junary 1 1970 to the specified date.
950
 
 
951
 
--shadowMax <n>
952
 
    User must change the password, at least, every 'n' days
953
 
 
954
 
--shadowMin <n>
955
 
    User must wait 'n' days once the password has changed before changing it again
956
 
 
957
 
--shadowInactive <n>
958
 
    Number of days of inactivity allowed for the specified user
959
 
 
960
 
--shadowWarning <n>
961
 
    User is warned that the password must be changed four days before the password expires
962
 
 
963
 
-L, --shadowLock
964
 
    Lock unix user's password. This puts a '!' in front of the encrypted password, effectively disabling the password.
965
 
 
966
 
-U, --shadowUnlock
967
 
    Unlock unix user's password. This removes the '!' in front of the encrypted password.
968
 
 
969
 
=head2 SAMBA options
970
 
 
971
 
-a, --addsambaSAMAccount
972
 
    Add the sambaSAMAccount objectclass to the specified user account. This allow the user to become a samba user.
973
 
 
974
 
-e, --expire <YYYY-MM-DD(HH:MM:SS)/n>
975
 
    Sets the expiration for both samba (--sambaExpire) and shadow (--shadowExpire).
976
 
 
977
 
--sambaExpire <YYYY-MM-DD HH:MM:SS/n>
978
 
    Set the expiration date for the user account. This only affects the samba account. The date must be in the following format: YYYY-MM-DD HH:MM:SS. The n-days format of shadowExpire is also supported. This option uses the internal 'timelocal' command to set calculate the number of seconds from Junary 1 1970 to the specified date.
979
 
 
980
 
-x
981
 
    Creates rid and primaryGroupID in hex instead of decimal (for Samba 2.2.2 unpatched only - higher versions always use decimal)
982
 
 
983
 
-A, --sambaPwdCanChange
984
 
    can change password ? 0 if no, 1 if yes
985
 
 
986
 
-B, --sambaPwdMustChange
987
 
    must change password ? 0 if no, 1 if yes
988
 
 
989
 
-C, --sambaHomePath path
990
 
    sambaHomePath (SMB home share, like '\\\\PDC-SRV\\homes')
991
 
 
992
 
-D, --sambaHomeDrive drive
993
 
    sambaHomeDrive (letter associated with home share, like 'H:')
994
 
 
995
 
-E, --sambaLogonScript script
996
 
    sambaLogonScript, relative to the [netlogon] share (DOS script to execute on login, like 'foo.bat')
997
 
 
998
 
-F, --sambaProfilePath path
999
 
    sambaProfilePath (profile directory, like '\\\\PDC-SRV\\profiles\\foo')
1000
 
 
1001
 
-H, --sambaAcctFlags flags
1002
 
    sambaAcctFlags, spaces and trailing bracket are ignored (samba account control bits like '[NDHTUMWSLKI]')
1003
 
 
1004
 
-I, --sambaDisable
1005
 
    disable user. Can't be used with -H or -J
1006
 
 
1007
 
-J, --sambaEnable
1008
 
    enable user. Can't be used with -H or -I
1009
 
 
1010
 
-h, --help
1011
 
    print this help
1012
 
 
1013
 
=head1 SEE ALSO
1014
 
 
1015
 
usermod(1)
1016
 
 
1017
 
=cut
1018
 
 
1019
 
#'