3 # Copyright Matthias Dieter Wallnoefer 2009
4 # Copyright Andrew Kroeger 2009
5 # Copyright Jelmer Vernooij 2007-2012
6 # Copyright Giampaolo Lauria 2011
7 # Copyright Matthieu Patou <mat@matws.net> 2011
8 # Copyright Andrew Bartlett 2008
9 # Copyright Stefan Metzmacher 2012
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 3 of the License, or
14 # (at your option) any later version.
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 import samba.getopt as options
32 from samba.net import Net, LIBNET_JOIN_AUTOMATIC
34 from samba.join import join_RODC, join_DC, join_subdomain
35 from samba.auth import system_session
36 from samba.samdb import SamDB
37 from samba.dcerpc import drsuapi
38 from samba.dcerpc.samr import DOMAIN_PASSWORD_COMPLEX, DOMAIN_PASSWORD_STORE_CLEARTEXT
39 from samba.netcmd import (
45 from samba.netcmd.common import netcmd_get_domain_infos_via_cldap
46 from samba.samba3 import Samba3
47 from samba.samba3 import param as s3param
48 from samba.upgrade import upgrade_from_samba3
49 from samba.drs_utils import (
50 sendDsReplicaSync, drsuapi_connect, drsException,
54 from samba.dsdb import (
55 DS_DOMAIN_FUNCTION_2000,
56 DS_DOMAIN_FUNCTION_2003,
57 DS_DOMAIN_FUNCTION_2003_MIXED,
58 DS_DOMAIN_FUNCTION_2008,
59 DS_DOMAIN_FUNCTION_2008_R2,
60 DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL,
61 DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL,
62 UF_WORKSTATION_TRUST_ACCOUNT,
63 UF_SERVER_TRUST_ACCOUNT,
64 UF_TRUSTED_FOR_DELEGATION
67 from samba.credentials import DONT_USE_KERBEROS
68 from samba.provision import (
76 def get_testparm_var(testparm, smbconf, varname):
77 cmd = "%s -s -l --parameter-name='%s' %s 2>/dev/null" % (testparm, varname, smbconf)
78 output = os.popen(cmd, 'r').readline()
83 class cmd_domain_export_keytab(Command):
84 """Dump Kerberos keys of the domain into a keytab."""
86 synopsis = "%prog <keytab> [options]"
88 takes_optiongroups = {
89 "sambaopts": options.SambaOptions,
90 "credopts": options.CredentialsOptions,
91 "versionopts": options.VersionOptions,
95 Option("--principal", help="extract only this principal", type=str),
98 takes_args = ["keytab"]
100 def run(self, keytab, credopts=None, sambaopts=None, versionopts=None, principal=None):
101 lp = sambaopts.get_loadparm()
103 net.export_keytab(keytab=keytab, principal=principal)
105 cmd_domain_export_keytab = None
108 class cmd_domain_info(Command):
109 """Print basic info about a domain and the DC passed as parameter."""
111 synopsis = "%prog <ip_address> [options]"
116 takes_optiongroups = {
117 "sambaopts": options.SambaOptions,
118 "credopts": options.CredentialsOptions,
119 "versionopts": options.VersionOptions,
122 takes_args = ["address"]
124 def run(self, address, credopts=None, sambaopts=None, versionopts=None):
125 lp = sambaopts.get_loadparm()
127 res = netcmd_get_domain_infos_via_cldap(lp, None, address)
129 raise CommandError("Invalid IP address '" + address + "'!")
130 self.outf.write("Forest : %s\n" % res.forest)
131 self.outf.write("Domain : %s\n" % res.dns_domain)
132 self.outf.write("Netbios domain : %s\n" % res.domain_name)
133 self.outf.write("DC name : %s\n" % res.pdc_dns_name)
134 self.outf.write("DC netbios name : %s\n" % res.pdc_name)
135 self.outf.write("Server site : %s\n" % res.server_site)
136 self.outf.write("Client site : %s\n" % res.client_site)
139 class cmd_domain_provision(Command):
140 """Provision a domain."""
142 synopsis = "%prog [options]"
144 takes_optiongroups = {
145 "sambaopts": options.SambaOptions,
146 "versionopts": options.VersionOptions,
147 "credopts": options.CredentialsOptions,
151 Option("--interactive", help="Ask for names", action="store_true"),
152 Option("--domain", type="string", metavar="DOMAIN",
154 Option("--domain-guid", type="string", metavar="GUID",
155 help="set domainguid (otherwise random)"),
156 Option("--domain-sid", type="string", metavar="SID",
157 help="set domainsid (otherwise random)"),
158 Option("--ntds-guid", type="string", metavar="GUID",
159 help="set NTDS object GUID (otherwise random)"),
160 Option("--invocationid", type="string", metavar="GUID",
161 help="set invocationid (otherwise random)"),
162 Option("--host-name", type="string", metavar="HOSTNAME",
163 help="set hostname"),
164 Option("--host-ip", type="string", metavar="IPADDRESS",
165 help="set IPv4 ipaddress"),
166 Option("--host-ip6", type="string", metavar="IP6ADDRESS",
167 help="set IPv6 ipaddress"),
168 Option("--adminpass", type="string", metavar="PASSWORD",
169 help="choose admin password (otherwise random)"),
170 Option("--krbtgtpass", type="string", metavar="PASSWORD",
171 help="choose krbtgt password (otherwise random)"),
172 Option("--machinepass", type="string", metavar="PASSWORD",
173 help="choose machine password (otherwise random)"),
174 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
175 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
176 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
177 "BIND9_FLATFILE uses bind9 text database to store zone information, "
178 "BIND9_DLZ uses samba4 AD to store zone information, "
179 "NONE skips the DNS setup entirely (not recommended)",
180 default="SAMBA_INTERNAL"),
181 Option("--dnspass", type="string", metavar="PASSWORD",
182 help="choose dns password (otherwise random)"),
183 Option("--ldapadminpass", type="string", metavar="PASSWORD",
184 help="choose password to set between Samba and it's LDAP backend (otherwise random)"),
185 Option("--root", type="string", metavar="USERNAME",
186 help="choose 'root' unix username"),
187 Option("--nobody", type="string", metavar="USERNAME",
188 help="choose 'nobody' user"),
189 Option("--users", type="string", metavar="GROUPNAME",
190 help="choose 'users' group"),
191 Option("--quiet", help="Be quiet", action="store_true"),
192 Option("--blank", action="store_true",
193 help="do not add users or groups, just the structure"),
194 Option("--ldap-backend-type", type="choice", metavar="LDAP-BACKEND-TYPE",
195 help="Test initialisation support for unsupported LDAP backend type (fedora-ds or openldap) DO NOT USE",
196 choices=["fedora-ds", "openldap"]),
197 Option("--server-role", type="choice", metavar="ROLE",
198 choices=["domain controller", "dc", "member server", "member", "standalone"],
199 help="The server role (domain controller | dc | member server | member | standalone). Default is dc.",
200 default="domain controller"),
201 Option("--function-level", type="choice", metavar="FOR-FUN-LEVEL",
202 choices=["2000", "2003", "2008", "2008_R2"],
203 help="The domain and forest function level (2000 | 2003 | 2008 | 2008_R2 - always native). Default is (Windows) 2003 Native.",
205 Option("--next-rid", type="int", metavar="NEXTRID", default=1000,
206 help="The initial nextRid value (only needed for upgrades). Default is 1000."),
207 Option("--partitions-only",
208 help="Configure Samba's partitions, but do not modify them (ie, join a BDC)", action="store_true"),
209 Option("--targetdir", type="string", metavar="DIR",
210 help="Set target directory"),
211 Option("--ol-mmr-urls", type="string", metavar="LDAPSERVER",
212 help="List of LDAP-URLS [ ldap://<FQHN>:<PORT>/ (where <PORT> has to be different than 389!) ] separated with comma (\",\") for use with OpenLDAP-MMR (Multi-Master-Replication), e.g.: \"ldap://s4dc1:9000,ldap://s4dc2:9000\""),
213 Option("--use-xattrs", type="choice", choices=["yes", "no", "auto"], help="Define if we should use the native fs capabilities or a tdb file for storing attributes likes ntacl, auto tries to make an inteligent guess based on the user rights and system capabilities", default="auto"),
214 Option("--use-ntvfs", action="store_true", help="Use NTVFS for the fileserver (default = no)"),
215 Option("--use-rfc2307", action="store_true", help="Use AD to store posix attributes (default = no)"),
219 def run(self, sambaopts=None, credopts=None, versionopts=None,
241 ldap_backend_type=None,
245 partitions_only=None,
252 self.logger = self.get_logger("provision")
254 self.logger.setLevel(logging.WARNING)
256 self.logger.setLevel(logging.INFO)
258 lp = sambaopts.get_loadparm()
259 smbconf = lp.configfile
261 creds = credopts.get_credentials(lp)
263 creds.set_kerberos_state(DONT_USE_KERBEROS)
265 if dns_forwarder is not None:
266 suggested_forwarder = dns_forwarder
268 suggested_forwarder = self._get_nameserver_ip()
269 if suggested_forwarder is None:
270 suggested_forwarder = "none"
272 if len(self.raw_argv) == 1:
276 from getpass import getpass
279 def ask(prompt, default=None):
280 if default is not None:
281 print "%s [%s]: " % (prompt, default),
283 print "%s: " % (prompt,),
284 return sys.stdin.readline().rstrip("\n") or default
287 default = socket.getfqdn().split(".", 1)[1].upper()
290 realm = ask("Realm", default)
291 if realm in (None, ""):
292 raise CommandError("No realm set!")
295 default = realm.split(".")[0]
298 domain = ask("Domain", default)
300 raise CommandError("No domain set!")
302 server_role = ask("Server Role (dc, member, standalone)", "dc")
304 dns_backend = ask("DNS backend (SAMBA_INTERNAL, BIND9_FLATFILE, BIND9_DLZ, NONE)", "SAMBA_INTERNAL")
305 if dns_backend in (None, ''):
306 raise CommandError("No DNS backend set!")
308 if dns_backend == "SAMBA_INTERNAL":
309 dns_forwarder = ask("DNS forwarder IP address (write 'none' to disable forwarding)", suggested_forwarder)
310 if dns_forwarder.lower() in (None, 'none'):
311 suggested_forwarder = None
315 adminpassplain = getpass("Administrator password: ")
316 if not adminpassplain:
317 self.errf.write("Invalid administrator password.\n")
319 adminpassverify = getpass("Retype password: ")
320 if not adminpassplain == adminpassverify:
321 self.errf.write("Sorry, passwords do not match.\n")
323 adminpass = adminpassplain
327 realm = sambaopts._lp.get('realm')
329 raise CommandError("No realm set!")
331 raise CommandError("No domain set!")
334 self.logger.info("Administrator password will be set randomly!")
336 if function_level == "2000":
337 dom_for_fun_level = DS_DOMAIN_FUNCTION_2000
338 elif function_level == "2003":
339 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
340 elif function_level == "2008":
341 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008
342 elif function_level == "2008_R2":
343 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
345 if dns_backend == "SAMBA_INTERNAL" and dns_forwarder is None:
346 dns_forwarder = suggested_forwarder
348 samdb_fill = FILL_FULL
350 samdb_fill = FILL_NT4SYNC
351 elif partitions_only:
352 samdb_fill = FILL_DRS
354 if targetdir is not None:
355 if not os.path.isdir(targetdir):
360 if use_xattrs == "yes":
362 elif use_xattrs == "auto" and not lp.get("posix:eadb"):
364 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
366 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
369 samba.ntacls.setntacl(lp, file.name,
370 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
373 self.logger.info("You are not root or your system do not support xattr, using tdb backend for attributes. ")
378 self.logger.info("not using extended attributes to store ACLs and other metadata. If you intend to use this provision in production, rerun the script as root on a system supporting xattrs.")
380 session = system_session()
382 result = provision(self.logger,
383 session, creds, smbconf=smbconf, targetdir=targetdir,
384 samdb_fill=samdb_fill, realm=realm, domain=domain,
385 domainguid=domain_guid, domainsid=domain_sid,
387 hostip=host_ip, hostip6=host_ip6,
389 invocationid=invocationid, adminpass=adminpass,
390 krbtgtpass=krbtgtpass, machinepass=machinepass,
391 dns_backend=dns_backend, dns_forwarder=dns_forwarder,
392 dnspass=dnspass, root=root, nobody=nobody,
394 serverrole=server_role, dom_for_fun_level=dom_for_fun_level,
395 backend_type=ldap_backend_type,
396 ldapadminpass=ldapadminpass, ol_mmr_urls=ol_mmr_urls,
397 useeadb=eadb, next_rid=next_rid, lp=lp, use_ntvfs=use_ntvfs,
398 use_rfc2307=use_rfc2307, skip_sysvolacl=False)
399 except ProvisioningError, e:
400 raise CommandError("Provision failed", e)
402 result.report_logger(self.logger)
404 def _get_nameserver_ip(self):
405 """Grab the nameserver IP address from /etc/resolv.conf."""
407 RESOLV_CONF="/etc/resolv.conf"
409 if not path.isfile(RESOLV_CONF):
410 self.logger.warning("Failed to locate %s" % RESOLV_CONF)
415 handle = open(RESOLV_CONF, 'r')
417 if not line.startswith('nameserver'):
419 # we want the last non-space continuous string of the line
420 return line.strip().split()[-1]
422 if handle is not None:
425 self.logger.warning("No nameserver found in %s" % RESOLV_CONF)
428 class cmd_domain_dcpromo(Command):
429 """Promote an existing domain member or NT4 PDC to an AD DC."""
431 synopsis = "%prog <dnsdomain> [DC|RODC] [options]"
433 takes_optiongroups = {
434 "sambaopts": options.SambaOptions,
435 "versionopts": options.VersionOptions,
436 "credopts": options.CredentialsOptions,
440 Option("--server", help="DC to join", type=str),
441 Option("--site", help="site to join", type=str),
442 Option("--targetdir", help="where to store provision", type=str),
443 Option("--domain-critical-only",
444 help="only replicate critical domain objects",
445 action="store_true"),
446 Option("--machinepass", type=str, metavar="PASSWORD",
447 help="choose machine password (otherwise random)"),
448 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
449 action="store_true"),
450 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
451 choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
452 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
453 "BIND9_DLZ uses samba4 AD to store zone information, "
454 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
455 default="SAMBA_INTERNAL"),
456 Option("--quiet", help="Be quiet", action="store_true"),
457 Option("--verbose", help="Be verbose", action="store_true")
460 takes_args = ["domain", "role?"]
462 def run(self, domain, role=None, sambaopts=None, credopts=None,
463 versionopts=None, server=None, site=None, targetdir=None,
464 domain_critical_only=False, parent_domain=None, machinepass=None,
465 use_ntvfs=False, dns_backend=None,
466 quiet=False, verbose=False):
467 lp = sambaopts.get_loadparm()
468 creds = credopts.get_credentials(lp)
469 net = Net(creds, lp, server=credopts.ipaddress)
472 site = "Default-First-Site-Name"
474 logger = self.get_logger()
476 logger.setLevel(logging.DEBUG)
478 logger.setLevel(logging.WARNING)
480 logger.setLevel(logging.INFO)
483 site = "Default-First-Site-Name"
485 netbios_name = lp.get("netbios name")
491 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
492 site=site, netbios_name=netbios_name, targetdir=targetdir,
493 domain_critical_only=domain_critical_only,
494 machinepass=machinepass, use_ntvfs=use_ntvfs,
495 dns_backend=dns_backend,
496 promote_existing=True)
498 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
499 site=site, netbios_name=netbios_name, targetdir=targetdir,
500 domain_critical_only=domain_critical_only,
501 machinepass=machinepass, use_ntvfs=use_ntvfs, dns_backend=dns_backend,
502 promote_existing=True)
504 raise CommandError("Invalid role '%s' (possible values: DC, RODC)" % role)
507 class cmd_domain_join(Command):
508 """Join domain as either member or backup domain controller."""
510 synopsis = "%prog <dnsdomain> [DC|RODC|MEMBER|SUBDOMAIN] [options]"
512 takes_optiongroups = {
513 "sambaopts": options.SambaOptions,
514 "versionopts": options.VersionOptions,
515 "credopts": options.CredentialsOptions,
519 Option("--server", help="DC to join", type=str),
520 Option("--site", help="site to join", type=str),
521 Option("--targetdir", help="where to store provision", type=str),
522 Option("--parent-domain", help="parent domain to create subdomain under", type=str),
523 Option("--domain-critical-only",
524 help="only replicate critical domain objects",
525 action="store_true"),
526 Option("--machinepass", type=str, metavar="PASSWORD",
527 help="choose machine password (otherwise random)"),
528 Option("--adminpass", type="string", metavar="PASSWORD",
529 help="choose adminstrator password when joining as a subdomain (otherwise random)"),
530 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
531 action="store_true"),
532 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
533 choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
534 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
535 "BIND9_DLZ uses samba4 AD to store zone information, "
536 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
537 default="SAMBA_INTERNAL"),
538 Option("--quiet", help="Be quiet", action="store_true"),
539 Option("--verbose", help="Be verbose", action="store_true")
542 takes_args = ["domain", "role?"]
544 def run(self, domain, role=None, sambaopts=None, credopts=None,
545 versionopts=None, server=None, site=None, targetdir=None,
546 domain_critical_only=False, parent_domain=None, machinepass=None,
547 use_ntvfs=False, dns_backend=None, adminpass=None,
548 quiet=False, verbose=False):
549 lp = sambaopts.get_loadparm()
550 creds = credopts.get_credentials(lp)
551 net = Net(creds, lp, server=credopts.ipaddress)
554 site = "Default-First-Site-Name"
556 logger = self.get_logger()
558 logger.setLevel(logging.DEBUG)
560 logger.setLevel(logging.WARNING)
562 logger.setLevel(logging.INFO)
564 netbios_name = lp.get("netbios name")
569 if role is None or role == "MEMBER":
570 (join_password, sid, domain_name) = net.join_member(
571 domain, netbios_name, LIBNET_JOIN_AUTOMATIC,
572 machinepass=machinepass)
574 self.errf.write("Joined domain %s (%s)\n" % (domain_name, sid))
576 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
577 site=site, netbios_name=netbios_name, targetdir=targetdir,
578 domain_critical_only=domain_critical_only,
579 machinepass=machinepass, use_ntvfs=use_ntvfs, dns_backend=dns_backend)
581 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
582 site=site, netbios_name=netbios_name, targetdir=targetdir,
583 domain_critical_only=domain_critical_only,
584 machinepass=machinepass, use_ntvfs=use_ntvfs,
585 dns_backend=dns_backend)
586 elif role == "SUBDOMAIN":
588 logger.info("Administrator password will be set randomly!")
590 netbios_domain = lp.get("workgroup")
591 if parent_domain is None:
592 parent_domain = ".".join(domain.split(".")[1:])
593 join_subdomain(logger=logger, server=server, creds=creds, lp=lp, dnsdomain=domain,
594 parent_domain=parent_domain, site=site,
595 netbios_name=netbios_name, netbios_domain=netbios_domain,
596 targetdir=targetdir, machinepass=machinepass,
597 use_ntvfs=use_ntvfs, dns_backend=dns_backend,
600 raise CommandError("Invalid role '%s' (possible values: MEMBER, DC, RODC, SUBDOMAIN)" % role)
603 class cmd_domain_demote(Command):
604 """Demote ourselves from the role of Domain Controller."""
606 synopsis = "%prog [options]"
609 Option("--server", help="DC to force replication before demote", type=str),
610 Option("--targetdir", help="where provision is stored", type=str),
613 takes_optiongroups = {
614 "sambaopts": options.SambaOptions,
615 "credopts": options.CredentialsOptions,
616 "versionopts": options.VersionOptions,
619 def run(self, sambaopts=None, credopts=None,
620 versionopts=None, server=None, targetdir=None):
621 lp = sambaopts.get_loadparm()
622 creds = credopts.get_credentials(lp)
623 net = Net(creds, lp, server=credopts.ipaddress)
625 netbios_name = lp.get("netbios name")
626 samdb = SamDB(session_info=system_session(), credentials=creds, lp=lp)
628 res = samdb.search(expression='(&(objectClass=computer)(serverReferenceBL=*))', attrs=["dnsHostName", "name"])
630 raise CommandError("Unable to search for servers")
633 raise CommandError("You are the latest server in the domain")
637 if str(e["name"]).lower() != netbios_name.lower():
638 server = e["dnsHostName"]
641 ntds_guid = samdb.get_ntds_GUID()
642 msg = samdb.search(base=str(samdb.get_config_basedn()),
643 scope=ldb.SCOPE_SUBTREE, expression="(objectGUID=%s)" % ntds_guid,
645 if len(msg) == 0 or "options" not in msg[0]:
646 raise CommandError("Failed to find options on %s" % ntds_guid)
649 dsa_options = int(str(msg[0]['options']))
651 res = samdb.search(expression="(fSMORoleOwner=%s)" % str(ntds_dn),
652 controls=["search_options:1:2"])
655 raise CommandError("Current DC is still the owner of %d role(s), use the role command to transfer roles to another DC" % len(res))
657 self.errf.write("Using %s as partner server for the demotion\n" %
659 (drsuapiBind, drsuapi_handle, supportedExtensions) = drsuapi_connect(server, lp, creds)
661 self.errf.write("Desactivating inbound replication\n")
666 dsa_options |= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
667 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
670 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
672 self.errf.write("Asking partner server %s to synchronize from us\n"
674 for part in (samdb.get_schema_basedn(),
675 samdb.get_config_basedn(),
676 samdb.get_root_basedn()):
678 sendDsReplicaSync(drsuapiBind, drsuapi_handle, ntds_guid, str(part), drsuapi.DRSUAPI_DRS_WRIT_REP)
679 except drsException, e:
681 "Error while demoting, "
682 "re-enabling inbound replication\n")
683 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
684 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
686 raise CommandError("Error while sending a DsReplicaSync for partion %s" % str(part), e)
688 remote_samdb = SamDB(url="ldap://%s" % server,
689 session_info=system_session(),
690 credentials=creds, lp=lp)
692 self.errf.write("Changing userControl and container\n")
693 res = remote_samdb.search(base=str(remote_samdb.get_root_basedn()),
694 expression="(&(objectClass=user)(sAMAccountName=%s$))" %
695 netbios_name.upper(),
696 attrs=["userAccountControl"])
698 uac = int(str(res[0]["userAccountControl"]))
702 "Error while demoting, re-enabling inbound replication\n")
703 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
704 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
706 raise CommandError("Error while changing account control", e)
710 "Error while demoting, re-enabling inbound replication")
711 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
712 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
714 raise CommandError("Unable to find object with samaccountName = %s$"
715 " in the remote dc" % netbios_name.upper())
719 uac ^= (UF_SERVER_TRUST_ACCOUNT|UF_TRUSTED_FOR_DELEGATION)
720 uac |= UF_WORKSTATION_TRUST_ACCOUNT
725 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
726 ldb.FLAG_MOD_REPLACE,
727 "userAccountControl")
729 remote_samdb.modify(msg)
732 "Error while demoting, re-enabling inbound replication")
733 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
734 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
737 raise CommandError("Error while changing account control", e)
739 parent = msg.dn.parent()
741 rdn = string.replace(rdn, ",%s" % str(parent), "")
742 # Let's move to the Computer container
746 computer_dn = ldb.Dn(remote_samdb, "CN=Computers,%s" % str(remote_samdb.get_root_basedn()))
747 res = remote_samdb.search(base=computer_dn, expression=rdn, scope=ldb.SCOPE_ONELEVEL)
750 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
751 scope=ldb.SCOPE_ONELEVEL)
752 while(len(res) != 0 and i < 100):
754 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
755 scope=ldb.SCOPE_ONELEVEL)
759 "Error while demoting, re-enabling inbound replication\n")
760 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
761 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
767 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
768 ldb.FLAG_MOD_REPLACE,
769 "userAccountControl")
771 remote_samdb.modify(msg)
773 raise CommandError("Unable to find a slot for renaming %s,"
774 " all names from %s-1 to %s-%d seemed used" %
775 (str(dc_dn), rdn, rdn, i - 9))
777 newrdn = "%s-%d" % (rdn, i)
780 newdn = ldb.Dn(remote_samdb, "%s,%s" % (newrdn, str(computer_dn)))
781 remote_samdb.rename(dc_dn, newdn)
784 "Error while demoting, re-enabling inbound replication\n")
785 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
786 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
792 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
793 ldb.FLAG_MOD_REPLACE,
794 "userAccountControl")
796 remote_samdb.modify(msg)
797 raise CommandError("Error while renaming %s to %s" % (str(dc_dn), str(newdn)), e)
800 server_dsa_dn = samdb.get_serverName()
801 domain = remote_samdb.get_root_basedn()
804 sendRemoveDsServer(drsuapiBind, drsuapi_handle, server_dsa_dn, domain)
805 except drsException, e:
807 "Error while demoting, re-enabling inbound replication\n")
808 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
809 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
815 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
816 ldb.FLAG_MOD_REPLACE,
817 "userAccountControl")
819 remote_samdb.modify(msg)
820 remote_samdb.rename(newdn, dc_dn)
821 raise CommandError("Error while sending a removeDsServer", e)
823 for s in ("CN=Entreprise,CN=Microsoft System Volumes,CN=System,CN=Configuration",
824 "CN=%s,CN=Microsoft System Volumes,CN=System,CN=Configuration" % lp.get("realm"),
825 "CN=Domain System Volumes (SYSVOL share),CN=File Replication Service,CN=System"):
827 remote_samdb.delete(ldb.Dn(remote_samdb,
828 "%s,%s,%s" % (str(rdn), s, str(remote_samdb.get_root_basedn()))))
829 except ldb.LdbError, l:
832 for s in ("CN=Entreprise,CN=NTFRS Subscriptions",
833 "CN=%s, CN=NTFRS Subscriptions" % lp.get("realm"),
834 "CN=Domain system Volumes (SYSVOL Share), CN=NTFRS Subscriptions",
835 "CN=NTFRS Subscriptions"):
837 remote_samdb.delete(ldb.Dn(remote_samdb,
838 "%s,%s" % (s, str(newdn))))
839 except ldb.LdbError, l:
842 self.errf.write("Demote successfull\n")
845 class cmd_domain_level(Command):
846 """Raise domain and forest function levels."""
848 synopsis = "%prog (show|raise <options>) [options]"
850 takes_optiongroups = {
851 "sambaopts": options.SambaOptions,
852 "credopts": options.CredentialsOptions,
853 "versionopts": options.VersionOptions,
857 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
858 metavar="URL", dest="H"),
859 Option("--quiet", help="Be quiet", action="store_true"),
860 Option("--forest-level", type="choice", choices=["2003", "2008", "2008_R2"],
861 help="The forest function level (2003 | 2008 | 2008_R2)"),
862 Option("--domain-level", type="choice", choices=["2003", "2008", "2008_R2"],
863 help="The domain function level (2003 | 2008 | 2008_R2)")
866 takes_args = ["subcommand"]
868 def run(self, subcommand, H=None, forest_level=None, domain_level=None,
869 quiet=False, credopts=None, sambaopts=None, versionopts=None):
870 lp = sambaopts.get_loadparm()
871 creds = credopts.get_credentials(lp, fallback_machine=True)
873 samdb = SamDB(url=H, session_info=system_session(),
874 credentials=creds, lp=lp)
876 domain_dn = samdb.domain_dn()
878 res_forest = samdb.search("CN=Partitions,%s" % samdb.get_config_basedn(),
879 scope=ldb.SCOPE_BASE, attrs=["msDS-Behavior-Version"])
880 assert len(res_forest) == 1
882 res_domain = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
883 attrs=["msDS-Behavior-Version", "nTMixedDomain"])
884 assert len(res_domain) == 1
886 res_dc_s = samdb.search("CN=Sites,%s" % samdb.get_config_basedn(),
887 scope=ldb.SCOPE_SUBTREE, expression="(objectClass=nTDSDSA)",
888 attrs=["msDS-Behavior-Version"])
889 assert len(res_dc_s) >= 1
892 level_forest = int(res_forest[0]["msDS-Behavior-Version"][0])
893 level_domain = int(res_domain[0]["msDS-Behavior-Version"][0])
894 level_domain_mixed = int(res_domain[0]["nTMixedDomain"][0])
896 min_level_dc = int(res_dc_s[0]["msDS-Behavior-Version"][0]) # Init value
898 if int(msg["msDS-Behavior-Version"][0]) < min_level_dc:
899 min_level_dc = int(msg["msDS-Behavior-Version"][0])
901 if level_forest < 0 or level_domain < 0:
902 raise CommandError("Domain and/or forest function level(s) is/are invalid. Correct them or reprovision!")
904 raise CommandError("Lowest function level of a DC is invalid. Correct this or reprovision!")
905 if level_forest > level_domain:
906 raise CommandError("Forest function level is higher than the domain level(s). Correct this or reprovision!")
907 if level_domain > min_level_dc:
908 raise CommandError("Domain function level is higher than the lowest function level of a DC. Correct this or reprovision!")
911 raise CommandError("Could not retrieve the actual domain, forest level and/or lowest DC function level!")
913 if subcommand == "show":
914 self.message("Domain and forest function level for domain '%s'" % domain_dn)
915 if level_forest == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
916 self.message("\nATTENTION: You run SAMBA 4 on a forest function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
917 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
918 self.message("\nATTENTION: You run SAMBA 4 on a domain function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
919 if min_level_dc == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
920 self.message("\nATTENTION: You run SAMBA 4 on a lowest function level of a DC lower than Windows 2003. This isn't supported! Please step-up or upgrade the concerning DC(s)!")
924 if level_forest == DS_DOMAIN_FUNCTION_2000:
926 elif level_forest == DS_DOMAIN_FUNCTION_2003_MIXED:
927 outstr = "2003 with mixed domains/interim (NT4 DC support)"
928 elif level_forest == DS_DOMAIN_FUNCTION_2003:
930 elif level_forest == DS_DOMAIN_FUNCTION_2008:
932 elif level_forest == DS_DOMAIN_FUNCTION_2008_R2:
935 outstr = "higher than 2008 R2"
936 self.message("Forest function level: (Windows) " + outstr)
938 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
939 outstr = "2000 mixed (NT4 DC support)"
940 elif level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed == 0:
942 elif level_domain == DS_DOMAIN_FUNCTION_2003_MIXED:
943 outstr = "2003 with mixed domains/interim (NT4 DC support)"
944 elif level_domain == DS_DOMAIN_FUNCTION_2003:
946 elif level_domain == DS_DOMAIN_FUNCTION_2008:
948 elif level_domain == DS_DOMAIN_FUNCTION_2008_R2:
951 outstr = "higher than 2008 R2"
952 self.message("Domain function level: (Windows) " + outstr)
954 if min_level_dc == DS_DOMAIN_FUNCTION_2000:
956 elif min_level_dc == DS_DOMAIN_FUNCTION_2003:
958 elif min_level_dc == DS_DOMAIN_FUNCTION_2008:
960 elif min_level_dc == DS_DOMAIN_FUNCTION_2008_R2:
963 outstr = "higher than 2008 R2"
964 self.message("Lowest function level of a DC: (Windows) " + outstr)
966 elif subcommand == "raise":
969 if domain_level is not None:
970 if domain_level == "2003":
971 new_level_domain = DS_DOMAIN_FUNCTION_2003
972 elif domain_level == "2008":
973 new_level_domain = DS_DOMAIN_FUNCTION_2008
974 elif domain_level == "2008_R2":
975 new_level_domain = DS_DOMAIN_FUNCTION_2008_R2
977 if new_level_domain <= level_domain and level_domain_mixed == 0:
978 raise CommandError("Domain function level can't be smaller than or equal to the actual one!")
980 if new_level_domain > min_level_dc:
981 raise CommandError("Domain function level can't be higher than the lowest function level of a DC!")
983 # Deactivate mixed/interim domain support
984 if level_domain_mixed != 0:
985 # Directly on the base DN
987 m.dn = ldb.Dn(samdb, domain_dn)
988 m["nTMixedDomain"] = ldb.MessageElement("0",
989 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
993 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup") + ",CN=Partitions,%s" % samdb.get_config_basedn())
994 m["nTMixedDomain"] = ldb.MessageElement("0",
995 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
998 except ldb.LdbError, (enum, emsg):
999 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1002 # Directly on the base DN
1004 m.dn = ldb.Dn(samdb, domain_dn)
1005 m["msDS-Behavior-Version"]= ldb.MessageElement(
1006 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1007 "msDS-Behavior-Version")
1011 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup")
1012 + ",CN=Partitions,%s" % samdb.get_config_basedn())
1013 m["msDS-Behavior-Version"]= ldb.MessageElement(
1014 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1015 "msDS-Behavior-Version")
1018 except ldb.LdbError, (enum, emsg):
1019 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1022 level_domain = new_level_domain
1023 msgs.append("Domain function level changed!")
1025 if forest_level is not None:
1026 if forest_level == "2003":
1027 new_level_forest = DS_DOMAIN_FUNCTION_2003
1028 elif forest_level == "2008":
1029 new_level_forest = DS_DOMAIN_FUNCTION_2008
1030 elif forest_level == "2008_R2":
1031 new_level_forest = DS_DOMAIN_FUNCTION_2008_R2
1032 if new_level_forest <= level_forest:
1033 raise CommandError("Forest function level can't be smaller than or equal to the actual one!")
1034 if new_level_forest > level_domain:
1035 raise CommandError("Forest function level can't be higher than the domain function level(s). Please raise it/them first!")
1037 m.dn = ldb.Dn(samdb, "CN=Partitions,%s" % samdb.get_config_basedn())
1038 m["msDS-Behavior-Version"]= ldb.MessageElement(
1039 str(new_level_forest), ldb.FLAG_MOD_REPLACE,
1040 "msDS-Behavior-Version")
1042 msgs.append("Forest function level changed!")
1043 msgs.append("All changes applied successfully!")
1044 self.message("\n".join(msgs))
1046 raise CommandError("invalid argument: '%s' (choose from 'show', 'raise')" % subcommand)
1049 class cmd_domain_passwordsettings(Command):
1050 """Set password settings.
1052 Password complexity, history length, minimum password length, the minimum
1053 and maximum password age) on a Samba4 server.
1056 synopsis = "%prog (show|set <options>) [options]"
1058 takes_optiongroups = {
1059 "sambaopts": options.SambaOptions,
1060 "versionopts": options.VersionOptions,
1061 "credopts": options.CredentialsOptions,
1065 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1066 metavar="URL", dest="H"),
1067 Option("--quiet", help="Be quiet", action="store_true"),
1068 Option("--complexity", type="choice", choices=["on","off","default"],
1069 help="The password complexity (on | off | default). Default is 'on'"),
1070 Option("--store-plaintext", type="choice", choices=["on","off","default"],
1071 help="Store plaintext passwords where account have 'store passwords with reversible encryption' set (on | off | default). Default is 'off'"),
1072 Option("--history-length",
1073 help="The password history length (<integer> | default). Default is 24.", type=str),
1074 Option("--min-pwd-length",
1075 help="The minimum password length (<integer> | default). Default is 7.", type=str),
1076 Option("--min-pwd-age",
1077 help="The minimum password age (<integer in days> | default). Default is 1.", type=str),
1078 Option("--max-pwd-age",
1079 help="The maximum password age (<integer in days> | default). Default is 43.", type=str),
1082 takes_args = ["subcommand"]
1084 def run(self, subcommand, H=None, min_pwd_age=None, max_pwd_age=None,
1085 quiet=False, complexity=None, store_plaintext=None, history_length=None,
1086 min_pwd_length=None, credopts=None, sambaopts=None,
1088 lp = sambaopts.get_loadparm()
1089 creds = credopts.get_credentials(lp)
1091 samdb = SamDB(url=H, session_info=system_session(),
1092 credentials=creds, lp=lp)
1094 domain_dn = samdb.domain_dn()
1095 res = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1096 attrs=["pwdProperties", "pwdHistoryLength", "minPwdLength",
1097 "minPwdAge", "maxPwdAge"])
1098 assert(len(res) == 1)
1100 pwd_props = int(res[0]["pwdProperties"][0])
1101 pwd_hist_len = int(res[0]["pwdHistoryLength"][0])
1102 cur_min_pwd_len = int(res[0]["minPwdLength"][0])
1104 cur_min_pwd_age = int(abs(int(res[0]["minPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1105 if int(res[0]["maxPwdAge"][0]) == -0x8000000000000000:
1108 cur_max_pwd_age = int(abs(int(res[0]["maxPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1109 except Exception, e:
1110 raise CommandError("Could not retrieve password properties!", e)
1112 if subcommand == "show":
1113 self.message("Password informations for domain '%s'" % domain_dn)
1115 if pwd_props & DOMAIN_PASSWORD_COMPLEX != 0:
1116 self.message("Password complexity: on")
1118 self.message("Password complexity: off")
1119 if pwd_props & DOMAIN_PASSWORD_STORE_CLEARTEXT != 0:
1120 self.message("Store plaintext passwords: on")
1122 self.message("Store plaintext passwords: off")
1123 self.message("Password history length: %d" % pwd_hist_len)
1124 self.message("Minimum password length: %d" % cur_min_pwd_len)
1125 self.message("Minimum password age (days): %d" % cur_min_pwd_age)
1126 self.message("Maximum password age (days): %d" % cur_max_pwd_age)
1127 elif subcommand == "set":
1130 m.dn = ldb.Dn(samdb, domain_dn)
1132 if complexity is not None:
1133 if complexity == "on" or complexity == "default":
1134 pwd_props = pwd_props | DOMAIN_PASSWORD_COMPLEX
1135 msgs.append("Password complexity activated!")
1136 elif complexity == "off":
1137 pwd_props = pwd_props & (~DOMAIN_PASSWORD_COMPLEX)
1138 msgs.append("Password complexity deactivated!")
1140 if store_plaintext is not None:
1141 if store_plaintext == "on" or store_plaintext == "default":
1142 pwd_props = pwd_props | DOMAIN_PASSWORD_STORE_CLEARTEXT
1143 msgs.append("Plaintext password storage for changed passwords activated!")
1144 elif store_plaintext == "off":
1145 pwd_props = pwd_props & (~DOMAIN_PASSWORD_STORE_CLEARTEXT)
1146 msgs.append("Plaintext password storage for changed passwords deactivated!")
1148 if complexity is not None or store_plaintext is not None:
1149 m["pwdProperties"] = ldb.MessageElement(str(pwd_props),
1150 ldb.FLAG_MOD_REPLACE, "pwdProperties")
1152 if history_length is not None:
1153 if history_length == "default":
1156 pwd_hist_len = int(history_length)
1158 if pwd_hist_len < 0 or pwd_hist_len > 24:
1159 raise CommandError("Password history length must be in the range of 0 to 24!")
1161 m["pwdHistoryLength"] = ldb.MessageElement(str(pwd_hist_len),
1162 ldb.FLAG_MOD_REPLACE, "pwdHistoryLength")
1163 msgs.append("Password history length changed!")
1165 if min_pwd_length is not None:
1166 if min_pwd_length == "default":
1169 min_pwd_len = int(min_pwd_length)
1171 if min_pwd_len < 0 or min_pwd_len > 14:
1172 raise CommandError("Minimum password length must be in the range of 0 to 14!")
1174 m["minPwdLength"] = ldb.MessageElement(str(min_pwd_len),
1175 ldb.FLAG_MOD_REPLACE, "minPwdLength")
1176 msgs.append("Minimum password length changed!")
1178 if min_pwd_age is not None:
1179 if min_pwd_age == "default":
1182 min_pwd_age = int(min_pwd_age)
1184 if min_pwd_age < 0 or min_pwd_age > 998:
1185 raise CommandError("Minimum password age must be in the range of 0 to 998!")
1188 min_pwd_age_ticks = -int(min_pwd_age * (24 * 60 * 60 * 1e7))
1190 m["minPwdAge"] = ldb.MessageElement(str(min_pwd_age_ticks),
1191 ldb.FLAG_MOD_REPLACE, "minPwdAge")
1192 msgs.append("Minimum password age changed!")
1194 if max_pwd_age is not None:
1195 if max_pwd_age == "default":
1198 max_pwd_age = int(max_pwd_age)
1200 if max_pwd_age < 0 or max_pwd_age > 999:
1201 raise CommandError("Maximum password age must be in the range of 0 to 999!")
1204 if max_pwd_age == 0:
1205 max_pwd_age_ticks = -0x8000000000000000
1207 max_pwd_age_ticks = -int(max_pwd_age * (24 * 60 * 60 * 1e7))
1209 m["maxPwdAge"] = ldb.MessageElement(str(max_pwd_age_ticks),
1210 ldb.FLAG_MOD_REPLACE, "maxPwdAge")
1211 msgs.append("Maximum password age changed!")
1213 if max_pwd_age > 0 and min_pwd_age >= max_pwd_age:
1214 raise CommandError("Maximum password age (%d) must be greater than minimum password age (%d)!" % (max_pwd_age, min_pwd_age))
1217 raise CommandError("You must specify at least one option to set. Try --help")
1219 msgs.append("All changes applied successfully!")
1220 self.message("\n".join(msgs))
1222 raise CommandError("Wrong argument '%s'!" % subcommand)
1225 class cmd_domain_classicupgrade(Command):
1226 """Upgrade from Samba classic (NT4-like) database to Samba AD DC database.
1228 Specify either a directory with all Samba classic DC databases and state files (with --dbdir) or
1229 the testparm utility from your classic installation (with --testparm).
1232 synopsis = "%prog [options] <classic_smb_conf>"
1234 takes_optiongroups = {
1235 "sambaopts": options.SambaOptions,
1236 "versionopts": options.VersionOptions
1240 Option("--dbdir", type="string", metavar="DIR",
1241 help="Path to samba classic DC database directory"),
1242 Option("--testparm", type="string", metavar="PATH",
1243 help="Path to samba classic DC testparm utility from the previous installation. This allows the default paths of the previous installation to be followed"),
1244 Option("--targetdir", type="string", metavar="DIR",
1245 help="Path prefix where the new Samba 4.0 AD domain should be initialised"),
1246 Option("--quiet", help="Be quiet", action="store_true"),
1247 Option("--verbose", help="Be verbose", action="store_true"),
1248 Option("--use-xattrs", type="choice", choices=["yes","no","auto"], metavar="[yes|no|auto]",
1249 help="Define if we should use the native fs capabilities or a tdb file for storing attributes likes ntacl, auto tries to make an inteligent guess based on the user rights and system capabilities", default="auto"),
1250 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
1251 action="store_true"),
1252 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
1253 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
1254 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
1255 "BIND9_FLATFILE uses bind9 text database to store zone information, "
1256 "BIND9_DLZ uses samba4 AD to store zone information, "
1257 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
1258 default="SAMBA_INTERNAL")
1261 takes_args = ["smbconf"]
1263 def run(self, smbconf=None, targetdir=None, dbdir=None, testparm=None,
1264 quiet=False, verbose=False, use_xattrs=None, sambaopts=None, versionopts=None,
1265 dns_backend=None, use_ntvfs=False):
1267 if not os.path.exists(smbconf):
1268 raise CommandError("File %s does not exist" % smbconf)
1270 if testparm and not os.path.exists(testparm):
1271 raise CommandError("Testparm utility %s does not exist" % testparm)
1273 if dbdir and not os.path.exists(dbdir):
1274 raise CommandError("Directory %s does not exist" % dbdir)
1276 if not dbdir and not testparm:
1277 raise CommandError("Please specify either dbdir or testparm")
1279 logger = self.get_logger()
1281 logger.setLevel(logging.DEBUG)
1283 logger.setLevel(logging.WARNING)
1285 logger.setLevel(logging.INFO)
1287 if dbdir and testparm:
1288 logger.warning("both dbdir and testparm specified, ignoring dbdir.")
1291 lp = sambaopts.get_loadparm()
1293 s3conf = s3param.get_context()
1296 s3conf.set("realm", sambaopts.realm)
1298 if targetdir is not None:
1299 if not os.path.isdir(targetdir):
1303 if use_xattrs == "yes":
1305 elif use_xattrs == "auto" and not s3conf.get("posix:eadb"):
1307 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
1309 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
1312 samba.ntacls.setntacl(lp, tmpfile.name,
1313 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
1316 # FIXME: Don't catch all exceptions here
1317 logger.info("You are not root or your system do not support xattr, using tdb backend for attributes. "
1318 "If you intend to use this provision in production, rerun the script as root on a system supporting xattrs.")
1322 # Set correct default values from dbdir or testparm
1325 paths["state directory"] = dbdir
1326 paths["private dir"] = dbdir
1327 paths["lock directory"] = dbdir
1328 paths["smb passwd file"] = dbdir + "/smbpasswd"
1330 paths["state directory"] = get_testparm_var(testparm, smbconf, "state directory")
1331 paths["private dir"] = get_testparm_var(testparm, smbconf, "private dir")
1332 paths["smb passwd file"] = get_testparm_var(testparm, smbconf, "smb passwd file")
1333 paths["lock directory"] = get_testparm_var(testparm, smbconf, "lock directory")
1334 # "testparm" from Samba 3 < 3.4.x is not aware of the parameter
1335 # "state directory", instead make use of "lock directory"
1336 if len(paths["state directory"]) == 0:
1337 paths["state directory"] = paths["lock directory"]
1340 s3conf.set(p, paths[p])
1342 # load smb.conf parameters
1343 logger.info("Reading smb.conf")
1344 s3conf.load(smbconf)
1345 samba3 = Samba3(smbconf, s3conf)
1347 logger.info("Provisioning")
1348 upgrade_from_samba3(samba3, logger, targetdir, session_info=system_session(),
1349 useeadb=eadb, dns_backend=dns_backend, use_ntvfs=use_ntvfs)
1352 class cmd_domain_samba3upgrade(cmd_domain_classicupgrade):
1353 __doc__ = cmd_domain_classicupgrade.__doc__
1355 # This command is present for backwards compatibility only,
1356 # and should not be shown.
1361 class cmd_domain(SuperCommand):
1362 """Domain management."""
1365 subcommands["demote"] = cmd_domain_demote()
1366 if cmd_domain_export_keytab is not None:
1367 subcommands["exportkeytab"] = cmd_domain_export_keytab()
1368 subcommands["info"] = cmd_domain_info()
1369 subcommands["provision"] = cmd_domain_provision()
1370 subcommands["join"] = cmd_domain_join()
1371 subcommands["dcpromo"] = cmd_domain_dcpromo()
1372 subcommands["level"] = cmd_domain_level()
1373 subcommands["passwordsettings"] = cmd_domain_passwordsettings()
1374 subcommands["classicupgrade"] = cmd_domain_classicupgrade()
1375 subcommands["samba3upgrade"] = cmd_domain_samba3upgrade()