use strict;
use FindBin;
use FindBin qw($RealBin);
use lib "$RealBin/";
use smbldap_tools;
use Crypt::SmbHash;
use Getopt::Std;
my %Options;
my $ok = getopts('o:abnmwiPG:u:g:d:s:c:k:t:A:B:C:D:E:F:H:M:N:S:T:?', \%Options);
if ( (!$ok) || (@ARGV < 1) || ($Options{'?'}) ) {
print_banner;
print "Usage: $0 [-awmugdsckABCDEFGHMNPST?] username\n";
print " -o add the user in the organizational unit (relative to the user suffix)\n";
print " -a is a Windows User (otherwise, Posix stuff only)\n";
print " -b is a AIX User\n";
print " -w is a Windows Workstation (otherwise, Posix stuff only)\n";
print " -i is a trust account (Windows Workstation)\n";
print " -u uid\n";
print " -g gid\n";
print " -G supplementary comma-separated groups\n";
print " -n do not create a group\n";
print " -d home\n";
print " -s shell\n";
print " -c gecos\n";
print " -m creates home directory and copies /etc/skel\n";
print " -k skeleton dir (with -m)\n";
print " -t time. Wait 'time' seconds before exiting (when adding Windows Workstation)\n";
print " -P ends by invoking smbldap-passwd\n";
print " -A can change password ? 0 if no, 1 if yes\n";
print " -B must change password ? 0 if no, 1 if yes\n";
print " -C sambaHomePath (SMB home share, like '\\\\PDC-SRV\\homes')\n";
print " -D sambaHomeDrive (letter associated with home share, like 'H:')\n";
print " -E sambaLogonScript (DOS script to execute on login)\n";
print " -F sambaProfilePath (profile directory, like '\\\\PDC-SRV\\profiles\\foo')\n";
print " -H sambaAcctFlags (samba account control bits like '[NDHTUMWSLKI]')\n";
print " -N surname\n";
print " -S family name\n";
print " -M local mailAddress (comma seperated)\n";
print " -T mailToAddress (forward address) (comma seperated)\n";
print " -? show this help message\n";
exit (1);
}
my $ldap_master=connect_ldap_master();
my $nscd_status = system "/etc/init.d/nscd status >/dev/null 2>&1";
if ($nscd_status == 0) {
system "/etc/init.d/nscd stop > /dev/null 2>&1";
}
my $userName = $ARGV[0];
if (defined($Options{'w'})) {
if ($userName =~ /[^\$]$/s) {
$userName .= "\$";
}
}
if ($userName =~ /^([\w -.]+\$?)$/) {
$userName = $1;
} else {
print "$0: illegal username\n";
exit (1);
}
my ($rc, $dn) = get_user_dn2($userName);
if ($rc and defined($dn)) {
print "$0: user $userName exists\n";
exit (9);
} elsif (!$rc) {
print "$0: error in get_user_dn2\n";
exit(10);
}
my $user_ou=$Options{'o'};
my $node;
if (defined $user_ou) {
if (!($user_ou =~ m/^ou=(.*)/)) {
$node=$user_ou;
$user_ou="ou=$user_ou";
} else {
($node)=($user_ou=~m/ou=(.*)/);
}
my $mesg = $ldap_master->search ( base => "$config{usersdn}",
scope => "one",
filter => "(&(objectClass=organizationalUnit)(ou=$node))"
);
$mesg->code && die $mesg->error;
if ($mesg->count eq 0) {
print "creating $user_ou first (as $user_ou,$config{usersdn})\n";
my $add = $ldap_master->add ("ou=$node,$config{usersdn}",
attr => [
'objectclass' => ['top','organizationalUnit'],
'ou' => "$node"
]
);
$add->code && die "failed to add entry: ", $add->error ;
}
$config{usersdn}="$user_ou,$config{usersdn}";
}
my $userUidNumber = $Options{'u'};
if (!defined($userUidNumber)) {
$userUidNumber=get_next_id($config{usersdn},"uidNumber");
} elsif (getpwuid($userUidNumber)) {
die "Uid already exists.\n";
}
if ($nscd_status == 0) {
system "/etc/init.d/nscd start > /dev/null 2>&1";
}
my $createGroup = 0;
my $userGidNumber = $Options{'g'};
if (!defined($userGidNumber)) {
if (defined($Options{'w'})) {
$userGidNumber = $config{defaultComputerGid};
} else {
$userGidNumber = $config{defaultUserGid};
}
} else {
my $gid;
if (($gid = parse_group($userGidNumber)) < 0) {
print "$0: unknown group $userGidNumber\n";
exit (6);
}
$userGidNumber = $gid;
}
my $group_entry;
my $userGroupSID;
my $userRid;
my $user_sid;
if (defined $Options{'a'} or defined $Options{'i'}) {
$group_entry = read_group_entry_gid($userGidNumber);
$userGroupSID = $group_entry->get_value('sambaSID');
unless ($userGroupSID) {
print "Error: SID not set for unix group $userGidNumber\n";
print "check if your unix group is mapped to an NT group\n";
exit (7);
}
$userRid = 2 * $userUidNumber + 1000;
$user_sid="$config{SID}-$userRid";
my $test_exist_sid=does_sid_exist($user_sid,$config{usersdn});
if ($test_exist_sid->count == 1) {
print "User SID already owned by\n";
foreach my $entry ($test_exist_sid->all_entries) {
my $dn= $entry->dn;
chomp($dn);
print "$dn\n";
}
exit(7);
}
}
my $userHomeDirectory;
my ($givenName,$userCN, $userSN);
my @userMailLocal;
my @userMailTo;
my $tmp;
if (!defined($userHomeDirectory = $Options{'d'})) {
$userHomeDirectory = &subst_user($config{userHome}, $userName);
}
$userHomeDirectory=~s/\/\//\//;
$config{userLoginShell} = $tmp if (defined($tmp = $Options{'s'}));
$config{userGecos} = $tmp if (defined($tmp = $Options{'c'}));
$config{skeletonDir} = $tmp if (defined($tmp = $Options{'k'}));
$givenName = (utf8Encode($Options{'N'}) || $userName);
$userSN = (utf8Encode($Options{'S'}) || $userName);
if ($Options{'N'} and $Options{'S'}) {
$userCN="$givenName"." $userSN";
} else {
$userCN=$userName;
}
@userMailLocal = &split_arg_comma($Options{'M'});
@userMailTo = &split_arg_comma($Options{'T'});
if (defined($Options{'w'}) or defined($Options{'i'})) {
if ( $Options{'i'} and !($userName =~ m/\$$/) ) {
$userName.="\$";
}
if (!add_posix_machine ($userName,$userUidNumber,$userGidNumber,$Options{'t'})) {
die "$0: error while adding posix account\n";
}
if (defined($Options{'i'})) {
my $pass;
my $pass2;
system "stty -echo";
print "New password : ";
chomp($pass=<STDIN>);
print "\n";
system "stty echo";
system "stty -echo";
print "Retype new password : ";
chomp($pass2=<STDIN>);
print "\n";
system "stty echo";
if ($pass ne $pass2) {
print "New passwords don't match!\n";
exit (10);
}
my ($lmpassword,$ntpassword) = ntlmgen $pass;
my $date=time;
my $modify = $ldap_master->modify ( "uid=$userName,$config{computersdn}",
changes => [
replace => [objectClass => ['top', 'person', 'organizationalPerson', 'inetOrgPerson', 'posixAccount', 'sambaSAMAccount']],
add => [sambaLogonTime => '0'],
add => [sambaLogoffTime => '2147483647'],
add => [sambaKickoffTime => '2147483647'],
add => [sambaPwdCanChange => '0'],
add => [sambaPwdMustChange => '2147483647'],
add => [sambaPwdLastSet => "$date"],
add => [sambaAcctFlags => '[I ]'],
add => [sambaLMPassword => "$lmpassword"],
add => [sambaNTPassword => "$ntpassword"],
add => [sambaSID => "$user_sid"],
add => [sambaPrimaryGroupSID => "$config{SID}-515"]
]
);
$modify->code && die "failed to add entry: ", $modify->error ;
}
$ldap_master->unbind;
exit 0;
}
my $add;
if (defined($Options{'b'})) {
$add = $ldap_master->add ("uid=$userName,$config{usersdn}",
attr => [
'objectclass' => ['top','person', 'organizationalPerson', 'posixAccount','shadowAccount'],
'cn' => "$userCN",
'sn' => "$userSN",
'uid' => "$userName",
'uidNumber' => "$userUidNumber",
'gidNumber' => "$userGidNumber",
'homeDirectory' => "$userHomeDirectory",
'loginShell' => "$config{userLoginShell}",
'gecos' => "$config{userGecos}",
'userPassword' => "{crypt}x"
]
);
} else {
$add = $ldap_master->add ("uid=$userName,$config{usersdn}",
attr => [
'objectclass' => ['top','person', 'organizationalPerson', 'inetOrgPerson','posixAccount','shadowAccount'],
'cn' => "$userCN",
'sn' => "$userSN",
'givenName' => "$givenName",
'uid' => "$userName",
'uidNumber' => "$userUidNumber",
'gidNumber' => "$userGidNumber",
'homeDirectory' => "$userHomeDirectory",
'loginShell' => "$config{userLoginShell}",
'gecos' => "$config{userGecos}",
'userPassword' => "{crypt}x"
]
);
}
$add->code && warn "failed to add entry: ", $add->error ;
if ($userGidNumber != $config{defaultUserGid}) {
group_add_user($userGidNumber, $userName);
}
my $grouplist;
if (defined($grouplist = $Options{'G'})) {
add_grouplist_user($grouplist, $userName);
}
if (defined($tmp = $Options{'m'})) {
unless ( $userName =~ /\$$/ ) {
if ( !(-e $userHomeDirectory) ) {
system "mkdir $userHomeDirectory 2>/dev/null";
system "cp -a $config{skeletonDir}/.[a-z,A-Z]* $config{skeletonDir}/* $userHomeDirectory 2>/dev/null";
system "chown -R $userUidNumber:$userGidNumber $userHomeDirectory 2>/dev/null";
if (defined $config{userHomeDirectoryMode}) {
system "chmod $config{userHomeDirectoryMode} $userHomeDirectory 2>/dev/null";
} else {
system "chmod 700 $userHomeDirectory 2>/dev/null";
}
}
}
}
my @adds;
if (@userMailLocal) {
my @mail;
foreach my $m (@userMailLocal) {
my $domain = $config{mailDomain};
if ($m =~ /^(.+)@/) {
push (@mail, $m);
$m= $1;
} else {
push(@mail, $m.($domain ? '@'.$domain : ''));
}
}
push(@adds, 'mailLocalAddress' => [ @userMailLocal ]);
push(@adds, 'mail' => [ @mail ]);
}
if (@userMailTo) {
push(@adds, 'mailRoutingAddress' => [ @userMailTo ]);
}
if (@userMailLocal || @userMailTo) {
push(@adds, 'objectClass' => 'inetLocalMailRecipient');
}
if (defined($Options{'a'})) {
if (!$config{with_smbpasswd}) {
my $winmagic = 2147483647;
my $valpwdcanchange = 0;
my $valpwdmustchange = $winmagic;
my $valpwdlastset = 0;
my $valacctflags = "[UX]";
if (defined($tmp = $Options{'A'})) {
if ($tmp != 0) {
$valpwdcanchange = "0";
} else {
$valpwdcanchange = "$winmagic";
}
}
if (defined($tmp = $Options{'B'})) {
if ($tmp != 0) {
$valpwdmustchange = "0";
$valpwdlastset=$winmagic;
$valacctflags = "[U]";
} else {
$valpwdmustchange = "$winmagic";
}
}
if (defined($tmp = $Options{'H'})) {
$valacctflags = "$tmp";
}
my $modify = $ldap_master->modify ( "uid=$userName,$config{usersdn}",
changes => [
add => [objectClass => 'sambaSAMAccount'],
add => [sambaPwdLastSet => "$valpwdlastset"],
add => [sambaLogonTime => '0'],
add => [sambaLogoffTime => '2147483647'],
add => [sambaKickoffTime => '2147483647'],
add => [sambaPwdCanChange => "$valpwdcanchange"],
add => [sambaPwdMustChange => "$valpwdmustchange"],
add => [displayName => "$config{userGecos}"],
add => [sambaAcctFlags => "$valacctflags"],
add => [sambaSID => "$config{SID}-$userRid"]
]
);
$modify->code && die "failed to add entry: ", $modify->error ;
} else {
my $FILE="|smbpasswd -s -a $userName >/dev/null" ;
open (FILE, $FILE) || die "$!\n";
print FILE <<EOF;
x
x
EOF
;
close FILE;
if ($?) {
print "$0: error adding samba account\n";
exit (10);
}
}
$tmp = defined($Options{'E'}) ? $Options{'E'} : $config{userScript};
my $valscriptpath = &subst_user($tmp, $userName);
$tmp = defined($Options{'C'}) ? $Options{'C'} : $config{userSmbHome};
my $valsmbhome = &subst_user($tmp, $userName);
my $valhomedrive = defined($Options{'D'}) ? $Options{'D'} : $config{userHomeDrive};
$valhomedrive .= ':' if ($valhomedrive && $valhomedrive !~ /:$/);
$tmp = defined($Options{'F'}) ? $Options{'F'} : $config{userProfile};
my $valprofilepath = &subst_user($tmp, $userName);
if ($valhomedrive) {
push(@adds, 'sambaHomeDrive' => $valhomedrive);
}
if ($valsmbhome) {
push(@adds, 'sambaHomePath' => $valsmbhome);
}
if ($valprofilepath) {
push(@adds, 'sambaProfilePath' => $valprofilepath);
}
if ($valscriptpath) {
push(@adds, 'sambaLogonScript' => $valscriptpath);
}
if (!$config{with_smbpasswd}) {
push(@adds, 'sambaPrimaryGroupSID' => $userGroupSID);
push(@adds, 'sambaLMPassword' => "XXX");
push(@adds, 'sambaNTPassword' => "XXX");
}
my $modify = $ldap_master->modify ( "uid=$userName,$config{usersdn}",
add => {
@adds
}
);
$modify->code && die "failed to add entry: ", $modify->error ;
}
if (defined($Options{'b'})) {
my $modify = $ldap_master->modify ( "uid=$userName,$config{usersdn}",
changes => [
add => [objectClass => 'aixAuxAccount'],
add => [passwordChar => "!"],
add => [isAdministrator => "false"]
]
);
$modify->code && die "failed to add entry: ", $modify->error ;
}
$ldap_master->unbind;
if (defined($Options{'P'})) {
exec "$RealBin/smbldap-passwd \"$userName\""
}
exit 0;
=head1 NAME
smbldap-useradd - Create a new user
=head1 SYNOPSIS
smbldap-useradd [-o user_ou] [-c comment] [-d home_dir] [-g initial_group] [-G group[,...]] [-m [-k skeleton_dir]] [-s shell] [-u uid [ -o]] [-P] [-A canchange] [-B mustchange] [-C smbhome] [-D homedrive] [-E scriptpath] [-F profilepath] [-H acctflags] login
=head1 DESCRIPTION
Creating New Users
The smbldap-useradd command creates a new user account using the values specified on the command line and the default values from the system and from the configuration files (in /etc/smbldap-tools directory).
For Samba users, rid is '2*uidNumber+1000', and sambaPrimaryGroupSID is '$SID-2*gidNumber+1001', where $SID is the domain SID. Thus you may want to use :
$ smbldap-useradd -a -g "Domain Admins" -u 500 Administrator
to create an domain administrator account (admin rid is 0x1F4 = 500 and grouprid is 0x200 = 512).
Without any option, the account created will be an Unix (Posix) account. The following options may be used to add information:
-o
The user's account will be created in the specified organazional unit. It is relative to the user suffix dn ($usersdn) defined in the configuration file.
-a
The user will have a Samba account (and Unix).
-b
The usrer is an AIX acount
-w
Creates an account for a Samba machine (Workstation), so that it can join a sambaDomainName.
-i
Creates an interdomain trust account (machine Workstation). A password will be asked for the trust account.
-c "comment"
The new user's comment field (gecos).
-d home_dir
The new user will be created using home_dir as the value for the user's login directory. The default is to append the login name to userHomePrefix (defined in the configuration file) and use that as the login directory name.
-g initial_group
The group name or number of the user's initial login group. The group name must exist. A group number must refer to an already existing group. The default group number is defined in the configuration file (defaultUserGid="513").
-G group,[...]
A list of supplementary groups which the user is also a member of. Each group is separated to 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. The default is for the user to belong only to the initial group.
-m
The user's home directory will be created if it does not exist. The files contained in skeletonDir will be copied to the home directory if the -k option is used, otherwise the files contained in /etc/skel will be used instead. Any directories contained in skeletonDir or /etc/skel will be created in the user's home directory as well. The -k option is only valid in conjunction with the -m option. The default is to not create the directory and to not copy any files.
-s shell
The name of the user's login shell. The default is to leave this field blank, which causes the system to select the default login shell.
-t time
Wait <time> seconds before exiting script when adding computer's account. This is useful when Master/PDC and Slaves/BDCs are connected through the internet (replication is not real time)
-u uid
The numerical value of the user's ID. This value must be unique, unless the -o option is used. The value must be nonnegative. The default is to use the smallest ID value greater than 1000 and greater than every other user.
-P
ends by invoking smbldap-passwd
-A
can change password ? 0 if no, 1 if yes
-B
must change password ? 0 if no, 1 if yes
-C sambaHomePath
SMB home share, like '\\\\PDC-SRV\\homes'
-D sambaHomeDrive
letter associated with home share, like 'H:'
-E sambaLogonScript
relative to the [netlogon] share (DOS script to execute on login, like 'foo.bat'
-F sambaProfilePath
profile directory, like '\\\\PDC-SRV\\profiles\\foo'
-H sambaAcctFlags
spaces and trailing bracket are ignored (samba account control bits like '[NDHTUMWSLKI]'
-M local mail aliases (multiple addresses are seperated by spaces)
-N canonical name
defaults to gecos or username, if gecos not set
-S surname
defaults to username
-T mailToAddress (forward address) (multiple addresses are seperated by spaces)
-n do not print banner message
=head1 SEE ALSO
useradd(1)
=cut