• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-WNDR4500v2-V1.0.0.60_1.0.38/ap/gpl/samba-3.0.13/examples/LDAP/smbldap-tools-0.8.7/
1#!/usr/bin/perl -w
2
3# $Id: smbldap-usermod,v 1.11 2005/01/08 12:04:45 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 ( $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" 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#'
634