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-2015
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
37 from samba import ntstatus
38 from samba import NTSTATUSError
39 from samba import werror
40 from getpass import getpass
41 from samba.net import Net, LIBNET_JOIN_AUTOMATIC
43 from samba.join import join_RODC, join_DC, join_subdomain
44 from samba.auth import system_session
45 from samba.samdb import SamDB
46 from samba.ndr import ndr_unpack, ndr_pack, ndr_print
47 from samba.dcerpc import drsuapi
48 from samba.dcerpc import drsblobs
49 from samba.dcerpc import lsa
50 from samba.dcerpc import netlogon
51 from samba.dcerpc import security
52 from samba.dcerpc import nbt
53 from samba.dcerpc import misc
54 from samba.dcerpc.samr import DOMAIN_PASSWORD_COMPLEX, DOMAIN_PASSWORD_STORE_CLEARTEXT
55 from samba.netcmd import (
61 from samba.netcmd.fsmo import get_fsmo_roleowner
62 from samba.netcmd.common import netcmd_get_domain_infos_via_cldap
63 from samba.samba3 import Samba3
64 from samba.samba3 import param as s3param
65 from samba.upgrade import upgrade_from_samba3
66 from samba.drs_utils import (
67 sendDsReplicaSync, drsuapi_connect, drsException,
69 from samba import remove_dc, arcfour_encrypt, string_to_byte_array
71 from samba.dsdb import (
72 DS_DOMAIN_FUNCTION_2000,
73 DS_DOMAIN_FUNCTION_2003,
74 DS_DOMAIN_FUNCTION_2003_MIXED,
75 DS_DOMAIN_FUNCTION_2008,
76 DS_DOMAIN_FUNCTION_2008_R2,
77 DS_DOMAIN_FUNCTION_2012,
78 DS_DOMAIN_FUNCTION_2012_R2,
79 DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL,
80 DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL,
81 UF_WORKSTATION_TRUST_ACCOUNT,
82 UF_SERVER_TRUST_ACCOUNT,
83 UF_TRUSTED_FOR_DELEGATION,
84 UF_PARTIAL_SECRETS_ACCOUNT
87 from samba.provision import (
90 DEFAULT_MIN_PWD_LENGTH,
94 from samba.provision.common import (
100 string_version_to_constant = {
101 "2008_R2" : DS_DOMAIN_FUNCTION_2008_R2,
102 "2012": DS_DOMAIN_FUNCTION_2012,
103 "2012_R2": DS_DOMAIN_FUNCTION_2012_R2,
106 def get_testparm_var(testparm, smbconf, varname):
107 errfile = open(os.devnull, 'w')
108 p = subprocess.Popen([testparm, '-s', '-l',
109 '--parameter-name=%s' % varname, smbconf],
110 stdout=subprocess.PIPE, stderr=errfile)
111 (out,err) = p.communicate()
113 lines = out.split('\n')
115 return lines[0].strip()
119 import samba.dckeytab
121 cmd_domain_export_keytab = None
123 class cmd_domain_export_keytab(Command):
124 """Dump Kerberos keys of the domain into a keytab."""
126 synopsis = "%prog <keytab> [options]"
128 takes_optiongroups = {
129 "sambaopts": options.SambaOptions,
130 "credopts": options.CredentialsOptions,
131 "versionopts": options.VersionOptions,
135 Option("--principal", help="extract only this principal", type=str),
138 takes_args = ["keytab"]
140 def run(self, keytab, credopts=None, sambaopts=None, versionopts=None, principal=None):
141 lp = sambaopts.get_loadparm()
143 net.export_keytab(keytab=keytab, principal=principal)
146 class cmd_domain_info(Command):
147 """Print basic info about a domain and the DC passed as parameter."""
149 synopsis = "%prog <ip_address> [options]"
154 takes_optiongroups = {
155 "sambaopts": options.SambaOptions,
156 "credopts": options.CredentialsOptions,
157 "versionopts": options.VersionOptions,
160 takes_args = ["address"]
162 def run(self, address, credopts=None, sambaopts=None, versionopts=None):
163 lp = sambaopts.get_loadparm()
165 res = netcmd_get_domain_infos_via_cldap(lp, None, address)
167 raise CommandError("Invalid IP address '" + address + "'!")
168 self.outf.write("Forest : %s\n" % res.forest)
169 self.outf.write("Domain : %s\n" % res.dns_domain)
170 self.outf.write("Netbios domain : %s\n" % res.domain_name)
171 self.outf.write("DC name : %s\n" % res.pdc_dns_name)
172 self.outf.write("DC netbios name : %s\n" % res.pdc_name)
173 self.outf.write("Server site : %s\n" % res.server_site)
174 self.outf.write("Client site : %s\n" % res.client_site)
177 class cmd_domain_provision(Command):
178 """Provision a domain."""
180 synopsis = "%prog [options]"
182 takes_optiongroups = {
183 "sambaopts": options.SambaOptions,
184 "versionopts": options.VersionOptions,
188 Option("--interactive", help="Ask for names", action="store_true"),
189 Option("--domain", type="string", metavar="DOMAIN",
190 help="NetBIOS domain name to use"),
191 Option("--domain-guid", type="string", metavar="GUID",
192 help="set domainguid (otherwise random)"),
193 Option("--domain-sid", type="string", metavar="SID",
194 help="set domainsid (otherwise random)"),
195 Option("--ntds-guid", type="string", metavar="GUID",
196 help="set NTDS object GUID (otherwise random)"),
197 Option("--invocationid", type="string", metavar="GUID",
198 help="set invocationid (otherwise random)"),
199 Option("--host-name", type="string", metavar="HOSTNAME",
200 help="set hostname"),
201 Option("--host-ip", type="string", metavar="IPADDRESS",
202 help="set IPv4 ipaddress"),
203 Option("--host-ip6", type="string", metavar="IP6ADDRESS",
204 help="set IPv6 ipaddress"),
205 Option("--site", type="string", metavar="SITENAME",
206 help="set site name"),
207 Option("--adminpass", type="string", metavar="PASSWORD",
208 help="choose admin password (otherwise random)"),
209 Option("--krbtgtpass", type="string", metavar="PASSWORD",
210 help="choose krbtgt password (otherwise random)"),
211 Option("--machinepass", type="string", metavar="PASSWORD",
212 help="choose machine password (otherwise random)"),
213 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
214 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
215 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
216 "BIND9_FLATFILE uses bind9 text database to store zone information, "
217 "BIND9_DLZ uses samba4 AD to store zone information, "
218 "NONE skips the DNS setup entirely (not recommended)",
219 default="SAMBA_INTERNAL"),
220 Option("--dnspass", type="string", metavar="PASSWORD",
221 help="choose dns password (otherwise random)"),
222 Option("--ldapadminpass", type="string", metavar="PASSWORD",
223 help="choose password to set between Samba and its LDAP backend (otherwise random)"),
224 Option("--root", type="string", metavar="USERNAME",
225 help="choose 'root' unix username"),
226 Option("--nobody", type="string", metavar="USERNAME",
227 help="choose 'nobody' user"),
228 Option("--users", type="string", metavar="GROUPNAME",
229 help="choose 'users' group"),
230 Option("--quiet", help="Be quiet", action="store_true"),
231 Option("--blank", action="store_true",
232 help="do not add users or groups, just the structure"),
233 Option("--ldap-backend-type", type="choice", metavar="LDAP-BACKEND-TYPE",
234 help="Test initialisation support for unsupported LDAP backend type (fedora-ds or openldap) DO NOT USE",
235 choices=["fedora-ds", "openldap"]),
236 Option("--server-role", type="choice", metavar="ROLE",
237 choices=["domain controller", "dc", "member server", "member", "standalone"],
238 help="The server role (domain controller | dc | member server | member | standalone). Default is dc.",
239 default="domain controller"),
240 Option("--function-level", type="choice", metavar="FOR-FUN-LEVEL",
241 choices=["2000", "2003", "2008", "2008_R2"],
242 help="The domain and forest function level (2000 | 2003 | 2008 | 2008_R2 - always native). Default is (Windows) 2008_R2 Native.",
244 Option("--base-schema", type="choice", metavar="BASE-SCHEMA",
245 choices=["2008_R2", "2008_R2_old", "2012", "2012_R2"],
246 help="The base schema files to use. Default is (Windows) 2008_R2.",
248 Option("--next-rid", type="int", metavar="NEXTRID", default=1000,
249 help="The initial nextRid value (only needed for upgrades). Default is 1000."),
250 Option("--partitions-only",
251 help="Configure Samba's partitions, but do not modify them (ie, join a BDC)", action="store_true"),
252 Option("--targetdir", type="string", metavar="DIR",
253 help="Set target directory"),
254 Option("--ol-mmr-urls", type="string", metavar="LDAPSERVER",
255 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\""),
256 Option("--use-rfc2307", action="store_true", help="Use AD to store posix attributes (default = no)"),
257 Option("--plaintext-secrets", action="store_true",
258 help="Store secret/sensitive values as plain text on disk" +
259 "(default is to encrypt secret/ensitive values)"),
263 Option("--ldap-dryrun-mode", help="Configure LDAP backend, but do not run any binaries and exit early. Used only for the test environment. DO NOT USE",
264 action="store_true"),
265 Option("--slapd-path", type="string", metavar="SLAPD-PATH",
266 help="Path to slapd for LDAP backend [e.g.:'/usr/local/libexec/slapd']. Required for Setup with LDAP-Backend. OpenLDAP Version >= 2.4.17 should be used."),
267 Option("--ldap-backend-extra-port", type="int", metavar="LDAP-BACKEND-EXTRA-PORT", help="Additional TCP port for LDAP backend server (to use for replication)"),
268 Option("--ldap-backend-forced-uri", type="string", metavar="LDAP-BACKEND-FORCED-URI",
269 help="Force the LDAP backend connection to be to a particular URI. Use this ONLY for 'existing' backends, or when debugging the interaction with the LDAP backend and you need to intercept the LDA"),
270 Option("--ldap-backend-nosync", help="Configure LDAP backend not to call fsync() (for performance in test environments)", action="store_true"),
274 Option("--use-ntvfs", action="store_true", help="Use NTVFS for the fileserver (default = no)"),
275 Option("--use-xattrs", type="choice", choices=["yes","no","auto"],
276 metavar="[yes|no|auto]",
277 help="Define if we should use the native fs capabilities or a tdb file for "
278 "storing attributes likes ntacl when --use-ntvfs is set. "
279 "auto tries to make an inteligent guess based on the user rights and system capabilities",
283 if os.getenv('TEST_LDAP', "no") == "yes":
284 takes_options.extend(openldap_options)
286 if samba.is_ntvfs_fileserver_built():
287 takes_options.extend(ntvfs_options)
291 def run(self, sambaopts=None, versionopts=None,
314 ldap_backend_type=None,
318 partitions_only=None,
325 ldap_backend_nosync=None,
326 ldap_backend_extra_port=None,
327 ldap_backend_forced_uri=None,
328 ldap_dryrun_mode=None,
330 plaintext_secrets=False):
332 self.logger = self.get_logger("provision")
334 self.logger.setLevel(logging.WARNING)
336 self.logger.setLevel(logging.INFO)
338 lp = sambaopts.get_loadparm()
339 smbconf = lp.configfile
341 if dns_forwarder is not None:
342 suggested_forwarder = dns_forwarder
344 suggested_forwarder = self._get_nameserver_ip()
345 if suggested_forwarder is None:
346 suggested_forwarder = "none"
348 if len(self.raw_argv) == 1:
352 from getpass import getpass
355 def ask(prompt, default=None):
356 if default is not None:
357 print "%s [%s]: " % (prompt, default),
359 print "%s: " % (prompt,),
360 return sys.stdin.readline().rstrip("\n") or default
363 default = socket.getfqdn().split(".", 1)[1].upper()
366 realm = ask("Realm", default)
367 if realm in (None, ""):
368 raise CommandError("No realm set!")
371 default = realm.split(".")[0]
374 domain = ask("Domain", default)
376 raise CommandError("No domain set!")
378 server_role = ask("Server Role (dc, member, standalone)", "dc")
380 dns_backend = ask("DNS backend (SAMBA_INTERNAL, BIND9_FLATFILE, BIND9_DLZ, NONE)", "SAMBA_INTERNAL")
381 if dns_backend in (None, ''):
382 raise CommandError("No DNS backend set!")
384 if dns_backend == "SAMBA_INTERNAL":
385 dns_forwarder = ask("DNS forwarder IP address (write 'none' to disable forwarding)", suggested_forwarder)
386 if dns_forwarder.lower() in (None, 'none'):
387 suggested_forwarder = None
391 adminpassplain = getpass("Administrator password: ")
392 issue = self._adminpass_issue(adminpassplain)
394 self.errf.write("%s.\n" % issue)
396 adminpassverify = getpass("Retype password: ")
397 if not adminpassplain == adminpassverify:
398 self.errf.write("Sorry, passwords do not match.\n")
400 adminpass = adminpassplain
404 realm = sambaopts._lp.get('realm')
406 raise CommandError("No realm set!")
408 raise CommandError("No domain set!")
411 issue = self._adminpass_issue(adminpass)
413 raise CommandError(issue)
415 self.logger.info("Administrator password will be set randomly!")
417 if function_level == "2000":
418 dom_for_fun_level = DS_DOMAIN_FUNCTION_2000
419 elif function_level == "2003":
420 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
421 elif function_level == "2008":
422 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008
423 elif function_level == "2008_R2":
424 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
426 if dns_backend == "SAMBA_INTERNAL" and dns_forwarder is None:
427 dns_forwarder = suggested_forwarder
429 samdb_fill = FILL_FULL
431 samdb_fill = FILL_NT4SYNC
432 elif partitions_only:
433 samdb_fill = FILL_DRS
435 if targetdir is not None:
436 if not os.path.isdir(targetdir):
441 if use_xattrs == "yes":
443 elif use_xattrs == "auto" and use_ntvfs == False:
445 elif use_ntvfs == False:
446 raise CommandError("--use-xattrs=no requires --use-ntvfs (not supported for production use). "
447 "Please re-run with --use-xattrs omitted.")
448 elif use_xattrs == "auto" and not lp.get("posix:eadb"):
450 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
452 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
455 samba.ntacls.setntacl(lp, file.name,
456 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
459 self.logger.info("You are not root or your system does not support xattr, using tdb backend for attributes. ")
464 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.")
465 if ldap_backend_type == "existing":
466 if ldap_backend_forced_uri is not None:
467 self.logger.warn("You have specified to use an existing LDAP server as the backend, please make sure an LDAP server is running at %s" % ldap_backend_forced_uri)
469 self.logger.info("You have specified to use an existing LDAP server as the backend, please make sure an LDAP server is running at the default location")
471 if ldap_backend_forced_uri is not None:
472 self.logger.warn("You have specified to use an fixed URI %s for connecting to your LDAP server backend. This is NOT RECOMMENDED, as our default communiation over ldapi:// is more secure and much less")
474 if domain_sid is not None:
475 domain_sid = security.dom_sid(domain_sid)
477 session = system_session()
479 result = provision(self.logger,
480 session, smbconf=smbconf, targetdir=targetdir,
481 samdb_fill=samdb_fill, realm=realm, domain=domain,
482 domainguid=domain_guid, domainsid=domain_sid,
484 hostip=host_ip, hostip6=host_ip6,
485 sitename=site, ntdsguid=ntds_guid,
486 invocationid=invocationid, adminpass=adminpass,
487 krbtgtpass=krbtgtpass, machinepass=machinepass,
488 dns_backend=dns_backend, dns_forwarder=dns_forwarder,
489 dnspass=dnspass, root=root, nobody=nobody,
491 serverrole=server_role, dom_for_fun_level=dom_for_fun_level,
492 backend_type=ldap_backend_type,
493 ldapadminpass=ldapadminpass, ol_mmr_urls=ol_mmr_urls, slapd_path=slapd_path,
494 useeadb=eadb, next_rid=next_rid, lp=lp, use_ntvfs=use_ntvfs,
495 use_rfc2307=use_rfc2307, skip_sysvolacl=False,
496 ldap_backend_extra_port=ldap_backend_extra_port,
497 ldap_backend_forced_uri=ldap_backend_forced_uri,
498 nosync=ldap_backend_nosync, ldap_dryrun_mode=ldap_dryrun_mode,
499 base_schema=base_schema,
500 plaintext_secrets=plaintext_secrets)
502 except ProvisioningError, e:
503 raise CommandError("Provision failed", e)
505 result.report_logger(self.logger)
507 def _get_nameserver_ip(self):
508 """Grab the nameserver IP address from /etc/resolv.conf."""
510 RESOLV_CONF="/etc/resolv.conf"
512 if not path.isfile(RESOLV_CONF):
513 self.logger.warning("Failed to locate %s" % RESOLV_CONF)
518 handle = open(RESOLV_CONF, 'r')
520 if not line.startswith('nameserver'):
522 # we want the last non-space continuous string of the line
523 return line.strip().split()[-1]
525 if handle is not None:
528 self.logger.warning("No nameserver found in %s" % RESOLV_CONF)
530 def _adminpass_issue(self, adminpass):
531 """Returns error string for a bad administrator password,
532 or None if acceptable"""
534 if len(adminpass.decode('utf-8')) < DEFAULT_MIN_PWD_LENGTH:
535 return "Administrator password does not meet the default minimum" \
536 " password length requirement (%d characters)" \
537 % DEFAULT_MIN_PWD_LENGTH
538 elif not samba.check_password_quality(adminpass):
539 return "Administrator password does not meet the default" \
545 class cmd_domain_dcpromo(Command):
546 """Promote an existing domain member or NT4 PDC to an AD DC."""
548 synopsis = "%prog <dnsdomain> [DC|RODC] [options]"
550 takes_optiongroups = {
551 "sambaopts": options.SambaOptions,
552 "versionopts": options.VersionOptions,
553 "credopts": options.CredentialsOptions,
557 Option("--server", help="DC to join", type=str),
558 Option("--site", help="site to join", type=str),
559 Option("--targetdir", help="where to store provision", type=str),
560 Option("--domain-critical-only",
561 help="only replicate critical domain objects",
562 action="store_true"),
563 Option("--machinepass", type=str, metavar="PASSWORD",
564 help="choose machine password (otherwise random)"),
565 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
566 choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
567 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
568 "BIND9_DLZ uses samba4 AD to store zone information, "
569 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
570 default="SAMBA_INTERNAL"),
571 Option("--quiet", help="Be quiet", action="store_true"),
572 Option("--verbose", help="Be verbose", action="store_true")
576 Option("--use-ntvfs", action="store_true", help="Use NTVFS for the fileserver (default = no)"),
579 if samba.is_ntvfs_fileserver_built():
580 takes_options.extend(ntvfs_options)
583 takes_args = ["domain", "role?"]
585 def run(self, domain, role=None, sambaopts=None, credopts=None,
586 versionopts=None, server=None, site=None, targetdir=None,
587 domain_critical_only=False, parent_domain=None, machinepass=None,
588 use_ntvfs=False, dns_backend=None,
589 quiet=False, verbose=False):
590 lp = sambaopts.get_loadparm()
591 creds = credopts.get_credentials(lp)
592 net = Net(creds, lp, server=credopts.ipaddress)
594 logger = self.get_logger()
596 logger.setLevel(logging.DEBUG)
598 logger.setLevel(logging.WARNING)
600 logger.setLevel(logging.INFO)
602 netbios_name = lp.get("netbios name")
608 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
609 site=site, netbios_name=netbios_name, targetdir=targetdir,
610 domain_critical_only=domain_critical_only,
611 machinepass=machinepass, use_ntvfs=use_ntvfs,
612 dns_backend=dns_backend,
613 promote_existing=True)
615 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
616 site=site, netbios_name=netbios_name, targetdir=targetdir,
617 domain_critical_only=domain_critical_only,
618 machinepass=machinepass, use_ntvfs=use_ntvfs, dns_backend=dns_backend,
619 promote_existing=True)
621 raise CommandError("Invalid role '%s' (possible values: DC, RODC)" % role)
624 class cmd_domain_join(Command):
625 """Join domain as either member or backup domain controller."""
627 synopsis = "%prog <dnsdomain> [DC|RODC|MEMBER|SUBDOMAIN] [options]"
629 takes_optiongroups = {
630 "sambaopts": options.SambaOptions,
631 "versionopts": options.VersionOptions,
632 "credopts": options.CredentialsOptions,
636 Option("--server", help="DC to join", type=str),
637 Option("--site", help="site to join", type=str),
638 Option("--targetdir", help="where to store provision", type=str),
639 Option("--parent-domain", help="parent domain to create subdomain under", type=str),
640 Option("--domain-critical-only",
641 help="only replicate critical domain objects",
642 action="store_true"),
643 Option("--machinepass", type=str, metavar="PASSWORD",
644 help="choose machine password (otherwise random)"),
645 Option("--adminpass", type="string", metavar="PASSWORD",
646 help="choose adminstrator password when joining as a subdomain (otherwise random)"),
647 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
648 choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
649 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
650 "BIND9_DLZ uses samba4 AD to store zone information, "
651 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
652 default="SAMBA_INTERNAL"),
653 Option("--plaintext-secrets", action="store_true",
654 help="Store secret/sensitive values as plain text on disk" +
655 "(default is to encrypt secret/ensitive values)"),
656 Option("--quiet", help="Be quiet", action="store_true"),
657 Option("--verbose", help="Be verbose", action="store_true")
661 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
664 if samba.is_ntvfs_fileserver_built():
665 takes_options.extend(ntvfs_options)
667 takes_args = ["domain", "role?"]
669 def run(self, domain, role=None, sambaopts=None, credopts=None,
670 versionopts=None, server=None, site=None, targetdir=None,
671 domain_critical_only=False, parent_domain=None, machinepass=None,
672 use_ntvfs=False, dns_backend=None, adminpass=None,
673 quiet=False, verbose=False, plaintext_secrets=False):
674 lp = sambaopts.get_loadparm()
675 creds = credopts.get_credentials(lp)
676 net = Net(creds, lp, server=credopts.ipaddress)
679 site = "Default-First-Site-Name"
681 logger = self.get_logger()
683 logger.setLevel(logging.DEBUG)
685 logger.setLevel(logging.WARNING)
687 logger.setLevel(logging.INFO)
689 netbios_name = lp.get("netbios name")
694 if role is None or role == "MEMBER":
695 (join_password, sid, domain_name) = net.join_member(
696 domain, netbios_name, LIBNET_JOIN_AUTOMATIC,
697 machinepass=machinepass)
699 self.errf.write("Joined domain %s (%s)\n" % (domain_name, sid))
701 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
702 site=site, netbios_name=netbios_name, targetdir=targetdir,
703 domain_critical_only=domain_critical_only,
704 machinepass=machinepass, use_ntvfs=use_ntvfs,
705 dns_backend=dns_backend,
706 plaintext_secrets=plaintext_secrets)
708 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
709 site=site, netbios_name=netbios_name, targetdir=targetdir,
710 domain_critical_only=domain_critical_only,
711 machinepass=machinepass, use_ntvfs=use_ntvfs,
712 dns_backend=dns_backend,
713 plaintext_secrets=plaintext_secrets)
714 elif role == "SUBDOMAIN":
716 logger.info("Administrator password will be set randomly!")
718 netbios_domain = lp.get("workgroup")
719 if parent_domain is None:
720 parent_domain = ".".join(domain.split(".")[1:])
721 join_subdomain(logger=logger, server=server, creds=creds, lp=lp, dnsdomain=domain,
722 parent_domain=parent_domain, site=site,
723 netbios_name=netbios_name, netbios_domain=netbios_domain,
724 targetdir=targetdir, machinepass=machinepass,
725 use_ntvfs=use_ntvfs, dns_backend=dns_backend,
727 plaintext_secrets=plaintext_secrets)
729 raise CommandError("Invalid role '%s' (possible values: MEMBER, DC, RODC, SUBDOMAIN)" % role)
732 class cmd_domain_demote(Command):
733 """Demote ourselves from the role of Domain Controller."""
735 synopsis = "%prog [options]"
738 Option("--server", help="writable DC to write demotion changes on", type=str),
739 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
740 metavar="URL", dest="H"),
741 Option("--remove-other-dead-server", help="Dead DC (name or NTDS GUID) "
742 "to remove ALL references to (rather than this DC)", type=str),
743 Option("--quiet", help="Be quiet", action="store_true"),
744 Option("--verbose", help="Be verbose", action="store_true"),
747 takes_optiongroups = {
748 "sambaopts": options.SambaOptions,
749 "credopts": options.CredentialsOptions,
750 "versionopts": options.VersionOptions,
753 def run(self, sambaopts=None, credopts=None,
754 versionopts=None, server=None,
755 remove_other_dead_server=None, H=None,
756 verbose=False, quiet=False):
757 lp = sambaopts.get_loadparm()
758 creds = credopts.get_credentials(lp)
759 net = Net(creds, lp, server=credopts.ipaddress)
761 logger = self.get_logger()
763 logger.setLevel(logging.DEBUG)
765 logger.setLevel(logging.WARNING)
767 logger.setLevel(logging.INFO)
769 if remove_other_dead_server is not None:
770 if server is not None:
771 samdb = SamDB(url="ldap://%s" % server,
772 session_info=system_session(),
773 credentials=creds, lp=lp)
775 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
777 remove_dc.remove_dc(samdb, logger, remove_other_dead_server)
778 except remove_dc.DemoteException as err:
779 raise CommandError("Demote failed: %s" % err)
782 netbios_name = lp.get("netbios name")
783 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
785 res = samdb.search(expression='(&(objectClass=computer)(serverReferenceBL=*))', attrs=["dnsHostName", "name"])
787 raise CommandError("Unable to search for servers")
790 raise CommandError("You are the latest server in the domain")
794 if str(e["name"]).lower() != netbios_name.lower():
795 server = e["dnsHostName"]
798 ntds_guid = samdb.get_ntds_GUID()
799 msg = samdb.search(base=str(samdb.get_config_basedn()),
800 scope=ldb.SCOPE_SUBTREE, expression="(objectGUID=%s)" % ntds_guid,
802 if len(msg) == 0 or "options" not in msg[0]:
803 raise CommandError("Failed to find options on %s" % ntds_guid)
806 dsa_options = int(str(msg[0]['options']))
808 res = samdb.search(expression="(fSMORoleOwner=%s)" % str(ntds_dn),
809 controls=["search_options:1:2"])
812 raise CommandError("Current DC is still the owner of %d role(s), use the role command to transfer roles to another DC" % len(res))
814 self.errf.write("Using %s as partner server for the demotion\n" %
816 (drsuapiBind, drsuapi_handle, supportedExtensions) = drsuapi_connect(server, lp, creds)
818 self.errf.write("Deactivating inbound replication\n")
823 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
824 dsa_options |= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
825 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
829 self.errf.write("Asking partner server %s to synchronize from us\n"
831 for part in (samdb.get_schema_basedn(),
832 samdb.get_config_basedn(),
833 samdb.get_root_basedn()):
834 nc = drsuapi.DsReplicaObjectIdentifier()
837 req1 = drsuapi.DsReplicaSyncRequest1()
838 req1.naming_context = nc;
839 req1.options = drsuapi.DRSUAPI_DRS_WRIT_REP
840 req1.source_dsa_guid = misc.GUID(ntds_guid)
843 drsuapiBind.DsReplicaSync(drsuapi_handle, 1, req1)
844 except RuntimeError as (werr, string):
845 if werr == werror.WERR_DS_DRA_NO_REPLICA:
849 "Error while replicating out last local changes from '%s' for demotion, "
850 "re-enabling inbound replication\n" % part)
851 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
852 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
854 raise CommandError("Error while sending a DsReplicaSync for partition '%s'" % str(part), string)
856 remote_samdb = SamDB(url="ldap://%s" % server,
857 session_info=system_session(),
858 credentials=creds, lp=lp)
860 self.errf.write("Changing userControl and container\n")
861 res = remote_samdb.search(base=str(remote_samdb.domain_dn()),
862 expression="(&(objectClass=user)(sAMAccountName=%s$))" %
863 netbios_name.upper(),
864 attrs=["userAccountControl"])
866 uac = int(str(res[0]["userAccountControl"]))
869 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
871 "Error while demoting, re-enabling inbound replication\n")
872 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
873 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
875 raise CommandError("Error while changing account control", e)
878 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
880 "Error while demoting, re-enabling inbound replication")
881 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
882 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
884 raise CommandError("Unable to find object with samaccountName = %s$"
885 " in the remote dc" % netbios_name.upper())
889 uac &= ~(UF_SERVER_TRUST_ACCOUNT|UF_TRUSTED_FOR_DELEGATION|UF_PARTIAL_SECRETS_ACCOUNT)
890 uac |= UF_WORKSTATION_TRUST_ACCOUNT
895 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
896 ldb.FLAG_MOD_REPLACE,
897 "userAccountControl")
899 remote_samdb.modify(msg)
901 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
903 "Error while demoting, re-enabling inbound replication")
904 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
905 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
908 raise CommandError("Error while changing account control", e)
910 parent = msg.dn.parent()
911 dc_name = res[0].dn.get_rdn_value()
912 rdn = "CN=%s" % dc_name
914 # Let's move to the Computer container
918 computer_dn = ldb.Dn(remote_samdb, "CN=Computers,%s" % str(remote_samdb.domain_dn()))
919 res = remote_samdb.search(base=computer_dn, expression=rdn, scope=ldb.SCOPE_ONELEVEL)
922 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
923 scope=ldb.SCOPE_ONELEVEL)
924 while(len(res) != 0 and i < 100):
926 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
927 scope=ldb.SCOPE_ONELEVEL)
930 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
932 "Error while demoting, re-enabling inbound replication\n")
933 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
934 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
940 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
941 ldb.FLAG_MOD_REPLACE,
942 "userAccountControl")
944 remote_samdb.modify(msg)
946 raise CommandError("Unable to find a slot for renaming %s,"
947 " all names from %s-1 to %s-%d seemed used" %
948 (str(dc_dn), rdn, rdn, i - 9))
950 newrdn = "%s-%d" % (rdn, i)
953 newdn = ldb.Dn(remote_samdb, "%s,%s" % (newrdn, str(computer_dn)))
954 remote_samdb.rename(dc_dn, newdn)
956 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
958 "Error while demoting, re-enabling inbound replication\n")
959 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
960 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
966 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
967 ldb.FLAG_MOD_REPLACE,
968 "userAccountControl")
970 remote_samdb.modify(msg)
971 raise CommandError("Error while renaming %s to %s" % (str(dc_dn), str(newdn)), e)
974 server_dsa_dn = samdb.get_serverName()
975 domain = remote_samdb.get_root_basedn()
978 req1 = drsuapi.DsRemoveDSServerRequest1()
979 req1.server_dn = str(server_dsa_dn)
980 req1.domain_dn = str(domain)
983 drsuapiBind.DsRemoveDSServer(drsuapi_handle, 1, req1)
984 except RuntimeError as (werr, string):
985 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
987 "Error while demoting, re-enabling inbound replication\n")
988 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
989 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
995 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
996 ldb.FLAG_MOD_REPLACE,
997 "userAccountControl")
998 remote_samdb.modify(msg)
999 remote_samdb.rename(newdn, dc_dn)
1000 if werr == werror.WERR_DS_DRA_NO_REPLICA:
1001 raise CommandError("The DC %s is not present on (already removed from) the remote server: " % server_dsa_dn, e)
1003 raise CommandError("Error while sending a removeDsServer of %s: " % server_dsa_dn, e)
1005 remove_dc.remove_sysvol_references(remote_samdb, logger, dc_name)
1007 # These are objects under the computer account that should be deleted
1008 for s in ("CN=Enterprise,CN=NTFRS Subscriptions",
1009 "CN=%s, CN=NTFRS Subscriptions" % lp.get("realm"),
1010 "CN=Domain system Volumes (SYSVOL Share), CN=NTFRS Subscriptions",
1011 "CN=NTFRS Subscriptions"):
1013 remote_samdb.delete(ldb.Dn(remote_samdb,
1014 "%s,%s" % (s, str(newdn))))
1015 except ldb.LdbError, l:
1018 self.errf.write("Demote successful\n")
1021 class cmd_domain_level(Command):
1022 """Raise domain and forest function levels."""
1024 synopsis = "%prog (show|raise <options>) [options]"
1026 takes_optiongroups = {
1027 "sambaopts": options.SambaOptions,
1028 "credopts": options.CredentialsOptions,
1029 "versionopts": options.VersionOptions,
1033 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1034 metavar="URL", dest="H"),
1035 Option("--quiet", help="Be quiet", action="store_true"),
1036 Option("--forest-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
1037 help="The forest function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)"),
1038 Option("--domain-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
1039 help="The domain function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)")
1042 takes_args = ["subcommand"]
1044 def run(self, subcommand, H=None, forest_level=None, domain_level=None,
1045 quiet=False, credopts=None, sambaopts=None, versionopts=None):
1046 lp = sambaopts.get_loadparm()
1047 creds = credopts.get_credentials(lp, fallback_machine=True)
1049 samdb = SamDB(url=H, session_info=system_session(),
1050 credentials=creds, lp=lp)
1052 domain_dn = samdb.domain_dn()
1054 res_forest = samdb.search("CN=Partitions,%s" % samdb.get_config_basedn(),
1055 scope=ldb.SCOPE_BASE, attrs=["msDS-Behavior-Version"])
1056 assert len(res_forest) == 1
1058 res_domain = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1059 attrs=["msDS-Behavior-Version", "nTMixedDomain"])
1060 assert len(res_domain) == 1
1062 res_dc_s = samdb.search("CN=Sites,%s" % samdb.get_config_basedn(),
1063 scope=ldb.SCOPE_SUBTREE, expression="(objectClass=nTDSDSA)",
1064 attrs=["msDS-Behavior-Version"])
1065 assert len(res_dc_s) >= 1
1067 # default values, since "msDS-Behavior-Version" does not exist on Windows 2000 AD
1068 level_forest = DS_DOMAIN_FUNCTION_2000
1069 level_domain = DS_DOMAIN_FUNCTION_2000
1071 if "msDS-Behavior-Version" in res_forest[0]:
1072 level_forest = int(res_forest[0]["msDS-Behavior-Version"][0])
1073 if "msDS-Behavior-Version" in res_domain[0]:
1074 level_domain = int(res_domain[0]["msDS-Behavior-Version"][0])
1075 level_domain_mixed = int(res_domain[0]["nTMixedDomain"][0])
1078 for msg in res_dc_s:
1079 if "msDS-Behavior-Version" in msg:
1080 if min_level_dc is None or int(msg["msDS-Behavior-Version"][0]) < min_level_dc:
1081 min_level_dc = int(msg["msDS-Behavior-Version"][0])
1083 min_level_dc = DS_DOMAIN_FUNCTION_2000
1084 # well, this is the least
1087 if level_forest < DS_DOMAIN_FUNCTION_2000 or level_domain < DS_DOMAIN_FUNCTION_2000:
1088 raise CommandError("Domain and/or forest function level(s) is/are invalid. Correct them or reprovision!")
1089 if min_level_dc < DS_DOMAIN_FUNCTION_2000:
1090 raise CommandError("Lowest function level of a DC is invalid. Correct this or reprovision!")
1091 if level_forest > level_domain:
1092 raise CommandError("Forest function level is higher than the domain level(s). Correct this or reprovision!")
1093 if level_domain > min_level_dc:
1094 raise CommandError("Domain function level is higher than the lowest function level of a DC. Correct this or reprovision!")
1096 if subcommand == "show":
1097 self.message("Domain and forest function level for domain '%s'" % domain_dn)
1098 if level_forest == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1099 self.message("\nATTENTION: You run SAMBA 4 on a forest function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1100 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1101 self.message("\nATTENTION: You run SAMBA 4 on a domain function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1102 if min_level_dc == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1103 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)!")
1107 if level_forest == DS_DOMAIN_FUNCTION_2000:
1109 elif level_forest == DS_DOMAIN_FUNCTION_2003_MIXED:
1110 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1111 elif level_forest == DS_DOMAIN_FUNCTION_2003:
1113 elif level_forest == DS_DOMAIN_FUNCTION_2008:
1115 elif level_forest == DS_DOMAIN_FUNCTION_2008_R2:
1117 elif level_forest == DS_DOMAIN_FUNCTION_2012:
1119 elif level_forest == DS_DOMAIN_FUNCTION_2012_R2:
1122 outstr = "higher than 2012 R2"
1123 self.message("Forest function level: (Windows) " + outstr)
1125 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1126 outstr = "2000 mixed (NT4 DC support)"
1127 elif level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed == 0:
1129 elif level_domain == DS_DOMAIN_FUNCTION_2003_MIXED:
1130 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1131 elif level_domain == DS_DOMAIN_FUNCTION_2003:
1133 elif level_domain == DS_DOMAIN_FUNCTION_2008:
1135 elif level_domain == DS_DOMAIN_FUNCTION_2008_R2:
1137 elif level_domain == DS_DOMAIN_FUNCTION_2012:
1139 elif level_domain == DS_DOMAIN_FUNCTION_2012_R2:
1142 outstr = "higher than 2012 R2"
1143 self.message("Domain function level: (Windows) " + outstr)
1145 if min_level_dc == DS_DOMAIN_FUNCTION_2000:
1147 elif min_level_dc == DS_DOMAIN_FUNCTION_2003:
1149 elif min_level_dc == DS_DOMAIN_FUNCTION_2008:
1151 elif min_level_dc == DS_DOMAIN_FUNCTION_2008_R2:
1153 elif min_level_dc == DS_DOMAIN_FUNCTION_2012:
1155 elif min_level_dc == DS_DOMAIN_FUNCTION_2012_R2:
1158 outstr = "higher than 2012 R2"
1159 self.message("Lowest function level of a DC: (Windows) " + outstr)
1161 elif subcommand == "raise":
1164 if domain_level is not None:
1165 if domain_level == "2003":
1166 new_level_domain = DS_DOMAIN_FUNCTION_2003
1167 elif domain_level == "2008":
1168 new_level_domain = DS_DOMAIN_FUNCTION_2008
1169 elif domain_level == "2008_R2":
1170 new_level_domain = DS_DOMAIN_FUNCTION_2008_R2
1171 elif domain_level == "2012":
1172 new_level_domain = DS_DOMAIN_FUNCTION_2012
1173 elif domain_level == "2012_R2":
1174 new_level_domain = DS_DOMAIN_FUNCTION_2012_R2
1176 if new_level_domain <= level_domain and level_domain_mixed == 0:
1177 raise CommandError("Domain function level can't be smaller than or equal to the actual one!")
1178 if new_level_domain > min_level_dc:
1179 raise CommandError("Domain function level can't be higher than the lowest function level of a DC!")
1181 # Deactivate mixed/interim domain support
1182 if level_domain_mixed != 0:
1183 # Directly on the base DN
1185 m.dn = ldb.Dn(samdb, domain_dn)
1186 m["nTMixedDomain"] = ldb.MessageElement("0",
1187 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1191 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup") + ",CN=Partitions,%s" % samdb.get_config_basedn())
1192 m["nTMixedDomain"] = ldb.MessageElement("0",
1193 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1196 except ldb.LdbError, (enum, emsg):
1197 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1200 # Directly on the base DN
1202 m.dn = ldb.Dn(samdb, domain_dn)
1203 m["msDS-Behavior-Version"]= ldb.MessageElement(
1204 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1205 "msDS-Behavior-Version")
1209 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup")
1210 + ",CN=Partitions,%s" % samdb.get_config_basedn())
1211 m["msDS-Behavior-Version"]= ldb.MessageElement(
1212 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1213 "msDS-Behavior-Version")
1216 except ldb.LdbError, (enum, emsg):
1217 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1220 level_domain = new_level_domain
1221 msgs.append("Domain function level changed!")
1223 if forest_level is not None:
1224 if forest_level == "2003":
1225 new_level_forest = DS_DOMAIN_FUNCTION_2003
1226 elif forest_level == "2008":
1227 new_level_forest = DS_DOMAIN_FUNCTION_2008
1228 elif forest_level == "2008_R2":
1229 new_level_forest = DS_DOMAIN_FUNCTION_2008_R2
1230 elif forest_level == "2012":
1231 new_level_forest = DS_DOMAIN_FUNCTION_2012
1232 elif forest_level == "2012_R2":
1233 new_level_forest = DS_DOMAIN_FUNCTION_2012_R2
1235 if new_level_forest <= level_forest:
1236 raise CommandError("Forest function level can't be smaller than or equal to the actual one!")
1237 if new_level_forest > level_domain:
1238 raise CommandError("Forest function level can't be higher than the domain function level(s). Please raise it/them first!")
1241 m.dn = ldb.Dn(samdb, "CN=Partitions,%s" % samdb.get_config_basedn())
1242 m["msDS-Behavior-Version"]= ldb.MessageElement(
1243 str(new_level_forest), ldb.FLAG_MOD_REPLACE,
1244 "msDS-Behavior-Version")
1246 msgs.append("Forest function level changed!")
1247 msgs.append("All changes applied successfully!")
1248 self.message("\n".join(msgs))
1250 raise CommandError("invalid argument: '%s' (choose from 'show', 'raise')" % subcommand)
1253 class cmd_domain_passwordsettings(Command):
1254 """Set password settings.
1256 Password complexity, password lockout policy, history length,
1257 minimum password length, the minimum and maximum password age) on
1258 a Samba AD DC server.
1260 Use against a Windows DC is possible, but group policy will override it.
1263 synopsis = "%prog (show|set <options>) [options]"
1265 takes_optiongroups = {
1266 "sambaopts": options.SambaOptions,
1267 "versionopts": options.VersionOptions,
1268 "credopts": options.CredentialsOptions,
1272 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1273 metavar="URL", dest="H"),
1274 Option("--quiet", help="Be quiet", action="store_true"),
1275 Option("--complexity", type="choice", choices=["on","off","default"],
1276 help="The password complexity (on | off | default). Default is 'on'"),
1277 Option("--store-plaintext", type="choice", choices=["on","off","default"],
1278 help="Store plaintext passwords where account have 'store passwords with reversible encryption' set (on | off | default). Default is 'off'"),
1279 Option("--history-length",
1280 help="The password history length (<integer> | default). Default is 24.", type=str),
1281 Option("--min-pwd-length",
1282 help="The minimum password length (<integer> | default). Default is 7.", type=str),
1283 Option("--min-pwd-age",
1284 help="The minimum password age (<integer in days> | default). Default is 1.", type=str),
1285 Option("--max-pwd-age",
1286 help="The maximum password age (<integer in days> | default). Default is 43.", type=str),
1287 Option("--account-lockout-duration",
1288 help="The the length of time an account is locked out after exeeding the limit on bad password attempts (<integer in mins> | default). Default is 30 mins.", type=str),
1289 Option("--account-lockout-threshold",
1290 help="The number of bad password attempts allowed before locking out the account (<integer> | default). Default is 0 (never lock out).", type=str),
1291 Option("--reset-account-lockout-after",
1292 help="After this time is elapsed, the recorded number of attempts restarts from zero (<integer> | default). Default is 30.", type=str),
1295 takes_args = ["subcommand"]
1297 def run(self, subcommand, H=None, min_pwd_age=None, max_pwd_age=None,
1298 quiet=False, complexity=None, store_plaintext=None, history_length=None,
1299 min_pwd_length=None, account_lockout_duration=None, account_lockout_threshold=None,
1300 reset_account_lockout_after=None, credopts=None, sambaopts=None,
1302 lp = sambaopts.get_loadparm()
1303 creds = credopts.get_credentials(lp)
1305 samdb = SamDB(url=H, session_info=system_session(),
1306 credentials=creds, lp=lp)
1308 domain_dn = samdb.domain_dn()
1309 res = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1310 attrs=["pwdProperties", "pwdHistoryLength", "minPwdLength",
1311 "minPwdAge", "maxPwdAge", "lockoutDuration", "lockoutThreshold",
1312 "lockOutObservationWindow"])
1313 assert(len(res) == 1)
1315 pwd_props = int(res[0]["pwdProperties"][0])
1316 pwd_hist_len = int(res[0]["pwdHistoryLength"][0])
1317 cur_min_pwd_len = int(res[0]["minPwdLength"][0])
1319 cur_min_pwd_age = int(abs(int(res[0]["minPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1320 if int(res[0]["maxPwdAge"][0]) == -0x8000000000000000:
1323 cur_max_pwd_age = int(abs(int(res[0]["maxPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1324 cur_account_lockout_threshold = int(res[0]["lockoutThreshold"][0])
1326 if int(res[0]["lockoutDuration"][0]) == -0x8000000000000000:
1327 cur_account_lockout_duration = 0
1329 cur_account_lockout_duration = abs(int(res[0]["lockoutDuration"][0])) / (1e7 * 60)
1330 cur_reset_account_lockout_after = abs(int(res[0]["lockOutObservationWindow"][0])) / (1e7 * 60)
1331 except Exception, e:
1332 raise CommandError("Could not retrieve password properties!", e)
1334 if subcommand == "show":
1335 self.message("Password informations for domain '%s'" % domain_dn)
1337 if pwd_props & DOMAIN_PASSWORD_COMPLEX != 0:
1338 self.message("Password complexity: on")
1340 self.message("Password complexity: off")
1341 if pwd_props & DOMAIN_PASSWORD_STORE_CLEARTEXT != 0:
1342 self.message("Store plaintext passwords: on")
1344 self.message("Store plaintext passwords: off")
1345 self.message("Password history length: %d" % pwd_hist_len)
1346 self.message("Minimum password length: %d" % cur_min_pwd_len)
1347 self.message("Minimum password age (days): %d" % cur_min_pwd_age)
1348 self.message("Maximum password age (days): %d" % cur_max_pwd_age)
1349 self.message("Account lockout duration (mins): %d" % cur_account_lockout_duration)
1350 self.message("Account lockout threshold (attempts): %d" % cur_account_lockout_threshold)
1351 self.message("Reset account lockout after (mins): %d" % cur_reset_account_lockout_after)
1352 elif subcommand == "set":
1355 m.dn = ldb.Dn(samdb, domain_dn)
1357 if complexity is not None:
1358 if complexity == "on" or complexity == "default":
1359 pwd_props = pwd_props | DOMAIN_PASSWORD_COMPLEX
1360 msgs.append("Password complexity activated!")
1361 elif complexity == "off":
1362 pwd_props = pwd_props & (~DOMAIN_PASSWORD_COMPLEX)
1363 msgs.append("Password complexity deactivated!")
1365 if store_plaintext is not None:
1366 if store_plaintext == "on" or store_plaintext == "default":
1367 pwd_props = pwd_props | DOMAIN_PASSWORD_STORE_CLEARTEXT
1368 msgs.append("Plaintext password storage for changed passwords activated!")
1369 elif store_plaintext == "off":
1370 pwd_props = pwd_props & (~DOMAIN_PASSWORD_STORE_CLEARTEXT)
1371 msgs.append("Plaintext password storage for changed passwords deactivated!")
1373 if complexity is not None or store_plaintext is not None:
1374 m["pwdProperties"] = ldb.MessageElement(str(pwd_props),
1375 ldb.FLAG_MOD_REPLACE, "pwdProperties")
1377 if history_length is not None:
1378 if history_length == "default":
1381 pwd_hist_len = int(history_length)
1383 if pwd_hist_len < 0 or pwd_hist_len > 24:
1384 raise CommandError("Password history length must be in the range of 0 to 24!")
1386 m["pwdHistoryLength"] = ldb.MessageElement(str(pwd_hist_len),
1387 ldb.FLAG_MOD_REPLACE, "pwdHistoryLength")
1388 msgs.append("Password history length changed!")
1390 if min_pwd_length is not None:
1391 if min_pwd_length == "default":
1394 min_pwd_len = int(min_pwd_length)
1396 if min_pwd_len < 0 or min_pwd_len > 14:
1397 raise CommandError("Minimum password length must be in the range of 0 to 14!")
1399 m["minPwdLength"] = ldb.MessageElement(str(min_pwd_len),
1400 ldb.FLAG_MOD_REPLACE, "minPwdLength")
1401 msgs.append("Minimum password length changed!")
1403 if min_pwd_age is not None:
1404 if min_pwd_age == "default":
1407 min_pwd_age = int(min_pwd_age)
1409 if min_pwd_age < 0 or min_pwd_age > 998:
1410 raise CommandError("Minimum password age must be in the range of 0 to 998!")
1413 min_pwd_age_ticks = -int(min_pwd_age * (24 * 60 * 60 * 1e7))
1415 m["minPwdAge"] = ldb.MessageElement(str(min_pwd_age_ticks),
1416 ldb.FLAG_MOD_REPLACE, "minPwdAge")
1417 msgs.append("Minimum password age changed!")
1419 if max_pwd_age is not None:
1420 if max_pwd_age == "default":
1423 max_pwd_age = int(max_pwd_age)
1425 if max_pwd_age < 0 or max_pwd_age > 999:
1426 raise CommandError("Maximum password age must be in the range of 0 to 999!")
1429 if max_pwd_age == 0:
1430 max_pwd_age_ticks = -0x8000000000000000
1432 max_pwd_age_ticks = -int(max_pwd_age * (24 * 60 * 60 * 1e7))
1434 m["maxPwdAge"] = ldb.MessageElement(str(max_pwd_age_ticks),
1435 ldb.FLAG_MOD_REPLACE, "maxPwdAge")
1436 msgs.append("Maximum password age changed!")
1438 if account_lockout_duration is not None:
1439 if account_lockout_duration == "default":
1440 account_lockout_duration = 30
1442 account_lockout_duration = int(account_lockout_duration)
1444 if account_lockout_duration < 0 or account_lockout_duration > 99999:
1445 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1448 if account_lockout_duration == 0:
1449 account_lockout_duration_ticks = -0x8000000000000000
1451 account_lockout_duration_ticks = -int(account_lockout_duration * (60 * 1e7))
1453 m["lockoutDuration"] = ldb.MessageElement(str(account_lockout_duration_ticks),
1454 ldb.FLAG_MOD_REPLACE, "lockoutDuration")
1455 msgs.append("Account lockout duration changed!")
1457 if account_lockout_threshold is not None:
1458 if account_lockout_threshold == "default":
1459 account_lockout_threshold = 0
1461 account_lockout_threshold = int(account_lockout_threshold)
1463 m["lockoutThreshold"] = ldb.MessageElement(str(account_lockout_threshold),
1464 ldb.FLAG_MOD_REPLACE, "lockoutThreshold")
1465 msgs.append("Account lockout threshold changed!")
1467 if reset_account_lockout_after is not None:
1468 if reset_account_lockout_after == "default":
1469 reset_account_lockout_after = 30
1471 reset_account_lockout_after = int(reset_account_lockout_after)
1473 if reset_account_lockout_after < 0 or reset_account_lockout_after > 99999:
1474 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1477 if reset_account_lockout_after == 0:
1478 reset_account_lockout_after_ticks = -0x8000000000000000
1480 reset_account_lockout_after_ticks = -int(reset_account_lockout_after * (60 * 1e7))
1482 m["lockOutObservationWindow"] = ldb.MessageElement(str(reset_account_lockout_after_ticks),
1483 ldb.FLAG_MOD_REPLACE, "lockOutObservationWindow")
1484 msgs.append("Duration to reset account lockout after changed!")
1486 if max_pwd_age > 0 and min_pwd_age >= max_pwd_age:
1487 raise CommandError("Maximum password age (%d) must be greater than minimum password age (%d)!" % (max_pwd_age, min_pwd_age))
1490 raise CommandError("You must specify at least one option to set. Try --help")
1492 msgs.append("All changes applied successfully!")
1493 self.message("\n".join(msgs))
1495 raise CommandError("Wrong argument '%s'!" % subcommand)
1498 class cmd_domain_classicupgrade(Command):
1499 """Upgrade from Samba classic (NT4-like) database to Samba AD DC database.
1501 Specify either a directory with all Samba classic DC databases and state files (with --dbdir) or
1502 the testparm utility from your classic installation (with --testparm).
1505 synopsis = "%prog [options] <classic_smb_conf>"
1507 takes_optiongroups = {
1508 "sambaopts": options.SambaOptions,
1509 "versionopts": options.VersionOptions
1513 Option("--dbdir", type="string", metavar="DIR",
1514 help="Path to samba classic DC database directory"),
1515 Option("--testparm", type="string", metavar="PATH",
1516 help="Path to samba classic DC testparm utility from the previous installation. This allows the default paths of the previous installation to be followed"),
1517 Option("--targetdir", type="string", metavar="DIR",
1518 help="Path prefix where the new Samba 4.0 AD domain should be initialised"),
1519 Option("--quiet", help="Be quiet", action="store_true"),
1520 Option("--verbose", help="Be verbose", action="store_true"),
1521 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
1522 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
1523 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
1524 "BIND9_FLATFILE uses bind9 text database to store zone information, "
1525 "BIND9_DLZ uses samba4 AD to store zone information, "
1526 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
1527 default="SAMBA_INTERNAL")
1531 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
1532 action="store_true"),
1533 Option("--use-xattrs", type="choice", choices=["yes","no","auto"],
1534 metavar="[yes|no|auto]",
1535 help="Define if we should use the native fs capabilities or a tdb file for "
1536 "storing attributes likes ntacl when --use-ntvfs is set. "
1537 "auto tries to make an inteligent guess based on the user rights and system capabilities",
1540 if samba.is_ntvfs_fileserver_built():
1541 takes_options.extend(ntvfs_options)
1543 takes_args = ["smbconf"]
1545 def run(self, smbconf=None, targetdir=None, dbdir=None, testparm=None,
1546 quiet=False, verbose=False, use_xattrs="auto", sambaopts=None, versionopts=None,
1547 dns_backend=None, use_ntvfs=False):
1549 if not os.path.exists(smbconf):
1550 raise CommandError("File %s does not exist" % smbconf)
1552 if testparm and not os.path.exists(testparm):
1553 raise CommandError("Testparm utility %s does not exist" % testparm)
1555 if dbdir and not os.path.exists(dbdir):
1556 raise CommandError("Directory %s does not exist" % dbdir)
1558 if not dbdir and not testparm:
1559 raise CommandError("Please specify either dbdir or testparm")
1561 logger = self.get_logger()
1563 logger.setLevel(logging.DEBUG)
1565 logger.setLevel(logging.WARNING)
1567 logger.setLevel(logging.INFO)
1569 if dbdir and testparm:
1570 logger.warning("both dbdir and testparm specified, ignoring dbdir.")
1573 lp = sambaopts.get_loadparm()
1575 s3conf = s3param.get_context()
1578 s3conf.set("realm", sambaopts.realm)
1580 if targetdir is not None:
1581 if not os.path.isdir(targetdir):
1585 if use_xattrs == "yes":
1587 elif use_xattrs == "auto" and use_ntvfs == False:
1589 elif use_ntvfs == False:
1590 raise CommandError("--use-xattrs=no requires --use-ntvfs (not supported for production use). "
1591 "Please re-run with --use-xattrs omitted.")
1592 elif use_xattrs == "auto" and not s3conf.get("posix:eadb"):
1594 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
1596 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
1599 samba.ntacls.setntacl(lp, tmpfile.name,
1600 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
1603 # FIXME: Don't catch all exceptions here
1604 logger.info("You are not root or your system does not support xattr, using tdb backend for attributes. "
1605 "If you intend to use this provision in production, rerun the script as root on a system supporting xattrs.")
1609 # Set correct default values from dbdir or testparm
1612 paths["state directory"] = dbdir
1613 paths["private dir"] = dbdir
1614 paths["lock directory"] = dbdir
1615 paths["smb passwd file"] = dbdir + "/smbpasswd"
1617 paths["state directory"] = get_testparm_var(testparm, smbconf, "state directory")
1618 paths["private dir"] = get_testparm_var(testparm, smbconf, "private dir")
1619 paths["smb passwd file"] = get_testparm_var(testparm, smbconf, "smb passwd file")
1620 paths["lock directory"] = get_testparm_var(testparm, smbconf, "lock directory")
1621 # "testparm" from Samba 3 < 3.4.x is not aware of the parameter
1622 # "state directory", instead make use of "lock directory"
1623 if len(paths["state directory"]) == 0:
1624 paths["state directory"] = paths["lock directory"]
1627 s3conf.set(p, paths[p])
1629 # load smb.conf parameters
1630 logger.info("Reading smb.conf")
1631 s3conf.load(smbconf)
1632 samba3 = Samba3(smbconf, s3conf)
1634 logger.info("Provisioning")
1635 upgrade_from_samba3(samba3, logger, targetdir, session_info=system_session(),
1636 useeadb=eadb, dns_backend=dns_backend, use_ntvfs=use_ntvfs)
1639 class cmd_domain_samba3upgrade(cmd_domain_classicupgrade):
1640 __doc__ = cmd_domain_classicupgrade.__doc__
1642 # This command is present for backwards compatibility only,
1643 # and should not be shown.
1647 class LocalDCCredentialsOptions(options.CredentialsOptions):
1648 def __init__(self, parser):
1649 options.CredentialsOptions.__init__(self, parser, special_name="local-dc")
1651 class DomainTrustCommand(Command):
1652 """List domain trusts."""
1655 Command.__init__(self)
1656 self.local_lp = None
1658 self.local_server = None
1659 self.local_binding_string = None
1660 self.local_creds = None
1662 self.remote_server = None
1663 self.remote_binding_string = None
1664 self.remote_creds = None
1666 def _uint32(self, v):
1667 return ctypes.c_uint32(v).value
1669 def check_runtime_error(self, runtime, val):
1673 err32 = self._uint32(runtime[0])
1679 class LocalRuntimeError(CommandError):
1680 def __init__(exception_self, self, runtime, message):
1681 err32 = self._uint32(runtime[0])
1683 msg = "LOCAL_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1684 self.local_server, message, err32, errstr)
1685 CommandError.__init__(exception_self, msg)
1687 class RemoteRuntimeError(CommandError):
1688 def __init__(exception_self, self, runtime, message):
1689 err32 = self._uint32(runtime[0])
1691 msg = "REMOTE_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1692 self.remote_server, message, err32, errstr)
1693 CommandError.__init__(exception_self, msg)
1695 class LocalLdbError(CommandError):
1696 def __init__(exception_self, self, ldb_error, message):
1697 errval = ldb_error[0]
1698 errstr = ldb_error[1]
1699 msg = "LOCAL_DC[%s]: %s - ERROR(%d) - %s" % (
1700 self.local_server, message, errval, errstr)
1701 CommandError.__init__(exception_self, msg)
1703 def setup_local_server(self, sambaopts, localdcopts):
1704 if self.local_server is not None:
1705 return self.local_server
1707 lp = sambaopts.get_loadparm()
1709 local_server = localdcopts.ipaddress
1710 if local_server is None:
1711 server_role = lp.server_role()
1712 if server_role != "ROLE_ACTIVE_DIRECTORY_DC":
1713 raise CommandError("Invalid server_role %s" % (server_role))
1714 local_server = lp.get('netbios name')
1715 local_transport = "ncalrpc"
1716 local_binding_options = ""
1717 local_binding_options += ",auth_type=ncalrpc_as_system"
1718 local_ldap_url = None
1721 local_transport = "ncacn_np"
1722 local_binding_options = ""
1723 local_ldap_url = "ldap://%s" % local_server
1724 local_creds = localdcopts.get_credentials(lp)
1728 self.local_server = local_server
1729 self.local_binding_string = "%s:%s[%s]" % (local_transport, local_server, local_binding_options)
1730 self.local_ldap_url = local_ldap_url
1731 self.local_creds = local_creds
1732 return self.local_server
1734 def new_local_lsa_connection(self):
1735 return lsa.lsarpc(self.local_binding_string, self.local_lp, self.local_creds)
1737 def new_local_netlogon_connection(self):
1738 return netlogon.netlogon(self.local_binding_string, self.local_lp, self.local_creds)
1740 def new_local_ldap_connection(self):
1741 return SamDB(url=self.local_ldap_url,
1742 session_info=system_session(),
1743 credentials=self.local_creds,
1746 def setup_remote_server(self, credopts, domain,
1748 require_writable=True):
1751 assert require_writable
1753 if self.remote_server is not None:
1754 return self.remote_server
1756 self.remote_server = "__unknown__remote_server__.%s" % domain
1757 assert self.local_server is not None
1759 remote_creds = credopts.get_credentials(self.local_lp)
1760 remote_server = credopts.ipaddress
1761 remote_binding_options = ""
1763 # TODO: we should also support NT4 domains
1764 # we could use local_netlogon.netr_DsRGetDCNameEx2() with the remote domain name
1765 # and delegate NBT or CLDAP to the local netlogon server
1767 remote_net = Net(remote_creds, self.local_lp, server=remote_server)
1768 remote_flags = nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS
1769 if require_writable:
1770 remote_flags |= nbt.NBT_SERVER_WRITABLE
1772 remote_flags |= nbt.NBT_SERVER_PDC
1773 remote_info = remote_net.finddc(flags=remote_flags, domain=domain, address=remote_server)
1774 except NTSTATUSError as error:
1775 raise CommandError("Failed to find a writeable DC for domain '%s': %s" %
1778 raise CommandError("Failed to find a writeable DC for domain '%s'" % domain)
1780 nbt.NBT_SERVER_PDC: "PDC",
1781 nbt.NBT_SERVER_GC: "GC",
1782 nbt.NBT_SERVER_LDAP: "LDAP",
1783 nbt.NBT_SERVER_DS: "DS",
1784 nbt.NBT_SERVER_KDC: "KDC",
1785 nbt.NBT_SERVER_TIMESERV: "TIMESERV",
1786 nbt.NBT_SERVER_CLOSEST: "CLOSEST",
1787 nbt.NBT_SERVER_WRITABLE: "WRITABLE",
1788 nbt.NBT_SERVER_GOOD_TIMESERV: "GOOD_TIMESERV",
1789 nbt.NBT_SERVER_NDNC: "NDNC",
1790 nbt.NBT_SERVER_SELECT_SECRET_DOMAIN_6: "SELECT_SECRET_DOMAIN_6",
1791 nbt.NBT_SERVER_FULL_SECRET_DOMAIN_6: "FULL_SECRET_DOMAIN_6",
1792 nbt.NBT_SERVER_ADS_WEB_SERVICE: "ADS_WEB_SERVICE",
1793 nbt.NBT_SERVER_DS_8: "DS_8",
1794 nbt.NBT_SERVER_HAS_DNS_NAME: "HAS_DNS_NAME",
1795 nbt.NBT_SERVER_IS_DEFAULT_NC: "IS_DEFAULT_NC",
1796 nbt.NBT_SERVER_FOREST_ROOT: "FOREST_ROOT",
1798 server_type_string = self.generic_bitmap_to_string(flag_map,
1799 remote_info.server_type, names_only=True)
1800 self.outf.write("RemoteDC Netbios[%s] DNS[%s] ServerType[%s]\n" % (
1801 remote_info.pdc_name,
1802 remote_info.pdc_dns_name,
1803 server_type_string))
1805 self.remote_server = remote_info.pdc_dns_name
1806 self.remote_binding_string="ncacn_np:%s[%s]" % (self.remote_server, remote_binding_options)
1807 self.remote_creds = remote_creds
1808 return self.remote_server
1810 def new_remote_lsa_connection(self):
1811 return lsa.lsarpc(self.remote_binding_string, self.local_lp, self.remote_creds)
1813 def new_remote_netlogon_connection(self):
1814 return netlogon.netlogon(self.remote_binding_string, self.local_lp, self.remote_creds)
1816 def get_lsa_info(self, conn, policy_access):
1817 objectAttr = lsa.ObjectAttribute()
1818 objectAttr.sec_qos = lsa.QosInfo()
1820 policy = conn.OpenPolicy2(''.decode('utf-8'),
1821 objectAttr, policy_access)
1823 info = conn.QueryInfoPolicy2(policy, lsa.LSA_POLICY_INFO_DNS)
1825 return (policy, info)
1827 def get_netlogon_dc_info(self, conn, server):
1828 info = conn.netr_DsRGetDCNameEx2(server,
1829 None, 0, None, None, None,
1830 netlogon.DS_RETURN_DNS_NAME)
1833 def netr_DomainTrust_to_name(self, t):
1834 if t.trust_type == lsa.LSA_TRUST_TYPE_DOWNLEVEL:
1835 return t.netbios_name
1839 def netr_DomainTrust_to_type(self, a, t):
1841 primary_parent = None
1843 if _t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
1845 if not _t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1846 primary_parent = a[_t.parent_index]
1849 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1850 if t is primary_parent:
1853 if t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1856 parent = a[t.parent_index]
1857 if parent is primary:
1862 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1867 def netr_DomainTrust_to_transitive(self, t):
1868 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1871 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE:
1874 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1879 def netr_DomainTrust_to_direction(self, t):
1880 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND and \
1881 t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1884 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND:
1887 if t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1892 def generic_enum_to_string(self, e_dict, v, names_only=False):
1896 v32 = self._uint32(v)
1897 w = "__unknown__%08X__" % v32
1899 r = "0x%x (%s)" % (v, w)
1902 def generic_bitmap_to_string(self, b_dict, v, names_only=False):
1907 for b in sorted(b_dict.keys()):
1914 c32 = self._uint32(c)
1915 s += ["__unknown_%08X__" % c32]
1920 r = "0x%x (%s)" % (v, w)
1923 def trustType_string(self, v):
1925 lsa.LSA_TRUST_TYPE_DOWNLEVEL : "DOWNLEVEL",
1926 lsa.LSA_TRUST_TYPE_UPLEVEL : "UPLEVEL",
1927 lsa.LSA_TRUST_TYPE_MIT : "MIT",
1928 lsa.LSA_TRUST_TYPE_DCE : "DCE",
1930 return self.generic_enum_to_string(types, v)
1932 def trustDirection_string(self, v):
1934 lsa.LSA_TRUST_DIRECTION_INBOUND |
1935 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "BOTH",
1936 lsa.LSA_TRUST_DIRECTION_INBOUND : "INBOUND",
1937 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "OUTBOUND",
1939 return self.generic_enum_to_string(directions, v)
1941 def trustAttributes_string(self, v):
1943 lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE : "NON_TRANSITIVE",
1944 lsa.LSA_TRUST_ATTRIBUTE_UPLEVEL_ONLY : "UPLEVEL_ONLY",
1945 lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN : "QUARANTINED_DOMAIN",
1946 lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE : "FOREST_TRANSITIVE",
1947 lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION : "CROSS_ORGANIZATION",
1948 lsa.LSA_TRUST_ATTRIBUTE_WITHIN_FOREST : "WITHIN_FOREST",
1949 lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL : "TREAT_AS_EXTERNAL",
1950 lsa.LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION : "USES_RC4_ENCRYPTION",
1952 return self.generic_bitmap_to_string(attributes, v)
1954 def kerb_EncTypes_string(self, v):
1956 security.KERB_ENCTYPE_DES_CBC_CRC : "DES_CBC_CRC",
1957 security.KERB_ENCTYPE_DES_CBC_MD5 : "DES_CBC_MD5",
1958 security.KERB_ENCTYPE_RC4_HMAC_MD5 : "RC4_HMAC_MD5",
1959 security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96 : "AES128_CTS_HMAC_SHA1_96",
1960 security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96 : "AES256_CTS_HMAC_SHA1_96",
1961 security.KERB_ENCTYPE_FAST_SUPPORTED : "FAST_SUPPORTED",
1962 security.KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED : "COMPOUND_IDENTITY_SUPPORTED",
1963 security.KERB_ENCTYPE_CLAIMS_SUPPORTED : "CLAIMS_SUPPORTED",
1964 security.KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED : "RESOURCE_SID_COMPRESSION_DISABLED",
1966 return self.generic_bitmap_to_string(enctypes, v)
1968 def entry_tln_status(self, e_flags, ):
1970 return "Status[Enabled]"
1973 lsa.LSA_TLN_DISABLED_NEW : "Disabled-New",
1974 lsa.LSA_TLN_DISABLED_ADMIN : "Disabled",
1975 lsa.LSA_TLN_DISABLED_CONFLICT : "Disabled-Conflicting",
1977 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
1979 def entry_dom_status(self, e_flags):
1981 return "Status[Enabled]"
1984 lsa.LSA_SID_DISABLED_ADMIN : "Disabled-SID",
1985 lsa.LSA_SID_DISABLED_CONFLICT : "Disabled-SID-Conflicting",
1986 lsa.LSA_NB_DISABLED_ADMIN : "Disabled-NB",
1987 lsa.LSA_NB_DISABLED_CONFLICT : "Disabled-NB-Conflicting",
1989 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
1991 def write_forest_trust_info(self, fti, tln=None, collisions=None):
1993 tln_string = " TDO[%s]" % tln
1997 self.outf.write("Namespaces[%d]%s:\n" % (
1998 len(fti.entries), tln_string))
2000 for i in xrange(0, len(fti.entries)):
2004 collision_string = ""
2006 if collisions is not None:
2007 for c in collisions.entries:
2011 collision_string = " Collision[%s]" % (c.name.string)
2013 d = e.forest_trust_data
2014 if e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
2015 self.outf.write("TLN: %-32s DNS[*.%s]%s\n" % (
2016 self.entry_tln_status(flags),
2017 d.string, collision_string))
2018 elif e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
2019 self.outf.write("TLN_EX: %-29s DNS[*.%s]\n" % (
2021 elif e.type == lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
2022 self.outf.write("DOM: %-32s DNS[%s] Netbios[%s] SID[%s]%s\n" % (
2023 self.entry_dom_status(flags),
2024 d.dns_domain_name.string,
2025 d.netbios_domain_name.string,
2026 d.domain_sid, collision_string))
2029 class cmd_domain_trust_list(DomainTrustCommand):
2030 """List domain trusts."""
2032 synopsis = "%prog [options]"
2034 takes_optiongroups = {
2035 "sambaopts": options.SambaOptions,
2036 "versionopts": options.VersionOptions,
2037 "localdcopts": LocalDCCredentialsOptions,
2043 def run(self, sambaopts=None, versionopts=None, localdcopts=None):
2045 local_server = self.setup_local_server(sambaopts, localdcopts)
2047 local_netlogon = self.new_local_netlogon_connection()
2048 except RuntimeError as error:
2049 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2052 local_netlogon_trusts = local_netlogon.netr_DsrEnumerateDomainTrusts(local_server,
2053 netlogon.NETR_TRUST_FLAG_IN_FOREST |
2054 netlogon.NETR_TRUST_FLAG_OUTBOUND |
2055 netlogon.NETR_TRUST_FLAG_INBOUND)
2056 except RuntimeError as error:
2057 if self.check_runtime_error(error, werror.WERR_RPC_S_PROCNUM_OUT_OF_RANGE):
2058 # TODO: we could implement a fallback to lsa.EnumTrustDom()
2059 raise CommandError("LOCAL_DC[%s]: netr_DsrEnumerateDomainTrusts not supported." % (
2061 raise self.LocalRuntimeError(self, error, "netr_DsrEnumerateDomainTrusts failed")
2063 a = local_netlogon_trusts.array
2065 if t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
2067 self.outf.write("%-14s %-15s %-19s %s\n" % (
2068 "Type[%s]" % self.netr_DomainTrust_to_type(a, t),
2069 "Transitive[%s]" % self.netr_DomainTrust_to_transitive(t),
2070 "Direction[%s]" % self.netr_DomainTrust_to_direction(t),
2071 "Name[%s]" % self.netr_DomainTrust_to_name(t)))
2074 class cmd_domain_trust_show(DomainTrustCommand):
2075 """Show trusted domain details."""
2077 synopsis = "%prog NAME [options]"
2079 takes_optiongroups = {
2080 "sambaopts": options.SambaOptions,
2081 "versionopts": options.VersionOptions,
2082 "localdcopts": LocalDCCredentialsOptions,
2088 takes_args = ["domain"]
2090 def run(self, domain, sambaopts=None, versionopts=None, localdcopts=None):
2092 local_server = self.setup_local_server(sambaopts, localdcopts)
2094 local_lsa = self.new_local_lsa_connection()
2095 except RuntimeError as error:
2096 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2099 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2100 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2101 except RuntimeError as error:
2102 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2104 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2105 local_lsa_info.name.string,
2106 local_lsa_info.dns_domain.string,
2107 local_lsa_info.sid))
2109 lsaString = lsa.String()
2110 lsaString.string = domain
2112 local_tdo_full = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2113 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2114 local_tdo_info = local_tdo_full.info_ex
2115 local_tdo_posix = local_tdo_full.posix_offset
2116 except NTSTATUSError as error:
2117 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2118 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2120 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(FULL_INFO) failed")
2123 local_tdo_enctypes = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2124 lsaString, lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES)
2125 except NTSTATUSError as error:
2126 if self.check_runtime_error(error, ntstatus.NT_STATUS_INVALID_PARAMETER):
2128 if self.check_runtime_error(error, ntstatus.NT_STATUS_INVALID_INFO_CLASS):
2131 if error is not None:
2132 raise self.LocalRuntimeError(self, error,
2133 "QueryTrustedDomainInfoByName(SUPPORTED_ENCRYPTION_TYPES) failed")
2135 local_tdo_enctypes = lsa.TrustDomainInfoSupportedEncTypes()
2136 local_tdo_enctypes.enc_types = 0
2139 local_tdo_forest = None
2140 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2141 local_tdo_forest = local_lsa.lsaRQueryForestTrustInformation(local_policy,
2142 lsaString, lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
2143 except RuntimeError as error:
2144 if self.check_runtime_error(error, ntstatus.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
2146 if self.check_runtime_error(error, ntstatus.NT_STATUS_NOT_FOUND):
2148 if error is not None:
2149 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation failed")
2151 local_tdo_forest = lsa.ForestTrustInformation()
2152 local_tdo_forest.count = 0
2153 local_tdo_forest.entries = []
2155 self.outf.write("TrusteDomain:\n\n");
2156 self.outf.write("NetbiosName: %s\n" % local_tdo_info.netbios_name.string)
2157 if local_tdo_info.netbios_name.string != local_tdo_info.domain_name.string:
2158 self.outf.write("DnsName: %s\n" % local_tdo_info.domain_name.string)
2159 self.outf.write("SID: %s\n" % local_tdo_info.sid)
2160 self.outf.write("Type: %s\n" % self.trustType_string(local_tdo_info.trust_type))
2161 self.outf.write("Direction: %s\n" % self.trustDirection_string(local_tdo_info.trust_direction))
2162 self.outf.write("Attributes: %s\n" % self.trustAttributes_string(local_tdo_info.trust_attributes))
2163 posix_offset_u32 = ctypes.c_uint32(local_tdo_posix.posix_offset).value
2164 posix_offset_i32 = ctypes.c_int32(local_tdo_posix.posix_offset).value
2165 self.outf.write("PosixOffset: 0x%08X (%d)\n" % (posix_offset_u32, posix_offset_i32))
2166 self.outf.write("kerb_EncTypes: %s\n" % self.kerb_EncTypes_string(local_tdo_enctypes.enc_types))
2168 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2169 self.write_forest_trust_info(local_tdo_forest,
2170 tln=local_tdo_info.domain_name.string)
2174 class cmd_domain_trust_create(DomainTrustCommand):
2175 """Create a domain or forest trust."""
2177 synopsis = "%prog DOMAIN [options]"
2179 takes_optiongroups = {
2180 "sambaopts": options.SambaOptions,
2181 "versionopts": options.VersionOptions,
2182 "credopts": options.CredentialsOptions,
2183 "localdcopts": LocalDCCredentialsOptions,
2187 Option("--type", type="choice", metavar="TYPE",
2188 choices=["external", "forest"],
2189 help="The type of the trust: 'external' or 'forest'.",
2191 default="external"),
2192 Option("--direction", type="choice", metavar="DIRECTION",
2193 choices=["incoming", "outgoing", "both"],
2194 help="The trust direction: 'incoming', 'outgoing' or 'both'.",
2195 dest='trust_direction',
2197 Option("--create-location", type="choice", metavar="LOCATION",
2198 choices=["local", "both"],
2199 help="Where to create the trusted domain object: 'local' or 'both'.",
2200 dest='create_location',
2202 Option("--cross-organisation", action="store_true",
2203 help="The related domains does not belong to the same organisation.",
2204 dest='cross_organisation',
2206 Option("--quarantined", type="choice", metavar="yes|no",
2207 choices=["yes", "no", None],
2208 help="Special SID filtering rules are applied to the trust. "
2209 "With --type=external the default is yes. "
2210 "With --type=forest the default is no.",
2211 dest='quarantined_arg',
2213 Option("--not-transitive", action="store_true",
2214 help="The forest trust is not transitive.",
2215 dest='not_transitive',
2217 Option("--treat-as-external", action="store_true",
2218 help="The treat the forest trust as external.",
2219 dest='treat_as_external',
2221 Option("--no-aes-keys", action="store_false",
2222 help="The trust uses aes kerberos keys.",
2223 dest='use_aes_keys',
2225 Option("--skip-validation", action="store_false",
2226 help="Skip validation of the trust.",
2231 takes_args = ["domain"]
2233 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2234 trust_type=None, trust_direction=None, create_location=None,
2235 cross_organisation=False, quarantined_arg=None,
2236 not_transitive=False, treat_as_external=False,
2237 use_aes_keys=False, validate=True):
2239 lsaString = lsa.String()
2242 if quarantined_arg is None:
2243 if trust_type == 'external':
2245 elif quarantined_arg == 'yes':
2248 if trust_type != 'forest':
2250 raise CommandError("--not-transitive requires --type=forest")
2251 if treat_as_external:
2252 raise CommandError("--treat-as-external requires --type=forest")
2256 enc_types = lsa.TrustDomainInfoSupportedEncTypes()
2257 enc_types.enc_types = security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96
2258 enc_types.enc_types |= security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96
2260 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2261 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2262 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2264 local_trust_info = lsa.TrustDomainInfoInfoEx()
2265 local_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2266 local_trust_info.trust_direction = 0
2267 if trust_direction == "both":
2268 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2269 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2270 elif trust_direction == "incoming":
2271 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2272 elif trust_direction == "outgoing":
2273 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2274 local_trust_info.trust_attributes = 0
2275 if cross_organisation:
2276 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2278 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2279 if trust_type == "forest":
2280 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2282 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2283 if treat_as_external:
2284 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2286 def get_password(name):
2289 if password is not None and password is not '':
2291 password = getpass("New %s Password: " % name)
2292 passwordverify = getpass("Retype %s Password: " % name)
2293 if not password == passwordverify:
2295 self.outf.write("Sorry, passwords do not match.\n")
2297 incoming_secret = None
2298 outgoing_secret = None
2299 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2300 if create_location == "local":
2301 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2302 incoming_password = get_password("Incoming Trust")
2303 incoming_secret = string_to_byte_array(incoming_password.encode('utf-16-le'))
2304 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2305 outgoing_password = get_password("Outgoing Trust")
2306 outgoing_secret = string_to_byte_array(outgoing_password.encode('utf-16-le'))
2308 remote_trust_info = None
2310 # We use 240 random bytes.
2311 # Windows uses 28 or 240 random bytes. I guess it's
2312 # based on the trust type external vs. forest.
2314 # The initial trust password can be up to 512 bytes
2315 # while the versioned passwords used for periodic updates
2316 # can only be up to 498 bytes, as netr_ServerPasswordSet2()
2317 # needs to pass the NL_PASSWORD_VERSION structure within the
2318 # 512 bytes and a 2 bytes confounder is required.
2320 def random_trust_secret(length):
2321 pw = samba.generate_random_machine_password(length/2, length/2)
2322 return string_to_byte_array(pw.encode('utf-16-le'))
2324 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2325 incoming_secret = random_trust_secret(240)
2326 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2327 outgoing_secret = random_trust_secret(240)
2329 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2330 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2332 remote_trust_info = lsa.TrustDomainInfoInfoEx()
2333 remote_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2334 remote_trust_info.trust_direction = 0
2335 if trust_direction == "both":
2336 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2337 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2338 elif trust_direction == "incoming":
2339 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2340 elif trust_direction == "outgoing":
2341 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2342 remote_trust_info.trust_attributes = 0
2343 if cross_organisation:
2344 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2346 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2347 if trust_type == "forest":
2348 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2350 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2351 if treat_as_external:
2352 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2354 local_server = self.setup_local_server(sambaopts, localdcopts)
2356 local_lsa = self.new_local_lsa_connection()
2357 except RuntimeError as error:
2358 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2361 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2362 except RuntimeError as error:
2363 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2365 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2366 local_lsa_info.name.string,
2367 local_lsa_info.dns_domain.string,
2368 local_lsa_info.sid))
2371 remote_server = self.setup_remote_server(credopts, domain)
2372 except RuntimeError as error:
2373 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2376 remote_lsa = self.new_remote_lsa_connection()
2377 except RuntimeError as error:
2378 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2381 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2382 except RuntimeError as error:
2383 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2385 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2386 remote_lsa_info.name.string,
2387 remote_lsa_info.dns_domain.string,
2388 remote_lsa_info.sid))
2390 local_trust_info.domain_name.string = remote_lsa_info.dns_domain.string
2391 local_trust_info.netbios_name.string = remote_lsa_info.name.string
2392 local_trust_info.sid = remote_lsa_info.sid
2394 if remote_trust_info:
2395 remote_trust_info.domain_name.string = local_lsa_info.dns_domain.string
2396 remote_trust_info.netbios_name.string = local_lsa_info.name.string
2397 remote_trust_info.sid = local_lsa_info.sid
2400 lsaString.string = local_trust_info.domain_name.string
2401 local_old_netbios = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2402 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2403 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2404 except NTSTATUSError as error:
2405 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2406 raise self.LocalRuntimeError(self, error,
2407 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2411 lsaString.string = local_trust_info.netbios_name.string
2412 local_old_dns = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2413 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2414 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2415 except NTSTATUSError as error:
2416 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2417 raise self.LocalRuntimeError(self, error,
2418 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2421 if remote_trust_info:
2423 lsaString.string = remote_trust_info.domain_name.string
2424 remote_old_netbios = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2425 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2426 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2427 except NTSTATUSError as error:
2428 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2429 raise self.RemoteRuntimeError(self, error,
2430 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2434 lsaString.string = remote_trust_info.netbios_name.string
2435 remote_old_dns = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2436 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2437 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2438 except NTSTATUSError as error:
2439 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2440 raise self.RemoteRuntimeError(self, error,
2441 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2445 local_netlogon = self.new_local_netlogon_connection()
2446 except RuntimeError as error:
2447 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2450 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
2451 except RuntimeError as error:
2452 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
2454 if remote_trust_info:
2456 remote_netlogon = self.new_remote_netlogon_connection()
2457 except RuntimeError as error:
2458 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2461 remote_netlogon_info = self.get_netlogon_dc_info(remote_netlogon, remote_server)
2462 except RuntimeError as error:
2463 raise self.RemoteRuntimeError(self, error, "failed to get netlogon dc info")
2465 def generate_AuthInOutBlob(secret, update_time):
2467 blob = drsblobs.trustAuthInOutBlob()
2472 clear = drsblobs.AuthInfoClear()
2473 clear.size = len(secret)
2474 clear.password = secret
2476 info = drsblobs.AuthenticationInformation()
2477 info.LastUpdateTime = samba.unix2nttime(update_time)
2478 info.AuthType = lsa.TRUST_AUTH_TYPE_CLEAR
2479 info.AuthInfo = clear
2481 array = drsblobs.AuthenticationInformationArray()
2483 array.array = [info]
2485 blob = drsblobs.trustAuthInOutBlob()
2487 blob.current = array
2491 def generate_AuthInfoInternal(session_key, incoming=None, outgoing=None):
2492 confounder = [0] * 512
2493 for i in range(len(confounder)):
2494 confounder[i] = random.randint(0, 255)
2496 trustpass = drsblobs.trustDomainPasswords()
2498 trustpass.confounder = confounder
2499 trustpass.outgoing = outgoing
2500 trustpass.incoming = incoming
2502 trustpass_blob = ndr_pack(trustpass)
2504 encrypted_trustpass = arcfour_encrypt(session_key, trustpass_blob)
2506 auth_blob = lsa.DATA_BUF2()
2507 auth_blob.size = len(encrypted_trustpass)
2508 auth_blob.data = string_to_byte_array(encrypted_trustpass)
2510 auth_info = lsa.TrustDomainInfoAuthInfoInternal()
2511 auth_info.auth_blob = auth_blob
2515 update_time = samba.current_unix_time()
2516 incoming_blob = generate_AuthInOutBlob(incoming_secret, update_time)
2517 outgoing_blob = generate_AuthInOutBlob(outgoing_secret, update_time)
2519 local_tdo_handle = None
2520 remote_tdo_handle = None
2522 local_auth_info = generate_AuthInfoInternal(local_lsa.session_key,
2523 incoming=incoming_blob,
2524 outgoing=outgoing_blob)
2525 if remote_trust_info:
2526 remote_auth_info = generate_AuthInfoInternal(remote_lsa.session_key,
2527 incoming=outgoing_blob,
2528 outgoing=incoming_blob)
2531 if remote_trust_info:
2532 self.outf.write("Creating remote TDO.\n")
2533 current_request = { "location": "remote", "name": "CreateTrustedDomainEx2"}
2534 remote_tdo_handle = remote_lsa.CreateTrustedDomainEx2(remote_policy,
2537 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2538 self.outf.write("Remote TDO created.\n")
2540 self.outf.write("Setting supported encryption types on remote TDO.\n")
2541 current_request = { "location": "remote", "name": "SetInformationTrustedDomain"}
2542 remote_lsa.SetInformationTrustedDomain(remote_tdo_handle,
2543 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2546 self.outf.write("Creating local TDO.\n")
2547 current_request = { "location": "local", "name": "CreateTrustedDomainEx2"}
2548 local_tdo_handle = local_lsa.CreateTrustedDomainEx2(local_policy,
2551 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2552 self.outf.write("Local TDO created\n")
2554 self.outf.write("Setting supported encryption types on local TDO.\n")
2555 current_request = { "location": "local", "name": "SetInformationTrustedDomain"}
2556 local_lsa.SetInformationTrustedDomain(local_tdo_handle,
2557 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2559 except RuntimeError as error:
2560 self.outf.write("Error: %s failed %sly - cleaning up\n" % (
2561 current_request['name'], current_request['location']))
2562 if remote_tdo_handle:
2563 self.outf.write("Deleting remote TDO.\n")
2564 remote_lsa.DeleteObject(remote_tdo_handle)
2565 remote_tdo_handle = None
2566 if local_tdo_handle:
2567 self.outf.write("Deleting local TDO.\n")
2568 local_lsa.DeleteObject(local_tdo_handle)
2569 local_tdo_handle = None
2570 if current_request['location'] is "remote":
2571 raise self.RemoteRuntimeError(self, error, "%s" % (
2572 current_request['name']))
2573 raise self.LocalRuntimeError(self, error, "%s" % (
2574 current_request['name']))
2577 if local_trust_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2578 self.outf.write("Setup local forest trust information...\n")
2580 # get all information about the remote trust
2581 # this triggers netr_GetForestTrustInformation to the remote domain
2582 # and lsaRSetForestTrustInformation() locally, but new top level
2583 # names are disabled by default.
2584 local_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
2585 remote_lsa_info.dns_domain.string,
2586 netlogon.DS_GFTI_UPDATE_TDO)
2587 except RuntimeError as error:
2588 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2591 # here we try to enable all top level names
2592 local_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
2593 remote_lsa_info.dns_domain,
2594 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2597 except RuntimeError as error:
2598 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2600 self.write_forest_trust_info(local_forest_info,
2601 tln=remote_lsa_info.dns_domain.string,
2602 collisions=local_forest_collision)
2604 if remote_trust_info:
2605 self.outf.write("Setup remote forest trust information...\n")
2607 # get all information about the local trust (from the perspective of the remote domain)
2608 # this triggers netr_GetForestTrustInformation to our domain.
2609 # and lsaRSetForestTrustInformation() remotely, but new top level
2610 # names are disabled by default.
2611 remote_forest_info = remote_netlogon.netr_DsRGetForestTrustInformation(remote_netlogon_info.dc_unc,
2612 local_lsa_info.dns_domain.string,
2613 netlogon.DS_GFTI_UPDATE_TDO)
2614 except RuntimeError as error:
2615 raise self.RemoteRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2618 # here we try to enable all top level names
2619 remote_forest_collision = remote_lsa.lsaRSetForestTrustInformation(remote_policy,
2620 local_lsa_info.dns_domain,
2621 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2624 except RuntimeError as error:
2625 raise self.RemoteRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2627 self.write_forest_trust_info(remote_forest_info,
2628 tln=local_lsa_info.dns_domain.string,
2629 collisions=remote_forest_collision)
2631 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2632 self.outf.write("Validating outgoing trust...\n")
2634 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_netlogon_info.dc_unc,
2635 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2637 remote_lsa_info.dns_domain.string)
2638 except RuntimeError as error:
2639 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2641 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2642 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2644 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2645 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2646 local_trust_verify.trusted_dc_name,
2647 local_trust_verify.tc_connection_status[1],
2648 local_trust_verify.pdc_connection_status[1])
2650 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2651 local_trust_verify.trusted_dc_name,
2652 local_trust_verify.tc_connection_status[1],
2653 local_trust_verify.pdc_connection_status[1])
2655 if local_trust_status != werror.WERR_SUCCESS or local_conn_status != werror.WERR_SUCCESS:
2656 raise CommandError(local_validation)
2658 self.outf.write("OK: %s\n" % local_validation)
2660 if remote_trust_info:
2661 if remote_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2662 self.outf.write("Validating incoming trust...\n")
2664 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_netlogon_info.dc_unc,
2665 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2667 local_lsa_info.dns_domain.string)
2668 except RuntimeError as error:
2669 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2671 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2672 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2674 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2675 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2676 remote_trust_verify.trusted_dc_name,
2677 remote_trust_verify.tc_connection_status[1],
2678 remote_trust_verify.pdc_connection_status[1])
2680 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2681 remote_trust_verify.trusted_dc_name,
2682 remote_trust_verify.tc_connection_status[1],
2683 remote_trust_verify.pdc_connection_status[1])
2685 if remote_trust_status != werror.WERR_SUCCESS or remote_conn_status != werror.WERR_SUCCESS:
2686 raise CommandError(remote_validation)
2688 self.outf.write("OK: %s\n" % remote_validation)
2690 if remote_tdo_handle is not None:
2692 remote_lsa.Close(remote_tdo_handle)
2693 except RuntimeError as error:
2695 remote_tdo_handle = None
2696 if local_tdo_handle is not None:
2698 local_lsa.Close(local_tdo_handle)
2699 except RuntimeError as error:
2701 local_tdo_handle = None
2703 self.outf.write("Success.\n")
2706 class cmd_domain_trust_delete(DomainTrustCommand):
2707 """Delete a domain trust."""
2709 synopsis = "%prog DOMAIN [options]"
2711 takes_optiongroups = {
2712 "sambaopts": options.SambaOptions,
2713 "versionopts": options.VersionOptions,
2714 "credopts": options.CredentialsOptions,
2715 "localdcopts": LocalDCCredentialsOptions,
2719 Option("--delete-location", type="choice", metavar="LOCATION",
2720 choices=["local", "both"],
2721 help="Where to delete the trusted domain object: 'local' or 'both'.",
2722 dest='delete_location',
2726 takes_args = ["domain"]
2728 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2729 delete_location=None):
2731 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2732 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2733 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2735 if delete_location == "local":
2736 remote_policy_access = None
2738 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2739 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2740 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2742 local_server = self.setup_local_server(sambaopts, localdcopts)
2744 local_lsa = self.new_local_lsa_connection()
2745 except RuntimeError as error:
2746 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2749 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2750 except RuntimeError as error:
2751 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2753 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2754 local_lsa_info.name.string,
2755 local_lsa_info.dns_domain.string,
2756 local_lsa_info.sid))
2758 local_tdo_info = None
2759 local_tdo_handle = None
2760 remote_tdo_info = None
2761 remote_tdo_handle = None
2763 lsaString = lsa.String()
2765 lsaString.string = domain
2766 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2767 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2768 except NTSTATUSError as error:
2769 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2770 raise CommandError("Failed to find trust for domain '%s'" % domain)
2771 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2774 if remote_policy_access is not None:
2776 remote_server = self.setup_remote_server(credopts, domain)
2777 except RuntimeError as error:
2778 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2781 remote_lsa = self.new_remote_lsa_connection()
2782 except RuntimeError as error:
2783 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2786 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2787 except RuntimeError as error:
2788 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2790 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2791 remote_lsa_info.name.string,
2792 remote_lsa_info.dns_domain.string,
2793 remote_lsa_info.sid))
2795 if remote_lsa_info.sid != local_tdo_info.sid or \
2796 remote_lsa_info.name.string != local_tdo_info.netbios_name.string or \
2797 remote_lsa_info.dns_domain.string != local_tdo_info.domain_name.string:
2798 raise CommandError("LocalTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2799 local_tdo_info.netbios_name.string,
2800 local_tdo_info.domain_name.string,
2801 local_tdo_info.sid))
2804 lsaString.string = local_lsa_info.dns_domain.string
2805 remote_tdo_info = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2806 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2807 except NTSTATUSError as error:
2808 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2809 raise self.RemoteRuntimeError(self, error, "QueryTrustedDomainInfoByName(%s)" % (
2813 if remote_tdo_info is not None:
2814 if local_lsa_info.sid != remote_tdo_info.sid or \
2815 local_lsa_info.name.string != remote_tdo_info.netbios_name.string or \
2816 local_lsa_info.dns_domain.string != remote_tdo_info.domain_name.string:
2817 raise CommandError("RemoteTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2818 remote_tdo_info.netbios_name.string,
2819 remote_tdo_info.domain_name.string,
2820 remote_tdo_info.sid))
2822 if local_tdo_info is not None:
2824 lsaString.string = local_tdo_info.domain_name.string
2825 local_tdo_handle = local_lsa.OpenTrustedDomainByName(local_policy,
2827 security.SEC_STD_DELETE)
2828 except RuntimeError as error:
2829 raise self.LocalRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2832 local_lsa.DeleteObject(local_tdo_handle)
2833 local_tdo_handle = None
2835 if remote_tdo_info is not None:
2837 lsaString.string = remote_tdo_info.domain_name.string
2838 remote_tdo_handle = remote_lsa.OpenTrustedDomainByName(remote_policy,
2840 security.SEC_STD_DELETE)
2841 except RuntimeError as error:
2842 raise self.RemoteRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2845 if remote_tdo_handle is not None:
2847 remote_lsa.DeleteObject(remote_tdo_handle)
2848 remote_tdo_handle = None
2849 self.outf.write("RemoteTDO deleted.\n")
2850 except RuntimeError as error:
2851 self.outf.write("%s\n" % self.RemoteRuntimeError(self, error, "DeleteObject() failed"))
2853 if local_tdo_handle is not None:
2855 local_lsa.DeleteObject(local_tdo_handle)
2856 local_tdo_handle = None
2857 self.outf.write("LocalTDO deleted.\n")
2858 except RuntimeError as error:
2859 self.outf.write("%s\n" % self.LocalRuntimeError(self, error, "DeleteObject() failed"))
2863 class cmd_domain_trust_validate(DomainTrustCommand):
2864 """Validate a domain trust."""
2866 synopsis = "%prog DOMAIN [options]"
2868 takes_optiongroups = {
2869 "sambaopts": options.SambaOptions,
2870 "versionopts": options.VersionOptions,
2871 "credopts": options.CredentialsOptions,
2872 "localdcopts": LocalDCCredentialsOptions,
2876 Option("--validate-location", type="choice", metavar="LOCATION",
2877 choices=["local", "both"],
2878 help="Where to validate the trusted domain object: 'local' or 'both'.",
2879 dest='validate_location',
2883 takes_args = ["domain"]
2885 def run(self, domain, sambaopts=None, versionopts=None, credopts=None, localdcopts=None,
2886 validate_location=None):
2888 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2890 local_server = self.setup_local_server(sambaopts, localdcopts)
2892 local_lsa = self.new_local_lsa_connection()
2893 except RuntimeError as error:
2894 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2897 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2898 except RuntimeError as error:
2899 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2901 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2902 local_lsa_info.name.string,
2903 local_lsa_info.dns_domain.string,
2904 local_lsa_info.sid))
2907 lsaString = lsa.String()
2908 lsaString.string = domain
2909 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2910 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2911 except NTSTATUSError as error:
2912 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2913 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2915 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
2917 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
2918 local_tdo_info.netbios_name.string,
2919 local_tdo_info.domain_name.string,
2920 local_tdo_info.sid))
2923 local_netlogon = self.new_local_netlogon_connection()
2924 except RuntimeError as error:
2925 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2928 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_server,
2929 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2931 local_tdo_info.domain_name.string)
2932 except RuntimeError as error:
2933 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2935 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2936 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2938 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2939 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2940 local_trust_verify.trusted_dc_name,
2941 local_trust_verify.tc_connection_status[1],
2942 local_trust_verify.pdc_connection_status[1])
2944 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2945 local_trust_verify.trusted_dc_name,
2946 local_trust_verify.tc_connection_status[1],
2947 local_trust_verify.pdc_connection_status[1])
2949 if local_trust_status != werror.WERR_SUCCESS or local_conn_status != werror.WERR_SUCCESS:
2950 raise CommandError(local_validation)
2952 self.outf.write("OK: %s\n" % local_validation)
2955 server = local_trust_verify.trusted_dc_name.replace('\\', '')
2956 domain_and_server = "%s\\%s" % (local_tdo_info.domain_name.string, server)
2957 local_trust_rediscover = local_netlogon.netr_LogonControl2Ex(local_server,
2958 netlogon.NETLOGON_CONTROL_REDISCOVER,
2961 except RuntimeError as error:
2962 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
2964 local_conn_status = self._uint32(local_trust_rediscover.tc_connection_status[0])
2965 local_rediscover = "LocalRediscover: DC[%s] CONNECTION[%s]" % (
2966 local_trust_rediscover.trusted_dc_name,
2967 local_trust_rediscover.tc_connection_status[1])
2969 if local_conn_status != werror.WERR_SUCCESS:
2970 raise CommandError(local_rediscover)
2972 self.outf.write("OK: %s\n" % local_rediscover)
2974 if validate_location != "local":
2976 remote_server = self.setup_remote_server(credopts, domain, require_pdc=False)
2977 except RuntimeError as error:
2978 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2981 remote_netlogon = self.new_remote_netlogon_connection()
2982 except RuntimeError as error:
2983 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2986 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_server,
2987 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2989 local_lsa_info.dns_domain.string)
2990 except RuntimeError as error:
2991 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2993 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2994 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2996 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2997 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2998 remote_trust_verify.trusted_dc_name,
2999 remote_trust_verify.tc_connection_status[1],
3000 remote_trust_verify.pdc_connection_status[1])
3002 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
3003 remote_trust_verify.trusted_dc_name,
3004 remote_trust_verify.tc_connection_status[1],
3005 remote_trust_verify.pdc_connection_status[1])
3007 if remote_trust_status != werror.WERR_SUCCESS or remote_conn_status != werror.WERR_SUCCESS:
3008 raise CommandError(remote_validation)
3010 self.outf.write("OK: %s\n" % remote_validation)
3013 server = remote_trust_verify.trusted_dc_name.replace('\\', '')
3014 domain_and_server = "%s\\%s" % (local_lsa_info.dns_domain.string, server)
3015 remote_trust_rediscover = remote_netlogon.netr_LogonControl2Ex(remote_server,
3016 netlogon.NETLOGON_CONTROL_REDISCOVER,
3019 except RuntimeError as error:
3020 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
3022 remote_conn_status = self._uint32(remote_trust_rediscover.tc_connection_status[0])
3024 remote_rediscover = "RemoteRediscover: DC[%s] CONNECTION[%s]" % (
3025 remote_trust_rediscover.trusted_dc_name,
3026 remote_trust_rediscover.tc_connection_status[1])
3028 if remote_conn_status != werror.WERR_SUCCESS:
3029 raise CommandError(remote_rediscover)
3031 self.outf.write("OK: %s\n" % remote_rediscover)
3035 class cmd_domain_trust_namespaces(DomainTrustCommand):
3036 """Manage forest trust namespaces."""
3038 synopsis = "%prog [DOMAIN] [options]"
3040 takes_optiongroups = {
3041 "sambaopts": options.SambaOptions,
3042 "versionopts": options.VersionOptions,
3043 "localdcopts": LocalDCCredentialsOptions,
3047 Option("--refresh", type="choice", metavar="check|store",
3048 choices=["check", "store", None],
3049 help="List and maybe store refreshed forest trust information: 'check' or 'store'.",
3052 Option("--enable-all", action="store_true",
3053 help="Try to update disabled entries, not allowed with --refresh=check.",
3056 Option("--enable-tln", action="append", metavar='DNSDOMAIN',
3057 help="Enable a top level name entry. Can be specified multiple times.",
3060 Option("--disable-tln", action="append", metavar='DNSDOMAIN',
3061 help="Disable a top level name entry. Can be specified multiple times.",
3064 Option("--add-tln-ex", action="append", metavar='DNSDOMAIN',
3065 help="Add a top level exclusion entry. Can be specified multiple times.",
3068 Option("--delete-tln-ex", action="append", metavar='DNSDOMAIN',
3069 help="Delete a top level exclusion entry. Can be specified multiple times.",
3070 dest='delete_tln_ex',
3072 Option("--enable-nb", action="append", metavar='NETBIOSDOMAIN',
3073 help="Enable a netbios name in a domain entry. Can be specified multiple times.",
3076 Option("--disable-nb", action="append", metavar='NETBIOSDOMAIN',
3077 help="Disable a netbios name in a domain entry. Can be specified multiple times.",
3080 Option("--enable-sid", action="append", metavar='DOMAINSID',
3081 help="Enable a SID in a domain entry. Can be specified multiple times.",
3082 dest='enable_sid_str',
3084 Option("--disable-sid", action="append", metavar='DOMAINSID',
3085 help="Disable a SID in a domain entry. Can be specified multiple times.",
3086 dest='disable_sid_str',
3088 Option("--add-upn-suffix", action="append", metavar='DNSDOMAIN',
3089 help="Add a new uPNSuffixes attribute for the local forest. Can be specified multiple times.",
3092 Option("--delete-upn-suffix", action="append", metavar='DNSDOMAIN',
3093 help="Delete an existing uPNSuffixes attribute of the local forest. Can be specified multiple times.",
3096 Option("--add-spn-suffix", action="append", metavar='DNSDOMAIN',
3097 help="Add a new msDS-SPNSuffixes attribute for the local forest. Can be specified multiple times.",
3100 Option("--delete-spn-suffix", action="append", metavar='DNSDOMAIN',
3101 help="Delete an existing msDS-SPNSuffixes attribute of the local forest. Can be specified multiple times.",
3106 takes_args = ["domain?"]
3108 def run(self, domain=None, sambaopts=None, localdcopts=None, versionopts=None,
3109 refresh=None, enable_all=False,
3110 enable_tln=[], disable_tln=[], add_tln_ex=[], delete_tln_ex=[],
3111 enable_sid_str=[], disable_sid_str=[], enable_nb=[], disable_nb=[],
3112 add_upn=[], delete_upn=[], add_spn=[], delete_spn=[]):
3114 require_update = False
3117 if refresh == "store":
3118 raise CommandError("--refresh=%s not allowed without DOMAIN" % refresh)
3121 raise CommandError("--enable-all not allowed without DOMAIN")
3123 if len(enable_tln) > 0:
3124 raise CommandError("--enable-tln not allowed without DOMAIN")
3125 if len(disable_tln) > 0:
3126 raise CommandError("--disable-tln not allowed without DOMAIN")
3128 if len(add_tln_ex) > 0:
3129 raise CommandError("--add-tln-ex not allowed without DOMAIN")
3130 if len(delete_tln_ex) > 0:
3131 raise CommandError("--delete-tln-ex not allowed without DOMAIN")
3133 if len(enable_nb) > 0:
3134 raise CommandError("--enable-nb not allowed without DOMAIN")
3135 if len(disable_nb) > 0:
3136 raise CommandError("--disable-nb not allowed without DOMAIN")
3138 if len(enable_sid_str) > 0:
3139 raise CommandError("--enable-sid not allowed without DOMAIN")
3140 if len(disable_sid_str) > 0:
3141 raise CommandError("--disable-sid not allowed without DOMAIN")
3143 if len(add_upn) > 0:
3145 if not n.startswith("*."):
3147 raise CommandError("value[%s] specified for --add-upn-suffix should not include with '*.'" % n)
3148 require_update = True
3149 if len(delete_upn) > 0:
3150 for n in delete_upn:
3151 if not n.startswith("*."):
3153 raise CommandError("value[%s] specified for --delete-upn-suffix should not include with '*.'" % n)
3154 require_update = True
3156 for d in delete_upn:
3157 if a.lower() != d.lower():
3159 raise CommandError("value[%s] specified for --add-upn-suffix and --delete-upn-suffix" % a)
3161 if len(add_spn) > 0:
3163 if not n.startswith("*."):
3165 raise CommandError("value[%s] specified for --add-spn-suffix should not include with '*.'" % n)
3166 require_update = True
3167 if len(delete_spn) > 0:
3168 for n in delete_spn:
3169 if not n.startswith("*."):
3171 raise CommandError("value[%s] specified for --delete-spn-suffix should not include with '*.'" % n)
3172 require_update = True
3174 for d in delete_spn:
3175 if a.lower() != d.lower():
3177 raise CommandError("value[%s] specified for --add-spn-suffix and --delete-spn-suffix" % a)
3179 if len(add_upn) > 0:
3180 raise CommandError("--add-upn-suffix not allowed together with DOMAIN")
3181 if len(delete_upn) > 0:
3182 raise CommandError("--delete-upn-suffix not allowed together with DOMAIN")
3183 if len(add_spn) > 0:
3184 raise CommandError("--add-spn-suffix not allowed together with DOMAIN")
3185 if len(delete_spn) > 0:
3186 raise CommandError("--delete-spn-suffix not allowed together with DOMAIN")
3188 if refresh is not None:
3189 if refresh == "store":
3190 require_update = True
3192 if enable_all and refresh != "store":
3193 raise CommandError("--enable-all not allowed together with --refresh=%s" % refresh)
3195 if len(enable_tln) > 0:
3196 raise CommandError("--enable-tln not allowed together with --refresh")
3197 if len(disable_tln) > 0:
3198 raise CommandError("--disable-tln not allowed together with --refresh")
3200 if len(add_tln_ex) > 0:
3201 raise CommandError("--add-tln-ex not allowed together with --refresh")
3202 if len(delete_tln_ex) > 0:
3203 raise CommandError("--delete-tln-ex not allowed together with --refresh")
3205 if len(enable_nb) > 0:
3206 raise CommandError("--enable-nb not allowed together with --refresh")
3207 if len(disable_nb) > 0:
3208 raise CommandError("--disable-nb not allowed together with --refresh")
3210 if len(enable_sid_str) > 0:
3211 raise CommandError("--enable-sid not allowed together with --refresh")
3212 if len(disable_sid_str) > 0:
3213 raise CommandError("--disable-sid not allowed together with --refresh")
3216 require_update = True
3218 if len(enable_tln) > 0:
3219 raise CommandError("--enable-tln not allowed together with --enable-all")
3221 if len(enable_nb) > 0:
3222 raise CommandError("--enable-nb not allowed together with --enable-all")
3224 if len(enable_sid_str) > 0:
3225 raise CommandError("--enable-sid not allowed together with --enable-all")
3227 if len(enable_tln) > 0:
3228 require_update = True
3229 if len(disable_tln) > 0:
3230 require_update = True
3231 for e in enable_tln:
3232 for d in disable_tln:
3233 if e.lower() != d.lower():
3235 raise CommandError("value[%s] specified for --enable-tln and --disable-tln" % e)
3237 if len(add_tln_ex) > 0:
3238 for n in add_tln_ex:
3239 if not n.startswith("*."):
3241 raise CommandError("value[%s] specified for --add-tln-ex should not include with '*.'" % n)
3242 require_update = True
3243 if len(delete_tln_ex) > 0:
3244 for n in delete_tln_ex:
3245 if not n.startswith("*."):
3247 raise CommandError("value[%s] specified for --delete-tln-ex should not include with '*.'" % n)
3248 require_update = True
3249 for a in add_tln_ex:
3250 for d in delete_tln_ex:
3251 if a.lower() != d.lower():
3253 raise CommandError("value[%s] specified for --add-tln-ex and --delete-tln-ex" % a)
3255 if len(enable_nb) > 0:
3256 require_update = True
3257 if len(disable_nb) > 0:
3258 require_update = True
3260 for d in disable_nb:
3261 if e.upper() != d.upper():
3263 raise CommandError("value[%s] specified for --enable-nb and --disable-nb" % e)
3266 for s in enable_sid_str:
3268 sid = security.dom_sid(s)
3269 except TypeError as error:
3270 raise CommandError("value[%s] specified for --enable-sid is not a valid SID" % s)
3271 enable_sid.append(sid)
3273 for s in disable_sid_str:
3275 sid = security.dom_sid(s)
3276 except TypeError as error:
3277 raise CommandError("value[%s] specified for --disable-sid is not a valid SID" % s)
3278 disable_sid.append(sid)
3279 if len(enable_sid) > 0:
3280 require_update = True
3281 if len(disable_sid) > 0:
3282 require_update = True
3283 for e in enable_sid:
3284 for d in disable_sid:
3287 raise CommandError("value[%s] specified for --enable-sid and --disable-sid" % e)
3289 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
3291 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
3293 local_server = self.setup_local_server(sambaopts, localdcopts)
3295 local_lsa = self.new_local_lsa_connection()
3296 except RuntimeError as error:
3297 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
3300 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
3301 except RuntimeError as error:
3302 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
3304 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
3305 local_lsa_info.name.string,
3306 local_lsa_info.dns_domain.string,
3307 local_lsa_info.sid))
3311 local_netlogon = self.new_local_netlogon_connection()
3312 except RuntimeError as error:
3313 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3316 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3317 except RuntimeError as error:
3318 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3320 if local_netlogon_info.domain_name != local_netlogon_info.forest_name:
3321 raise CommandError("The local domain [%s] is not the forest root [%s]" % (
3322 local_netlogon_info.domain_name,
3323 local_netlogon_info.forest_name))
3326 # get all information about our own forest
3327 own_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3329 except RuntimeError as error:
3330 if self.check_runtime_error(error, werror.WERR_RPC_S_PROCNUM_OUT_OF_RANGE):
3331 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3334 if self.check_runtime_error(error, werror.WERR_INVALID_FUNCTION):
3335 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3338 if self.check_runtime_error(error, werror.WERR_NERR_ACFNOTLOADED):
3339 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3342 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3344 self.outf.write("Own forest trust information...\n")
3345 self.write_forest_trust_info(own_forest_info,
3346 tln=local_lsa_info.dns_domain.string)
3349 local_samdb = self.new_local_ldap_connection()
3350 except RuntimeError as error:
3351 raise self.LocalRuntimeError(self, error, "failed to connect to SamDB")
3353 local_partitions_dn = "CN=Partitions,%s" % str(local_samdb.get_config_basedn())
3354 attrs = ['uPNSuffixes', 'msDS-SPNSuffixes']
3356 msgs = local_samdb.search(base=local_partitions_dn,
3357 scope=ldb.SCOPE_BASE,
3358 expression="(objectClass=crossRefContainer)",
3360 stored_msg = msgs[0]
3361 except ldb.LdbError as error:
3362 raise self.LocalLdbError(self, error, "failed to search partition dn")
3364 stored_upn_vals = []
3365 if 'uPNSuffixes' in stored_msg:
3366 stored_upn_vals.extend(stored_msg['uPNSuffixes'])
3368 stored_spn_vals = []
3369 if 'msDS-SPNSuffixes' in stored_msg:
3370 stored_spn_vals.extend(stored_msg['msDS-SPNSuffixes'])
3372 self.outf.write("Stored uPNSuffixes attributes[%d]:\n" % len(stored_upn_vals))
3373 for v in stored_upn_vals:
3374 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3375 self.outf.write("Stored msDS-SPNSuffixes attributes[%d]:\n" % len(stored_spn_vals))
3376 for v in stored_spn_vals:
3377 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3379 if not require_update:
3383 update_upn_vals = []
3384 update_upn_vals.extend(stored_upn_vals)
3387 update_spn_vals = []
3388 update_spn_vals.extend(stored_spn_vals)
3392 for i in xrange(0, len(update_upn_vals)):
3393 v = update_upn_vals[i]
3394 if v.lower() != upn.lower():
3399 raise CommandError("Entry already present for value[%s] specified for --add-upn-suffix" % upn)
3400 update_upn_vals.append(upn)
3403 for upn in delete_upn:
3405 for i in xrange(0, len(update_upn_vals)):
3406 v = update_upn_vals[i]
3407 if v.lower() != upn.lower():
3412 raise CommandError("Entry not found for value[%s] specified for --delete-upn-suffix" % upn)
3414 update_upn_vals.pop(idx)
3419 for i in xrange(0, len(update_spn_vals)):
3420 v = update_spn_vals[i]
3421 if v.lower() != spn.lower():
3426 raise CommandError("Entry already present for value[%s] specified for --add-spn-suffix" % spn)
3427 update_spn_vals.append(spn)
3430 for spn in delete_spn:
3432 for i in xrange(0, len(update_spn_vals)):
3433 v = update_spn_vals[i]
3434 if v.lower() != spn.lower():
3439 raise CommandError("Entry not found for value[%s] specified for --delete-spn-suffix" % spn)
3441 update_spn_vals.pop(idx)
3444 self.outf.write("Update uPNSuffixes attributes[%d]:\n" % len(update_upn_vals))
3445 for v in update_upn_vals:
3446 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3447 self.outf.write("Update msDS-SPNSuffixes attributes[%d]:\n" % len(update_spn_vals))
3448 for v in update_spn_vals:
3449 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3451 update_msg = ldb.Message()
3452 update_msg.dn = stored_msg.dn
3455 update_msg['uPNSuffixes'] = ldb.MessageElement(update_upn_vals,
3456 ldb.FLAG_MOD_REPLACE,
3459 update_msg['msDS-SPNSuffixes'] = ldb.MessageElement(update_spn_vals,
3460 ldb.FLAG_MOD_REPLACE,
3463 local_samdb.modify(update_msg)
3464 except ldb.LdbError as error:
3465 raise self.LocalLdbError(self, error, "failed to update partition dn")
3468 stored_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3470 except RuntimeError as error:
3471 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3473 self.outf.write("Stored forest trust information...\n")
3474 self.write_forest_trust_info(stored_forest_info,
3475 tln=local_lsa_info.dns_domain.string)
3479 lsaString = lsa.String()
3480 lsaString.string = domain
3481 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
3482 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
3483 except NTSTATUSError as error:
3484 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
3485 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
3487 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
3489 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
3490 local_tdo_info.netbios_name.string,
3491 local_tdo_info.domain_name.string,
3492 local_tdo_info.sid))
3494 if not local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
3495 raise CommandError("trusted domain object for domain [%s] is not marked as FOREST_TRANSITIVE." % domain)
3497 if refresh is not None:
3499 local_netlogon = self.new_local_netlogon_connection()
3500 except RuntimeError as error:
3501 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3504 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3505 except RuntimeError as error:
3506 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3508 lsa_update_check = 1
3509 if refresh == "store":
3510 netlogon_update_tdo = netlogon.DS_GFTI_UPDATE_TDO
3512 lsa_update_check = 0
3514 netlogon_update_tdo = 0
3517 # get all information about the remote trust
3518 # this triggers netr_GetForestTrustInformation to the remote domain
3519 # and lsaRSetForestTrustInformation() locally, but new top level
3520 # names are disabled by default.
3521 fresh_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3522 local_tdo_info.domain_name.string,
3523 netlogon_update_tdo)
3524 except RuntimeError as error:
3525 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3528 fresh_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3529 local_tdo_info.domain_name,
3530 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3533 except RuntimeError as error:
3534 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3536 self.outf.write("Fresh forest trust information...\n")
3537 self.write_forest_trust_info(fresh_forest_info,
3538 tln=local_tdo_info.domain_name.string,
3539 collisions=fresh_forest_collision)
3541 if refresh == "store":
3543 lsaString = lsa.String()
3544 lsaString.string = local_tdo_info.domain_name.string
3545 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3547 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3548 except RuntimeError as error:
3549 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3551 self.outf.write("Stored forest trust information...\n")
3552 self.write_forest_trust_info(stored_forest_info,
3553 tln=local_tdo_info.domain_name.string)
3558 # The none --refresh path
3562 lsaString = lsa.String()
3563 lsaString.string = local_tdo_info.domain_name.string
3564 local_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3566 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3567 except RuntimeError as error:
3568 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3570 self.outf.write("Local forest trust information...\n")
3571 self.write_forest_trust_info(local_forest_info,
3572 tln=local_tdo_info.domain_name.string)
3574 if not require_update:
3578 entries.extend(local_forest_info.entries)
3579 update_forest_info = lsa.ForestTrustInformation()
3580 update_forest_info.count = len(entries)
3581 update_forest_info.entries = entries
3584 for i in xrange(0, len(update_forest_info.entries)):
3585 r = update_forest_info.entries[i]
3586 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3588 if update_forest_info.entries[i].flags == 0:
3590 update_forest_info.entries[i].time = 0
3591 update_forest_info.entries[i].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3592 for i in xrange(0, len(update_forest_info.entries)):
3593 r = update_forest_info.entries[i]
3594 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3596 if update_forest_info.entries[i].flags == 0:
3598 update_forest_info.entries[i].time = 0
3599 update_forest_info.entries[i].flags &= ~lsa.LSA_NB_DISABLED_MASK
3600 update_forest_info.entries[i].flags &= ~lsa.LSA_SID_DISABLED_MASK
3602 for tln in enable_tln:
3604 for i in xrange(0, len(update_forest_info.entries)):
3605 r = update_forest_info.entries[i]
3606 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3608 if r.forest_trust_data.string.lower() != tln.lower():
3613 raise CommandError("Entry not found for value[%s] specified for --enable-tln" % tln)
3614 if not update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_MASK:
3615 raise CommandError("Entry found for value[%s] specified for --enable-tln is already enabled" % tln)
3616 update_forest_info.entries[idx].time = 0
3617 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3619 for tln in disable_tln:
3621 for i in xrange(0, len(update_forest_info.entries)):
3622 r = update_forest_info.entries[i]
3623 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3625 if r.forest_trust_data.string.lower() != tln.lower():
3630 raise CommandError("Entry not found for value[%s] specified for --disable-tln" % tln)
3631 if update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_ADMIN:
3632 raise CommandError("Entry found for value[%s] specified for --disable-tln is already disabled" % tln)
3633 update_forest_info.entries[idx].time = 0
3634 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3635 update_forest_info.entries[idx].flags |= lsa.LSA_TLN_DISABLED_ADMIN
3637 for tln_ex in add_tln_ex:
3639 for i in xrange(0, len(update_forest_info.entries)):
3640 r = update_forest_info.entries[i]
3641 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3643 if r.forest_trust_data.string.lower() != tln_ex.lower():
3648 raise CommandError("Entry already present for value[%s] specified for --add-tln-ex" % tln_ex)
3650 tln_dot = ".%s" % tln_ex.lower()
3652 for i in xrange(0, len(update_forest_info.entries)):
3653 r = update_forest_info.entries[i]
3654 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3656 r_dot = ".%s" % r.forest_trust_data.string.lower()
3657 if tln_dot == r_dot:
3658 raise CommandError("TLN entry present for value[%s] specified for --add-tln-ex" % tln_ex)
3659 if not tln_dot.endswith(r_dot):
3665 raise CommandError("No TLN parent present for value[%s] specified for --add-tln-ex" % tln_ex)
3667 r = lsa.ForestTrustRecord()
3668 r.type = lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX
3671 r.forest_trust_data.string = tln_ex
3674 entries.extend(update_forest_info.entries)
3675 entries.insert(idx + 1, r)
3676 update_forest_info.count = len(entries)
3677 update_forest_info.entries = entries
3679 for tln_ex in delete_tln_ex:
3681 for i in xrange(0, len(update_forest_info.entries)):
3682 r = update_forest_info.entries[i]
3683 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3685 if r.forest_trust_data.string.lower() != tln_ex.lower():
3690 raise CommandError("Entry not found for value[%s] specified for --delete-tln-ex" % tln_ex)
3693 entries.extend(update_forest_info.entries)
3695 update_forest_info.count = len(entries)
3696 update_forest_info.entries = entries
3698 for nb in enable_nb:
3700 for i in xrange(0, len(update_forest_info.entries)):
3701 r = update_forest_info.entries[i]
3702 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3704 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3709 raise CommandError("Entry not found for value[%s] specified for --enable-nb" % nb)
3710 if not update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_MASK:
3711 raise CommandError("Entry found for value[%s] specified for --enable-nb is already enabled" % nb)
3712 update_forest_info.entries[idx].time = 0
3713 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3715 for nb in disable_nb:
3717 for i in xrange(0, len(update_forest_info.entries)):
3718 r = update_forest_info.entries[i]
3719 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3721 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3726 raise CommandError("Entry not found for value[%s] specified for --delete-nb" % nb)
3727 if update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_ADMIN:
3728 raise CommandError("Entry found for value[%s] specified for --disable-nb is already disabled" % nb)
3729 update_forest_info.entries[idx].time = 0
3730 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3731 update_forest_info.entries[idx].flags |= lsa.LSA_NB_DISABLED_ADMIN
3733 for sid in enable_sid:
3735 for i in xrange(0, len(update_forest_info.entries)):
3736 r = update_forest_info.entries[i]
3737 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3739 if r.forest_trust_data.domain_sid != sid:
3744 raise CommandError("Entry not found for value[%s] specified for --enable-sid" % sid)
3745 if not update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_MASK:
3746 raise CommandError("Entry found for value[%s] specified for --enable-sid is already enabled" % nb)
3747 update_forest_info.entries[idx].time = 0
3748 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3750 for sid in disable_sid:
3752 for i in xrange(0, len(update_forest_info.entries)):
3753 r = update_forest_info.entries[i]
3754 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3756 if r.forest_trust_data.domain_sid != sid:
3761 raise CommandError("Entry not found for value[%s] specified for --delete-sid" % sid)
3762 if update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_ADMIN:
3763 raise CommandError("Entry found for value[%s] specified for --disable-sid is already disabled" % nb)
3764 update_forest_info.entries[idx].time = 0
3765 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3766 update_forest_info.entries[idx].flags |= lsa.LSA_SID_DISABLED_ADMIN
3769 update_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3770 local_tdo_info.domain_name,
3771 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3772 update_forest_info, 0)
3773 except RuntimeError as error:
3774 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3776 self.outf.write("Updated forest trust information...\n")
3777 self.write_forest_trust_info(update_forest_info,
3778 tln=local_tdo_info.domain_name.string,
3779 collisions=update_forest_collision)
3782 lsaString = lsa.String()
3783 lsaString.string = local_tdo_info.domain_name.string
3784 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3786 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3787 except RuntimeError as error:
3788 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3790 self.outf.write("Stored forest trust information...\n")
3791 self.write_forest_trust_info(stored_forest_info,
3792 tln=local_tdo_info.domain_name.string)
3795 class cmd_domain_tombstones_expunge(Command):
3796 """Expunge tombstones from the database.
3798 This command expunges tombstones from the database."""
3799 synopsis = "%prog NC [NC [...]] [options]"
3802 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
3803 metavar="URL", dest="H"),
3804 Option("--current-time",
3805 help="The current time to evaluate the tombstone lifetime from, expressed as YYYY-MM-DD",
3807 Option("--tombstone-lifetime", help="Number of days a tombstone should be preserved for", type=int),
3810 takes_args = ["nc*"]
3812 takes_optiongroups = {
3813 "sambaopts": options.SambaOptions,
3814 "credopts": options.CredentialsOptions,
3815 "versionopts": options.VersionOptions,
3818 def run(self, *ncs, **kwargs):
3819 sambaopts = kwargs.get("sambaopts")
3820 credopts = kwargs.get("credopts")
3821 versionpts = kwargs.get("versionopts")
3823 current_time_string = kwargs.get("current_time")
3824 tombstone_lifetime = kwargs.get("tombstone_lifetime")
3825 lp = sambaopts.get_loadparm()
3826 creds = credopts.get_credentials(lp)
3827 samdb = SamDB(url=H, session_info=system_session(),
3828 credentials=creds, lp=lp)
3830 if current_time_string is not None:
3831 current_time_obj = time.strptime(current_time_string, "%Y-%m-%d")
3832 current_time = long(time.mktime(current_time_obj))
3835 current_time = long(time.time())
3838 res = samdb.search(expression="", base="", scope=ldb.SCOPE_BASE,
3839 attrs=["namingContexts"])
3842 for nc in res[0]["namingContexts"]:
3847 started_transaction = False
3849 samdb.transaction_start()
3850 started_transaction = True
3852 removed_links) = samdb.garbage_collect_tombstones(ncs,
3853 current_time=current_time,
3854 tombstone_lifetime=tombstone_lifetime)
3856 except Exception, err:
3857 if started_transaction:
3858 samdb.transaction_cancel()
3859 raise CommandError("Failed to expunge / garbage collect tombstones", err)
3861 samdb.transaction_commit()
3863 self.outf.write("Removed %d objects and %d links successfully\n"
3864 % (removed_objects, removed_links))
3868 class cmd_domain_trust(SuperCommand):
3869 """Domain and forest trust management."""
3872 subcommands["list"] = cmd_domain_trust_list()
3873 subcommands["show"] = cmd_domain_trust_show()
3874 subcommands["create"] = cmd_domain_trust_create()
3875 subcommands["delete"] = cmd_domain_trust_delete()
3876 subcommands["validate"] = cmd_domain_trust_validate()
3877 subcommands["namespaces"] = cmd_domain_trust_namespaces()
3879 class cmd_domain_tombstones(SuperCommand):
3880 """Domain tombstone and recycled object management."""
3883 subcommands["expunge"] = cmd_domain_tombstones_expunge()
3885 class ldif_schema_update:
3886 """Helper class for applying LDIF schema updates"""
3889 self.is_defunct = False
3890 self.unknown_oid = None
3894 def _ldap_schemaUpdateNow(self, samdb):
3898 add: schemaUpdateNow
3901 samdb.modify_ldif(ldif)
3903 def can_ignore_failure(self, error):
3904 """Checks if we can safely ignore failure to apply an LDIF update"""
3905 (num, errstr) = error.args
3907 # Microsoft has marked objects as defunct that Samba doesn't know about
3908 if num == ldb.ERR_NO_SUCH_OBJECT and self.is_defunct:
3909 print("Defunct object %s doesn't exist, skipping" % self.dn)
3911 elif self.unknown_oid is not None:
3912 print("Skipping unknown OID %s for object %s" %(self.unknown_oid, self.dn))
3917 def apply(self, samdb):
3918 """Applies a single LDIF update to the schema"""
3922 samdb.modify_ldif(self.ldif, controls=['relax:0'])
3923 except ldb.LdbError as e:
3924 if e.args[0] == ldb.ERR_INVALID_ATTRIBUTE_SYNTAX:
3926 # REFRESH after a failed change
3928 # Otherwise the OID-to-attribute mapping in
3929 # _apply_updates_in_file() won't work, because it
3930 # can't lookup the new OID in the schema
3931 self._ldap_schemaUpdateNow(samdb)
3933 samdb.modify_ldif(self.ldif, controls=['relax:0'])
3936 except ldb.LdbError as e:
3937 if self.can_ignore_failure(e):
3940 print("Exception: %s" % e)
3941 print("Encountered while trying to apply the following LDIF")
3942 print("----------------------------------------------------")
3943 print("%s" % self.ldif)
3949 class cmd_domain_schema_upgrade(Command):
3950 """Domain schema upgrading"""
3952 synopsis = "%prog [options]"
3954 takes_optiongroups = {
3955 "sambaopts": options.SambaOptions,
3956 "versionopts": options.VersionOptions,
3957 "credopts": options.CredentialsOptions,
3961 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
3962 metavar="URL", dest="H"),
3963 Option("--quiet", help="Be quiet", action="store_true"),
3964 Option("--verbose", help="Be verbose", action="store_true"),
3965 Option("--schema", type="choice", metavar="SCHEMA",
3966 choices=["2012", "2012_R2"],
3967 help="The schema file to upgrade to. Default is (Windows) 2012_R2.",
3969 Option("--ldf-file", type=str, default=None,
3970 help="Just apply the schema updates in the adprep/.LDF file(s) specified"),
3971 Option("--base-dir", type=str, default=None,
3972 help="Location of ldf files Default is ${SETUPDIR}/adprep.")
3975 def _apply_updates_in_file(self, samdb, ldif_file):
3977 Applies a series of updates specified in an .LDIF file. The .LDIF file
3978 is based on the adprep Schema updates provided by Microsoft.
3981 ldif_op = ldif_schema_update()
3983 # parse the file line by line and work out each update operation to apply
3984 for line in ldif_file:
3986 line = line.rstrip()
3988 # the operations in the .LDIF file are separated by blank lines. If
3989 # we hit a blank line, try to apply the update we've parsed so far
3992 # keep going if we haven't parsed anything yet
3993 if ldif_op.ldif == '':
3996 # Apply the individual change
3997 count += ldif_op.apply(samdb)
3999 # start storing the next operation from scratch again
4000 ldif_op = ldif_schema_update()
4003 # replace the placeholder domain name in the .ldif file with the real domain
4004 if line.upper().endswith('DC=X'):
4005 line = line[:-len('DC=X')] + str(samdb.get_default_basedn())
4006 elif line.upper().endswith('CN=X'):
4007 line = line[:-len('CN=X')] + str(samdb.get_default_basedn())
4009 values = line.split(':')
4011 if values[0].lower() == 'dn':
4012 ldif_op.dn = values[1].strip()
4014 # replace the Windows-specific operation with the Samba one
4015 if values[0].lower() == 'changetype':
4016 line = line.lower().replace(': ntdsschemaadd',
4018 line = line.lower().replace(': ntdsschemamodify',
4021 if values[0].lower() in ['rdnattid', 'subclassof',
4022 'systemposssuperiors',
4024 'systemauxiliaryclass']:
4027 # The Microsoft updates contain some OIDs we don't recognize.
4028 # Query the DB to see if we can work out the OID this update is
4029 # referring to. If we find a match, then replace the OID with
4030 # the ldapDisplayname
4032 res = samdb.search(base=samdb.get_schema_basedn(),
4033 expression="(|(attributeId=%s)(governsId=%s))" %
4035 attrs=['ldapDisplayName'])
4038 ldif_op.unknown_oid = value
4040 display_name = res[0]['ldapDisplayName'][0]
4041 line = line.replace(value, ' ' + display_name)
4043 # Microsoft has marked objects as defunct that Samba doesn't know about
4044 if values[0].lower() == 'isdefunct' and values[1].strip().lower() == 'true':
4045 ldif_op.is_defunct = True
4047 # Samba has added the showInAdvancedViewOnly attribute to all objects,
4048 # so rather than doing an add, we need to do a replace
4049 if values[0].lower() == 'add' and values[1].strip().lower() == 'showinadvancedviewonly':
4050 line = 'replace: showInAdvancedViewOnly'
4052 # Add the line to the current LDIF operation (including the newline
4053 # we stripped off at the start of the loop)
4054 ldif_op.ldif += line + '\n'
4059 def _apply_update(self, samdb, update_file, base_dir):
4060 """Wrapper function for parsing an LDIF file and applying the updates"""
4062 print("Applying %s updates..." % update_file)
4066 ldif_file = open(os.path.join(base_dir, update_file))
4068 count = self._apply_updates_in_file(samdb, ldif_file)
4074 print("%u changes applied" % count)
4078 def run(self, **kwargs):
4079 from samba.ms_schema_markdown import read_ms_markdown
4080 from samba.schema import Schema
4082 updates_allowed_overriden = False
4083 sambaopts = kwargs.get("sambaopts")
4084 credopts = kwargs.get("credopts")
4085 versionpts = kwargs.get("versionopts")
4086 lp = sambaopts.get_loadparm()
4087 creds = credopts.get_credentials(lp)
4089 target_schema = kwargs.get("schema")
4090 ldf_files = kwargs.get("ldf_file")
4091 base_dir = kwargs.get("base_dir")
4095 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
4097 # we're not going to get far if the config doesn't allow schema updates
4098 if lp.get("dsdb:schema update allowed") is None:
4099 lp.set("dsdb:schema update allowed", "yes")
4100 print("Temporarily overriding 'dsdb:schema update allowed' setting")
4101 updates_allowed_overriden = True
4103 own_dn = ldb.Dn(samdb, samdb.get_dsServiceName())
4104 master = get_fsmo_roleowner(samdb, str(samdb.get_schema_basedn()),
4106 if own_dn != master:
4107 raise CommandError("This server is not the schema master.")
4109 # if specific LDIF files were specified, just apply them
4111 schema_updates = ldf_files.split(",")
4115 # work out the version of the target schema we're upgrading to
4116 end = Schema.get_version(target_schema)
4118 # work out the version of the schema we're currently using
4119 res = samdb.search(base=samdb.get_schema_basedn(),
4120 scope=ldb.SCOPE_BASE, attrs=['objectVersion'])
4123 raise CommandError('Could not determine current schema version')
4124 start = int(res[0]['objectVersion'][0]) + 1
4126 diff_dir = setup_path("adprep/WindowsServerDocs")
4127 if base_dir is None:
4128 # Read from the Schema-Updates.md file
4129 temp_folder = tempfile.mkdtemp()
4131 update_file = setup_path("adprep/WindowsServerDocs/Schema-Updates.md")
4134 read_ms_markdown(update_file, temp_folder)
4135 except Exception as e:
4136 print("Exception in markdown parsing: %s" % e)
4137 shutil.rmtree(temp_folder)
4138 raise CommandError('Failed to upgrade schema')
4140 base_dir = temp_folder
4142 for version in range(start, end + 1):
4143 update = 'Sch%d.ldf' % version
4144 schema_updates.append(update)
4146 # Apply patches if we parsed the Schema-Updates.md file
4147 diff = os.path.abspath(os.path.join(diff_dir, update + '.diff'))
4148 if temp_folder and os.path.exists(diff):
4149 p = subprocess.Popen(['patch', update, '-i', diff],
4150 stdout=subprocess.PIPE,
4151 stderr=subprocess.PIPE, cwd=temp_folder)
4152 stdout, stderr = p.communicate()
4155 print("Exception in patch: %s\n%s" % (stdout, stderr))
4156 shutil.rmtree(temp_folder)
4157 raise CommandError('Failed to upgrade schema')
4159 print("Patched %s using %s" % (update, diff))
4161 if base_dir is None:
4162 base_dir = setup_path("adprep")
4164 samdb.transaction_start()
4166 error_encountered = False
4169 # Apply the schema updates needed to move to the new schema version
4170 for ldif_file in schema_updates:
4171 count += self._apply_update(samdb, ldif_file, base_dir)
4174 samdb.transaction_commit()
4175 print("Schema successfully updated")
4177 print("No changes applied to schema")
4178 samdb.transaction_cancel()
4179 except Exception as e:
4180 print("Exception: %s" % e)
4181 print("Error encountered, aborting schema upgrade")
4182 samdb.transaction_cancel()
4183 error_encountered = True
4185 if updates_allowed_overriden:
4186 lp.set("dsdb:schema update allowed", "no")
4189 shutil.rmtree(temp_folder)
4191 if error_encountered:
4192 raise CommandError('Failed to upgrade schema')
4194 class cmd_domain_functional_prep(Command):
4195 """Domain functional level preparation"""
4197 synopsis = "%prog [options]"
4199 takes_optiongroups = {
4200 "sambaopts": options.SambaOptions,
4201 "versionopts": options.VersionOptions,
4202 "credopts": options.CredentialsOptions,
4206 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
4207 metavar="URL", dest="H"),
4208 Option("--quiet", help="Be quiet", action="store_true"),
4209 Option("--verbose", help="Be verbose", action="store_true"),
4210 Option("--function-level", type="choice", metavar="FUNCTION_LEVEL",
4211 choices=["2008_R2", "2012", "2012_R2"],
4212 help="The schema file to upgrade to. Default is (Windows) 2012_R2.",
4214 Option("--forest-prep", action="store_true",
4215 help="Run the forest prep (by default, both the domain and forest prep are run)."),
4216 Option("--domain-prep", action="store_true",
4217 help="Run the domain prep (by default, both the domain and forest prep are run).")
4220 def run(self, **kwargs):
4221 updates_allowed_overriden = False
4222 sambaopts = kwargs.get("sambaopts")
4223 credopts = kwargs.get("credopts")
4224 versionpts = kwargs.get("versionopts")
4225 lp = sambaopts.get_loadparm()
4226 creds = credopts.get_credentials(lp)
4228 target_level = string_version_to_constant[kwargs.get("function_level")]
4229 forest_prep = kwargs.get("forest_prep")
4230 domain_prep = kwargs.get("domain_prep")
4232 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
4234 # we're not going to get far if the config doesn't allow schema updates
4235 if lp.get("dsdb:schema update allowed") is None:
4236 lp.set("dsdb:schema update allowed", "yes")
4237 print("Temporarily overriding 'dsdb:schema update allowed' setting")
4238 updates_allowed_overriden = True
4240 if forest_prep is None and domain_prep is None:
4244 own_dn = ldb.Dn(samdb, samdb.get_dsServiceName())
4246 master = get_fsmo_roleowner(samdb, str(samdb.get_schema_basedn()),
4248 if own_dn != master:
4249 raise CommandError("This server is not the schema master.")
4252 domain_dn = samdb.domain_dn()
4253 infrastructure_dn = "CN=Infrastructure," + domain_dn
4254 master = get_fsmo_roleowner(samdb, infrastructure_dn,
4256 if own_dn != master:
4257 raise CommandError("This server is not the infrastructure master.")
4260 samdb.transaction_start()
4261 error_encountered = False
4263 from samba.forest_update import ForestUpdate
4264 forest = ForestUpdate(samdb, fix=True)
4266 forest.check_updates_iterator([53, 79, 80, 81, 82, 83])
4267 forest.check_updates_functional_level(target_level,
4268 DS_DOMAIN_FUNCTION_2008_R2,
4269 update_revision=True)
4271 samdb.transaction_commit()
4272 except Exception as e:
4273 print("Exception: %s" % e)
4274 samdb.transaction_cancel()
4275 error_encountered = True
4278 samdb.transaction_start()
4279 error_encountered = False
4281 from samba.domain_update import DomainUpdate
4283 domain = DomainUpdate(samdb, fix=True)
4284 domain.check_updates_functional_level(target_level,
4285 DS_DOMAIN_FUNCTION_2008,
4286 update_revision=True)
4288 samdb.transaction_commit()
4289 except Exception as e:
4290 print("Exception: %s" % e)
4291 samdb.transaction_cancel()
4292 error_encountered = True
4294 if updates_allowed_overriden:
4295 lp.set("dsdb:schema update allowed", "no")
4297 if error_encountered:
4298 raise CommandError('Failed to perform functional prep')
4300 class cmd_domain(SuperCommand):
4301 """Domain management."""
4304 subcommands["demote"] = cmd_domain_demote()
4305 if cmd_domain_export_keytab is not None:
4306 subcommands["exportkeytab"] = cmd_domain_export_keytab()
4307 subcommands["info"] = cmd_domain_info()
4308 subcommands["provision"] = cmd_domain_provision()
4309 subcommands["join"] = cmd_domain_join()
4310 subcommands["dcpromo"] = cmd_domain_dcpromo()
4311 subcommands["level"] = cmd_domain_level()
4312 subcommands["passwordsettings"] = cmd_domain_passwordsettings()
4313 subcommands["classicupgrade"] = cmd_domain_classicupgrade()
4314 subcommands["samba3upgrade"] = cmd_domain_samba3upgrade()
4315 subcommands["trust"] = cmd_domain_trust()
4316 subcommands["tombstones"] = cmd_domain_tombstones()
4317 subcommands["schemaupgrade"] = cmd_domain_schema_upgrade()
4318 subcommands["functionalprep"] = cmd_domain_functional_prep()