3 # Copyright Jelmer Vernooij 2010 <jelmer@samba.org>
4 # Copyright Theresa Halloran 2011 <theresahalloran@gmail.com>
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 3 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 import samba.getopt as options
23 from getpass import getpass
24 from samba.auth import system_session
25 from samba.samdb import SamDB
29 generate_random_password,
31 from samba.net import Net
33 from samba.netcmd import (
41 class cmd_user_create(Command):
44 This command creates a new user account in the Active Directory domain. The username specified on the command is the sAMaccountName.
46 User accounts may represent physical entities, such as people or may be used as service accounts for applications. User accounts are also referred to as security principals and are assigned a security identifier (SID).
48 A user account enables a user to logon to a computer and domain with an identity that can be authenticated. To maximize security, each user should have their own unique user account and password. A user's access to domain resources is based on permissions assigned to the user account.
50 Unix (RFC2307) attributes may be added to the user account. Attributes taken from NSS are obtained on the local machine. Explicitly given values override values obtained from NSS. Configure 'idmap_ldb:use rfc2307 = Yes' to use these attributes for UID/GID mapping.
52 The command may be run from the root userid or another authorized userid. The -H or --URL= option can be used to execute the command against a remote server.
55 samba-tool user create User1 passw0rd --given-name=John --surname=Smith --must-change-at-next-login -H ldap://samba.samdom.example.com -Uadministrator%passw1rd
57 Example1 shows how to create a new user in the domain against a remote LDAP server. The -H parameter is used to specify the remote target server. The -U option is used to pass the userid and password authorized to issue the command remotely.
60 sudo samba-tool user create User2 passw2rd --given-name=Jane --surname=Doe --must-change-at-next-login
62 Example2 shows how to create a new user in the domain against the local server. sudo is used so a user may run the command as root. In this example, after User2 is created, he/she will be forced to change their password when they logon.
65 samba-tool user create User3 passw3rd --userou='OU=OrgUnit'
67 Example3 shows how to create a new user in the OrgUnit organizational unit.
70 samba-tool user create User4 passw4rd --rfc2307-from-nss --gecos 'some text'
72 Example4 shows how to create a new user with Unix UID, GID and login-shell set from the local NSS and GECOS set to 'some text'.
75 samba-tool user create User5 passw5rd --nis-domain=samdom --unix-home=/home/User5 \
76 --uid-number=10005 --login-shell=/bin/false --gid-number=10000
78 Example5 shows how to create an RFC2307/NIS domain enabled user account. If
79 --nis-domain is set, then the other four parameters are mandatory.
82 synopsis = "%prog <username> [<password>] [options]"
85 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
86 metavar="URL", dest="H"),
87 Option("--must-change-at-next-login",
88 help="Force password to be changed on next login",
90 Option("--random-password",
91 help="Generate random password",
93 Option("--smartcard-required",
94 help="Require a smartcard for interactive logons",
96 Option("--use-username-as-cn",
97 help="Force use of username as user's CN",
100 help="DN of alternative location (without domainDN counterpart) to default CN=Users in which new user object will be created. E. g. 'OU=<OU name>'",
102 Option("--surname", help="User's surname", type=str),
103 Option("--given-name", help="User's given name", type=str),
104 Option("--initials", help="User's initials", type=str),
105 Option("--profile-path", help="User's profile path", type=str),
106 Option("--script-path", help="User's logon script path", type=str),
107 Option("--home-drive", help="User's home drive letter", type=str),
108 Option("--home-directory", help="User's home directory path", type=str),
109 Option("--job-title", help="User's job title", type=str),
110 Option("--department", help="User's department", type=str),
111 Option("--company", help="User's company", type=str),
112 Option("--description", help="User's description", type=str),
113 Option("--mail-address", help="User's email address", type=str),
114 Option("--internet-address", help="User's home page", type=str),
115 Option("--telephone-number", help="User's phone number", type=str),
116 Option("--physical-delivery-office", help="User's office location", type=str),
117 Option("--rfc2307-from-nss",
118 help="Copy Unix user attributes from NSS (will be overridden by explicit UID/GID/GECOS/shell)",
119 action="store_true"),
120 Option("--nis-domain", help="User's Unix/RFC2307 NIS domain", type=str),
121 Option("--unix-home", help="User's Unix/RFC2307 home directory",
123 Option("--uid", help="User's Unix/RFC2307 username", type=str),
124 Option("--uid-number", help="User's Unix/RFC2307 numeric UID", type=int),
125 Option("--gid-number", help="User's Unix/RFC2307 primary GID number", type=int),
126 Option("--gecos", help="User's Unix/RFC2307 GECOS field", type=str),
127 Option("--login-shell", help="User's Unix/RFC2307 login shell", type=str),
130 takes_args = ["username", "password?"]
132 takes_optiongroups = {
133 "sambaopts": options.SambaOptions,
134 "credopts": options.CredentialsOptions,
135 "versionopts": options.VersionOptions,
138 def run(self, username, password=None, credopts=None, sambaopts=None,
139 versionopts=None, H=None, must_change_at_next_login=False,
140 random_password=False, use_username_as_cn=False, userou=None,
141 surname=None, given_name=None, initials=None, profile_path=None,
142 script_path=None, home_drive=None, home_directory=None,
143 job_title=None, department=None, company=None, description=None,
144 mail_address=None, internet_address=None, telephone_number=None,
145 physical_delivery_office=None, rfc2307_from_nss=False,
146 nis_domain=None, unix_home=None, uid=None, uid_number=None,
147 gid_number=None, gecos=None, login_shell=None,
148 smartcard_required=False):
150 if smartcard_required:
151 if password is not None and password is not '':
152 raise CommandError('It is not allowed to specifiy '
154 'together with --smartcard-required.')
155 if must_change_at_next_login:
156 raise CommandError('It is not allowed to specifiy '
157 '--must-change-at-next-login '
158 'together with --smartcard-required.')
160 if random_password and not smartcard_required:
161 password = generate_random_password(128, 255)
164 if smartcard_required:
166 if password is not None and password is not '':
168 password = getpass("New Password: ")
169 passwordverify = getpass("Retype Password: ")
170 if not password == passwordverify:
172 self.outf.write("Sorry, passwords do not match.\n")
175 pwent = pwd.getpwnam(username)
178 if uid_number is None:
179 uid_number = pwent[2]
180 if gid_number is None:
181 gid_number = pwent[3]
184 if login_shell is None:
185 login_shell = pwent[6]
187 lp = sambaopts.get_loadparm()
188 creds = credopts.get_credentials(lp)
190 if uid_number or gid_number:
191 if not lp.get("idmap_ldb:use rfc2307"):
192 self.outf.write("You are setting a Unix/RFC2307 UID or GID. You may want to set 'idmap_ldb:use rfc2307 = Yes' to use those attributes for XID/SID-mapping.\n")
194 if nis_domain is not None:
195 if None in (uid_number, login_shell, unix_home, gid_number):
196 raise CommandError('Missing parameters. To enable NIS features, '
197 'the following options have to be given: '
198 '--nis-domain=, --uidNumber=, --login-shell='
199 ', --unix-home=, --gid-number= Operation '
203 samdb = SamDB(url=H, session_info=system_session(),
204 credentials=creds, lp=lp)
205 samdb.newuser(username, password, force_password_change_at_next_login_req=must_change_at_next_login,
206 useusernameascn=use_username_as_cn, userou=userou, surname=surname, givenname=given_name, initials=initials,
207 profilepath=profile_path, homedrive=home_drive, scriptpath=script_path, homedirectory=home_directory,
208 jobtitle=job_title, department=department, company=company, description=description,
209 mailaddress=mail_address, internetaddress=internet_address,
210 telephonenumber=telephone_number, physicaldeliveryoffice=physical_delivery_office,
211 nisdomain=nis_domain, unixhome=unix_home, uid=uid,
212 uidnumber=uid_number, gidnumber=gid_number,
213 gecos=gecos, loginshell=login_shell,
214 smartcard_required=smartcard_required)
216 raise CommandError("Failed to add user '%s': " % username, e)
218 self.outf.write("User '%s' created successfully\n" % username)
221 class cmd_user_add(cmd_user_create):
222 __doc__ = cmd_user_create.__doc__
223 # take this print out after the add subcommand is removed.
224 # the add subcommand is deprecated but left in for now to allow people to
227 def run(self, *args, **kwargs):
229 "Note: samba-tool user add is deprecated. "
230 "Please use samba-tool user create for the same function.\n")
231 return super(cmd_user_add, self).run(*args, **kwargs)
234 class cmd_user_delete(Command):
237 This command deletes a user account from the Active Directory domain. The username specified on the command is the sAMAccountName.
239 Once the account is deleted, all permissions and memberships associated with that account are deleted. If a new user account is added with the same name as a previously deleted account name, the new user does not have the previous permissions. The new account user will be assigned a new security identifier (SID) and permissions and memberships will have to be added.
241 The command may be run from the root userid or another authorized userid. The -H or --URL= option can be used to execute the command against a remote server.
244 samba-tool user delete User1 -H ldap://samba.samdom.example.com --username=administrator --password=passw1rd
246 Example1 shows how to delete a user in the domain against a remote LDAP server. The -H parameter is used to specify the remote target server. The --username= and --password= options are used to pass the username and password of a user that exists on the remote server and is authorized to issue the command on that server.
249 sudo samba-tool user delete User2
251 Example2 shows how to delete a user in the domain against the local server. sudo is used so a user may run the command as root.
254 synopsis = "%prog <username> [options]"
257 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
258 metavar="URL", dest="H"),
261 takes_args = ["username"]
262 takes_optiongroups = {
263 "sambaopts": options.SambaOptions,
264 "credopts": options.CredentialsOptions,
265 "versionopts": options.VersionOptions,
268 def run(self, username, credopts=None, sambaopts=None, versionopts=None,
270 lp = sambaopts.get_loadparm()
271 creds = credopts.get_credentials(lp, fallback_machine=True)
274 samdb = SamDB(url=H, session_info=system_session(),
275 credentials=creds, lp=lp)
276 samdb.deleteuser(username)
278 raise CommandError('Failed to remove user "%s"' % username, e)
279 self.outf.write("Deleted user %s\n" % username)
282 class cmd_user_list(Command):
283 """List all users."""
285 synopsis = "%prog [options]"
288 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
289 metavar="URL", dest="H"),
292 takes_optiongroups = {
293 "sambaopts": options.SambaOptions,
294 "credopts": options.CredentialsOptions,
295 "versionopts": options.VersionOptions,
298 def run(self, sambaopts=None, credopts=None, versionopts=None, H=None):
299 lp = sambaopts.get_loadparm()
300 creds = credopts.get_credentials(lp, fallback_machine=True)
302 samdb = SamDB(url=H, session_info=system_session(),
303 credentials=creds, lp=lp)
305 domain_dn = samdb.domain_dn()
306 res = samdb.search(domain_dn, scope=ldb.SCOPE_SUBTREE,
307 expression=("(&(objectClass=user)(userAccountControl:%s:=%u))"
308 % (ldb.OID_COMPARATOR_AND, dsdb.UF_NORMAL_ACCOUNT)),
309 attrs=["samaccountname"])
314 self.outf.write("%s\n" % msg.get("samaccountname", idx=0))
317 class cmd_user_enable(Command):
320 This command enables a user account for logon to an Active Directory domain. The username specified on the command is the sAMAccountName. The username may also be specified using the --filter option.
322 There are many reasons why an account may become disabled. These include:
323 - If a user exceeds the account policy for logon attempts
324 - If an administrator disables the account
325 - If the account expires
327 The samba-tool user enable command allows an administrator to enable an account which has become disabled.
329 Additionally, the enable function allows an administrator to have a set of created user accounts defined and setup with default permissions that can be easily enabled for use.
331 The command may be run from the root userid or another authorized userid. The -H or --URL= option can be used to execute the command against a remote server.
334 samba-tool user enable Testuser1 --URL=ldap://samba.samdom.example.com --username=administrator --password=passw1rd
336 Example1 shows how to enable a user in the domain against a remote LDAP server. The --URL parameter is used to specify the remote target server. The --username= and --password= options are used to pass the username and password of a user that exists on the remote server and is authorized to update that server.
339 su samba-tool user enable Testuser2
341 Example2 shows how to enable user Testuser2 for use in the domain on the local server. sudo is used so a user may run the command as root.
344 samba-tool user enable --filter=samaccountname=Testuser3
346 Example3 shows how to enable a user in the domain against a local LDAP server. It uses the --filter=samaccountname to specify the username.
349 synopsis = "%prog (<username>|--filter <filter>) [options]"
352 takes_optiongroups = {
353 "sambaopts": options.SambaOptions,
354 "versionopts": options.VersionOptions,
355 "credopts": options.CredentialsOptions,
359 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
360 metavar="URL", dest="H"),
361 Option("--filter", help="LDAP Filter to set password on", type=str),
364 takes_args = ["username?"]
366 def run(self, username=None, sambaopts=None, credopts=None,
367 versionopts=None, filter=None, H=None):
368 if username is None and filter is None:
369 raise CommandError("Either the username or '--filter' must be specified!")
372 filter = "(&(objectClass=user)(sAMAccountName=%s))" % (ldb.binary_encode(username))
374 lp = sambaopts.get_loadparm()
375 creds = credopts.get_credentials(lp, fallback_machine=True)
377 samdb = SamDB(url=H, session_info=system_session(),
378 credentials=creds, lp=lp)
380 samdb.enable_account(filter)
381 except Exception, msg:
382 raise CommandError("Failed to enable user '%s': %s" % (username or filter, msg))
383 self.outf.write("Enabled user '%s'\n" % (username or filter))
386 class cmd_user_disable(Command):
387 """Disable an user."""
389 synopsis = "%prog (<username>|--filter <filter>) [options]"
392 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
393 metavar="URL", dest="H"),
394 Option("--filter", help="LDAP Filter to set password on", type=str),
397 takes_args = ["username?"]
399 takes_optiongroups = {
400 "sambaopts": options.SambaOptions,
401 "credopts": options.CredentialsOptions,
402 "versionopts": options.VersionOptions,
405 def run(self, username=None, sambaopts=None, credopts=None,
406 versionopts=None, filter=None, H=None):
407 if username is None and filter is None:
408 raise CommandError("Either the username or '--filter' must be specified!")
411 filter = "(&(objectClass=user)(sAMAccountName=%s))" % (ldb.binary_encode(username))
413 lp = sambaopts.get_loadparm()
414 creds = credopts.get_credentials(lp, fallback_machine=True)
416 samdb = SamDB(url=H, session_info=system_session(),
417 credentials=creds, lp=lp)
419 samdb.disable_account(filter)
420 except Exception, msg:
421 raise CommandError("Failed to disable user '%s': %s" % (username or filter, msg))
424 class cmd_user_setexpiry(Command):
425 """Set the expiration of a user account.
427 The user can either be specified by their sAMAccountName or using the --filter option.
429 When a user account expires, it becomes disabled and the user is unable to logon. The administrator may issue the samba-tool user enable command to enable the account for logon. The permissions and memberships associated with the account are retained when the account is enabled.
431 The command may be run from the root userid or another authorized userid. The -H or --URL= option can be used to execute the command on a remote server.
434 samba-tool user setexpiry User1 --days=20 --URL=ldap://samba.samdom.example.com --username=administrator --password=passw1rd
436 Example1 shows how to set the expiration of an account in a remote LDAP server. The --URL parameter is used to specify the remote target server. The --username= and --password= options are used to pass the username and password of a user that exists on the remote server and is authorized to update that server.
439 su samba-tool user setexpiry User2
441 Example2 shows how to set the account expiration of user User2 so it will never expire. The user in this example resides on the local server. sudo is used so a user may run the command as root.
444 samba-tool user setexpiry --days=20 --filter=samaccountname=User3
446 Example3 shows how to set the account expiration date to end of day 20 days from the current day. The username or sAMAccountName is specified using the --filter= parameter and the username in this example is User3.
449 samba-tool user setexpiry --noexpiry User4
450 Example4 shows how to set the account expiration so that it will never expire. The username and sAMAccountName in this example is User4.
453 synopsis = "%prog (<username>|--filter <filter>) [options]"
455 takes_optiongroups = {
456 "sambaopts": options.SambaOptions,
457 "versionopts": options.VersionOptions,
458 "credopts": options.CredentialsOptions,
462 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
463 metavar="URL", dest="H"),
464 Option("--filter", help="LDAP Filter to set password on", type=str),
465 Option("--days", help="Days to expiry", type=int, default=0),
466 Option("--noexpiry", help="Password does never expire", action="store_true", default=False),
469 takes_args = ["username?"]
471 def run(self, username=None, sambaopts=None, credopts=None,
472 versionopts=None, H=None, filter=None, days=None, noexpiry=None):
473 if username is None and filter is None:
474 raise CommandError("Either the username or '--filter' must be specified!")
477 filter = "(&(objectClass=user)(sAMAccountName=%s))" % (ldb.binary_encode(username))
479 lp = sambaopts.get_loadparm()
480 creds = credopts.get_credentials(lp)
482 samdb = SamDB(url=H, session_info=system_session(),
483 credentials=creds, lp=lp)
486 samdb.setexpiry(filter, days*24*3600, no_expiry_req=noexpiry)
487 except Exception, msg:
488 # FIXME: Catch more specific exception
489 raise CommandError("Failed to set expiry for user '%s': %s" % (
490 username or filter, msg))
492 self.outf.write("Expiry for user '%s' disabled.\n" % (
495 self.outf.write("Expiry for user '%s' set to %u days.\n" % (
496 username or filter, days))
499 class cmd_user_password(Command):
500 """Change password for a user account (the one provided in authentication).
503 synopsis = "%prog [options]"
506 Option("--newpassword", help="New password", type=str),
509 takes_optiongroups = {
510 "sambaopts": options.SambaOptions,
511 "credopts": options.CredentialsOptions,
512 "versionopts": options.VersionOptions,
515 def run(self, credopts=None, sambaopts=None, versionopts=None,
518 lp = sambaopts.get_loadparm()
519 creds = credopts.get_credentials(lp)
521 # get old password now, to get the password prompts in the right order
522 old_password = creds.get_password()
524 net = Net(creds, lp, server=credopts.ipaddress)
526 password = newpassword
528 if password is not None and password is not '':
530 password = getpass("New Password: ")
531 passwordverify = getpass("Retype Password: ")
532 if not password == passwordverify:
534 self.outf.write("Sorry, passwords do not match.\n")
537 net.change_password(password)
538 except Exception, msg:
539 # FIXME: catch more specific exception
540 raise CommandError("Failed to change password : %s" % msg)
541 self.outf.write("Changed password OK\n")
544 class cmd_user_setpassword(Command):
545 """Set or reset the password of a user account.
547 This command sets or resets the logon password for a user account. The username specified on the command is the sAMAccountName. The username may also be specified using the --filter option.
549 If the password is not specified on the command through the --newpassword parameter, the user is prompted for the password to be entered through the command line.
551 It is good security practice for the administrator to use the --must-change-at-next-login option which requires that when the user logs on to the account for the first time following the password change, he/she must change the password.
553 The command may be run from the root userid or another authorized userid. The -H or --URL= option can be used to execute the command against a remote server.
556 samba-tool user setpassword TestUser1 --newpassword=passw0rd --URL=ldap://samba.samdom.example.com -Uadministrator%passw1rd
558 Example1 shows how to set the password of user TestUser1 on a remote LDAP server. The --URL parameter is used to specify the remote target server. The -U option is used to pass the username and password of a user that exists on the remote server and is authorized to update the server.
561 sudo samba-tool user setpassword TestUser2 --newpassword=passw0rd --must-change-at-next-login
563 Example2 shows how an administrator would reset the TestUser2 user's password to passw0rd. The user is running under the root userid using the sudo command. In this example the user TestUser2 must change their password the next time they logon to the account.
566 samba-tool user setpassword --filter=samaccountname=TestUser3 --newpassword=passw0rd
568 Example3 shows how an administrator would reset TestUser3 user's password to passw0rd using the --filter= option to specify the username.
571 synopsis = "%prog (<username>|--filter <filter>) [options]"
573 takes_optiongroups = {
574 "sambaopts": options.SambaOptions,
575 "versionopts": options.VersionOptions,
576 "credopts": options.CredentialsOptions,
580 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
581 metavar="URL", dest="H"),
582 Option("--filter", help="LDAP Filter to set password on", type=str),
583 Option("--newpassword", help="Set password", type=str),
584 Option("--must-change-at-next-login",
585 help="Force password to be changed on next login",
586 action="store_true"),
587 Option("--random-password",
588 help="Generate random password",
589 action="store_true"),
592 takes_args = ["username?"]
594 def run(self, username=None, filter=None, credopts=None, sambaopts=None,
595 versionopts=None, H=None, newpassword=None,
596 must_change_at_next_login=False, random_password=False):
597 if filter is None and username is None:
598 raise CommandError("Either the username or '--filter' must be specified!")
601 password = generate_random_password(128, 255)
603 password = newpassword
606 if password is not None and password is not '':
608 password = getpass("New Password: ")
611 filter = "(&(objectClass=user)(sAMAccountName=%s))" % (ldb.binary_encode(username))
613 lp = sambaopts.get_loadparm()
614 creds = credopts.get_credentials(lp)
616 creds.set_gensec_features(creds.get_gensec_features() | gensec.FEATURE_SEAL)
618 samdb = SamDB(url=H, session_info=system_session(),
619 credentials=creds, lp=lp)
622 samdb.setpassword(filter, password,
623 force_change_at_next_login=must_change_at_next_login,
625 except Exception, msg:
626 # FIXME: catch more specific exception
627 raise CommandError("Failed to set password for user '%s': %s" % (username or filter, msg))
628 self.outf.write("Changed password OK\n")
631 class cmd_user(SuperCommand):
632 """User management."""
635 subcommands["add"] = cmd_user_add()
636 subcommands["create"] = cmd_user_create()
637 subcommands["delete"] = cmd_user_delete()
638 subcommands["disable"] = cmd_user_disable()
639 subcommands["enable"] = cmd_user_enable()
640 subcommands["list"] = cmd_user_list()
641 subcommands["setexpiry"] = cmd_user_setexpiry()
642 subcommands["password"] = cmd_user_password()
643 subcommands["setpassword"] = cmd_user_setpassword()