3 # Copyright Matthias Dieter Wallnoefer 2009
4 # Copyright Andrew Kroeger 2009
5 # Copyright Jelmer Vernooij 2007-2012
6 # Copyright Giampaolo Lauria 2011
7 # Copyright Matthieu Patou <mat@matws.net> 2011
8 # Copyright Andrew Bartlett 2008-2015
9 # Copyright Stefan Metzmacher 2012
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 3 of the License, or
14 # (at your option) any later version.
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 from __future__ import print_function
26 from __future__ import division
27 import samba.getopt as options
39 from samba import ntstatus
40 from samba import NTSTATUSError
41 from samba import werror
42 from getpass import getpass
43 from samba.net import Net, LIBNET_JOIN_AUTOMATIC
45 from samba.join import join_RODC, join_DC, join_subdomain
46 from samba.auth import system_session
47 from samba.samdb import SamDB, get_default_backend_store
48 from samba.ndr import ndr_unpack, ndr_pack, ndr_print
49 from samba.dcerpc import drsuapi
50 from samba.dcerpc import drsblobs
51 from samba.dcerpc import lsa
52 from samba.dcerpc import netlogon
53 from samba.dcerpc import security
54 from samba.dcerpc import nbt
55 from samba.dcerpc import misc
56 from samba.dcerpc.samr import DOMAIN_PASSWORD_COMPLEX, DOMAIN_PASSWORD_STORE_CLEARTEXT
57 from samba.netcmd import (
63 from samba.netcmd.fsmo import get_fsmo_roleowner
64 from samba.netcmd.common import netcmd_get_domain_infos_via_cldap
65 from samba.samba3 import Samba3
66 from samba.samba3 import param as s3param
67 from samba.upgrade import upgrade_from_samba3
68 from samba.drs_utils import (
69 sendDsReplicaSync, drsuapi_connect, drsException,
71 from samba import remove_dc, arcfour_encrypt, string_to_byte_array
73 from samba.dsdb import (
74 DS_DOMAIN_FUNCTION_2000,
75 DS_DOMAIN_FUNCTION_2003,
76 DS_DOMAIN_FUNCTION_2003_MIXED,
77 DS_DOMAIN_FUNCTION_2008,
78 DS_DOMAIN_FUNCTION_2008_R2,
79 DS_DOMAIN_FUNCTION_2012,
80 DS_DOMAIN_FUNCTION_2012_R2,
81 DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL,
82 DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL,
83 UF_WORKSTATION_TRUST_ACCOUNT,
84 UF_SERVER_TRUST_ACCOUNT,
85 UF_TRUSTED_FOR_DELEGATION,
86 UF_PARTIAL_SECRETS_ACCOUNT
89 from samba.provision import (
92 DEFAULT_MIN_PWD_LENGTH,
96 from samba.provision.common import (
102 string_version_to_constant = {
103 "2008_R2" : DS_DOMAIN_FUNCTION_2008_R2,
104 "2012": DS_DOMAIN_FUNCTION_2012,
105 "2012_R2": DS_DOMAIN_FUNCTION_2012_R2,
108 common_provision_join_options = [
109 Option("--machinepass", type="string", metavar="PASSWORD",
110 help="choose machine password (otherwise random)"),
111 Option("--plaintext-secrets", action="store_true",
112 help="Store secret/sensitive values as plain text on disk" +
113 "(default is to encrypt secret/ensitive values)"),
114 Option("--targetdir", metavar="DIR",
115 help="Set target directory (where to store provision)", type=str),
116 Option("--quiet", help="Be quiet", action="store_true"),
119 def get_testparm_var(testparm, smbconf, varname):
120 errfile = open(os.devnull, 'w')
121 p = subprocess.Popen([testparm, '-s', '-l',
122 '--parameter-name=%s' % varname, smbconf],
123 stdout=subprocess.PIPE, stderr=errfile)
124 (out,err) = p.communicate()
126 lines = out.split('\n')
128 return lines[0].strip()
132 import samba.dckeytab
134 cmd_domain_export_keytab = None
136 class cmd_domain_export_keytab(Command):
137 """Dump Kerberos keys of the domain into a keytab."""
139 synopsis = "%prog <keytab> [options]"
141 takes_optiongroups = {
142 "sambaopts": options.SambaOptions,
143 "credopts": options.CredentialsOptions,
144 "versionopts": options.VersionOptions,
148 Option("--principal", help="extract only this principal", type=str),
151 takes_args = ["keytab"]
153 def run(self, keytab, credopts=None, sambaopts=None, versionopts=None, principal=None):
154 lp = sambaopts.get_loadparm()
156 net.export_keytab(keytab=keytab, principal=principal)
159 class cmd_domain_info(Command):
160 """Print basic info about a domain and the DC passed as parameter."""
162 synopsis = "%prog <ip_address> [options]"
167 takes_optiongroups = {
168 "sambaopts": options.SambaOptions,
169 "credopts": options.CredentialsOptions,
170 "versionopts": options.VersionOptions,
173 takes_args = ["address"]
175 def run(self, address, credopts=None, sambaopts=None, versionopts=None):
176 lp = sambaopts.get_loadparm()
178 res = netcmd_get_domain_infos_via_cldap(lp, None, address)
180 raise CommandError("Invalid IP address '" + address + "'!")
181 self.outf.write("Forest : %s\n" % res.forest)
182 self.outf.write("Domain : %s\n" % res.dns_domain)
183 self.outf.write("Netbios domain : %s\n" % res.domain_name)
184 self.outf.write("DC name : %s\n" % res.pdc_dns_name)
185 self.outf.write("DC netbios name : %s\n" % res.pdc_name)
186 self.outf.write("Server site : %s\n" % res.server_site)
187 self.outf.write("Client site : %s\n" % res.client_site)
190 class cmd_domain_provision(Command):
191 """Provision a domain."""
193 synopsis = "%prog [options]"
195 takes_optiongroups = {
196 "sambaopts": options.SambaOptions,
197 "versionopts": options.VersionOptions,
201 Option("--interactive", help="Ask for names", action="store_true"),
202 Option("--domain", type="string", metavar="DOMAIN",
203 help="NetBIOS domain name to use"),
204 Option("--domain-guid", type="string", metavar="GUID",
205 help="set domainguid (otherwise random)"),
206 Option("--domain-sid", type="string", metavar="SID",
207 help="set domainsid (otherwise random)"),
208 Option("--ntds-guid", type="string", metavar="GUID",
209 help="set NTDS object GUID (otherwise random)"),
210 Option("--invocationid", type="string", metavar="GUID",
211 help="set invocationid (otherwise random)"),
212 Option("--host-name", type="string", metavar="HOSTNAME",
213 help="set hostname"),
214 Option("--host-ip", type="string", metavar="IPADDRESS",
215 help="set IPv4 ipaddress"),
216 Option("--host-ip6", type="string", metavar="IP6ADDRESS",
217 help="set IPv6 ipaddress"),
218 Option("--site", type="string", metavar="SITENAME",
219 help="set site name"),
220 Option("--adminpass", type="string", metavar="PASSWORD",
221 help="choose admin password (otherwise random)"),
222 Option("--krbtgtpass", type="string", metavar="PASSWORD",
223 help="choose krbtgt password (otherwise random)"),
224 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
225 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
226 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
227 "BIND9_FLATFILE uses bind9 text database to store zone information, "
228 "BIND9_DLZ uses samba4 AD to store zone information, "
229 "NONE skips the DNS setup entirely (not recommended)",
230 default="SAMBA_INTERNAL"),
231 Option("--dnspass", type="string", metavar="PASSWORD",
232 help="choose dns password (otherwise random)"),
233 Option("--root", type="string", metavar="USERNAME",
234 help="choose 'root' unix username"),
235 Option("--nobody", type="string", metavar="USERNAME",
236 help="choose 'nobody' user"),
237 Option("--users", type="string", metavar="GROUPNAME",
238 help="choose 'users' group"),
239 Option("--blank", action="store_true",
240 help="do not add users or groups, just the structure"),
241 Option("--server-role", type="choice", metavar="ROLE",
242 choices=["domain controller", "dc", "member server", "member", "standalone"],
243 help="The server role (domain controller | dc | member server | member | standalone). Default is dc.",
244 default="domain controller"),
245 Option("--function-level", type="choice", metavar="FOR-FUN-LEVEL",
246 choices=["2000", "2003", "2008", "2008_R2"],
247 help="The domain and forest function level (2000 | 2003 | 2008 | 2008_R2 - always native). Default is (Windows) 2008_R2 Native.",
249 Option("--base-schema", type="choice", metavar="BASE-SCHEMA",
250 choices=["2008_R2", "2008_R2_old", "2012", "2012_R2"],
251 help="The base schema files to use. Default is (Windows) 2008_R2.",
253 Option("--next-rid", type="int", metavar="NEXTRID", default=1000,
254 help="The initial nextRid value (only needed for upgrades). Default is 1000."),
255 Option("--partitions-only",
256 help="Configure Samba's partitions, but do not modify them (ie, join a BDC)", action="store_true"),
257 Option("--use-rfc2307", action="store_true", help="Use AD to store posix attributes (default = no)"),
258 Option("--backend-store", type="choice", metavar="BACKENDSTORE",
259 choices=["tdb", "mdb"],
260 help="Specify the database backend to be used "
261 "(default is %s)" % get_default_backend_store()),
265 Option("--ldapadminpass", type="string", metavar="PASSWORD",
266 help="choose password to set between Samba and its LDAP backend (otherwise random)"),
267 Option("--ldap-backend-type", type="choice", metavar="LDAP-BACKEND-TYPE",
268 help="Test initialisation support for unsupported LDAP backend type (fedora-ds or openldap) DO NOT USE",
269 choices=["fedora-ds", "openldap"]),
270 Option("--ol-mmr-urls", type="string", metavar="LDAPSERVER",
271 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\""),
272 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",
273 action="store_true"),
274 Option("--slapd-path", type="string", metavar="SLAPD-PATH",
275 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."),
276 Option("--ldap-backend-extra-port", type="int", metavar="LDAP-BACKEND-EXTRA-PORT", help="Additional TCP port for LDAP backend server (to use for replication)"),
277 Option("--ldap-backend-forced-uri", type="string", metavar="LDAP-BACKEND-FORCED-URI",
278 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"),
279 Option("--ldap-backend-nosync", help="Configure LDAP backend not to call fsync() (for performance in test environments)", action="store_true"),
283 Option("--use-ntvfs", action="store_true", help="Use NTVFS for the fileserver (default = no)"),
284 Option("--use-xattrs", type="choice", choices=["yes","no","auto"],
285 metavar="[yes|no|auto]",
286 help="Define if we should use the native fs capabilities or a tdb file for "
287 "storing attributes likes ntacl when --use-ntvfs is set. "
288 "auto tries to make an inteligent guess based on the user rights and system capabilities",
292 takes_options.extend(common_provision_join_options)
294 if os.getenv('TEST_LDAP', "no") == "yes":
295 takes_options.extend(openldap_options)
297 if samba.is_ntvfs_fileserver_built():
298 takes_options.extend(ntvfs_options)
302 def run(self, sambaopts=None, versionopts=None,
325 ldap_backend_type=None,
329 partitions_only=None,
336 ldap_backend_nosync=None,
337 ldap_backend_extra_port=None,
338 ldap_backend_forced_uri=None,
339 ldap_dryrun_mode=None,
341 plaintext_secrets=False,
344 self.logger = self.get_logger("provision")
346 self.logger.setLevel(logging.WARNING)
348 self.logger.setLevel(logging.INFO)
350 lp = sambaopts.get_loadparm()
351 smbconf = lp.configfile
353 if dns_forwarder is not None:
354 suggested_forwarder = dns_forwarder
356 suggested_forwarder = self._get_nameserver_ip()
357 if suggested_forwarder is None:
358 suggested_forwarder = "none"
360 if len(self.raw_argv) == 1:
364 from getpass import getpass
367 def ask(prompt, default=None):
368 if default is not None:
369 print("%s [%s]: " % (prompt, default), end=' ')
371 print("%s: " % (prompt,), end=' ')
372 return sys.stdin.readline().rstrip("\n") or default
375 default = socket.getfqdn().split(".", 1)[1].upper()
378 realm = ask("Realm", default)
379 if realm in (None, ""):
380 raise CommandError("No realm set!")
383 default = realm.split(".")[0]
386 domain = ask("Domain", default)
388 raise CommandError("No domain set!")
390 server_role = ask("Server Role (dc, member, standalone)", "dc")
392 dns_backend = ask("DNS backend (SAMBA_INTERNAL, BIND9_FLATFILE, BIND9_DLZ, NONE)", "SAMBA_INTERNAL")
393 if dns_backend in (None, ''):
394 raise CommandError("No DNS backend set!")
396 if dns_backend == "SAMBA_INTERNAL":
397 dns_forwarder = ask("DNS forwarder IP address (write 'none' to disable forwarding)", suggested_forwarder)
398 if dns_forwarder.lower() in (None, 'none'):
399 suggested_forwarder = None
403 adminpassplain = getpass("Administrator password: ")
404 issue = self._adminpass_issue(adminpassplain)
406 self.errf.write("%s.\n" % issue)
408 adminpassverify = getpass("Retype password: ")
409 if not adminpassplain == adminpassverify:
410 self.errf.write("Sorry, passwords do not match.\n")
412 adminpass = adminpassplain
416 realm = sambaopts._lp.get('realm')
418 raise CommandError("No realm set!")
420 raise CommandError("No domain set!")
423 issue = self._adminpass_issue(adminpass)
425 raise CommandError(issue)
427 self.logger.info("Administrator password will be set randomly!")
429 if function_level == "2000":
430 dom_for_fun_level = DS_DOMAIN_FUNCTION_2000
431 elif function_level == "2003":
432 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
433 elif function_level == "2008":
434 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008
435 elif function_level == "2008_R2":
436 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
438 if dns_backend == "SAMBA_INTERNAL" and dns_forwarder is None:
439 dns_forwarder = suggested_forwarder
441 samdb_fill = FILL_FULL
443 samdb_fill = FILL_NT4SYNC
444 elif partitions_only:
445 samdb_fill = FILL_DRS
447 if targetdir is not None:
448 if not os.path.isdir(targetdir):
453 if use_xattrs == "yes":
455 elif use_xattrs == "auto" and use_ntvfs == False:
457 elif use_ntvfs == False:
458 raise CommandError("--use-xattrs=no requires --use-ntvfs (not supported for production use). "
459 "Please re-run with --use-xattrs omitted.")
460 elif use_xattrs == "auto" and not lp.get("posix:eadb"):
462 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
464 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
467 samba.ntacls.setntacl(lp, file.name,
468 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
471 self.logger.info("You are not root or your system does not support xattr, using tdb backend for attributes. ")
476 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.")
477 if ldap_backend_type == "existing":
478 if ldap_backend_forced_uri is not None:
479 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)
481 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")
483 if ldap_backend_forced_uri is not None:
484 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")
486 if domain_sid is not None:
487 domain_sid = security.dom_sid(domain_sid)
489 session = system_session()
490 if backend_store is None:
491 backend_store = get_default_backend_store()
493 result = provision(self.logger,
494 session, smbconf=smbconf, targetdir=targetdir,
495 samdb_fill=samdb_fill, realm=realm, domain=domain,
496 domainguid=domain_guid, domainsid=domain_sid,
498 hostip=host_ip, hostip6=host_ip6,
499 sitename=site, ntdsguid=ntds_guid,
500 invocationid=invocationid, adminpass=adminpass,
501 krbtgtpass=krbtgtpass, machinepass=machinepass,
502 dns_backend=dns_backend, dns_forwarder=dns_forwarder,
503 dnspass=dnspass, root=root, nobody=nobody,
505 serverrole=server_role, dom_for_fun_level=dom_for_fun_level,
506 backend_type=ldap_backend_type,
507 ldapadminpass=ldapadminpass, ol_mmr_urls=ol_mmr_urls, slapd_path=slapd_path,
508 useeadb=eadb, next_rid=next_rid, lp=lp, use_ntvfs=use_ntvfs,
509 use_rfc2307=use_rfc2307, skip_sysvolacl=False,
510 ldap_backend_extra_port=ldap_backend_extra_port,
511 ldap_backend_forced_uri=ldap_backend_forced_uri,
512 nosync=ldap_backend_nosync, ldap_dryrun_mode=ldap_dryrun_mode,
513 base_schema=base_schema,
514 plaintext_secrets=plaintext_secrets,
515 backend_store=backend_store)
517 except ProvisioningError as e:
518 raise CommandError("Provision failed", e)
520 result.report_logger(self.logger)
522 def _get_nameserver_ip(self):
523 """Grab the nameserver IP address from /etc/resolv.conf."""
525 RESOLV_CONF="/etc/resolv.conf"
527 if not path.isfile(RESOLV_CONF):
528 self.logger.warning("Failed to locate %s" % RESOLV_CONF)
533 handle = open(RESOLV_CONF, 'r')
535 if not line.startswith('nameserver'):
537 # we want the last non-space continuous string of the line
538 return line.strip().split()[-1]
540 if handle is not None:
543 self.logger.warning("No nameserver found in %s" % RESOLV_CONF)
545 def _adminpass_issue(self, adminpass):
546 """Returns error string for a bad administrator password,
547 or None if acceptable"""
549 if len(adminpass.decode('utf-8')) < DEFAULT_MIN_PWD_LENGTH:
550 return "Administrator password does not meet the default minimum" \
551 " password length requirement (%d characters)" \
552 % DEFAULT_MIN_PWD_LENGTH
553 elif not samba.check_password_quality(adminpass):
554 return "Administrator password does not meet the default" \
560 class cmd_domain_dcpromo(Command):
561 """Promote an existing domain member or NT4 PDC to an AD DC."""
563 synopsis = "%prog <dnsdomain> [DC|RODC] [options]"
565 takes_optiongroups = {
566 "sambaopts": options.SambaOptions,
567 "versionopts": options.VersionOptions,
568 "credopts": options.CredentialsOptions,
572 Option("--server", help="DC to join", type=str),
573 Option("--site", help="site to join", type=str),
574 Option("--domain-critical-only",
575 help="only replicate critical domain objects",
576 action="store_true"),
577 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
578 choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
579 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
580 "BIND9_DLZ uses samba4 AD to store zone information, "
581 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
582 default="SAMBA_INTERNAL"),
583 Option("--verbose", help="Be verbose", action="store_true")
586 takes_options.extend(common_provision_join_options)
589 Option("--use-ntvfs", action="store_true", help="Use NTVFS for the fileserver (default = no)"),
592 if samba.is_ntvfs_fileserver_built():
593 takes_options.extend(ntvfs_options)
596 takes_args = ["domain", "role?"]
598 def run(self, domain, role=None, sambaopts=None, credopts=None,
599 versionopts=None, server=None, site=None, targetdir=None,
600 domain_critical_only=False, parent_domain=None, machinepass=None,
601 use_ntvfs=False, dns_backend=None,
602 quiet=False, verbose=False, plaintext_secrets=False):
603 lp = sambaopts.get_loadparm()
604 creds = credopts.get_credentials(lp)
605 net = Net(creds, lp, server=credopts.ipaddress)
607 logger = self.get_logger()
609 logger.setLevel(logging.DEBUG)
611 logger.setLevel(logging.WARNING)
613 logger.setLevel(logging.INFO)
615 netbios_name = lp.get("netbios name")
621 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
622 site=site, netbios_name=netbios_name, targetdir=targetdir,
623 domain_critical_only=domain_critical_only,
624 machinepass=machinepass, use_ntvfs=use_ntvfs,
625 dns_backend=dns_backend,
626 promote_existing=True, plaintext_secrets=plaintext_secrets)
628 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
629 site=site, netbios_name=netbios_name, targetdir=targetdir,
630 domain_critical_only=domain_critical_only,
631 machinepass=machinepass, use_ntvfs=use_ntvfs, dns_backend=dns_backend,
632 promote_existing=True, plaintext_secrets=plaintext_secrets)
634 raise CommandError("Invalid role '%s' (possible values: DC, RODC)" % role)
637 class cmd_domain_join(Command):
638 """Join domain as either member or backup domain controller."""
640 synopsis = "%prog <dnsdomain> [DC|RODC|MEMBER|SUBDOMAIN] [options]"
642 takes_optiongroups = {
643 "sambaopts": options.SambaOptions,
644 "versionopts": options.VersionOptions,
645 "credopts": options.CredentialsOptions,
649 Option("--server", help="DC to join", type=str),
650 Option("--site", help="site to join", type=str),
651 Option("--parent-domain", help="parent domain to create subdomain under", type=str),
652 Option("--domain-critical-only",
653 help="only replicate critical domain objects",
654 action="store_true"),
655 Option("--adminpass", type="string", metavar="PASSWORD",
656 help="choose adminstrator password when joining as a subdomain (otherwise random)"),
657 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
658 choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
659 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
660 "BIND9_DLZ uses samba4 AD to store zone information, "
661 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
662 default="SAMBA_INTERNAL"),
663 Option("--verbose", help="Be verbose", action="store_true")
667 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
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, plaintext_secrets=False):
682 lp = sambaopts.get_loadparm()
683 creds = credopts.get_credentials(lp)
684 net = Net(creds, lp, server=credopts.ipaddress)
687 site = "Default-First-Site-Name"
689 logger = self.get_logger()
691 logger.setLevel(logging.DEBUG)
693 logger.setLevel(logging.WARNING)
695 logger.setLevel(logging.INFO)
697 netbios_name = lp.get("netbios name")
702 if role is None or role == "MEMBER":
703 (join_password, sid, domain_name) = net.join_member(
704 domain, netbios_name, LIBNET_JOIN_AUTOMATIC,
705 machinepass=machinepass)
707 self.errf.write("Joined domain %s (%s)\n" % (domain_name, sid))
709 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
710 site=site, netbios_name=netbios_name, targetdir=targetdir,
711 domain_critical_only=domain_critical_only,
712 machinepass=machinepass, use_ntvfs=use_ntvfs,
713 dns_backend=dns_backend,
714 plaintext_secrets=plaintext_secrets)
716 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
717 site=site, netbios_name=netbios_name, targetdir=targetdir,
718 domain_critical_only=domain_critical_only,
719 machinepass=machinepass, use_ntvfs=use_ntvfs,
720 dns_backend=dns_backend,
721 plaintext_secrets=plaintext_secrets)
722 elif role == "SUBDOMAIN":
724 logger.info("Administrator password will be set randomly!")
726 netbios_domain = lp.get("workgroup")
727 if parent_domain is None:
728 parent_domain = ".".join(domain.split(".")[1:])
729 join_subdomain(logger=logger, server=server, creds=creds, lp=lp, dnsdomain=domain,
730 parent_domain=parent_domain, site=site,
731 netbios_name=netbios_name, netbios_domain=netbios_domain,
732 targetdir=targetdir, machinepass=machinepass,
733 use_ntvfs=use_ntvfs, dns_backend=dns_backend,
735 plaintext_secrets=plaintext_secrets)
737 raise CommandError("Invalid role '%s' (possible values: MEMBER, DC, RODC, SUBDOMAIN)" % role)
740 class cmd_domain_demote(Command):
741 """Demote ourselves from the role of Domain Controller."""
743 synopsis = "%prog [options]"
746 Option("--server", help="writable DC to write demotion changes on", type=str),
747 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
748 metavar="URL", dest="H"),
749 Option("--remove-other-dead-server", help="Dead DC (name or NTDS GUID) "
750 "to remove ALL references to (rather than this DC)", type=str),
751 Option("--quiet", help="Be quiet", action="store_true"),
752 Option("--verbose", help="Be verbose", action="store_true"),
755 takes_optiongroups = {
756 "sambaopts": options.SambaOptions,
757 "credopts": options.CredentialsOptions,
758 "versionopts": options.VersionOptions,
761 def run(self, sambaopts=None, credopts=None,
762 versionopts=None, server=None,
763 remove_other_dead_server=None, H=None,
764 verbose=False, quiet=False):
765 lp = sambaopts.get_loadparm()
766 creds = credopts.get_credentials(lp)
767 net = Net(creds, lp, server=credopts.ipaddress)
769 logger = self.get_logger()
771 logger.setLevel(logging.DEBUG)
773 logger.setLevel(logging.WARNING)
775 logger.setLevel(logging.INFO)
777 if remove_other_dead_server is not None:
778 if server is not None:
779 samdb = SamDB(url="ldap://%s" % server,
780 session_info=system_session(),
781 credentials=creds, lp=lp)
783 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
785 remove_dc.remove_dc(samdb, logger, remove_other_dead_server)
786 except remove_dc.DemoteException as err:
787 raise CommandError("Demote failed: %s" % err)
790 netbios_name = lp.get("netbios name")
791 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
793 res = samdb.search(expression='(&(objectClass=computer)(serverReferenceBL=*))', attrs=["dnsHostName", "name"])
795 raise CommandError("Unable to search for servers")
798 raise CommandError("You are the latest server in the domain")
802 if str(e["name"]).lower() != netbios_name.lower():
803 server = e["dnsHostName"]
806 ntds_guid = samdb.get_ntds_GUID()
807 msg = samdb.search(base=str(samdb.get_config_basedn()),
808 scope=ldb.SCOPE_SUBTREE, expression="(objectGUID=%s)" % ntds_guid,
810 if len(msg) == 0 or "options" not in msg[0]:
811 raise CommandError("Failed to find options on %s" % ntds_guid)
814 dsa_options = int(str(msg[0]['options']))
816 res = samdb.search(expression="(fSMORoleOwner=%s)" % str(ntds_dn),
817 controls=["search_options:1:2"])
820 raise CommandError("Current DC is still the owner of %d role(s), use the role command to transfer roles to another DC" % len(res))
822 self.errf.write("Using %s as partner server for the demotion\n" %
824 (drsuapiBind, drsuapi_handle, supportedExtensions) = drsuapi_connect(server, lp, creds)
826 self.errf.write("Deactivating inbound replication\n")
831 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
832 dsa_options |= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
833 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
837 self.errf.write("Asking partner server %s to synchronize from us\n"
839 for part in (samdb.get_schema_basedn(),
840 samdb.get_config_basedn(),
841 samdb.get_root_basedn()):
842 nc = drsuapi.DsReplicaObjectIdentifier()
845 req1 = drsuapi.DsReplicaSyncRequest1()
846 req1.naming_context = nc;
847 req1.options = drsuapi.DRSUAPI_DRS_WRIT_REP
848 req1.source_dsa_guid = misc.GUID(ntds_guid)
851 drsuapiBind.DsReplicaSync(drsuapi_handle, 1, req1)
852 except RuntimeError as e1:
853 (werr, string) = e1.args
854 if werr == werror.WERR_DS_DRA_NO_REPLICA:
858 "Error while replicating out last local changes from '%s' for demotion, "
859 "re-enabling inbound replication\n" % part)
860 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
861 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
863 raise CommandError("Error while sending a DsReplicaSync for partition '%s'" % str(part), string)
865 remote_samdb = SamDB(url="ldap://%s" % server,
866 session_info=system_session(),
867 credentials=creds, lp=lp)
869 self.errf.write("Changing userControl and container\n")
870 res = remote_samdb.search(base=str(remote_samdb.domain_dn()),
871 expression="(&(objectClass=user)(sAMAccountName=%s$))" %
872 netbios_name.upper(),
873 attrs=["userAccountControl"])
875 uac = int(str(res[0]["userAccountControl"]))
877 except Exception as e:
878 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
880 "Error while demoting, re-enabling inbound replication\n")
881 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
882 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
884 raise CommandError("Error while changing account control", e)
887 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
889 "Error while demoting, re-enabling inbound replication")
890 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
891 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
893 raise CommandError("Unable to find object with samaccountName = %s$"
894 " in the remote dc" % netbios_name.upper())
898 uac &= ~(UF_SERVER_TRUST_ACCOUNT|UF_TRUSTED_FOR_DELEGATION|UF_PARTIAL_SECRETS_ACCOUNT)
899 uac |= UF_WORKSTATION_TRUST_ACCOUNT
904 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
905 ldb.FLAG_MOD_REPLACE,
906 "userAccountControl")
908 remote_samdb.modify(msg)
909 except Exception as e:
910 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
912 "Error while demoting, re-enabling inbound replication")
913 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
914 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
917 raise CommandError("Error while changing account control", e)
919 parent = msg.dn.parent()
920 dc_name = res[0].dn.get_rdn_value()
921 rdn = "CN=%s" % dc_name
923 # Let's move to the Computer container
927 computer_dn = ldb.Dn(remote_samdb, "CN=Computers,%s" % str(remote_samdb.domain_dn()))
928 res = remote_samdb.search(base=computer_dn, expression=rdn, scope=ldb.SCOPE_ONELEVEL)
931 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
932 scope=ldb.SCOPE_ONELEVEL)
933 while(len(res) != 0 and i < 100):
935 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
936 scope=ldb.SCOPE_ONELEVEL)
939 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
941 "Error while demoting, re-enabling inbound replication\n")
942 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
943 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
949 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
950 ldb.FLAG_MOD_REPLACE,
951 "userAccountControl")
953 remote_samdb.modify(msg)
955 raise CommandError("Unable to find a slot for renaming %s,"
956 " all names from %s-1 to %s-%d seemed used" %
957 (str(dc_dn), rdn, rdn, i - 9))
959 newrdn = "%s-%d" % (rdn, i)
962 newdn = ldb.Dn(remote_samdb, "%s,%s" % (newrdn, str(computer_dn)))
963 remote_samdb.rename(dc_dn, newdn)
964 except Exception as e:
965 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
967 "Error while demoting, re-enabling inbound replication\n")
968 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
969 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
975 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
976 ldb.FLAG_MOD_REPLACE,
977 "userAccountControl")
979 remote_samdb.modify(msg)
980 raise CommandError("Error while renaming %s to %s" % (str(dc_dn), str(newdn)), e)
983 server_dsa_dn = samdb.get_serverName()
984 domain = remote_samdb.get_root_basedn()
987 req1 = drsuapi.DsRemoveDSServerRequest1()
988 req1.server_dn = str(server_dsa_dn)
989 req1.domain_dn = str(domain)
992 drsuapiBind.DsRemoveDSServer(drsuapi_handle, 1, req1)
993 except RuntimeError as e3:
994 (werr, string) = e3.args
995 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
997 "Error while demoting, re-enabling inbound replication\n")
998 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
999 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
1005 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
1006 ldb.FLAG_MOD_REPLACE,
1007 "userAccountControl")
1008 remote_samdb.modify(msg)
1009 remote_samdb.rename(newdn, dc_dn)
1010 if werr == werror.WERR_DS_DRA_NO_REPLICA:
1011 raise CommandError("The DC %s is not present on (already removed from) the remote server: " % server_dsa_dn, e)
1013 raise CommandError("Error while sending a removeDsServer of %s: " % server_dsa_dn, e)
1015 remove_dc.remove_sysvol_references(remote_samdb, logger, dc_name)
1017 # These are objects under the computer account that should be deleted
1018 for s in ("CN=Enterprise,CN=NTFRS Subscriptions",
1019 "CN=%s, CN=NTFRS Subscriptions" % lp.get("realm"),
1020 "CN=Domain system Volumes (SYSVOL Share), CN=NTFRS Subscriptions",
1021 "CN=NTFRS Subscriptions"):
1023 remote_samdb.delete(ldb.Dn(remote_samdb,
1024 "%s,%s" % (s, str(newdn))))
1025 except ldb.LdbError as l:
1028 self.errf.write("Demote successful\n")
1031 class cmd_domain_level(Command):
1032 """Raise domain and forest function levels."""
1034 synopsis = "%prog (show|raise <options>) [options]"
1036 takes_optiongroups = {
1037 "sambaopts": options.SambaOptions,
1038 "credopts": options.CredentialsOptions,
1039 "versionopts": options.VersionOptions,
1043 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1044 metavar="URL", dest="H"),
1045 Option("--quiet", help="Be quiet", action="store_true"),
1046 Option("--forest-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
1047 help="The forest function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)"),
1048 Option("--domain-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
1049 help="The domain function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)")
1052 takes_args = ["subcommand"]
1054 def run(self, subcommand, H=None, forest_level=None, domain_level=None,
1055 quiet=False, credopts=None, sambaopts=None, versionopts=None):
1056 lp = sambaopts.get_loadparm()
1057 creds = credopts.get_credentials(lp, fallback_machine=True)
1059 samdb = SamDB(url=H, session_info=system_session(),
1060 credentials=creds, lp=lp)
1062 domain_dn = samdb.domain_dn()
1064 res_forest = samdb.search("CN=Partitions,%s" % samdb.get_config_basedn(),
1065 scope=ldb.SCOPE_BASE, attrs=["msDS-Behavior-Version"])
1066 assert len(res_forest) == 1
1068 res_domain = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1069 attrs=["msDS-Behavior-Version", "nTMixedDomain"])
1070 assert len(res_domain) == 1
1072 res_dc_s = samdb.search("CN=Sites,%s" % samdb.get_config_basedn(),
1073 scope=ldb.SCOPE_SUBTREE, expression="(objectClass=nTDSDSA)",
1074 attrs=["msDS-Behavior-Version"])
1075 assert len(res_dc_s) >= 1
1077 # default values, since "msDS-Behavior-Version" does not exist on Windows 2000 AD
1078 level_forest = DS_DOMAIN_FUNCTION_2000
1079 level_domain = DS_DOMAIN_FUNCTION_2000
1081 if "msDS-Behavior-Version" in res_forest[0]:
1082 level_forest = int(res_forest[0]["msDS-Behavior-Version"][0])
1083 if "msDS-Behavior-Version" in res_domain[0]:
1084 level_domain = int(res_domain[0]["msDS-Behavior-Version"][0])
1085 level_domain_mixed = int(res_domain[0]["nTMixedDomain"][0])
1088 for msg in res_dc_s:
1089 if "msDS-Behavior-Version" in msg:
1090 if min_level_dc is None or int(msg["msDS-Behavior-Version"][0]) < min_level_dc:
1091 min_level_dc = int(msg["msDS-Behavior-Version"][0])
1093 min_level_dc = DS_DOMAIN_FUNCTION_2000
1094 # well, this is the least
1097 if level_forest < DS_DOMAIN_FUNCTION_2000 or level_domain < DS_DOMAIN_FUNCTION_2000:
1098 raise CommandError("Domain and/or forest function level(s) is/are invalid. Correct them or reprovision!")
1099 if min_level_dc < DS_DOMAIN_FUNCTION_2000:
1100 raise CommandError("Lowest function level of a DC is invalid. Correct this or reprovision!")
1101 if level_forest > level_domain:
1102 raise CommandError("Forest function level is higher than the domain level(s). Correct this or reprovision!")
1103 if level_domain > min_level_dc:
1104 raise CommandError("Domain function level is higher than the lowest function level of a DC. Correct this or reprovision!")
1106 if subcommand == "show":
1107 self.message("Domain and forest function level for domain '%s'" % domain_dn)
1108 if level_forest == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1109 self.message("\nATTENTION: You run SAMBA 4 on a forest function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1110 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1111 self.message("\nATTENTION: You run SAMBA 4 on a domain function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1112 if min_level_dc == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1113 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)!")
1117 if level_forest == DS_DOMAIN_FUNCTION_2000:
1119 elif level_forest == DS_DOMAIN_FUNCTION_2003_MIXED:
1120 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1121 elif level_forest == DS_DOMAIN_FUNCTION_2003:
1123 elif level_forest == DS_DOMAIN_FUNCTION_2008:
1125 elif level_forest == DS_DOMAIN_FUNCTION_2008_R2:
1127 elif level_forest == DS_DOMAIN_FUNCTION_2012:
1129 elif level_forest == DS_DOMAIN_FUNCTION_2012_R2:
1132 outstr = "higher than 2012 R2"
1133 self.message("Forest function level: (Windows) " + outstr)
1135 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1136 outstr = "2000 mixed (NT4 DC support)"
1137 elif level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed == 0:
1139 elif level_domain == DS_DOMAIN_FUNCTION_2003_MIXED:
1140 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1141 elif level_domain == DS_DOMAIN_FUNCTION_2003:
1143 elif level_domain == DS_DOMAIN_FUNCTION_2008:
1145 elif level_domain == DS_DOMAIN_FUNCTION_2008_R2:
1147 elif level_domain == DS_DOMAIN_FUNCTION_2012:
1149 elif level_domain == DS_DOMAIN_FUNCTION_2012_R2:
1152 outstr = "higher than 2012 R2"
1153 self.message("Domain function level: (Windows) " + outstr)
1155 if min_level_dc == DS_DOMAIN_FUNCTION_2000:
1157 elif min_level_dc == DS_DOMAIN_FUNCTION_2003:
1159 elif min_level_dc == DS_DOMAIN_FUNCTION_2008:
1161 elif min_level_dc == DS_DOMAIN_FUNCTION_2008_R2:
1163 elif min_level_dc == DS_DOMAIN_FUNCTION_2012:
1165 elif min_level_dc == DS_DOMAIN_FUNCTION_2012_R2:
1168 outstr = "higher than 2012 R2"
1169 self.message("Lowest function level of a DC: (Windows) " + outstr)
1171 elif subcommand == "raise":
1174 if domain_level is not None:
1175 if domain_level == "2003":
1176 new_level_domain = DS_DOMAIN_FUNCTION_2003
1177 elif domain_level == "2008":
1178 new_level_domain = DS_DOMAIN_FUNCTION_2008
1179 elif domain_level == "2008_R2":
1180 new_level_domain = DS_DOMAIN_FUNCTION_2008_R2
1181 elif domain_level == "2012":
1182 new_level_domain = DS_DOMAIN_FUNCTION_2012
1183 elif domain_level == "2012_R2":
1184 new_level_domain = DS_DOMAIN_FUNCTION_2012_R2
1186 if new_level_domain <= level_domain and level_domain_mixed == 0:
1187 raise CommandError("Domain function level can't be smaller than or equal to the actual one!")
1188 if new_level_domain > min_level_dc:
1189 raise CommandError("Domain function level can't be higher than the lowest function level of a DC!")
1191 # Deactivate mixed/interim domain support
1192 if level_domain_mixed != 0:
1193 # Directly on the base DN
1195 m.dn = ldb.Dn(samdb, domain_dn)
1196 m["nTMixedDomain"] = ldb.MessageElement("0",
1197 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1201 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup") + ",CN=Partitions,%s" % samdb.get_config_basedn())
1202 m["nTMixedDomain"] = ldb.MessageElement("0",
1203 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1206 except ldb.LdbError as e:
1207 (enum, emsg) = e.args
1208 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1211 # Directly on the base DN
1213 m.dn = ldb.Dn(samdb, domain_dn)
1214 m["msDS-Behavior-Version"]= ldb.MessageElement(
1215 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1216 "msDS-Behavior-Version")
1220 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup")
1221 + ",CN=Partitions,%s" % samdb.get_config_basedn())
1222 m["msDS-Behavior-Version"]= ldb.MessageElement(
1223 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1224 "msDS-Behavior-Version")
1227 except ldb.LdbError as e2:
1228 (enum, emsg) = e2.args
1229 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1232 level_domain = new_level_domain
1233 msgs.append("Domain function level changed!")
1235 if forest_level is not None:
1236 if forest_level == "2003":
1237 new_level_forest = DS_DOMAIN_FUNCTION_2003
1238 elif forest_level == "2008":
1239 new_level_forest = DS_DOMAIN_FUNCTION_2008
1240 elif forest_level == "2008_R2":
1241 new_level_forest = DS_DOMAIN_FUNCTION_2008_R2
1242 elif forest_level == "2012":
1243 new_level_forest = DS_DOMAIN_FUNCTION_2012
1244 elif forest_level == "2012_R2":
1245 new_level_forest = DS_DOMAIN_FUNCTION_2012_R2
1247 if new_level_forest <= level_forest:
1248 raise CommandError("Forest function level can't be smaller than or equal to the actual one!")
1249 if new_level_forest > level_domain:
1250 raise CommandError("Forest function level can't be higher than the domain function level(s). Please raise it/them first!")
1253 m.dn = ldb.Dn(samdb, "CN=Partitions,%s" % samdb.get_config_basedn())
1254 m["msDS-Behavior-Version"]= ldb.MessageElement(
1255 str(new_level_forest), ldb.FLAG_MOD_REPLACE,
1256 "msDS-Behavior-Version")
1258 msgs.append("Forest function level changed!")
1259 msgs.append("All changes applied successfully!")
1260 self.message("\n".join(msgs))
1262 raise CommandError("invalid argument: '%s' (choose from 'show', 'raise')" % subcommand)
1264 class cmd_domain_passwordsettings_show(Command):
1265 """Display current password settings for the domain."""
1267 synopsis = "%prog [options]"
1269 takes_optiongroups = {
1270 "sambaopts": options.SambaOptions,
1271 "versionopts": options.VersionOptions,
1272 "credopts": options.CredentialsOptions,
1276 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1277 metavar="URL", dest="H"),
1280 def run(self, H=None, credopts=None, sambaopts=None, versionopts=None):
1281 lp = sambaopts.get_loadparm()
1282 creds = credopts.get_credentials(lp)
1284 samdb = SamDB(url=H, session_info=system_session(),
1285 credentials=creds, lp=lp)
1287 domain_dn = samdb.domain_dn()
1288 res = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1289 attrs=["pwdProperties", "pwdHistoryLength", "minPwdLength",
1290 "minPwdAge", "maxPwdAge", "lockoutDuration", "lockoutThreshold",
1291 "lockOutObservationWindow"])
1292 assert(len(res) == 1)
1294 pwd_props = int(res[0]["pwdProperties"][0])
1295 pwd_hist_len = int(res[0]["pwdHistoryLength"][0])
1296 cur_min_pwd_len = int(res[0]["minPwdLength"][0])
1298 cur_min_pwd_age = int(abs(int(res[0]["minPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1299 if int(res[0]["maxPwdAge"][0]) == -0x8000000000000000:
1302 cur_max_pwd_age = int(abs(int(res[0]["maxPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1303 cur_account_lockout_threshold = int(res[0]["lockoutThreshold"][0])
1305 if int(res[0]["lockoutDuration"][0]) == -0x8000000000000000:
1306 cur_account_lockout_duration = 0
1308 cur_account_lockout_duration = abs(int(res[0]["lockoutDuration"][0])) / (1e7 * 60)
1309 cur_reset_account_lockout_after = abs(int(res[0]["lockOutObservationWindow"][0])) / (1e7 * 60)
1310 except Exception as e:
1311 raise CommandError("Could not retrieve password properties!", e)
1313 self.message("Password informations for domain '%s'" % domain_dn)
1315 if pwd_props & DOMAIN_PASSWORD_COMPLEX != 0:
1316 self.message("Password complexity: on")
1318 self.message("Password complexity: off")
1319 if pwd_props & DOMAIN_PASSWORD_STORE_CLEARTEXT != 0:
1320 self.message("Store plaintext passwords: on")
1322 self.message("Store plaintext passwords: off")
1323 self.message("Password history length: %d" % pwd_hist_len)
1324 self.message("Minimum password length: %d" % cur_min_pwd_len)
1325 self.message("Minimum password age (days): %d" % cur_min_pwd_age)
1326 self.message("Maximum password age (days): %d" % cur_max_pwd_age)
1327 self.message("Account lockout duration (mins): %d" % cur_account_lockout_duration)
1328 self.message("Account lockout threshold (attempts): %d" % cur_account_lockout_threshold)
1329 self.message("Reset account lockout after (mins): %d" % cur_reset_account_lockout_after)
1331 class cmd_domain_passwordsettings_set(Command):
1332 """Set password settings.
1334 Password complexity, password lockout policy, history length,
1335 minimum password length, the minimum and maximum password age) on
1336 a Samba AD DC server.
1338 Use against a Windows DC is possible, but group policy will override it.
1341 synopsis = "%prog <options> [options]"
1343 takes_optiongroups = {
1344 "sambaopts": options.SambaOptions,
1345 "versionopts": options.VersionOptions,
1346 "credopts": options.CredentialsOptions,
1350 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1351 metavar="URL", dest="H"),
1352 Option("--quiet", help="Be quiet", action="store_true"),
1353 Option("--complexity", type="choice", choices=["on","off","default"],
1354 help="The password complexity (on | off | default). Default is 'on'"),
1355 Option("--store-plaintext", type="choice", choices=["on","off","default"],
1356 help="Store plaintext passwords where account have 'store passwords with reversible encryption' set (on | off | default). Default is 'off'"),
1357 Option("--history-length",
1358 help="The password history length (<integer> | default). Default is 24.", type=str),
1359 Option("--min-pwd-length",
1360 help="The minimum password length (<integer> | default). Default is 7.", type=str),
1361 Option("--min-pwd-age",
1362 help="The minimum password age (<integer in days> | default). Default is 1.", type=str),
1363 Option("--max-pwd-age",
1364 help="The maximum password age (<integer in days> | default). Default is 43.", type=str),
1365 Option("--account-lockout-duration",
1366 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),
1367 Option("--account-lockout-threshold",
1368 help="The number of bad password attempts allowed before locking out the account (<integer> | default). Default is 0 (never lock out).", type=str),
1369 Option("--reset-account-lockout-after",
1370 help="After this time is elapsed, the recorded number of attempts restarts from zero (<integer> | default). Default is 30.", type=str),
1373 def run(self, H=None, min_pwd_age=None, max_pwd_age=None,
1374 quiet=False, complexity=None, store_plaintext=None, history_length=None,
1375 min_pwd_length=None, account_lockout_duration=None, account_lockout_threshold=None,
1376 reset_account_lockout_after=None, credopts=None, sambaopts=None,
1378 lp = sambaopts.get_loadparm()
1379 creds = credopts.get_credentials(lp)
1381 samdb = SamDB(url=H, session_info=system_session(),
1382 credentials=creds, lp=lp)
1384 domain_dn = samdb.domain_dn()
1387 m.dn = ldb.Dn(samdb, domain_dn)
1388 pwd_props = int(samdb.get_pwdProperties())
1390 if complexity is not None:
1391 if complexity == "on" or complexity == "default":
1392 pwd_props = pwd_props | DOMAIN_PASSWORD_COMPLEX
1393 msgs.append("Password complexity activated!")
1394 elif complexity == "off":
1395 pwd_props = pwd_props & (~DOMAIN_PASSWORD_COMPLEX)
1396 msgs.append("Password complexity deactivated!")
1398 if store_plaintext is not None:
1399 if store_plaintext == "on" or store_plaintext == "default":
1400 pwd_props = pwd_props | DOMAIN_PASSWORD_STORE_CLEARTEXT
1401 msgs.append("Plaintext password storage for changed passwords activated!")
1402 elif store_plaintext == "off":
1403 pwd_props = pwd_props & (~DOMAIN_PASSWORD_STORE_CLEARTEXT)
1404 msgs.append("Plaintext password storage for changed passwords deactivated!")
1406 if complexity is not None or store_plaintext is not None:
1407 m["pwdProperties"] = ldb.MessageElement(str(pwd_props),
1408 ldb.FLAG_MOD_REPLACE, "pwdProperties")
1410 if history_length is not None:
1411 if history_length == "default":
1414 pwd_hist_len = int(history_length)
1416 if pwd_hist_len < 0 or pwd_hist_len > 24:
1417 raise CommandError("Password history length must be in the range of 0 to 24!")
1419 m["pwdHistoryLength"] = ldb.MessageElement(str(pwd_hist_len),
1420 ldb.FLAG_MOD_REPLACE, "pwdHistoryLength")
1421 msgs.append("Password history length changed!")
1423 if min_pwd_length is not None:
1424 if min_pwd_length == "default":
1427 min_pwd_len = int(min_pwd_length)
1429 if min_pwd_len < 0 or min_pwd_len > 14:
1430 raise CommandError("Minimum password length must be in the range of 0 to 14!")
1432 m["minPwdLength"] = ldb.MessageElement(str(min_pwd_len),
1433 ldb.FLAG_MOD_REPLACE, "minPwdLength")
1434 msgs.append("Minimum password length changed!")
1436 if min_pwd_age is not None:
1437 if min_pwd_age == "default":
1440 min_pwd_age = int(min_pwd_age)
1442 if min_pwd_age < 0 or min_pwd_age > 998:
1443 raise CommandError("Minimum password age must be in the range of 0 to 998!")
1446 min_pwd_age_ticks = -int(min_pwd_age * (24 * 60 * 60 * 1e7))
1448 m["minPwdAge"] = ldb.MessageElement(str(min_pwd_age_ticks),
1449 ldb.FLAG_MOD_REPLACE, "minPwdAge")
1450 msgs.append("Minimum password age changed!")
1452 if max_pwd_age is not None:
1453 if max_pwd_age == "default":
1456 max_pwd_age = int(max_pwd_age)
1458 if max_pwd_age < 0 or max_pwd_age > 999:
1459 raise CommandError("Maximum password age must be in the range of 0 to 999!")
1462 if max_pwd_age == 0:
1463 max_pwd_age_ticks = -0x8000000000000000
1465 max_pwd_age_ticks = -int(max_pwd_age * (24 * 60 * 60 * 1e7))
1467 m["maxPwdAge"] = ldb.MessageElement(str(max_pwd_age_ticks),
1468 ldb.FLAG_MOD_REPLACE, "maxPwdAge")
1469 msgs.append("Maximum password age changed!")
1471 if account_lockout_duration is not None:
1472 if account_lockout_duration == "default":
1473 account_lockout_duration = 30
1475 account_lockout_duration = int(account_lockout_duration)
1477 if account_lockout_duration < 0 or account_lockout_duration > 99999:
1478 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1481 if account_lockout_duration == 0:
1482 account_lockout_duration_ticks = -0x8000000000000000
1484 account_lockout_duration_ticks = -int(account_lockout_duration * (60 * 1e7))
1486 m["lockoutDuration"] = ldb.MessageElement(str(account_lockout_duration_ticks),
1487 ldb.FLAG_MOD_REPLACE, "lockoutDuration")
1488 msgs.append("Account lockout duration changed!")
1490 if account_lockout_threshold is not None:
1491 if account_lockout_threshold == "default":
1492 account_lockout_threshold = 0
1494 account_lockout_threshold = int(account_lockout_threshold)
1496 m["lockoutThreshold"] = ldb.MessageElement(str(account_lockout_threshold),
1497 ldb.FLAG_MOD_REPLACE, "lockoutThreshold")
1498 msgs.append("Account lockout threshold changed!")
1500 if reset_account_lockout_after is not None:
1501 if reset_account_lockout_after == "default":
1502 reset_account_lockout_after = 30
1504 reset_account_lockout_after = int(reset_account_lockout_after)
1506 if reset_account_lockout_after < 0 or reset_account_lockout_after > 99999:
1507 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1510 if reset_account_lockout_after == 0:
1511 reset_account_lockout_after_ticks = -0x8000000000000000
1513 reset_account_lockout_after_ticks = -int(reset_account_lockout_after * (60 * 1e7))
1515 m["lockOutObservationWindow"] = ldb.MessageElement(str(reset_account_lockout_after_ticks),
1516 ldb.FLAG_MOD_REPLACE, "lockOutObservationWindow")
1517 msgs.append("Duration to reset account lockout after changed!")
1519 if max_pwd_age > 0 and min_pwd_age >= max_pwd_age:
1520 raise CommandError("Maximum password age (%d) must be greater than minimum password age (%d)!" % (max_pwd_age, min_pwd_age))
1523 raise CommandError("You must specify at least one option to set. Try --help")
1525 msgs.append("All changes applied successfully!")
1526 self.message("\n".join(msgs))
1528 class cmd_domain_passwordsettings(SuperCommand):
1529 """Manage password policy settings."""
1532 subcommands["show"] = cmd_domain_passwordsettings_show()
1533 subcommands["set"] = cmd_domain_passwordsettings_set()
1535 class cmd_domain_classicupgrade(Command):
1536 """Upgrade from Samba classic (NT4-like) database to Samba AD DC database.
1538 Specify either a directory with all Samba classic DC databases and state files (with --dbdir) or
1539 the testparm utility from your classic installation (with --testparm).
1542 synopsis = "%prog [options] <classic_smb_conf>"
1544 takes_optiongroups = {
1545 "sambaopts": options.SambaOptions,
1546 "versionopts": options.VersionOptions
1550 Option("--dbdir", type="string", metavar="DIR",
1551 help="Path to samba classic DC database directory"),
1552 Option("--testparm", type="string", metavar="PATH",
1553 help="Path to samba classic DC testparm utility from the previous installation. This allows the default paths of the previous installation to be followed"),
1554 Option("--targetdir", type="string", metavar="DIR",
1555 help="Path prefix where the new Samba 4.0 AD domain should be initialised"),
1556 Option("--quiet", help="Be quiet", action="store_true"),
1557 Option("--verbose", help="Be verbose", action="store_true"),
1558 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
1559 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
1560 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
1561 "BIND9_FLATFILE uses bind9 text database to store zone information, "
1562 "BIND9_DLZ uses samba4 AD to store zone information, "
1563 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
1564 default="SAMBA_INTERNAL")
1568 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
1569 action="store_true"),
1570 Option("--use-xattrs", type="choice", choices=["yes","no","auto"],
1571 metavar="[yes|no|auto]",
1572 help="Define if we should use the native fs capabilities or a tdb file for "
1573 "storing attributes likes ntacl when --use-ntvfs is set. "
1574 "auto tries to make an inteligent guess based on the user rights and system capabilities",
1577 if samba.is_ntvfs_fileserver_built():
1578 takes_options.extend(ntvfs_options)
1580 takes_args = ["smbconf"]
1582 def run(self, smbconf=None, targetdir=None, dbdir=None, testparm=None,
1583 quiet=False, verbose=False, use_xattrs="auto", sambaopts=None, versionopts=None,
1584 dns_backend=None, use_ntvfs=False):
1586 if not os.path.exists(smbconf):
1587 raise CommandError("File %s does not exist" % smbconf)
1589 if testparm and not os.path.exists(testparm):
1590 raise CommandError("Testparm utility %s does not exist" % testparm)
1592 if dbdir and not os.path.exists(dbdir):
1593 raise CommandError("Directory %s does not exist" % dbdir)
1595 if not dbdir and not testparm:
1596 raise CommandError("Please specify either dbdir or testparm")
1598 logger = self.get_logger()
1600 logger.setLevel(logging.DEBUG)
1602 logger.setLevel(logging.WARNING)
1604 logger.setLevel(logging.INFO)
1606 if dbdir and testparm:
1607 logger.warning("both dbdir and testparm specified, ignoring dbdir.")
1610 lp = sambaopts.get_loadparm()
1612 s3conf = s3param.get_context()
1615 s3conf.set("realm", sambaopts.realm)
1617 if targetdir is not None:
1618 if not os.path.isdir(targetdir):
1622 if use_xattrs == "yes":
1624 elif use_xattrs == "auto" and use_ntvfs == False:
1626 elif use_ntvfs == False:
1627 raise CommandError("--use-xattrs=no requires --use-ntvfs (not supported for production use). "
1628 "Please re-run with --use-xattrs omitted.")
1629 elif use_xattrs == "auto" and not s3conf.get("posix:eadb"):
1631 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
1633 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
1636 samba.ntacls.setntacl(lp, tmpfile.name,
1637 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
1640 # FIXME: Don't catch all exceptions here
1641 logger.info("You are not root or your system does not support xattr, using tdb backend for attributes. "
1642 "If you intend to use this provision in production, rerun the script as root on a system supporting xattrs.")
1646 # Set correct default values from dbdir or testparm
1649 paths["state directory"] = dbdir
1650 paths["private dir"] = dbdir
1651 paths["lock directory"] = dbdir
1652 paths["smb passwd file"] = dbdir + "/smbpasswd"
1654 paths["state directory"] = get_testparm_var(testparm, smbconf, "state directory")
1655 paths["private dir"] = get_testparm_var(testparm, smbconf, "private dir")
1656 paths["smb passwd file"] = get_testparm_var(testparm, smbconf, "smb passwd file")
1657 paths["lock directory"] = get_testparm_var(testparm, smbconf, "lock directory")
1658 # "testparm" from Samba 3 < 3.4.x is not aware of the parameter
1659 # "state directory", instead make use of "lock directory"
1660 if len(paths["state directory"]) == 0:
1661 paths["state directory"] = paths["lock directory"]
1664 s3conf.set(p, paths[p])
1666 # load smb.conf parameters
1667 logger.info("Reading smb.conf")
1668 s3conf.load(smbconf)
1669 samba3 = Samba3(smbconf, s3conf)
1671 logger.info("Provisioning")
1672 upgrade_from_samba3(samba3, logger, targetdir, session_info=system_session(),
1673 useeadb=eadb, dns_backend=dns_backend, use_ntvfs=use_ntvfs)
1676 class cmd_domain_samba3upgrade(cmd_domain_classicupgrade):
1677 __doc__ = cmd_domain_classicupgrade.__doc__
1679 # This command is present for backwards compatibility only,
1680 # and should not be shown.
1684 class LocalDCCredentialsOptions(options.CredentialsOptions):
1685 def __init__(self, parser):
1686 options.CredentialsOptions.__init__(self, parser, special_name="local-dc")
1688 class DomainTrustCommand(Command):
1689 """List domain trusts."""
1692 Command.__init__(self)
1693 self.local_lp = None
1695 self.local_server = None
1696 self.local_binding_string = None
1697 self.local_creds = None
1699 self.remote_server = None
1700 self.remote_binding_string = None
1701 self.remote_creds = None
1703 def _uint32(self, v):
1704 return ctypes.c_uint32(v).value
1706 def check_runtime_error(self, runtime, val):
1710 err32 = self._uint32(runtime[0])
1716 class LocalRuntimeError(CommandError):
1717 def __init__(exception_self, self, runtime, message):
1718 err32 = self._uint32(runtime[0])
1720 msg = "LOCAL_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1721 self.local_server, message, err32, errstr)
1722 CommandError.__init__(exception_self, msg)
1724 class RemoteRuntimeError(CommandError):
1725 def __init__(exception_self, self, runtime, message):
1726 err32 = self._uint32(runtime[0])
1728 msg = "REMOTE_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1729 self.remote_server, message, err32, errstr)
1730 CommandError.__init__(exception_self, msg)
1732 class LocalLdbError(CommandError):
1733 def __init__(exception_self, self, ldb_error, message):
1734 errval = ldb_error[0]
1735 errstr = ldb_error[1]
1736 msg = "LOCAL_DC[%s]: %s - ERROR(%d) - %s" % (
1737 self.local_server, message, errval, errstr)
1738 CommandError.__init__(exception_self, msg)
1740 def setup_local_server(self, sambaopts, localdcopts):
1741 if self.local_server is not None:
1742 return self.local_server
1744 lp = sambaopts.get_loadparm()
1746 local_server = localdcopts.ipaddress
1747 if local_server is None:
1748 server_role = lp.server_role()
1749 if server_role != "ROLE_ACTIVE_DIRECTORY_DC":
1750 raise CommandError("Invalid server_role %s" % (server_role))
1751 local_server = lp.get('netbios name')
1752 local_transport = "ncalrpc"
1753 local_binding_options = ""
1754 local_binding_options += ",auth_type=ncalrpc_as_system"
1755 local_ldap_url = None
1758 local_transport = "ncacn_np"
1759 local_binding_options = ""
1760 local_ldap_url = "ldap://%s" % local_server
1761 local_creds = localdcopts.get_credentials(lp)
1765 self.local_server = local_server
1766 self.local_binding_string = "%s:%s[%s]" % (local_transport, local_server, local_binding_options)
1767 self.local_ldap_url = local_ldap_url
1768 self.local_creds = local_creds
1769 return self.local_server
1771 def new_local_lsa_connection(self):
1772 return lsa.lsarpc(self.local_binding_string, self.local_lp, self.local_creds)
1774 def new_local_netlogon_connection(self):
1775 return netlogon.netlogon(self.local_binding_string, self.local_lp, self.local_creds)
1777 def new_local_ldap_connection(self):
1778 return SamDB(url=self.local_ldap_url,
1779 session_info=system_session(),
1780 credentials=self.local_creds,
1783 def setup_remote_server(self, credopts, domain,
1785 require_writable=True):
1788 assert require_writable
1790 if self.remote_server is not None:
1791 return self.remote_server
1793 self.remote_server = "__unknown__remote_server__.%s" % domain
1794 assert self.local_server is not None
1796 remote_creds = credopts.get_credentials(self.local_lp)
1797 remote_server = credopts.ipaddress
1798 remote_binding_options = ""
1800 # TODO: we should also support NT4 domains
1801 # we could use local_netlogon.netr_DsRGetDCNameEx2() with the remote domain name
1802 # and delegate NBT or CLDAP to the local netlogon server
1804 remote_net = Net(remote_creds, self.local_lp, server=remote_server)
1805 remote_flags = nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS
1806 if require_writable:
1807 remote_flags |= nbt.NBT_SERVER_WRITABLE
1809 remote_flags |= nbt.NBT_SERVER_PDC
1810 remote_info = remote_net.finddc(flags=remote_flags, domain=domain, address=remote_server)
1811 except NTSTATUSError as error:
1812 raise CommandError("Failed to find a writeable DC for domain '%s': %s" %
1815 raise CommandError("Failed to find a writeable DC for domain '%s'" % domain)
1817 nbt.NBT_SERVER_PDC: "PDC",
1818 nbt.NBT_SERVER_GC: "GC",
1819 nbt.NBT_SERVER_LDAP: "LDAP",
1820 nbt.NBT_SERVER_DS: "DS",
1821 nbt.NBT_SERVER_KDC: "KDC",
1822 nbt.NBT_SERVER_TIMESERV: "TIMESERV",
1823 nbt.NBT_SERVER_CLOSEST: "CLOSEST",
1824 nbt.NBT_SERVER_WRITABLE: "WRITABLE",
1825 nbt.NBT_SERVER_GOOD_TIMESERV: "GOOD_TIMESERV",
1826 nbt.NBT_SERVER_NDNC: "NDNC",
1827 nbt.NBT_SERVER_SELECT_SECRET_DOMAIN_6: "SELECT_SECRET_DOMAIN_6",
1828 nbt.NBT_SERVER_FULL_SECRET_DOMAIN_6: "FULL_SECRET_DOMAIN_6",
1829 nbt.NBT_SERVER_ADS_WEB_SERVICE: "ADS_WEB_SERVICE",
1830 nbt.NBT_SERVER_DS_8: "DS_8",
1831 nbt.NBT_SERVER_HAS_DNS_NAME: "HAS_DNS_NAME",
1832 nbt.NBT_SERVER_IS_DEFAULT_NC: "IS_DEFAULT_NC",
1833 nbt.NBT_SERVER_FOREST_ROOT: "FOREST_ROOT",
1835 server_type_string = self.generic_bitmap_to_string(flag_map,
1836 remote_info.server_type, names_only=True)
1837 self.outf.write("RemoteDC Netbios[%s] DNS[%s] ServerType[%s]\n" % (
1838 remote_info.pdc_name,
1839 remote_info.pdc_dns_name,
1840 server_type_string))
1842 self.remote_server = remote_info.pdc_dns_name
1843 self.remote_binding_string="ncacn_np:%s[%s]" % (self.remote_server, remote_binding_options)
1844 self.remote_creds = remote_creds
1845 return self.remote_server
1847 def new_remote_lsa_connection(self):
1848 return lsa.lsarpc(self.remote_binding_string, self.local_lp, self.remote_creds)
1850 def new_remote_netlogon_connection(self):
1851 return netlogon.netlogon(self.remote_binding_string, self.local_lp, self.remote_creds)
1853 def get_lsa_info(self, conn, policy_access):
1854 objectAttr = lsa.ObjectAttribute()
1855 objectAttr.sec_qos = lsa.QosInfo()
1857 policy = conn.OpenPolicy2(''.decode('utf-8'),
1858 objectAttr, policy_access)
1860 info = conn.QueryInfoPolicy2(policy, lsa.LSA_POLICY_INFO_DNS)
1862 return (policy, info)
1864 def get_netlogon_dc_info(self, conn, server):
1865 info = conn.netr_DsRGetDCNameEx2(server,
1866 None, 0, None, None, None,
1867 netlogon.DS_RETURN_DNS_NAME)
1870 def netr_DomainTrust_to_name(self, t):
1871 if t.trust_type == lsa.LSA_TRUST_TYPE_DOWNLEVEL:
1872 return t.netbios_name
1876 def netr_DomainTrust_to_type(self, a, t):
1878 primary_parent = None
1880 if _t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
1882 if not _t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1883 primary_parent = a[_t.parent_index]
1886 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1887 if t is primary_parent:
1890 if t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1893 parent = a[t.parent_index]
1894 if parent is primary:
1899 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1904 def netr_DomainTrust_to_transitive(self, t):
1905 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1908 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE:
1911 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1916 def netr_DomainTrust_to_direction(self, t):
1917 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND and \
1918 t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1921 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND:
1924 if t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1929 def generic_enum_to_string(self, e_dict, v, names_only=False):
1933 v32 = self._uint32(v)
1934 w = "__unknown__%08X__" % v32
1936 r = "0x%x (%s)" % (v, w)
1939 def generic_bitmap_to_string(self, b_dict, v, names_only=False):
1944 for b in sorted(b_dict.keys()):
1951 c32 = self._uint32(c)
1952 s += ["__unknown_%08X__" % c32]
1957 r = "0x%x (%s)" % (v, w)
1960 def trustType_string(self, v):
1962 lsa.LSA_TRUST_TYPE_DOWNLEVEL : "DOWNLEVEL",
1963 lsa.LSA_TRUST_TYPE_UPLEVEL : "UPLEVEL",
1964 lsa.LSA_TRUST_TYPE_MIT : "MIT",
1965 lsa.LSA_TRUST_TYPE_DCE : "DCE",
1967 return self.generic_enum_to_string(types, v)
1969 def trustDirection_string(self, v):
1971 lsa.LSA_TRUST_DIRECTION_INBOUND |
1972 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "BOTH",
1973 lsa.LSA_TRUST_DIRECTION_INBOUND : "INBOUND",
1974 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "OUTBOUND",
1976 return self.generic_enum_to_string(directions, v)
1978 def trustAttributes_string(self, v):
1980 lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE : "NON_TRANSITIVE",
1981 lsa.LSA_TRUST_ATTRIBUTE_UPLEVEL_ONLY : "UPLEVEL_ONLY",
1982 lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN : "QUARANTINED_DOMAIN",
1983 lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE : "FOREST_TRANSITIVE",
1984 lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION : "CROSS_ORGANIZATION",
1985 lsa.LSA_TRUST_ATTRIBUTE_WITHIN_FOREST : "WITHIN_FOREST",
1986 lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL : "TREAT_AS_EXTERNAL",
1987 lsa.LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION : "USES_RC4_ENCRYPTION",
1989 return self.generic_bitmap_to_string(attributes, v)
1991 def kerb_EncTypes_string(self, v):
1993 security.KERB_ENCTYPE_DES_CBC_CRC : "DES_CBC_CRC",
1994 security.KERB_ENCTYPE_DES_CBC_MD5 : "DES_CBC_MD5",
1995 security.KERB_ENCTYPE_RC4_HMAC_MD5 : "RC4_HMAC_MD5",
1996 security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96 : "AES128_CTS_HMAC_SHA1_96",
1997 security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96 : "AES256_CTS_HMAC_SHA1_96",
1998 security.KERB_ENCTYPE_FAST_SUPPORTED : "FAST_SUPPORTED",
1999 security.KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED : "COMPOUND_IDENTITY_SUPPORTED",
2000 security.KERB_ENCTYPE_CLAIMS_SUPPORTED : "CLAIMS_SUPPORTED",
2001 security.KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED : "RESOURCE_SID_COMPRESSION_DISABLED",
2003 return self.generic_bitmap_to_string(enctypes, v)
2005 def entry_tln_status(self, e_flags, ):
2007 return "Status[Enabled]"
2010 lsa.LSA_TLN_DISABLED_NEW : "Disabled-New",
2011 lsa.LSA_TLN_DISABLED_ADMIN : "Disabled",
2012 lsa.LSA_TLN_DISABLED_CONFLICT : "Disabled-Conflicting",
2014 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
2016 def entry_dom_status(self, e_flags):
2018 return "Status[Enabled]"
2021 lsa.LSA_SID_DISABLED_ADMIN : "Disabled-SID",
2022 lsa.LSA_SID_DISABLED_CONFLICT : "Disabled-SID-Conflicting",
2023 lsa.LSA_NB_DISABLED_ADMIN : "Disabled-NB",
2024 lsa.LSA_NB_DISABLED_CONFLICT : "Disabled-NB-Conflicting",
2026 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
2028 def write_forest_trust_info(self, fti, tln=None, collisions=None):
2030 tln_string = " TDO[%s]" % tln
2034 self.outf.write("Namespaces[%d]%s:\n" % (
2035 len(fti.entries), tln_string))
2037 for i in xrange(0, len(fti.entries)):
2041 collision_string = ""
2043 if collisions is not None:
2044 for c in collisions.entries:
2048 collision_string = " Collision[%s]" % (c.name.string)
2050 d = e.forest_trust_data
2051 if e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
2052 self.outf.write("TLN: %-32s DNS[*.%s]%s\n" % (
2053 self.entry_tln_status(flags),
2054 d.string, collision_string))
2055 elif e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
2056 self.outf.write("TLN_EX: %-29s DNS[*.%s]\n" % (
2058 elif e.type == lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
2059 self.outf.write("DOM: %-32s DNS[%s] Netbios[%s] SID[%s]%s\n" % (
2060 self.entry_dom_status(flags),
2061 d.dns_domain_name.string,
2062 d.netbios_domain_name.string,
2063 d.domain_sid, collision_string))
2066 class cmd_domain_trust_list(DomainTrustCommand):
2067 """List domain trusts."""
2069 synopsis = "%prog [options]"
2071 takes_optiongroups = {
2072 "sambaopts": options.SambaOptions,
2073 "versionopts": options.VersionOptions,
2074 "localdcopts": LocalDCCredentialsOptions,
2080 def run(self, sambaopts=None, versionopts=None, localdcopts=None):
2082 local_server = self.setup_local_server(sambaopts, localdcopts)
2084 local_netlogon = self.new_local_netlogon_connection()
2085 except RuntimeError as error:
2086 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2089 local_netlogon_trusts = local_netlogon.netr_DsrEnumerateDomainTrusts(local_server,
2090 netlogon.NETR_TRUST_FLAG_IN_FOREST |
2091 netlogon.NETR_TRUST_FLAG_OUTBOUND |
2092 netlogon.NETR_TRUST_FLAG_INBOUND)
2093 except RuntimeError as error:
2094 if self.check_runtime_error(error, werror.WERR_RPC_S_PROCNUM_OUT_OF_RANGE):
2095 # TODO: we could implement a fallback to lsa.EnumTrustDom()
2096 raise CommandError("LOCAL_DC[%s]: netr_DsrEnumerateDomainTrusts not supported." % (
2098 raise self.LocalRuntimeError(self, error, "netr_DsrEnumerateDomainTrusts failed")
2100 a = local_netlogon_trusts.array
2102 if t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
2104 self.outf.write("%-14s %-15s %-19s %s\n" % (
2105 "Type[%s]" % self.netr_DomainTrust_to_type(a, t),
2106 "Transitive[%s]" % self.netr_DomainTrust_to_transitive(t),
2107 "Direction[%s]" % self.netr_DomainTrust_to_direction(t),
2108 "Name[%s]" % self.netr_DomainTrust_to_name(t)))
2111 class cmd_domain_trust_show(DomainTrustCommand):
2112 """Show trusted domain details."""
2114 synopsis = "%prog NAME [options]"
2116 takes_optiongroups = {
2117 "sambaopts": options.SambaOptions,
2118 "versionopts": options.VersionOptions,
2119 "localdcopts": LocalDCCredentialsOptions,
2125 takes_args = ["domain"]
2127 def run(self, domain, sambaopts=None, versionopts=None, localdcopts=None):
2129 local_server = self.setup_local_server(sambaopts, localdcopts)
2131 local_lsa = self.new_local_lsa_connection()
2132 except RuntimeError as error:
2133 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2136 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2137 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2138 except RuntimeError as error:
2139 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2141 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2142 local_lsa_info.name.string,
2143 local_lsa_info.dns_domain.string,
2144 local_lsa_info.sid))
2146 lsaString = lsa.String()
2147 lsaString.string = domain
2149 local_tdo_full = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2150 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2151 local_tdo_info = local_tdo_full.info_ex
2152 local_tdo_posix = local_tdo_full.posix_offset
2153 except NTSTATUSError as error:
2154 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2155 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2157 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(FULL_INFO) failed")
2160 local_tdo_enctypes = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2161 lsaString, lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES)
2162 except NTSTATUSError as error:
2163 if self.check_runtime_error(error, ntstatus.NT_STATUS_INVALID_PARAMETER):
2165 if self.check_runtime_error(error, ntstatus.NT_STATUS_INVALID_INFO_CLASS):
2168 if error is not None:
2169 raise self.LocalRuntimeError(self, error,
2170 "QueryTrustedDomainInfoByName(SUPPORTED_ENCRYPTION_TYPES) failed")
2172 local_tdo_enctypes = lsa.TrustDomainInfoSupportedEncTypes()
2173 local_tdo_enctypes.enc_types = 0
2176 local_tdo_forest = None
2177 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2178 local_tdo_forest = local_lsa.lsaRQueryForestTrustInformation(local_policy,
2179 lsaString, lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
2180 except RuntimeError as error:
2181 if self.check_runtime_error(error, ntstatus.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
2183 if self.check_runtime_error(error, ntstatus.NT_STATUS_NOT_FOUND):
2185 if error is not None:
2186 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation failed")
2188 local_tdo_forest = lsa.ForestTrustInformation()
2189 local_tdo_forest.count = 0
2190 local_tdo_forest.entries = []
2192 self.outf.write("TrustedDomain:\n\n");
2193 self.outf.write("NetbiosName: %s\n" % local_tdo_info.netbios_name.string)
2194 if local_tdo_info.netbios_name.string != local_tdo_info.domain_name.string:
2195 self.outf.write("DnsName: %s\n" % local_tdo_info.domain_name.string)
2196 self.outf.write("SID: %s\n" % local_tdo_info.sid)
2197 self.outf.write("Type: %s\n" % self.trustType_string(local_tdo_info.trust_type))
2198 self.outf.write("Direction: %s\n" % self.trustDirection_string(local_tdo_info.trust_direction))
2199 self.outf.write("Attributes: %s\n" % self.trustAttributes_string(local_tdo_info.trust_attributes))
2200 posix_offset_u32 = ctypes.c_uint32(local_tdo_posix.posix_offset).value
2201 posix_offset_i32 = ctypes.c_int32(local_tdo_posix.posix_offset).value
2202 self.outf.write("PosixOffset: 0x%08X (%d)\n" % (posix_offset_u32, posix_offset_i32))
2203 self.outf.write("kerb_EncTypes: %s\n" % self.kerb_EncTypes_string(local_tdo_enctypes.enc_types))
2205 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2206 self.write_forest_trust_info(local_tdo_forest,
2207 tln=local_tdo_info.domain_name.string)
2211 class cmd_domain_trust_create(DomainTrustCommand):
2212 """Create a domain or forest trust."""
2214 synopsis = "%prog DOMAIN [options]"
2216 takes_optiongroups = {
2217 "sambaopts": options.SambaOptions,
2218 "versionopts": options.VersionOptions,
2219 "credopts": options.CredentialsOptions,
2220 "localdcopts": LocalDCCredentialsOptions,
2224 Option("--type", type="choice", metavar="TYPE",
2225 choices=["external", "forest"],
2226 help="The type of the trust: 'external' or 'forest'.",
2228 default="external"),
2229 Option("--direction", type="choice", metavar="DIRECTION",
2230 choices=["incoming", "outgoing", "both"],
2231 help="The trust direction: 'incoming', 'outgoing' or 'both'.",
2232 dest='trust_direction',
2234 Option("--create-location", type="choice", metavar="LOCATION",
2235 choices=["local", "both"],
2236 help="Where to create the trusted domain object: 'local' or 'both'.",
2237 dest='create_location',
2239 Option("--cross-organisation", action="store_true",
2240 help="The related domains does not belong to the same organisation.",
2241 dest='cross_organisation',
2243 Option("--quarantined", type="choice", metavar="yes|no",
2244 choices=["yes", "no", None],
2245 help="Special SID filtering rules are applied to the trust. "
2246 "With --type=external the default is yes. "
2247 "With --type=forest the default is no.",
2248 dest='quarantined_arg',
2250 Option("--not-transitive", action="store_true",
2251 help="The forest trust is not transitive.",
2252 dest='not_transitive',
2254 Option("--treat-as-external", action="store_true",
2255 help="The treat the forest trust as external.",
2256 dest='treat_as_external',
2258 Option("--no-aes-keys", action="store_false",
2259 help="The trust uses aes kerberos keys.",
2260 dest='use_aes_keys',
2262 Option("--skip-validation", action="store_false",
2263 help="Skip validation of the trust.",
2268 takes_args = ["domain"]
2270 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2271 trust_type=None, trust_direction=None, create_location=None,
2272 cross_organisation=False, quarantined_arg=None,
2273 not_transitive=False, treat_as_external=False,
2274 use_aes_keys=False, validate=True):
2276 lsaString = lsa.String()
2279 if quarantined_arg is None:
2280 if trust_type == 'external':
2282 elif quarantined_arg == 'yes':
2285 if trust_type != 'forest':
2287 raise CommandError("--not-transitive requires --type=forest")
2288 if treat_as_external:
2289 raise CommandError("--treat-as-external requires --type=forest")
2293 enc_types = lsa.TrustDomainInfoSupportedEncTypes()
2294 enc_types.enc_types = security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96
2295 enc_types.enc_types |= security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96
2297 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2298 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2299 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2301 local_trust_info = lsa.TrustDomainInfoInfoEx()
2302 local_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2303 local_trust_info.trust_direction = 0
2304 if trust_direction == "both":
2305 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2306 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2307 elif trust_direction == "incoming":
2308 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2309 elif trust_direction == "outgoing":
2310 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2311 local_trust_info.trust_attributes = 0
2312 if cross_organisation:
2313 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2315 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2316 if trust_type == "forest":
2317 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2319 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2320 if treat_as_external:
2321 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2323 def get_password(name):
2326 if password is not None and password is not '':
2328 password = getpass("New %s Password: " % name)
2329 passwordverify = getpass("Retype %s Password: " % name)
2330 if not password == passwordverify:
2332 self.outf.write("Sorry, passwords do not match.\n")
2334 incoming_secret = None
2335 outgoing_secret = None
2336 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2337 if create_location == "local":
2338 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2339 incoming_password = get_password("Incoming Trust")
2340 incoming_secret = string_to_byte_array(incoming_password.encode('utf-16-le'))
2341 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2342 outgoing_password = get_password("Outgoing Trust")
2343 outgoing_secret = string_to_byte_array(outgoing_password.encode('utf-16-le'))
2345 remote_trust_info = None
2347 # We use 240 random bytes.
2348 # Windows uses 28 or 240 random bytes. I guess it's
2349 # based on the trust type external vs. forest.
2351 # The initial trust password can be up to 512 bytes
2352 # while the versioned passwords used for periodic updates
2353 # can only be up to 498 bytes, as netr_ServerPasswordSet2()
2354 # needs to pass the NL_PASSWORD_VERSION structure within the
2355 # 512 bytes and a 2 bytes confounder is required.
2357 def random_trust_secret(length):
2358 pw = samba.generate_random_machine_password(length//2, length//2)
2359 return string_to_byte_array(pw.encode('utf-16-le'))
2361 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2362 incoming_secret = random_trust_secret(240)
2363 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2364 outgoing_secret = random_trust_secret(240)
2366 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2367 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2369 remote_trust_info = lsa.TrustDomainInfoInfoEx()
2370 remote_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2371 remote_trust_info.trust_direction = 0
2372 if trust_direction == "both":
2373 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2374 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2375 elif trust_direction == "incoming":
2376 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2377 elif trust_direction == "outgoing":
2378 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2379 remote_trust_info.trust_attributes = 0
2380 if cross_organisation:
2381 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2383 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2384 if trust_type == "forest":
2385 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2387 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2388 if treat_as_external:
2389 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2391 local_server = self.setup_local_server(sambaopts, localdcopts)
2393 local_lsa = self.new_local_lsa_connection()
2394 except RuntimeError as error:
2395 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2398 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2399 except RuntimeError as error:
2400 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2402 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2403 local_lsa_info.name.string,
2404 local_lsa_info.dns_domain.string,
2405 local_lsa_info.sid))
2408 remote_server = self.setup_remote_server(credopts, domain)
2409 except RuntimeError as error:
2410 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2413 remote_lsa = self.new_remote_lsa_connection()
2414 except RuntimeError as error:
2415 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2418 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2419 except RuntimeError as error:
2420 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2422 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2423 remote_lsa_info.name.string,
2424 remote_lsa_info.dns_domain.string,
2425 remote_lsa_info.sid))
2427 local_trust_info.domain_name.string = remote_lsa_info.dns_domain.string
2428 local_trust_info.netbios_name.string = remote_lsa_info.name.string
2429 local_trust_info.sid = remote_lsa_info.sid
2431 if remote_trust_info:
2432 remote_trust_info.domain_name.string = local_lsa_info.dns_domain.string
2433 remote_trust_info.netbios_name.string = local_lsa_info.name.string
2434 remote_trust_info.sid = local_lsa_info.sid
2437 lsaString.string = local_trust_info.domain_name.string
2438 local_old_netbios = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2439 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2440 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2441 except NTSTATUSError as error:
2442 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2443 raise self.LocalRuntimeError(self, error,
2444 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2448 lsaString.string = local_trust_info.netbios_name.string
2449 local_old_dns = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2450 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2451 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2452 except NTSTATUSError as error:
2453 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2454 raise self.LocalRuntimeError(self, error,
2455 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2458 if remote_trust_info:
2460 lsaString.string = remote_trust_info.domain_name.string
2461 remote_old_netbios = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2462 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2463 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2464 except NTSTATUSError as error:
2465 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2466 raise self.RemoteRuntimeError(self, error,
2467 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2471 lsaString.string = remote_trust_info.netbios_name.string
2472 remote_old_dns = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2473 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2474 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2475 except NTSTATUSError as error:
2476 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2477 raise self.RemoteRuntimeError(self, error,
2478 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2482 local_netlogon = self.new_local_netlogon_connection()
2483 except RuntimeError as error:
2484 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2487 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
2488 except RuntimeError as error:
2489 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
2491 if remote_trust_info:
2493 remote_netlogon = self.new_remote_netlogon_connection()
2494 except RuntimeError as error:
2495 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2498 remote_netlogon_info = self.get_netlogon_dc_info(remote_netlogon, remote_server)
2499 except RuntimeError as error:
2500 raise self.RemoteRuntimeError(self, error, "failed to get netlogon dc info")
2502 def generate_AuthInOutBlob(secret, update_time):
2504 blob = drsblobs.trustAuthInOutBlob()
2509 clear = drsblobs.AuthInfoClear()
2510 clear.size = len(secret)
2511 clear.password = secret
2513 info = drsblobs.AuthenticationInformation()
2514 info.LastUpdateTime = samba.unix2nttime(update_time)
2515 info.AuthType = lsa.TRUST_AUTH_TYPE_CLEAR
2516 info.AuthInfo = clear
2518 array = drsblobs.AuthenticationInformationArray()
2520 array.array = [info]
2522 blob = drsblobs.trustAuthInOutBlob()
2524 blob.current = array
2528 def generate_AuthInfoInternal(session_key, incoming=None, outgoing=None):
2529 confounder = [0] * 512
2530 for i in range(len(confounder)):
2531 confounder[i] = random.randint(0, 255)
2533 trustpass = drsblobs.trustDomainPasswords()
2535 trustpass.confounder = confounder
2536 trustpass.outgoing = outgoing
2537 trustpass.incoming = incoming
2539 trustpass_blob = ndr_pack(trustpass)
2541 encrypted_trustpass = arcfour_encrypt(session_key, trustpass_blob)
2543 auth_blob = lsa.DATA_BUF2()
2544 auth_blob.size = len(encrypted_trustpass)
2545 auth_blob.data = string_to_byte_array(encrypted_trustpass)
2547 auth_info = lsa.TrustDomainInfoAuthInfoInternal()
2548 auth_info.auth_blob = auth_blob
2552 update_time = samba.current_unix_time()
2553 incoming_blob = generate_AuthInOutBlob(incoming_secret, update_time)
2554 outgoing_blob = generate_AuthInOutBlob(outgoing_secret, update_time)
2556 local_tdo_handle = None
2557 remote_tdo_handle = None
2559 local_auth_info = generate_AuthInfoInternal(local_lsa.session_key,
2560 incoming=incoming_blob,
2561 outgoing=outgoing_blob)
2562 if remote_trust_info:
2563 remote_auth_info = generate_AuthInfoInternal(remote_lsa.session_key,
2564 incoming=outgoing_blob,
2565 outgoing=incoming_blob)
2568 if remote_trust_info:
2569 self.outf.write("Creating remote TDO.\n")
2570 current_request = { "location": "remote", "name": "CreateTrustedDomainEx2"}
2571 remote_tdo_handle = remote_lsa.CreateTrustedDomainEx2(remote_policy,
2574 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2575 self.outf.write("Remote TDO created.\n")
2577 self.outf.write("Setting supported encryption types on remote TDO.\n")
2578 current_request = { "location": "remote", "name": "SetInformationTrustedDomain"}
2579 remote_lsa.SetInformationTrustedDomain(remote_tdo_handle,
2580 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2583 self.outf.write("Creating local TDO.\n")
2584 current_request = { "location": "local", "name": "CreateTrustedDomainEx2"}
2585 local_tdo_handle = local_lsa.CreateTrustedDomainEx2(local_policy,
2588 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2589 self.outf.write("Local TDO created\n")
2591 self.outf.write("Setting supported encryption types on local TDO.\n")
2592 current_request = { "location": "local", "name": "SetInformationTrustedDomain"}
2593 local_lsa.SetInformationTrustedDomain(local_tdo_handle,
2594 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2596 except RuntimeError as error:
2597 self.outf.write("Error: %s failed %sly - cleaning up\n" % (
2598 current_request['name'], current_request['location']))
2599 if remote_tdo_handle:
2600 self.outf.write("Deleting remote TDO.\n")
2601 remote_lsa.DeleteObject(remote_tdo_handle)
2602 remote_tdo_handle = None
2603 if local_tdo_handle:
2604 self.outf.write("Deleting local TDO.\n")
2605 local_lsa.DeleteObject(local_tdo_handle)
2606 local_tdo_handle = None
2607 if current_request['location'] is "remote":
2608 raise self.RemoteRuntimeError(self, error, "%s" % (
2609 current_request['name']))
2610 raise self.LocalRuntimeError(self, error, "%s" % (
2611 current_request['name']))
2614 if local_trust_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2615 self.outf.write("Setup local forest trust information...\n")
2617 # get all information about the remote trust
2618 # this triggers netr_GetForestTrustInformation to the remote domain
2619 # and lsaRSetForestTrustInformation() locally, but new top level
2620 # names are disabled by default.
2621 local_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
2622 remote_lsa_info.dns_domain.string,
2623 netlogon.DS_GFTI_UPDATE_TDO)
2624 except RuntimeError as error:
2625 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2628 # here we try to enable all top level names
2629 local_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
2630 remote_lsa_info.dns_domain,
2631 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2634 except RuntimeError as error:
2635 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2637 self.write_forest_trust_info(local_forest_info,
2638 tln=remote_lsa_info.dns_domain.string,
2639 collisions=local_forest_collision)
2641 if remote_trust_info:
2642 self.outf.write("Setup remote forest trust information...\n")
2644 # get all information about the local trust (from the perspective of the remote domain)
2645 # this triggers netr_GetForestTrustInformation to our domain.
2646 # and lsaRSetForestTrustInformation() remotely, but new top level
2647 # names are disabled by default.
2648 remote_forest_info = remote_netlogon.netr_DsRGetForestTrustInformation(remote_netlogon_info.dc_unc,
2649 local_lsa_info.dns_domain.string,
2650 netlogon.DS_GFTI_UPDATE_TDO)
2651 except RuntimeError as error:
2652 raise self.RemoteRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2655 # here we try to enable all top level names
2656 remote_forest_collision = remote_lsa.lsaRSetForestTrustInformation(remote_policy,
2657 local_lsa_info.dns_domain,
2658 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2661 except RuntimeError as error:
2662 raise self.RemoteRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2664 self.write_forest_trust_info(remote_forest_info,
2665 tln=local_lsa_info.dns_domain.string,
2666 collisions=remote_forest_collision)
2668 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2669 self.outf.write("Validating outgoing trust...\n")
2671 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_netlogon_info.dc_unc,
2672 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2674 remote_lsa_info.dns_domain.string)
2675 except RuntimeError as error:
2676 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2678 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2679 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2681 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2682 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2683 local_trust_verify.trusted_dc_name,
2684 local_trust_verify.tc_connection_status[1],
2685 local_trust_verify.pdc_connection_status[1])
2687 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2688 local_trust_verify.trusted_dc_name,
2689 local_trust_verify.tc_connection_status[1],
2690 local_trust_verify.pdc_connection_status[1])
2692 if local_trust_status != werror.WERR_SUCCESS or local_conn_status != werror.WERR_SUCCESS:
2693 raise CommandError(local_validation)
2695 self.outf.write("OK: %s\n" % local_validation)
2697 if remote_trust_info:
2698 if remote_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2699 self.outf.write("Validating incoming trust...\n")
2701 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_netlogon_info.dc_unc,
2702 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2704 local_lsa_info.dns_domain.string)
2705 except RuntimeError as error:
2706 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2708 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2709 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2711 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2712 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2713 remote_trust_verify.trusted_dc_name,
2714 remote_trust_verify.tc_connection_status[1],
2715 remote_trust_verify.pdc_connection_status[1])
2717 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2718 remote_trust_verify.trusted_dc_name,
2719 remote_trust_verify.tc_connection_status[1],
2720 remote_trust_verify.pdc_connection_status[1])
2722 if remote_trust_status != werror.WERR_SUCCESS or remote_conn_status != werror.WERR_SUCCESS:
2723 raise CommandError(remote_validation)
2725 self.outf.write("OK: %s\n" % remote_validation)
2727 if remote_tdo_handle is not None:
2729 remote_lsa.Close(remote_tdo_handle)
2730 except RuntimeError as error:
2732 remote_tdo_handle = None
2733 if local_tdo_handle is not None:
2735 local_lsa.Close(local_tdo_handle)
2736 except RuntimeError as error:
2738 local_tdo_handle = None
2740 self.outf.write("Success.\n")
2743 class cmd_domain_trust_delete(DomainTrustCommand):
2744 """Delete a domain trust."""
2746 synopsis = "%prog DOMAIN [options]"
2748 takes_optiongroups = {
2749 "sambaopts": options.SambaOptions,
2750 "versionopts": options.VersionOptions,
2751 "credopts": options.CredentialsOptions,
2752 "localdcopts": LocalDCCredentialsOptions,
2756 Option("--delete-location", type="choice", metavar="LOCATION",
2757 choices=["local", "both"],
2758 help="Where to delete the trusted domain object: 'local' or 'both'.",
2759 dest='delete_location',
2763 takes_args = ["domain"]
2765 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2766 delete_location=None):
2768 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2769 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2770 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2772 if delete_location == "local":
2773 remote_policy_access = None
2775 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2776 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2777 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2779 local_server = self.setup_local_server(sambaopts, localdcopts)
2781 local_lsa = self.new_local_lsa_connection()
2782 except RuntimeError as error:
2783 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2786 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2787 except RuntimeError as error:
2788 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2790 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2791 local_lsa_info.name.string,
2792 local_lsa_info.dns_domain.string,
2793 local_lsa_info.sid))
2795 local_tdo_info = None
2796 local_tdo_handle = None
2797 remote_tdo_info = None
2798 remote_tdo_handle = None
2800 lsaString = lsa.String()
2802 lsaString.string = domain
2803 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2804 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2805 except NTSTATUSError as error:
2806 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2807 raise CommandError("Failed to find trust for domain '%s'" % domain)
2808 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2811 if remote_policy_access is not None:
2813 remote_server = self.setup_remote_server(credopts, domain)
2814 except RuntimeError as error:
2815 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2818 remote_lsa = self.new_remote_lsa_connection()
2819 except RuntimeError as error:
2820 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2823 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2824 except RuntimeError as error:
2825 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2827 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2828 remote_lsa_info.name.string,
2829 remote_lsa_info.dns_domain.string,
2830 remote_lsa_info.sid))
2832 if remote_lsa_info.sid != local_tdo_info.sid or \
2833 remote_lsa_info.name.string != local_tdo_info.netbios_name.string or \
2834 remote_lsa_info.dns_domain.string != local_tdo_info.domain_name.string:
2835 raise CommandError("LocalTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2836 local_tdo_info.netbios_name.string,
2837 local_tdo_info.domain_name.string,
2838 local_tdo_info.sid))
2841 lsaString.string = local_lsa_info.dns_domain.string
2842 remote_tdo_info = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2843 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2844 except NTSTATUSError as error:
2845 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2846 raise self.RemoteRuntimeError(self, error, "QueryTrustedDomainInfoByName(%s)" % (
2850 if remote_tdo_info is not None:
2851 if local_lsa_info.sid != remote_tdo_info.sid or \
2852 local_lsa_info.name.string != remote_tdo_info.netbios_name.string or \
2853 local_lsa_info.dns_domain.string != remote_tdo_info.domain_name.string:
2854 raise CommandError("RemoteTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2855 remote_tdo_info.netbios_name.string,
2856 remote_tdo_info.domain_name.string,
2857 remote_tdo_info.sid))
2859 if local_tdo_info is not None:
2861 lsaString.string = local_tdo_info.domain_name.string
2862 local_tdo_handle = local_lsa.OpenTrustedDomainByName(local_policy,
2864 security.SEC_STD_DELETE)
2865 except RuntimeError as error:
2866 raise self.LocalRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2869 local_lsa.DeleteObject(local_tdo_handle)
2870 local_tdo_handle = None
2872 if remote_tdo_info is not None:
2874 lsaString.string = remote_tdo_info.domain_name.string
2875 remote_tdo_handle = remote_lsa.OpenTrustedDomainByName(remote_policy,
2877 security.SEC_STD_DELETE)
2878 except RuntimeError as error:
2879 raise self.RemoteRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2882 if remote_tdo_handle is not None:
2884 remote_lsa.DeleteObject(remote_tdo_handle)
2885 remote_tdo_handle = None
2886 self.outf.write("RemoteTDO deleted.\n")
2887 except RuntimeError as error:
2888 self.outf.write("%s\n" % self.RemoteRuntimeError(self, error, "DeleteObject() failed"))
2890 if local_tdo_handle is not None:
2892 local_lsa.DeleteObject(local_tdo_handle)
2893 local_tdo_handle = None
2894 self.outf.write("LocalTDO deleted.\n")
2895 except RuntimeError as error:
2896 self.outf.write("%s\n" % self.LocalRuntimeError(self, error, "DeleteObject() failed"))
2900 class cmd_domain_trust_validate(DomainTrustCommand):
2901 """Validate a domain trust."""
2903 synopsis = "%prog DOMAIN [options]"
2905 takes_optiongroups = {
2906 "sambaopts": options.SambaOptions,
2907 "versionopts": options.VersionOptions,
2908 "credopts": options.CredentialsOptions,
2909 "localdcopts": LocalDCCredentialsOptions,
2913 Option("--validate-location", type="choice", metavar="LOCATION",
2914 choices=["local", "both"],
2915 help="Where to validate the trusted domain object: 'local' or 'both'.",
2916 dest='validate_location',
2920 takes_args = ["domain"]
2922 def run(self, domain, sambaopts=None, versionopts=None, credopts=None, localdcopts=None,
2923 validate_location=None):
2925 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2927 local_server = self.setup_local_server(sambaopts, localdcopts)
2929 local_lsa = self.new_local_lsa_connection()
2930 except RuntimeError as error:
2931 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2934 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2935 except RuntimeError as error:
2936 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2938 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2939 local_lsa_info.name.string,
2940 local_lsa_info.dns_domain.string,
2941 local_lsa_info.sid))
2944 lsaString = lsa.String()
2945 lsaString.string = domain
2946 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2947 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2948 except NTSTATUSError as error:
2949 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2950 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2952 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
2954 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
2955 local_tdo_info.netbios_name.string,
2956 local_tdo_info.domain_name.string,
2957 local_tdo_info.sid))
2960 local_netlogon = self.new_local_netlogon_connection()
2961 except RuntimeError as error:
2962 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2965 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_server,
2966 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2968 local_tdo_info.domain_name.string)
2969 except RuntimeError as error:
2970 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2972 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2973 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2975 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2976 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2977 local_trust_verify.trusted_dc_name,
2978 local_trust_verify.tc_connection_status[1],
2979 local_trust_verify.pdc_connection_status[1])
2981 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2982 local_trust_verify.trusted_dc_name,
2983 local_trust_verify.tc_connection_status[1],
2984 local_trust_verify.pdc_connection_status[1])
2986 if local_trust_status != werror.WERR_SUCCESS or local_conn_status != werror.WERR_SUCCESS:
2987 raise CommandError(local_validation)
2989 self.outf.write("OK: %s\n" % local_validation)
2992 server = local_trust_verify.trusted_dc_name.replace('\\', '')
2993 domain_and_server = "%s\\%s" % (local_tdo_info.domain_name.string, server)
2994 local_trust_rediscover = local_netlogon.netr_LogonControl2Ex(local_server,
2995 netlogon.NETLOGON_CONTROL_REDISCOVER,
2998 except RuntimeError as error:
2999 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
3001 local_conn_status = self._uint32(local_trust_rediscover.tc_connection_status[0])
3002 local_rediscover = "LocalRediscover: DC[%s] CONNECTION[%s]" % (
3003 local_trust_rediscover.trusted_dc_name,
3004 local_trust_rediscover.tc_connection_status[1])
3006 if local_conn_status != werror.WERR_SUCCESS:
3007 raise CommandError(local_rediscover)
3009 self.outf.write("OK: %s\n" % local_rediscover)
3011 if validate_location != "local":
3013 remote_server = self.setup_remote_server(credopts, domain, require_pdc=False)
3014 except RuntimeError as error:
3015 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
3018 remote_netlogon = self.new_remote_netlogon_connection()
3019 except RuntimeError as error:
3020 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
3023 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_server,
3024 netlogon.NETLOGON_CONTROL_TC_VERIFY,
3026 local_lsa_info.dns_domain.string)
3027 except RuntimeError as error:
3028 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
3030 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
3031 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
3033 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
3034 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
3035 remote_trust_verify.trusted_dc_name,
3036 remote_trust_verify.tc_connection_status[1],
3037 remote_trust_verify.pdc_connection_status[1])
3039 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
3040 remote_trust_verify.trusted_dc_name,
3041 remote_trust_verify.tc_connection_status[1],
3042 remote_trust_verify.pdc_connection_status[1])
3044 if remote_trust_status != werror.WERR_SUCCESS or remote_conn_status != werror.WERR_SUCCESS:
3045 raise CommandError(remote_validation)
3047 self.outf.write("OK: %s\n" % remote_validation)
3050 server = remote_trust_verify.trusted_dc_name.replace('\\', '')
3051 domain_and_server = "%s\\%s" % (local_lsa_info.dns_domain.string, server)
3052 remote_trust_rediscover = remote_netlogon.netr_LogonControl2Ex(remote_server,
3053 netlogon.NETLOGON_CONTROL_REDISCOVER,
3056 except RuntimeError as error:
3057 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
3059 remote_conn_status = self._uint32(remote_trust_rediscover.tc_connection_status[0])
3061 remote_rediscover = "RemoteRediscover: DC[%s] CONNECTION[%s]" % (
3062 remote_trust_rediscover.trusted_dc_name,
3063 remote_trust_rediscover.tc_connection_status[1])
3065 if remote_conn_status != werror.WERR_SUCCESS:
3066 raise CommandError(remote_rediscover)
3068 self.outf.write("OK: %s\n" % remote_rediscover)
3072 class cmd_domain_trust_namespaces(DomainTrustCommand):
3073 """Manage forest trust namespaces."""
3075 synopsis = "%prog [DOMAIN] [options]"
3077 takes_optiongroups = {
3078 "sambaopts": options.SambaOptions,
3079 "versionopts": options.VersionOptions,
3080 "localdcopts": LocalDCCredentialsOptions,
3084 Option("--refresh", type="choice", metavar="check|store",
3085 choices=["check", "store", None],
3086 help="List and maybe store refreshed forest trust information: 'check' or 'store'.",
3089 Option("--enable-all", action="store_true",
3090 help="Try to update disabled entries, not allowed with --refresh=check.",
3093 Option("--enable-tln", action="append", metavar='DNSDOMAIN',
3094 help="Enable a top level name entry. Can be specified multiple times.",
3097 Option("--disable-tln", action="append", metavar='DNSDOMAIN',
3098 help="Disable a top level name entry. Can be specified multiple times.",
3101 Option("--add-tln-ex", action="append", metavar='DNSDOMAIN',
3102 help="Add a top level exclusion entry. Can be specified multiple times.",
3105 Option("--delete-tln-ex", action="append", metavar='DNSDOMAIN',
3106 help="Delete a top level exclusion entry. Can be specified multiple times.",
3107 dest='delete_tln_ex',
3109 Option("--enable-nb", action="append", metavar='NETBIOSDOMAIN',
3110 help="Enable a netbios name in a domain entry. Can be specified multiple times.",
3113 Option("--disable-nb", action="append", metavar='NETBIOSDOMAIN',
3114 help="Disable a netbios name in a domain entry. Can be specified multiple times.",
3117 Option("--enable-sid", action="append", metavar='DOMAINSID',
3118 help="Enable a SID in a domain entry. Can be specified multiple times.",
3119 dest='enable_sid_str',
3121 Option("--disable-sid", action="append", metavar='DOMAINSID',
3122 help="Disable a SID in a domain entry. Can be specified multiple times.",
3123 dest='disable_sid_str',
3125 Option("--add-upn-suffix", action="append", metavar='DNSDOMAIN',
3126 help="Add a new uPNSuffixes attribute for the local forest. Can be specified multiple times.",
3129 Option("--delete-upn-suffix", action="append", metavar='DNSDOMAIN',
3130 help="Delete an existing uPNSuffixes attribute of the local forest. Can be specified multiple times.",
3133 Option("--add-spn-suffix", action="append", metavar='DNSDOMAIN',
3134 help="Add a new msDS-SPNSuffixes attribute for the local forest. Can be specified multiple times.",
3137 Option("--delete-spn-suffix", action="append", metavar='DNSDOMAIN',
3138 help="Delete an existing msDS-SPNSuffixes attribute of the local forest. Can be specified multiple times.",
3143 takes_args = ["domain?"]
3145 def run(self, domain=None, sambaopts=None, localdcopts=None, versionopts=None,
3146 refresh=None, enable_all=False,
3147 enable_tln=[], disable_tln=[], add_tln_ex=[], delete_tln_ex=[],
3148 enable_sid_str=[], disable_sid_str=[], enable_nb=[], disable_nb=[],
3149 add_upn=[], delete_upn=[], add_spn=[], delete_spn=[]):
3151 require_update = False
3154 if refresh == "store":
3155 raise CommandError("--refresh=%s not allowed without DOMAIN" % refresh)
3158 raise CommandError("--enable-all not allowed without DOMAIN")
3160 if len(enable_tln) > 0:
3161 raise CommandError("--enable-tln not allowed without DOMAIN")
3162 if len(disable_tln) > 0:
3163 raise CommandError("--disable-tln not allowed without DOMAIN")
3165 if len(add_tln_ex) > 0:
3166 raise CommandError("--add-tln-ex not allowed without DOMAIN")
3167 if len(delete_tln_ex) > 0:
3168 raise CommandError("--delete-tln-ex not allowed without DOMAIN")
3170 if len(enable_nb) > 0:
3171 raise CommandError("--enable-nb not allowed without DOMAIN")
3172 if len(disable_nb) > 0:
3173 raise CommandError("--disable-nb not allowed without DOMAIN")
3175 if len(enable_sid_str) > 0:
3176 raise CommandError("--enable-sid not allowed without DOMAIN")
3177 if len(disable_sid_str) > 0:
3178 raise CommandError("--disable-sid not allowed without DOMAIN")
3180 if len(add_upn) > 0:
3182 if not n.startswith("*."):
3184 raise CommandError("value[%s] specified for --add-upn-suffix should not include with '*.'" % n)
3185 require_update = True
3186 if len(delete_upn) > 0:
3187 for n in delete_upn:
3188 if not n.startswith("*."):
3190 raise CommandError("value[%s] specified for --delete-upn-suffix should not include with '*.'" % n)
3191 require_update = True
3193 for d in delete_upn:
3194 if a.lower() != d.lower():
3196 raise CommandError("value[%s] specified for --add-upn-suffix and --delete-upn-suffix" % a)
3198 if len(add_spn) > 0:
3200 if not n.startswith("*."):
3202 raise CommandError("value[%s] specified for --add-spn-suffix should not include with '*.'" % n)
3203 require_update = True
3204 if len(delete_spn) > 0:
3205 for n in delete_spn:
3206 if not n.startswith("*."):
3208 raise CommandError("value[%s] specified for --delete-spn-suffix should not include with '*.'" % n)
3209 require_update = True
3211 for d in delete_spn:
3212 if a.lower() != d.lower():
3214 raise CommandError("value[%s] specified for --add-spn-suffix and --delete-spn-suffix" % a)
3216 if len(add_upn) > 0:
3217 raise CommandError("--add-upn-suffix not allowed together with DOMAIN")
3218 if len(delete_upn) > 0:
3219 raise CommandError("--delete-upn-suffix not allowed together with DOMAIN")
3220 if len(add_spn) > 0:
3221 raise CommandError("--add-spn-suffix not allowed together with DOMAIN")
3222 if len(delete_spn) > 0:
3223 raise CommandError("--delete-spn-suffix not allowed together with DOMAIN")
3225 if refresh is not None:
3226 if refresh == "store":
3227 require_update = True
3229 if enable_all and refresh != "store":
3230 raise CommandError("--enable-all not allowed together with --refresh=%s" % refresh)
3232 if len(enable_tln) > 0:
3233 raise CommandError("--enable-tln not allowed together with --refresh")
3234 if len(disable_tln) > 0:
3235 raise CommandError("--disable-tln not allowed together with --refresh")
3237 if len(add_tln_ex) > 0:
3238 raise CommandError("--add-tln-ex not allowed together with --refresh")
3239 if len(delete_tln_ex) > 0:
3240 raise CommandError("--delete-tln-ex not allowed together with --refresh")
3242 if len(enable_nb) > 0:
3243 raise CommandError("--enable-nb not allowed together with --refresh")
3244 if len(disable_nb) > 0:
3245 raise CommandError("--disable-nb not allowed together with --refresh")
3247 if len(enable_sid_str) > 0:
3248 raise CommandError("--enable-sid not allowed together with --refresh")
3249 if len(disable_sid_str) > 0:
3250 raise CommandError("--disable-sid not allowed together with --refresh")
3253 require_update = True
3255 if len(enable_tln) > 0:
3256 raise CommandError("--enable-tln not allowed together with --enable-all")
3258 if len(enable_nb) > 0:
3259 raise CommandError("--enable-nb not allowed together with --enable-all")
3261 if len(enable_sid_str) > 0:
3262 raise CommandError("--enable-sid not allowed together with --enable-all")
3264 if len(enable_tln) > 0:
3265 require_update = True
3266 if len(disable_tln) > 0:
3267 require_update = True
3268 for e in enable_tln:
3269 for d in disable_tln:
3270 if e.lower() != d.lower():
3272 raise CommandError("value[%s] specified for --enable-tln and --disable-tln" % e)
3274 if len(add_tln_ex) > 0:
3275 for n in add_tln_ex:
3276 if not n.startswith("*."):
3278 raise CommandError("value[%s] specified for --add-tln-ex should not include with '*.'" % n)
3279 require_update = True
3280 if len(delete_tln_ex) > 0:
3281 for n in delete_tln_ex:
3282 if not n.startswith("*."):
3284 raise CommandError("value[%s] specified for --delete-tln-ex should not include with '*.'" % n)
3285 require_update = True
3286 for a in add_tln_ex:
3287 for d in delete_tln_ex:
3288 if a.lower() != d.lower():
3290 raise CommandError("value[%s] specified for --add-tln-ex and --delete-tln-ex" % a)
3292 if len(enable_nb) > 0:
3293 require_update = True
3294 if len(disable_nb) > 0:
3295 require_update = True
3297 for d in disable_nb:
3298 if e.upper() != d.upper():
3300 raise CommandError("value[%s] specified for --enable-nb and --disable-nb" % e)
3303 for s in enable_sid_str:
3305 sid = security.dom_sid(s)
3306 except TypeError as error:
3307 raise CommandError("value[%s] specified for --enable-sid is not a valid SID" % s)
3308 enable_sid.append(sid)
3310 for s in disable_sid_str:
3312 sid = security.dom_sid(s)
3313 except TypeError as error:
3314 raise CommandError("value[%s] specified for --disable-sid is not a valid SID" % s)
3315 disable_sid.append(sid)
3316 if len(enable_sid) > 0:
3317 require_update = True
3318 if len(disable_sid) > 0:
3319 require_update = True
3320 for e in enable_sid:
3321 for d in disable_sid:
3324 raise CommandError("value[%s] specified for --enable-sid and --disable-sid" % e)
3326 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
3328 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
3330 local_server = self.setup_local_server(sambaopts, localdcopts)
3332 local_lsa = self.new_local_lsa_connection()
3333 except RuntimeError as error:
3334 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
3337 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
3338 except RuntimeError as error:
3339 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
3341 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
3342 local_lsa_info.name.string,
3343 local_lsa_info.dns_domain.string,
3344 local_lsa_info.sid))
3348 local_netlogon = self.new_local_netlogon_connection()
3349 except RuntimeError as error:
3350 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3353 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3354 except RuntimeError as error:
3355 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3357 if local_netlogon_info.domain_name != local_netlogon_info.forest_name:
3358 raise CommandError("The local domain [%s] is not the forest root [%s]" % (
3359 local_netlogon_info.domain_name,
3360 local_netlogon_info.forest_name))
3363 # get all information about our own forest
3364 own_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3366 except RuntimeError as error:
3367 if self.check_runtime_error(error, werror.WERR_RPC_S_PROCNUM_OUT_OF_RANGE):
3368 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3371 if self.check_runtime_error(error, werror.WERR_INVALID_FUNCTION):
3372 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3375 if self.check_runtime_error(error, werror.WERR_NERR_ACFNOTLOADED):
3376 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3379 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3381 self.outf.write("Own forest trust information...\n")
3382 self.write_forest_trust_info(own_forest_info,
3383 tln=local_lsa_info.dns_domain.string)
3386 local_samdb = self.new_local_ldap_connection()
3387 except RuntimeError as error:
3388 raise self.LocalRuntimeError(self, error, "failed to connect to SamDB")
3390 local_partitions_dn = "CN=Partitions,%s" % str(local_samdb.get_config_basedn())
3391 attrs = ['uPNSuffixes', 'msDS-SPNSuffixes']
3393 msgs = local_samdb.search(base=local_partitions_dn,
3394 scope=ldb.SCOPE_BASE,
3395 expression="(objectClass=crossRefContainer)",
3397 stored_msg = msgs[0]
3398 except ldb.LdbError as error:
3399 raise self.LocalLdbError(self, error, "failed to search partition dn")
3401 stored_upn_vals = []
3402 if 'uPNSuffixes' in stored_msg:
3403 stored_upn_vals.extend(stored_msg['uPNSuffixes'])
3405 stored_spn_vals = []
3406 if 'msDS-SPNSuffixes' in stored_msg:
3407 stored_spn_vals.extend(stored_msg['msDS-SPNSuffixes'])
3409 self.outf.write("Stored uPNSuffixes attributes[%d]:\n" % len(stored_upn_vals))
3410 for v in stored_upn_vals:
3411 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3412 self.outf.write("Stored msDS-SPNSuffixes attributes[%d]:\n" % len(stored_spn_vals))
3413 for v in stored_spn_vals:
3414 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3416 if not require_update:
3420 update_upn_vals = []
3421 update_upn_vals.extend(stored_upn_vals)
3424 update_spn_vals = []
3425 update_spn_vals.extend(stored_spn_vals)
3429 for i in xrange(0, len(update_upn_vals)):
3430 v = update_upn_vals[i]
3431 if v.lower() != upn.lower():
3436 raise CommandError("Entry already present for value[%s] specified for --add-upn-suffix" % upn)
3437 update_upn_vals.append(upn)
3440 for upn in delete_upn:
3442 for i in xrange(0, len(update_upn_vals)):
3443 v = update_upn_vals[i]
3444 if v.lower() != upn.lower():
3449 raise CommandError("Entry not found for value[%s] specified for --delete-upn-suffix" % upn)
3451 update_upn_vals.pop(idx)
3456 for i in xrange(0, len(update_spn_vals)):
3457 v = update_spn_vals[i]
3458 if v.lower() != spn.lower():
3463 raise CommandError("Entry already present for value[%s] specified for --add-spn-suffix" % spn)
3464 update_spn_vals.append(spn)
3467 for spn in delete_spn:
3469 for i in xrange(0, len(update_spn_vals)):
3470 v = update_spn_vals[i]
3471 if v.lower() != spn.lower():
3476 raise CommandError("Entry not found for value[%s] specified for --delete-spn-suffix" % spn)
3478 update_spn_vals.pop(idx)
3481 self.outf.write("Update uPNSuffixes attributes[%d]:\n" % len(update_upn_vals))
3482 for v in update_upn_vals:
3483 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3484 self.outf.write("Update msDS-SPNSuffixes attributes[%d]:\n" % len(update_spn_vals))
3485 for v in update_spn_vals:
3486 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3488 update_msg = ldb.Message()
3489 update_msg.dn = stored_msg.dn
3492 update_msg['uPNSuffixes'] = ldb.MessageElement(update_upn_vals,
3493 ldb.FLAG_MOD_REPLACE,
3496 update_msg['msDS-SPNSuffixes'] = ldb.MessageElement(update_spn_vals,
3497 ldb.FLAG_MOD_REPLACE,
3500 local_samdb.modify(update_msg)
3501 except ldb.LdbError as error:
3502 raise self.LocalLdbError(self, error, "failed to update partition dn")
3505 stored_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3507 except RuntimeError as error:
3508 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3510 self.outf.write("Stored forest trust information...\n")
3511 self.write_forest_trust_info(stored_forest_info,
3512 tln=local_lsa_info.dns_domain.string)
3516 lsaString = lsa.String()
3517 lsaString.string = domain
3518 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
3519 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
3520 except NTSTATUSError as error:
3521 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
3522 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
3524 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
3526 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
3527 local_tdo_info.netbios_name.string,
3528 local_tdo_info.domain_name.string,
3529 local_tdo_info.sid))
3531 if not local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
3532 raise CommandError("trusted domain object for domain [%s] is not marked as FOREST_TRANSITIVE." % domain)
3534 if refresh is not None:
3536 local_netlogon = self.new_local_netlogon_connection()
3537 except RuntimeError as error:
3538 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3541 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3542 except RuntimeError as error:
3543 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3545 lsa_update_check = 1
3546 if refresh == "store":
3547 netlogon_update_tdo = netlogon.DS_GFTI_UPDATE_TDO
3549 lsa_update_check = 0
3551 netlogon_update_tdo = 0
3554 # get all information about the remote trust
3555 # this triggers netr_GetForestTrustInformation to the remote domain
3556 # and lsaRSetForestTrustInformation() locally, but new top level
3557 # names are disabled by default.
3558 fresh_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3559 local_tdo_info.domain_name.string,
3560 netlogon_update_tdo)
3561 except RuntimeError as error:
3562 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3565 fresh_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3566 local_tdo_info.domain_name,
3567 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3570 except RuntimeError as error:
3571 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3573 self.outf.write("Fresh forest trust information...\n")
3574 self.write_forest_trust_info(fresh_forest_info,
3575 tln=local_tdo_info.domain_name.string,
3576 collisions=fresh_forest_collision)
3578 if refresh == "store":
3580 lsaString = lsa.String()
3581 lsaString.string = local_tdo_info.domain_name.string
3582 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3584 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3585 except RuntimeError as error:
3586 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3588 self.outf.write("Stored forest trust information...\n")
3589 self.write_forest_trust_info(stored_forest_info,
3590 tln=local_tdo_info.domain_name.string)
3595 # The none --refresh path
3599 lsaString = lsa.String()
3600 lsaString.string = local_tdo_info.domain_name.string
3601 local_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3603 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3604 except RuntimeError as error:
3605 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3607 self.outf.write("Local forest trust information...\n")
3608 self.write_forest_trust_info(local_forest_info,
3609 tln=local_tdo_info.domain_name.string)
3611 if not require_update:
3615 entries.extend(local_forest_info.entries)
3616 update_forest_info = lsa.ForestTrustInformation()
3617 update_forest_info.count = len(entries)
3618 update_forest_info.entries = entries
3621 for i in xrange(0, len(update_forest_info.entries)):
3622 r = update_forest_info.entries[i]
3623 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3625 if update_forest_info.entries[i].flags == 0:
3627 update_forest_info.entries[i].time = 0
3628 update_forest_info.entries[i].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3629 for i in xrange(0, len(update_forest_info.entries)):
3630 r = update_forest_info.entries[i]
3631 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3633 if update_forest_info.entries[i].flags == 0:
3635 update_forest_info.entries[i].time = 0
3636 update_forest_info.entries[i].flags &= ~lsa.LSA_NB_DISABLED_MASK
3637 update_forest_info.entries[i].flags &= ~lsa.LSA_SID_DISABLED_MASK
3639 for tln in enable_tln:
3641 for i in xrange(0, len(update_forest_info.entries)):
3642 r = update_forest_info.entries[i]
3643 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3645 if r.forest_trust_data.string.lower() != tln.lower():
3650 raise CommandError("Entry not found for value[%s] specified for --enable-tln" % tln)
3651 if not update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_MASK:
3652 raise CommandError("Entry found for value[%s] specified for --enable-tln is already enabled" % tln)
3653 update_forest_info.entries[idx].time = 0
3654 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3656 for tln in disable_tln:
3658 for i in xrange(0, len(update_forest_info.entries)):
3659 r = update_forest_info.entries[i]
3660 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3662 if r.forest_trust_data.string.lower() != tln.lower():
3667 raise CommandError("Entry not found for value[%s] specified for --disable-tln" % tln)
3668 if update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_ADMIN:
3669 raise CommandError("Entry found for value[%s] specified for --disable-tln is already disabled" % tln)
3670 update_forest_info.entries[idx].time = 0
3671 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3672 update_forest_info.entries[idx].flags |= lsa.LSA_TLN_DISABLED_ADMIN
3674 for tln_ex in add_tln_ex:
3676 for i in xrange(0, len(update_forest_info.entries)):
3677 r = update_forest_info.entries[i]
3678 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3680 if r.forest_trust_data.string.lower() != tln_ex.lower():
3685 raise CommandError("Entry already present for value[%s] specified for --add-tln-ex" % tln_ex)
3687 tln_dot = ".%s" % tln_ex.lower()
3689 for i in xrange(0, len(update_forest_info.entries)):
3690 r = update_forest_info.entries[i]
3691 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3693 r_dot = ".%s" % r.forest_trust_data.string.lower()
3694 if tln_dot == r_dot:
3695 raise CommandError("TLN entry present for value[%s] specified for --add-tln-ex" % tln_ex)
3696 if not tln_dot.endswith(r_dot):
3702 raise CommandError("No TLN parent present for value[%s] specified for --add-tln-ex" % tln_ex)
3704 r = lsa.ForestTrustRecord()
3705 r.type = lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX
3708 r.forest_trust_data.string = tln_ex
3711 entries.extend(update_forest_info.entries)
3712 entries.insert(idx + 1, r)
3713 update_forest_info.count = len(entries)
3714 update_forest_info.entries = entries
3716 for tln_ex in delete_tln_ex:
3718 for i in xrange(0, len(update_forest_info.entries)):
3719 r = update_forest_info.entries[i]
3720 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3722 if r.forest_trust_data.string.lower() != tln_ex.lower():
3727 raise CommandError("Entry not found for value[%s] specified for --delete-tln-ex" % tln_ex)
3730 entries.extend(update_forest_info.entries)
3732 update_forest_info.count = len(entries)
3733 update_forest_info.entries = entries
3735 for nb in enable_nb:
3737 for i in xrange(0, len(update_forest_info.entries)):
3738 r = update_forest_info.entries[i]
3739 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3741 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3746 raise CommandError("Entry not found for value[%s] specified for --enable-nb" % nb)
3747 if not update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_MASK:
3748 raise CommandError("Entry found for value[%s] specified for --enable-nb is already enabled" % nb)
3749 update_forest_info.entries[idx].time = 0
3750 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3752 for nb in disable_nb:
3754 for i in xrange(0, len(update_forest_info.entries)):
3755 r = update_forest_info.entries[i]
3756 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3758 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3763 raise CommandError("Entry not found for value[%s] specified for --delete-nb" % nb)
3764 if update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_ADMIN:
3765 raise CommandError("Entry found for value[%s] specified for --disable-nb is already disabled" % nb)
3766 update_forest_info.entries[idx].time = 0
3767 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3768 update_forest_info.entries[idx].flags |= lsa.LSA_NB_DISABLED_ADMIN
3770 for sid in enable_sid:
3772 for i in xrange(0, len(update_forest_info.entries)):
3773 r = update_forest_info.entries[i]
3774 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3776 if r.forest_trust_data.domain_sid != sid:
3781 raise CommandError("Entry not found for value[%s] specified for --enable-sid" % sid)
3782 if not update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_MASK:
3783 raise CommandError("Entry found for value[%s] specified for --enable-sid is already enabled" % nb)
3784 update_forest_info.entries[idx].time = 0
3785 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3787 for sid in disable_sid:
3789 for i in xrange(0, len(update_forest_info.entries)):
3790 r = update_forest_info.entries[i]
3791 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3793 if r.forest_trust_data.domain_sid != sid:
3798 raise CommandError("Entry not found for value[%s] specified for --delete-sid" % sid)
3799 if update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_ADMIN:
3800 raise CommandError("Entry found for value[%s] specified for --disable-sid is already disabled" % nb)
3801 update_forest_info.entries[idx].time = 0
3802 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3803 update_forest_info.entries[idx].flags |= lsa.LSA_SID_DISABLED_ADMIN
3806 update_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3807 local_tdo_info.domain_name,
3808 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3809 update_forest_info, 0)
3810 except RuntimeError as error:
3811 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3813 self.outf.write("Updated forest trust information...\n")
3814 self.write_forest_trust_info(update_forest_info,
3815 tln=local_tdo_info.domain_name.string,
3816 collisions=update_forest_collision)
3819 lsaString = lsa.String()
3820 lsaString.string = local_tdo_info.domain_name.string
3821 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3823 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3824 except RuntimeError as error:
3825 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3827 self.outf.write("Stored forest trust information...\n")
3828 self.write_forest_trust_info(stored_forest_info,
3829 tln=local_tdo_info.domain_name.string)
3832 class cmd_domain_tombstones_expunge(Command):
3833 """Expunge tombstones from the database.
3835 This command expunges tombstones from the database."""
3836 synopsis = "%prog NC [NC [...]] [options]"
3839 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
3840 metavar="URL", dest="H"),
3841 Option("--current-time",
3842 help="The current time to evaluate the tombstone lifetime from, expressed as YYYY-MM-DD",
3844 Option("--tombstone-lifetime", help="Number of days a tombstone should be preserved for", type=int),
3847 takes_args = ["nc*"]
3849 takes_optiongroups = {
3850 "sambaopts": options.SambaOptions,
3851 "credopts": options.CredentialsOptions,
3852 "versionopts": options.VersionOptions,
3855 def run(self, *ncs, **kwargs):
3856 sambaopts = kwargs.get("sambaopts")
3857 credopts = kwargs.get("credopts")
3858 versionpts = kwargs.get("versionopts")
3860 current_time_string = kwargs.get("current_time")
3861 tombstone_lifetime = kwargs.get("tombstone_lifetime")
3862 lp = sambaopts.get_loadparm()
3863 creds = credopts.get_credentials(lp)
3864 samdb = SamDB(url=H, session_info=system_session(),
3865 credentials=creds, lp=lp)
3867 if current_time_string is not None:
3868 current_time_obj = time.strptime(current_time_string, "%Y-%m-%d")
3869 current_time = long(time.mktime(current_time_obj))
3872 current_time = long(time.time())
3875 res = samdb.search(expression="", base="", scope=ldb.SCOPE_BASE,
3876 attrs=["namingContexts"])
3879 for nc in res[0]["namingContexts"]:
3884 started_transaction = False
3886 samdb.transaction_start()
3887 started_transaction = True
3889 removed_links) = samdb.garbage_collect_tombstones(ncs,
3890 current_time=current_time,
3891 tombstone_lifetime=tombstone_lifetime)
3893 except Exception as err:
3894 if started_transaction:
3895 samdb.transaction_cancel()
3896 raise CommandError("Failed to expunge / garbage collect tombstones", err)
3898 samdb.transaction_commit()
3900 self.outf.write("Removed %d objects and %d links successfully\n"
3901 % (removed_objects, removed_links))
3905 class cmd_domain_trust(SuperCommand):
3906 """Domain and forest trust management."""
3909 subcommands["list"] = cmd_domain_trust_list()
3910 subcommands["show"] = cmd_domain_trust_show()
3911 subcommands["create"] = cmd_domain_trust_create()
3912 subcommands["delete"] = cmd_domain_trust_delete()
3913 subcommands["validate"] = cmd_domain_trust_validate()
3914 subcommands["namespaces"] = cmd_domain_trust_namespaces()
3916 class cmd_domain_tombstones(SuperCommand):
3917 """Domain tombstone and recycled object management."""
3920 subcommands["expunge"] = cmd_domain_tombstones_expunge()
3922 class ldif_schema_update:
3923 """Helper class for applying LDIF schema updates"""
3926 self.is_defunct = False
3927 self.unknown_oid = None
3931 def _ldap_schemaUpdateNow(self, samdb):
3935 add: schemaUpdateNow
3938 samdb.modify_ldif(ldif)
3940 def can_ignore_failure(self, error):
3941 """Checks if we can safely ignore failure to apply an LDIF update"""
3942 (num, errstr) = error.args
3944 # Microsoft has marked objects as defunct that Samba doesn't know about
3945 if num == ldb.ERR_NO_SUCH_OBJECT and self.is_defunct:
3946 print("Defunct object %s doesn't exist, skipping" % self.dn)
3948 elif self.unknown_oid is not None:
3949 print("Skipping unknown OID %s for object %s" %(self.unknown_oid, self.dn))
3954 def apply(self, samdb):
3955 """Applies a single LDIF update to the schema"""
3959 samdb.modify_ldif(self.ldif, controls=['relax:0'])
3960 except ldb.LdbError as e:
3961 if e.args[0] == ldb.ERR_INVALID_ATTRIBUTE_SYNTAX:
3963 # REFRESH after a failed change
3965 # Otherwise the OID-to-attribute mapping in
3966 # _apply_updates_in_file() won't work, because it
3967 # can't lookup the new OID in the schema
3968 self._ldap_schemaUpdateNow(samdb)
3970 samdb.modify_ldif(self.ldif, controls=['relax:0'])
3973 except ldb.LdbError as e:
3974 if self.can_ignore_failure(e):
3977 print("Exception: %s" % e)
3978 print("Encountered while trying to apply the following LDIF")
3979 print("----------------------------------------------------")
3980 print("%s" % self.ldif)
3986 class cmd_domain_schema_upgrade(Command):
3987 """Domain schema upgrading"""
3989 synopsis = "%prog [options]"
3991 takes_optiongroups = {
3992 "sambaopts": options.SambaOptions,
3993 "versionopts": options.VersionOptions,
3994 "credopts": options.CredentialsOptions,
3998 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
3999 metavar="URL", dest="H"),
4000 Option("--quiet", help="Be quiet", action="store_true"),
4001 Option("--verbose", help="Be verbose", action="store_true"),
4002 Option("--schema", type="choice", metavar="SCHEMA",
4003 choices=["2012", "2012_R2"],
4004 help="The schema file to upgrade to. Default is (Windows) 2012_R2.",
4006 Option("--ldf-file", type=str, default=None,
4007 help="Just apply the schema updates in the adprep/.LDF file(s) specified"),
4008 Option("--base-dir", type=str, default=None,
4009 help="Location of ldf files Default is ${SETUPDIR}/adprep.")
4012 def _apply_updates_in_file(self, samdb, ldif_file):
4014 Applies a series of updates specified in an .LDIF file. The .LDIF file
4015 is based on the adprep Schema updates provided by Microsoft.
4018 ldif_op = ldif_schema_update()
4020 # parse the file line by line and work out each update operation to apply
4021 for line in ldif_file:
4023 line = line.rstrip()
4025 # the operations in the .LDIF file are separated by blank lines. If
4026 # we hit a blank line, try to apply the update we've parsed so far
4029 # keep going if we haven't parsed anything yet
4030 if ldif_op.ldif == '':
4033 # Apply the individual change
4034 count += ldif_op.apply(samdb)
4036 # start storing the next operation from scratch again
4037 ldif_op = ldif_schema_update()
4040 # replace the placeholder domain name in the .ldif file with the real domain
4041 if line.upper().endswith('DC=X'):
4042 line = line[:-len('DC=X')] + str(samdb.get_default_basedn())
4043 elif line.upper().endswith('CN=X'):
4044 line = line[:-len('CN=X')] + str(samdb.get_default_basedn())
4046 values = line.split(':')
4048 if values[0].lower() == 'dn':
4049 ldif_op.dn = values[1].strip()
4051 # replace the Windows-specific operation with the Samba one
4052 if values[0].lower() == 'changetype':
4053 line = line.lower().replace(': ntdsschemaadd',
4055 line = line.lower().replace(': ntdsschemamodify',
4058 if values[0].lower() in ['rdnattid', 'subclassof',
4059 'systemposssuperiors',
4061 'systemauxiliaryclass']:
4064 # The Microsoft updates contain some OIDs we don't recognize.
4065 # Query the DB to see if we can work out the OID this update is
4066 # referring to. If we find a match, then replace the OID with
4067 # the ldapDisplayname
4069 res = samdb.search(base=samdb.get_schema_basedn(),
4070 expression="(|(attributeId=%s)(governsId=%s))" %
4072 attrs=['ldapDisplayName'])
4075 ldif_op.unknown_oid = value
4077 display_name = res[0]['ldapDisplayName'][0]
4078 line = line.replace(value, ' ' + display_name)
4080 # Microsoft has marked objects as defunct that Samba doesn't know about
4081 if values[0].lower() == 'isdefunct' and values[1].strip().lower() == 'true':
4082 ldif_op.is_defunct = True
4084 # Samba has added the showInAdvancedViewOnly attribute to all objects,
4085 # so rather than doing an add, we need to do a replace
4086 if values[0].lower() == 'add' and values[1].strip().lower() == 'showinadvancedviewonly':
4087 line = 'replace: showInAdvancedViewOnly'
4089 # Add the line to the current LDIF operation (including the newline
4090 # we stripped off at the start of the loop)
4091 ldif_op.ldif += line + '\n'
4096 def _apply_update(self, samdb, update_file, base_dir):
4097 """Wrapper function for parsing an LDIF file and applying the updates"""
4099 print("Applying %s updates..." % update_file)
4103 ldif_file = open(os.path.join(base_dir, update_file))
4105 count = self._apply_updates_in_file(samdb, ldif_file)
4111 print("%u changes applied" % count)
4115 def run(self, **kwargs):
4116 from samba.ms_schema_markdown import read_ms_markdown
4117 from samba.schema import Schema
4119 updates_allowed_overriden = False
4120 sambaopts = kwargs.get("sambaopts")
4121 credopts = kwargs.get("credopts")
4122 versionpts = kwargs.get("versionopts")
4123 lp = sambaopts.get_loadparm()
4124 creds = credopts.get_credentials(lp)
4126 target_schema = kwargs.get("schema")
4127 ldf_files = kwargs.get("ldf_file")
4128 base_dir = kwargs.get("base_dir")
4132 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
4134 # we're not going to get far if the config doesn't allow schema updates
4135 if lp.get("dsdb:schema update allowed") is None:
4136 lp.set("dsdb:schema update allowed", "yes")
4137 print("Temporarily overriding 'dsdb:schema update allowed' setting")
4138 updates_allowed_overriden = True
4140 own_dn = ldb.Dn(samdb, samdb.get_dsServiceName())
4141 master = get_fsmo_roleowner(samdb, str(samdb.get_schema_basedn()),
4143 if own_dn != master:
4144 raise CommandError("This server is not the schema master.")
4146 # if specific LDIF files were specified, just apply them
4148 schema_updates = ldf_files.split(",")
4152 # work out the version of the target schema we're upgrading to
4153 end = Schema.get_version(target_schema)
4155 # work out the version of the schema we're currently using
4156 res = samdb.search(base=samdb.get_schema_basedn(),
4157 scope=ldb.SCOPE_BASE, attrs=['objectVersion'])
4160 raise CommandError('Could not determine current schema version')
4161 start = int(res[0]['objectVersion'][0]) + 1
4163 diff_dir = setup_path("adprep/WindowsServerDocs")
4164 if base_dir is None:
4165 # Read from the Schema-Updates.md file
4166 temp_folder = tempfile.mkdtemp()
4168 update_file = setup_path("adprep/WindowsServerDocs/Schema-Updates.md")
4171 read_ms_markdown(update_file, temp_folder)
4172 except Exception as e:
4173 print("Exception in markdown parsing: %s" % e)
4174 shutil.rmtree(temp_folder)
4175 raise CommandError('Failed to upgrade schema')
4177 base_dir = temp_folder
4179 for version in range(start, end + 1):
4180 update = 'Sch%d.ldf' % version
4181 schema_updates.append(update)
4183 # Apply patches if we parsed the Schema-Updates.md file
4184 diff = os.path.abspath(os.path.join(diff_dir, update + '.diff'))
4185 if temp_folder and os.path.exists(diff):
4187 p = subprocess.Popen(['patch', update, '-i', diff],
4188 stdout=subprocess.PIPE,
4189 stderr=subprocess.PIPE, cwd=temp_folder)
4190 except (OSError, IOError):
4191 shutil.rmtree(temp_folder)
4192 raise CommandError("Failed to upgrade schema. Check if 'patch' is installed.")
4194 stdout, stderr = p.communicate()
4197 print("Exception in patch: %s\n%s" % (stdout, stderr))
4198 shutil.rmtree(temp_folder)
4199 raise CommandError('Failed to upgrade schema')
4201 print("Patched %s using %s" % (update, diff))
4203 if base_dir is None:
4204 base_dir = setup_path("adprep")
4206 samdb.transaction_start()
4208 error_encountered = False
4211 # Apply the schema updates needed to move to the new schema version
4212 for ldif_file in schema_updates:
4213 count += self._apply_update(samdb, ldif_file, base_dir)
4216 samdb.transaction_commit()
4217 print("Schema successfully updated")
4219 print("No changes applied to schema")
4220 samdb.transaction_cancel()
4221 except Exception as e:
4222 print("Exception: %s" % e)
4223 print("Error encountered, aborting schema upgrade")
4224 samdb.transaction_cancel()
4225 error_encountered = True
4227 if updates_allowed_overriden:
4228 lp.set("dsdb:schema update allowed", "no")
4231 shutil.rmtree(temp_folder)
4233 if error_encountered:
4234 raise CommandError('Failed to upgrade schema')
4236 class cmd_domain_functional_prep(Command):
4237 """Domain functional level preparation"""
4239 synopsis = "%prog [options]"
4241 takes_optiongroups = {
4242 "sambaopts": options.SambaOptions,
4243 "versionopts": options.VersionOptions,
4244 "credopts": options.CredentialsOptions,
4248 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
4249 metavar="URL", dest="H"),
4250 Option("--quiet", help="Be quiet", action="store_true"),
4251 Option("--verbose", help="Be verbose", action="store_true"),
4252 Option("--function-level", type="choice", metavar="FUNCTION_LEVEL",
4253 choices=["2008_R2", "2012", "2012_R2"],
4254 help="The schema file to upgrade to. Default is (Windows) 2012_R2.",
4256 Option("--forest-prep", action="store_true",
4257 help="Run the forest prep (by default, both the domain and forest prep are run)."),
4258 Option("--domain-prep", action="store_true",
4259 help="Run the domain prep (by default, both the domain and forest prep are run).")
4262 def run(self, **kwargs):
4263 updates_allowed_overriden = False
4264 sambaopts = kwargs.get("sambaopts")
4265 credopts = kwargs.get("credopts")
4266 versionpts = kwargs.get("versionopts")
4267 lp = sambaopts.get_loadparm()
4268 creds = credopts.get_credentials(lp)
4270 target_level = string_version_to_constant[kwargs.get("function_level")]
4271 forest_prep = kwargs.get("forest_prep")
4272 domain_prep = kwargs.get("domain_prep")
4274 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
4276 # we're not going to get far if the config doesn't allow schema updates
4277 if lp.get("dsdb:schema update allowed") is None:
4278 lp.set("dsdb:schema update allowed", "yes")
4279 print("Temporarily overriding 'dsdb:schema update allowed' setting")
4280 updates_allowed_overriden = True
4282 if forest_prep is None and domain_prep is None:
4286 own_dn = ldb.Dn(samdb, samdb.get_dsServiceName())
4288 master = get_fsmo_roleowner(samdb, str(samdb.get_schema_basedn()),
4290 if own_dn != master:
4291 raise CommandError("This server is not the schema master.")
4294 domain_dn = samdb.domain_dn()
4295 infrastructure_dn = "CN=Infrastructure," + domain_dn
4296 master = get_fsmo_roleowner(samdb, infrastructure_dn,
4298 if own_dn != master:
4299 raise CommandError("This server is not the infrastructure master.")
4302 samdb.transaction_start()
4303 error_encountered = False
4305 from samba.forest_update import ForestUpdate
4306 forest = ForestUpdate(samdb, fix=True)
4308 forest.check_updates_iterator([53, 79, 80, 81, 82, 83])
4309 forest.check_updates_functional_level(target_level,
4310 DS_DOMAIN_FUNCTION_2008_R2,
4311 update_revision=True)
4313 samdb.transaction_commit()
4314 except Exception as e:
4315 print("Exception: %s" % e)
4316 samdb.transaction_cancel()
4317 error_encountered = True
4320 samdb.transaction_start()
4321 error_encountered = False
4323 from samba.domain_update import DomainUpdate
4325 domain = DomainUpdate(samdb, fix=True)
4326 domain.check_updates_functional_level(target_level,
4327 DS_DOMAIN_FUNCTION_2008,
4328 update_revision=True)
4330 samdb.transaction_commit()
4331 except Exception as e:
4332 print("Exception: %s" % e)
4333 samdb.transaction_cancel()
4334 error_encountered = True
4336 if updates_allowed_overriden:
4337 lp.set("dsdb:schema update allowed", "no")
4339 if error_encountered:
4340 raise CommandError('Failed to perform functional prep')
4342 class cmd_domain(SuperCommand):
4343 """Domain management."""
4346 subcommands["demote"] = cmd_domain_demote()
4347 if cmd_domain_export_keytab is not None:
4348 subcommands["exportkeytab"] = cmd_domain_export_keytab()
4349 subcommands["info"] = cmd_domain_info()
4350 subcommands["provision"] = cmd_domain_provision()
4351 subcommands["join"] = cmd_domain_join()
4352 subcommands["dcpromo"] = cmd_domain_dcpromo()
4353 subcommands["level"] = cmd_domain_level()
4354 subcommands["passwordsettings"] = cmd_domain_passwordsettings()
4355 subcommands["classicupgrade"] = cmd_domain_classicupgrade()
4356 subcommands["samba3upgrade"] = cmd_domain_samba3upgrade()
4357 subcommands["trust"] = cmd_domain_trust()
4358 subcommands["tombstones"] = cmd_domain_tombstones()
4359 subcommands["schemaupgrade"] = cmd_domain_schema_upgrade()
4360 subcommands["functionalprep"] = cmd_domain_functional_prep()