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 from __future__ import print_function
26 from __future__ import division
27 import samba.getopt as options
39 from samba import ntstatus
40 from samba import NTSTATUSError
41 from samba import werror
42 from getpass import getpass
43 from samba.net import Net, LIBNET_JOIN_AUTOMATIC
45 from samba.join import join_RODC, join_DC, join_subdomain
46 from samba.auth import system_session
47 from samba.samdb import SamDB, get_default_backend_store
48 from samba.ndr import ndr_unpack, ndr_pack, ndr_print
49 from samba.dcerpc import drsuapi
50 from samba.dcerpc import drsblobs
51 from samba.dcerpc import lsa
52 from samba.dcerpc import netlogon
53 from samba.dcerpc import security
54 from samba.dcerpc import nbt
55 from samba.dcerpc import misc
56 from samba.dcerpc.samr import DOMAIN_PASSWORD_COMPLEX, DOMAIN_PASSWORD_STORE_CLEARTEXT
57 from samba.netcmd import (
63 from samba.netcmd.fsmo import get_fsmo_roleowner
64 from samba.netcmd.common import netcmd_get_domain_infos_via_cldap
65 from samba.samba3 import Samba3
66 from samba.samba3 import param as s3param
67 from samba.upgrade import upgrade_from_samba3
68 from samba.drs_utils import (
69 sendDsReplicaSync, drsuapi_connect, drsException,
71 from samba import remove_dc, arcfour_encrypt, string_to_byte_array
73 from samba.dsdb import (
74 DS_DOMAIN_FUNCTION_2000,
75 DS_DOMAIN_FUNCTION_2003,
76 DS_DOMAIN_FUNCTION_2003_MIXED,
77 DS_DOMAIN_FUNCTION_2008,
78 DS_DOMAIN_FUNCTION_2008_R2,
79 DS_DOMAIN_FUNCTION_2012,
80 DS_DOMAIN_FUNCTION_2012_R2,
81 DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL,
82 DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL,
83 UF_WORKSTATION_TRUST_ACCOUNT,
84 UF_SERVER_TRUST_ACCOUNT,
85 UF_TRUSTED_FOR_DELEGATION,
86 UF_PARTIAL_SECRETS_ACCOUNT
89 from samba.provision import (
92 DEFAULT_MIN_PWD_LENGTH,
96 from samba.provision.common import (
102 string_version_to_constant = {
103 "2008_R2" : DS_DOMAIN_FUNCTION_2008_R2,
104 "2012": DS_DOMAIN_FUNCTION_2012,
105 "2012_R2": DS_DOMAIN_FUNCTION_2012_R2,
108 common_provision_join_options = [
109 Option("--machinepass", type="string", metavar="PASSWORD",
110 help="choose machine password (otherwise random)"),
111 Option("--plaintext-secrets", action="store_true",
112 help="Store secret/sensitive values as plain text on disk" +
113 "(default is to encrypt secret/ensitive values)"),
114 Option("--backend-store", type="choice", metavar="BACKENDSTORE",
115 choices=["tdb", "mdb"],
116 help="Specify the database backend to be used "
117 "(default is %s)" % get_default_backend_store()),
118 Option("--targetdir", metavar="DIR",
119 help="Set target directory (where to store provision)", type=str),
120 Option("--quiet", help="Be quiet", action="store_true"),
123 common_join_options = [
124 Option("--server", help="DC to join", type=str),
125 Option("--site", help="site to join", type=str),
126 Option("--domain-critical-only",
127 help="only replicate critical domain objects",
128 action="store_true"),
129 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
130 choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
131 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
132 "BIND9_DLZ uses samba4 AD to store zone information, "
133 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
134 default="SAMBA_INTERNAL"),
135 Option("--verbose", help="Be verbose", action="store_true")
138 common_ntvfs_options = [
139 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
143 def get_testparm_var(testparm, smbconf, varname):
144 errfile = open(os.devnull, 'w')
145 p = subprocess.Popen([testparm, '-s', '-l',
146 '--parameter-name=%s' % varname, smbconf],
147 stdout=subprocess.PIPE, stderr=errfile)
148 (out,err) = p.communicate()
150 lines = out.split('\n')
152 return lines[0].strip()
156 import samba.dckeytab
158 cmd_domain_export_keytab = None
160 class cmd_domain_export_keytab(Command):
161 """Dump Kerberos keys of the domain into a keytab."""
163 synopsis = "%prog <keytab> [options]"
165 takes_optiongroups = {
166 "sambaopts": options.SambaOptions,
167 "credopts": options.CredentialsOptions,
168 "versionopts": options.VersionOptions,
172 Option("--principal", help="extract only this principal", type=str),
175 takes_args = ["keytab"]
177 def run(self, keytab, credopts=None, sambaopts=None, versionopts=None, principal=None):
178 lp = sambaopts.get_loadparm()
180 net.export_keytab(keytab=keytab, principal=principal)
183 class cmd_domain_info(Command):
184 """Print basic info about a domain and the DC passed as parameter."""
186 synopsis = "%prog <ip_address> [options]"
191 takes_optiongroups = {
192 "sambaopts": options.SambaOptions,
193 "credopts": options.CredentialsOptions,
194 "versionopts": options.VersionOptions,
197 takes_args = ["address"]
199 def run(self, address, credopts=None, sambaopts=None, versionopts=None):
200 lp = sambaopts.get_loadparm()
202 res = netcmd_get_domain_infos_via_cldap(lp, None, address)
204 raise CommandError("Invalid IP address '" + address + "'!")
205 self.outf.write("Forest : %s\n" % res.forest)
206 self.outf.write("Domain : %s\n" % res.dns_domain)
207 self.outf.write("Netbios domain : %s\n" % res.domain_name)
208 self.outf.write("DC name : %s\n" % res.pdc_dns_name)
209 self.outf.write("DC netbios name : %s\n" % res.pdc_name)
210 self.outf.write("Server site : %s\n" % res.server_site)
211 self.outf.write("Client site : %s\n" % res.client_site)
214 class cmd_domain_provision(Command):
215 """Provision a domain."""
217 synopsis = "%prog [options]"
219 takes_optiongroups = {
220 "sambaopts": options.SambaOptions,
221 "versionopts": options.VersionOptions,
225 Option("--interactive", help="Ask for names", action="store_true"),
226 Option("--domain", type="string", metavar="DOMAIN",
227 help="NetBIOS domain name to use"),
228 Option("--domain-guid", type="string", metavar="GUID",
229 help="set domainguid (otherwise random)"),
230 Option("--domain-sid", type="string", metavar="SID",
231 help="set domainsid (otherwise random)"),
232 Option("--ntds-guid", type="string", metavar="GUID",
233 help="set NTDS object GUID (otherwise random)"),
234 Option("--invocationid", type="string", metavar="GUID",
235 help="set invocationid (otherwise random)"),
236 Option("--host-name", type="string", metavar="HOSTNAME",
237 help="set hostname"),
238 Option("--host-ip", type="string", metavar="IPADDRESS",
239 help="set IPv4 ipaddress"),
240 Option("--host-ip6", type="string", metavar="IP6ADDRESS",
241 help="set IPv6 ipaddress"),
242 Option("--site", type="string", metavar="SITENAME",
243 help="set site name"),
244 Option("--adminpass", type="string", metavar="PASSWORD",
245 help="choose admin password (otherwise random)"),
246 Option("--krbtgtpass", type="string", metavar="PASSWORD",
247 help="choose krbtgt password (otherwise random)"),
248 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
249 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
250 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
251 "BIND9_FLATFILE uses bind9 text database to store zone information, "
252 "BIND9_DLZ uses samba4 AD to store zone information, "
253 "NONE skips the DNS setup entirely (not recommended)",
254 default="SAMBA_INTERNAL"),
255 Option("--dnspass", type="string", metavar="PASSWORD",
256 help="choose dns password (otherwise random)"),
257 Option("--root", type="string", metavar="USERNAME",
258 help="choose 'root' unix username"),
259 Option("--nobody", type="string", metavar="USERNAME",
260 help="choose 'nobody' user"),
261 Option("--users", type="string", metavar="GROUPNAME",
262 help="choose 'users' group"),
263 Option("--blank", action="store_true",
264 help="do not add users or groups, just the structure"),
265 Option("--server-role", type="choice", metavar="ROLE",
266 choices=["domain controller", "dc", "member server", "member", "standalone"],
267 help="The server role (domain controller | dc | member server | member | standalone). Default is dc.",
268 default="domain controller"),
269 Option("--function-level", type="choice", metavar="FOR-FUN-LEVEL",
270 choices=["2000", "2003", "2008", "2008_R2"],
271 help="The domain and forest function level (2000 | 2003 | 2008 | 2008_R2 - always native). Default is (Windows) 2008_R2 Native.",
273 Option("--base-schema", type="choice", metavar="BASE-SCHEMA",
274 choices=["2008_R2", "2008_R2_old", "2012", "2012_R2"],
275 help="The base schema files to use. Default is (Windows) 2008_R2.",
277 Option("--next-rid", type="int", metavar="NEXTRID", default=1000,
278 help="The initial nextRid value (only needed for upgrades). Default is 1000."),
279 Option("--partitions-only",
280 help="Configure Samba's partitions, but do not modify them (ie, join a BDC)", action="store_true"),
281 Option("--use-rfc2307", action="store_true", help="Use AD to store posix attributes (default = no)"),
285 Option("--ldapadminpass", type="string", metavar="PASSWORD",
286 help="choose password to set between Samba and its LDAP backend (otherwise random)"),
287 Option("--ldap-backend-type", type="choice", metavar="LDAP-BACKEND-TYPE",
288 help="Test initialisation support for unsupported LDAP backend type (fedora-ds or openldap) DO NOT USE",
289 choices=["fedora-ds", "openldap"]),
290 Option("--ol-mmr-urls", type="string", metavar="LDAPSERVER",
291 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\""),
292 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",
293 action="store_true"),
294 Option("--slapd-path", type="string", metavar="SLAPD-PATH",
295 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."),
296 Option("--ldap-backend-extra-port", type="int", metavar="LDAP-BACKEND-EXTRA-PORT", help="Additional TCP port for LDAP backend server (to use for replication)"),
297 Option("--ldap-backend-forced-uri", type="string", metavar="LDAP-BACKEND-FORCED-URI",
298 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"),
299 Option("--ldap-backend-nosync", help="Configure LDAP backend not to call fsync() (for performance in test environments)", action="store_true"),
303 Option("--use-xattrs", type="choice", choices=["yes","no","auto"],
304 metavar="[yes|no|auto]",
305 help="Define if we should use the native fs capabilities or a tdb file for "
306 "storing attributes likes ntacl when --use-ntvfs is set. "
307 "auto tries to make an inteligent guess based on the user rights and system capabilities",
311 takes_options.extend(common_provision_join_options)
313 if os.getenv('TEST_LDAP', "no") == "yes":
314 takes_options.extend(openldap_options)
316 if samba.is_ntvfs_fileserver_built():
317 takes_options.extend(common_ntvfs_options)
318 takes_options.extend(ntvfs_options)
322 def run(self, sambaopts=None, versionopts=None,
345 ldap_backend_type=None,
349 partitions_only=None,
356 ldap_backend_nosync=None,
357 ldap_backend_extra_port=None,
358 ldap_backend_forced_uri=None,
359 ldap_dryrun_mode=None,
361 plaintext_secrets=False,
364 self.logger = self.get_logger("provision")
366 self.logger.setLevel(logging.WARNING)
368 self.logger.setLevel(logging.INFO)
370 lp = sambaopts.get_loadparm()
371 smbconf = lp.configfile
373 if dns_forwarder is not None:
374 suggested_forwarder = dns_forwarder
376 suggested_forwarder = self._get_nameserver_ip()
377 if suggested_forwarder is None:
378 suggested_forwarder = "none"
380 if len(self.raw_argv) == 1:
384 from getpass import getpass
387 def ask(prompt, default=None):
388 if default is not None:
389 print("%s [%s]: " % (prompt, default), end=' ')
391 print("%s: " % (prompt,), end=' ')
392 return sys.stdin.readline().rstrip("\n") or default
395 default = socket.getfqdn().split(".", 1)[1].upper()
398 realm = ask("Realm", default)
399 if realm in (None, ""):
400 raise CommandError("No realm set!")
403 default = realm.split(".")[0]
406 domain = ask("Domain", default)
408 raise CommandError("No domain set!")
410 server_role = ask("Server Role (dc, member, standalone)", "dc")
412 dns_backend = ask("DNS backend (SAMBA_INTERNAL, BIND9_FLATFILE, BIND9_DLZ, NONE)", "SAMBA_INTERNAL")
413 if dns_backend in (None, ''):
414 raise CommandError("No DNS backend set!")
416 if dns_backend == "SAMBA_INTERNAL":
417 dns_forwarder = ask("DNS forwarder IP address (write 'none' to disable forwarding)", suggested_forwarder)
418 if dns_forwarder.lower() in (None, 'none'):
419 suggested_forwarder = None
423 adminpassplain = getpass("Administrator password: ")
424 issue = self._adminpass_issue(adminpassplain)
426 self.errf.write("%s.\n" % issue)
428 adminpassverify = getpass("Retype password: ")
429 if not adminpassplain == adminpassverify:
430 self.errf.write("Sorry, passwords do not match.\n")
432 adminpass = adminpassplain
436 realm = sambaopts._lp.get('realm')
438 raise CommandError("No realm set!")
440 raise CommandError("No domain set!")
443 issue = self._adminpass_issue(adminpass)
445 raise CommandError(issue)
447 self.logger.info("Administrator password will be set randomly!")
449 if function_level == "2000":
450 dom_for_fun_level = DS_DOMAIN_FUNCTION_2000
451 elif function_level == "2003":
452 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
453 elif function_level == "2008":
454 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008
455 elif function_level == "2008_R2":
456 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
458 if dns_backend == "SAMBA_INTERNAL" and dns_forwarder is None:
459 dns_forwarder = suggested_forwarder
461 samdb_fill = FILL_FULL
463 samdb_fill = FILL_NT4SYNC
464 elif partitions_only:
465 samdb_fill = FILL_DRS
467 if targetdir is not None:
468 if not os.path.isdir(targetdir):
473 if use_xattrs == "yes":
475 elif use_xattrs == "auto" and use_ntvfs == False:
477 elif use_ntvfs == False:
478 raise CommandError("--use-xattrs=no requires --use-ntvfs (not supported for production use). "
479 "Please re-run with --use-xattrs omitted.")
480 elif use_xattrs == "auto" and not lp.get("posix:eadb"):
482 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
484 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
487 samba.ntacls.setntacl(lp, file.name,
488 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
491 self.logger.info("You are not root or your system does not support xattr, using tdb backend for attributes. ")
496 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.")
497 if ldap_backend_type == "existing":
498 if ldap_backend_forced_uri is not None:
499 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)
501 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")
503 if ldap_backend_forced_uri is not None:
504 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")
506 if domain_sid is not None:
507 domain_sid = security.dom_sid(domain_sid)
509 session = system_session()
510 if backend_store is None:
511 backend_store = get_default_backend_store()
513 result = provision(self.logger,
514 session, smbconf=smbconf, targetdir=targetdir,
515 samdb_fill=samdb_fill, realm=realm, domain=domain,
516 domainguid=domain_guid, domainsid=domain_sid,
518 hostip=host_ip, hostip6=host_ip6,
519 sitename=site, ntdsguid=ntds_guid,
520 invocationid=invocationid, adminpass=adminpass,
521 krbtgtpass=krbtgtpass, machinepass=machinepass,
522 dns_backend=dns_backend, dns_forwarder=dns_forwarder,
523 dnspass=dnspass, root=root, nobody=nobody,
525 serverrole=server_role, dom_for_fun_level=dom_for_fun_level,
526 backend_type=ldap_backend_type,
527 ldapadminpass=ldapadminpass, ol_mmr_urls=ol_mmr_urls, slapd_path=slapd_path,
528 useeadb=eadb, next_rid=next_rid, lp=lp, use_ntvfs=use_ntvfs,
529 use_rfc2307=use_rfc2307, skip_sysvolacl=False,
530 ldap_backend_extra_port=ldap_backend_extra_port,
531 ldap_backend_forced_uri=ldap_backend_forced_uri,
532 nosync=ldap_backend_nosync, ldap_dryrun_mode=ldap_dryrun_mode,
533 base_schema=base_schema,
534 plaintext_secrets=plaintext_secrets,
535 backend_store=backend_store)
537 except ProvisioningError as e:
538 raise CommandError("Provision failed", e)
540 result.report_logger(self.logger)
542 def _get_nameserver_ip(self):
543 """Grab the nameserver IP address from /etc/resolv.conf."""
545 RESOLV_CONF="/etc/resolv.conf"
547 if not path.isfile(RESOLV_CONF):
548 self.logger.warning("Failed to locate %s" % RESOLV_CONF)
553 handle = open(RESOLV_CONF, 'r')
555 if not line.startswith('nameserver'):
557 # we want the last non-space continuous string of the line
558 return line.strip().split()[-1]
560 if handle is not None:
563 self.logger.warning("No nameserver found in %s" % RESOLV_CONF)
565 def _adminpass_issue(self, adminpass):
566 """Returns error string for a bad administrator password,
567 or None if acceptable"""
569 if len(adminpass.decode('utf-8')) < DEFAULT_MIN_PWD_LENGTH:
570 return "Administrator password does not meet the default minimum" \
571 " password length requirement (%d characters)" \
572 % DEFAULT_MIN_PWD_LENGTH
573 elif not samba.check_password_quality(adminpass):
574 return "Administrator password does not meet the default" \
580 class cmd_domain_dcpromo(Command):
581 """Promote an existing domain member or NT4 PDC to an AD DC."""
583 synopsis = "%prog <dnsdomain> [DC|RODC] [options]"
585 takes_optiongroups = {
586 "sambaopts": options.SambaOptions,
587 "versionopts": options.VersionOptions,
588 "credopts": options.CredentialsOptions,
592 takes_options.extend(common_join_options)
594 takes_options.extend(common_provision_join_options)
596 if samba.is_ntvfs_fileserver_built():
597 takes_options.extend(common_ntvfs_options)
600 takes_args = ["domain", "role?"]
602 def run(self, domain, role=None, sambaopts=None, credopts=None,
603 versionopts=None, server=None, site=None, targetdir=None,
604 domain_critical_only=False, parent_domain=None, machinepass=None,
605 use_ntvfs=False, dns_backend=None,
606 quiet=False, verbose=False, plaintext_secrets=False,
608 lp = sambaopts.get_loadparm()
609 creds = credopts.get_credentials(lp)
610 net = Net(creds, lp, server=credopts.ipaddress)
612 logger = self.get_logger()
614 logger.setLevel(logging.DEBUG)
616 logger.setLevel(logging.WARNING)
618 logger.setLevel(logging.INFO)
620 netbios_name = lp.get("netbios name")
626 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
627 site=site, netbios_name=netbios_name, targetdir=targetdir,
628 domain_critical_only=domain_critical_only,
629 machinepass=machinepass, use_ntvfs=use_ntvfs,
630 dns_backend=dns_backend,
631 promote_existing=True, plaintext_secrets=plaintext_secrets,
632 backend_store=backend_store)
634 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
635 site=site, netbios_name=netbios_name, targetdir=targetdir,
636 domain_critical_only=domain_critical_only,
637 machinepass=machinepass, use_ntvfs=use_ntvfs, dns_backend=dns_backend,
638 promote_existing=True, plaintext_secrets=plaintext_secrets,
639 backend_store=backend_store)
641 raise CommandError("Invalid role '%s' (possible values: DC, RODC)" % role)
644 class cmd_domain_join(Command):
645 """Join domain as either member or backup domain controller."""
647 synopsis = "%prog <dnsdomain> [DC|RODC|MEMBER|SUBDOMAIN] [options]"
649 takes_optiongroups = {
650 "sambaopts": options.SambaOptions,
651 "versionopts": options.VersionOptions,
652 "credopts": options.CredentialsOptions,
656 Option("--parent-domain", help="parent domain to create subdomain under", type=str),
657 Option("--adminpass", type="string", metavar="PASSWORD",
658 help="choose adminstrator password when joining as a subdomain (otherwise random)"),
662 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
665 takes_options.extend(common_join_options)
666 takes_options.extend(common_provision_join_options)
668 if samba.is_ntvfs_fileserver_built():
669 takes_options.extend(ntvfs_options)
671 takes_args = ["domain", "role?"]
673 def run(self, domain, role=None, sambaopts=None, credopts=None,
674 versionopts=None, server=None, site=None, targetdir=None,
675 domain_critical_only=False, parent_domain=None, machinepass=None,
676 use_ntvfs=False, dns_backend=None, adminpass=None,
677 quiet=False, verbose=False,
678 plaintext_secrets=False,
680 lp = sambaopts.get_loadparm()
681 creds = credopts.get_credentials(lp)
682 net = Net(creds, lp, server=credopts.ipaddress)
685 site = "Default-First-Site-Name"
687 logger = self.get_logger()
689 logger.setLevel(logging.DEBUG)
691 logger.setLevel(logging.WARNING)
693 logger.setLevel(logging.INFO)
695 netbios_name = lp.get("netbios name")
700 if role is None or role == "MEMBER":
701 (join_password, sid, domain_name) = net.join_member(
702 domain, netbios_name, LIBNET_JOIN_AUTOMATIC,
703 machinepass=machinepass)
705 self.errf.write("Joined domain %s (%s)\n" % (domain_name, sid))
707 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
708 site=site, netbios_name=netbios_name, targetdir=targetdir,
709 domain_critical_only=domain_critical_only,
710 machinepass=machinepass, use_ntvfs=use_ntvfs,
711 dns_backend=dns_backend,
712 plaintext_secrets=plaintext_secrets,
713 backend_store=backend_store)
715 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
716 site=site, netbios_name=netbios_name, targetdir=targetdir,
717 domain_critical_only=domain_critical_only,
718 machinepass=machinepass, use_ntvfs=use_ntvfs,
719 dns_backend=dns_backend,
720 plaintext_secrets=plaintext_secrets,
721 backend_store=backend_store)
722 elif role == "SUBDOMAIN":
724 logger.info("Administrator password will be set randomly!")
726 netbios_domain = lp.get("workgroup")
727 if parent_domain is None:
728 parent_domain = ".".join(domain.split(".")[1:])
729 join_subdomain(logger=logger, server=server, creds=creds, lp=lp, dnsdomain=domain,
730 parent_domain=parent_domain, site=site,
731 netbios_name=netbios_name, netbios_domain=netbios_domain,
732 targetdir=targetdir, machinepass=machinepass,
733 use_ntvfs=use_ntvfs, dns_backend=dns_backend,
735 plaintext_secrets=plaintext_secrets,
736 backend_store=backend_store)
738 raise CommandError("Invalid role '%s' (possible values: MEMBER, DC, RODC, SUBDOMAIN)" % role)
741 class cmd_domain_demote(Command):
742 """Demote ourselves from the role of Domain Controller."""
744 synopsis = "%prog [options]"
747 Option("--server", help="writable DC to write demotion changes on", type=str),
748 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
749 metavar="URL", dest="H"),
750 Option("--remove-other-dead-server", help="Dead DC (name or NTDS GUID) "
751 "to remove ALL references to (rather than this DC)", type=str),
752 Option("--quiet", help="Be quiet", action="store_true"),
753 Option("--verbose", help="Be verbose", action="store_true"),
756 takes_optiongroups = {
757 "sambaopts": options.SambaOptions,
758 "credopts": options.CredentialsOptions,
759 "versionopts": options.VersionOptions,
762 def run(self, sambaopts=None, credopts=None,
763 versionopts=None, server=None,
764 remove_other_dead_server=None, H=None,
765 verbose=False, quiet=False):
766 lp = sambaopts.get_loadparm()
767 creds = credopts.get_credentials(lp)
768 net = Net(creds, lp, server=credopts.ipaddress)
770 logger = self.get_logger()
772 logger.setLevel(logging.DEBUG)
774 logger.setLevel(logging.WARNING)
776 logger.setLevel(logging.INFO)
778 if remove_other_dead_server is not None:
779 if server is not None:
780 samdb = SamDB(url="ldap://%s" % server,
781 session_info=system_session(),
782 credentials=creds, lp=lp)
784 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
786 remove_dc.remove_dc(samdb, logger, remove_other_dead_server)
787 except remove_dc.DemoteException as err:
788 raise CommandError("Demote failed: %s" % err)
791 netbios_name = lp.get("netbios name")
792 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
794 res = samdb.search(expression='(&(objectClass=computer)(serverReferenceBL=*))', attrs=["dnsHostName", "name"])
796 raise CommandError("Unable to search for servers")
799 raise CommandError("You are the latest server in the domain")
803 if str(e["name"]).lower() != netbios_name.lower():
804 server = e["dnsHostName"]
807 ntds_guid = samdb.get_ntds_GUID()
808 msg = samdb.search(base=str(samdb.get_config_basedn()),
809 scope=ldb.SCOPE_SUBTREE, expression="(objectGUID=%s)" % ntds_guid,
811 if len(msg) == 0 or "options" not in msg[0]:
812 raise CommandError("Failed to find options on %s" % ntds_guid)
815 dsa_options = int(str(msg[0]['options']))
817 res = samdb.search(expression="(fSMORoleOwner=%s)" % str(ntds_dn),
818 controls=["search_options:1:2"])
821 raise CommandError("Current DC is still the owner of %d role(s), use the role command to transfer roles to another DC" % len(res))
823 self.errf.write("Using %s as partner server for the demotion\n" %
825 (drsuapiBind, drsuapi_handle, supportedExtensions) = drsuapi_connect(server, lp, creds)
827 self.errf.write("Deactivating inbound replication\n")
832 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
833 dsa_options |= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
834 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
838 self.errf.write("Asking partner server %s to synchronize from us\n"
840 for part in (samdb.get_schema_basedn(),
841 samdb.get_config_basedn(),
842 samdb.get_root_basedn()):
843 nc = drsuapi.DsReplicaObjectIdentifier()
846 req1 = drsuapi.DsReplicaSyncRequest1()
847 req1.naming_context = nc;
848 req1.options = drsuapi.DRSUAPI_DRS_WRIT_REP
849 req1.source_dsa_guid = misc.GUID(ntds_guid)
852 drsuapiBind.DsReplicaSync(drsuapi_handle, 1, req1)
853 except RuntimeError as e1:
854 (werr, string) = e1.args
855 if werr == werror.WERR_DS_DRA_NO_REPLICA:
859 "Error while replicating out last local changes from '%s' for demotion, "
860 "re-enabling inbound replication\n" % part)
861 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
862 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
864 raise CommandError("Error while sending a DsReplicaSync for partition '%s'" % str(part), string)
866 remote_samdb = SamDB(url="ldap://%s" % server,
867 session_info=system_session(),
868 credentials=creds, lp=lp)
870 self.errf.write("Changing userControl and container\n")
871 res = remote_samdb.search(base=str(remote_samdb.domain_dn()),
872 expression="(&(objectClass=user)(sAMAccountName=%s$))" %
873 netbios_name.upper(),
874 attrs=["userAccountControl"])
876 uac = int(str(res[0]["userAccountControl"]))
878 except Exception as e:
879 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
881 "Error while demoting, re-enabling inbound replication\n")
882 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
883 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
885 raise CommandError("Error while changing account control", e)
888 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
890 "Error while demoting, re-enabling inbound replication")
891 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
892 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
894 raise CommandError("Unable to find object with samaccountName = %s$"
895 " in the remote dc" % netbios_name.upper())
899 uac &= ~(UF_SERVER_TRUST_ACCOUNT|UF_TRUSTED_FOR_DELEGATION|UF_PARTIAL_SECRETS_ACCOUNT)
900 uac |= UF_WORKSTATION_TRUST_ACCOUNT
905 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
906 ldb.FLAG_MOD_REPLACE,
907 "userAccountControl")
909 remote_samdb.modify(msg)
910 except Exception as e:
911 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
913 "Error while demoting, re-enabling inbound replication")
914 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
915 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
918 raise CommandError("Error while changing account control", e)
920 parent = msg.dn.parent()
921 dc_name = res[0].dn.get_rdn_value()
922 rdn = "CN=%s" % dc_name
924 # Let's move to the Computer container
928 computer_dn = ldb.Dn(remote_samdb, "CN=Computers,%s" % str(remote_samdb.domain_dn()))
929 res = remote_samdb.search(base=computer_dn, expression=rdn, scope=ldb.SCOPE_ONELEVEL)
932 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
933 scope=ldb.SCOPE_ONELEVEL)
934 while(len(res) != 0 and i < 100):
936 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
937 scope=ldb.SCOPE_ONELEVEL)
940 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
942 "Error while demoting, re-enabling inbound replication\n")
943 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
944 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
950 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
951 ldb.FLAG_MOD_REPLACE,
952 "userAccountControl")
954 remote_samdb.modify(msg)
956 raise CommandError("Unable to find a slot for renaming %s,"
957 " all names from %s-1 to %s-%d seemed used" %
958 (str(dc_dn), rdn, rdn, i - 9))
960 newrdn = "%s-%d" % (rdn, i)
963 newdn = ldb.Dn(remote_samdb, "%s,%s" % (newrdn, str(computer_dn)))
964 remote_samdb.rename(dc_dn, newdn)
965 except Exception as e:
966 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
968 "Error while demoting, re-enabling inbound replication\n")
969 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
970 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
976 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
977 ldb.FLAG_MOD_REPLACE,
978 "userAccountControl")
980 remote_samdb.modify(msg)
981 raise CommandError("Error while renaming %s to %s" % (str(dc_dn), str(newdn)), e)
984 server_dsa_dn = samdb.get_serverName()
985 domain = remote_samdb.get_root_basedn()
988 req1 = drsuapi.DsRemoveDSServerRequest1()
989 req1.server_dn = str(server_dsa_dn)
990 req1.domain_dn = str(domain)
993 drsuapiBind.DsRemoveDSServer(drsuapi_handle, 1, req1)
994 except RuntimeError as e3:
995 (werr, string) = e3.args
996 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
998 "Error while demoting, re-enabling inbound replication\n")
999 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
1000 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
1006 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
1007 ldb.FLAG_MOD_REPLACE,
1008 "userAccountControl")
1009 remote_samdb.modify(msg)
1010 remote_samdb.rename(newdn, dc_dn)
1011 if werr == werror.WERR_DS_DRA_NO_REPLICA:
1012 raise CommandError("The DC %s is not present on (already removed from) the remote server: " % server_dsa_dn, e)
1014 raise CommandError("Error while sending a removeDsServer of %s: " % server_dsa_dn, e)
1016 remove_dc.remove_sysvol_references(remote_samdb, logger, dc_name)
1018 # These are objects under the computer account that should be deleted
1019 for s in ("CN=Enterprise,CN=NTFRS Subscriptions",
1020 "CN=%s, CN=NTFRS Subscriptions" % lp.get("realm"),
1021 "CN=Domain system Volumes (SYSVOL Share), CN=NTFRS Subscriptions",
1022 "CN=NTFRS Subscriptions"):
1024 remote_samdb.delete(ldb.Dn(remote_samdb,
1025 "%s,%s" % (s, str(newdn))))
1026 except ldb.LdbError as l:
1029 self.errf.write("Demote successful\n")
1032 class cmd_domain_level(Command):
1033 """Raise domain and forest function levels."""
1035 synopsis = "%prog (show|raise <options>) [options]"
1037 takes_optiongroups = {
1038 "sambaopts": options.SambaOptions,
1039 "credopts": options.CredentialsOptions,
1040 "versionopts": options.VersionOptions,
1044 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1045 metavar="URL", dest="H"),
1046 Option("--quiet", help="Be quiet", action="store_true"),
1047 Option("--forest-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
1048 help="The forest function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)"),
1049 Option("--domain-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
1050 help="The domain function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)")
1053 takes_args = ["subcommand"]
1055 def run(self, subcommand, H=None, forest_level=None, domain_level=None,
1056 quiet=False, credopts=None, sambaopts=None, versionopts=None):
1057 lp = sambaopts.get_loadparm()
1058 creds = credopts.get_credentials(lp, fallback_machine=True)
1060 samdb = SamDB(url=H, session_info=system_session(),
1061 credentials=creds, lp=lp)
1063 domain_dn = samdb.domain_dn()
1065 res_forest = samdb.search("CN=Partitions,%s" % samdb.get_config_basedn(),
1066 scope=ldb.SCOPE_BASE, attrs=["msDS-Behavior-Version"])
1067 assert len(res_forest) == 1
1069 res_domain = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1070 attrs=["msDS-Behavior-Version", "nTMixedDomain"])
1071 assert len(res_domain) == 1
1073 res_dc_s = samdb.search("CN=Sites,%s" % samdb.get_config_basedn(),
1074 scope=ldb.SCOPE_SUBTREE, expression="(objectClass=nTDSDSA)",
1075 attrs=["msDS-Behavior-Version"])
1076 assert len(res_dc_s) >= 1
1078 # default values, since "msDS-Behavior-Version" does not exist on Windows 2000 AD
1079 level_forest = DS_DOMAIN_FUNCTION_2000
1080 level_domain = DS_DOMAIN_FUNCTION_2000
1082 if "msDS-Behavior-Version" in res_forest[0]:
1083 level_forest = int(res_forest[0]["msDS-Behavior-Version"][0])
1084 if "msDS-Behavior-Version" in res_domain[0]:
1085 level_domain = int(res_domain[0]["msDS-Behavior-Version"][0])
1086 level_domain_mixed = int(res_domain[0]["nTMixedDomain"][0])
1089 for msg in res_dc_s:
1090 if "msDS-Behavior-Version" in msg:
1091 if min_level_dc is None or int(msg["msDS-Behavior-Version"][0]) < min_level_dc:
1092 min_level_dc = int(msg["msDS-Behavior-Version"][0])
1094 min_level_dc = DS_DOMAIN_FUNCTION_2000
1095 # well, this is the least
1098 if level_forest < DS_DOMAIN_FUNCTION_2000 or level_domain < DS_DOMAIN_FUNCTION_2000:
1099 raise CommandError("Domain and/or forest function level(s) is/are invalid. Correct them or reprovision!")
1100 if min_level_dc < DS_DOMAIN_FUNCTION_2000:
1101 raise CommandError("Lowest function level of a DC is invalid. Correct this or reprovision!")
1102 if level_forest > level_domain:
1103 raise CommandError("Forest function level is higher than the domain level(s). Correct this or reprovision!")
1104 if level_domain > min_level_dc:
1105 raise CommandError("Domain function level is higher than the lowest function level of a DC. Correct this or reprovision!")
1107 if subcommand == "show":
1108 self.message("Domain and forest function level for domain '%s'" % domain_dn)
1109 if level_forest == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1110 self.message("\nATTENTION: You run SAMBA 4 on a forest function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1111 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1112 self.message("\nATTENTION: You run SAMBA 4 on a domain function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1113 if min_level_dc == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1114 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)!")
1118 if level_forest == DS_DOMAIN_FUNCTION_2000:
1120 elif level_forest == DS_DOMAIN_FUNCTION_2003_MIXED:
1121 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1122 elif level_forest == DS_DOMAIN_FUNCTION_2003:
1124 elif level_forest == DS_DOMAIN_FUNCTION_2008:
1126 elif level_forest == DS_DOMAIN_FUNCTION_2008_R2:
1128 elif level_forest == DS_DOMAIN_FUNCTION_2012:
1130 elif level_forest == DS_DOMAIN_FUNCTION_2012_R2:
1133 outstr = "higher than 2012 R2"
1134 self.message("Forest function level: (Windows) " + outstr)
1136 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1137 outstr = "2000 mixed (NT4 DC support)"
1138 elif level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed == 0:
1140 elif level_domain == DS_DOMAIN_FUNCTION_2003_MIXED:
1141 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1142 elif level_domain == DS_DOMAIN_FUNCTION_2003:
1144 elif level_domain == DS_DOMAIN_FUNCTION_2008:
1146 elif level_domain == DS_DOMAIN_FUNCTION_2008_R2:
1148 elif level_domain == DS_DOMAIN_FUNCTION_2012:
1150 elif level_domain == DS_DOMAIN_FUNCTION_2012_R2:
1153 outstr = "higher than 2012 R2"
1154 self.message("Domain function level: (Windows) " + outstr)
1156 if min_level_dc == DS_DOMAIN_FUNCTION_2000:
1158 elif min_level_dc == DS_DOMAIN_FUNCTION_2003:
1160 elif min_level_dc == DS_DOMAIN_FUNCTION_2008:
1162 elif min_level_dc == DS_DOMAIN_FUNCTION_2008_R2:
1164 elif min_level_dc == DS_DOMAIN_FUNCTION_2012:
1166 elif min_level_dc == DS_DOMAIN_FUNCTION_2012_R2:
1169 outstr = "higher than 2012 R2"
1170 self.message("Lowest function level of a DC: (Windows) " + outstr)
1172 elif subcommand == "raise":
1175 if domain_level is not None:
1176 if domain_level == "2003":
1177 new_level_domain = DS_DOMAIN_FUNCTION_2003
1178 elif domain_level == "2008":
1179 new_level_domain = DS_DOMAIN_FUNCTION_2008
1180 elif domain_level == "2008_R2":
1181 new_level_domain = DS_DOMAIN_FUNCTION_2008_R2
1182 elif domain_level == "2012":
1183 new_level_domain = DS_DOMAIN_FUNCTION_2012
1184 elif domain_level == "2012_R2":
1185 new_level_domain = DS_DOMAIN_FUNCTION_2012_R2
1187 if new_level_domain <= level_domain and level_domain_mixed == 0:
1188 raise CommandError("Domain function level can't be smaller than or equal to the actual one!")
1189 if new_level_domain > min_level_dc:
1190 raise CommandError("Domain function level can't be higher than the lowest function level of a DC!")
1192 # Deactivate mixed/interim domain support
1193 if level_domain_mixed != 0:
1194 # Directly on the base DN
1196 m.dn = ldb.Dn(samdb, domain_dn)
1197 m["nTMixedDomain"] = ldb.MessageElement("0",
1198 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1202 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup") + ",CN=Partitions,%s" % samdb.get_config_basedn())
1203 m["nTMixedDomain"] = ldb.MessageElement("0",
1204 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1207 except ldb.LdbError as e:
1208 (enum, emsg) = e.args
1209 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1212 # Directly on the base DN
1214 m.dn = ldb.Dn(samdb, domain_dn)
1215 m["msDS-Behavior-Version"]= ldb.MessageElement(
1216 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1217 "msDS-Behavior-Version")
1221 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup")
1222 + ",CN=Partitions,%s" % samdb.get_config_basedn())
1223 m["msDS-Behavior-Version"]= ldb.MessageElement(
1224 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1225 "msDS-Behavior-Version")
1228 except ldb.LdbError as e2:
1229 (enum, emsg) = e2.args
1230 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1233 level_domain = new_level_domain
1234 msgs.append("Domain function level changed!")
1236 if forest_level is not None:
1237 if forest_level == "2003":
1238 new_level_forest = DS_DOMAIN_FUNCTION_2003
1239 elif forest_level == "2008":
1240 new_level_forest = DS_DOMAIN_FUNCTION_2008
1241 elif forest_level == "2008_R2":
1242 new_level_forest = DS_DOMAIN_FUNCTION_2008_R2
1243 elif forest_level == "2012":
1244 new_level_forest = DS_DOMAIN_FUNCTION_2012
1245 elif forest_level == "2012_R2":
1246 new_level_forest = DS_DOMAIN_FUNCTION_2012_R2
1248 if new_level_forest <= level_forest:
1249 raise CommandError("Forest function level can't be smaller than or equal to the actual one!")
1250 if new_level_forest > level_domain:
1251 raise CommandError("Forest function level can't be higher than the domain function level(s). Please raise it/them first!")
1254 m.dn = ldb.Dn(samdb, "CN=Partitions,%s" % samdb.get_config_basedn())
1255 m["msDS-Behavior-Version"]= ldb.MessageElement(
1256 str(new_level_forest), ldb.FLAG_MOD_REPLACE,
1257 "msDS-Behavior-Version")
1259 msgs.append("Forest function level changed!")
1260 msgs.append("All changes applied successfully!")
1261 self.message("\n".join(msgs))
1263 raise CommandError("invalid argument: '%s' (choose from 'show', 'raise')" % subcommand)
1265 class cmd_domain_passwordsettings_show(Command):
1266 """Display current password settings for the domain."""
1268 synopsis = "%prog [options]"
1270 takes_optiongroups = {
1271 "sambaopts": options.SambaOptions,
1272 "versionopts": options.VersionOptions,
1273 "credopts": options.CredentialsOptions,
1277 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1278 metavar="URL", dest="H"),
1281 def run(self, H=None, credopts=None, sambaopts=None, versionopts=None):
1282 lp = sambaopts.get_loadparm()
1283 creds = credopts.get_credentials(lp)
1285 samdb = SamDB(url=H, session_info=system_session(),
1286 credentials=creds, lp=lp)
1288 domain_dn = samdb.domain_dn()
1289 res = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1290 attrs=["pwdProperties", "pwdHistoryLength", "minPwdLength",
1291 "minPwdAge", "maxPwdAge", "lockoutDuration", "lockoutThreshold",
1292 "lockOutObservationWindow"])
1293 assert(len(res) == 1)
1295 pwd_props = int(res[0]["pwdProperties"][0])
1296 pwd_hist_len = int(res[0]["pwdHistoryLength"][0])
1297 cur_min_pwd_len = int(res[0]["minPwdLength"][0])
1299 cur_min_pwd_age = int(abs(int(res[0]["minPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1300 if int(res[0]["maxPwdAge"][0]) == -0x8000000000000000:
1303 cur_max_pwd_age = int(abs(int(res[0]["maxPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1304 cur_account_lockout_threshold = int(res[0]["lockoutThreshold"][0])
1306 if int(res[0]["lockoutDuration"][0]) == -0x8000000000000000:
1307 cur_account_lockout_duration = 0
1309 cur_account_lockout_duration = abs(int(res[0]["lockoutDuration"][0])) / (1e7 * 60)
1310 cur_reset_account_lockout_after = abs(int(res[0]["lockOutObservationWindow"][0])) / (1e7 * 60)
1311 except Exception as e:
1312 raise CommandError("Could not retrieve password properties!", e)
1314 self.message("Password informations for domain '%s'" % domain_dn)
1316 if pwd_props & DOMAIN_PASSWORD_COMPLEX != 0:
1317 self.message("Password complexity: on")
1319 self.message("Password complexity: off")
1320 if pwd_props & DOMAIN_PASSWORD_STORE_CLEARTEXT != 0:
1321 self.message("Store plaintext passwords: on")
1323 self.message("Store plaintext passwords: off")
1324 self.message("Password history length: %d" % pwd_hist_len)
1325 self.message("Minimum password length: %d" % cur_min_pwd_len)
1326 self.message("Minimum password age (days): %d" % cur_min_pwd_age)
1327 self.message("Maximum password age (days): %d" % cur_max_pwd_age)
1328 self.message("Account lockout duration (mins): %d" % cur_account_lockout_duration)
1329 self.message("Account lockout threshold (attempts): %d" % cur_account_lockout_threshold)
1330 self.message("Reset account lockout after (mins): %d" % cur_reset_account_lockout_after)
1332 class cmd_domain_passwordsettings_set(Command):
1333 """Set password settings.
1335 Password complexity, password lockout policy, history length,
1336 minimum password length, the minimum and maximum password age) on
1337 a Samba AD DC server.
1339 Use against a Windows DC is possible, but group policy will override it.
1342 synopsis = "%prog <options> [options]"
1344 takes_optiongroups = {
1345 "sambaopts": options.SambaOptions,
1346 "versionopts": options.VersionOptions,
1347 "credopts": options.CredentialsOptions,
1351 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1352 metavar="URL", dest="H"),
1353 Option("--quiet", help="Be quiet", action="store_true"),
1354 Option("--complexity", type="choice", choices=["on","off","default"],
1355 help="The password complexity (on | off | default). Default is 'on'"),
1356 Option("--store-plaintext", type="choice", choices=["on","off","default"],
1357 help="Store plaintext passwords where account have 'store passwords with reversible encryption' set (on | off | default). Default is 'off'"),
1358 Option("--history-length",
1359 help="The password history length (<integer> | default). Default is 24.", type=str),
1360 Option("--min-pwd-length",
1361 help="The minimum password length (<integer> | default). Default is 7.", type=str),
1362 Option("--min-pwd-age",
1363 help="The minimum password age (<integer in days> | default). Default is 1.", type=str),
1364 Option("--max-pwd-age",
1365 help="The maximum password age (<integer in days> | default). Default is 43.", type=str),
1366 Option("--account-lockout-duration",
1367 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),
1368 Option("--account-lockout-threshold",
1369 help="The number of bad password attempts allowed before locking out the account (<integer> | default). Default is 0 (never lock out).", type=str),
1370 Option("--reset-account-lockout-after",
1371 help="After this time is elapsed, the recorded number of attempts restarts from zero (<integer> | default). Default is 30.", type=str),
1374 def run(self, H=None, min_pwd_age=None, max_pwd_age=None,
1375 quiet=False, complexity=None, store_plaintext=None, history_length=None,
1376 min_pwd_length=None, account_lockout_duration=None, account_lockout_threshold=None,
1377 reset_account_lockout_after=None, credopts=None, sambaopts=None,
1379 lp = sambaopts.get_loadparm()
1380 creds = credopts.get_credentials(lp)
1382 samdb = SamDB(url=H, session_info=system_session(),
1383 credentials=creds, lp=lp)
1385 domain_dn = samdb.domain_dn()
1388 m.dn = ldb.Dn(samdb, domain_dn)
1389 pwd_props = int(samdb.get_pwdProperties())
1391 if complexity is not None:
1392 if complexity == "on" or complexity == "default":
1393 pwd_props = pwd_props | DOMAIN_PASSWORD_COMPLEX
1394 msgs.append("Password complexity activated!")
1395 elif complexity == "off":
1396 pwd_props = pwd_props & (~DOMAIN_PASSWORD_COMPLEX)
1397 msgs.append("Password complexity deactivated!")
1399 if store_plaintext is not None:
1400 if store_plaintext == "on" or store_plaintext == "default":
1401 pwd_props = pwd_props | DOMAIN_PASSWORD_STORE_CLEARTEXT
1402 msgs.append("Plaintext password storage for changed passwords activated!")
1403 elif store_plaintext == "off":
1404 pwd_props = pwd_props & (~DOMAIN_PASSWORD_STORE_CLEARTEXT)
1405 msgs.append("Plaintext password storage for changed passwords deactivated!")
1407 if complexity is not None or store_plaintext is not None:
1408 m["pwdProperties"] = ldb.MessageElement(str(pwd_props),
1409 ldb.FLAG_MOD_REPLACE, "pwdProperties")
1411 if history_length is not None:
1412 if history_length == "default":
1415 pwd_hist_len = int(history_length)
1417 if pwd_hist_len < 0 or pwd_hist_len > 24:
1418 raise CommandError("Password history length must be in the range of 0 to 24!")
1420 m["pwdHistoryLength"] = ldb.MessageElement(str(pwd_hist_len),
1421 ldb.FLAG_MOD_REPLACE, "pwdHistoryLength")
1422 msgs.append("Password history length changed!")
1424 if min_pwd_length is not None:
1425 if min_pwd_length == "default":
1428 min_pwd_len = int(min_pwd_length)
1430 if min_pwd_len < 0 or min_pwd_len > 14:
1431 raise CommandError("Minimum password length must be in the range of 0 to 14!")
1433 m["minPwdLength"] = ldb.MessageElement(str(min_pwd_len),
1434 ldb.FLAG_MOD_REPLACE, "minPwdLength")
1435 msgs.append("Minimum password length changed!")
1437 if min_pwd_age is not None:
1438 if min_pwd_age == "default":
1441 min_pwd_age = int(min_pwd_age)
1443 if min_pwd_age < 0 or min_pwd_age > 998:
1444 raise CommandError("Minimum password age must be in the range of 0 to 998!")
1447 min_pwd_age_ticks = -int(min_pwd_age * (24 * 60 * 60 * 1e7))
1449 m["minPwdAge"] = ldb.MessageElement(str(min_pwd_age_ticks),
1450 ldb.FLAG_MOD_REPLACE, "minPwdAge")
1451 msgs.append("Minimum password age changed!")
1453 if max_pwd_age is not None:
1454 if max_pwd_age == "default":
1457 max_pwd_age = int(max_pwd_age)
1459 if max_pwd_age < 0 or max_pwd_age > 999:
1460 raise CommandError("Maximum password age must be in the range of 0 to 999!")
1463 if max_pwd_age == 0:
1464 max_pwd_age_ticks = -0x8000000000000000
1466 max_pwd_age_ticks = -int(max_pwd_age * (24 * 60 * 60 * 1e7))
1468 m["maxPwdAge"] = ldb.MessageElement(str(max_pwd_age_ticks),
1469 ldb.FLAG_MOD_REPLACE, "maxPwdAge")
1470 msgs.append("Maximum password age changed!")
1472 if account_lockout_duration is not None:
1473 if account_lockout_duration == "default":
1474 account_lockout_duration = 30
1476 account_lockout_duration = int(account_lockout_duration)
1478 if account_lockout_duration < 0 or account_lockout_duration > 99999:
1479 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1482 if account_lockout_duration == 0:
1483 account_lockout_duration_ticks = -0x8000000000000000
1485 account_lockout_duration_ticks = -int(account_lockout_duration * (60 * 1e7))
1487 m["lockoutDuration"] = ldb.MessageElement(str(account_lockout_duration_ticks),
1488 ldb.FLAG_MOD_REPLACE, "lockoutDuration")
1489 msgs.append("Account lockout duration changed!")
1491 if account_lockout_threshold is not None:
1492 if account_lockout_threshold == "default":
1493 account_lockout_threshold = 0
1495 account_lockout_threshold = int(account_lockout_threshold)
1497 m["lockoutThreshold"] = ldb.MessageElement(str(account_lockout_threshold),
1498 ldb.FLAG_MOD_REPLACE, "lockoutThreshold")
1499 msgs.append("Account lockout threshold changed!")
1501 if reset_account_lockout_after is not None:
1502 if reset_account_lockout_after == "default":
1503 reset_account_lockout_after = 30
1505 reset_account_lockout_after = int(reset_account_lockout_after)
1507 if reset_account_lockout_after < 0 or reset_account_lockout_after > 99999:
1508 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1511 if reset_account_lockout_after == 0:
1512 reset_account_lockout_after_ticks = -0x8000000000000000
1514 reset_account_lockout_after_ticks = -int(reset_account_lockout_after * (60 * 1e7))
1516 m["lockOutObservationWindow"] = ldb.MessageElement(str(reset_account_lockout_after_ticks),
1517 ldb.FLAG_MOD_REPLACE, "lockOutObservationWindow")
1518 msgs.append("Duration to reset account lockout after changed!")
1520 if max_pwd_age > 0 and min_pwd_age >= max_pwd_age:
1521 raise CommandError("Maximum password age (%d) must be greater than minimum password age (%d)!" % (max_pwd_age, min_pwd_age))
1524 raise CommandError("You must specify at least one option to set. Try --help")
1526 msgs.append("All changes applied successfully!")
1527 self.message("\n".join(msgs))
1529 class cmd_domain_passwordsettings(SuperCommand):
1530 """Manage password policy settings."""
1533 subcommands["show"] = cmd_domain_passwordsettings_show()
1534 subcommands["set"] = cmd_domain_passwordsettings_set()
1536 class cmd_domain_classicupgrade(Command):
1537 """Upgrade from Samba classic (NT4-like) database to Samba AD DC database.
1539 Specify either a directory with all Samba classic DC databases and state files (with --dbdir) or
1540 the testparm utility from your classic installation (with --testparm).
1543 synopsis = "%prog [options] <classic_smb_conf>"
1545 takes_optiongroups = {
1546 "sambaopts": options.SambaOptions,
1547 "versionopts": options.VersionOptions
1551 Option("--dbdir", type="string", metavar="DIR",
1552 help="Path to samba classic DC database directory"),
1553 Option("--testparm", type="string", metavar="PATH",
1554 help="Path to samba classic DC testparm utility from the previous installation. This allows the default paths of the previous installation to be followed"),
1555 Option("--targetdir", type="string", metavar="DIR",
1556 help="Path prefix where the new Samba 4.0 AD domain should be initialised"),
1557 Option("--quiet", help="Be quiet", action="store_true"),
1558 Option("--verbose", help="Be verbose", action="store_true"),
1559 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
1560 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
1561 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
1562 "BIND9_FLATFILE uses bind9 text database to store zone information, "
1563 "BIND9_DLZ uses samba4 AD to store zone information, "
1564 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
1565 default="SAMBA_INTERNAL")
1569 Option("--use-xattrs", type="choice", choices=["yes","no","auto"],
1570 metavar="[yes|no|auto]",
1571 help="Define if we should use the native fs capabilities or a tdb file for "
1572 "storing attributes likes ntacl when --use-ntvfs is set. "
1573 "auto tries to make an inteligent guess based on the user rights and system capabilities",
1576 if samba.is_ntvfs_fileserver_built():
1577 takes_options.extend(common_ntvfs_options)
1578 takes_options.extend(ntvfs_options)
1580 takes_args = ["smbconf"]
1582 def run(self, smbconf=None, targetdir=None, dbdir=None, testparm=None,
1583 quiet=False, verbose=False, use_xattrs="auto", sambaopts=None, versionopts=None,
1584 dns_backend=None, use_ntvfs=False):
1586 if not os.path.exists(smbconf):
1587 raise CommandError("File %s does not exist" % smbconf)
1589 if testparm and not os.path.exists(testparm):
1590 raise CommandError("Testparm utility %s does not exist" % testparm)
1592 if dbdir and not os.path.exists(dbdir):
1593 raise CommandError("Directory %s does not exist" % dbdir)
1595 if not dbdir and not testparm:
1596 raise CommandError("Please specify either dbdir or testparm")
1598 logger = self.get_logger()
1600 logger.setLevel(logging.DEBUG)
1602 logger.setLevel(logging.WARNING)
1604 logger.setLevel(logging.INFO)
1606 if dbdir and testparm:
1607 logger.warning("both dbdir and testparm specified, ignoring dbdir.")
1610 lp = sambaopts.get_loadparm()
1612 s3conf = s3param.get_context()
1615 s3conf.set("realm", sambaopts.realm)
1617 if targetdir is not None:
1618 if not os.path.isdir(targetdir):
1622 if use_xattrs == "yes":
1624 elif use_xattrs == "auto" and use_ntvfs == False:
1626 elif use_ntvfs == False:
1627 raise CommandError("--use-xattrs=no requires --use-ntvfs (not supported for production use). "
1628 "Please re-run with --use-xattrs omitted.")
1629 elif use_xattrs == "auto" and not s3conf.get("posix:eadb"):
1631 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
1633 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
1636 samba.ntacls.setntacl(lp, tmpfile.name,
1637 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
1640 # FIXME: Don't catch all exceptions here
1641 logger.info("You are not root or your system does not support xattr, using tdb backend for attributes. "
1642 "If you intend to use this provision in production, rerun the script as root on a system supporting xattrs.")
1646 # Set correct default values from dbdir or testparm
1649 paths["state directory"] = dbdir
1650 paths["private dir"] = dbdir
1651 paths["lock directory"] = dbdir
1652 paths["smb passwd file"] = dbdir + "/smbpasswd"
1654 paths["state directory"] = get_testparm_var(testparm, smbconf, "state directory")
1655 paths["private dir"] = get_testparm_var(testparm, smbconf, "private dir")
1656 paths["smb passwd file"] = get_testparm_var(testparm, smbconf, "smb passwd file")
1657 paths["lock directory"] = get_testparm_var(testparm, smbconf, "lock directory")
1658 # "testparm" from Samba 3 < 3.4.x is not aware of the parameter
1659 # "state directory", instead make use of "lock directory"
1660 if len(paths["state directory"]) == 0:
1661 paths["state directory"] = paths["lock directory"]
1664 s3conf.set(p, paths[p])
1666 # load smb.conf parameters
1667 logger.info("Reading smb.conf")
1668 s3conf.load(smbconf)
1669 samba3 = Samba3(smbconf, s3conf)
1671 logger.info("Provisioning")
1672 upgrade_from_samba3(samba3, logger, targetdir, session_info=system_session(),
1673 useeadb=eadb, dns_backend=dns_backend, use_ntvfs=use_ntvfs)
1676 class cmd_domain_samba3upgrade(cmd_domain_classicupgrade):
1677 __doc__ = cmd_domain_classicupgrade.__doc__
1679 # This command is present for backwards compatibility only,
1680 # and should not be shown.
1684 class LocalDCCredentialsOptions(options.CredentialsOptions):
1685 def __init__(self, parser):
1686 options.CredentialsOptions.__init__(self, parser, special_name="local-dc")
1688 class DomainTrustCommand(Command):
1689 """List domain trusts."""
1692 Command.__init__(self)
1693 self.local_lp = None
1695 self.local_server = None
1696 self.local_binding_string = None
1697 self.local_creds = None
1699 self.remote_server = None
1700 self.remote_binding_string = None
1701 self.remote_creds = None
1703 def _uint32(self, v):
1704 return ctypes.c_uint32(v).value
1706 def check_runtime_error(self, runtime, val):
1710 err32 = self._uint32(runtime[0])
1716 class LocalRuntimeError(CommandError):
1717 def __init__(exception_self, self, runtime, message):
1718 err32 = self._uint32(runtime[0])
1720 msg = "LOCAL_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1721 self.local_server, message, err32, errstr)
1722 CommandError.__init__(exception_self, msg)
1724 class RemoteRuntimeError(CommandError):
1725 def __init__(exception_self, self, runtime, message):
1726 err32 = self._uint32(runtime[0])
1728 msg = "REMOTE_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1729 self.remote_server, message, err32, errstr)
1730 CommandError.__init__(exception_self, msg)
1732 class LocalLdbError(CommandError):
1733 def __init__(exception_self, self, ldb_error, message):
1734 errval = ldb_error[0]
1735 errstr = ldb_error[1]
1736 msg = "LOCAL_DC[%s]: %s - ERROR(%d) - %s" % (
1737 self.local_server, message, errval, errstr)
1738 CommandError.__init__(exception_self, msg)
1740 def setup_local_server(self, sambaopts, localdcopts):
1741 if self.local_server is not None:
1742 return self.local_server
1744 lp = sambaopts.get_loadparm()
1746 local_server = localdcopts.ipaddress
1747 if local_server is None:
1748 server_role = lp.server_role()
1749 if server_role != "ROLE_ACTIVE_DIRECTORY_DC":
1750 raise CommandError("Invalid server_role %s" % (server_role))
1751 local_server = lp.get('netbios name')
1752 local_transport = "ncalrpc"
1753 local_binding_options = ""
1754 local_binding_options += ",auth_type=ncalrpc_as_system"
1755 local_ldap_url = None
1758 local_transport = "ncacn_np"
1759 local_binding_options = ""
1760 local_ldap_url = "ldap://%s" % local_server
1761 local_creds = localdcopts.get_credentials(lp)
1765 self.local_server = local_server
1766 self.local_binding_string = "%s:%s[%s]" % (local_transport, local_server, local_binding_options)
1767 self.local_ldap_url = local_ldap_url
1768 self.local_creds = local_creds
1769 return self.local_server
1771 def new_local_lsa_connection(self):
1772 return lsa.lsarpc(self.local_binding_string, self.local_lp, self.local_creds)
1774 def new_local_netlogon_connection(self):
1775 return netlogon.netlogon(self.local_binding_string, self.local_lp, self.local_creds)
1777 def new_local_ldap_connection(self):
1778 return SamDB(url=self.local_ldap_url,
1779 session_info=system_session(),
1780 credentials=self.local_creds,
1783 def setup_remote_server(self, credopts, domain,
1785 require_writable=True):
1788 assert require_writable
1790 if self.remote_server is not None:
1791 return self.remote_server
1793 self.remote_server = "__unknown__remote_server__.%s" % domain
1794 assert self.local_server is not None
1796 remote_creds = credopts.get_credentials(self.local_lp)
1797 remote_server = credopts.ipaddress
1798 remote_binding_options = ""
1800 # TODO: we should also support NT4 domains
1801 # we could use local_netlogon.netr_DsRGetDCNameEx2() with the remote domain name
1802 # and delegate NBT or CLDAP to the local netlogon server
1804 remote_net = Net(remote_creds, self.local_lp, server=remote_server)
1805 remote_flags = nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS
1806 if require_writable:
1807 remote_flags |= nbt.NBT_SERVER_WRITABLE
1809 remote_flags |= nbt.NBT_SERVER_PDC
1810 remote_info = remote_net.finddc(flags=remote_flags, domain=domain, address=remote_server)
1811 except NTSTATUSError as error:
1812 raise CommandError("Failed to find a writeable DC for domain '%s': %s" %
1815 raise CommandError("Failed to find a writeable DC for domain '%s'" % domain)
1817 nbt.NBT_SERVER_PDC: "PDC",
1818 nbt.NBT_SERVER_GC: "GC",
1819 nbt.NBT_SERVER_LDAP: "LDAP",
1820 nbt.NBT_SERVER_DS: "DS",
1821 nbt.NBT_SERVER_KDC: "KDC",
1822 nbt.NBT_SERVER_TIMESERV: "TIMESERV",
1823 nbt.NBT_SERVER_CLOSEST: "CLOSEST",
1824 nbt.NBT_SERVER_WRITABLE: "WRITABLE",
1825 nbt.NBT_SERVER_GOOD_TIMESERV: "GOOD_TIMESERV",
1826 nbt.NBT_SERVER_NDNC: "NDNC",
1827 nbt.NBT_SERVER_SELECT_SECRET_DOMAIN_6: "SELECT_SECRET_DOMAIN_6",
1828 nbt.NBT_SERVER_FULL_SECRET_DOMAIN_6: "FULL_SECRET_DOMAIN_6",
1829 nbt.NBT_SERVER_ADS_WEB_SERVICE: "ADS_WEB_SERVICE",
1830 nbt.NBT_SERVER_DS_8: "DS_8",
1831 nbt.NBT_SERVER_HAS_DNS_NAME: "HAS_DNS_NAME",
1832 nbt.NBT_SERVER_IS_DEFAULT_NC: "IS_DEFAULT_NC",
1833 nbt.NBT_SERVER_FOREST_ROOT: "FOREST_ROOT",
1835 server_type_string = self.generic_bitmap_to_string(flag_map,
1836 remote_info.server_type, names_only=True)
1837 self.outf.write("RemoteDC Netbios[%s] DNS[%s] ServerType[%s]\n" % (
1838 remote_info.pdc_name,
1839 remote_info.pdc_dns_name,
1840 server_type_string))
1842 self.remote_server = remote_info.pdc_dns_name
1843 self.remote_binding_string="ncacn_np:%s[%s]" % (self.remote_server, remote_binding_options)
1844 self.remote_creds = remote_creds
1845 return self.remote_server
1847 def new_remote_lsa_connection(self):
1848 return lsa.lsarpc(self.remote_binding_string, self.local_lp, self.remote_creds)
1850 def new_remote_netlogon_connection(self):
1851 return netlogon.netlogon(self.remote_binding_string, self.local_lp, self.remote_creds)
1853 def get_lsa_info(self, conn, policy_access):
1854 objectAttr = lsa.ObjectAttribute()
1855 objectAttr.sec_qos = lsa.QosInfo()
1857 policy = conn.OpenPolicy2(''.decode('utf-8'),
1858 objectAttr, policy_access)
1860 info = conn.QueryInfoPolicy2(policy, lsa.LSA_POLICY_INFO_DNS)
1862 return (policy, info)
1864 def get_netlogon_dc_info(self, conn, server):
1865 info = conn.netr_DsRGetDCNameEx2(server,
1866 None, 0, None, None, None,
1867 netlogon.DS_RETURN_DNS_NAME)
1870 def netr_DomainTrust_to_name(self, t):
1871 if t.trust_type == lsa.LSA_TRUST_TYPE_DOWNLEVEL:
1872 return t.netbios_name
1876 def netr_DomainTrust_to_type(self, a, t):
1878 primary_parent = None
1880 if _t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
1882 if not _t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1883 primary_parent = a[_t.parent_index]
1886 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1887 if t is primary_parent:
1890 if t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1893 parent = a[t.parent_index]
1894 if parent is primary:
1899 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1904 def netr_DomainTrust_to_transitive(self, t):
1905 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1908 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE:
1911 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1916 def netr_DomainTrust_to_direction(self, t):
1917 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND and \
1918 t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1921 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND:
1924 if t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1929 def generic_enum_to_string(self, e_dict, v, names_only=False):
1933 v32 = self._uint32(v)
1934 w = "__unknown__%08X__" % v32
1936 r = "0x%x (%s)" % (v, w)
1939 def generic_bitmap_to_string(self, b_dict, v, names_only=False):
1944 for b in sorted(b_dict.keys()):
1951 c32 = self._uint32(c)
1952 s += ["__unknown_%08X__" % c32]
1957 r = "0x%x (%s)" % (v, w)
1960 def trustType_string(self, v):
1962 lsa.LSA_TRUST_TYPE_DOWNLEVEL : "DOWNLEVEL",
1963 lsa.LSA_TRUST_TYPE_UPLEVEL : "UPLEVEL",
1964 lsa.LSA_TRUST_TYPE_MIT : "MIT",
1965 lsa.LSA_TRUST_TYPE_DCE : "DCE",
1967 return self.generic_enum_to_string(types, v)
1969 def trustDirection_string(self, v):
1971 lsa.LSA_TRUST_DIRECTION_INBOUND |
1972 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "BOTH",
1973 lsa.LSA_TRUST_DIRECTION_INBOUND : "INBOUND",
1974 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "OUTBOUND",
1976 return self.generic_enum_to_string(directions, v)
1978 def trustAttributes_string(self, v):
1980 lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE : "NON_TRANSITIVE",
1981 lsa.LSA_TRUST_ATTRIBUTE_UPLEVEL_ONLY : "UPLEVEL_ONLY",
1982 lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN : "QUARANTINED_DOMAIN",
1983 lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE : "FOREST_TRANSITIVE",
1984 lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION : "CROSS_ORGANIZATION",
1985 lsa.LSA_TRUST_ATTRIBUTE_WITHIN_FOREST : "WITHIN_FOREST",
1986 lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL : "TREAT_AS_EXTERNAL",
1987 lsa.LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION : "USES_RC4_ENCRYPTION",
1989 return self.generic_bitmap_to_string(attributes, v)
1991 def kerb_EncTypes_string(self, v):
1993 security.KERB_ENCTYPE_DES_CBC_CRC : "DES_CBC_CRC",
1994 security.KERB_ENCTYPE_DES_CBC_MD5 : "DES_CBC_MD5",
1995 security.KERB_ENCTYPE_RC4_HMAC_MD5 : "RC4_HMAC_MD5",
1996 security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96 : "AES128_CTS_HMAC_SHA1_96",
1997 security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96 : "AES256_CTS_HMAC_SHA1_96",
1998 security.KERB_ENCTYPE_FAST_SUPPORTED : "FAST_SUPPORTED",
1999 security.KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED : "COMPOUND_IDENTITY_SUPPORTED",
2000 security.KERB_ENCTYPE_CLAIMS_SUPPORTED : "CLAIMS_SUPPORTED",
2001 security.KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED : "RESOURCE_SID_COMPRESSION_DISABLED",
2003 return self.generic_bitmap_to_string(enctypes, v)
2005 def entry_tln_status(self, e_flags, ):
2007 return "Status[Enabled]"
2010 lsa.LSA_TLN_DISABLED_NEW : "Disabled-New",
2011 lsa.LSA_TLN_DISABLED_ADMIN : "Disabled",
2012 lsa.LSA_TLN_DISABLED_CONFLICT : "Disabled-Conflicting",
2014 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
2016 def entry_dom_status(self, e_flags):
2018 return "Status[Enabled]"
2021 lsa.LSA_SID_DISABLED_ADMIN : "Disabled-SID",
2022 lsa.LSA_SID_DISABLED_CONFLICT : "Disabled-SID-Conflicting",
2023 lsa.LSA_NB_DISABLED_ADMIN : "Disabled-NB",
2024 lsa.LSA_NB_DISABLED_CONFLICT : "Disabled-NB-Conflicting",
2026 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
2028 def write_forest_trust_info(self, fti, tln=None, collisions=None):
2030 tln_string = " TDO[%s]" % tln
2034 self.outf.write("Namespaces[%d]%s:\n" % (
2035 len(fti.entries), tln_string))
2037 for i, e in enumerate(fti.entries):
2040 collision_string = ""
2042 if collisions is not None:
2043 for c in collisions.entries:
2047 collision_string = " Collision[%s]" % (c.name.string)
2049 d = e.forest_trust_data
2050 if e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
2051 self.outf.write("TLN: %-32s DNS[*.%s]%s\n" % (
2052 self.entry_tln_status(flags),
2053 d.string, collision_string))
2054 elif e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
2055 self.outf.write("TLN_EX: %-29s DNS[*.%s]\n" % (
2057 elif e.type == lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
2058 self.outf.write("DOM: %-32s DNS[%s] Netbios[%s] SID[%s]%s\n" % (
2059 self.entry_dom_status(flags),
2060 d.dns_domain_name.string,
2061 d.netbios_domain_name.string,
2062 d.domain_sid, collision_string))
2065 class cmd_domain_trust_list(DomainTrustCommand):
2066 """List domain trusts."""
2068 synopsis = "%prog [options]"
2070 takes_optiongroups = {
2071 "sambaopts": options.SambaOptions,
2072 "versionopts": options.VersionOptions,
2073 "localdcopts": LocalDCCredentialsOptions,
2079 def run(self, sambaopts=None, versionopts=None, localdcopts=None):
2081 local_server = self.setup_local_server(sambaopts, localdcopts)
2083 local_netlogon = self.new_local_netlogon_connection()
2084 except RuntimeError as error:
2085 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2088 local_netlogon_trusts = local_netlogon.netr_DsrEnumerateDomainTrusts(local_server,
2089 netlogon.NETR_TRUST_FLAG_IN_FOREST |
2090 netlogon.NETR_TRUST_FLAG_OUTBOUND |
2091 netlogon.NETR_TRUST_FLAG_INBOUND)
2092 except RuntimeError as error:
2093 if self.check_runtime_error(error, werror.WERR_RPC_S_PROCNUM_OUT_OF_RANGE):
2094 # TODO: we could implement a fallback to lsa.EnumTrustDom()
2095 raise CommandError("LOCAL_DC[%s]: netr_DsrEnumerateDomainTrusts not supported." % (
2097 raise self.LocalRuntimeError(self, error, "netr_DsrEnumerateDomainTrusts failed")
2099 a = local_netlogon_trusts.array
2101 if t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
2103 self.outf.write("%-14s %-15s %-19s %s\n" % (
2104 "Type[%s]" % self.netr_DomainTrust_to_type(a, t),
2105 "Transitive[%s]" % self.netr_DomainTrust_to_transitive(t),
2106 "Direction[%s]" % self.netr_DomainTrust_to_direction(t),
2107 "Name[%s]" % self.netr_DomainTrust_to_name(t)))
2110 class cmd_domain_trust_show(DomainTrustCommand):
2111 """Show trusted domain details."""
2113 synopsis = "%prog NAME [options]"
2115 takes_optiongroups = {
2116 "sambaopts": options.SambaOptions,
2117 "versionopts": options.VersionOptions,
2118 "localdcopts": LocalDCCredentialsOptions,
2124 takes_args = ["domain"]
2126 def run(self, domain, sambaopts=None, versionopts=None, localdcopts=None):
2128 local_server = self.setup_local_server(sambaopts, localdcopts)
2130 local_lsa = self.new_local_lsa_connection()
2131 except RuntimeError as error:
2132 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2135 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2136 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2137 except RuntimeError as error:
2138 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2140 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2141 local_lsa_info.name.string,
2142 local_lsa_info.dns_domain.string,
2143 local_lsa_info.sid))
2145 lsaString = lsa.String()
2146 lsaString.string = domain
2148 local_tdo_full = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2149 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2150 local_tdo_info = local_tdo_full.info_ex
2151 local_tdo_posix = local_tdo_full.posix_offset
2152 except NTSTATUSError as error:
2153 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2154 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2156 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(FULL_INFO) failed")
2159 local_tdo_enctypes = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2160 lsaString, lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES)
2161 except NTSTATUSError as error:
2162 if self.check_runtime_error(error, ntstatus.NT_STATUS_INVALID_PARAMETER):
2164 if self.check_runtime_error(error, ntstatus.NT_STATUS_INVALID_INFO_CLASS):
2167 if error is not None:
2168 raise self.LocalRuntimeError(self, error,
2169 "QueryTrustedDomainInfoByName(SUPPORTED_ENCRYPTION_TYPES) failed")
2171 local_tdo_enctypes = lsa.TrustDomainInfoSupportedEncTypes()
2172 local_tdo_enctypes.enc_types = 0
2175 local_tdo_forest = None
2176 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2177 local_tdo_forest = local_lsa.lsaRQueryForestTrustInformation(local_policy,
2178 lsaString, lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
2179 except RuntimeError as error:
2180 if self.check_runtime_error(error, ntstatus.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
2182 if self.check_runtime_error(error, ntstatus.NT_STATUS_NOT_FOUND):
2184 if error is not None:
2185 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation failed")
2187 local_tdo_forest = lsa.ForestTrustInformation()
2188 local_tdo_forest.count = 0
2189 local_tdo_forest.entries = []
2191 self.outf.write("TrustedDomain:\n\n");
2192 self.outf.write("NetbiosName: %s\n" % local_tdo_info.netbios_name.string)
2193 if local_tdo_info.netbios_name.string != local_tdo_info.domain_name.string:
2194 self.outf.write("DnsName: %s\n" % local_tdo_info.domain_name.string)
2195 self.outf.write("SID: %s\n" % local_tdo_info.sid)
2196 self.outf.write("Type: %s\n" % self.trustType_string(local_tdo_info.trust_type))
2197 self.outf.write("Direction: %s\n" % self.trustDirection_string(local_tdo_info.trust_direction))
2198 self.outf.write("Attributes: %s\n" % self.trustAttributes_string(local_tdo_info.trust_attributes))
2199 posix_offset_u32 = ctypes.c_uint32(local_tdo_posix.posix_offset).value
2200 posix_offset_i32 = ctypes.c_int32(local_tdo_posix.posix_offset).value
2201 self.outf.write("PosixOffset: 0x%08X (%d)\n" % (posix_offset_u32, posix_offset_i32))
2202 self.outf.write("kerb_EncTypes: %s\n" % self.kerb_EncTypes_string(local_tdo_enctypes.enc_types))
2204 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2205 self.write_forest_trust_info(local_tdo_forest,
2206 tln=local_tdo_info.domain_name.string)
2210 class cmd_domain_trust_create(DomainTrustCommand):
2211 """Create a domain or forest trust."""
2213 synopsis = "%prog DOMAIN [options]"
2215 takes_optiongroups = {
2216 "sambaopts": options.SambaOptions,
2217 "versionopts": options.VersionOptions,
2218 "credopts": options.CredentialsOptions,
2219 "localdcopts": LocalDCCredentialsOptions,
2223 Option("--type", type="choice", metavar="TYPE",
2224 choices=["external", "forest"],
2225 help="The type of the trust: 'external' or 'forest'.",
2227 default="external"),
2228 Option("--direction", type="choice", metavar="DIRECTION",
2229 choices=["incoming", "outgoing", "both"],
2230 help="The trust direction: 'incoming', 'outgoing' or 'both'.",
2231 dest='trust_direction',
2233 Option("--create-location", type="choice", metavar="LOCATION",
2234 choices=["local", "both"],
2235 help="Where to create the trusted domain object: 'local' or 'both'.",
2236 dest='create_location',
2238 Option("--cross-organisation", action="store_true",
2239 help="The related domains does not belong to the same organisation.",
2240 dest='cross_organisation',
2242 Option("--quarantined", type="choice", metavar="yes|no",
2243 choices=["yes", "no", None],
2244 help="Special SID filtering rules are applied to the trust. "
2245 "With --type=external the default is yes. "
2246 "With --type=forest the default is no.",
2247 dest='quarantined_arg',
2249 Option("--not-transitive", action="store_true",
2250 help="The forest trust is not transitive.",
2251 dest='not_transitive',
2253 Option("--treat-as-external", action="store_true",
2254 help="The treat the forest trust as external.",
2255 dest='treat_as_external',
2257 Option("--no-aes-keys", action="store_false",
2258 help="The trust uses aes kerberos keys.",
2259 dest='use_aes_keys',
2261 Option("--skip-validation", action="store_false",
2262 help="Skip validation of the trust.",
2267 takes_args = ["domain"]
2269 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2270 trust_type=None, trust_direction=None, create_location=None,
2271 cross_organisation=False, quarantined_arg=None,
2272 not_transitive=False, treat_as_external=False,
2273 use_aes_keys=False, validate=True):
2275 lsaString = lsa.String()
2278 if quarantined_arg is None:
2279 if trust_type == 'external':
2281 elif quarantined_arg == 'yes':
2284 if trust_type != 'forest':
2286 raise CommandError("--not-transitive requires --type=forest")
2287 if treat_as_external:
2288 raise CommandError("--treat-as-external requires --type=forest")
2292 enc_types = lsa.TrustDomainInfoSupportedEncTypes()
2293 enc_types.enc_types = security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96
2294 enc_types.enc_types |= security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96
2296 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2297 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2298 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2300 local_trust_info = lsa.TrustDomainInfoInfoEx()
2301 local_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2302 local_trust_info.trust_direction = 0
2303 if trust_direction == "both":
2304 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2305 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2306 elif trust_direction == "incoming":
2307 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2308 elif trust_direction == "outgoing":
2309 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2310 local_trust_info.trust_attributes = 0
2311 if cross_organisation:
2312 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2314 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2315 if trust_type == "forest":
2316 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2318 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2319 if treat_as_external:
2320 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2322 def get_password(name):
2325 if password is not None and password is not '':
2327 password = getpass("New %s Password: " % name)
2328 passwordverify = getpass("Retype %s Password: " % name)
2329 if not password == passwordverify:
2331 self.outf.write("Sorry, passwords do not match.\n")
2333 incoming_secret = None
2334 outgoing_secret = None
2335 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2336 if create_location == "local":
2337 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2338 incoming_password = get_password("Incoming Trust")
2339 incoming_secret = string_to_byte_array(incoming_password.encode('utf-16-le'))
2340 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2341 outgoing_password = get_password("Outgoing Trust")
2342 outgoing_secret = string_to_byte_array(outgoing_password.encode('utf-16-le'))
2344 remote_trust_info = None
2346 # We use 240 random bytes.
2347 # Windows uses 28 or 240 random bytes. I guess it's
2348 # based on the trust type external vs. forest.
2350 # The initial trust password can be up to 512 bytes
2351 # while the versioned passwords used for periodic updates
2352 # can only be up to 498 bytes, as netr_ServerPasswordSet2()
2353 # needs to pass the NL_PASSWORD_VERSION structure within the
2354 # 512 bytes and a 2 bytes confounder is required.
2356 def random_trust_secret(length):
2357 pw = samba.generate_random_machine_password(length//2, length//2)
2358 return string_to_byte_array(pw.encode('utf-16-le'))
2360 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2361 incoming_secret = random_trust_secret(240)
2362 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2363 outgoing_secret = random_trust_secret(240)
2365 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2366 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2368 remote_trust_info = lsa.TrustDomainInfoInfoEx()
2369 remote_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2370 remote_trust_info.trust_direction = 0
2371 if trust_direction == "both":
2372 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2373 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2374 elif trust_direction == "incoming":
2375 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2376 elif trust_direction == "outgoing":
2377 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2378 remote_trust_info.trust_attributes = 0
2379 if cross_organisation:
2380 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2382 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2383 if trust_type == "forest":
2384 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2386 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2387 if treat_as_external:
2388 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2390 local_server = self.setup_local_server(sambaopts, localdcopts)
2392 local_lsa = self.new_local_lsa_connection()
2393 except RuntimeError as error:
2394 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2397 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2398 except RuntimeError as error:
2399 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2401 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2402 local_lsa_info.name.string,
2403 local_lsa_info.dns_domain.string,
2404 local_lsa_info.sid))
2407 remote_server = self.setup_remote_server(credopts, domain)
2408 except RuntimeError as error:
2409 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2412 remote_lsa = self.new_remote_lsa_connection()
2413 except RuntimeError as error:
2414 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2417 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2418 except RuntimeError as error:
2419 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2421 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2422 remote_lsa_info.name.string,
2423 remote_lsa_info.dns_domain.string,
2424 remote_lsa_info.sid))
2426 local_trust_info.domain_name.string = remote_lsa_info.dns_domain.string
2427 local_trust_info.netbios_name.string = remote_lsa_info.name.string
2428 local_trust_info.sid = remote_lsa_info.sid
2430 if remote_trust_info:
2431 remote_trust_info.domain_name.string = local_lsa_info.dns_domain.string
2432 remote_trust_info.netbios_name.string = local_lsa_info.name.string
2433 remote_trust_info.sid = local_lsa_info.sid
2436 lsaString.string = local_trust_info.domain_name.string
2437 local_old_netbios = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2438 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2439 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2440 except NTSTATUSError as error:
2441 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2442 raise self.LocalRuntimeError(self, error,
2443 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2447 lsaString.string = local_trust_info.netbios_name.string
2448 local_old_dns = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2449 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2450 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2451 except NTSTATUSError as error:
2452 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2453 raise self.LocalRuntimeError(self, error,
2454 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2457 if remote_trust_info:
2459 lsaString.string = remote_trust_info.domain_name.string
2460 remote_old_netbios = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2461 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2462 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2463 except NTSTATUSError as error:
2464 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2465 raise self.RemoteRuntimeError(self, error,
2466 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2470 lsaString.string = remote_trust_info.netbios_name.string
2471 remote_old_dns = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2472 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2473 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2474 except NTSTATUSError as error:
2475 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2476 raise self.RemoteRuntimeError(self, error,
2477 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2481 local_netlogon = self.new_local_netlogon_connection()
2482 except RuntimeError as error:
2483 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2486 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
2487 except RuntimeError as error:
2488 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
2490 if remote_trust_info:
2492 remote_netlogon = self.new_remote_netlogon_connection()
2493 except RuntimeError as error:
2494 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2497 remote_netlogon_info = self.get_netlogon_dc_info(remote_netlogon, remote_server)
2498 except RuntimeError as error:
2499 raise self.RemoteRuntimeError(self, error, "failed to get netlogon dc info")
2501 def generate_AuthInOutBlob(secret, update_time):
2503 blob = drsblobs.trustAuthInOutBlob()
2508 clear = drsblobs.AuthInfoClear()
2509 clear.size = len(secret)
2510 clear.password = secret
2512 info = drsblobs.AuthenticationInformation()
2513 info.LastUpdateTime = samba.unix2nttime(update_time)
2514 info.AuthType = lsa.TRUST_AUTH_TYPE_CLEAR
2515 info.AuthInfo = clear
2517 array = drsblobs.AuthenticationInformationArray()
2519 array.array = [info]
2521 blob = drsblobs.trustAuthInOutBlob()
2523 blob.current = array
2527 def generate_AuthInfoInternal(session_key, incoming=None, outgoing=None):
2528 confounder = [0] * 512
2529 for i in range(len(confounder)):
2530 confounder[i] = random.randint(0, 255)
2532 trustpass = drsblobs.trustDomainPasswords()
2534 trustpass.confounder = confounder
2535 trustpass.outgoing = outgoing
2536 trustpass.incoming = incoming
2538 trustpass_blob = ndr_pack(trustpass)
2540 encrypted_trustpass = arcfour_encrypt(session_key, trustpass_blob)
2542 auth_blob = lsa.DATA_BUF2()
2543 auth_blob.size = len(encrypted_trustpass)
2544 auth_blob.data = string_to_byte_array(encrypted_trustpass)
2546 auth_info = lsa.TrustDomainInfoAuthInfoInternal()
2547 auth_info.auth_blob = auth_blob
2551 update_time = samba.current_unix_time()
2552 incoming_blob = generate_AuthInOutBlob(incoming_secret, update_time)
2553 outgoing_blob = generate_AuthInOutBlob(outgoing_secret, update_time)
2555 local_tdo_handle = None
2556 remote_tdo_handle = None
2558 local_auth_info = generate_AuthInfoInternal(local_lsa.session_key,
2559 incoming=incoming_blob,
2560 outgoing=outgoing_blob)
2561 if remote_trust_info:
2562 remote_auth_info = generate_AuthInfoInternal(remote_lsa.session_key,
2563 incoming=outgoing_blob,
2564 outgoing=incoming_blob)
2567 if remote_trust_info:
2568 self.outf.write("Creating remote TDO.\n")
2569 current_request = { "location": "remote", "name": "CreateTrustedDomainEx2"}
2570 remote_tdo_handle = remote_lsa.CreateTrustedDomainEx2(remote_policy,
2573 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2574 self.outf.write("Remote TDO created.\n")
2576 self.outf.write("Setting supported encryption types on remote TDO.\n")
2577 current_request = { "location": "remote", "name": "SetInformationTrustedDomain"}
2578 remote_lsa.SetInformationTrustedDomain(remote_tdo_handle,
2579 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2582 self.outf.write("Creating local TDO.\n")
2583 current_request = { "location": "local", "name": "CreateTrustedDomainEx2"}
2584 local_tdo_handle = local_lsa.CreateTrustedDomainEx2(local_policy,
2587 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2588 self.outf.write("Local TDO created\n")
2590 self.outf.write("Setting supported encryption types on local TDO.\n")
2591 current_request = { "location": "local", "name": "SetInformationTrustedDomain"}
2592 local_lsa.SetInformationTrustedDomain(local_tdo_handle,
2593 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2595 except RuntimeError as error:
2596 self.outf.write("Error: %s failed %sly - cleaning up\n" % (
2597 current_request['name'], current_request['location']))
2598 if remote_tdo_handle:
2599 self.outf.write("Deleting remote TDO.\n")
2600 remote_lsa.DeleteObject(remote_tdo_handle)
2601 remote_tdo_handle = None
2602 if local_tdo_handle:
2603 self.outf.write("Deleting local TDO.\n")
2604 local_lsa.DeleteObject(local_tdo_handle)
2605 local_tdo_handle = None
2606 if current_request['location'] is "remote":
2607 raise self.RemoteRuntimeError(self, error, "%s" % (
2608 current_request['name']))
2609 raise self.LocalRuntimeError(self, error, "%s" % (
2610 current_request['name']))
2613 if local_trust_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2614 self.outf.write("Setup local forest trust information...\n")
2616 # get all information about the remote trust
2617 # this triggers netr_GetForestTrustInformation to the remote domain
2618 # and lsaRSetForestTrustInformation() locally, but new top level
2619 # names are disabled by default.
2620 local_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
2621 remote_lsa_info.dns_domain.string,
2622 netlogon.DS_GFTI_UPDATE_TDO)
2623 except RuntimeError as error:
2624 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2627 # here we try to enable all top level names
2628 local_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
2629 remote_lsa_info.dns_domain,
2630 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2633 except RuntimeError as error:
2634 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2636 self.write_forest_trust_info(local_forest_info,
2637 tln=remote_lsa_info.dns_domain.string,
2638 collisions=local_forest_collision)
2640 if remote_trust_info:
2641 self.outf.write("Setup remote forest trust information...\n")
2643 # get all information about the local trust (from the perspective of the remote domain)
2644 # this triggers netr_GetForestTrustInformation to our domain.
2645 # and lsaRSetForestTrustInformation() remotely, but new top level
2646 # names are disabled by default.
2647 remote_forest_info = remote_netlogon.netr_DsRGetForestTrustInformation(remote_netlogon_info.dc_unc,
2648 local_lsa_info.dns_domain.string,
2649 netlogon.DS_GFTI_UPDATE_TDO)
2650 except RuntimeError as error:
2651 raise self.RemoteRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2654 # here we try to enable all top level names
2655 remote_forest_collision = remote_lsa.lsaRSetForestTrustInformation(remote_policy,
2656 local_lsa_info.dns_domain,
2657 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2660 except RuntimeError as error:
2661 raise self.RemoteRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2663 self.write_forest_trust_info(remote_forest_info,
2664 tln=local_lsa_info.dns_domain.string,
2665 collisions=remote_forest_collision)
2667 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2668 self.outf.write("Validating outgoing trust...\n")
2670 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_netlogon_info.dc_unc,
2671 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2673 remote_lsa_info.dns_domain.string)
2674 except RuntimeError as error:
2675 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2677 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2678 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2680 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2681 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2682 local_trust_verify.trusted_dc_name,
2683 local_trust_verify.tc_connection_status[1],
2684 local_trust_verify.pdc_connection_status[1])
2686 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2687 local_trust_verify.trusted_dc_name,
2688 local_trust_verify.tc_connection_status[1],
2689 local_trust_verify.pdc_connection_status[1])
2691 if local_trust_status != werror.WERR_SUCCESS or local_conn_status != werror.WERR_SUCCESS:
2692 raise CommandError(local_validation)
2694 self.outf.write("OK: %s\n" % local_validation)
2696 if remote_trust_info:
2697 if remote_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2698 self.outf.write("Validating incoming trust...\n")
2700 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_netlogon_info.dc_unc,
2701 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2703 local_lsa_info.dns_domain.string)
2704 except RuntimeError as error:
2705 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2707 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2708 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2710 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2711 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2712 remote_trust_verify.trusted_dc_name,
2713 remote_trust_verify.tc_connection_status[1],
2714 remote_trust_verify.pdc_connection_status[1])
2716 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2717 remote_trust_verify.trusted_dc_name,
2718 remote_trust_verify.tc_connection_status[1],
2719 remote_trust_verify.pdc_connection_status[1])
2721 if remote_trust_status != werror.WERR_SUCCESS or remote_conn_status != werror.WERR_SUCCESS:
2722 raise CommandError(remote_validation)
2724 self.outf.write("OK: %s\n" % remote_validation)
2726 if remote_tdo_handle is not None:
2728 remote_lsa.Close(remote_tdo_handle)
2729 except RuntimeError as error:
2731 remote_tdo_handle = None
2732 if local_tdo_handle is not None:
2734 local_lsa.Close(local_tdo_handle)
2735 except RuntimeError as error:
2737 local_tdo_handle = None
2739 self.outf.write("Success.\n")
2742 class cmd_domain_trust_delete(DomainTrustCommand):
2743 """Delete a domain trust."""
2745 synopsis = "%prog DOMAIN [options]"
2747 takes_optiongroups = {
2748 "sambaopts": options.SambaOptions,
2749 "versionopts": options.VersionOptions,
2750 "credopts": options.CredentialsOptions,
2751 "localdcopts": LocalDCCredentialsOptions,
2755 Option("--delete-location", type="choice", metavar="LOCATION",
2756 choices=["local", "both"],
2757 help="Where to delete the trusted domain object: 'local' or 'both'.",
2758 dest='delete_location',
2762 takes_args = ["domain"]
2764 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2765 delete_location=None):
2767 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2768 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2769 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2771 if delete_location == "local":
2772 remote_policy_access = None
2774 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2775 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2776 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2778 local_server = self.setup_local_server(sambaopts, localdcopts)
2780 local_lsa = self.new_local_lsa_connection()
2781 except RuntimeError as error:
2782 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2785 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2786 except RuntimeError as error:
2787 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2789 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2790 local_lsa_info.name.string,
2791 local_lsa_info.dns_domain.string,
2792 local_lsa_info.sid))
2794 local_tdo_info = None
2795 local_tdo_handle = None
2796 remote_tdo_info = None
2797 remote_tdo_handle = None
2799 lsaString = lsa.String()
2801 lsaString.string = domain
2802 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2803 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2804 except NTSTATUSError as error:
2805 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2806 raise CommandError("Failed to find trust for domain '%s'" % domain)
2807 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2810 if remote_policy_access is not None:
2812 remote_server = self.setup_remote_server(credopts, domain)
2813 except RuntimeError as error:
2814 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2817 remote_lsa = self.new_remote_lsa_connection()
2818 except RuntimeError as error:
2819 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2822 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2823 except RuntimeError as error:
2824 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2826 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2827 remote_lsa_info.name.string,
2828 remote_lsa_info.dns_domain.string,
2829 remote_lsa_info.sid))
2831 if remote_lsa_info.sid != local_tdo_info.sid or \
2832 remote_lsa_info.name.string != local_tdo_info.netbios_name.string or \
2833 remote_lsa_info.dns_domain.string != local_tdo_info.domain_name.string:
2834 raise CommandError("LocalTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2835 local_tdo_info.netbios_name.string,
2836 local_tdo_info.domain_name.string,
2837 local_tdo_info.sid))
2840 lsaString.string = local_lsa_info.dns_domain.string
2841 remote_tdo_info = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2842 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2843 except NTSTATUSError as error:
2844 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2845 raise self.RemoteRuntimeError(self, error, "QueryTrustedDomainInfoByName(%s)" % (
2849 if remote_tdo_info is not None:
2850 if local_lsa_info.sid != remote_tdo_info.sid or \
2851 local_lsa_info.name.string != remote_tdo_info.netbios_name.string or \
2852 local_lsa_info.dns_domain.string != remote_tdo_info.domain_name.string:
2853 raise CommandError("RemoteTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2854 remote_tdo_info.netbios_name.string,
2855 remote_tdo_info.domain_name.string,
2856 remote_tdo_info.sid))
2858 if local_tdo_info is not None:
2860 lsaString.string = local_tdo_info.domain_name.string
2861 local_tdo_handle = local_lsa.OpenTrustedDomainByName(local_policy,
2863 security.SEC_STD_DELETE)
2864 except RuntimeError as error:
2865 raise self.LocalRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2868 local_lsa.DeleteObject(local_tdo_handle)
2869 local_tdo_handle = None
2871 if remote_tdo_info is not None:
2873 lsaString.string = remote_tdo_info.domain_name.string
2874 remote_tdo_handle = remote_lsa.OpenTrustedDomainByName(remote_policy,
2876 security.SEC_STD_DELETE)
2877 except RuntimeError as error:
2878 raise self.RemoteRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2881 if remote_tdo_handle is not None:
2883 remote_lsa.DeleteObject(remote_tdo_handle)
2884 remote_tdo_handle = None
2885 self.outf.write("RemoteTDO deleted.\n")
2886 except RuntimeError as error:
2887 self.outf.write("%s\n" % self.RemoteRuntimeError(self, error, "DeleteObject() failed"))
2889 if local_tdo_handle is not None:
2891 local_lsa.DeleteObject(local_tdo_handle)
2892 local_tdo_handle = None
2893 self.outf.write("LocalTDO deleted.\n")
2894 except RuntimeError as error:
2895 self.outf.write("%s\n" % self.LocalRuntimeError(self, error, "DeleteObject() failed"))
2899 class cmd_domain_trust_validate(DomainTrustCommand):
2900 """Validate a domain trust."""
2902 synopsis = "%prog DOMAIN [options]"
2904 takes_optiongroups = {
2905 "sambaopts": options.SambaOptions,
2906 "versionopts": options.VersionOptions,
2907 "credopts": options.CredentialsOptions,
2908 "localdcopts": LocalDCCredentialsOptions,
2912 Option("--validate-location", type="choice", metavar="LOCATION",
2913 choices=["local", "both"],
2914 help="Where to validate the trusted domain object: 'local' or 'both'.",
2915 dest='validate_location',
2919 takes_args = ["domain"]
2921 def run(self, domain, sambaopts=None, versionopts=None, credopts=None, localdcopts=None,
2922 validate_location=None):
2924 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2926 local_server = self.setup_local_server(sambaopts, localdcopts)
2928 local_lsa = self.new_local_lsa_connection()
2929 except RuntimeError as error:
2930 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2933 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2934 except RuntimeError as error:
2935 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2937 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2938 local_lsa_info.name.string,
2939 local_lsa_info.dns_domain.string,
2940 local_lsa_info.sid))
2943 lsaString = lsa.String()
2944 lsaString.string = domain
2945 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2946 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2947 except NTSTATUSError as error:
2948 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2949 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2951 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
2953 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
2954 local_tdo_info.netbios_name.string,
2955 local_tdo_info.domain_name.string,
2956 local_tdo_info.sid))
2959 local_netlogon = self.new_local_netlogon_connection()
2960 except RuntimeError as error:
2961 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2964 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_server,
2965 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2967 local_tdo_info.domain_name.string)
2968 except RuntimeError as error:
2969 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2971 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2972 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2974 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2975 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2976 local_trust_verify.trusted_dc_name,
2977 local_trust_verify.tc_connection_status[1],
2978 local_trust_verify.pdc_connection_status[1])
2980 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2981 local_trust_verify.trusted_dc_name,
2982 local_trust_verify.tc_connection_status[1],
2983 local_trust_verify.pdc_connection_status[1])
2985 if local_trust_status != werror.WERR_SUCCESS or local_conn_status != werror.WERR_SUCCESS:
2986 raise CommandError(local_validation)
2988 self.outf.write("OK: %s\n" % local_validation)
2991 server = local_trust_verify.trusted_dc_name.replace('\\', '')
2992 domain_and_server = "%s\\%s" % (local_tdo_info.domain_name.string, server)
2993 local_trust_rediscover = local_netlogon.netr_LogonControl2Ex(local_server,
2994 netlogon.NETLOGON_CONTROL_REDISCOVER,
2997 except RuntimeError as error:
2998 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
3000 local_conn_status = self._uint32(local_trust_rediscover.tc_connection_status[0])
3001 local_rediscover = "LocalRediscover: DC[%s] CONNECTION[%s]" % (
3002 local_trust_rediscover.trusted_dc_name,
3003 local_trust_rediscover.tc_connection_status[1])
3005 if local_conn_status != werror.WERR_SUCCESS:
3006 raise CommandError(local_rediscover)
3008 self.outf.write("OK: %s\n" % local_rediscover)
3010 if validate_location != "local":
3012 remote_server = self.setup_remote_server(credopts, domain, require_pdc=False)
3013 except RuntimeError as error:
3014 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
3017 remote_netlogon = self.new_remote_netlogon_connection()
3018 except RuntimeError as error:
3019 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
3022 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_server,
3023 netlogon.NETLOGON_CONTROL_TC_VERIFY,
3025 local_lsa_info.dns_domain.string)
3026 except RuntimeError as error:
3027 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
3029 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
3030 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
3032 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
3033 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
3034 remote_trust_verify.trusted_dc_name,
3035 remote_trust_verify.tc_connection_status[1],
3036 remote_trust_verify.pdc_connection_status[1])
3038 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
3039 remote_trust_verify.trusted_dc_name,
3040 remote_trust_verify.tc_connection_status[1],
3041 remote_trust_verify.pdc_connection_status[1])
3043 if remote_trust_status != werror.WERR_SUCCESS or remote_conn_status != werror.WERR_SUCCESS:
3044 raise CommandError(remote_validation)
3046 self.outf.write("OK: %s\n" % remote_validation)
3049 server = remote_trust_verify.trusted_dc_name.replace('\\', '')
3050 domain_and_server = "%s\\%s" % (local_lsa_info.dns_domain.string, server)
3051 remote_trust_rediscover = remote_netlogon.netr_LogonControl2Ex(remote_server,
3052 netlogon.NETLOGON_CONTROL_REDISCOVER,
3055 except RuntimeError as error:
3056 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
3058 remote_conn_status = self._uint32(remote_trust_rediscover.tc_connection_status[0])
3060 remote_rediscover = "RemoteRediscover: DC[%s] CONNECTION[%s]" % (
3061 remote_trust_rediscover.trusted_dc_name,
3062 remote_trust_rediscover.tc_connection_status[1])
3064 if remote_conn_status != werror.WERR_SUCCESS:
3065 raise CommandError(remote_rediscover)
3067 self.outf.write("OK: %s\n" % remote_rediscover)
3071 class cmd_domain_trust_namespaces(DomainTrustCommand):
3072 """Manage forest trust namespaces."""
3074 synopsis = "%prog [DOMAIN] [options]"
3076 takes_optiongroups = {
3077 "sambaopts": options.SambaOptions,
3078 "versionopts": options.VersionOptions,
3079 "localdcopts": LocalDCCredentialsOptions,
3083 Option("--refresh", type="choice", metavar="check|store",
3084 choices=["check", "store", None],
3085 help="List and maybe store refreshed forest trust information: 'check' or 'store'.",
3088 Option("--enable-all", action="store_true",
3089 help="Try to update disabled entries, not allowed with --refresh=check.",
3092 Option("--enable-tln", action="append", metavar='DNSDOMAIN',
3093 help="Enable a top level name entry. Can be specified multiple times.",
3096 Option("--disable-tln", action="append", metavar='DNSDOMAIN',
3097 help="Disable a top level name entry. Can be specified multiple times.",
3100 Option("--add-tln-ex", action="append", metavar='DNSDOMAIN',
3101 help="Add a top level exclusion entry. Can be specified multiple times.",
3104 Option("--delete-tln-ex", action="append", metavar='DNSDOMAIN',
3105 help="Delete a top level exclusion entry. Can be specified multiple times.",
3106 dest='delete_tln_ex',
3108 Option("--enable-nb", action="append", metavar='NETBIOSDOMAIN',
3109 help="Enable a netbios name in a domain entry. Can be specified multiple times.",
3112 Option("--disable-nb", action="append", metavar='NETBIOSDOMAIN',
3113 help="Disable a netbios name in a domain entry. Can be specified multiple times.",
3116 Option("--enable-sid", action="append", metavar='DOMAINSID',
3117 help="Enable a SID in a domain entry. Can be specified multiple times.",
3118 dest='enable_sid_str',
3120 Option("--disable-sid", action="append", metavar='DOMAINSID',
3121 help="Disable a SID in a domain entry. Can be specified multiple times.",
3122 dest='disable_sid_str',
3124 Option("--add-upn-suffix", action="append", metavar='DNSDOMAIN',
3125 help="Add a new uPNSuffixes attribute for the local forest. Can be specified multiple times.",
3128 Option("--delete-upn-suffix", action="append", metavar='DNSDOMAIN',
3129 help="Delete an existing uPNSuffixes attribute of the local forest. Can be specified multiple times.",
3132 Option("--add-spn-suffix", action="append", metavar='DNSDOMAIN',
3133 help="Add a new msDS-SPNSuffixes attribute for the local forest. Can be specified multiple times.",
3136 Option("--delete-spn-suffix", action="append", metavar='DNSDOMAIN',
3137 help="Delete an existing msDS-SPNSuffixes attribute of the local forest. Can be specified multiple times.",
3142 takes_args = ["domain?"]
3144 def run(self, domain=None, sambaopts=None, localdcopts=None, versionopts=None,
3145 refresh=None, enable_all=False,
3146 enable_tln=[], disable_tln=[], add_tln_ex=[], delete_tln_ex=[],
3147 enable_sid_str=[], disable_sid_str=[], enable_nb=[], disable_nb=[],
3148 add_upn=[], delete_upn=[], add_spn=[], delete_spn=[]):
3150 require_update = False
3153 if refresh == "store":
3154 raise CommandError("--refresh=%s not allowed without DOMAIN" % refresh)
3157 raise CommandError("--enable-all not allowed without DOMAIN")
3159 if len(enable_tln) > 0:
3160 raise CommandError("--enable-tln not allowed without DOMAIN")
3161 if len(disable_tln) > 0:
3162 raise CommandError("--disable-tln not allowed without DOMAIN")
3164 if len(add_tln_ex) > 0:
3165 raise CommandError("--add-tln-ex not allowed without DOMAIN")
3166 if len(delete_tln_ex) > 0:
3167 raise CommandError("--delete-tln-ex not allowed without DOMAIN")
3169 if len(enable_nb) > 0:
3170 raise CommandError("--enable-nb not allowed without DOMAIN")
3171 if len(disable_nb) > 0:
3172 raise CommandError("--disable-nb not allowed without DOMAIN")
3174 if len(enable_sid_str) > 0:
3175 raise CommandError("--enable-sid not allowed without DOMAIN")
3176 if len(disable_sid_str) > 0:
3177 raise CommandError("--disable-sid not allowed without DOMAIN")
3179 if len(add_upn) > 0:
3181 if not n.startswith("*."):
3183 raise CommandError("value[%s] specified for --add-upn-suffix should not include with '*.'" % n)
3184 require_update = True
3185 if len(delete_upn) > 0:
3186 for n in delete_upn:
3187 if not n.startswith("*."):
3189 raise CommandError("value[%s] specified for --delete-upn-suffix should not include with '*.'" % n)
3190 require_update = True
3192 for d in delete_upn:
3193 if a.lower() != d.lower():
3195 raise CommandError("value[%s] specified for --add-upn-suffix and --delete-upn-suffix" % a)
3197 if len(add_spn) > 0:
3199 if not n.startswith("*."):
3201 raise CommandError("value[%s] specified for --add-spn-suffix should not include with '*.'" % n)
3202 require_update = True
3203 if len(delete_spn) > 0:
3204 for n in delete_spn:
3205 if not n.startswith("*."):
3207 raise CommandError("value[%s] specified for --delete-spn-suffix should not include with '*.'" % n)
3208 require_update = True
3210 for d in delete_spn:
3211 if a.lower() != d.lower():
3213 raise CommandError("value[%s] specified for --add-spn-suffix and --delete-spn-suffix" % a)
3215 if len(add_upn) > 0:
3216 raise CommandError("--add-upn-suffix not allowed together with DOMAIN")
3217 if len(delete_upn) > 0:
3218 raise CommandError("--delete-upn-suffix not allowed together with DOMAIN")
3219 if len(add_spn) > 0:
3220 raise CommandError("--add-spn-suffix not allowed together with DOMAIN")
3221 if len(delete_spn) > 0:
3222 raise CommandError("--delete-spn-suffix not allowed together with DOMAIN")
3224 if refresh is not None:
3225 if refresh == "store":
3226 require_update = True
3228 if enable_all and refresh != "store":
3229 raise CommandError("--enable-all not allowed together with --refresh=%s" % refresh)
3231 if len(enable_tln) > 0:
3232 raise CommandError("--enable-tln not allowed together with --refresh")
3233 if len(disable_tln) > 0:
3234 raise CommandError("--disable-tln not allowed together with --refresh")
3236 if len(add_tln_ex) > 0:
3237 raise CommandError("--add-tln-ex not allowed together with --refresh")
3238 if len(delete_tln_ex) > 0:
3239 raise CommandError("--delete-tln-ex not allowed together with --refresh")
3241 if len(enable_nb) > 0:
3242 raise CommandError("--enable-nb not allowed together with --refresh")
3243 if len(disable_nb) > 0:
3244 raise CommandError("--disable-nb not allowed together with --refresh")
3246 if len(enable_sid_str) > 0:
3247 raise CommandError("--enable-sid not allowed together with --refresh")
3248 if len(disable_sid_str) > 0:
3249 raise CommandError("--disable-sid not allowed together with --refresh")
3252 require_update = True
3254 if len(enable_tln) > 0:
3255 raise CommandError("--enable-tln not allowed together with --enable-all")
3257 if len(enable_nb) > 0:
3258 raise CommandError("--enable-nb not allowed together with --enable-all")
3260 if len(enable_sid_str) > 0:
3261 raise CommandError("--enable-sid not allowed together with --enable-all")
3263 if len(enable_tln) > 0:
3264 require_update = True
3265 if len(disable_tln) > 0:
3266 require_update = True
3267 for e in enable_tln:
3268 for d in disable_tln:
3269 if e.lower() != d.lower():
3271 raise CommandError("value[%s] specified for --enable-tln and --disable-tln" % e)
3273 if len(add_tln_ex) > 0:
3274 for n in add_tln_ex:
3275 if not n.startswith("*."):
3277 raise CommandError("value[%s] specified for --add-tln-ex should not include with '*.'" % n)
3278 require_update = True
3279 if len(delete_tln_ex) > 0:
3280 for n in delete_tln_ex:
3281 if not n.startswith("*."):
3283 raise CommandError("value[%s] specified for --delete-tln-ex should not include with '*.'" % n)
3284 require_update = True
3285 for a in add_tln_ex:
3286 for d in delete_tln_ex:
3287 if a.lower() != d.lower():
3289 raise CommandError("value[%s] specified for --add-tln-ex and --delete-tln-ex" % a)
3291 if len(enable_nb) > 0:
3292 require_update = True
3293 if len(disable_nb) > 0:
3294 require_update = True
3296 for d in disable_nb:
3297 if e.upper() != d.upper():
3299 raise CommandError("value[%s] specified for --enable-nb and --disable-nb" % e)
3302 for s in enable_sid_str:
3304 sid = security.dom_sid(s)
3305 except TypeError as error:
3306 raise CommandError("value[%s] specified for --enable-sid is not a valid SID" % s)
3307 enable_sid.append(sid)
3309 for s in disable_sid_str:
3311 sid = security.dom_sid(s)
3312 except TypeError as error:
3313 raise CommandError("value[%s] specified for --disable-sid is not a valid SID" % s)
3314 disable_sid.append(sid)
3315 if len(enable_sid) > 0:
3316 require_update = True
3317 if len(disable_sid) > 0:
3318 require_update = True
3319 for e in enable_sid:
3320 for d in disable_sid:
3323 raise CommandError("value[%s] specified for --enable-sid and --disable-sid" % e)
3325 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
3327 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
3329 local_server = self.setup_local_server(sambaopts, localdcopts)
3331 local_lsa = self.new_local_lsa_connection()
3332 except RuntimeError as error:
3333 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
3336 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
3337 except RuntimeError as error:
3338 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
3340 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
3341 local_lsa_info.name.string,
3342 local_lsa_info.dns_domain.string,
3343 local_lsa_info.sid))
3347 local_netlogon = self.new_local_netlogon_connection()
3348 except RuntimeError as error:
3349 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3352 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3353 except RuntimeError as error:
3354 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3356 if local_netlogon_info.domain_name != local_netlogon_info.forest_name:
3357 raise CommandError("The local domain [%s] is not the forest root [%s]" % (
3358 local_netlogon_info.domain_name,
3359 local_netlogon_info.forest_name))
3362 # get all information about our own forest
3363 own_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3365 except RuntimeError as error:
3366 if self.check_runtime_error(error, werror.WERR_RPC_S_PROCNUM_OUT_OF_RANGE):
3367 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3370 if self.check_runtime_error(error, werror.WERR_INVALID_FUNCTION):
3371 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3374 if self.check_runtime_error(error, werror.WERR_NERR_ACFNOTLOADED):
3375 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3378 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3380 self.outf.write("Own forest trust information...\n")
3381 self.write_forest_trust_info(own_forest_info,
3382 tln=local_lsa_info.dns_domain.string)
3385 local_samdb = self.new_local_ldap_connection()
3386 except RuntimeError as error:
3387 raise self.LocalRuntimeError(self, error, "failed to connect to SamDB")
3389 local_partitions_dn = "CN=Partitions,%s" % str(local_samdb.get_config_basedn())
3390 attrs = ['uPNSuffixes', 'msDS-SPNSuffixes']
3392 msgs = local_samdb.search(base=local_partitions_dn,
3393 scope=ldb.SCOPE_BASE,
3394 expression="(objectClass=crossRefContainer)",
3396 stored_msg = msgs[0]
3397 except ldb.LdbError as error:
3398 raise self.LocalLdbError(self, error, "failed to search partition dn")
3400 stored_upn_vals = []
3401 if 'uPNSuffixes' in stored_msg:
3402 stored_upn_vals.extend(stored_msg['uPNSuffixes'])
3404 stored_spn_vals = []
3405 if 'msDS-SPNSuffixes' in stored_msg:
3406 stored_spn_vals.extend(stored_msg['msDS-SPNSuffixes'])
3408 self.outf.write("Stored uPNSuffixes attributes[%d]:\n" % len(stored_upn_vals))
3409 for v in stored_upn_vals:
3410 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3411 self.outf.write("Stored msDS-SPNSuffixes attributes[%d]:\n" % len(stored_spn_vals))
3412 for v in stored_spn_vals:
3413 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3415 if not require_update:
3419 update_upn_vals = []
3420 update_upn_vals.extend(stored_upn_vals)
3423 update_spn_vals = []
3424 update_spn_vals.extend(stored_spn_vals)
3427 for i, v in enumerate(update_upn_vals):
3428 if v.lower() == upn.lower():
3429 raise CommandError("Entry already present for "
3430 "value[%s] specified for "
3431 "--add-upn-suffix" % upn)
3432 update_upn_vals.append(upn)
3435 for upn in delete_upn:
3437 for i, v in enumerate(update_upn_vals):
3438 if v.lower() != upn.lower():
3443 raise CommandError("Entry not found for value[%s] specified for --delete-upn-suffix" % upn)
3445 update_upn_vals.pop(idx)
3449 for i, v in enumerate(update_spn_vals):
3450 if v.lower() == spn.lower():
3451 raise CommandError("Entry already present for "
3452 "value[%s] specified for "
3453 "--add-spn-suffix" % spn)
3454 update_spn_vals.append(spn)
3457 for spn in delete_spn:
3459 for i, v in enumerate(update_spn_vals):
3460 if v.lower() != spn.lower():
3465 raise CommandError("Entry not found for value[%s] specified for --delete-spn-suffix" % spn)
3467 update_spn_vals.pop(idx)
3470 self.outf.write("Update uPNSuffixes attributes[%d]:\n" % len(update_upn_vals))
3471 for v in update_upn_vals:
3472 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3473 self.outf.write("Update msDS-SPNSuffixes attributes[%d]:\n" % len(update_spn_vals))
3474 for v in update_spn_vals:
3475 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3477 update_msg = ldb.Message()
3478 update_msg.dn = stored_msg.dn
3481 update_msg['uPNSuffixes'] = ldb.MessageElement(update_upn_vals,
3482 ldb.FLAG_MOD_REPLACE,
3485 update_msg['msDS-SPNSuffixes'] = ldb.MessageElement(update_spn_vals,
3486 ldb.FLAG_MOD_REPLACE,
3489 local_samdb.modify(update_msg)
3490 except ldb.LdbError as error:
3491 raise self.LocalLdbError(self, error, "failed to update partition dn")
3494 stored_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3496 except RuntimeError as error:
3497 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3499 self.outf.write("Stored forest trust information...\n")
3500 self.write_forest_trust_info(stored_forest_info,
3501 tln=local_lsa_info.dns_domain.string)
3505 lsaString = lsa.String()
3506 lsaString.string = domain
3507 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
3508 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
3509 except NTSTATUSError as error:
3510 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
3511 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
3513 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
3515 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
3516 local_tdo_info.netbios_name.string,
3517 local_tdo_info.domain_name.string,
3518 local_tdo_info.sid))
3520 if not local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
3521 raise CommandError("trusted domain object for domain [%s] is not marked as FOREST_TRANSITIVE." % domain)
3523 if refresh is not None:
3525 local_netlogon = self.new_local_netlogon_connection()
3526 except RuntimeError as error:
3527 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3530 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3531 except RuntimeError as error:
3532 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3534 lsa_update_check = 1
3535 if refresh == "store":
3536 netlogon_update_tdo = netlogon.DS_GFTI_UPDATE_TDO
3538 lsa_update_check = 0
3540 netlogon_update_tdo = 0
3543 # get all information about the remote trust
3544 # this triggers netr_GetForestTrustInformation to the remote domain
3545 # and lsaRSetForestTrustInformation() locally, but new top level
3546 # names are disabled by default.
3547 fresh_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3548 local_tdo_info.domain_name.string,
3549 netlogon_update_tdo)
3550 except RuntimeError as error:
3551 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3554 fresh_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3555 local_tdo_info.domain_name,
3556 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3559 except RuntimeError as error:
3560 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3562 self.outf.write("Fresh forest trust information...\n")
3563 self.write_forest_trust_info(fresh_forest_info,
3564 tln=local_tdo_info.domain_name.string,
3565 collisions=fresh_forest_collision)
3567 if refresh == "store":
3569 lsaString = lsa.String()
3570 lsaString.string = local_tdo_info.domain_name.string
3571 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3573 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3574 except RuntimeError as error:
3575 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3577 self.outf.write("Stored forest trust information...\n")
3578 self.write_forest_trust_info(stored_forest_info,
3579 tln=local_tdo_info.domain_name.string)
3584 # The none --refresh path
3588 lsaString = lsa.String()
3589 lsaString.string = local_tdo_info.domain_name.string
3590 local_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3592 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3593 except RuntimeError as error:
3594 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3596 self.outf.write("Local forest trust information...\n")
3597 self.write_forest_trust_info(local_forest_info,
3598 tln=local_tdo_info.domain_name.string)
3600 if not require_update:
3604 entries.extend(local_forest_info.entries)
3605 update_forest_info = lsa.ForestTrustInformation()
3606 update_forest_info.count = len(entries)
3607 update_forest_info.entries = entries
3610 for i, r in enumerate(update_forest_info.entries):
3611 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3613 if update_forest_info.entries[i].flags == 0:
3615 update_forest_info.entries[i].time = 0
3616 update_forest_info.entries[i].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3617 for i, r in enumerate(update_forest_info.entries):
3618 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3620 if update_forest_info.entries[i].flags == 0:
3622 update_forest_info.entries[i].time = 0
3623 update_forest_info.entries[i].flags &= ~lsa.LSA_NB_DISABLED_MASK
3624 update_forest_info.entries[i].flags &= ~lsa.LSA_SID_DISABLED_MASK
3626 for tln in enable_tln:
3628 for i, r in enumerate(update_forest_info.entries):
3629 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3631 if r.forest_trust_data.string.lower() != tln.lower():
3636 raise CommandError("Entry not found for value[%s] specified for --enable-tln" % tln)
3637 if not update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_MASK:
3638 raise CommandError("Entry found for value[%s] specified for --enable-tln is already enabled" % tln)
3639 update_forest_info.entries[idx].time = 0
3640 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3642 for tln in disable_tln:
3644 for i, r in enumerate(update_forest_info.entries):
3645 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3647 if r.forest_trust_data.string.lower() != tln.lower():
3652 raise CommandError("Entry not found for value[%s] specified for --disable-tln" % tln)
3653 if update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_ADMIN:
3654 raise CommandError("Entry found for value[%s] specified for --disable-tln is already disabled" % tln)
3655 update_forest_info.entries[idx].time = 0
3656 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3657 update_forest_info.entries[idx].flags |= lsa.LSA_TLN_DISABLED_ADMIN
3659 for tln_ex in add_tln_ex:
3661 for i, r in enumerate(update_forest_info.entries):
3662 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3664 if r.forest_trust_data.string.lower() != tln_ex.lower():
3669 raise CommandError("Entry already present for value[%s] specified for --add-tln-ex" % tln_ex)
3671 tln_dot = ".%s" % tln_ex.lower()
3673 for i, r in enumerate(update_forest_info.entries):
3674 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3676 r_dot = ".%s" % r.forest_trust_data.string.lower()
3677 if tln_dot == r_dot:
3678 raise CommandError("TLN entry present for value[%s] specified for --add-tln-ex" % tln_ex)
3679 if not tln_dot.endswith(r_dot):
3685 raise CommandError("No TLN parent present for value[%s] specified for --add-tln-ex" % tln_ex)
3687 r = lsa.ForestTrustRecord()
3688 r.type = lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX
3691 r.forest_trust_data.string = tln_ex
3694 entries.extend(update_forest_info.entries)
3695 entries.insert(idx + 1, r)
3696 update_forest_info.count = len(entries)
3697 update_forest_info.entries = entries
3699 for tln_ex in delete_tln_ex:
3701 for i, r in enumerate(update_forest_info.entries):
3702 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3704 if r.forest_trust_data.string.lower() != tln_ex.lower():
3709 raise CommandError("Entry not found for value[%s] specified for --delete-tln-ex" % tln_ex)
3712 entries.extend(update_forest_info.entries)
3714 update_forest_info.count = len(entries)
3715 update_forest_info.entries = entries
3717 for nb in enable_nb:
3719 for i, r in enumerate(update_forest_info.entries):
3720 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3722 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3727 raise CommandError("Entry not found for value[%s] specified for --enable-nb" % nb)
3728 if not update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_MASK:
3729 raise CommandError("Entry found for value[%s] specified for --enable-nb is already enabled" % nb)
3730 update_forest_info.entries[idx].time = 0
3731 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3733 for nb in disable_nb:
3735 for i, r in enumerate(update_forest_info.entries):
3736 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3738 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3743 raise CommandError("Entry not found for value[%s] specified for --delete-nb" % nb)
3744 if update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_ADMIN:
3745 raise CommandError("Entry found for value[%s] specified for --disable-nb is already disabled" % nb)
3746 update_forest_info.entries[idx].time = 0
3747 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3748 update_forest_info.entries[idx].flags |= lsa.LSA_NB_DISABLED_ADMIN
3750 for sid in enable_sid:
3752 for i, r in enumerate(update_forest_info.entries):
3753 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3755 if r.forest_trust_data.domain_sid != sid:
3760 raise CommandError("Entry not found for value[%s] specified for --enable-sid" % sid)
3761 if not update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_MASK:
3762 raise CommandError("Entry found for value[%s] specified for --enable-sid is already enabled" % nb)
3763 update_forest_info.entries[idx].time = 0
3764 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3766 for sid in disable_sid:
3768 for i, r in enumerate(update_forest_info.entries):
3769 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3771 if r.forest_trust_data.domain_sid != sid:
3776 raise CommandError("Entry not found for value[%s] specified for --delete-sid" % sid)
3777 if update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_ADMIN:
3778 raise CommandError("Entry found for value[%s] specified for --disable-sid is already disabled" % nb)
3779 update_forest_info.entries[idx].time = 0
3780 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3781 update_forest_info.entries[idx].flags |= lsa.LSA_SID_DISABLED_ADMIN
3784 update_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3785 local_tdo_info.domain_name,
3786 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3787 update_forest_info, 0)
3788 except RuntimeError as error:
3789 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3791 self.outf.write("Updated forest trust information...\n")
3792 self.write_forest_trust_info(update_forest_info,
3793 tln=local_tdo_info.domain_name.string,
3794 collisions=update_forest_collision)
3797 lsaString = lsa.String()
3798 lsaString.string = local_tdo_info.domain_name.string
3799 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3801 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3802 except RuntimeError as error:
3803 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3805 self.outf.write("Stored forest trust information...\n")
3806 self.write_forest_trust_info(stored_forest_info,
3807 tln=local_tdo_info.domain_name.string)
3810 class cmd_domain_tombstones_expunge(Command):
3811 """Expunge tombstones from the database.
3813 This command expunges tombstones from the database."""
3814 synopsis = "%prog NC [NC [...]] [options]"
3817 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
3818 metavar="URL", dest="H"),
3819 Option("--current-time",
3820 help="The current time to evaluate the tombstone lifetime from, expressed as YYYY-MM-DD",
3822 Option("--tombstone-lifetime", help="Number of days a tombstone should be preserved for", type=int),
3825 takes_args = ["nc*"]
3827 takes_optiongroups = {
3828 "sambaopts": options.SambaOptions,
3829 "credopts": options.CredentialsOptions,
3830 "versionopts": options.VersionOptions,
3833 def run(self, *ncs, **kwargs):
3834 sambaopts = kwargs.get("sambaopts")
3835 credopts = kwargs.get("credopts")
3836 versionpts = kwargs.get("versionopts")
3838 current_time_string = kwargs.get("current_time")
3839 tombstone_lifetime = kwargs.get("tombstone_lifetime")
3840 lp = sambaopts.get_loadparm()
3841 creds = credopts.get_credentials(lp)
3842 samdb = SamDB(url=H, session_info=system_session(),
3843 credentials=creds, lp=lp)
3845 if current_time_string is not None:
3846 current_time_obj = time.strptime(current_time_string, "%Y-%m-%d")
3847 current_time = long(time.mktime(current_time_obj))
3850 current_time = long(time.time())
3853 res = samdb.search(expression="", base="", scope=ldb.SCOPE_BASE,
3854 attrs=["namingContexts"])
3857 for nc in res[0]["namingContexts"]:
3862 started_transaction = False
3864 samdb.transaction_start()
3865 started_transaction = True
3867 removed_links) = samdb.garbage_collect_tombstones(ncs,
3868 current_time=current_time,
3869 tombstone_lifetime=tombstone_lifetime)
3871 except Exception as err:
3872 if started_transaction:
3873 samdb.transaction_cancel()
3874 raise CommandError("Failed to expunge / garbage collect tombstones", err)
3876 samdb.transaction_commit()
3878 self.outf.write("Removed %d objects and %d links successfully\n"
3879 % (removed_objects, removed_links))
3883 class cmd_domain_trust(SuperCommand):
3884 """Domain and forest trust management."""
3887 subcommands["list"] = cmd_domain_trust_list()
3888 subcommands["show"] = cmd_domain_trust_show()
3889 subcommands["create"] = cmd_domain_trust_create()
3890 subcommands["delete"] = cmd_domain_trust_delete()
3891 subcommands["validate"] = cmd_domain_trust_validate()
3892 subcommands["namespaces"] = cmd_domain_trust_namespaces()
3894 class cmd_domain_tombstones(SuperCommand):
3895 """Domain tombstone and recycled object management."""
3898 subcommands["expunge"] = cmd_domain_tombstones_expunge()
3900 class ldif_schema_update:
3901 """Helper class for applying LDIF schema updates"""
3904 self.is_defunct = False
3905 self.unknown_oid = None
3909 def _ldap_schemaUpdateNow(self, samdb):
3913 add: schemaUpdateNow
3916 samdb.modify_ldif(ldif)
3918 def can_ignore_failure(self, error):
3919 """Checks if we can safely ignore failure to apply an LDIF update"""
3920 (num, errstr) = error.args
3922 # Microsoft has marked objects as defunct that Samba doesn't know about
3923 if num == ldb.ERR_NO_SUCH_OBJECT and self.is_defunct:
3924 print("Defunct object %s doesn't exist, skipping" % self.dn)
3926 elif self.unknown_oid is not None:
3927 print("Skipping unknown OID %s for object %s" %(self.unknown_oid, self.dn))
3932 def apply(self, samdb):
3933 """Applies a single LDIF update to the schema"""
3937 samdb.modify_ldif(self.ldif, controls=['relax:0'])
3938 except ldb.LdbError as e:
3939 if e.args[0] == ldb.ERR_INVALID_ATTRIBUTE_SYNTAX:
3941 # REFRESH after a failed change
3943 # Otherwise the OID-to-attribute mapping in
3944 # _apply_updates_in_file() won't work, because it
3945 # can't lookup the new OID in the schema
3946 self._ldap_schemaUpdateNow(samdb)
3948 samdb.modify_ldif(self.ldif, controls=['relax:0'])
3951 except ldb.LdbError as e:
3952 if self.can_ignore_failure(e):
3955 print("Exception: %s" % e)
3956 print("Encountered while trying to apply the following LDIF")
3957 print("----------------------------------------------------")
3958 print("%s" % self.ldif)
3964 class cmd_domain_schema_upgrade(Command):
3965 """Domain schema upgrading"""
3967 synopsis = "%prog [options]"
3969 takes_optiongroups = {
3970 "sambaopts": options.SambaOptions,
3971 "versionopts": options.VersionOptions,
3972 "credopts": options.CredentialsOptions,
3976 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
3977 metavar="URL", dest="H"),
3978 Option("--quiet", help="Be quiet", action="store_true"),
3979 Option("--verbose", help="Be verbose", action="store_true"),
3980 Option("--schema", type="choice", metavar="SCHEMA",
3981 choices=["2012", "2012_R2"],
3982 help="The schema file to upgrade to. Default is (Windows) 2012_R2.",
3984 Option("--ldf-file", type=str, default=None,
3985 help="Just apply the schema updates in the adprep/.LDF file(s) specified"),
3986 Option("--base-dir", type=str, default=None,
3987 help="Location of ldf files Default is ${SETUPDIR}/adprep.")
3990 def _apply_updates_in_file(self, samdb, ldif_file):
3992 Applies a series of updates specified in an .LDIF file. The .LDIF file
3993 is based on the adprep Schema updates provided by Microsoft.
3996 ldif_op = ldif_schema_update()
3998 # parse the file line by line and work out each update operation to apply
3999 for line in ldif_file:
4001 line = line.rstrip()
4003 # the operations in the .LDIF file are separated by blank lines. If
4004 # we hit a blank line, try to apply the update we've parsed so far
4007 # keep going if we haven't parsed anything yet
4008 if ldif_op.ldif == '':
4011 # Apply the individual change
4012 count += ldif_op.apply(samdb)
4014 # start storing the next operation from scratch again
4015 ldif_op = ldif_schema_update()
4018 # replace the placeholder domain name in the .ldif file with the real domain
4019 if line.upper().endswith('DC=X'):
4020 line = line[:-len('DC=X')] + str(samdb.get_default_basedn())
4021 elif line.upper().endswith('CN=X'):
4022 line = line[:-len('CN=X')] + str(samdb.get_default_basedn())
4024 values = line.split(':')
4026 if values[0].lower() == 'dn':
4027 ldif_op.dn = values[1].strip()
4029 # replace the Windows-specific operation with the Samba one
4030 if values[0].lower() == 'changetype':
4031 line = line.lower().replace(': ntdsschemaadd',
4033 line = line.lower().replace(': ntdsschemamodify',
4036 if values[0].lower() in ['rdnattid', 'subclassof',
4037 'systemposssuperiors',
4039 'systemauxiliaryclass']:
4042 # The Microsoft updates contain some OIDs we don't recognize.
4043 # Query the DB to see if we can work out the OID this update is
4044 # referring to. If we find a match, then replace the OID with
4045 # the ldapDisplayname
4047 res = samdb.search(base=samdb.get_schema_basedn(),
4048 expression="(|(attributeId=%s)(governsId=%s))" %
4050 attrs=['ldapDisplayName'])
4053 ldif_op.unknown_oid = value
4055 display_name = res[0]['ldapDisplayName'][0]
4056 line = line.replace(value, ' ' + display_name)
4058 # Microsoft has marked objects as defunct that Samba doesn't know about
4059 if values[0].lower() == 'isdefunct' and values[1].strip().lower() == 'true':
4060 ldif_op.is_defunct = True
4062 # Samba has added the showInAdvancedViewOnly attribute to all objects,
4063 # so rather than doing an add, we need to do a replace
4064 if values[0].lower() == 'add' and values[1].strip().lower() == 'showinadvancedviewonly':
4065 line = 'replace: showInAdvancedViewOnly'
4067 # Add the line to the current LDIF operation (including the newline
4068 # we stripped off at the start of the loop)
4069 ldif_op.ldif += line + '\n'
4074 def _apply_update(self, samdb, update_file, base_dir):
4075 """Wrapper function for parsing an LDIF file and applying the updates"""
4077 print("Applying %s updates..." % update_file)
4081 ldif_file = open(os.path.join(base_dir, update_file))
4083 count = self._apply_updates_in_file(samdb, ldif_file)
4089 print("%u changes applied" % count)
4093 def run(self, **kwargs):
4094 from samba.ms_schema_markdown import read_ms_markdown
4095 from samba.schema import Schema
4097 updates_allowed_overriden = False
4098 sambaopts = kwargs.get("sambaopts")
4099 credopts = kwargs.get("credopts")
4100 versionpts = kwargs.get("versionopts")
4101 lp = sambaopts.get_loadparm()
4102 creds = credopts.get_credentials(lp)
4104 target_schema = kwargs.get("schema")
4105 ldf_files = kwargs.get("ldf_file")
4106 base_dir = kwargs.get("base_dir")
4110 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
4112 # we're not going to get far if the config doesn't allow schema updates
4113 if lp.get("dsdb:schema update allowed") is None:
4114 lp.set("dsdb:schema update allowed", "yes")
4115 print("Temporarily overriding 'dsdb:schema update allowed' setting")
4116 updates_allowed_overriden = True
4118 own_dn = ldb.Dn(samdb, samdb.get_dsServiceName())
4119 master = get_fsmo_roleowner(samdb, str(samdb.get_schema_basedn()),
4121 if own_dn != master:
4122 raise CommandError("This server is not the schema master.")
4124 # if specific LDIF files were specified, just apply them
4126 schema_updates = ldf_files.split(",")
4130 # work out the version of the target schema we're upgrading to
4131 end = Schema.get_version(target_schema)
4133 # work out the version of the schema we're currently using
4134 res = samdb.search(base=samdb.get_schema_basedn(),
4135 scope=ldb.SCOPE_BASE, attrs=['objectVersion'])
4138 raise CommandError('Could not determine current schema version')
4139 start = int(res[0]['objectVersion'][0]) + 1
4141 diff_dir = setup_path("adprep/WindowsServerDocs")
4142 if base_dir is None:
4143 # Read from the Schema-Updates.md file
4144 temp_folder = tempfile.mkdtemp()
4146 update_file = setup_path("adprep/WindowsServerDocs/Schema-Updates.md")
4149 read_ms_markdown(update_file, temp_folder)
4150 except Exception as e:
4151 print("Exception in markdown parsing: %s" % e)
4152 shutil.rmtree(temp_folder)
4153 raise CommandError('Failed to upgrade schema')
4155 base_dir = temp_folder
4157 for version in range(start, end + 1):
4158 update = 'Sch%d.ldf' % version
4159 schema_updates.append(update)
4161 # Apply patches if we parsed the Schema-Updates.md file
4162 diff = os.path.abspath(os.path.join(diff_dir, update + '.diff'))
4163 if temp_folder and os.path.exists(diff):
4165 p = subprocess.Popen(['patch', update, '-i', diff],
4166 stdout=subprocess.PIPE,
4167 stderr=subprocess.PIPE, cwd=temp_folder)
4168 except (OSError, IOError):
4169 shutil.rmtree(temp_folder)
4170 raise CommandError("Failed to upgrade schema. Check if 'patch' is installed.")
4172 stdout, stderr = p.communicate()
4175 print("Exception in patch: %s\n%s" % (stdout, stderr))
4176 shutil.rmtree(temp_folder)
4177 raise CommandError('Failed to upgrade schema')
4179 print("Patched %s using %s" % (update, diff))
4181 if base_dir is None:
4182 base_dir = setup_path("adprep")
4184 samdb.transaction_start()
4186 error_encountered = False
4189 # Apply the schema updates needed to move to the new schema version
4190 for ldif_file in schema_updates:
4191 count += self._apply_update(samdb, ldif_file, base_dir)
4194 samdb.transaction_commit()
4195 print("Schema successfully updated")
4197 print("No changes applied to schema")
4198 samdb.transaction_cancel()
4199 except Exception as e:
4200 print("Exception: %s" % e)
4201 print("Error encountered, aborting schema upgrade")
4202 samdb.transaction_cancel()
4203 error_encountered = True
4205 if updates_allowed_overriden:
4206 lp.set("dsdb:schema update allowed", "no")
4209 shutil.rmtree(temp_folder)
4211 if error_encountered:
4212 raise CommandError('Failed to upgrade schema')
4214 class cmd_domain_functional_prep(Command):
4215 """Domain functional level preparation"""
4217 synopsis = "%prog [options]"
4219 takes_optiongroups = {
4220 "sambaopts": options.SambaOptions,
4221 "versionopts": options.VersionOptions,
4222 "credopts": options.CredentialsOptions,
4226 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
4227 metavar="URL", dest="H"),
4228 Option("--quiet", help="Be quiet", action="store_true"),
4229 Option("--verbose", help="Be verbose", action="store_true"),
4230 Option("--function-level", type="choice", metavar="FUNCTION_LEVEL",
4231 choices=["2008_R2", "2012", "2012_R2"],
4232 help="The schema file to upgrade to. Default is (Windows) 2012_R2.",
4234 Option("--forest-prep", action="store_true",
4235 help="Run the forest prep (by default, both the domain and forest prep are run)."),
4236 Option("--domain-prep", action="store_true",
4237 help="Run the domain prep (by default, both the domain and forest prep are run).")
4240 def run(self, **kwargs):
4241 updates_allowed_overriden = False
4242 sambaopts = kwargs.get("sambaopts")
4243 credopts = kwargs.get("credopts")
4244 versionpts = kwargs.get("versionopts")
4245 lp = sambaopts.get_loadparm()
4246 creds = credopts.get_credentials(lp)
4248 target_level = string_version_to_constant[kwargs.get("function_level")]
4249 forest_prep = kwargs.get("forest_prep")
4250 domain_prep = kwargs.get("domain_prep")
4252 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
4254 # we're not going to get far if the config doesn't allow schema updates
4255 if lp.get("dsdb:schema update allowed") is None:
4256 lp.set("dsdb:schema update allowed", "yes")
4257 print("Temporarily overriding 'dsdb:schema update allowed' setting")
4258 updates_allowed_overriden = True
4260 if forest_prep is None and domain_prep is None:
4264 own_dn = ldb.Dn(samdb, samdb.get_dsServiceName())
4266 master = get_fsmo_roleowner(samdb, str(samdb.get_schema_basedn()),
4268 if own_dn != master:
4269 raise CommandError("This server is not the schema master.")
4272 domain_dn = samdb.domain_dn()
4273 infrastructure_dn = "CN=Infrastructure," + domain_dn
4274 master = get_fsmo_roleowner(samdb, infrastructure_dn,
4276 if own_dn != master:
4277 raise CommandError("This server is not the infrastructure master.")
4280 samdb.transaction_start()
4281 error_encountered = False
4283 from samba.forest_update import ForestUpdate
4284 forest = ForestUpdate(samdb, fix=True)
4286 forest.check_updates_iterator([53, 79, 80, 81, 82, 83])
4287 forest.check_updates_functional_level(target_level,
4288 DS_DOMAIN_FUNCTION_2008_R2,
4289 update_revision=True)
4291 samdb.transaction_commit()
4292 except Exception as e:
4293 print("Exception: %s" % e)
4294 samdb.transaction_cancel()
4295 error_encountered = True
4298 samdb.transaction_start()
4299 error_encountered = False
4301 from samba.domain_update import DomainUpdate
4303 domain = DomainUpdate(samdb, fix=True)
4304 domain.check_updates_functional_level(target_level,
4305 DS_DOMAIN_FUNCTION_2008,
4306 update_revision=True)
4308 samdb.transaction_commit()
4309 except Exception as e:
4310 print("Exception: %s" % e)
4311 samdb.transaction_cancel()
4312 error_encountered = True
4314 if updates_allowed_overriden:
4315 lp.set("dsdb:schema update allowed", "no")
4317 if error_encountered:
4318 raise CommandError('Failed to perform functional prep')
4320 class cmd_domain(SuperCommand):
4321 """Domain management."""
4324 subcommands["demote"] = cmd_domain_demote()
4325 if cmd_domain_export_keytab is not None:
4326 subcommands["exportkeytab"] = cmd_domain_export_keytab()
4327 subcommands["info"] = cmd_domain_info()
4328 subcommands["provision"] = cmd_domain_provision()
4329 subcommands["join"] = cmd_domain_join()
4330 subcommands["dcpromo"] = cmd_domain_dcpromo()
4331 subcommands["level"] = cmd_domain_level()
4332 subcommands["passwordsettings"] = cmd_domain_passwordsettings()
4333 subcommands["classicupgrade"] = cmd_domain_classicupgrade()
4334 subcommands["samba3upgrade"] = cmd_domain_samba3upgrade()
4335 subcommands["trust"] = cmd_domain_trust()
4336 subcommands["tombstones"] = cmd_domain_tombstones()
4337 subcommands["schemaupgrade"] = cmd_domain_schema_upgrade()
4338 subcommands["functionalprep"] = cmd_domain_functional_prep()