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 from samba.netcmd.pso import cmd_domain_passwordsettings_pso
103 from samba.netcmd.domain_backup import cmd_domain_backup
105 string_version_to_constant = {
106 "2008_R2": DS_DOMAIN_FUNCTION_2008_R2,
107 "2012": DS_DOMAIN_FUNCTION_2012,
108 "2012_R2": DS_DOMAIN_FUNCTION_2012_R2,
111 common_provision_join_options = [
112 Option("--machinepass", type="string", metavar="PASSWORD",
113 help="choose machine password (otherwise random)"),
114 Option("--plaintext-secrets", action="store_true",
115 help="Store secret/sensitive values as plain text on disk" +
116 "(default is to encrypt secret/ensitive values)"),
117 Option("--backend-store", type="choice", metavar="BACKENDSTORE",
118 choices=["tdb", "mdb"],
119 help="Specify the database backend to be used "
120 "(default is %s)" % get_default_backend_store()),
121 Option("--targetdir", metavar="DIR",
122 help="Set target directory (where to store provision)", type=str),
123 Option("-q", "--quiet", help="Be quiet", action="store_true"),
126 common_join_options = [
127 Option("--server", help="DC to join", type=str),
128 Option("--site", help="site to join", type=str),
129 Option("--domain-critical-only",
130 help="only replicate critical domain objects",
131 action="store_true"),
132 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
133 choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
134 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
135 "BIND9_DLZ uses samba4 AD to store zone information, "
136 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
137 default="SAMBA_INTERNAL"),
138 Option("-v", "--verbose", help="Be verbose", action="store_true")
141 common_ntvfs_options = [
142 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
147 def get_testparm_var(testparm, smbconf, varname):
148 errfile = open(os.devnull, 'w')
149 p = subprocess.Popen([testparm, '-s', '-l',
150 '--parameter-name=%s' % varname, smbconf],
151 stdout=subprocess.PIPE, stderr=errfile)
152 (out, err) = p.communicate()
154 lines = out.split('\n')
156 return lines[0].strip()
161 import samba.dckeytab
163 cmd_domain_export_keytab = None
165 class cmd_domain_export_keytab(Command):
166 """Dump Kerberos keys of the domain into a keytab."""
168 synopsis = "%prog <keytab> [options]"
170 takes_optiongroups = {
171 "sambaopts": options.SambaOptions,
172 "credopts": options.CredentialsOptions,
173 "versionopts": options.VersionOptions,
177 Option("--principal", help="extract only this principal", type=str),
180 takes_args = ["keytab"]
182 def run(self, keytab, credopts=None, sambaopts=None, versionopts=None, principal=None):
183 lp = sambaopts.get_loadparm()
185 net.export_keytab(keytab=keytab, principal=principal)
188 class cmd_domain_info(Command):
189 """Print basic info about a domain and the DC passed as parameter."""
191 synopsis = "%prog <ip_address> [options]"
196 takes_optiongroups = {
197 "sambaopts": options.SambaOptions,
198 "credopts": options.CredentialsOptions,
199 "versionopts": options.VersionOptions,
202 takes_args = ["address"]
204 def run(self, address, credopts=None, sambaopts=None, versionopts=None):
205 lp = sambaopts.get_loadparm()
207 res = netcmd_get_domain_infos_via_cldap(lp, None, address)
209 raise CommandError("Invalid IP address '" + address + "'!")
210 self.outf.write("Forest : %s\n" % res.forest)
211 self.outf.write("Domain : %s\n" % res.dns_domain)
212 self.outf.write("Netbios domain : %s\n" % res.domain_name)
213 self.outf.write("DC name : %s\n" % res.pdc_dns_name)
214 self.outf.write("DC netbios name : %s\n" % res.pdc_name)
215 self.outf.write("Server site : %s\n" % res.server_site)
216 self.outf.write("Client site : %s\n" % res.client_site)
219 class cmd_domain_provision(Command):
220 """Provision a domain."""
222 synopsis = "%prog [options]"
224 takes_optiongroups = {
225 "sambaopts": options.SambaOptions,
226 "versionopts": options.VersionOptions,
230 Option("--interactive", help="Ask for names", action="store_true"),
231 Option("--domain", type="string", metavar="DOMAIN",
232 help="NetBIOS domain name to use"),
233 Option("--domain-guid", type="string", metavar="GUID",
234 help="set domainguid (otherwise random)"),
235 Option("--domain-sid", type="string", metavar="SID",
236 help="set domainsid (otherwise random)"),
237 Option("--ntds-guid", type="string", metavar="GUID",
238 help="set NTDS object GUID (otherwise random)"),
239 Option("--invocationid", type="string", metavar="GUID",
240 help="set invocationid (otherwise random)"),
241 Option("--host-name", type="string", metavar="HOSTNAME",
242 help="set hostname"),
243 Option("--host-ip", type="string", metavar="IPADDRESS",
244 help="set IPv4 ipaddress"),
245 Option("--host-ip6", type="string", metavar="IP6ADDRESS",
246 help="set IPv6 ipaddress"),
247 Option("--site", type="string", metavar="SITENAME",
248 help="set site name"),
249 Option("--adminpass", type="string", metavar="PASSWORD",
250 help="choose admin password (otherwise random)"),
251 Option("--krbtgtpass", type="string", metavar="PASSWORD",
252 help="choose krbtgt password (otherwise random)"),
253 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
254 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
255 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
256 "BIND9_FLATFILE uses bind9 text database to store zone information, "
257 "BIND9_DLZ uses samba4 AD to store zone information, "
258 "NONE skips the DNS setup entirely (not recommended)",
259 default="SAMBA_INTERNAL"),
260 Option("--dnspass", type="string", metavar="PASSWORD",
261 help="choose dns password (otherwise random)"),
262 Option("--root", type="string", metavar="USERNAME",
263 help="choose 'root' unix username"),
264 Option("--nobody", type="string", metavar="USERNAME",
265 help="choose 'nobody' user"),
266 Option("--users", type="string", metavar="GROUPNAME",
267 help="choose 'users' group"),
268 Option("--blank", action="store_true",
269 help="do not add users or groups, just the structure"),
270 Option("--server-role", type="choice", metavar="ROLE",
271 choices=["domain controller", "dc", "member server", "member", "standalone"],
272 help="The server role (domain controller | dc | member server | member | standalone). Default is dc.",
273 default="domain controller"),
274 Option("--function-level", type="choice", metavar="FOR-FUN-LEVEL",
275 choices=["2000", "2003", "2008", "2008_R2"],
276 help="The domain and forest function level (2000 | 2003 | 2008 | 2008_R2 - always native). Default is (Windows) 2008_R2 Native.",
278 Option("--base-schema", type="choice", metavar="BASE-SCHEMA",
279 choices=["2008_R2", "2008_R2_old", "2012", "2012_R2"],
280 help="The base schema files to use. Default is (Windows) 2008_R2.",
282 Option("--next-rid", type="int", metavar="NEXTRID", default=1000,
283 help="The initial nextRid value (only needed for upgrades). Default is 1000."),
284 Option("--partitions-only",
285 help="Configure Samba's partitions, but do not modify them (ie, join a BDC)", action="store_true"),
286 Option("--use-rfc2307", action="store_true", help="Use AD to store posix attributes (default = no)"),
290 Option("--ldapadminpass", type="string", metavar="PASSWORD",
291 help="choose password to set between Samba and its LDAP backend (otherwise random)"),
292 Option("--ldap-backend-type", type="choice", metavar="LDAP-BACKEND-TYPE",
293 help="Test initialisation support for unsupported LDAP backend type (fedora-ds or openldap) DO NOT USE",
294 choices=["fedora-ds", "openldap"]),
295 Option("--ol-mmr-urls", type="string", metavar="LDAPSERVER",
296 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\""),
297 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",
298 action="store_true"),
299 Option("--slapd-path", type="string", metavar="SLAPD-PATH",
300 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."),
301 Option("--ldap-backend-extra-port", type="int", metavar="LDAP-BACKEND-EXTRA-PORT", help="Additional TCP port for LDAP backend server (to use for replication)"),
302 Option("--ldap-backend-forced-uri", type="string", metavar="LDAP-BACKEND-FORCED-URI",
303 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"),
304 Option("--ldap-backend-nosync", help="Configure LDAP backend not to call fsync() (for performance in test environments)", action="store_true"),
308 Option("--use-xattrs", type="choice", choices=["yes", "no", "auto"],
309 metavar="[yes|no|auto]",
310 help="Define if we should use the native fs capabilities or a tdb file for "
311 "storing attributes likes ntacl when --use-ntvfs is set. "
312 "auto tries to make an inteligent guess based on the user rights and system capabilities",
316 takes_options.extend(common_provision_join_options)
318 if os.getenv('TEST_LDAP', "no") == "yes":
319 takes_options.extend(openldap_options)
321 if samba.is_ntvfs_fileserver_built():
322 takes_options.extend(common_ntvfs_options)
323 takes_options.extend(ntvfs_options)
327 def run(self, sambaopts=None, versionopts=None,
350 ldap_backend_type=None,
354 partitions_only=None,
361 ldap_backend_nosync=None,
362 ldap_backend_extra_port=None,
363 ldap_backend_forced_uri=None,
364 ldap_dryrun_mode=None,
366 plaintext_secrets=False,
369 self.logger = self.get_logger("provision")
371 self.logger.setLevel(logging.WARNING)
373 self.logger.setLevel(logging.INFO)
375 lp = sambaopts.get_loadparm()
376 smbconf = lp.configfile
378 if dns_forwarder is not None:
379 suggested_forwarder = dns_forwarder
381 suggested_forwarder = self._get_nameserver_ip()
382 if suggested_forwarder is None:
383 suggested_forwarder = "none"
385 if len(self.raw_argv) == 1:
389 from getpass import getpass
392 def ask(prompt, default=None):
393 if default is not None:
394 print("%s [%s]: " % (prompt, default), end=' ')
396 print("%s: " % (prompt,), end=' ')
397 return sys.stdin.readline().rstrip("\n") or default
400 default = socket.getfqdn().split(".", 1)[1].upper()
403 realm = ask("Realm", default)
404 if realm in (None, ""):
405 raise CommandError("No realm set!")
408 default = realm.split(".")[0]
411 domain = ask("Domain", default)
413 raise CommandError("No domain set!")
415 server_role = ask("Server Role (dc, member, standalone)", "dc")
417 dns_backend = ask("DNS backend (SAMBA_INTERNAL, BIND9_FLATFILE, BIND9_DLZ, NONE)", "SAMBA_INTERNAL")
418 if dns_backend in (None, ''):
419 raise CommandError("No DNS backend set!")
421 if dns_backend == "SAMBA_INTERNAL":
422 dns_forwarder = ask("DNS forwarder IP address (write 'none' to disable forwarding)", suggested_forwarder)
423 if dns_forwarder.lower() in (None, 'none'):
424 suggested_forwarder = None
428 adminpassplain = getpass("Administrator password: ")
429 issue = self._adminpass_issue(adminpassplain)
431 self.errf.write("%s.\n" % issue)
433 adminpassverify = getpass("Retype password: ")
434 if not adminpassplain == adminpassverify:
435 self.errf.write("Sorry, passwords do not match.\n")
437 adminpass = adminpassplain
441 realm = sambaopts._lp.get('realm')
443 raise CommandError("No realm set!")
445 raise CommandError("No domain set!")
448 issue = self._adminpass_issue(adminpass)
450 raise CommandError(issue)
452 self.logger.info("Administrator password will be set randomly!")
454 if function_level == "2000":
455 dom_for_fun_level = DS_DOMAIN_FUNCTION_2000
456 elif function_level == "2003":
457 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
458 elif function_level == "2008":
459 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008
460 elif function_level == "2008_R2":
461 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
463 if dns_backend == "SAMBA_INTERNAL" and dns_forwarder is None:
464 dns_forwarder = suggested_forwarder
466 samdb_fill = FILL_FULL
468 samdb_fill = FILL_NT4SYNC
469 elif partitions_only:
470 samdb_fill = FILL_DRS
472 if targetdir is not None:
473 if not os.path.isdir(targetdir):
478 if use_xattrs == "yes":
480 elif use_xattrs == "auto" and use_ntvfs == False:
482 elif use_ntvfs == False:
483 raise CommandError("--use-xattrs=no requires --use-ntvfs (not supported for production use). "
484 "Please re-run with --use-xattrs omitted.")
485 elif use_xattrs == "auto" and not lp.get("posix:eadb"):
487 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
489 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
492 samba.ntacls.setntacl(lp, file.name,
493 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
496 self.logger.info("You are not root or your system does not support xattr, using tdb backend for attributes. ")
501 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.")
502 if ldap_backend_type == "existing":
503 if ldap_backend_forced_uri is not None:
504 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)
506 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")
508 if ldap_backend_forced_uri is not None:
509 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")
511 if domain_sid is not None:
512 domain_sid = security.dom_sid(domain_sid)
514 session = system_session()
515 if backend_store is None:
516 backend_store = get_default_backend_store()
518 result = provision(self.logger,
519 session, smbconf=smbconf, targetdir=targetdir,
520 samdb_fill=samdb_fill, realm=realm, domain=domain,
521 domainguid=domain_guid, domainsid=domain_sid,
523 hostip=host_ip, hostip6=host_ip6,
524 sitename=site, ntdsguid=ntds_guid,
525 invocationid=invocationid, adminpass=adminpass,
526 krbtgtpass=krbtgtpass, machinepass=machinepass,
527 dns_backend=dns_backend, dns_forwarder=dns_forwarder,
528 dnspass=dnspass, root=root, nobody=nobody,
530 serverrole=server_role, dom_for_fun_level=dom_for_fun_level,
531 backend_type=ldap_backend_type,
532 ldapadminpass=ldapadminpass, ol_mmr_urls=ol_mmr_urls, slapd_path=slapd_path,
533 useeadb=eadb, next_rid=next_rid, lp=lp, use_ntvfs=use_ntvfs,
534 use_rfc2307=use_rfc2307, skip_sysvolacl=False,
535 ldap_backend_extra_port=ldap_backend_extra_port,
536 ldap_backend_forced_uri=ldap_backend_forced_uri,
537 nosync=ldap_backend_nosync, ldap_dryrun_mode=ldap_dryrun_mode,
538 base_schema=base_schema,
539 plaintext_secrets=plaintext_secrets,
540 backend_store=backend_store)
542 except ProvisioningError as e:
543 raise CommandError("Provision failed", e)
545 result.report_logger(self.logger)
547 def _get_nameserver_ip(self):
548 """Grab the nameserver IP address from /etc/resolv.conf."""
550 RESOLV_CONF = "/etc/resolv.conf"
552 if not path.isfile(RESOLV_CONF):
553 self.logger.warning("Failed to locate %s" % RESOLV_CONF)
558 handle = open(RESOLV_CONF, 'r')
560 if not line.startswith('nameserver'):
562 # we want the last non-space continuous string of the line
563 return line.strip().split()[-1]
565 if handle is not None:
568 self.logger.warning("No nameserver found in %s" % RESOLV_CONF)
570 def _adminpass_issue(self, adminpass):
571 """Returns error string for a bad administrator password,
572 or None if acceptable"""
574 if len(adminpass.decode('utf-8')) < DEFAULT_MIN_PWD_LENGTH:
575 return "Administrator password does not meet the default minimum" \
576 " password length requirement (%d characters)" \
577 % DEFAULT_MIN_PWD_LENGTH
578 elif not samba.check_password_quality(adminpass):
579 return "Administrator password does not meet the default" \
585 class cmd_domain_dcpromo(Command):
586 """Promote an existing domain member or NT4 PDC to an AD DC."""
588 synopsis = "%prog <dnsdomain> [DC|RODC] [options]"
590 takes_optiongroups = {
591 "sambaopts": options.SambaOptions,
592 "versionopts": options.VersionOptions,
593 "credopts": options.CredentialsOptions,
597 takes_options.extend(common_join_options)
599 takes_options.extend(common_provision_join_options)
601 if samba.is_ntvfs_fileserver_built():
602 takes_options.extend(common_ntvfs_options)
604 takes_args = ["domain", "role?"]
606 def run(self, domain, role=None, sambaopts=None, credopts=None,
607 versionopts=None, server=None, site=None, targetdir=None,
608 domain_critical_only=False, parent_domain=None, machinepass=None,
609 use_ntvfs=False, dns_backend=None,
610 quiet=False, verbose=False, plaintext_secrets=False,
612 lp = sambaopts.get_loadparm()
613 creds = credopts.get_credentials(lp)
614 net = Net(creds, lp, server=credopts.ipaddress)
616 logger = self.get_logger()
618 logger.setLevel(logging.DEBUG)
620 logger.setLevel(logging.WARNING)
622 logger.setLevel(logging.INFO)
624 netbios_name = lp.get("netbios name")
630 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
631 site=site, netbios_name=netbios_name, targetdir=targetdir,
632 domain_critical_only=domain_critical_only,
633 machinepass=machinepass, use_ntvfs=use_ntvfs,
634 dns_backend=dns_backend,
635 promote_existing=True, plaintext_secrets=plaintext_secrets,
636 backend_store=backend_store)
638 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
639 site=site, netbios_name=netbios_name, targetdir=targetdir,
640 domain_critical_only=domain_critical_only,
641 machinepass=machinepass, use_ntvfs=use_ntvfs, dns_backend=dns_backend,
642 promote_existing=True, plaintext_secrets=plaintext_secrets,
643 backend_store=backend_store)
645 raise CommandError("Invalid role '%s' (possible values: DC, RODC)" % role)
648 class cmd_domain_join(Command):
649 """Join domain as either member or backup domain controller."""
651 synopsis = "%prog <dnsdomain> [DC|RODC|MEMBER|SUBDOMAIN] [options]"
653 takes_optiongroups = {
654 "sambaopts": options.SambaOptions,
655 "versionopts": options.VersionOptions,
656 "credopts": options.CredentialsOptions,
660 Option("--parent-domain", help="parent domain to create subdomain under", type=str),
661 Option("--adminpass", type="string", metavar="PASSWORD",
662 help="choose adminstrator password when joining as a subdomain (otherwise random)"),
666 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
669 takes_options.extend(common_join_options)
670 takes_options.extend(common_provision_join_options)
672 if samba.is_ntvfs_fileserver_built():
673 takes_options.extend(ntvfs_options)
675 takes_args = ["domain", "role?"]
677 def run(self, domain, role=None, sambaopts=None, credopts=None,
678 versionopts=None, server=None, site=None, targetdir=None,
679 domain_critical_only=False, parent_domain=None, machinepass=None,
680 use_ntvfs=False, dns_backend=None, adminpass=None,
681 quiet=False, verbose=False,
682 plaintext_secrets=False,
684 lp = sambaopts.get_loadparm()
685 creds = credopts.get_credentials(lp)
686 net = Net(creds, lp, server=credopts.ipaddress)
689 site = "Default-First-Site-Name"
691 logger = self.get_logger()
693 logger.setLevel(logging.DEBUG)
695 logger.setLevel(logging.WARNING)
697 logger.setLevel(logging.INFO)
699 netbios_name = lp.get("netbios name")
704 if role is None or role == "MEMBER":
705 (join_password, sid, domain_name) = net.join_member(
706 domain, netbios_name, LIBNET_JOIN_AUTOMATIC,
707 machinepass=machinepass)
709 self.errf.write("Joined domain %s (%s)\n" % (domain_name, sid))
711 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
712 site=site, netbios_name=netbios_name, targetdir=targetdir,
713 domain_critical_only=domain_critical_only,
714 machinepass=machinepass, use_ntvfs=use_ntvfs,
715 dns_backend=dns_backend,
716 plaintext_secrets=plaintext_secrets,
717 backend_store=backend_store)
719 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
720 site=site, netbios_name=netbios_name, targetdir=targetdir,
721 domain_critical_only=domain_critical_only,
722 machinepass=machinepass, use_ntvfs=use_ntvfs,
723 dns_backend=dns_backend,
724 plaintext_secrets=plaintext_secrets,
725 backend_store=backend_store)
726 elif role == "SUBDOMAIN":
728 logger.info("Administrator password will be set randomly!")
730 netbios_domain = lp.get("workgroup")
731 if parent_domain is None:
732 parent_domain = ".".join(domain.split(".")[1:])
733 join_subdomain(logger=logger, server=server, creds=creds, lp=lp, dnsdomain=domain,
734 parent_domain=parent_domain, site=site,
735 netbios_name=netbios_name, netbios_domain=netbios_domain,
736 targetdir=targetdir, machinepass=machinepass,
737 use_ntvfs=use_ntvfs, dns_backend=dns_backend,
739 plaintext_secrets=plaintext_secrets,
740 backend_store=backend_store)
742 raise CommandError("Invalid role '%s' (possible values: MEMBER, DC, RODC, SUBDOMAIN)" % role)
745 class cmd_domain_demote(Command):
746 """Demote ourselves from the role of Domain Controller."""
748 synopsis = "%prog [options]"
751 Option("--server", help="writable DC to write demotion changes on", type=str),
752 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
753 metavar="URL", dest="H"),
754 Option("--remove-other-dead-server", help="Dead DC (name or NTDS GUID) "
755 "to remove ALL references to (rather than this DC)", type=str),
756 Option("-q", "--quiet", help="Be quiet", action="store_true"),
757 Option("-v", "--verbose", help="Be verbose", action="store_true"),
760 takes_optiongroups = {
761 "sambaopts": options.SambaOptions,
762 "credopts": options.CredentialsOptions,
763 "versionopts": options.VersionOptions,
766 def run(self, sambaopts=None, credopts=None,
767 versionopts=None, server=None,
768 remove_other_dead_server=None, H=None,
769 verbose=False, quiet=False):
770 lp = sambaopts.get_loadparm()
771 creds = credopts.get_credentials(lp)
772 net = Net(creds, lp, server=credopts.ipaddress)
774 logger = self.get_logger()
776 logger.setLevel(logging.DEBUG)
778 logger.setLevel(logging.WARNING)
780 logger.setLevel(logging.INFO)
782 if remove_other_dead_server is not None:
783 if server is not None:
784 samdb = SamDB(url="ldap://%s" % server,
785 session_info=system_session(),
786 credentials=creds, lp=lp)
788 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
790 remove_dc.remove_dc(samdb, logger, remove_other_dead_server)
791 except remove_dc.DemoteException as err:
792 raise CommandError("Demote failed: %s" % err)
795 netbios_name = lp.get("netbios name")
796 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
798 res = samdb.search(expression='(&(objectClass=computer)(serverReferenceBL=*))', attrs=["dnsHostName", "name"])
800 raise CommandError("Unable to search for servers")
803 raise CommandError("You are the last server in the domain")
807 if str(e["name"]).lower() != netbios_name.lower():
808 server = e["dnsHostName"]
811 ntds_guid = samdb.get_ntds_GUID()
812 msg = samdb.search(base=str(samdb.get_config_basedn()),
813 scope=ldb.SCOPE_SUBTREE, expression="(objectGUID=%s)" % ntds_guid,
815 if len(msg) == 0 or "options" not in msg[0]:
816 raise CommandError("Failed to find options on %s" % ntds_guid)
819 dsa_options = int(str(msg[0]['options']))
821 res = samdb.search(expression="(fSMORoleOwner=%s)" % str(ntds_dn),
822 controls=["search_options:1:2"])
825 raise CommandError("Current DC is still the owner of %d role(s), "
826 "use the role command to transfer roles to "
830 self.errf.write("Using %s as partner server for the demotion\n" %
832 (drsuapiBind, drsuapi_handle, supportedExtensions) = drsuapi_connect(server, lp, creds)
834 self.errf.write("Deactivating inbound replication\n")
839 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
840 dsa_options |= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
841 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
844 self.errf.write("Asking partner server %s to synchronize from us\n"
846 for part in (samdb.get_schema_basedn(),
847 samdb.get_config_basedn(),
848 samdb.get_root_basedn()):
849 nc = drsuapi.DsReplicaObjectIdentifier()
852 req1 = drsuapi.DsReplicaSyncRequest1()
853 req1.naming_context = nc
854 req1.options = drsuapi.DRSUAPI_DRS_WRIT_REP
855 req1.source_dsa_guid = misc.GUID(ntds_guid)
858 drsuapiBind.DsReplicaSync(drsuapi_handle, 1, req1)
859 except RuntimeError as e1:
860 (werr, string) = e1.args
861 if werr == werror.WERR_DS_DRA_NO_REPLICA:
865 "Error while replicating out last local changes from '%s' for demotion, "
866 "re-enabling inbound replication\n" % part)
867 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
868 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
870 raise CommandError("Error while sending a DsReplicaSync for partition '%s'" % str(part), string)
872 remote_samdb = SamDB(url="ldap://%s" % server,
873 session_info=system_session(),
874 credentials=creds, lp=lp)
876 self.errf.write("Changing userControl and container\n")
877 res = remote_samdb.search(base=str(remote_samdb.domain_dn()),
878 expression="(&(objectClass=user)(sAMAccountName=%s$))" %
879 netbios_name.upper(),
880 attrs=["userAccountControl"])
882 uac = int(str(res[0]["userAccountControl"]))
884 except Exception as e:
885 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
887 "Error while demoting, re-enabling inbound replication\n")
888 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
889 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
891 raise CommandError("Error while changing account control", e)
894 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
896 "Error while demoting, re-enabling inbound replication")
897 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
898 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
900 raise CommandError("Unable to find object with samaccountName = %s$"
901 " in the remote dc" % netbios_name.upper())
905 uac &= ~(UF_SERVER_TRUST_ACCOUNT |UF_TRUSTED_FOR_DELEGATION |UF_PARTIAL_SECRETS_ACCOUNT)
906 uac |= UF_WORKSTATION_TRUST_ACCOUNT
911 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
912 ldb.FLAG_MOD_REPLACE,
913 "userAccountControl")
915 remote_samdb.modify(msg)
916 except Exception as e:
917 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
919 "Error while demoting, re-enabling inbound replication")
920 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
921 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
924 raise CommandError("Error while changing account control", e)
926 parent = msg.dn.parent()
927 dc_name = res[0].dn.get_rdn_value()
928 rdn = "CN=%s" % dc_name
930 # Let's move to the Computer container
934 computer_dn = ldb.Dn(remote_samdb, "CN=Computers,%s" % str(remote_samdb.domain_dn()))
935 res = remote_samdb.search(base=computer_dn, expression=rdn, scope=ldb.SCOPE_ONELEVEL)
938 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
939 scope=ldb.SCOPE_ONELEVEL)
940 while(len(res) != 0 and i < 100):
942 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
943 scope=ldb.SCOPE_ONELEVEL)
946 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
948 "Error while demoting, re-enabling inbound replication\n")
949 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
950 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
956 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
957 ldb.FLAG_MOD_REPLACE,
958 "userAccountControl")
960 remote_samdb.modify(msg)
962 raise CommandError("Unable to find a slot for renaming %s,"
963 " all names from %s-1 to %s-%d seemed used" %
964 (str(dc_dn), rdn, rdn, i - 9))
966 newrdn = "%s-%d" % (rdn, i)
969 newdn = ldb.Dn(remote_samdb, "%s,%s" % (newrdn, str(computer_dn)))
970 remote_samdb.rename(dc_dn, newdn)
971 except Exception as e:
972 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
974 "Error while demoting, re-enabling inbound replication\n")
975 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
976 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
982 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
983 ldb.FLAG_MOD_REPLACE,
984 "userAccountControl")
986 remote_samdb.modify(msg)
987 raise CommandError("Error while renaming %s to %s" % (str(dc_dn), str(newdn)), e)
989 server_dsa_dn = samdb.get_serverName()
990 domain = remote_samdb.get_root_basedn()
993 req1 = drsuapi.DsRemoveDSServerRequest1()
994 req1.server_dn = str(server_dsa_dn)
995 req1.domain_dn = str(domain)
998 drsuapiBind.DsRemoveDSServer(drsuapi_handle, 1, req1)
999 except RuntimeError as e3:
1000 (werr, string) = e3.args
1001 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
1003 "Error while demoting, re-enabling inbound replication\n")
1004 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
1005 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
1011 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
1012 ldb.FLAG_MOD_REPLACE,
1013 "userAccountControl")
1014 remote_samdb.modify(msg)
1015 remote_samdb.rename(newdn, dc_dn)
1016 if werr == werror.WERR_DS_DRA_NO_REPLICA:
1017 raise CommandError("The DC %s is not present on (already "
1018 "removed from) the remote server: %s" %
1019 (server_dsa_dn, e3))
1021 raise CommandError("Error while sending a removeDsServer "
1023 (server_dsa_dn, e3))
1025 remove_dc.remove_sysvol_references(remote_samdb, logger, dc_name)
1027 # These are objects under the computer account that should be deleted
1028 for s in ("CN=Enterprise,CN=NTFRS Subscriptions",
1029 "CN=%s, CN=NTFRS Subscriptions" % lp.get("realm"),
1030 "CN=Domain system Volumes (SYSVOL Share), CN=NTFRS Subscriptions",
1031 "CN=NTFRS Subscriptions"):
1033 remote_samdb.delete(ldb.Dn(remote_samdb,
1034 "%s,%s" % (s, str(newdn))))
1035 except ldb.LdbError as l:
1038 # get dns host name for target server to demote, remove dns references
1039 remove_dc.remove_dns_references(remote_samdb, logger, samdb.host_dns_name(),
1040 ignore_no_name=True)
1042 self.errf.write("Demote successful\n")
1045 class cmd_domain_level(Command):
1046 """Raise domain and forest function levels."""
1048 synopsis = "%prog (show|raise <options>) [options]"
1050 takes_optiongroups = {
1051 "sambaopts": options.SambaOptions,
1052 "credopts": options.CredentialsOptions,
1053 "versionopts": options.VersionOptions,
1057 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1058 metavar="URL", dest="H"),
1059 Option("-q", "--quiet", help="Be quiet", action="store_true"), # unused
1060 Option("--forest-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
1061 help="The forest function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)"),
1062 Option("--domain-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
1063 help="The domain function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)")
1066 takes_args = ["subcommand"]
1068 def run(self, subcommand, H=None, forest_level=None, domain_level=None,
1069 quiet=False, credopts=None, sambaopts=None, versionopts=None):
1070 lp = sambaopts.get_loadparm()
1071 creds = credopts.get_credentials(lp, fallback_machine=True)
1073 samdb = SamDB(url=H, session_info=system_session(),
1074 credentials=creds, lp=lp)
1076 domain_dn = samdb.domain_dn()
1078 res_forest = samdb.search("CN=Partitions,%s" % samdb.get_config_basedn(),
1079 scope=ldb.SCOPE_BASE, attrs=["msDS-Behavior-Version"])
1080 assert len(res_forest) == 1
1082 res_domain = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1083 attrs=["msDS-Behavior-Version", "nTMixedDomain"])
1084 assert len(res_domain) == 1
1086 res_dc_s = samdb.search("CN=Sites,%s" % samdb.get_config_basedn(),
1087 scope=ldb.SCOPE_SUBTREE, expression="(objectClass=nTDSDSA)",
1088 attrs=["msDS-Behavior-Version"])
1089 assert len(res_dc_s) >= 1
1091 # default values, since "msDS-Behavior-Version" does not exist on Windows 2000 AD
1092 level_forest = DS_DOMAIN_FUNCTION_2000
1093 level_domain = DS_DOMAIN_FUNCTION_2000
1095 if "msDS-Behavior-Version" in res_forest[0]:
1096 level_forest = int(res_forest[0]["msDS-Behavior-Version"][0])
1097 if "msDS-Behavior-Version" in res_domain[0]:
1098 level_domain = int(res_domain[0]["msDS-Behavior-Version"][0])
1099 level_domain_mixed = int(res_domain[0]["nTMixedDomain"][0])
1102 for msg in res_dc_s:
1103 if "msDS-Behavior-Version" in msg:
1104 if min_level_dc is None or int(msg["msDS-Behavior-Version"][0]) < min_level_dc:
1105 min_level_dc = int(msg["msDS-Behavior-Version"][0])
1107 min_level_dc = DS_DOMAIN_FUNCTION_2000
1108 # well, this is the least
1111 if level_forest < DS_DOMAIN_FUNCTION_2000 or level_domain < DS_DOMAIN_FUNCTION_2000:
1112 raise CommandError("Domain and/or forest function level(s) is/are invalid. Correct them or reprovision!")
1113 if min_level_dc < DS_DOMAIN_FUNCTION_2000:
1114 raise CommandError("Lowest function level of a DC is invalid. Correct this or reprovision!")
1115 if level_forest > level_domain:
1116 raise CommandError("Forest function level is higher than the domain level(s). Correct this or reprovision!")
1117 if level_domain > min_level_dc:
1118 raise CommandError("Domain function level is higher than the lowest function level of a DC. Correct this or reprovision!")
1120 if subcommand == "show":
1121 self.message("Domain and forest function level for domain '%s'" % domain_dn)
1122 if level_forest == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1123 self.message("\nATTENTION: You run SAMBA 4 on a forest function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1124 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1125 self.message("\nATTENTION: You run SAMBA 4 on a domain function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1126 if min_level_dc == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1127 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)!")
1131 if level_forest == DS_DOMAIN_FUNCTION_2000:
1133 elif level_forest == DS_DOMAIN_FUNCTION_2003_MIXED:
1134 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1135 elif level_forest == DS_DOMAIN_FUNCTION_2003:
1137 elif level_forest == DS_DOMAIN_FUNCTION_2008:
1139 elif level_forest == DS_DOMAIN_FUNCTION_2008_R2:
1141 elif level_forest == DS_DOMAIN_FUNCTION_2012:
1143 elif level_forest == DS_DOMAIN_FUNCTION_2012_R2:
1146 outstr = "higher than 2012 R2"
1147 self.message("Forest function level: (Windows) " + outstr)
1149 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1150 outstr = "2000 mixed (NT4 DC support)"
1151 elif level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed == 0:
1153 elif level_domain == DS_DOMAIN_FUNCTION_2003_MIXED:
1154 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1155 elif level_domain == DS_DOMAIN_FUNCTION_2003:
1157 elif level_domain == DS_DOMAIN_FUNCTION_2008:
1159 elif level_domain == DS_DOMAIN_FUNCTION_2008_R2:
1161 elif level_domain == DS_DOMAIN_FUNCTION_2012:
1163 elif level_domain == DS_DOMAIN_FUNCTION_2012_R2:
1166 outstr = "higher than 2012 R2"
1167 self.message("Domain function level: (Windows) " + outstr)
1169 if min_level_dc == DS_DOMAIN_FUNCTION_2000:
1171 elif min_level_dc == DS_DOMAIN_FUNCTION_2003:
1173 elif min_level_dc == DS_DOMAIN_FUNCTION_2008:
1175 elif min_level_dc == DS_DOMAIN_FUNCTION_2008_R2:
1177 elif min_level_dc == DS_DOMAIN_FUNCTION_2012:
1179 elif min_level_dc == DS_DOMAIN_FUNCTION_2012_R2:
1182 outstr = "higher than 2012 R2"
1183 self.message("Lowest function level of a DC: (Windows) " + outstr)
1185 elif subcommand == "raise":
1188 if domain_level is not None:
1189 if domain_level == "2003":
1190 new_level_domain = DS_DOMAIN_FUNCTION_2003
1191 elif domain_level == "2008":
1192 new_level_domain = DS_DOMAIN_FUNCTION_2008
1193 elif domain_level == "2008_R2":
1194 new_level_domain = DS_DOMAIN_FUNCTION_2008_R2
1195 elif domain_level == "2012":
1196 new_level_domain = DS_DOMAIN_FUNCTION_2012
1197 elif domain_level == "2012_R2":
1198 new_level_domain = DS_DOMAIN_FUNCTION_2012_R2
1200 if new_level_domain <= level_domain and level_domain_mixed == 0:
1201 raise CommandError("Domain function level can't be smaller than or equal to the actual one!")
1202 if new_level_domain > min_level_dc:
1203 raise CommandError("Domain function level can't be higher than the lowest function level of a DC!")
1205 # Deactivate mixed/interim domain support
1206 if level_domain_mixed != 0:
1207 # Directly on the base DN
1209 m.dn = ldb.Dn(samdb, domain_dn)
1210 m["nTMixedDomain"] = ldb.MessageElement("0",
1211 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1215 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup") + ",CN=Partitions,%s" % samdb.get_config_basedn())
1216 m["nTMixedDomain"] = ldb.MessageElement("0",
1217 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1220 except ldb.LdbError as e:
1221 (enum, emsg) = e.args
1222 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1225 # Directly on the base DN
1227 m.dn = ldb.Dn(samdb, domain_dn)
1228 m["msDS-Behavior-Version"] = ldb.MessageElement(
1229 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1230 "msDS-Behavior-Version")
1234 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup")
1235 + ",CN=Partitions,%s" % samdb.get_config_basedn())
1236 m["msDS-Behavior-Version"] = ldb.MessageElement(
1237 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1238 "msDS-Behavior-Version")
1241 except ldb.LdbError as e2:
1242 (enum, emsg) = e2.args
1243 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1246 level_domain = new_level_domain
1247 msgs.append("Domain function level changed!")
1249 if forest_level is not None:
1250 if forest_level == "2003":
1251 new_level_forest = DS_DOMAIN_FUNCTION_2003
1252 elif forest_level == "2008":
1253 new_level_forest = DS_DOMAIN_FUNCTION_2008
1254 elif forest_level == "2008_R2":
1255 new_level_forest = DS_DOMAIN_FUNCTION_2008_R2
1256 elif forest_level == "2012":
1257 new_level_forest = DS_DOMAIN_FUNCTION_2012
1258 elif forest_level == "2012_R2":
1259 new_level_forest = DS_DOMAIN_FUNCTION_2012_R2
1261 if new_level_forest <= level_forest:
1262 raise CommandError("Forest function level can't be smaller than or equal to the actual one!")
1263 if new_level_forest > level_domain:
1264 raise CommandError("Forest function level can't be higher than the domain function level(s). Please raise it/them first!")
1267 m.dn = ldb.Dn(samdb, "CN=Partitions,%s" % samdb.get_config_basedn())
1268 m["msDS-Behavior-Version"] = ldb.MessageElement(
1269 str(new_level_forest), ldb.FLAG_MOD_REPLACE,
1270 "msDS-Behavior-Version")
1272 msgs.append("Forest function level changed!")
1273 msgs.append("All changes applied successfully!")
1274 self.message("\n".join(msgs))
1276 raise CommandError("invalid argument: '%s' (choose from 'show', 'raise')" % subcommand)
1279 class cmd_domain_passwordsettings_show(Command):
1280 """Display current password settings for the domain."""
1282 synopsis = "%prog [options]"
1284 takes_optiongroups = {
1285 "sambaopts": options.SambaOptions,
1286 "versionopts": options.VersionOptions,
1287 "credopts": options.CredentialsOptions,
1291 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1292 metavar="URL", dest="H"),
1295 def run(self, H=None, credopts=None, sambaopts=None, versionopts=None):
1296 lp = sambaopts.get_loadparm()
1297 creds = credopts.get_credentials(lp)
1299 samdb = SamDB(url=H, session_info=system_session(),
1300 credentials=creds, lp=lp)
1302 domain_dn = samdb.domain_dn()
1303 res = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1304 attrs=["pwdProperties", "pwdHistoryLength", "minPwdLength",
1305 "minPwdAge", "maxPwdAge", "lockoutDuration", "lockoutThreshold",
1306 "lockOutObservationWindow"])
1307 assert(len(res) == 1)
1309 pwd_props = int(res[0]["pwdProperties"][0])
1310 pwd_hist_len = int(res[0]["pwdHistoryLength"][0])
1311 cur_min_pwd_len = int(res[0]["minPwdLength"][0])
1313 cur_min_pwd_age = int(abs(int(res[0]["minPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1314 if int(res[0]["maxPwdAge"][0]) == -0x8000000000000000:
1317 cur_max_pwd_age = int(abs(int(res[0]["maxPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1318 cur_account_lockout_threshold = int(res[0]["lockoutThreshold"][0])
1320 if int(res[0]["lockoutDuration"][0]) == -0x8000000000000000:
1321 cur_account_lockout_duration = 0
1323 cur_account_lockout_duration = abs(int(res[0]["lockoutDuration"][0])) / (1e7 * 60)
1324 cur_reset_account_lockout_after = abs(int(res[0]["lockOutObservationWindow"][0])) / (1e7 * 60)
1325 except Exception as e:
1326 raise CommandError("Could not retrieve password properties!", e)
1328 self.message("Password informations for domain '%s'" % domain_dn)
1330 if pwd_props & DOMAIN_PASSWORD_COMPLEX != 0:
1331 self.message("Password complexity: on")
1333 self.message("Password complexity: off")
1334 if pwd_props & DOMAIN_PASSWORD_STORE_CLEARTEXT != 0:
1335 self.message("Store plaintext passwords: on")
1337 self.message("Store plaintext passwords: off")
1338 self.message("Password history length: %d" % pwd_hist_len)
1339 self.message("Minimum password length: %d" % cur_min_pwd_len)
1340 self.message("Minimum password age (days): %d" % cur_min_pwd_age)
1341 self.message("Maximum password age (days): %d" % cur_max_pwd_age)
1342 self.message("Account lockout duration (mins): %d" % cur_account_lockout_duration)
1343 self.message("Account lockout threshold (attempts): %d" % cur_account_lockout_threshold)
1344 self.message("Reset account lockout after (mins): %d" % cur_reset_account_lockout_after)
1347 class cmd_domain_passwordsettings_set(Command):
1348 """Set password settings.
1350 Password complexity, password lockout policy, history length,
1351 minimum password length, the minimum and maximum password age) on
1352 a Samba AD DC server.
1354 Use against a Windows DC is possible, but group policy will override it.
1357 synopsis = "%prog <options> [options]"
1359 takes_optiongroups = {
1360 "sambaopts": options.SambaOptions,
1361 "versionopts": options.VersionOptions,
1362 "credopts": options.CredentialsOptions,
1366 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1367 metavar="URL", dest="H"),
1368 Option("-q", "--quiet", help="Be quiet", action="store_true"), # unused
1369 Option("--complexity", type="choice", choices=["on", "off", "default"],
1370 help="The password complexity (on | off | default). Default is 'on'"),
1371 Option("--store-plaintext", type="choice", choices=["on", "off", "default"],
1372 help="Store plaintext passwords where account have 'store passwords with reversible encryption' set (on | off | default). Default is 'off'"),
1373 Option("--history-length",
1374 help="The password history length (<integer> | default). Default is 24.", type=str),
1375 Option("--min-pwd-length",
1376 help="The minimum password length (<integer> | default). Default is 7.", type=str),
1377 Option("--min-pwd-age",
1378 help="The minimum password age (<integer in days> | default). Default is 1.", type=str),
1379 Option("--max-pwd-age",
1380 help="The maximum password age (<integer in days> | default). Default is 43.", type=str),
1381 Option("--account-lockout-duration",
1382 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),
1383 Option("--account-lockout-threshold",
1384 help="The number of bad password attempts allowed before locking out the account (<integer> | default). Default is 0 (never lock out).", type=str),
1385 Option("--reset-account-lockout-after",
1386 help="After this time is elapsed, the recorded number of attempts restarts from zero (<integer> | default). Default is 30.", type=str),
1389 def run(self, H=None, min_pwd_age=None, max_pwd_age=None,
1390 quiet=False, complexity=None, store_plaintext=None, history_length=None,
1391 min_pwd_length=None, account_lockout_duration=None, account_lockout_threshold=None,
1392 reset_account_lockout_after=None, credopts=None, sambaopts=None,
1394 lp = sambaopts.get_loadparm()
1395 creds = credopts.get_credentials(lp)
1397 samdb = SamDB(url=H, session_info=system_session(),
1398 credentials=creds, lp=lp)
1400 domain_dn = samdb.domain_dn()
1403 m.dn = ldb.Dn(samdb, domain_dn)
1404 pwd_props = int(samdb.get_pwdProperties())
1406 if complexity is not None:
1407 if complexity == "on" or complexity == "default":
1408 pwd_props = pwd_props | DOMAIN_PASSWORD_COMPLEX
1409 msgs.append("Password complexity activated!")
1410 elif complexity == "off":
1411 pwd_props = pwd_props & (~DOMAIN_PASSWORD_COMPLEX)
1412 msgs.append("Password complexity deactivated!")
1414 if store_plaintext is not None:
1415 if store_plaintext == "on" or store_plaintext == "default":
1416 pwd_props = pwd_props | DOMAIN_PASSWORD_STORE_CLEARTEXT
1417 msgs.append("Plaintext password storage for changed passwords activated!")
1418 elif store_plaintext == "off":
1419 pwd_props = pwd_props & (~DOMAIN_PASSWORD_STORE_CLEARTEXT)
1420 msgs.append("Plaintext password storage for changed passwords deactivated!")
1422 if complexity is not None or store_plaintext is not None:
1423 m["pwdProperties"] = ldb.MessageElement(str(pwd_props),
1424 ldb.FLAG_MOD_REPLACE, "pwdProperties")
1426 if history_length is not None:
1427 if history_length == "default":
1430 pwd_hist_len = int(history_length)
1432 if pwd_hist_len < 0 or pwd_hist_len > 24:
1433 raise CommandError("Password history length must be in the range of 0 to 24!")
1435 m["pwdHistoryLength"] = ldb.MessageElement(str(pwd_hist_len),
1436 ldb.FLAG_MOD_REPLACE, "pwdHistoryLength")
1437 msgs.append("Password history length changed!")
1439 if min_pwd_length is not None:
1440 if min_pwd_length == "default":
1443 min_pwd_len = int(min_pwd_length)
1445 if min_pwd_len < 0 or min_pwd_len > 14:
1446 raise CommandError("Minimum password length must be in the range of 0 to 14!")
1448 m["minPwdLength"] = ldb.MessageElement(str(min_pwd_len),
1449 ldb.FLAG_MOD_REPLACE, "minPwdLength")
1450 msgs.append("Minimum password length changed!")
1452 if min_pwd_age is not None:
1453 if min_pwd_age == "default":
1456 min_pwd_age = int(min_pwd_age)
1458 if min_pwd_age < 0 or min_pwd_age > 998:
1459 raise CommandError("Minimum password age must be in the range of 0 to 998!")
1462 min_pwd_age_ticks = -int(min_pwd_age * (24 * 60 * 60 * 1e7))
1464 m["minPwdAge"] = ldb.MessageElement(str(min_pwd_age_ticks),
1465 ldb.FLAG_MOD_REPLACE, "minPwdAge")
1466 msgs.append("Minimum password age changed!")
1468 if max_pwd_age is not None:
1469 if max_pwd_age == "default":
1472 max_pwd_age = int(max_pwd_age)
1474 if max_pwd_age < 0 or max_pwd_age > 999:
1475 raise CommandError("Maximum password age must be in the range of 0 to 999!")
1478 if max_pwd_age == 0:
1479 max_pwd_age_ticks = -0x8000000000000000
1481 max_pwd_age_ticks = -int(max_pwd_age * (24 * 60 * 60 * 1e7))
1483 m["maxPwdAge"] = ldb.MessageElement(str(max_pwd_age_ticks),
1484 ldb.FLAG_MOD_REPLACE, "maxPwdAge")
1485 msgs.append("Maximum password age changed!")
1487 if account_lockout_duration is not None:
1488 if account_lockout_duration == "default":
1489 account_lockout_duration = 30
1491 account_lockout_duration = int(account_lockout_duration)
1493 if account_lockout_duration < 0 or account_lockout_duration > 99999:
1494 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1497 if account_lockout_duration == 0:
1498 account_lockout_duration_ticks = -0x8000000000000000
1500 account_lockout_duration_ticks = -int(account_lockout_duration * (60 * 1e7))
1502 m["lockoutDuration"] = ldb.MessageElement(str(account_lockout_duration_ticks),
1503 ldb.FLAG_MOD_REPLACE, "lockoutDuration")
1504 msgs.append("Account lockout duration changed!")
1506 if account_lockout_threshold is not None:
1507 if account_lockout_threshold == "default":
1508 account_lockout_threshold = 0
1510 account_lockout_threshold = int(account_lockout_threshold)
1512 m["lockoutThreshold"] = ldb.MessageElement(str(account_lockout_threshold),
1513 ldb.FLAG_MOD_REPLACE, "lockoutThreshold")
1514 msgs.append("Account lockout threshold changed!")
1516 if reset_account_lockout_after is not None:
1517 if reset_account_lockout_after == "default":
1518 reset_account_lockout_after = 30
1520 reset_account_lockout_after = int(reset_account_lockout_after)
1522 if reset_account_lockout_after < 0 or reset_account_lockout_after > 99999:
1523 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1526 if reset_account_lockout_after == 0:
1527 reset_account_lockout_after_ticks = -0x8000000000000000
1529 reset_account_lockout_after_ticks = -int(reset_account_lockout_after * (60 * 1e7))
1531 m["lockOutObservationWindow"] = ldb.MessageElement(str(reset_account_lockout_after_ticks),
1532 ldb.FLAG_MOD_REPLACE, "lockOutObservationWindow")
1533 msgs.append("Duration to reset account lockout after changed!")
1535 if max_pwd_age and max_pwd_age > 0 and min_pwd_age >= max_pwd_age:
1536 raise CommandError("Maximum password age (%d) must be greater than minimum password age (%d)!" % (max_pwd_age, min_pwd_age))
1539 raise CommandError("You must specify at least one option to set. Try --help")
1541 msgs.append("All changes applied successfully!")
1542 self.message("\n".join(msgs))
1545 class cmd_domain_passwordsettings(SuperCommand):
1546 """Manage password policy settings."""
1549 subcommands["pso"] = cmd_domain_passwordsettings_pso()
1550 subcommands["show"] = cmd_domain_passwordsettings_show()
1551 subcommands["set"] = cmd_domain_passwordsettings_set()
1554 class cmd_domain_classicupgrade(Command):
1555 """Upgrade from Samba classic (NT4-like) database to Samba AD DC database.
1557 Specify either a directory with all Samba classic DC databases and state files (with --dbdir) or
1558 the testparm utility from your classic installation (with --testparm).
1561 synopsis = "%prog [options] <classic_smb_conf>"
1563 takes_optiongroups = {
1564 "sambaopts": options.SambaOptions,
1565 "versionopts": options.VersionOptions
1569 Option("--dbdir", type="string", metavar="DIR",
1570 help="Path to samba classic DC database directory"),
1571 Option("--testparm", type="string", metavar="PATH",
1572 help="Path to samba classic DC testparm utility from the previous installation. This allows the default paths of the previous installation to be followed"),
1573 Option("--targetdir", type="string", metavar="DIR",
1574 help="Path prefix where the new Samba 4.0 AD domain should be initialised"),
1575 Option("-q", "--quiet", help="Be quiet", action="store_true"),
1576 Option("-v", "--verbose", help="Be verbose", action="store_true"),
1577 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
1578 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
1579 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
1580 "BIND9_FLATFILE uses bind9 text database to store zone information, "
1581 "BIND9_DLZ uses samba4 AD to store zone information, "
1582 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
1583 default="SAMBA_INTERNAL")
1587 Option("--use-xattrs", type="choice", choices=["yes", "no", "auto"],
1588 metavar="[yes|no|auto]",
1589 help="Define if we should use the native fs capabilities or a tdb file for "
1590 "storing attributes likes ntacl when --use-ntvfs is set. "
1591 "auto tries to make an inteligent guess based on the user rights and system capabilities",
1594 if samba.is_ntvfs_fileserver_built():
1595 takes_options.extend(common_ntvfs_options)
1596 takes_options.extend(ntvfs_options)
1598 takes_args = ["smbconf"]
1600 def run(self, smbconf=None, targetdir=None, dbdir=None, testparm=None,
1601 quiet=False, verbose=False, use_xattrs="auto", sambaopts=None, versionopts=None,
1602 dns_backend=None, use_ntvfs=False):
1604 if not os.path.exists(smbconf):
1605 raise CommandError("File %s does not exist" % smbconf)
1607 if testparm and not os.path.exists(testparm):
1608 raise CommandError("Testparm utility %s does not exist" % testparm)
1610 if dbdir and not os.path.exists(dbdir):
1611 raise CommandError("Directory %s does not exist" % dbdir)
1613 if not dbdir and not testparm:
1614 raise CommandError("Please specify either dbdir or testparm")
1616 logger = self.get_logger()
1618 logger.setLevel(logging.DEBUG)
1620 logger.setLevel(logging.WARNING)
1622 logger.setLevel(logging.INFO)
1624 if dbdir and testparm:
1625 logger.warning("both dbdir and testparm specified, ignoring dbdir.")
1628 lp = sambaopts.get_loadparm()
1630 s3conf = s3param.get_context()
1633 s3conf.set("realm", sambaopts.realm)
1635 if targetdir is not None:
1636 if not os.path.isdir(targetdir):
1640 if use_xattrs == "yes":
1642 elif use_xattrs == "auto" and use_ntvfs == False:
1644 elif use_ntvfs == False:
1645 raise CommandError("--use-xattrs=no requires --use-ntvfs (not supported for production use). "
1646 "Please re-run with --use-xattrs omitted.")
1647 elif use_xattrs == "auto" and not s3conf.get("posix:eadb"):
1649 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
1651 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
1654 samba.ntacls.setntacl(lp, tmpfile.name,
1655 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
1658 # FIXME: Don't catch all exceptions here
1659 logger.info("You are not root or your system does not support xattr, using tdb backend for attributes. "
1660 "If you intend to use this provision in production, rerun the script as root on a system supporting xattrs.")
1664 # Set correct default values from dbdir or testparm
1667 paths["state directory"] = dbdir
1668 paths["private dir"] = dbdir
1669 paths["lock directory"] = dbdir
1670 paths["smb passwd file"] = dbdir + "/smbpasswd"
1672 paths["state directory"] = get_testparm_var(testparm, smbconf, "state directory")
1673 paths["private dir"] = get_testparm_var(testparm, smbconf, "private dir")
1674 paths["smb passwd file"] = get_testparm_var(testparm, smbconf, "smb passwd file")
1675 paths["lock directory"] = get_testparm_var(testparm, smbconf, "lock directory")
1676 # "testparm" from Samba 3 < 3.4.x is not aware of the parameter
1677 # "state directory", instead make use of "lock directory"
1678 if len(paths["state directory"]) == 0:
1679 paths["state directory"] = paths["lock directory"]
1682 s3conf.set(p, paths[p])
1684 # load smb.conf parameters
1685 logger.info("Reading smb.conf")
1686 s3conf.load(smbconf)
1687 samba3 = Samba3(smbconf, s3conf)
1689 logger.info("Provisioning")
1690 upgrade_from_samba3(samba3, logger, targetdir, session_info=system_session(),
1691 useeadb=eadb, dns_backend=dns_backend, use_ntvfs=use_ntvfs)
1694 class cmd_domain_samba3upgrade(cmd_domain_classicupgrade):
1695 __doc__ = cmd_domain_classicupgrade.__doc__
1697 # This command is present for backwards compatibility only,
1698 # and should not be shown.
1703 class LocalDCCredentialsOptions(options.CredentialsOptions):
1704 def __init__(self, parser):
1705 options.CredentialsOptions.__init__(self, parser, special_name="local-dc")
1708 class DomainTrustCommand(Command):
1709 """List domain trusts."""
1712 Command.__init__(self)
1713 self.local_lp = None
1715 self.local_server = None
1716 self.local_binding_string = None
1717 self.local_creds = None
1719 self.remote_server = None
1720 self.remote_binding_string = None
1721 self.remote_creds = None
1723 def _uint32(self, v):
1724 return ctypes.c_uint32(v).value
1726 def check_runtime_error(self, runtime, val):
1730 err32 = self._uint32(runtime.args[0])
1736 class LocalRuntimeError(CommandError):
1737 def __init__(exception_self, self, runtime, message):
1738 err32 = self._uint32(runtime.args[0])
1739 errstr = runtime.args[1]
1740 msg = "LOCAL_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1741 self.local_server, message, err32, errstr)
1742 CommandError.__init__(exception_self, msg)
1744 class RemoteRuntimeError(CommandError):
1745 def __init__(exception_self, self, runtime, message):
1746 err32 = self._uint32(runtime.args[0])
1747 errstr = runtime.args[1]
1748 msg = "REMOTE_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1749 self.remote_server, message, err32, errstr)
1750 CommandError.__init__(exception_self, msg)
1752 class LocalLdbError(CommandError):
1753 def __init__(exception_self, self, ldb_error, message):
1754 errval = ldb_error.args[0]
1755 errstr = ldb_error.args[1]
1756 msg = "LOCAL_DC[%s]: %s - ERROR(%d) - %s" % (
1757 self.local_server, message, errval, errstr)
1758 CommandError.__init__(exception_self, msg)
1760 def setup_local_server(self, sambaopts, localdcopts):
1761 if self.local_server is not None:
1762 return self.local_server
1764 lp = sambaopts.get_loadparm()
1766 local_server = localdcopts.ipaddress
1767 if local_server is None:
1768 server_role = lp.server_role()
1769 if server_role != "ROLE_ACTIVE_DIRECTORY_DC":
1770 raise CommandError("Invalid server_role %s" % (server_role))
1771 local_server = lp.get('netbios name')
1772 local_transport = "ncalrpc"
1773 local_binding_options = ""
1774 local_binding_options += ",auth_type=ncalrpc_as_system"
1775 local_ldap_url = None
1778 local_transport = "ncacn_np"
1779 local_binding_options = ""
1780 local_ldap_url = "ldap://%s" % local_server
1781 local_creds = localdcopts.get_credentials(lp)
1785 self.local_server = local_server
1786 self.local_binding_string = "%s:%s[%s]" % (local_transport, local_server, local_binding_options)
1787 self.local_ldap_url = local_ldap_url
1788 self.local_creds = local_creds
1789 return self.local_server
1791 def new_local_lsa_connection(self):
1792 return lsa.lsarpc(self.local_binding_string, self.local_lp, self.local_creds)
1794 def new_local_netlogon_connection(self):
1795 return netlogon.netlogon(self.local_binding_string, self.local_lp, self.local_creds)
1797 def new_local_ldap_connection(self):
1798 return SamDB(url=self.local_ldap_url,
1799 session_info=system_session(),
1800 credentials=self.local_creds,
1803 def setup_remote_server(self, credopts, domain,
1805 require_writable=True):
1808 assert require_writable
1810 if self.remote_server is not None:
1811 return self.remote_server
1813 self.remote_server = "__unknown__remote_server__.%s" % domain
1814 assert self.local_server is not None
1816 remote_creds = credopts.get_credentials(self.local_lp)
1817 remote_server = credopts.ipaddress
1818 remote_binding_options = ""
1820 # TODO: we should also support NT4 domains
1821 # we could use local_netlogon.netr_DsRGetDCNameEx2() with the remote domain name
1822 # and delegate NBT or CLDAP to the local netlogon server
1824 remote_net = Net(remote_creds, self.local_lp, server=remote_server)
1825 remote_flags = nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS
1826 if require_writable:
1827 remote_flags |= nbt.NBT_SERVER_WRITABLE
1829 remote_flags |= nbt.NBT_SERVER_PDC
1830 remote_info = remote_net.finddc(flags=remote_flags, domain=domain, address=remote_server)
1831 except NTSTATUSError as error:
1832 raise CommandError("Failed to find a writeable DC for domain '%s': %s" %
1835 raise CommandError("Failed to find a writeable DC for domain '%s'" % domain)
1837 nbt.NBT_SERVER_PDC: "PDC",
1838 nbt.NBT_SERVER_GC: "GC",
1839 nbt.NBT_SERVER_LDAP: "LDAP",
1840 nbt.NBT_SERVER_DS: "DS",
1841 nbt.NBT_SERVER_KDC: "KDC",
1842 nbt.NBT_SERVER_TIMESERV: "TIMESERV",
1843 nbt.NBT_SERVER_CLOSEST: "CLOSEST",
1844 nbt.NBT_SERVER_WRITABLE: "WRITABLE",
1845 nbt.NBT_SERVER_GOOD_TIMESERV: "GOOD_TIMESERV",
1846 nbt.NBT_SERVER_NDNC: "NDNC",
1847 nbt.NBT_SERVER_SELECT_SECRET_DOMAIN_6: "SELECT_SECRET_DOMAIN_6",
1848 nbt.NBT_SERVER_FULL_SECRET_DOMAIN_6: "FULL_SECRET_DOMAIN_6",
1849 nbt.NBT_SERVER_ADS_WEB_SERVICE: "ADS_WEB_SERVICE",
1850 nbt.NBT_SERVER_DS_8: "DS_8",
1851 nbt.NBT_SERVER_HAS_DNS_NAME: "HAS_DNS_NAME",
1852 nbt.NBT_SERVER_IS_DEFAULT_NC: "IS_DEFAULT_NC",
1853 nbt.NBT_SERVER_FOREST_ROOT: "FOREST_ROOT",
1855 server_type_string = self.generic_bitmap_to_string(flag_map,
1856 remote_info.server_type, names_only=True)
1857 self.outf.write("RemoteDC Netbios[%s] DNS[%s] ServerType[%s]\n" % (
1858 remote_info.pdc_name,
1859 remote_info.pdc_dns_name,
1860 server_type_string))
1862 self.remote_server = remote_info.pdc_dns_name
1863 self.remote_binding_string = "ncacn_np:%s[%s]" % (self.remote_server, remote_binding_options)
1864 self.remote_creds = remote_creds
1865 return self.remote_server
1867 def new_remote_lsa_connection(self):
1868 return lsa.lsarpc(self.remote_binding_string, self.local_lp, self.remote_creds)
1870 def new_remote_netlogon_connection(self):
1871 return netlogon.netlogon(self.remote_binding_string, self.local_lp, self.remote_creds)
1873 def get_lsa_info(self, conn, policy_access):
1874 objectAttr = lsa.ObjectAttribute()
1875 objectAttr.sec_qos = lsa.QosInfo()
1877 policy = conn.OpenPolicy2(''.decode('utf-8'),
1878 objectAttr, policy_access)
1880 info = conn.QueryInfoPolicy2(policy, lsa.LSA_POLICY_INFO_DNS)
1882 return (policy, info)
1884 def get_netlogon_dc_unc(self, conn, server, domain):
1886 info = conn.netr_DsRGetDCNameEx2(server,
1887 None, 0, None, None, None,
1888 netlogon.DS_RETURN_DNS_NAME)
1890 except RuntimeError:
1891 return conn.netr_GetDcName(server, domain)
1893 def get_netlogon_dc_info(self, conn, server):
1894 info = conn.netr_DsRGetDCNameEx2(server,
1895 None, 0, None, None, None,
1896 netlogon.DS_RETURN_DNS_NAME)
1899 def netr_DomainTrust_to_name(self, t):
1900 if t.trust_type == lsa.LSA_TRUST_TYPE_DOWNLEVEL:
1901 return t.netbios_name
1905 def netr_DomainTrust_to_type(self, a, t):
1907 primary_parent = None
1909 if _t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
1911 if not _t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1912 primary_parent = a[_t.parent_index]
1915 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1916 if t is primary_parent:
1919 if t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1922 parent = a[t.parent_index]
1923 if parent is primary:
1928 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1933 def netr_DomainTrust_to_transitive(self, t):
1934 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1937 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE:
1940 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1945 def netr_DomainTrust_to_direction(self, t):
1946 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND and \
1947 t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1950 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND:
1953 if t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1958 def generic_enum_to_string(self, e_dict, v, names_only=False):
1962 v32 = self._uint32(v)
1963 w = "__unknown__%08X__" % v32
1965 r = "0x%x (%s)" % (v, w)
1968 def generic_bitmap_to_string(self, b_dict, v, names_only=False):
1973 for b in sorted(b_dict.keys()):
1980 c32 = self._uint32(c)
1981 s += ["__unknown_%08X__" % c32]
1986 r = "0x%x (%s)" % (v, w)
1989 def trustType_string(self, v):
1991 lsa.LSA_TRUST_TYPE_DOWNLEVEL: "DOWNLEVEL",
1992 lsa.LSA_TRUST_TYPE_UPLEVEL: "UPLEVEL",
1993 lsa.LSA_TRUST_TYPE_MIT: "MIT",
1994 lsa.LSA_TRUST_TYPE_DCE: "DCE",
1996 return self.generic_enum_to_string(types, v)
1998 def trustDirection_string(self, v):
2000 lsa.LSA_TRUST_DIRECTION_INBOUND |
2001 lsa.LSA_TRUST_DIRECTION_OUTBOUND: "BOTH",
2002 lsa.LSA_TRUST_DIRECTION_INBOUND: "INBOUND",
2003 lsa.LSA_TRUST_DIRECTION_OUTBOUND: "OUTBOUND",
2005 return self.generic_enum_to_string(directions, v)
2007 def trustAttributes_string(self, v):
2009 lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE: "NON_TRANSITIVE",
2010 lsa.LSA_TRUST_ATTRIBUTE_UPLEVEL_ONLY: "UPLEVEL_ONLY",
2011 lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN: "QUARANTINED_DOMAIN",
2012 lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE: "FOREST_TRANSITIVE",
2013 lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION: "CROSS_ORGANIZATION",
2014 lsa.LSA_TRUST_ATTRIBUTE_WITHIN_FOREST: "WITHIN_FOREST",
2015 lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL: "TREAT_AS_EXTERNAL",
2016 lsa.LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION: "USES_RC4_ENCRYPTION",
2018 return self.generic_bitmap_to_string(attributes, v)
2020 def kerb_EncTypes_string(self, v):
2022 security.KERB_ENCTYPE_DES_CBC_CRC: "DES_CBC_CRC",
2023 security.KERB_ENCTYPE_DES_CBC_MD5: "DES_CBC_MD5",
2024 security.KERB_ENCTYPE_RC4_HMAC_MD5: "RC4_HMAC_MD5",
2025 security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96: "AES128_CTS_HMAC_SHA1_96",
2026 security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96: "AES256_CTS_HMAC_SHA1_96",
2027 security.KERB_ENCTYPE_FAST_SUPPORTED: "FAST_SUPPORTED",
2028 security.KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED: "COMPOUND_IDENTITY_SUPPORTED",
2029 security.KERB_ENCTYPE_CLAIMS_SUPPORTED: "CLAIMS_SUPPORTED",
2030 security.KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED: "RESOURCE_SID_COMPRESSION_DISABLED",
2032 return self.generic_bitmap_to_string(enctypes, v)
2034 def entry_tln_status(self, e_flags, ):
2036 return "Status[Enabled]"
2039 lsa.LSA_TLN_DISABLED_NEW: "Disabled-New",
2040 lsa.LSA_TLN_DISABLED_ADMIN: "Disabled",
2041 lsa.LSA_TLN_DISABLED_CONFLICT: "Disabled-Conflicting",
2043 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
2045 def entry_dom_status(self, e_flags):
2047 return "Status[Enabled]"
2050 lsa.LSA_SID_DISABLED_ADMIN: "Disabled-SID",
2051 lsa.LSA_SID_DISABLED_CONFLICT: "Disabled-SID-Conflicting",
2052 lsa.LSA_NB_DISABLED_ADMIN: "Disabled-NB",
2053 lsa.LSA_NB_DISABLED_CONFLICT: "Disabled-NB-Conflicting",
2055 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
2057 def write_forest_trust_info(self, fti, tln=None, collisions=None):
2059 tln_string = " TDO[%s]" % tln
2063 self.outf.write("Namespaces[%d]%s:\n" % (
2064 len(fti.entries), tln_string))
2066 for i, e in enumerate(fti.entries):
2069 collision_string = ""
2071 if collisions is not None:
2072 for c in collisions.entries:
2076 collision_string = " Collision[%s]" % (c.name.string)
2078 d = e.forest_trust_data
2079 if e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
2080 self.outf.write("TLN: %-32s DNS[*.%s]%s\n" % (
2081 self.entry_tln_status(flags),
2082 d.string, collision_string))
2083 elif e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
2084 self.outf.write("TLN_EX: %-29s DNS[*.%s]\n" % (
2086 elif e.type == lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
2087 self.outf.write("DOM: %-32s DNS[%s] Netbios[%s] SID[%s]%s\n" % (
2088 self.entry_dom_status(flags),
2089 d.dns_domain_name.string,
2090 d.netbios_domain_name.string,
2091 d.domain_sid, collision_string))
2095 class cmd_domain_trust_list(DomainTrustCommand):
2096 """List domain trusts."""
2098 synopsis = "%prog [options]"
2100 takes_optiongroups = {
2101 "sambaopts": options.SambaOptions,
2102 "versionopts": options.VersionOptions,
2103 "localdcopts": LocalDCCredentialsOptions,
2109 def run(self, sambaopts=None, versionopts=None, localdcopts=None):
2111 local_server = self.setup_local_server(sambaopts, localdcopts)
2113 local_netlogon = self.new_local_netlogon_connection()
2114 except RuntimeError as error:
2115 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2118 local_netlogon_trusts = \
2119 local_netlogon.netr_DsrEnumerateDomainTrusts(local_server,
2120 netlogon.NETR_TRUST_FLAG_IN_FOREST |
2121 netlogon.NETR_TRUST_FLAG_OUTBOUND |
2122 netlogon.NETR_TRUST_FLAG_INBOUND)
2123 except RuntimeError as error:
2124 if self.check_runtime_error(error, werror.WERR_RPC_S_PROCNUM_OUT_OF_RANGE):
2125 # TODO: we could implement a fallback to lsa.EnumTrustDom()
2126 raise CommandError("LOCAL_DC[%s]: netr_DsrEnumerateDomainTrusts not supported." % (
2128 raise self.LocalRuntimeError(self, error, "netr_DsrEnumerateDomainTrusts failed")
2130 a = local_netlogon_trusts.array
2132 if t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
2134 self.outf.write("%-14s %-15s %-19s %s\n" % (
2135 "Type[%s]" % self.netr_DomainTrust_to_type(a, t),
2136 "Transitive[%s]" % self.netr_DomainTrust_to_transitive(t),
2137 "Direction[%s]" % self.netr_DomainTrust_to_direction(t),
2138 "Name[%s]" % self.netr_DomainTrust_to_name(t)))
2142 class cmd_domain_trust_show(DomainTrustCommand):
2143 """Show trusted domain details."""
2145 synopsis = "%prog NAME [options]"
2147 takes_optiongroups = {
2148 "sambaopts": options.SambaOptions,
2149 "versionopts": options.VersionOptions,
2150 "localdcopts": LocalDCCredentialsOptions,
2156 takes_args = ["domain"]
2158 def run(self, domain, sambaopts=None, versionopts=None, localdcopts=None):
2160 local_server = self.setup_local_server(sambaopts, localdcopts)
2162 local_lsa = self.new_local_lsa_connection()
2163 except RuntimeError as error:
2164 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2167 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2168 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2169 except RuntimeError as error:
2170 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2172 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2173 local_lsa_info.name.string,
2174 local_lsa_info.dns_domain.string,
2175 local_lsa_info.sid))
2177 lsaString = lsa.String()
2178 lsaString.string = domain
2181 local_lsa.QueryTrustedDomainInfoByName(local_policy,
2183 lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2184 local_tdo_info = local_tdo_full.info_ex
2185 local_tdo_posix = local_tdo_full.posix_offset
2186 except NTSTATUSError as error:
2187 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2188 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2190 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(FULL_INFO) failed")
2193 local_tdo_enctypes = \
2194 local_lsa.QueryTrustedDomainInfoByName(local_policy,
2196 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES)
2197 except NTSTATUSError as error:
2198 if self.check_runtime_error(error, ntstatus.NT_STATUS_INVALID_PARAMETER):
2200 if self.check_runtime_error(error, ntstatus.NT_STATUS_INVALID_INFO_CLASS):
2203 if error is not None:
2204 raise self.LocalRuntimeError(self, error,
2205 "QueryTrustedDomainInfoByName(SUPPORTED_ENCRYPTION_TYPES) failed")
2207 local_tdo_enctypes = lsa.TrustDomainInfoSupportedEncTypes()
2208 local_tdo_enctypes.enc_types = 0
2211 local_tdo_forest = None
2212 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2213 local_tdo_forest = \
2214 local_lsa.lsaRQueryForestTrustInformation(local_policy,
2216 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
2217 except RuntimeError as error:
2218 if self.check_runtime_error(error, ntstatus.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
2220 if self.check_runtime_error(error, ntstatus.NT_STATUS_NOT_FOUND):
2222 if error is not None:
2223 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation failed")
2225 local_tdo_forest = lsa.ForestTrustInformation()
2226 local_tdo_forest.count = 0
2227 local_tdo_forest.entries = []
2229 self.outf.write("TrustedDomain:\n\n")
2230 self.outf.write("NetbiosName: %s\n" % local_tdo_info.netbios_name.string)
2231 if local_tdo_info.netbios_name.string != local_tdo_info.domain_name.string:
2232 self.outf.write("DnsName: %s\n" % local_tdo_info.domain_name.string)
2233 self.outf.write("SID: %s\n" % local_tdo_info.sid)
2234 self.outf.write("Type: %s\n" % self.trustType_string(local_tdo_info.trust_type))
2235 self.outf.write("Direction: %s\n" % self.trustDirection_string(local_tdo_info.trust_direction))
2236 self.outf.write("Attributes: %s\n" % self.trustAttributes_string(local_tdo_info.trust_attributes))
2237 posix_offset_u32 = ctypes.c_uint32(local_tdo_posix.posix_offset).value
2238 posix_offset_i32 = ctypes.c_int32(local_tdo_posix.posix_offset).value
2239 self.outf.write("PosixOffset: 0x%08X (%d)\n" % (posix_offset_u32, posix_offset_i32))
2240 self.outf.write("kerb_EncTypes: %s\n" % self.kerb_EncTypes_string(local_tdo_enctypes.enc_types))
2242 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2243 self.write_forest_trust_info(local_tdo_forest,
2244 tln=local_tdo_info.domain_name.string)
2249 class cmd_domain_trust_create(DomainTrustCommand):
2250 """Create a domain or forest trust."""
2252 synopsis = "%prog DOMAIN [options]"
2254 takes_optiongroups = {
2255 "sambaopts": options.SambaOptions,
2256 "versionopts": options.VersionOptions,
2257 "credopts": options.CredentialsOptions,
2258 "localdcopts": LocalDCCredentialsOptions,
2262 Option("--type", type="choice", metavar="TYPE",
2263 choices=["external", "forest"],
2264 help="The type of the trust: 'external' or 'forest'.",
2266 default="external"),
2267 Option("--direction", type="choice", metavar="DIRECTION",
2268 choices=["incoming", "outgoing", "both"],
2269 help="The trust direction: 'incoming', 'outgoing' or 'both'.",
2270 dest='trust_direction',
2272 Option("--create-location", type="choice", metavar="LOCATION",
2273 choices=["local", "both"],
2274 help="Where to create the trusted domain object: 'local' or 'both'.",
2275 dest='create_location',
2277 Option("--cross-organisation", action="store_true",
2278 help="The related domains does not belong to the same organisation.",
2279 dest='cross_organisation',
2281 Option("--quarantined", type="choice", metavar="yes|no",
2282 choices=["yes", "no", None],
2283 help="Special SID filtering rules are applied to the trust. "
2284 "With --type=external the default is yes. "
2285 "With --type=forest the default is no.",
2286 dest='quarantined_arg',
2288 Option("--not-transitive", action="store_true",
2289 help="The forest trust is not transitive.",
2290 dest='not_transitive',
2292 Option("--treat-as-external", action="store_true",
2293 help="The treat the forest trust as external.",
2294 dest='treat_as_external',
2296 Option("--no-aes-keys", action="store_false",
2297 help="The trust uses aes kerberos keys.",
2298 dest='use_aes_keys',
2300 Option("--skip-validation", action="store_false",
2301 help="Skip validation of the trust.",
2306 takes_args = ["domain"]
2308 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2309 trust_type=None, trust_direction=None, create_location=None,
2310 cross_organisation=False, quarantined_arg=None,
2311 not_transitive=False, treat_as_external=False,
2312 use_aes_keys=False, validate=True):
2314 lsaString = lsa.String()
2317 if quarantined_arg is None:
2318 if trust_type == 'external':
2320 elif quarantined_arg == 'yes':
2323 if trust_type != 'forest':
2325 raise CommandError("--not-transitive requires --type=forest")
2326 if treat_as_external:
2327 raise CommandError("--treat-as-external requires --type=forest")
2331 enc_types = lsa.TrustDomainInfoSupportedEncTypes()
2332 enc_types.enc_types = security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96
2333 enc_types.enc_types |= security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96
2335 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2336 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2337 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2339 local_trust_info = lsa.TrustDomainInfoInfoEx()
2340 local_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2341 local_trust_info.trust_direction = 0
2342 if trust_direction == "both":
2343 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2344 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2345 elif trust_direction == "incoming":
2346 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2347 elif trust_direction == "outgoing":
2348 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2349 local_trust_info.trust_attributes = 0
2350 if cross_organisation:
2351 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2353 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2354 if trust_type == "forest":
2355 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2357 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2358 if treat_as_external:
2359 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2361 def get_password(name):
2364 if password is not None and password is not '':
2366 password = getpass("New %s Password: " % name)
2367 passwordverify = getpass("Retype %s Password: " % name)
2368 if not password == passwordverify:
2370 self.outf.write("Sorry, passwords do not match.\n")
2372 incoming_secret = None
2373 outgoing_secret = None
2374 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2375 if create_location == "local":
2376 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2377 incoming_password = get_password("Incoming Trust")
2378 incoming_secret = string_to_byte_array(incoming_password.encode('utf-16-le'))
2379 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2380 outgoing_password = get_password("Outgoing Trust")
2381 outgoing_secret = string_to_byte_array(outgoing_password.encode('utf-16-le'))
2383 remote_trust_info = None
2385 # We use 240 random bytes.
2386 # Windows uses 28 or 240 random bytes. I guess it's
2387 # based on the trust type external vs. forest.
2389 # The initial trust password can be up to 512 bytes
2390 # while the versioned passwords used for periodic updates
2391 # can only be up to 498 bytes, as netr_ServerPasswordSet2()
2392 # needs to pass the NL_PASSWORD_VERSION structure within the
2393 # 512 bytes and a 2 bytes confounder is required.
2395 def random_trust_secret(length):
2396 pw = samba.generate_random_machine_password(length // 2, length // 2)
2397 return string_to_byte_array(pw.encode('utf-16-le'))
2399 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2400 incoming_secret = random_trust_secret(240)
2401 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2402 outgoing_secret = random_trust_secret(240)
2404 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2405 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2407 remote_trust_info = lsa.TrustDomainInfoInfoEx()
2408 remote_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2409 remote_trust_info.trust_direction = 0
2410 if trust_direction == "both":
2411 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2412 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2413 elif trust_direction == "incoming":
2414 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2415 elif trust_direction == "outgoing":
2416 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2417 remote_trust_info.trust_attributes = 0
2418 if cross_organisation:
2419 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2421 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2422 if trust_type == "forest":
2423 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2425 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2426 if treat_as_external:
2427 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2429 local_server = self.setup_local_server(sambaopts, localdcopts)
2431 local_lsa = self.new_local_lsa_connection()
2432 except RuntimeError as error:
2433 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2436 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2437 except RuntimeError as error:
2438 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2440 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2441 local_lsa_info.name.string,
2442 local_lsa_info.dns_domain.string,
2443 local_lsa_info.sid))
2446 remote_server = self.setup_remote_server(credopts, domain)
2447 except RuntimeError as error:
2448 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2451 remote_lsa = self.new_remote_lsa_connection()
2452 except RuntimeError as error:
2453 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2456 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2457 except RuntimeError as error:
2458 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2460 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2461 remote_lsa_info.name.string,
2462 remote_lsa_info.dns_domain.string,
2463 remote_lsa_info.sid))
2465 local_trust_info.domain_name.string = remote_lsa_info.dns_domain.string
2466 local_trust_info.netbios_name.string = remote_lsa_info.name.string
2467 local_trust_info.sid = remote_lsa_info.sid
2469 if remote_trust_info:
2470 remote_trust_info.domain_name.string = local_lsa_info.dns_domain.string
2471 remote_trust_info.netbios_name.string = local_lsa_info.name.string
2472 remote_trust_info.sid = local_lsa_info.sid
2475 lsaString.string = local_trust_info.domain_name.string
2476 local_old_netbios = \
2477 local_lsa.QueryTrustedDomainInfoByName(local_policy,
2479 lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2480 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2481 except NTSTATUSError as error:
2482 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2483 raise self.LocalRuntimeError(self, error,
2484 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2488 lsaString.string = local_trust_info.netbios_name.string
2490 local_lsa.QueryTrustedDomainInfoByName(local_policy,
2492 lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2493 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2494 except NTSTATUSError as error:
2495 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2496 raise self.LocalRuntimeError(self, error,
2497 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2500 if remote_trust_info:
2502 lsaString.string = remote_trust_info.domain_name.string
2503 remote_old_netbios = \
2504 remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2506 lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2507 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2508 except NTSTATUSError as error:
2509 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2510 raise self.RemoteRuntimeError(self, error,
2511 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2515 lsaString.string = remote_trust_info.netbios_name.string
2517 remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2519 lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2520 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2521 except NTSTATUSError as error:
2522 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2523 raise self.RemoteRuntimeError(self, error,
2524 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2528 local_netlogon = self.new_local_netlogon_connection()
2529 except RuntimeError as error:
2530 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2533 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
2534 except RuntimeError as error:
2535 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
2537 if remote_trust_info:
2539 remote_netlogon = self.new_remote_netlogon_connection()
2540 except RuntimeError as error:
2541 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2544 remote_netlogon_dc_unc = self.get_netlogon_dc_unc(remote_netlogon,
2545 remote_server, domain)
2546 except RuntimeError as error:
2547 raise self.RemoteRuntimeError(self, error, "failed to get netlogon dc info")
2549 def generate_AuthInOutBlob(secret, update_time):
2551 blob = drsblobs.trustAuthInOutBlob()
2556 clear = drsblobs.AuthInfoClear()
2557 clear.size = len(secret)
2558 clear.password = secret
2560 info = drsblobs.AuthenticationInformation()
2561 info.LastUpdateTime = samba.unix2nttime(update_time)
2562 info.AuthType = lsa.TRUST_AUTH_TYPE_CLEAR
2563 info.AuthInfo = clear
2565 array = drsblobs.AuthenticationInformationArray()
2567 array.array = [info]
2569 blob = drsblobs.trustAuthInOutBlob()
2571 blob.current = array
2575 def generate_AuthInfoInternal(session_key, incoming=None, outgoing=None):
2576 confounder = [0] * 512
2577 for i in range(len(confounder)):
2578 confounder[i] = random.randint(0, 255)
2580 trustpass = drsblobs.trustDomainPasswords()
2582 trustpass.confounder = confounder
2583 trustpass.outgoing = outgoing
2584 trustpass.incoming = incoming
2586 trustpass_blob = ndr_pack(trustpass)
2588 encrypted_trustpass = arcfour_encrypt(session_key, trustpass_blob)
2590 auth_blob = lsa.DATA_BUF2()
2591 auth_blob.size = len(encrypted_trustpass)
2592 auth_blob.data = string_to_byte_array(encrypted_trustpass)
2594 auth_info = lsa.TrustDomainInfoAuthInfoInternal()
2595 auth_info.auth_blob = auth_blob
2599 update_time = samba.current_unix_time()
2600 incoming_blob = generate_AuthInOutBlob(incoming_secret, update_time)
2601 outgoing_blob = generate_AuthInOutBlob(outgoing_secret, update_time)
2603 local_tdo_handle = None
2604 remote_tdo_handle = None
2606 local_auth_info = generate_AuthInfoInternal(local_lsa.session_key,
2607 incoming=incoming_blob,
2608 outgoing=outgoing_blob)
2609 if remote_trust_info:
2610 remote_auth_info = generate_AuthInfoInternal(remote_lsa.session_key,
2611 incoming=outgoing_blob,
2612 outgoing=incoming_blob)
2615 if remote_trust_info:
2616 self.outf.write("Creating remote TDO.\n")
2617 current_request = {"location": "remote", "name": "CreateTrustedDomainEx2"}
2618 remote_tdo_handle = \
2619 remote_lsa.CreateTrustedDomainEx2(remote_policy,
2622 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2623 self.outf.write("Remote TDO created.\n")
2625 self.outf.write("Setting supported encryption types on remote TDO.\n")
2626 current_request = {"location": "remote", "name": "SetInformationTrustedDomain"}
2627 remote_lsa.SetInformationTrustedDomain(remote_tdo_handle,
2628 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2631 self.outf.write("Creating local TDO.\n")
2632 current_request = {"location": "local", "name": "CreateTrustedDomainEx2"}
2633 local_tdo_handle = local_lsa.CreateTrustedDomainEx2(local_policy,
2636 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2637 self.outf.write("Local TDO created\n")
2639 self.outf.write("Setting supported encryption types on local TDO.\n")
2640 current_request = {"location": "local", "name": "SetInformationTrustedDomain"}
2641 local_lsa.SetInformationTrustedDomain(local_tdo_handle,
2642 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2644 except RuntimeError as error:
2645 self.outf.write("Error: %s failed %sly - cleaning up\n" % (
2646 current_request['name'], current_request['location']))
2647 if remote_tdo_handle:
2648 self.outf.write("Deleting remote TDO.\n")
2649 remote_lsa.DeleteObject(remote_tdo_handle)
2650 remote_tdo_handle = None
2651 if local_tdo_handle:
2652 self.outf.write("Deleting local TDO.\n")
2653 local_lsa.DeleteObject(local_tdo_handle)
2654 local_tdo_handle = None
2655 if current_request['location'] is "remote":
2656 raise self.RemoteRuntimeError(self, error, "%s" % (
2657 current_request['name']))
2658 raise self.LocalRuntimeError(self, error, "%s" % (
2659 current_request['name']))
2662 if local_trust_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2663 self.outf.write("Setup local forest trust information...\n")
2665 # get all information about the remote trust
2666 # this triggers netr_GetForestTrustInformation to the remote domain
2667 # and lsaRSetForestTrustInformation() locally, but new top level
2668 # names are disabled by default.
2669 local_forest_info = \
2670 local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
2671 remote_lsa_info.dns_domain.string,
2672 netlogon.DS_GFTI_UPDATE_TDO)
2673 except RuntimeError as error:
2674 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2677 # here we try to enable all top level names
2678 local_forest_collision = \
2679 local_lsa.lsaRSetForestTrustInformation(local_policy,
2680 remote_lsa_info.dns_domain,
2681 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2684 except RuntimeError as error:
2685 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2687 self.write_forest_trust_info(local_forest_info,
2688 tln=remote_lsa_info.dns_domain.string,
2689 collisions=local_forest_collision)
2691 if remote_trust_info:
2692 self.outf.write("Setup remote forest trust information...\n")
2694 # get all information about the local trust (from the perspective of the remote domain)
2695 # this triggers netr_GetForestTrustInformation to our domain.
2696 # and lsaRSetForestTrustInformation() remotely, but new top level
2697 # names are disabled by default.
2698 remote_forest_info = \
2699 remote_netlogon.netr_DsRGetForestTrustInformation(remote_netlogon_dc_unc,
2700 local_lsa_info.dns_domain.string,
2701 netlogon.DS_GFTI_UPDATE_TDO)
2702 except RuntimeError as error:
2703 raise self.RemoteRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2706 # here we try to enable all top level names
2707 remote_forest_collision = \
2708 remote_lsa.lsaRSetForestTrustInformation(remote_policy,
2709 local_lsa_info.dns_domain,
2710 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2713 except RuntimeError as error:
2714 raise self.RemoteRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2716 self.write_forest_trust_info(remote_forest_info,
2717 tln=local_lsa_info.dns_domain.string,
2718 collisions=remote_forest_collision)
2720 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2721 self.outf.write("Validating outgoing trust...\n")
2723 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_netlogon_info.dc_unc,
2724 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2726 remote_lsa_info.dns_domain.string)
2727 except RuntimeError as error:
2728 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2730 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2731 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2733 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2734 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2735 local_trust_verify.trusted_dc_name,
2736 local_trust_verify.tc_connection_status[1],
2737 local_trust_verify.pdc_connection_status[1])
2739 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2740 local_trust_verify.trusted_dc_name,
2741 local_trust_verify.tc_connection_status[1],
2742 local_trust_verify.pdc_connection_status[1])
2744 if local_trust_status != werror.WERR_SUCCESS or local_conn_status != werror.WERR_SUCCESS:
2745 raise CommandError(local_validation)
2747 self.outf.write("OK: %s\n" % local_validation)
2749 if remote_trust_info:
2750 if remote_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2751 self.outf.write("Validating incoming trust...\n")
2753 remote_trust_verify = \
2754 remote_netlogon.netr_LogonControl2Ex(remote_netlogon_dc_unc,
2755 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2757 local_lsa_info.dns_domain.string)
2758 except RuntimeError as error:
2759 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2761 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2762 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2764 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2765 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2766 remote_trust_verify.trusted_dc_name,
2767 remote_trust_verify.tc_connection_status[1],
2768 remote_trust_verify.pdc_connection_status[1])
2770 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2771 remote_trust_verify.trusted_dc_name,
2772 remote_trust_verify.tc_connection_status[1],
2773 remote_trust_verify.pdc_connection_status[1])
2775 if remote_trust_status != werror.WERR_SUCCESS or remote_conn_status != werror.WERR_SUCCESS:
2776 raise CommandError(remote_validation)
2778 self.outf.write("OK: %s\n" % remote_validation)
2780 if remote_tdo_handle is not None:
2782 remote_lsa.Close(remote_tdo_handle)
2783 except RuntimeError as error:
2785 remote_tdo_handle = None
2786 if local_tdo_handle is not None:
2788 local_lsa.Close(local_tdo_handle)
2789 except RuntimeError as error:
2791 local_tdo_handle = None
2793 self.outf.write("Success.\n")
2797 class cmd_domain_trust_delete(DomainTrustCommand):
2798 """Delete a domain trust."""
2800 synopsis = "%prog DOMAIN [options]"
2802 takes_optiongroups = {
2803 "sambaopts": options.SambaOptions,
2804 "versionopts": options.VersionOptions,
2805 "credopts": options.CredentialsOptions,
2806 "localdcopts": LocalDCCredentialsOptions,
2810 Option("--delete-location", type="choice", metavar="LOCATION",
2811 choices=["local", "both"],
2812 help="Where to delete the trusted domain object: 'local' or 'both'.",
2813 dest='delete_location',
2817 takes_args = ["domain"]
2819 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2820 delete_location=None):
2822 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2823 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2824 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2826 if delete_location == "local":
2827 remote_policy_access = None
2829 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2830 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2831 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2833 local_server = self.setup_local_server(sambaopts, localdcopts)
2835 local_lsa = self.new_local_lsa_connection()
2836 except RuntimeError as error:
2837 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2840 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2841 except RuntimeError as error:
2842 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2844 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2845 local_lsa_info.name.string,
2846 local_lsa_info.dns_domain.string,
2847 local_lsa_info.sid))
2849 local_tdo_info = None
2850 local_tdo_handle = None
2851 remote_tdo_info = None
2852 remote_tdo_handle = None
2854 lsaString = lsa.String()
2856 lsaString.string = domain
2857 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2858 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2859 except NTSTATUSError as error:
2860 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2861 raise CommandError("Failed to find trust for domain '%s'" % domain)
2862 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2864 if remote_policy_access is not None:
2866 remote_server = self.setup_remote_server(credopts, domain)
2867 except RuntimeError as error:
2868 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2871 remote_lsa = self.new_remote_lsa_connection()
2872 except RuntimeError as error:
2873 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2876 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2877 except RuntimeError as error:
2878 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2880 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2881 remote_lsa_info.name.string,
2882 remote_lsa_info.dns_domain.string,
2883 remote_lsa_info.sid))
2885 if remote_lsa_info.sid != local_tdo_info.sid or \
2886 remote_lsa_info.name.string != local_tdo_info.netbios_name.string or \
2887 remote_lsa_info.dns_domain.string != local_tdo_info.domain_name.string:
2888 raise CommandError("LocalTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2889 local_tdo_info.netbios_name.string,
2890 local_tdo_info.domain_name.string,
2891 local_tdo_info.sid))
2894 lsaString.string = local_lsa_info.dns_domain.string
2896 remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2898 lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2899 except NTSTATUSError as error:
2900 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2901 raise self.RemoteRuntimeError(self, error, "QueryTrustedDomainInfoByName(%s)" % (
2905 if remote_tdo_info is not None:
2906 if local_lsa_info.sid != remote_tdo_info.sid or \
2907 local_lsa_info.name.string != remote_tdo_info.netbios_name.string or \
2908 local_lsa_info.dns_domain.string != remote_tdo_info.domain_name.string:
2909 raise CommandError("RemoteTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2910 remote_tdo_info.netbios_name.string,
2911 remote_tdo_info.domain_name.string,
2912 remote_tdo_info.sid))
2914 if local_tdo_info is not None:
2916 lsaString.string = local_tdo_info.domain_name.string
2917 local_tdo_handle = \
2918 local_lsa.OpenTrustedDomainByName(local_policy,
2920 security.SEC_STD_DELETE)
2921 except RuntimeError as error:
2922 raise self.LocalRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2925 local_lsa.DeleteObject(local_tdo_handle)
2926 local_tdo_handle = None
2928 if remote_tdo_info is not None:
2930 lsaString.string = remote_tdo_info.domain_name.string
2931 remote_tdo_handle = \
2932 remote_lsa.OpenTrustedDomainByName(remote_policy,
2934 security.SEC_STD_DELETE)
2935 except RuntimeError as error:
2936 raise self.RemoteRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2939 if remote_tdo_handle is not None:
2941 remote_lsa.DeleteObject(remote_tdo_handle)
2942 remote_tdo_handle = None
2943 self.outf.write("RemoteTDO deleted.\n")
2944 except RuntimeError as error:
2945 self.outf.write("%s\n" % self.RemoteRuntimeError(self, error, "DeleteObject() failed"))
2947 if local_tdo_handle is not None:
2949 local_lsa.DeleteObject(local_tdo_handle)
2950 local_tdo_handle = None
2951 self.outf.write("LocalTDO deleted.\n")
2952 except RuntimeError as error:
2953 self.outf.write("%s\n" % self.LocalRuntimeError(self, error, "DeleteObject() failed"))
2958 class cmd_domain_trust_validate(DomainTrustCommand):
2959 """Validate a domain trust."""
2961 synopsis = "%prog DOMAIN [options]"
2963 takes_optiongroups = {
2964 "sambaopts": options.SambaOptions,
2965 "versionopts": options.VersionOptions,
2966 "credopts": options.CredentialsOptions,
2967 "localdcopts": LocalDCCredentialsOptions,
2971 Option("--validate-location", type="choice", metavar="LOCATION",
2972 choices=["local", "both"],
2973 help="Where to validate the trusted domain object: 'local' or 'both'.",
2974 dest='validate_location',
2978 takes_args = ["domain"]
2980 def run(self, domain, sambaopts=None, versionopts=None, credopts=None, localdcopts=None,
2981 validate_location=None):
2983 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2985 local_server = self.setup_local_server(sambaopts, localdcopts)
2987 local_lsa = self.new_local_lsa_connection()
2988 except RuntimeError as error:
2989 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2992 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2993 except RuntimeError as error:
2994 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2996 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2997 local_lsa_info.name.string,
2998 local_lsa_info.dns_domain.string,
2999 local_lsa_info.sid))
3002 lsaString = lsa.String()
3003 lsaString.string = domain
3005 local_lsa.QueryTrustedDomainInfoByName(local_policy,
3007 lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
3008 except NTSTATUSError as error:
3009 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
3010 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
3012 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
3014 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
3015 local_tdo_info.netbios_name.string,
3016 local_tdo_info.domain_name.string,
3017 local_tdo_info.sid))
3020 local_netlogon = self.new_local_netlogon_connection()
3021 except RuntimeError as error:
3022 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3025 local_trust_verify = \
3026 local_netlogon.netr_LogonControl2Ex(local_server,
3027 netlogon.NETLOGON_CONTROL_TC_VERIFY,
3029 local_tdo_info.domain_name.string)
3030 except RuntimeError as error:
3031 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
3033 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
3034 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
3036 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
3037 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
3038 local_trust_verify.trusted_dc_name,
3039 local_trust_verify.tc_connection_status[1],
3040 local_trust_verify.pdc_connection_status[1])
3042 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
3043 local_trust_verify.trusted_dc_name,
3044 local_trust_verify.tc_connection_status[1],
3045 local_trust_verify.pdc_connection_status[1])
3047 if local_trust_status != werror.WERR_SUCCESS or local_conn_status != werror.WERR_SUCCESS:
3048 raise CommandError(local_validation)
3050 self.outf.write("OK: %s\n" % local_validation)
3053 server = local_trust_verify.trusted_dc_name.replace('\\', '')
3054 domain_and_server = "%s\\%s" % (local_tdo_info.domain_name.string, server)
3055 local_trust_rediscover = \
3056 local_netlogon.netr_LogonControl2Ex(local_server,
3057 netlogon.NETLOGON_CONTROL_REDISCOVER,
3060 except RuntimeError as error:
3061 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
3063 local_conn_status = self._uint32(local_trust_rediscover.tc_connection_status[0])
3064 local_rediscover = "LocalRediscover: DC[%s] CONNECTION[%s]" % (
3065 local_trust_rediscover.trusted_dc_name,
3066 local_trust_rediscover.tc_connection_status[1])
3068 if local_conn_status != werror.WERR_SUCCESS:
3069 raise CommandError(local_rediscover)
3071 self.outf.write("OK: %s\n" % local_rediscover)
3073 if validate_location != "local":
3075 remote_server = self.setup_remote_server(credopts, domain, require_pdc=False)
3076 except RuntimeError as error:
3077 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
3080 remote_netlogon = self.new_remote_netlogon_connection()
3081 except RuntimeError as error:
3082 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
3085 remote_trust_verify = \
3086 remote_netlogon.netr_LogonControl2Ex(remote_server,
3087 netlogon.NETLOGON_CONTROL_TC_VERIFY,
3089 local_lsa_info.dns_domain.string)
3090 except RuntimeError as error:
3091 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
3093 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
3094 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
3096 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
3097 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
3098 remote_trust_verify.trusted_dc_name,
3099 remote_trust_verify.tc_connection_status[1],
3100 remote_trust_verify.pdc_connection_status[1])
3102 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
3103 remote_trust_verify.trusted_dc_name,
3104 remote_trust_verify.tc_connection_status[1],
3105 remote_trust_verify.pdc_connection_status[1])
3107 if remote_trust_status != werror.WERR_SUCCESS or remote_conn_status != werror.WERR_SUCCESS:
3108 raise CommandError(remote_validation)
3110 self.outf.write("OK: %s\n" % remote_validation)
3113 server = remote_trust_verify.trusted_dc_name.replace('\\', '')
3114 domain_and_server = "%s\\%s" % (local_lsa_info.dns_domain.string, server)
3115 remote_trust_rediscover = \
3116 remote_netlogon.netr_LogonControl2Ex(remote_server,
3117 netlogon.NETLOGON_CONTROL_REDISCOVER,
3120 except RuntimeError as error:
3121 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
3123 remote_conn_status = self._uint32(remote_trust_rediscover.tc_connection_status[0])
3125 remote_rediscover = "RemoteRediscover: DC[%s] CONNECTION[%s]" % (
3126 remote_trust_rediscover.trusted_dc_name,
3127 remote_trust_rediscover.tc_connection_status[1])
3129 if remote_conn_status != werror.WERR_SUCCESS:
3130 raise CommandError(remote_rediscover)
3132 self.outf.write("OK: %s\n" % remote_rediscover)
3137 class cmd_domain_trust_namespaces(DomainTrustCommand):
3138 """Manage forest trust namespaces."""
3140 synopsis = "%prog [DOMAIN] [options]"
3142 takes_optiongroups = {
3143 "sambaopts": options.SambaOptions,
3144 "versionopts": options.VersionOptions,
3145 "localdcopts": LocalDCCredentialsOptions,
3149 Option("--refresh", type="choice", metavar="check|store",
3150 choices=["check", "store", None],
3151 help="List and maybe store refreshed forest trust information: 'check' or 'store'.",
3154 Option("--enable-all", action="store_true",
3155 help="Try to update disabled entries, not allowed with --refresh=check.",
3158 Option("--enable-tln", action="append", metavar='DNSDOMAIN',
3159 help="Enable a top level name entry. Can be specified multiple times.",
3162 Option("--disable-tln", action="append", metavar='DNSDOMAIN',
3163 help="Disable a top level name entry. Can be specified multiple times.",
3166 Option("--add-tln-ex", action="append", metavar='DNSDOMAIN',
3167 help="Add a top level exclusion entry. Can be specified multiple times.",
3170 Option("--delete-tln-ex", action="append", metavar='DNSDOMAIN',
3171 help="Delete a top level exclusion entry. Can be specified multiple times.",
3172 dest='delete_tln_ex',
3174 Option("--enable-nb", action="append", metavar='NETBIOSDOMAIN',
3175 help="Enable a netbios name in a domain entry. Can be specified multiple times.",
3178 Option("--disable-nb", action="append", metavar='NETBIOSDOMAIN',
3179 help="Disable a netbios name in a domain entry. Can be specified multiple times.",
3182 Option("--enable-sid", action="append", metavar='DOMAINSID',
3183 help="Enable a SID in a domain entry. Can be specified multiple times.",
3184 dest='enable_sid_str',
3186 Option("--disable-sid", action="append", metavar='DOMAINSID',
3187 help="Disable a SID in a domain entry. Can be specified multiple times.",
3188 dest='disable_sid_str',
3190 Option("--add-upn-suffix", action="append", metavar='DNSDOMAIN',
3191 help="Add a new uPNSuffixes attribute for the local forest. Can be specified multiple times.",
3194 Option("--delete-upn-suffix", action="append", metavar='DNSDOMAIN',
3195 help="Delete an existing uPNSuffixes attribute of the local forest. Can be specified multiple times.",
3198 Option("--add-spn-suffix", action="append", metavar='DNSDOMAIN',
3199 help="Add a new msDS-SPNSuffixes attribute for the local forest. Can be specified multiple times.",
3202 Option("--delete-spn-suffix", action="append", metavar='DNSDOMAIN',
3203 help="Delete an existing msDS-SPNSuffixes attribute of the local forest. Can be specified multiple times.",
3208 takes_args = ["domain?"]
3210 def run(self, domain=None, sambaopts=None, localdcopts=None, versionopts=None,
3211 refresh=None, enable_all=False,
3212 enable_tln=[], disable_tln=[], add_tln_ex=[], delete_tln_ex=[],
3213 enable_sid_str=[], disable_sid_str=[], enable_nb=[], disable_nb=[],
3214 add_upn=[], delete_upn=[], add_spn=[], delete_spn=[]):
3216 require_update = False
3219 if refresh == "store":
3220 raise CommandError("--refresh=%s not allowed without DOMAIN" % refresh)
3223 raise CommandError("--enable-all not allowed without DOMAIN")
3225 if len(enable_tln) > 0:
3226 raise CommandError("--enable-tln not allowed without DOMAIN")
3227 if len(disable_tln) > 0:
3228 raise CommandError("--disable-tln not allowed without DOMAIN")
3230 if len(add_tln_ex) > 0:
3231 raise CommandError("--add-tln-ex not allowed without DOMAIN")
3232 if len(delete_tln_ex) > 0:
3233 raise CommandError("--delete-tln-ex not allowed without DOMAIN")
3235 if len(enable_nb) > 0:
3236 raise CommandError("--enable-nb not allowed without DOMAIN")
3237 if len(disable_nb) > 0:
3238 raise CommandError("--disable-nb not allowed without DOMAIN")
3240 if len(enable_sid_str) > 0:
3241 raise CommandError("--enable-sid not allowed without DOMAIN")
3242 if len(disable_sid_str) > 0:
3243 raise CommandError("--disable-sid not allowed without DOMAIN")
3245 if len(add_upn) > 0:
3247 if not n.startswith("*."):
3249 raise CommandError("value[%s] specified for --add-upn-suffix should not include with '*.'" % n)
3250 require_update = True
3251 if len(delete_upn) > 0:
3252 for n in delete_upn:
3253 if not n.startswith("*."):
3255 raise CommandError("value[%s] specified for --delete-upn-suffix should not include with '*.'" % n)
3256 require_update = True
3258 for d in delete_upn:
3259 if a.lower() != d.lower():
3261 raise CommandError("value[%s] specified for --add-upn-suffix and --delete-upn-suffix" % a)
3263 if len(add_spn) > 0:
3265 if not n.startswith("*."):
3267 raise CommandError("value[%s] specified for --add-spn-suffix should not include with '*.'" % n)
3268 require_update = True
3269 if len(delete_spn) > 0:
3270 for n in delete_spn:
3271 if not n.startswith("*."):
3273 raise CommandError("value[%s] specified for --delete-spn-suffix should not include with '*.'" % n)
3274 require_update = True
3276 for d in delete_spn:
3277 if a.lower() != d.lower():
3279 raise CommandError("value[%s] specified for --add-spn-suffix and --delete-spn-suffix" % a)
3281 if len(add_upn) > 0:
3282 raise CommandError("--add-upn-suffix not allowed together with DOMAIN")
3283 if len(delete_upn) > 0:
3284 raise CommandError("--delete-upn-suffix not allowed together with DOMAIN")
3285 if len(add_spn) > 0:
3286 raise CommandError("--add-spn-suffix not allowed together with DOMAIN")
3287 if len(delete_spn) > 0:
3288 raise CommandError("--delete-spn-suffix not allowed together with DOMAIN")
3290 if refresh is not None:
3291 if refresh == "store":
3292 require_update = True
3294 if enable_all and refresh != "store":
3295 raise CommandError("--enable-all not allowed together with --refresh=%s" % refresh)
3297 if len(enable_tln) > 0:
3298 raise CommandError("--enable-tln not allowed together with --refresh")
3299 if len(disable_tln) > 0:
3300 raise CommandError("--disable-tln not allowed together with --refresh")
3302 if len(add_tln_ex) > 0:
3303 raise CommandError("--add-tln-ex not allowed together with --refresh")
3304 if len(delete_tln_ex) > 0:
3305 raise CommandError("--delete-tln-ex not allowed together with --refresh")
3307 if len(enable_nb) > 0:
3308 raise CommandError("--enable-nb not allowed together with --refresh")
3309 if len(disable_nb) > 0:
3310 raise CommandError("--disable-nb not allowed together with --refresh")
3312 if len(enable_sid_str) > 0:
3313 raise CommandError("--enable-sid not allowed together with --refresh")
3314 if len(disable_sid_str) > 0:
3315 raise CommandError("--disable-sid not allowed together with --refresh")
3318 require_update = True
3320 if len(enable_tln) > 0:
3321 raise CommandError("--enable-tln not allowed together with --enable-all")
3323 if len(enable_nb) > 0:
3324 raise CommandError("--enable-nb not allowed together with --enable-all")
3326 if len(enable_sid_str) > 0:
3327 raise CommandError("--enable-sid not allowed together with --enable-all")
3329 if len(enable_tln) > 0:
3330 require_update = True
3331 if len(disable_tln) > 0:
3332 require_update = True
3333 for e in enable_tln:
3334 for d in disable_tln:
3335 if e.lower() != d.lower():
3337 raise CommandError("value[%s] specified for --enable-tln and --disable-tln" % e)
3339 if len(add_tln_ex) > 0:
3340 for n in add_tln_ex:
3341 if not n.startswith("*."):
3343 raise CommandError("value[%s] specified for --add-tln-ex should not include with '*.'" % n)
3344 require_update = True
3345 if len(delete_tln_ex) > 0:
3346 for n in delete_tln_ex:
3347 if not n.startswith("*."):
3349 raise CommandError("value[%s] specified for --delete-tln-ex should not include with '*.'" % n)
3350 require_update = True
3351 for a in add_tln_ex:
3352 for d in delete_tln_ex:
3353 if a.lower() != d.lower():
3355 raise CommandError("value[%s] specified for --add-tln-ex and --delete-tln-ex" % a)
3357 if len(enable_nb) > 0:
3358 require_update = True
3359 if len(disable_nb) > 0:
3360 require_update = True
3362 for d in disable_nb:
3363 if e.upper() != d.upper():
3365 raise CommandError("value[%s] specified for --enable-nb and --disable-nb" % e)
3368 for s in enable_sid_str:
3370 sid = security.dom_sid(s)
3371 except TypeError as error:
3372 raise CommandError("value[%s] specified for --enable-sid is not a valid SID" % s)
3373 enable_sid.append(sid)
3375 for s in disable_sid_str:
3377 sid = security.dom_sid(s)
3378 except TypeError as error:
3379 raise CommandError("value[%s] specified for --disable-sid is not a valid SID" % s)
3380 disable_sid.append(sid)
3381 if len(enable_sid) > 0:
3382 require_update = True
3383 if len(disable_sid) > 0:
3384 require_update = True
3385 for e in enable_sid:
3386 for d in disable_sid:
3389 raise CommandError("value[%s] specified for --enable-sid and --disable-sid" % e)
3391 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
3393 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
3395 local_server = self.setup_local_server(sambaopts, localdcopts)
3397 local_lsa = self.new_local_lsa_connection()
3398 except RuntimeError as error:
3399 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
3402 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
3403 except RuntimeError as error:
3404 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
3406 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
3407 local_lsa_info.name.string,
3408 local_lsa_info.dns_domain.string,
3409 local_lsa_info.sid))
3413 local_netlogon = self.new_local_netlogon_connection()
3414 except RuntimeError as error:
3415 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3418 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3419 except RuntimeError as error:
3420 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3422 if local_netlogon_info.domain_name != local_netlogon_info.forest_name:
3423 raise CommandError("The local domain [%s] is not the forest root [%s]" % (
3424 local_netlogon_info.domain_name,
3425 local_netlogon_info.forest_name))
3428 # get all information about our own forest
3429 own_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3431 except RuntimeError as error:
3432 if self.check_runtime_error(error, werror.WERR_RPC_S_PROCNUM_OUT_OF_RANGE):
3433 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3436 if self.check_runtime_error(error, werror.WERR_INVALID_FUNCTION):
3437 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3440 if self.check_runtime_error(error, werror.WERR_NERR_ACFNOTLOADED):
3441 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3444 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3446 self.outf.write("Own forest trust information...\n")
3447 self.write_forest_trust_info(own_forest_info,
3448 tln=local_lsa_info.dns_domain.string)
3451 local_samdb = self.new_local_ldap_connection()
3452 except RuntimeError as error:
3453 raise self.LocalRuntimeError(self, error, "failed to connect to SamDB")
3455 local_partitions_dn = "CN=Partitions,%s" % str(local_samdb.get_config_basedn())
3456 attrs = ['uPNSuffixes', 'msDS-SPNSuffixes']
3458 msgs = local_samdb.search(base=local_partitions_dn,
3459 scope=ldb.SCOPE_BASE,
3460 expression="(objectClass=crossRefContainer)",
3462 stored_msg = msgs[0]
3463 except ldb.LdbError as error:
3464 raise self.LocalLdbError(self, error, "failed to search partition dn")
3466 stored_upn_vals = []
3467 if 'uPNSuffixes' in stored_msg:
3468 stored_upn_vals.extend(stored_msg['uPNSuffixes'])
3470 stored_spn_vals = []
3471 if 'msDS-SPNSuffixes' in stored_msg:
3472 stored_spn_vals.extend(stored_msg['msDS-SPNSuffixes'])
3474 self.outf.write("Stored uPNSuffixes attributes[%d]:\n" % len(stored_upn_vals))
3475 for v in stored_upn_vals:
3476 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3477 self.outf.write("Stored msDS-SPNSuffixes attributes[%d]:\n" % len(stored_spn_vals))
3478 for v in stored_spn_vals:
3479 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3481 if not require_update:
3485 update_upn_vals = []
3486 update_upn_vals.extend(stored_upn_vals)
3489 update_spn_vals = []
3490 update_spn_vals.extend(stored_spn_vals)
3493 for i, v in enumerate(update_upn_vals):
3494 if v.lower() == upn.lower():
3495 raise CommandError("Entry already present for "
3496 "value[%s] specified for "
3497 "--add-upn-suffix" % upn)
3498 update_upn_vals.append(upn)
3501 for upn in delete_upn:
3503 for i, v in enumerate(update_upn_vals):
3504 if v.lower() != upn.lower():
3509 raise CommandError("Entry not found for value[%s] specified for --delete-upn-suffix" % upn)
3511 update_upn_vals.pop(idx)
3515 for i, v in enumerate(update_spn_vals):
3516 if v.lower() == spn.lower():
3517 raise CommandError("Entry already present for "
3518 "value[%s] specified for "
3519 "--add-spn-suffix" % spn)
3520 update_spn_vals.append(spn)
3523 for spn in delete_spn:
3525 for i, v in enumerate(update_spn_vals):
3526 if v.lower() != spn.lower():
3531 raise CommandError("Entry not found for value[%s] specified for --delete-spn-suffix" % spn)
3533 update_spn_vals.pop(idx)
3536 self.outf.write("Update uPNSuffixes attributes[%d]:\n" % len(update_upn_vals))
3537 for v in update_upn_vals:
3538 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3539 self.outf.write("Update msDS-SPNSuffixes attributes[%d]:\n" % len(update_spn_vals))
3540 for v in update_spn_vals:
3541 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3543 update_msg = ldb.Message()
3544 update_msg.dn = stored_msg.dn
3547 update_msg['uPNSuffixes'] = ldb.MessageElement(update_upn_vals,
3548 ldb.FLAG_MOD_REPLACE,
3551 update_msg['msDS-SPNSuffixes'] = ldb.MessageElement(update_spn_vals,
3552 ldb.FLAG_MOD_REPLACE,
3555 local_samdb.modify(update_msg)
3556 except ldb.LdbError as error:
3557 raise self.LocalLdbError(self, error, "failed to update partition dn")
3560 stored_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3562 except RuntimeError as error:
3563 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3565 self.outf.write("Stored forest trust information...\n")
3566 self.write_forest_trust_info(stored_forest_info,
3567 tln=local_lsa_info.dns_domain.string)
3571 lsaString = lsa.String()
3572 lsaString.string = domain
3574 local_lsa.QueryTrustedDomainInfoByName(local_policy,
3576 lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
3577 except NTSTATUSError as error:
3578 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
3579 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
3581 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
3583 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
3584 local_tdo_info.netbios_name.string,
3585 local_tdo_info.domain_name.string,
3586 local_tdo_info.sid))
3588 if not local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
3589 raise CommandError("trusted domain object for domain [%s] is not marked as FOREST_TRANSITIVE." % domain)
3591 if refresh is not None:
3593 local_netlogon = self.new_local_netlogon_connection()
3594 except RuntimeError as error:
3595 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3598 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3599 except RuntimeError as error:
3600 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3602 lsa_update_check = 1
3603 if refresh == "store":
3604 netlogon_update_tdo = netlogon.DS_GFTI_UPDATE_TDO
3606 lsa_update_check = 0
3608 netlogon_update_tdo = 0
3611 # get all information about the remote trust
3612 # this triggers netr_GetForestTrustInformation to the remote domain
3613 # and lsaRSetForestTrustInformation() locally, but new top level
3614 # names are disabled by default.
3615 fresh_forest_info = \
3616 local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3617 local_tdo_info.domain_name.string,
3618 netlogon_update_tdo)
3619 except RuntimeError as error:
3620 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3623 fresh_forest_collision = \
3624 local_lsa.lsaRSetForestTrustInformation(local_policy,
3625 local_tdo_info.domain_name,
3626 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3629 except RuntimeError as error:
3630 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3632 self.outf.write("Fresh forest trust information...\n")
3633 self.write_forest_trust_info(fresh_forest_info,
3634 tln=local_tdo_info.domain_name.string,
3635 collisions=fresh_forest_collision)
3637 if refresh == "store":
3639 lsaString = lsa.String()
3640 lsaString.string = local_tdo_info.domain_name.string
3641 stored_forest_info = \
3642 local_lsa.lsaRQueryForestTrustInformation(local_policy,
3644 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3645 except RuntimeError as error:
3646 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3648 self.outf.write("Stored forest trust information...\n")
3649 self.write_forest_trust_info(stored_forest_info,
3650 tln=local_tdo_info.domain_name.string)
3655 # The none --refresh path
3659 lsaString = lsa.String()
3660 lsaString.string = local_tdo_info.domain_name.string
3661 local_forest_info = \
3662 local_lsa.lsaRQueryForestTrustInformation(local_policy,
3664 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3665 except RuntimeError as error:
3666 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3668 self.outf.write("Local forest trust information...\n")
3669 self.write_forest_trust_info(local_forest_info,
3670 tln=local_tdo_info.domain_name.string)
3672 if not require_update:
3676 entries.extend(local_forest_info.entries)
3677 update_forest_info = lsa.ForestTrustInformation()
3678 update_forest_info.count = len(entries)
3679 update_forest_info.entries = entries
3682 for i, r in enumerate(update_forest_info.entries):
3683 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3685 if update_forest_info.entries[i].flags == 0:
3687 update_forest_info.entries[i].time = 0
3688 update_forest_info.entries[i].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3689 for i, r in enumerate(update_forest_info.entries):
3690 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3692 if update_forest_info.entries[i].flags == 0:
3694 update_forest_info.entries[i].time = 0
3695 update_forest_info.entries[i].flags &= ~lsa.LSA_NB_DISABLED_MASK
3696 update_forest_info.entries[i].flags &= ~lsa.LSA_SID_DISABLED_MASK
3698 for tln in enable_tln:
3700 for i, r in enumerate(update_forest_info.entries):
3701 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3703 if r.forest_trust_data.string.lower() != tln.lower():
3708 raise CommandError("Entry not found for value[%s] specified for --enable-tln" % tln)
3709 if not update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_MASK:
3710 raise CommandError("Entry found for value[%s] specified for --enable-tln is already enabled" % tln)
3711 update_forest_info.entries[idx].time = 0
3712 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3714 for tln in disable_tln:
3716 for i, r in enumerate(update_forest_info.entries):
3717 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3719 if r.forest_trust_data.string.lower() != tln.lower():
3724 raise CommandError("Entry not found for value[%s] specified for --disable-tln" % tln)
3725 if update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_ADMIN:
3726 raise CommandError("Entry found for value[%s] specified for --disable-tln is already disabled" % tln)
3727 update_forest_info.entries[idx].time = 0
3728 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3729 update_forest_info.entries[idx].flags |= lsa.LSA_TLN_DISABLED_ADMIN
3731 for tln_ex in add_tln_ex:
3733 for i, r in enumerate(update_forest_info.entries):
3734 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3736 if r.forest_trust_data.string.lower() != tln_ex.lower():
3741 raise CommandError("Entry already present for value[%s] specified for --add-tln-ex" % tln_ex)
3743 tln_dot = ".%s" % tln_ex.lower()
3745 for i, r in enumerate(update_forest_info.entries):
3746 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3748 r_dot = ".%s" % r.forest_trust_data.string.lower()
3749 if tln_dot == r_dot:
3750 raise CommandError("TLN entry present for value[%s] specified for --add-tln-ex" % tln_ex)
3751 if not tln_dot.endswith(r_dot):
3757 raise CommandError("No TLN parent present for value[%s] specified for --add-tln-ex" % tln_ex)
3759 r = lsa.ForestTrustRecord()
3760 r.type = lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX
3763 r.forest_trust_data.string = tln_ex
3766 entries.extend(update_forest_info.entries)
3767 entries.insert(idx + 1, r)
3768 update_forest_info.count = len(entries)
3769 update_forest_info.entries = entries
3771 for tln_ex in delete_tln_ex:
3773 for i, r in enumerate(update_forest_info.entries):
3774 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3776 if r.forest_trust_data.string.lower() != tln_ex.lower():
3781 raise CommandError("Entry not found for value[%s] specified for --delete-tln-ex" % tln_ex)
3784 entries.extend(update_forest_info.entries)
3786 update_forest_info.count = len(entries)
3787 update_forest_info.entries = entries
3789 for nb in enable_nb:
3791 for i, r in enumerate(update_forest_info.entries):
3792 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3794 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3799 raise CommandError("Entry not found for value[%s] specified for --enable-nb" % nb)
3800 if not update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_MASK:
3801 raise CommandError("Entry found for value[%s] specified for --enable-nb is already enabled" % nb)
3802 update_forest_info.entries[idx].time = 0
3803 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3805 for nb in disable_nb:
3807 for i, r in enumerate(update_forest_info.entries):
3808 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3810 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3815 raise CommandError("Entry not found for value[%s] specified for --delete-nb" % nb)
3816 if update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_ADMIN:
3817 raise CommandError("Entry found for value[%s] specified for --disable-nb is already disabled" % nb)
3818 update_forest_info.entries[idx].time = 0
3819 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3820 update_forest_info.entries[idx].flags |= lsa.LSA_NB_DISABLED_ADMIN
3822 for sid in enable_sid:
3824 for i, r in enumerate(update_forest_info.entries):
3825 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3827 if r.forest_trust_data.domain_sid != sid:
3832 raise CommandError("Entry not found for value[%s] specified for --enable-sid" % sid)
3833 if not update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_MASK:
3834 raise CommandError("Entry found for value[%s] specified for --enable-sid is already enabled" % nb)
3835 update_forest_info.entries[idx].time = 0
3836 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3838 for sid in disable_sid:
3840 for i, r in enumerate(update_forest_info.entries):
3841 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3843 if r.forest_trust_data.domain_sid != sid:
3848 raise CommandError("Entry not found for value[%s] specified for --delete-sid" % sid)
3849 if update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_ADMIN:
3850 raise CommandError("Entry found for value[%s] specified for --disable-sid is already disabled" % nb)
3851 update_forest_info.entries[idx].time = 0
3852 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3853 update_forest_info.entries[idx].flags |= lsa.LSA_SID_DISABLED_ADMIN
3856 update_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3857 local_tdo_info.domain_name,
3858 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3859 update_forest_info, 0)
3860 except RuntimeError as error:
3861 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3863 self.outf.write("Updated forest trust information...\n")
3864 self.write_forest_trust_info(update_forest_info,
3865 tln=local_tdo_info.domain_name.string,
3866 collisions=update_forest_collision)
3869 lsaString = lsa.String()
3870 lsaString.string = local_tdo_info.domain_name.string
3871 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3873 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3874 except RuntimeError as error:
3875 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3877 self.outf.write("Stored forest trust information...\n")
3878 self.write_forest_trust_info(stored_forest_info,
3879 tln=local_tdo_info.domain_name.string)
3883 class cmd_domain_tombstones_expunge(Command):
3884 """Expunge tombstones from the database.
3886 This command expunges tombstones from the database."""
3887 synopsis = "%prog NC [NC [...]] [options]"
3890 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
3891 metavar="URL", dest="H"),
3892 Option("--current-time",
3893 help="The current time to evaluate the tombstone lifetime from, expressed as YYYY-MM-DD",
3895 Option("--tombstone-lifetime", help="Number of days a tombstone should be preserved for", type=int),
3898 takes_args = ["nc*"]
3900 takes_optiongroups = {
3901 "sambaopts": options.SambaOptions,
3902 "credopts": options.CredentialsOptions,
3903 "versionopts": options.VersionOptions,
3906 def run(self, *ncs, **kwargs):
3907 sambaopts = kwargs.get("sambaopts")
3908 credopts = kwargs.get("credopts")
3909 versionpts = kwargs.get("versionopts")
3911 current_time_string = kwargs.get("current_time")
3912 tombstone_lifetime = kwargs.get("tombstone_lifetime")
3913 lp = sambaopts.get_loadparm()
3914 creds = credopts.get_credentials(lp)
3915 samdb = SamDB(url=H, session_info=system_session(),
3916 credentials=creds, lp=lp)
3918 if current_time_string is not None:
3919 current_time_obj = time.strptime(current_time_string, "%Y-%m-%d")
3920 current_time = long(time.mktime(current_time_obj))
3923 current_time = long(time.time())
3926 res = samdb.search(expression="", base="", scope=ldb.SCOPE_BASE,
3927 attrs=["namingContexts"])
3930 for nc in res[0]["namingContexts"]:
3935 started_transaction = False
3937 samdb.transaction_start()
3938 started_transaction = True
3940 removed_links) = samdb.garbage_collect_tombstones(ncs,
3941 current_time=current_time,
3942 tombstone_lifetime=tombstone_lifetime)
3944 except Exception as err:
3945 if started_transaction:
3946 samdb.transaction_cancel()
3947 raise CommandError("Failed to expunge / garbage collect tombstones", err)
3949 samdb.transaction_commit()
3951 self.outf.write("Removed %d objects and %d links successfully\n"
3952 % (removed_objects, removed_links))
3955 class cmd_domain_trust(SuperCommand):
3956 """Domain and forest trust management."""
3959 subcommands["list"] = cmd_domain_trust_list()
3960 subcommands["show"] = cmd_domain_trust_show()
3961 subcommands["create"] = cmd_domain_trust_create()
3962 subcommands["delete"] = cmd_domain_trust_delete()
3963 subcommands["validate"] = cmd_domain_trust_validate()
3964 subcommands["namespaces"] = cmd_domain_trust_namespaces()
3967 class cmd_domain_tombstones(SuperCommand):
3968 """Domain tombstone and recycled object management."""
3971 subcommands["expunge"] = cmd_domain_tombstones_expunge()
3974 class ldif_schema_update:
3975 """Helper class for applying LDIF schema updates"""
3978 self.is_defunct = False
3979 self.unknown_oid = None
3983 def can_ignore_failure(self, error):
3984 """Checks if we can safely ignore failure to apply an LDIF update"""
3985 (num, errstr) = error.args
3987 # Microsoft has marked objects as defunct that Samba doesn't know about
3988 if num == ldb.ERR_NO_SUCH_OBJECT and self.is_defunct:
3989 print("Defunct object %s doesn't exist, skipping" % self.dn)
3991 elif self.unknown_oid is not None:
3992 print("Skipping unknown OID %s for object %s" % (self.unknown_oid, self.dn))
3997 def apply(self, samdb):
3998 """Applies a single LDIF update to the schema"""
4002 samdb.modify_ldif(self.ldif, controls=['relax:0'])
4003 except ldb.LdbError as e:
4004 if e.args[0] == ldb.ERR_INVALID_ATTRIBUTE_SYNTAX:
4006 # REFRESH after a failed change
4008 # Otherwise the OID-to-attribute mapping in
4009 # _apply_updates_in_file() won't work, because it
4010 # can't lookup the new OID in the schema
4011 samdb.set_schema_update_now()
4013 samdb.modify_ldif(self.ldif, controls=['relax:0'])
4016 except ldb.LdbError as e:
4017 if self.can_ignore_failure(e):
4020 print("Exception: %s" % e)
4021 print("Encountered while trying to apply the following LDIF")
4022 print("----------------------------------------------------")
4023 print("%s" % self.ldif)
4030 class cmd_domain_schema_upgrade(Command):
4031 """Domain schema upgrading"""
4033 synopsis = "%prog [options]"
4035 takes_optiongroups = {
4036 "sambaopts": options.SambaOptions,
4037 "versionopts": options.VersionOptions,
4038 "credopts": options.CredentialsOptions,
4042 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
4043 metavar="URL", dest="H"),
4044 Option("-q", "--quiet", help="Be quiet", action="store_true"), # unused
4045 Option("-v", "--verbose", help="Be verbose", action="store_true"),
4046 Option("--schema", type="choice", metavar="SCHEMA",
4047 choices=["2012", "2012_R2"],
4048 help="The schema file to upgrade to. Default is (Windows) 2012_R2.",
4050 Option("--ldf-file", type=str, default=None,
4051 help="Just apply the schema updates in the adprep/.LDF file(s) specified"),
4052 Option("--base-dir", type=str, default=None,
4053 help="Location of ldf files Default is ${SETUPDIR}/adprep.")
4056 def _apply_updates_in_file(self, samdb, ldif_file):
4058 Applies a series of updates specified in an .LDIF file. The .LDIF file
4059 is based on the adprep Schema updates provided by Microsoft.
4062 ldif_op = ldif_schema_update()
4064 # parse the file line by line and work out each update operation to apply
4065 for line in ldif_file:
4067 line = line.rstrip()
4069 # the operations in the .LDIF file are separated by blank lines. If
4070 # we hit a blank line, try to apply the update we've parsed so far
4073 # keep going if we haven't parsed anything yet
4074 if ldif_op.ldif == '':
4077 # Apply the individual change
4078 count += ldif_op.apply(samdb)
4080 # start storing the next operation from scratch again
4081 ldif_op = ldif_schema_update()
4084 # replace the placeholder domain name in the .ldif file with the real domain
4085 if line.upper().endswith('DC=X'):
4086 line = line[:-len('DC=X')] + str(samdb.get_default_basedn())
4087 elif line.upper().endswith('CN=X'):
4088 line = line[:-len('CN=X')] + str(samdb.get_default_basedn())
4090 values = line.split(':')
4092 if values[0].lower() == 'dn':
4093 ldif_op.dn = values[1].strip()
4095 # replace the Windows-specific operation with the Samba one
4096 if values[0].lower() == 'changetype':
4097 line = line.lower().replace(': ntdsschemaadd',
4099 line = line.lower().replace(': ntdsschemamodify',
4102 if values[0].lower() in ['rdnattid', 'subclassof',
4103 'systemposssuperiors',
4105 'systemauxiliaryclass']:
4108 # The Microsoft updates contain some OIDs we don't recognize.
4109 # Query the DB to see if we can work out the OID this update is
4110 # referring to. If we find a match, then replace the OID with
4111 # the ldapDisplayname
4113 res = samdb.search(base=samdb.get_schema_basedn(),
4114 expression="(|(attributeId=%s)(governsId=%s))" %
4116 attrs=['ldapDisplayName'])
4119 ldif_op.unknown_oid = value
4121 display_name = res[0]['ldapDisplayName'][0]
4122 line = line.replace(value, ' ' + display_name)
4124 # Microsoft has marked objects as defunct that Samba doesn't know about
4125 if values[0].lower() == 'isdefunct' and values[1].strip().lower() == 'true':
4126 ldif_op.is_defunct = True
4128 # Samba has added the showInAdvancedViewOnly attribute to all objects,
4129 # so rather than doing an add, we need to do a replace
4130 if values[0].lower() == 'add' and values[1].strip().lower() == 'showinadvancedviewonly':
4131 line = 'replace: showInAdvancedViewOnly'
4133 # Add the line to the current LDIF operation (including the newline
4134 # we stripped off at the start of the loop)
4135 ldif_op.ldif += line + '\n'
4139 def _apply_update(self, samdb, update_file, base_dir):
4140 """Wrapper function for parsing an LDIF file and applying the updates"""
4142 print("Applying %s updates..." % update_file)
4146 ldif_file = open(os.path.join(base_dir, update_file))
4148 count = self._apply_updates_in_file(samdb, ldif_file)
4154 print("%u changes applied" % count)
4158 def run(self, **kwargs):
4159 from samba.ms_schema_markdown import read_ms_markdown
4160 from samba.schema import Schema
4162 updates_allowed_overriden = False
4163 sambaopts = kwargs.get("sambaopts")
4164 credopts = kwargs.get("credopts")
4165 versionpts = kwargs.get("versionopts")
4166 lp = sambaopts.get_loadparm()
4167 creds = credopts.get_credentials(lp)
4169 target_schema = kwargs.get("schema")
4170 ldf_files = kwargs.get("ldf_file")
4171 base_dir = kwargs.get("base_dir")
4175 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
4177 # we're not going to get far if the config doesn't allow schema updates
4178 if lp.get("dsdb:schema update allowed") is None:
4179 lp.set("dsdb:schema update allowed", "yes")
4180 print("Temporarily overriding 'dsdb:schema update allowed' setting")
4181 updates_allowed_overriden = True
4183 own_dn = ldb.Dn(samdb, samdb.get_dsServiceName())
4184 master = get_fsmo_roleowner(samdb, str(samdb.get_schema_basedn()),
4186 if own_dn != master:
4187 raise CommandError("This server is not the schema master.")
4189 # if specific LDIF files were specified, just apply them
4191 schema_updates = ldf_files.split(",")
4195 # work out the version of the target schema we're upgrading to
4196 end = Schema.get_version(target_schema)
4198 # work out the version of the schema we're currently using
4199 res = samdb.search(base=samdb.get_schema_basedn(),
4200 scope=ldb.SCOPE_BASE, attrs=['objectVersion'])
4203 raise CommandError('Could not determine current schema version')
4204 start = int(res[0]['objectVersion'][0]) + 1
4206 diff_dir = setup_path("adprep/WindowsServerDocs")
4207 if base_dir is None:
4208 # Read from the Schema-Updates.md file
4209 temp_folder = tempfile.mkdtemp()
4211 update_file = setup_path("adprep/WindowsServerDocs/Schema-Updates.md")
4214 read_ms_markdown(update_file, temp_folder)
4215 except Exception as e:
4216 print("Exception in markdown parsing: %s" % e)
4217 shutil.rmtree(temp_folder)
4218 raise CommandError('Failed to upgrade schema')
4220 base_dir = temp_folder
4222 for version in range(start, end + 1):
4223 update = 'Sch%d.ldf' % version
4224 schema_updates.append(update)
4226 # Apply patches if we parsed the Schema-Updates.md file
4227 diff = os.path.abspath(os.path.join(diff_dir, update + '.diff'))
4228 if temp_folder and os.path.exists(diff):
4230 p = subprocess.Popen(['patch', update, '-i', diff],
4231 stdout=subprocess.PIPE,
4232 stderr=subprocess.PIPE, cwd=temp_folder)
4233 except (OSError, IOError):
4234 shutil.rmtree(temp_folder)
4235 raise CommandError("Failed to upgrade schema. Check if 'patch' is installed.")
4237 stdout, stderr = p.communicate()
4240 print("Exception in patch: %s\n%s" % (stdout, stderr))
4241 shutil.rmtree(temp_folder)
4242 raise CommandError('Failed to upgrade schema')
4244 print("Patched %s using %s" % (update, diff))
4246 if base_dir is None:
4247 base_dir = setup_path("adprep")
4249 samdb.transaction_start()
4251 error_encountered = False
4254 # Apply the schema updates needed to move to the new schema version
4255 for ldif_file in schema_updates:
4256 count += self._apply_update(samdb, ldif_file, base_dir)
4259 samdb.transaction_commit()
4260 print("Schema successfully updated")
4262 print("No changes applied to schema")
4263 samdb.transaction_cancel()
4264 except Exception as e:
4265 print("Exception: %s" % e)
4266 print("Error encountered, aborting schema upgrade")
4267 samdb.transaction_cancel()
4268 error_encountered = True
4270 if updates_allowed_overriden:
4271 lp.set("dsdb:schema update allowed", "no")
4274 shutil.rmtree(temp_folder)
4276 if error_encountered:
4277 raise CommandError('Failed to upgrade schema')
4280 class cmd_domain_functional_prep(Command):
4281 """Domain functional level preparation"""
4283 synopsis = "%prog [options]"
4285 takes_optiongroups = {
4286 "sambaopts": options.SambaOptions,
4287 "versionopts": options.VersionOptions,
4288 "credopts": options.CredentialsOptions,
4292 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
4293 metavar="URL", dest="H"),
4294 Option("-q", "--quiet", help="Be quiet", action="store_true"),
4295 Option("-v", "--verbose", help="Be verbose", action="store_true"),
4296 Option("--function-level", type="choice", metavar="FUNCTION_LEVEL",
4297 choices=["2008_R2", "2012", "2012_R2"],
4298 help="The schema file to upgrade to. Default is (Windows) 2012_R2.",
4300 Option("--forest-prep", action="store_true",
4301 help="Run the forest prep (by default, both the domain and forest prep are run)."),
4302 Option("--domain-prep", action="store_true",
4303 help="Run the domain prep (by default, both the domain and forest prep are run).")
4306 def run(self, **kwargs):
4307 updates_allowed_overriden = False
4308 sambaopts = kwargs.get("sambaopts")
4309 credopts = kwargs.get("credopts")
4310 versionpts = kwargs.get("versionopts")
4311 lp = sambaopts.get_loadparm()
4312 creds = credopts.get_credentials(lp)
4314 target_level = string_version_to_constant[kwargs.get("function_level")]
4315 forest_prep = kwargs.get("forest_prep")
4316 domain_prep = kwargs.get("domain_prep")
4318 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
4320 # we're not going to get far if the config doesn't allow schema updates
4321 if lp.get("dsdb:schema update allowed") is None:
4322 lp.set("dsdb:schema update allowed", "yes")
4323 print("Temporarily overriding 'dsdb:schema update allowed' setting")
4324 updates_allowed_overriden = True
4326 if forest_prep is None and domain_prep is None:
4330 own_dn = ldb.Dn(samdb, samdb.get_dsServiceName())
4332 master = get_fsmo_roleowner(samdb, str(samdb.get_schema_basedn()),
4334 if own_dn != master:
4335 raise CommandError("This server is not the schema master.")
4338 domain_dn = samdb.domain_dn()
4339 infrastructure_dn = "CN=Infrastructure," + domain_dn
4340 master = get_fsmo_roleowner(samdb, infrastructure_dn,
4342 if own_dn != master:
4343 raise CommandError("This server is not the infrastructure master.")
4346 samdb.transaction_start()
4347 error_encountered = False
4349 from samba.forest_update import ForestUpdate
4350 forest = ForestUpdate(samdb, fix=True)
4352 forest.check_updates_iterator([53, 79, 80, 81, 82, 83])
4353 forest.check_updates_functional_level(target_level,
4354 DS_DOMAIN_FUNCTION_2008_R2,
4355 update_revision=True)
4357 samdb.transaction_commit()
4358 except Exception as e:
4359 print("Exception: %s" % e)
4360 samdb.transaction_cancel()
4361 error_encountered = True
4364 samdb.transaction_start()
4365 error_encountered = False
4367 from samba.domain_update import DomainUpdate
4369 domain = DomainUpdate(samdb, fix=True)
4370 domain.check_updates_functional_level(target_level,
4371 DS_DOMAIN_FUNCTION_2008,
4372 update_revision=True)
4374 samdb.transaction_commit()
4375 except Exception as e:
4376 print("Exception: %s" % e)
4377 samdb.transaction_cancel()
4378 error_encountered = True
4380 if updates_allowed_overriden:
4381 lp.set("dsdb:schema update allowed", "no")
4383 if error_encountered:
4384 raise CommandError('Failed to perform functional prep')
4387 class cmd_domain(SuperCommand):
4388 """Domain management."""
4391 subcommands["demote"] = cmd_domain_demote()
4392 if cmd_domain_export_keytab is not None:
4393 subcommands["exportkeytab"] = cmd_domain_export_keytab()
4394 subcommands["info"] = cmd_domain_info()
4395 subcommands["provision"] = cmd_domain_provision()
4396 subcommands["join"] = cmd_domain_join()
4397 subcommands["dcpromo"] = cmd_domain_dcpromo()
4398 subcommands["level"] = cmd_domain_level()
4399 subcommands["passwordsettings"] = cmd_domain_passwordsettings()
4400 subcommands["classicupgrade"] = cmd_domain_classicupgrade()
4401 subcommands["samba3upgrade"] = cmd_domain_samba3upgrade()
4402 subcommands["trust"] = cmd_domain_trust()
4403 subcommands["tombstones"] = cmd_domain_tombstones()
4404 subcommands["schemaupgrade"] = cmd_domain_schema_upgrade()
4405 subcommands["functionalprep"] = cmd_domain_functional_prep()
4406 subcommands["backup"] = cmd_domain_backup()