3 # Copyright Matthias Dieter Wallnoefer 2009
4 # Copyright Andrew Kroeger 2009
5 # Copyright Jelmer Vernooij 2007-2012
6 # Copyright Giampaolo Lauria 2011
7 # Copyright Matthieu Patou <mat@matws.net> 2011
8 # Copyright Andrew Bartlett 2008-2015
9 # Copyright Stefan Metzmacher 2012
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 3 of the License, or
14 # (at your option) any later version.
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 from __future__ import print_function
26 from __future__ import division
27 import samba.getopt as options
39 from samba import ntstatus
40 from samba import NTSTATUSError
41 from samba import werror
42 from getpass import getpass
43 from samba.net import Net, LIBNET_JOIN_AUTOMATIC
45 from samba.join import join_RODC, join_DC, join_subdomain
46 from samba.auth import system_session
47 from samba.samdb import SamDB, get_default_backend_store
48 from samba.ndr import ndr_unpack, ndr_pack, ndr_print
49 from samba.dcerpc import drsuapi
50 from samba.dcerpc import drsblobs
51 from samba.dcerpc import lsa
52 from samba.dcerpc import netlogon
53 from samba.dcerpc import security
54 from samba.dcerpc import nbt
55 from samba.dcerpc import misc
56 from samba.dcerpc.samr import DOMAIN_PASSWORD_COMPLEX, DOMAIN_PASSWORD_STORE_CLEARTEXT
57 from samba.netcmd import (
63 from samba.netcmd.fsmo import get_fsmo_roleowner
64 from samba.netcmd.common import netcmd_get_domain_infos_via_cldap
65 from samba.samba3 import Samba3
66 from samba.samba3 import param as s3param
67 from samba.upgrade import upgrade_from_samba3
68 from samba.drs_utils import (
69 sendDsReplicaSync, drsuapi_connect, drsException,
71 from samba import remove_dc, arcfour_encrypt, string_to_byte_array
73 from samba.dsdb import (
74 DS_DOMAIN_FUNCTION_2000,
75 DS_DOMAIN_FUNCTION_2003,
76 DS_DOMAIN_FUNCTION_2003_MIXED,
77 DS_DOMAIN_FUNCTION_2008,
78 DS_DOMAIN_FUNCTION_2008_R2,
79 DS_DOMAIN_FUNCTION_2012,
80 DS_DOMAIN_FUNCTION_2012_R2,
81 DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL,
82 DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL,
83 UF_WORKSTATION_TRUST_ACCOUNT,
84 UF_SERVER_TRUST_ACCOUNT,
85 UF_TRUSTED_FOR_DELEGATION,
86 UF_PARTIAL_SECRETS_ACCOUNT
89 from samba.provision import (
92 DEFAULT_MIN_PWD_LENGTH,
96 from samba.provision.common import (
102 string_version_to_constant = {
103 "2008_R2" : DS_DOMAIN_FUNCTION_2008_R2,
104 "2012": DS_DOMAIN_FUNCTION_2012,
105 "2012_R2": DS_DOMAIN_FUNCTION_2012_R2,
108 common_provision_join_options = [
109 Option("--machinepass", type="string", metavar="PASSWORD",
110 help="choose machine password (otherwise random)"),
111 Option("--plaintext-secrets", action="store_true",
112 help="Store secret/sensitive values as plain text on disk" +
113 "(default is to encrypt secret/ensitive values)"),
114 Option("--backend-store", type="choice", metavar="BACKENDSTORE",
115 choices=["tdb", "mdb"],
116 help="Specify the database backend to be used "
117 "(default is %s)" % get_default_backend_store()),
118 Option("--targetdir", metavar="DIR",
119 help="Set target directory (where to store provision)", type=str),
120 Option("--quiet", help="Be quiet", action="store_true"),
123 def get_testparm_var(testparm, smbconf, varname):
124 errfile = open(os.devnull, 'w')
125 p = subprocess.Popen([testparm, '-s', '-l',
126 '--parameter-name=%s' % varname, smbconf],
127 stdout=subprocess.PIPE, stderr=errfile)
128 (out,err) = p.communicate()
130 lines = out.split('\n')
132 return lines[0].strip()
136 import samba.dckeytab
138 cmd_domain_export_keytab = None
140 class cmd_domain_export_keytab(Command):
141 """Dump Kerberos keys of the domain into a keytab."""
143 synopsis = "%prog <keytab> [options]"
145 takes_optiongroups = {
146 "sambaopts": options.SambaOptions,
147 "credopts": options.CredentialsOptions,
148 "versionopts": options.VersionOptions,
152 Option("--principal", help="extract only this principal", type=str),
155 takes_args = ["keytab"]
157 def run(self, keytab, credopts=None, sambaopts=None, versionopts=None, principal=None):
158 lp = sambaopts.get_loadparm()
160 net.export_keytab(keytab=keytab, principal=principal)
163 class cmd_domain_info(Command):
164 """Print basic info about a domain and the DC passed as parameter."""
166 synopsis = "%prog <ip_address> [options]"
171 takes_optiongroups = {
172 "sambaopts": options.SambaOptions,
173 "credopts": options.CredentialsOptions,
174 "versionopts": options.VersionOptions,
177 takes_args = ["address"]
179 def run(self, address, credopts=None, sambaopts=None, versionopts=None):
180 lp = sambaopts.get_loadparm()
182 res = netcmd_get_domain_infos_via_cldap(lp, None, address)
184 raise CommandError("Invalid IP address '" + address + "'!")
185 self.outf.write("Forest : %s\n" % res.forest)
186 self.outf.write("Domain : %s\n" % res.dns_domain)
187 self.outf.write("Netbios domain : %s\n" % res.domain_name)
188 self.outf.write("DC name : %s\n" % res.pdc_dns_name)
189 self.outf.write("DC netbios name : %s\n" % res.pdc_name)
190 self.outf.write("Server site : %s\n" % res.server_site)
191 self.outf.write("Client site : %s\n" % res.client_site)
194 class cmd_domain_provision(Command):
195 """Provision a domain."""
197 synopsis = "%prog [options]"
199 takes_optiongroups = {
200 "sambaopts": options.SambaOptions,
201 "versionopts": options.VersionOptions,
205 Option("--interactive", help="Ask for names", action="store_true"),
206 Option("--domain", type="string", metavar="DOMAIN",
207 help="NetBIOS domain name to use"),
208 Option("--domain-guid", type="string", metavar="GUID",
209 help="set domainguid (otherwise random)"),
210 Option("--domain-sid", type="string", metavar="SID",
211 help="set domainsid (otherwise random)"),
212 Option("--ntds-guid", type="string", metavar="GUID",
213 help="set NTDS object GUID (otherwise random)"),
214 Option("--invocationid", type="string", metavar="GUID",
215 help="set invocationid (otherwise random)"),
216 Option("--host-name", type="string", metavar="HOSTNAME",
217 help="set hostname"),
218 Option("--host-ip", type="string", metavar="IPADDRESS",
219 help="set IPv4 ipaddress"),
220 Option("--host-ip6", type="string", metavar="IP6ADDRESS",
221 help="set IPv6 ipaddress"),
222 Option("--site", type="string", metavar="SITENAME",
223 help="set site name"),
224 Option("--adminpass", type="string", metavar="PASSWORD",
225 help="choose admin password (otherwise random)"),
226 Option("--krbtgtpass", type="string", metavar="PASSWORD",
227 help="choose krbtgt password (otherwise random)"),
228 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
229 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
230 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
231 "BIND9_FLATFILE uses bind9 text database to store zone information, "
232 "BIND9_DLZ uses samba4 AD to store zone information, "
233 "NONE skips the DNS setup entirely (not recommended)",
234 default="SAMBA_INTERNAL"),
235 Option("--dnspass", type="string", metavar="PASSWORD",
236 help="choose dns password (otherwise random)"),
237 Option("--root", type="string", metavar="USERNAME",
238 help="choose 'root' unix username"),
239 Option("--nobody", type="string", metavar="USERNAME",
240 help="choose 'nobody' user"),
241 Option("--users", type="string", metavar="GROUPNAME",
242 help="choose 'users' group"),
243 Option("--blank", action="store_true",
244 help="do not add users or groups, just the structure"),
245 Option("--server-role", type="choice", metavar="ROLE",
246 choices=["domain controller", "dc", "member server", "member", "standalone"],
247 help="The server role (domain controller | dc | member server | member | standalone). Default is dc.",
248 default="domain controller"),
249 Option("--function-level", type="choice", metavar="FOR-FUN-LEVEL",
250 choices=["2000", "2003", "2008", "2008_R2"],
251 help="The domain and forest function level (2000 | 2003 | 2008 | 2008_R2 - always native). Default is (Windows) 2008_R2 Native.",
253 Option("--base-schema", type="choice", metavar="BASE-SCHEMA",
254 choices=["2008_R2", "2008_R2_old", "2012", "2012_R2"],
255 help="The base schema files to use. Default is (Windows) 2008_R2.",
257 Option("--next-rid", type="int", metavar="NEXTRID", default=1000,
258 help="The initial nextRid value (only needed for upgrades). Default is 1000."),
259 Option("--partitions-only",
260 help="Configure Samba's partitions, but do not modify them (ie, join a BDC)", action="store_true"),
261 Option("--use-rfc2307", action="store_true", help="Use AD to store posix attributes (default = no)"),
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,
604 lp = sambaopts.get_loadparm()
605 creds = credopts.get_credentials(lp)
606 net = Net(creds, lp, server=credopts.ipaddress)
608 logger = self.get_logger()
610 logger.setLevel(logging.DEBUG)
612 logger.setLevel(logging.WARNING)
614 logger.setLevel(logging.INFO)
616 netbios_name = lp.get("netbios name")
622 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
623 site=site, netbios_name=netbios_name, targetdir=targetdir,
624 domain_critical_only=domain_critical_only,
625 machinepass=machinepass, use_ntvfs=use_ntvfs,
626 dns_backend=dns_backend,
627 promote_existing=True, plaintext_secrets=plaintext_secrets,
628 backend_store=backend_store)
630 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
631 site=site, netbios_name=netbios_name, targetdir=targetdir,
632 domain_critical_only=domain_critical_only,
633 machinepass=machinepass, use_ntvfs=use_ntvfs, dns_backend=dns_backend,
634 promote_existing=True, plaintext_secrets=plaintext_secrets,
635 backend_store=backend_store)
637 raise CommandError("Invalid role '%s' (possible values: DC, RODC)" % role)
640 class cmd_domain_join(Command):
641 """Join domain as either member or backup domain controller."""
643 synopsis = "%prog <dnsdomain> [DC|RODC|MEMBER|SUBDOMAIN] [options]"
645 takes_optiongroups = {
646 "sambaopts": options.SambaOptions,
647 "versionopts": options.VersionOptions,
648 "credopts": options.CredentialsOptions,
652 Option("--server", help="DC to join", type=str),
653 Option("--site", help="site to join", type=str),
654 Option("--parent-domain", help="parent domain to create subdomain under", type=str),
655 Option("--domain-critical-only",
656 help="only replicate critical domain objects",
657 action="store_true"),
658 Option("--adminpass", type="string", metavar="PASSWORD",
659 help="choose adminstrator password when joining as a subdomain (otherwise random)"),
660 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
661 choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
662 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
663 "BIND9_DLZ uses samba4 AD to store zone information, "
664 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
665 default="SAMBA_INTERNAL"),
666 Option("--verbose", help="Be verbose", action="store_true")
670 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
673 takes_options.extend(common_provision_join_options)
675 if samba.is_ntvfs_fileserver_built():
676 takes_options.extend(ntvfs_options)
678 takes_args = ["domain", "role?"]
680 def run(self, domain, role=None, sambaopts=None, credopts=None,
681 versionopts=None, server=None, site=None, targetdir=None,
682 domain_critical_only=False, parent_domain=None, machinepass=None,
683 use_ntvfs=False, dns_backend=None, adminpass=None,
684 quiet=False, verbose=False,
685 plaintext_secrets=False,
687 lp = sambaopts.get_loadparm()
688 creds = credopts.get_credentials(lp)
689 net = Net(creds, lp, server=credopts.ipaddress)
692 site = "Default-First-Site-Name"
694 logger = self.get_logger()
696 logger.setLevel(logging.DEBUG)
698 logger.setLevel(logging.WARNING)
700 logger.setLevel(logging.INFO)
702 netbios_name = lp.get("netbios name")
707 if role is None or role == "MEMBER":
708 (join_password, sid, domain_name) = net.join_member(
709 domain, netbios_name, LIBNET_JOIN_AUTOMATIC,
710 machinepass=machinepass)
712 self.errf.write("Joined domain %s (%s)\n" % (domain_name, sid))
714 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
715 site=site, netbios_name=netbios_name, targetdir=targetdir,
716 domain_critical_only=domain_critical_only,
717 machinepass=machinepass, use_ntvfs=use_ntvfs,
718 dns_backend=dns_backend,
719 plaintext_secrets=plaintext_secrets,
720 backend_store=backend_store)
722 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
723 site=site, netbios_name=netbios_name, targetdir=targetdir,
724 domain_critical_only=domain_critical_only,
725 machinepass=machinepass, use_ntvfs=use_ntvfs,
726 dns_backend=dns_backend,
727 plaintext_secrets=plaintext_secrets,
728 backend_store=backend_store)
729 elif role == "SUBDOMAIN":
731 logger.info("Administrator password will be set randomly!")
733 netbios_domain = lp.get("workgroup")
734 if parent_domain is None:
735 parent_domain = ".".join(domain.split(".")[1:])
736 join_subdomain(logger=logger, server=server, creds=creds, lp=lp, dnsdomain=domain,
737 parent_domain=parent_domain, site=site,
738 netbios_name=netbios_name, netbios_domain=netbios_domain,
739 targetdir=targetdir, machinepass=machinepass,
740 use_ntvfs=use_ntvfs, dns_backend=dns_backend,
742 plaintext_secrets=plaintext_secrets,
743 backend_store=backend_store)
745 raise CommandError("Invalid role '%s' (possible values: MEMBER, DC, RODC, SUBDOMAIN)" % role)
748 class cmd_domain_demote(Command):
749 """Demote ourselves from the role of Domain Controller."""
751 synopsis = "%prog [options]"
754 Option("--server", help="writable DC to write demotion changes on", type=str),
755 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
756 metavar="URL", dest="H"),
757 Option("--remove-other-dead-server", help="Dead DC (name or NTDS GUID) "
758 "to remove ALL references to (rather than this DC)", type=str),
759 Option("--quiet", help="Be quiet", action="store_true"),
760 Option("--verbose", help="Be verbose", action="store_true"),
763 takes_optiongroups = {
764 "sambaopts": options.SambaOptions,
765 "credopts": options.CredentialsOptions,
766 "versionopts": options.VersionOptions,
769 def run(self, sambaopts=None, credopts=None,
770 versionopts=None, server=None,
771 remove_other_dead_server=None, H=None,
772 verbose=False, quiet=False):
773 lp = sambaopts.get_loadparm()
774 creds = credopts.get_credentials(lp)
775 net = Net(creds, lp, server=credopts.ipaddress)
777 logger = self.get_logger()
779 logger.setLevel(logging.DEBUG)
781 logger.setLevel(logging.WARNING)
783 logger.setLevel(logging.INFO)
785 if remove_other_dead_server is not None:
786 if server is not None:
787 samdb = SamDB(url="ldap://%s" % server,
788 session_info=system_session(),
789 credentials=creds, lp=lp)
791 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
793 remove_dc.remove_dc(samdb, logger, remove_other_dead_server)
794 except remove_dc.DemoteException as err:
795 raise CommandError("Demote failed: %s" % err)
798 netbios_name = lp.get("netbios name")
799 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
801 res = samdb.search(expression='(&(objectClass=computer)(serverReferenceBL=*))', attrs=["dnsHostName", "name"])
803 raise CommandError("Unable to search for servers")
806 raise CommandError("You are the latest server in the domain")
810 if str(e["name"]).lower() != netbios_name.lower():
811 server = e["dnsHostName"]
814 ntds_guid = samdb.get_ntds_GUID()
815 msg = samdb.search(base=str(samdb.get_config_basedn()),
816 scope=ldb.SCOPE_SUBTREE, expression="(objectGUID=%s)" % ntds_guid,
818 if len(msg) == 0 or "options" not in msg[0]:
819 raise CommandError("Failed to find options on %s" % ntds_guid)
822 dsa_options = int(str(msg[0]['options']))
824 res = samdb.search(expression="(fSMORoleOwner=%s)" % str(ntds_dn),
825 controls=["search_options:1:2"])
828 raise CommandError("Current DC is still the owner of %d role(s), use the role command to transfer roles to another DC" % len(res))
830 self.errf.write("Using %s as partner server for the demotion\n" %
832 (drsuapiBind, drsuapi_handle, supportedExtensions) = drsuapi_connect(server, lp, creds)
834 self.errf.write("Deactivating inbound replication\n")
839 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
840 dsa_options |= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
841 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
845 self.errf.write("Asking partner server %s to synchronize from us\n"
847 for part in (samdb.get_schema_basedn(),
848 samdb.get_config_basedn(),
849 samdb.get_root_basedn()):
850 nc = drsuapi.DsReplicaObjectIdentifier()
853 req1 = drsuapi.DsReplicaSyncRequest1()
854 req1.naming_context = nc;
855 req1.options = drsuapi.DRSUAPI_DRS_WRIT_REP
856 req1.source_dsa_guid = misc.GUID(ntds_guid)
859 drsuapiBind.DsReplicaSync(drsuapi_handle, 1, req1)
860 except RuntimeError as e1:
861 (werr, string) = e1.args
862 if werr == werror.WERR_DS_DRA_NO_REPLICA:
866 "Error while replicating out last local changes from '%s' for demotion, "
867 "re-enabling inbound replication\n" % part)
868 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
869 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
871 raise CommandError("Error while sending a DsReplicaSync for partition '%s'" % str(part), string)
873 remote_samdb = SamDB(url="ldap://%s" % server,
874 session_info=system_session(),
875 credentials=creds, lp=lp)
877 self.errf.write("Changing userControl and container\n")
878 res = remote_samdb.search(base=str(remote_samdb.domain_dn()),
879 expression="(&(objectClass=user)(sAMAccountName=%s$))" %
880 netbios_name.upper(),
881 attrs=["userAccountControl"])
883 uac = int(str(res[0]["userAccountControl"]))
885 except Exception as e:
886 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
888 "Error while demoting, re-enabling inbound replication\n")
889 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
890 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
892 raise CommandError("Error while changing account control", e)
895 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
897 "Error while demoting, re-enabling inbound replication")
898 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
899 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
901 raise CommandError("Unable to find object with samaccountName = %s$"
902 " in the remote dc" % netbios_name.upper())
906 uac &= ~(UF_SERVER_TRUST_ACCOUNT|UF_TRUSTED_FOR_DELEGATION|UF_PARTIAL_SECRETS_ACCOUNT)
907 uac |= UF_WORKSTATION_TRUST_ACCOUNT
912 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
913 ldb.FLAG_MOD_REPLACE,
914 "userAccountControl")
916 remote_samdb.modify(msg)
917 except Exception as e:
918 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
920 "Error while demoting, re-enabling inbound replication")
921 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
922 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
925 raise CommandError("Error while changing account control", e)
927 parent = msg.dn.parent()
928 dc_name = res[0].dn.get_rdn_value()
929 rdn = "CN=%s" % dc_name
931 # Let's move to the Computer container
935 computer_dn = ldb.Dn(remote_samdb, "CN=Computers,%s" % str(remote_samdb.domain_dn()))
936 res = remote_samdb.search(base=computer_dn, expression=rdn, scope=ldb.SCOPE_ONELEVEL)
939 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
940 scope=ldb.SCOPE_ONELEVEL)
941 while(len(res) != 0 and i < 100):
943 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
944 scope=ldb.SCOPE_ONELEVEL)
947 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
949 "Error while demoting, re-enabling inbound replication\n")
950 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
951 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
957 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
958 ldb.FLAG_MOD_REPLACE,
959 "userAccountControl")
961 remote_samdb.modify(msg)
963 raise CommandError("Unable to find a slot for renaming %s,"
964 " all names from %s-1 to %s-%d seemed used" %
965 (str(dc_dn), rdn, rdn, i - 9))
967 newrdn = "%s-%d" % (rdn, i)
970 newdn = ldb.Dn(remote_samdb, "%s,%s" % (newrdn, str(computer_dn)))
971 remote_samdb.rename(dc_dn, newdn)
972 except Exception as e:
973 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
975 "Error while demoting, re-enabling inbound replication\n")
976 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
977 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
983 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
984 ldb.FLAG_MOD_REPLACE,
985 "userAccountControl")
987 remote_samdb.modify(msg)
988 raise CommandError("Error while renaming %s to %s" % (str(dc_dn), str(newdn)), e)
991 server_dsa_dn = samdb.get_serverName()
992 domain = remote_samdb.get_root_basedn()
995 req1 = drsuapi.DsRemoveDSServerRequest1()
996 req1.server_dn = str(server_dsa_dn)
997 req1.domain_dn = str(domain)
1000 drsuapiBind.DsRemoveDSServer(drsuapi_handle, 1, req1)
1001 except RuntimeError as e3:
1002 (werr, string) = e3.args
1003 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
1005 "Error while demoting, re-enabling inbound replication\n")
1006 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
1007 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
1013 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
1014 ldb.FLAG_MOD_REPLACE,
1015 "userAccountControl")
1016 remote_samdb.modify(msg)
1017 remote_samdb.rename(newdn, dc_dn)
1018 if werr == werror.WERR_DS_DRA_NO_REPLICA:
1019 raise CommandError("The DC %s is not present on (already removed from) the remote server: " % server_dsa_dn, e)
1021 raise CommandError("Error while sending a removeDsServer of %s: " % server_dsa_dn, e)
1023 remove_dc.remove_sysvol_references(remote_samdb, logger, dc_name)
1025 # These are objects under the computer account that should be deleted
1026 for s in ("CN=Enterprise,CN=NTFRS Subscriptions",
1027 "CN=%s, CN=NTFRS Subscriptions" % lp.get("realm"),
1028 "CN=Domain system Volumes (SYSVOL Share), CN=NTFRS Subscriptions",
1029 "CN=NTFRS Subscriptions"):
1031 remote_samdb.delete(ldb.Dn(remote_samdb,
1032 "%s,%s" % (s, str(newdn))))
1033 except ldb.LdbError as l:
1036 self.errf.write("Demote successful\n")
1039 class cmd_domain_level(Command):
1040 """Raise domain and forest function levels."""
1042 synopsis = "%prog (show|raise <options>) [options]"
1044 takes_optiongroups = {
1045 "sambaopts": options.SambaOptions,
1046 "credopts": options.CredentialsOptions,
1047 "versionopts": options.VersionOptions,
1051 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1052 metavar="URL", dest="H"),
1053 Option("--quiet", help="Be quiet", action="store_true"),
1054 Option("--forest-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
1055 help="The forest function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)"),
1056 Option("--domain-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
1057 help="The domain function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)")
1060 takes_args = ["subcommand"]
1062 def run(self, subcommand, H=None, forest_level=None, domain_level=None,
1063 quiet=False, credopts=None, sambaopts=None, versionopts=None):
1064 lp = sambaopts.get_loadparm()
1065 creds = credopts.get_credentials(lp, fallback_machine=True)
1067 samdb = SamDB(url=H, session_info=system_session(),
1068 credentials=creds, lp=lp)
1070 domain_dn = samdb.domain_dn()
1072 res_forest = samdb.search("CN=Partitions,%s" % samdb.get_config_basedn(),
1073 scope=ldb.SCOPE_BASE, attrs=["msDS-Behavior-Version"])
1074 assert len(res_forest) == 1
1076 res_domain = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1077 attrs=["msDS-Behavior-Version", "nTMixedDomain"])
1078 assert len(res_domain) == 1
1080 res_dc_s = samdb.search("CN=Sites,%s" % samdb.get_config_basedn(),
1081 scope=ldb.SCOPE_SUBTREE, expression="(objectClass=nTDSDSA)",
1082 attrs=["msDS-Behavior-Version"])
1083 assert len(res_dc_s) >= 1
1085 # default values, since "msDS-Behavior-Version" does not exist on Windows 2000 AD
1086 level_forest = DS_DOMAIN_FUNCTION_2000
1087 level_domain = DS_DOMAIN_FUNCTION_2000
1089 if "msDS-Behavior-Version" in res_forest[0]:
1090 level_forest = int(res_forest[0]["msDS-Behavior-Version"][0])
1091 if "msDS-Behavior-Version" in res_domain[0]:
1092 level_domain = int(res_domain[0]["msDS-Behavior-Version"][0])
1093 level_domain_mixed = int(res_domain[0]["nTMixedDomain"][0])
1096 for msg in res_dc_s:
1097 if "msDS-Behavior-Version" in msg:
1098 if min_level_dc is None or int(msg["msDS-Behavior-Version"][0]) < min_level_dc:
1099 min_level_dc = int(msg["msDS-Behavior-Version"][0])
1101 min_level_dc = DS_DOMAIN_FUNCTION_2000
1102 # well, this is the least
1105 if level_forest < DS_DOMAIN_FUNCTION_2000 or level_domain < DS_DOMAIN_FUNCTION_2000:
1106 raise CommandError("Domain and/or forest function level(s) is/are invalid. Correct them or reprovision!")
1107 if min_level_dc < DS_DOMAIN_FUNCTION_2000:
1108 raise CommandError("Lowest function level of a DC is invalid. Correct this or reprovision!")
1109 if level_forest > level_domain:
1110 raise CommandError("Forest function level is higher than the domain level(s). Correct this or reprovision!")
1111 if level_domain > min_level_dc:
1112 raise CommandError("Domain function level is higher than the lowest function level of a DC. Correct this or reprovision!")
1114 if subcommand == "show":
1115 self.message("Domain and forest function level for domain '%s'" % domain_dn)
1116 if level_forest == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1117 self.message("\nATTENTION: You run SAMBA 4 on a forest function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1118 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1119 self.message("\nATTENTION: You run SAMBA 4 on a domain function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1120 if min_level_dc == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1121 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)!")
1125 if level_forest == DS_DOMAIN_FUNCTION_2000:
1127 elif level_forest == DS_DOMAIN_FUNCTION_2003_MIXED:
1128 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1129 elif level_forest == DS_DOMAIN_FUNCTION_2003:
1131 elif level_forest == DS_DOMAIN_FUNCTION_2008:
1133 elif level_forest == DS_DOMAIN_FUNCTION_2008_R2:
1135 elif level_forest == DS_DOMAIN_FUNCTION_2012:
1137 elif level_forest == DS_DOMAIN_FUNCTION_2012_R2:
1140 outstr = "higher than 2012 R2"
1141 self.message("Forest function level: (Windows) " + outstr)
1143 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1144 outstr = "2000 mixed (NT4 DC support)"
1145 elif level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed == 0:
1147 elif level_domain == DS_DOMAIN_FUNCTION_2003_MIXED:
1148 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1149 elif level_domain == DS_DOMAIN_FUNCTION_2003:
1151 elif level_domain == DS_DOMAIN_FUNCTION_2008:
1153 elif level_domain == DS_DOMAIN_FUNCTION_2008_R2:
1155 elif level_domain == DS_DOMAIN_FUNCTION_2012:
1157 elif level_domain == DS_DOMAIN_FUNCTION_2012_R2:
1160 outstr = "higher than 2012 R2"
1161 self.message("Domain function level: (Windows) " + outstr)
1163 if min_level_dc == DS_DOMAIN_FUNCTION_2000:
1165 elif min_level_dc == DS_DOMAIN_FUNCTION_2003:
1167 elif min_level_dc == DS_DOMAIN_FUNCTION_2008:
1169 elif min_level_dc == DS_DOMAIN_FUNCTION_2008_R2:
1171 elif min_level_dc == DS_DOMAIN_FUNCTION_2012:
1173 elif min_level_dc == DS_DOMAIN_FUNCTION_2012_R2:
1176 outstr = "higher than 2012 R2"
1177 self.message("Lowest function level of a DC: (Windows) " + outstr)
1179 elif subcommand == "raise":
1182 if domain_level is not None:
1183 if domain_level == "2003":
1184 new_level_domain = DS_DOMAIN_FUNCTION_2003
1185 elif domain_level == "2008":
1186 new_level_domain = DS_DOMAIN_FUNCTION_2008
1187 elif domain_level == "2008_R2":
1188 new_level_domain = DS_DOMAIN_FUNCTION_2008_R2
1189 elif domain_level == "2012":
1190 new_level_domain = DS_DOMAIN_FUNCTION_2012
1191 elif domain_level == "2012_R2":
1192 new_level_domain = DS_DOMAIN_FUNCTION_2012_R2
1194 if new_level_domain <= level_domain and level_domain_mixed == 0:
1195 raise CommandError("Domain function level can't be smaller than or equal to the actual one!")
1196 if new_level_domain > min_level_dc:
1197 raise CommandError("Domain function level can't be higher than the lowest function level of a DC!")
1199 # Deactivate mixed/interim domain support
1200 if level_domain_mixed != 0:
1201 # Directly on the base DN
1203 m.dn = ldb.Dn(samdb, domain_dn)
1204 m["nTMixedDomain"] = ldb.MessageElement("0",
1205 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1209 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup") + ",CN=Partitions,%s" % samdb.get_config_basedn())
1210 m["nTMixedDomain"] = ldb.MessageElement("0",
1211 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1214 except ldb.LdbError as e:
1215 (enum, emsg) = e.args
1216 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1219 # Directly on the base DN
1221 m.dn = ldb.Dn(samdb, domain_dn)
1222 m["msDS-Behavior-Version"]= ldb.MessageElement(
1223 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1224 "msDS-Behavior-Version")
1228 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup")
1229 + ",CN=Partitions,%s" % samdb.get_config_basedn())
1230 m["msDS-Behavior-Version"]= ldb.MessageElement(
1231 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1232 "msDS-Behavior-Version")
1235 except ldb.LdbError as e2:
1236 (enum, emsg) = e2.args
1237 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1240 level_domain = new_level_domain
1241 msgs.append("Domain function level changed!")
1243 if forest_level is not None:
1244 if forest_level == "2003":
1245 new_level_forest = DS_DOMAIN_FUNCTION_2003
1246 elif forest_level == "2008":
1247 new_level_forest = DS_DOMAIN_FUNCTION_2008
1248 elif forest_level == "2008_R2":
1249 new_level_forest = DS_DOMAIN_FUNCTION_2008_R2
1250 elif forest_level == "2012":
1251 new_level_forest = DS_DOMAIN_FUNCTION_2012
1252 elif forest_level == "2012_R2":
1253 new_level_forest = DS_DOMAIN_FUNCTION_2012_R2
1255 if new_level_forest <= level_forest:
1256 raise CommandError("Forest function level can't be smaller than or equal to the actual one!")
1257 if new_level_forest > level_domain:
1258 raise CommandError("Forest function level can't be higher than the domain function level(s). Please raise it/them first!")
1261 m.dn = ldb.Dn(samdb, "CN=Partitions,%s" % samdb.get_config_basedn())
1262 m["msDS-Behavior-Version"]= ldb.MessageElement(
1263 str(new_level_forest), ldb.FLAG_MOD_REPLACE,
1264 "msDS-Behavior-Version")
1266 msgs.append("Forest function level changed!")
1267 msgs.append("All changes applied successfully!")
1268 self.message("\n".join(msgs))
1270 raise CommandError("invalid argument: '%s' (choose from 'show', 'raise')" % subcommand)
1272 class cmd_domain_passwordsettings_show(Command):
1273 """Display current password settings for the domain."""
1275 synopsis = "%prog [options]"
1277 takes_optiongroups = {
1278 "sambaopts": options.SambaOptions,
1279 "versionopts": options.VersionOptions,
1280 "credopts": options.CredentialsOptions,
1284 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1285 metavar="URL", dest="H"),
1288 def run(self, H=None, credopts=None, sambaopts=None, versionopts=None):
1289 lp = sambaopts.get_loadparm()
1290 creds = credopts.get_credentials(lp)
1292 samdb = SamDB(url=H, session_info=system_session(),
1293 credentials=creds, lp=lp)
1295 domain_dn = samdb.domain_dn()
1296 res = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1297 attrs=["pwdProperties", "pwdHistoryLength", "minPwdLength",
1298 "minPwdAge", "maxPwdAge", "lockoutDuration", "lockoutThreshold",
1299 "lockOutObservationWindow"])
1300 assert(len(res) == 1)
1302 pwd_props = int(res[0]["pwdProperties"][0])
1303 pwd_hist_len = int(res[0]["pwdHistoryLength"][0])
1304 cur_min_pwd_len = int(res[0]["minPwdLength"][0])
1306 cur_min_pwd_age = int(abs(int(res[0]["minPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1307 if int(res[0]["maxPwdAge"][0]) == -0x8000000000000000:
1310 cur_max_pwd_age = int(abs(int(res[0]["maxPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1311 cur_account_lockout_threshold = int(res[0]["lockoutThreshold"][0])
1313 if int(res[0]["lockoutDuration"][0]) == -0x8000000000000000:
1314 cur_account_lockout_duration = 0
1316 cur_account_lockout_duration = abs(int(res[0]["lockoutDuration"][0])) / (1e7 * 60)
1317 cur_reset_account_lockout_after = abs(int(res[0]["lockOutObservationWindow"][0])) / (1e7 * 60)
1318 except Exception as e:
1319 raise CommandError("Could not retrieve password properties!", e)
1321 self.message("Password informations for domain '%s'" % domain_dn)
1323 if pwd_props & DOMAIN_PASSWORD_COMPLEX != 0:
1324 self.message("Password complexity: on")
1326 self.message("Password complexity: off")
1327 if pwd_props & DOMAIN_PASSWORD_STORE_CLEARTEXT != 0:
1328 self.message("Store plaintext passwords: on")
1330 self.message("Store plaintext passwords: off")
1331 self.message("Password history length: %d" % pwd_hist_len)
1332 self.message("Minimum password length: %d" % cur_min_pwd_len)
1333 self.message("Minimum password age (days): %d" % cur_min_pwd_age)
1334 self.message("Maximum password age (days): %d" % cur_max_pwd_age)
1335 self.message("Account lockout duration (mins): %d" % cur_account_lockout_duration)
1336 self.message("Account lockout threshold (attempts): %d" % cur_account_lockout_threshold)
1337 self.message("Reset account lockout after (mins): %d" % cur_reset_account_lockout_after)
1339 class cmd_domain_passwordsettings_set(Command):
1340 """Set password settings.
1342 Password complexity, password lockout policy, history length,
1343 minimum password length, the minimum and maximum password age) on
1344 a Samba AD DC server.
1346 Use against a Windows DC is possible, but group policy will override it.
1349 synopsis = "%prog <options> [options]"
1351 takes_optiongroups = {
1352 "sambaopts": options.SambaOptions,
1353 "versionopts": options.VersionOptions,
1354 "credopts": options.CredentialsOptions,
1358 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1359 metavar="URL", dest="H"),
1360 Option("--quiet", help="Be quiet", action="store_true"),
1361 Option("--complexity", type="choice", choices=["on","off","default"],
1362 help="The password complexity (on | off | default). Default is 'on'"),
1363 Option("--store-plaintext", type="choice", choices=["on","off","default"],
1364 help="Store plaintext passwords where account have 'store passwords with reversible encryption' set (on | off | default). Default is 'off'"),
1365 Option("--history-length",
1366 help="The password history length (<integer> | default). Default is 24.", type=str),
1367 Option("--min-pwd-length",
1368 help="The minimum password length (<integer> | default). Default is 7.", type=str),
1369 Option("--min-pwd-age",
1370 help="The minimum password age (<integer in days> | default). Default is 1.", type=str),
1371 Option("--max-pwd-age",
1372 help="The maximum password age (<integer in days> | default). Default is 43.", type=str),
1373 Option("--account-lockout-duration",
1374 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),
1375 Option("--account-lockout-threshold",
1376 help="The number of bad password attempts allowed before locking out the account (<integer> | default). Default is 0 (never lock out).", type=str),
1377 Option("--reset-account-lockout-after",
1378 help="After this time is elapsed, the recorded number of attempts restarts from zero (<integer> | default). Default is 30.", type=str),
1381 def run(self, H=None, min_pwd_age=None, max_pwd_age=None,
1382 quiet=False, complexity=None, store_plaintext=None, history_length=None,
1383 min_pwd_length=None, account_lockout_duration=None, account_lockout_threshold=None,
1384 reset_account_lockout_after=None, credopts=None, sambaopts=None,
1386 lp = sambaopts.get_loadparm()
1387 creds = credopts.get_credentials(lp)
1389 samdb = SamDB(url=H, session_info=system_session(),
1390 credentials=creds, lp=lp)
1392 domain_dn = samdb.domain_dn()
1395 m.dn = ldb.Dn(samdb, domain_dn)
1396 pwd_props = int(samdb.get_pwdProperties())
1398 if complexity is not None:
1399 if complexity == "on" or complexity == "default":
1400 pwd_props = pwd_props | DOMAIN_PASSWORD_COMPLEX
1401 msgs.append("Password complexity activated!")
1402 elif complexity == "off":
1403 pwd_props = pwd_props & (~DOMAIN_PASSWORD_COMPLEX)
1404 msgs.append("Password complexity deactivated!")
1406 if store_plaintext is not None:
1407 if store_plaintext == "on" or store_plaintext == "default":
1408 pwd_props = pwd_props | DOMAIN_PASSWORD_STORE_CLEARTEXT
1409 msgs.append("Plaintext password storage for changed passwords activated!")
1410 elif store_plaintext == "off":
1411 pwd_props = pwd_props & (~DOMAIN_PASSWORD_STORE_CLEARTEXT)
1412 msgs.append("Plaintext password storage for changed passwords deactivated!")
1414 if complexity is not None or store_plaintext is not None:
1415 m["pwdProperties"] = ldb.MessageElement(str(pwd_props),
1416 ldb.FLAG_MOD_REPLACE, "pwdProperties")
1418 if history_length is not None:
1419 if history_length == "default":
1422 pwd_hist_len = int(history_length)
1424 if pwd_hist_len < 0 or pwd_hist_len > 24:
1425 raise CommandError("Password history length must be in the range of 0 to 24!")
1427 m["pwdHistoryLength"] = ldb.MessageElement(str(pwd_hist_len),
1428 ldb.FLAG_MOD_REPLACE, "pwdHistoryLength")
1429 msgs.append("Password history length changed!")
1431 if min_pwd_length is not None:
1432 if min_pwd_length == "default":
1435 min_pwd_len = int(min_pwd_length)
1437 if min_pwd_len < 0 or min_pwd_len > 14:
1438 raise CommandError("Minimum password length must be in the range of 0 to 14!")
1440 m["minPwdLength"] = ldb.MessageElement(str(min_pwd_len),
1441 ldb.FLAG_MOD_REPLACE, "minPwdLength")
1442 msgs.append("Minimum password length changed!")
1444 if min_pwd_age is not None:
1445 if min_pwd_age == "default":
1448 min_pwd_age = int(min_pwd_age)
1450 if min_pwd_age < 0 or min_pwd_age > 998:
1451 raise CommandError("Minimum password age must be in the range of 0 to 998!")
1454 min_pwd_age_ticks = -int(min_pwd_age * (24 * 60 * 60 * 1e7))
1456 m["minPwdAge"] = ldb.MessageElement(str(min_pwd_age_ticks),
1457 ldb.FLAG_MOD_REPLACE, "minPwdAge")
1458 msgs.append("Minimum password age changed!")
1460 if max_pwd_age is not None:
1461 if max_pwd_age == "default":
1464 max_pwd_age = int(max_pwd_age)
1466 if max_pwd_age < 0 or max_pwd_age > 999:
1467 raise CommandError("Maximum password age must be in the range of 0 to 999!")
1470 if max_pwd_age == 0:
1471 max_pwd_age_ticks = -0x8000000000000000
1473 max_pwd_age_ticks = -int(max_pwd_age * (24 * 60 * 60 * 1e7))
1475 m["maxPwdAge"] = ldb.MessageElement(str(max_pwd_age_ticks),
1476 ldb.FLAG_MOD_REPLACE, "maxPwdAge")
1477 msgs.append("Maximum password age changed!")
1479 if account_lockout_duration is not None:
1480 if account_lockout_duration == "default":
1481 account_lockout_duration = 30
1483 account_lockout_duration = int(account_lockout_duration)
1485 if account_lockout_duration < 0 or account_lockout_duration > 99999:
1486 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1489 if account_lockout_duration == 0:
1490 account_lockout_duration_ticks = -0x8000000000000000
1492 account_lockout_duration_ticks = -int(account_lockout_duration * (60 * 1e7))
1494 m["lockoutDuration"] = ldb.MessageElement(str(account_lockout_duration_ticks),
1495 ldb.FLAG_MOD_REPLACE, "lockoutDuration")
1496 msgs.append("Account lockout duration changed!")
1498 if account_lockout_threshold is not None:
1499 if account_lockout_threshold == "default":
1500 account_lockout_threshold = 0
1502 account_lockout_threshold = int(account_lockout_threshold)
1504 m["lockoutThreshold"] = ldb.MessageElement(str(account_lockout_threshold),
1505 ldb.FLAG_MOD_REPLACE, "lockoutThreshold")
1506 msgs.append("Account lockout threshold changed!")
1508 if reset_account_lockout_after is not None:
1509 if reset_account_lockout_after == "default":
1510 reset_account_lockout_after = 30
1512 reset_account_lockout_after = int(reset_account_lockout_after)
1514 if reset_account_lockout_after < 0 or reset_account_lockout_after > 99999:
1515 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1518 if reset_account_lockout_after == 0:
1519 reset_account_lockout_after_ticks = -0x8000000000000000
1521 reset_account_lockout_after_ticks = -int(reset_account_lockout_after * (60 * 1e7))
1523 m["lockOutObservationWindow"] = ldb.MessageElement(str(reset_account_lockout_after_ticks),
1524 ldb.FLAG_MOD_REPLACE, "lockOutObservationWindow")
1525 msgs.append("Duration to reset account lockout after changed!")
1527 if max_pwd_age > 0 and min_pwd_age >= max_pwd_age:
1528 raise CommandError("Maximum password age (%d) must be greater than minimum password age (%d)!" % (max_pwd_age, min_pwd_age))
1531 raise CommandError("You must specify at least one option to set. Try --help")
1533 msgs.append("All changes applied successfully!")
1534 self.message("\n".join(msgs))
1536 class cmd_domain_passwordsettings(SuperCommand):
1537 """Manage password policy settings."""
1540 subcommands["show"] = cmd_domain_passwordsettings_show()
1541 subcommands["set"] = cmd_domain_passwordsettings_set()
1543 class cmd_domain_classicupgrade(Command):
1544 """Upgrade from Samba classic (NT4-like) database to Samba AD DC database.
1546 Specify either a directory with all Samba classic DC databases and state files (with --dbdir) or
1547 the testparm utility from your classic installation (with --testparm).
1550 synopsis = "%prog [options] <classic_smb_conf>"
1552 takes_optiongroups = {
1553 "sambaopts": options.SambaOptions,
1554 "versionopts": options.VersionOptions
1558 Option("--dbdir", type="string", metavar="DIR",
1559 help="Path to samba classic DC database directory"),
1560 Option("--testparm", type="string", metavar="PATH",
1561 help="Path to samba classic DC testparm utility from the previous installation. This allows the default paths of the previous installation to be followed"),
1562 Option("--targetdir", type="string", metavar="DIR",
1563 help="Path prefix where the new Samba 4.0 AD domain should be initialised"),
1564 Option("--quiet", help="Be quiet", action="store_true"),
1565 Option("--verbose", help="Be verbose", action="store_true"),
1566 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
1567 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
1568 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
1569 "BIND9_FLATFILE uses bind9 text database to store zone information, "
1570 "BIND9_DLZ uses samba4 AD to store zone information, "
1571 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
1572 default="SAMBA_INTERNAL")
1576 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
1577 action="store_true"),
1578 Option("--use-xattrs", type="choice", choices=["yes","no","auto"],
1579 metavar="[yes|no|auto]",
1580 help="Define if we should use the native fs capabilities or a tdb file for "
1581 "storing attributes likes ntacl when --use-ntvfs is set. "
1582 "auto tries to make an inteligent guess based on the user rights and system capabilities",
1585 if samba.is_ntvfs_fileserver_built():
1586 takes_options.extend(ntvfs_options)
1588 takes_args = ["smbconf"]
1590 def run(self, smbconf=None, targetdir=None, dbdir=None, testparm=None,
1591 quiet=False, verbose=False, use_xattrs="auto", sambaopts=None, versionopts=None,
1592 dns_backend=None, use_ntvfs=False):
1594 if not os.path.exists(smbconf):
1595 raise CommandError("File %s does not exist" % smbconf)
1597 if testparm and not os.path.exists(testparm):
1598 raise CommandError("Testparm utility %s does not exist" % testparm)
1600 if dbdir and not os.path.exists(dbdir):
1601 raise CommandError("Directory %s does not exist" % dbdir)
1603 if not dbdir and not testparm:
1604 raise CommandError("Please specify either dbdir or testparm")
1606 logger = self.get_logger()
1608 logger.setLevel(logging.DEBUG)
1610 logger.setLevel(logging.WARNING)
1612 logger.setLevel(logging.INFO)
1614 if dbdir and testparm:
1615 logger.warning("both dbdir and testparm specified, ignoring dbdir.")
1618 lp = sambaopts.get_loadparm()
1620 s3conf = s3param.get_context()
1623 s3conf.set("realm", sambaopts.realm)
1625 if targetdir is not None:
1626 if not os.path.isdir(targetdir):
1630 if use_xattrs == "yes":
1632 elif use_xattrs == "auto" and use_ntvfs == False:
1634 elif use_ntvfs == False:
1635 raise CommandError("--use-xattrs=no requires --use-ntvfs (not supported for production use). "
1636 "Please re-run with --use-xattrs omitted.")
1637 elif use_xattrs == "auto" and not s3conf.get("posix:eadb"):
1639 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
1641 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
1644 samba.ntacls.setntacl(lp, tmpfile.name,
1645 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
1648 # FIXME: Don't catch all exceptions here
1649 logger.info("You are not root or your system does not support xattr, using tdb backend for attributes. "
1650 "If you intend to use this provision in production, rerun the script as root on a system supporting xattrs.")
1654 # Set correct default values from dbdir or testparm
1657 paths["state directory"] = dbdir
1658 paths["private dir"] = dbdir
1659 paths["lock directory"] = dbdir
1660 paths["smb passwd file"] = dbdir + "/smbpasswd"
1662 paths["state directory"] = get_testparm_var(testparm, smbconf, "state directory")
1663 paths["private dir"] = get_testparm_var(testparm, smbconf, "private dir")
1664 paths["smb passwd file"] = get_testparm_var(testparm, smbconf, "smb passwd file")
1665 paths["lock directory"] = get_testparm_var(testparm, smbconf, "lock directory")
1666 # "testparm" from Samba 3 < 3.4.x is not aware of the parameter
1667 # "state directory", instead make use of "lock directory"
1668 if len(paths["state directory"]) == 0:
1669 paths["state directory"] = paths["lock directory"]
1672 s3conf.set(p, paths[p])
1674 # load smb.conf parameters
1675 logger.info("Reading smb.conf")
1676 s3conf.load(smbconf)
1677 samba3 = Samba3(smbconf, s3conf)
1679 logger.info("Provisioning")
1680 upgrade_from_samba3(samba3, logger, targetdir, session_info=system_session(),
1681 useeadb=eadb, dns_backend=dns_backend, use_ntvfs=use_ntvfs)
1684 class cmd_domain_samba3upgrade(cmd_domain_classicupgrade):
1685 __doc__ = cmd_domain_classicupgrade.__doc__
1687 # This command is present for backwards compatibility only,
1688 # and should not be shown.
1692 class LocalDCCredentialsOptions(options.CredentialsOptions):
1693 def __init__(self, parser):
1694 options.CredentialsOptions.__init__(self, parser, special_name="local-dc")
1696 class DomainTrustCommand(Command):
1697 """List domain trusts."""
1700 Command.__init__(self)
1701 self.local_lp = None
1703 self.local_server = None
1704 self.local_binding_string = None
1705 self.local_creds = None
1707 self.remote_server = None
1708 self.remote_binding_string = None
1709 self.remote_creds = None
1711 def _uint32(self, v):
1712 return ctypes.c_uint32(v).value
1714 def check_runtime_error(self, runtime, val):
1718 err32 = self._uint32(runtime[0])
1724 class LocalRuntimeError(CommandError):
1725 def __init__(exception_self, self, runtime, message):
1726 err32 = self._uint32(runtime[0])
1728 msg = "LOCAL_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1729 self.local_server, message, err32, errstr)
1730 CommandError.__init__(exception_self, msg)
1732 class RemoteRuntimeError(CommandError):
1733 def __init__(exception_self, self, runtime, message):
1734 err32 = self._uint32(runtime[0])
1736 msg = "REMOTE_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1737 self.remote_server, message, err32, errstr)
1738 CommandError.__init__(exception_self, msg)
1740 class LocalLdbError(CommandError):
1741 def __init__(exception_self, self, ldb_error, message):
1742 errval = ldb_error[0]
1743 errstr = ldb_error[1]
1744 msg = "LOCAL_DC[%s]: %s - ERROR(%d) - %s" % (
1745 self.local_server, message, errval, errstr)
1746 CommandError.__init__(exception_self, msg)
1748 def setup_local_server(self, sambaopts, localdcopts):
1749 if self.local_server is not None:
1750 return self.local_server
1752 lp = sambaopts.get_loadparm()
1754 local_server = localdcopts.ipaddress
1755 if local_server is None:
1756 server_role = lp.server_role()
1757 if server_role != "ROLE_ACTIVE_DIRECTORY_DC":
1758 raise CommandError("Invalid server_role %s" % (server_role))
1759 local_server = lp.get('netbios name')
1760 local_transport = "ncalrpc"
1761 local_binding_options = ""
1762 local_binding_options += ",auth_type=ncalrpc_as_system"
1763 local_ldap_url = None
1766 local_transport = "ncacn_np"
1767 local_binding_options = ""
1768 local_ldap_url = "ldap://%s" % local_server
1769 local_creds = localdcopts.get_credentials(lp)
1773 self.local_server = local_server
1774 self.local_binding_string = "%s:%s[%s]" % (local_transport, local_server, local_binding_options)
1775 self.local_ldap_url = local_ldap_url
1776 self.local_creds = local_creds
1777 return self.local_server
1779 def new_local_lsa_connection(self):
1780 return lsa.lsarpc(self.local_binding_string, self.local_lp, self.local_creds)
1782 def new_local_netlogon_connection(self):
1783 return netlogon.netlogon(self.local_binding_string, self.local_lp, self.local_creds)
1785 def new_local_ldap_connection(self):
1786 return SamDB(url=self.local_ldap_url,
1787 session_info=system_session(),
1788 credentials=self.local_creds,
1791 def setup_remote_server(self, credopts, domain,
1793 require_writable=True):
1796 assert require_writable
1798 if self.remote_server is not None:
1799 return self.remote_server
1801 self.remote_server = "__unknown__remote_server__.%s" % domain
1802 assert self.local_server is not None
1804 remote_creds = credopts.get_credentials(self.local_lp)
1805 remote_server = credopts.ipaddress
1806 remote_binding_options = ""
1808 # TODO: we should also support NT4 domains
1809 # we could use local_netlogon.netr_DsRGetDCNameEx2() with the remote domain name
1810 # and delegate NBT or CLDAP to the local netlogon server
1812 remote_net = Net(remote_creds, self.local_lp, server=remote_server)
1813 remote_flags = nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS
1814 if require_writable:
1815 remote_flags |= nbt.NBT_SERVER_WRITABLE
1817 remote_flags |= nbt.NBT_SERVER_PDC
1818 remote_info = remote_net.finddc(flags=remote_flags, domain=domain, address=remote_server)
1819 except NTSTATUSError as error:
1820 raise CommandError("Failed to find a writeable DC for domain '%s': %s" %
1823 raise CommandError("Failed to find a writeable DC for domain '%s'" % domain)
1825 nbt.NBT_SERVER_PDC: "PDC",
1826 nbt.NBT_SERVER_GC: "GC",
1827 nbt.NBT_SERVER_LDAP: "LDAP",
1828 nbt.NBT_SERVER_DS: "DS",
1829 nbt.NBT_SERVER_KDC: "KDC",
1830 nbt.NBT_SERVER_TIMESERV: "TIMESERV",
1831 nbt.NBT_SERVER_CLOSEST: "CLOSEST",
1832 nbt.NBT_SERVER_WRITABLE: "WRITABLE",
1833 nbt.NBT_SERVER_GOOD_TIMESERV: "GOOD_TIMESERV",
1834 nbt.NBT_SERVER_NDNC: "NDNC",
1835 nbt.NBT_SERVER_SELECT_SECRET_DOMAIN_6: "SELECT_SECRET_DOMAIN_6",
1836 nbt.NBT_SERVER_FULL_SECRET_DOMAIN_6: "FULL_SECRET_DOMAIN_6",
1837 nbt.NBT_SERVER_ADS_WEB_SERVICE: "ADS_WEB_SERVICE",
1838 nbt.NBT_SERVER_DS_8: "DS_8",
1839 nbt.NBT_SERVER_HAS_DNS_NAME: "HAS_DNS_NAME",
1840 nbt.NBT_SERVER_IS_DEFAULT_NC: "IS_DEFAULT_NC",
1841 nbt.NBT_SERVER_FOREST_ROOT: "FOREST_ROOT",
1843 server_type_string = self.generic_bitmap_to_string(flag_map,
1844 remote_info.server_type, names_only=True)
1845 self.outf.write("RemoteDC Netbios[%s] DNS[%s] ServerType[%s]\n" % (
1846 remote_info.pdc_name,
1847 remote_info.pdc_dns_name,
1848 server_type_string))
1850 self.remote_server = remote_info.pdc_dns_name
1851 self.remote_binding_string="ncacn_np:%s[%s]" % (self.remote_server, remote_binding_options)
1852 self.remote_creds = remote_creds
1853 return self.remote_server
1855 def new_remote_lsa_connection(self):
1856 return lsa.lsarpc(self.remote_binding_string, self.local_lp, self.remote_creds)
1858 def new_remote_netlogon_connection(self):
1859 return netlogon.netlogon(self.remote_binding_string, self.local_lp, self.remote_creds)
1861 def get_lsa_info(self, conn, policy_access):
1862 objectAttr = lsa.ObjectAttribute()
1863 objectAttr.sec_qos = lsa.QosInfo()
1865 policy = conn.OpenPolicy2(''.decode('utf-8'),
1866 objectAttr, policy_access)
1868 info = conn.QueryInfoPolicy2(policy, lsa.LSA_POLICY_INFO_DNS)
1870 return (policy, info)
1872 def get_netlogon_dc_info(self, conn, server):
1873 info = conn.netr_DsRGetDCNameEx2(server,
1874 None, 0, None, None, None,
1875 netlogon.DS_RETURN_DNS_NAME)
1878 def netr_DomainTrust_to_name(self, t):
1879 if t.trust_type == lsa.LSA_TRUST_TYPE_DOWNLEVEL:
1880 return t.netbios_name
1884 def netr_DomainTrust_to_type(self, a, t):
1886 primary_parent = None
1888 if _t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
1890 if not _t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1891 primary_parent = a[_t.parent_index]
1894 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1895 if t is primary_parent:
1898 if t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1901 parent = a[t.parent_index]
1902 if parent is primary:
1907 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1912 def netr_DomainTrust_to_transitive(self, t):
1913 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1916 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE:
1919 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1924 def netr_DomainTrust_to_direction(self, t):
1925 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND and \
1926 t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1929 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND:
1932 if t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1937 def generic_enum_to_string(self, e_dict, v, names_only=False):
1941 v32 = self._uint32(v)
1942 w = "__unknown__%08X__" % v32
1944 r = "0x%x (%s)" % (v, w)
1947 def generic_bitmap_to_string(self, b_dict, v, names_only=False):
1952 for b in sorted(b_dict.keys()):
1959 c32 = self._uint32(c)
1960 s += ["__unknown_%08X__" % c32]
1965 r = "0x%x (%s)" % (v, w)
1968 def trustType_string(self, v):
1970 lsa.LSA_TRUST_TYPE_DOWNLEVEL : "DOWNLEVEL",
1971 lsa.LSA_TRUST_TYPE_UPLEVEL : "UPLEVEL",
1972 lsa.LSA_TRUST_TYPE_MIT : "MIT",
1973 lsa.LSA_TRUST_TYPE_DCE : "DCE",
1975 return self.generic_enum_to_string(types, v)
1977 def trustDirection_string(self, v):
1979 lsa.LSA_TRUST_DIRECTION_INBOUND |
1980 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "BOTH",
1981 lsa.LSA_TRUST_DIRECTION_INBOUND : "INBOUND",
1982 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "OUTBOUND",
1984 return self.generic_enum_to_string(directions, v)
1986 def trustAttributes_string(self, v):
1988 lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE : "NON_TRANSITIVE",
1989 lsa.LSA_TRUST_ATTRIBUTE_UPLEVEL_ONLY : "UPLEVEL_ONLY",
1990 lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN : "QUARANTINED_DOMAIN",
1991 lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE : "FOREST_TRANSITIVE",
1992 lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION : "CROSS_ORGANIZATION",
1993 lsa.LSA_TRUST_ATTRIBUTE_WITHIN_FOREST : "WITHIN_FOREST",
1994 lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL : "TREAT_AS_EXTERNAL",
1995 lsa.LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION : "USES_RC4_ENCRYPTION",
1997 return self.generic_bitmap_to_string(attributes, v)
1999 def kerb_EncTypes_string(self, v):
2001 security.KERB_ENCTYPE_DES_CBC_CRC : "DES_CBC_CRC",
2002 security.KERB_ENCTYPE_DES_CBC_MD5 : "DES_CBC_MD5",
2003 security.KERB_ENCTYPE_RC4_HMAC_MD5 : "RC4_HMAC_MD5",
2004 security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96 : "AES128_CTS_HMAC_SHA1_96",
2005 security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96 : "AES256_CTS_HMAC_SHA1_96",
2006 security.KERB_ENCTYPE_FAST_SUPPORTED : "FAST_SUPPORTED",
2007 security.KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED : "COMPOUND_IDENTITY_SUPPORTED",
2008 security.KERB_ENCTYPE_CLAIMS_SUPPORTED : "CLAIMS_SUPPORTED",
2009 security.KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED : "RESOURCE_SID_COMPRESSION_DISABLED",
2011 return self.generic_bitmap_to_string(enctypes, v)
2013 def entry_tln_status(self, e_flags, ):
2015 return "Status[Enabled]"
2018 lsa.LSA_TLN_DISABLED_NEW : "Disabled-New",
2019 lsa.LSA_TLN_DISABLED_ADMIN : "Disabled",
2020 lsa.LSA_TLN_DISABLED_CONFLICT : "Disabled-Conflicting",
2022 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
2024 def entry_dom_status(self, e_flags):
2026 return "Status[Enabled]"
2029 lsa.LSA_SID_DISABLED_ADMIN : "Disabled-SID",
2030 lsa.LSA_SID_DISABLED_CONFLICT : "Disabled-SID-Conflicting",
2031 lsa.LSA_NB_DISABLED_ADMIN : "Disabled-NB",
2032 lsa.LSA_NB_DISABLED_CONFLICT : "Disabled-NB-Conflicting",
2034 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
2036 def write_forest_trust_info(self, fti, tln=None, collisions=None):
2038 tln_string = " TDO[%s]" % tln
2042 self.outf.write("Namespaces[%d]%s:\n" % (
2043 len(fti.entries), tln_string))
2045 for i in xrange(0, len(fti.entries)):
2049 collision_string = ""
2051 if collisions is not None:
2052 for c in collisions.entries:
2056 collision_string = " Collision[%s]" % (c.name.string)
2058 d = e.forest_trust_data
2059 if e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
2060 self.outf.write("TLN: %-32s DNS[*.%s]%s\n" % (
2061 self.entry_tln_status(flags),
2062 d.string, collision_string))
2063 elif e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
2064 self.outf.write("TLN_EX: %-29s DNS[*.%s]\n" % (
2066 elif e.type == lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
2067 self.outf.write("DOM: %-32s DNS[%s] Netbios[%s] SID[%s]%s\n" % (
2068 self.entry_dom_status(flags),
2069 d.dns_domain_name.string,
2070 d.netbios_domain_name.string,
2071 d.domain_sid, collision_string))
2074 class cmd_domain_trust_list(DomainTrustCommand):
2075 """List domain trusts."""
2077 synopsis = "%prog [options]"
2079 takes_optiongroups = {
2080 "sambaopts": options.SambaOptions,
2081 "versionopts": options.VersionOptions,
2082 "localdcopts": LocalDCCredentialsOptions,
2088 def run(self, sambaopts=None, versionopts=None, localdcopts=None):
2090 local_server = self.setup_local_server(sambaopts, localdcopts)
2092 local_netlogon = self.new_local_netlogon_connection()
2093 except RuntimeError as error:
2094 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2097 local_netlogon_trusts = local_netlogon.netr_DsrEnumerateDomainTrusts(local_server,
2098 netlogon.NETR_TRUST_FLAG_IN_FOREST |
2099 netlogon.NETR_TRUST_FLAG_OUTBOUND |
2100 netlogon.NETR_TRUST_FLAG_INBOUND)
2101 except RuntimeError as error:
2102 if self.check_runtime_error(error, werror.WERR_RPC_S_PROCNUM_OUT_OF_RANGE):
2103 # TODO: we could implement a fallback to lsa.EnumTrustDom()
2104 raise CommandError("LOCAL_DC[%s]: netr_DsrEnumerateDomainTrusts not supported." % (
2106 raise self.LocalRuntimeError(self, error, "netr_DsrEnumerateDomainTrusts failed")
2108 a = local_netlogon_trusts.array
2110 if t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
2112 self.outf.write("%-14s %-15s %-19s %s\n" % (
2113 "Type[%s]" % self.netr_DomainTrust_to_type(a, t),
2114 "Transitive[%s]" % self.netr_DomainTrust_to_transitive(t),
2115 "Direction[%s]" % self.netr_DomainTrust_to_direction(t),
2116 "Name[%s]" % self.netr_DomainTrust_to_name(t)))
2119 class cmd_domain_trust_show(DomainTrustCommand):
2120 """Show trusted domain details."""
2122 synopsis = "%prog NAME [options]"
2124 takes_optiongroups = {
2125 "sambaopts": options.SambaOptions,
2126 "versionopts": options.VersionOptions,
2127 "localdcopts": LocalDCCredentialsOptions,
2133 takes_args = ["domain"]
2135 def run(self, domain, sambaopts=None, versionopts=None, localdcopts=None):
2137 local_server = self.setup_local_server(sambaopts, localdcopts)
2139 local_lsa = self.new_local_lsa_connection()
2140 except RuntimeError as error:
2141 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2144 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2145 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2146 except RuntimeError as error:
2147 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2149 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2150 local_lsa_info.name.string,
2151 local_lsa_info.dns_domain.string,
2152 local_lsa_info.sid))
2154 lsaString = lsa.String()
2155 lsaString.string = domain
2157 local_tdo_full = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2158 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2159 local_tdo_info = local_tdo_full.info_ex
2160 local_tdo_posix = local_tdo_full.posix_offset
2161 except NTSTATUSError as error:
2162 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2163 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2165 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(FULL_INFO) failed")
2168 local_tdo_enctypes = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2169 lsaString, lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES)
2170 except NTSTATUSError as error:
2171 if self.check_runtime_error(error, ntstatus.NT_STATUS_INVALID_PARAMETER):
2173 if self.check_runtime_error(error, ntstatus.NT_STATUS_INVALID_INFO_CLASS):
2176 if error is not None:
2177 raise self.LocalRuntimeError(self, error,
2178 "QueryTrustedDomainInfoByName(SUPPORTED_ENCRYPTION_TYPES) failed")
2180 local_tdo_enctypes = lsa.TrustDomainInfoSupportedEncTypes()
2181 local_tdo_enctypes.enc_types = 0
2184 local_tdo_forest = None
2185 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2186 local_tdo_forest = local_lsa.lsaRQueryForestTrustInformation(local_policy,
2187 lsaString, lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
2188 except RuntimeError as error:
2189 if self.check_runtime_error(error, ntstatus.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
2191 if self.check_runtime_error(error, ntstatus.NT_STATUS_NOT_FOUND):
2193 if error is not None:
2194 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation failed")
2196 local_tdo_forest = lsa.ForestTrustInformation()
2197 local_tdo_forest.count = 0
2198 local_tdo_forest.entries = []
2200 self.outf.write("TrustedDomain:\n\n");
2201 self.outf.write("NetbiosName: %s\n" % local_tdo_info.netbios_name.string)
2202 if local_tdo_info.netbios_name.string != local_tdo_info.domain_name.string:
2203 self.outf.write("DnsName: %s\n" % local_tdo_info.domain_name.string)
2204 self.outf.write("SID: %s\n" % local_tdo_info.sid)
2205 self.outf.write("Type: %s\n" % self.trustType_string(local_tdo_info.trust_type))
2206 self.outf.write("Direction: %s\n" % self.trustDirection_string(local_tdo_info.trust_direction))
2207 self.outf.write("Attributes: %s\n" % self.trustAttributes_string(local_tdo_info.trust_attributes))
2208 posix_offset_u32 = ctypes.c_uint32(local_tdo_posix.posix_offset).value
2209 posix_offset_i32 = ctypes.c_int32(local_tdo_posix.posix_offset).value
2210 self.outf.write("PosixOffset: 0x%08X (%d)\n" % (posix_offset_u32, posix_offset_i32))
2211 self.outf.write("kerb_EncTypes: %s\n" % self.kerb_EncTypes_string(local_tdo_enctypes.enc_types))
2213 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2214 self.write_forest_trust_info(local_tdo_forest,
2215 tln=local_tdo_info.domain_name.string)
2219 class cmd_domain_trust_create(DomainTrustCommand):
2220 """Create a domain or forest trust."""
2222 synopsis = "%prog DOMAIN [options]"
2224 takes_optiongroups = {
2225 "sambaopts": options.SambaOptions,
2226 "versionopts": options.VersionOptions,
2227 "credopts": options.CredentialsOptions,
2228 "localdcopts": LocalDCCredentialsOptions,
2232 Option("--type", type="choice", metavar="TYPE",
2233 choices=["external", "forest"],
2234 help="The type of the trust: 'external' or 'forest'.",
2236 default="external"),
2237 Option("--direction", type="choice", metavar="DIRECTION",
2238 choices=["incoming", "outgoing", "both"],
2239 help="The trust direction: 'incoming', 'outgoing' or 'both'.",
2240 dest='trust_direction',
2242 Option("--create-location", type="choice", metavar="LOCATION",
2243 choices=["local", "both"],
2244 help="Where to create the trusted domain object: 'local' or 'both'.",
2245 dest='create_location',
2247 Option("--cross-organisation", action="store_true",
2248 help="The related domains does not belong to the same organisation.",
2249 dest='cross_organisation',
2251 Option("--quarantined", type="choice", metavar="yes|no",
2252 choices=["yes", "no", None],
2253 help="Special SID filtering rules are applied to the trust. "
2254 "With --type=external the default is yes. "
2255 "With --type=forest the default is no.",
2256 dest='quarantined_arg',
2258 Option("--not-transitive", action="store_true",
2259 help="The forest trust is not transitive.",
2260 dest='not_transitive',
2262 Option("--treat-as-external", action="store_true",
2263 help="The treat the forest trust as external.",
2264 dest='treat_as_external',
2266 Option("--no-aes-keys", action="store_false",
2267 help="The trust uses aes kerberos keys.",
2268 dest='use_aes_keys',
2270 Option("--skip-validation", action="store_false",
2271 help="Skip validation of the trust.",
2276 takes_args = ["domain"]
2278 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2279 trust_type=None, trust_direction=None, create_location=None,
2280 cross_organisation=False, quarantined_arg=None,
2281 not_transitive=False, treat_as_external=False,
2282 use_aes_keys=False, validate=True):
2284 lsaString = lsa.String()
2287 if quarantined_arg is None:
2288 if trust_type == 'external':
2290 elif quarantined_arg == 'yes':
2293 if trust_type != 'forest':
2295 raise CommandError("--not-transitive requires --type=forest")
2296 if treat_as_external:
2297 raise CommandError("--treat-as-external requires --type=forest")
2301 enc_types = lsa.TrustDomainInfoSupportedEncTypes()
2302 enc_types.enc_types = security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96
2303 enc_types.enc_types |= security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96
2305 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2306 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2307 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2309 local_trust_info = lsa.TrustDomainInfoInfoEx()
2310 local_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2311 local_trust_info.trust_direction = 0
2312 if trust_direction == "both":
2313 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2314 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2315 elif trust_direction == "incoming":
2316 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2317 elif trust_direction == "outgoing":
2318 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2319 local_trust_info.trust_attributes = 0
2320 if cross_organisation:
2321 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2323 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2324 if trust_type == "forest":
2325 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2327 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2328 if treat_as_external:
2329 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2331 def get_password(name):
2334 if password is not None and password is not '':
2336 password = getpass("New %s Password: " % name)
2337 passwordverify = getpass("Retype %s Password: " % name)
2338 if not password == passwordverify:
2340 self.outf.write("Sorry, passwords do not match.\n")
2342 incoming_secret = None
2343 outgoing_secret = None
2344 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2345 if create_location == "local":
2346 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2347 incoming_password = get_password("Incoming Trust")
2348 incoming_secret = string_to_byte_array(incoming_password.encode('utf-16-le'))
2349 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2350 outgoing_password = get_password("Outgoing Trust")
2351 outgoing_secret = string_to_byte_array(outgoing_password.encode('utf-16-le'))
2353 remote_trust_info = None
2355 # We use 240 random bytes.
2356 # Windows uses 28 or 240 random bytes. I guess it's
2357 # based on the trust type external vs. forest.
2359 # The initial trust password can be up to 512 bytes
2360 # while the versioned passwords used for periodic updates
2361 # can only be up to 498 bytes, as netr_ServerPasswordSet2()
2362 # needs to pass the NL_PASSWORD_VERSION structure within the
2363 # 512 bytes and a 2 bytes confounder is required.
2365 def random_trust_secret(length):
2366 pw = samba.generate_random_machine_password(length//2, length//2)
2367 return string_to_byte_array(pw.encode('utf-16-le'))
2369 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2370 incoming_secret = random_trust_secret(240)
2371 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2372 outgoing_secret = random_trust_secret(240)
2374 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2375 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2377 remote_trust_info = lsa.TrustDomainInfoInfoEx()
2378 remote_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2379 remote_trust_info.trust_direction = 0
2380 if trust_direction == "both":
2381 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2382 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2383 elif trust_direction == "incoming":
2384 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2385 elif trust_direction == "outgoing":
2386 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2387 remote_trust_info.trust_attributes = 0
2388 if cross_organisation:
2389 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2391 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2392 if trust_type == "forest":
2393 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2395 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2396 if treat_as_external:
2397 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2399 local_server = self.setup_local_server(sambaopts, localdcopts)
2401 local_lsa = self.new_local_lsa_connection()
2402 except RuntimeError as error:
2403 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2406 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2407 except RuntimeError as error:
2408 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2410 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2411 local_lsa_info.name.string,
2412 local_lsa_info.dns_domain.string,
2413 local_lsa_info.sid))
2416 remote_server = self.setup_remote_server(credopts, domain)
2417 except RuntimeError as error:
2418 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2421 remote_lsa = self.new_remote_lsa_connection()
2422 except RuntimeError as error:
2423 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2426 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2427 except RuntimeError as error:
2428 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2430 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2431 remote_lsa_info.name.string,
2432 remote_lsa_info.dns_domain.string,
2433 remote_lsa_info.sid))
2435 local_trust_info.domain_name.string = remote_lsa_info.dns_domain.string
2436 local_trust_info.netbios_name.string = remote_lsa_info.name.string
2437 local_trust_info.sid = remote_lsa_info.sid
2439 if remote_trust_info:
2440 remote_trust_info.domain_name.string = local_lsa_info.dns_domain.string
2441 remote_trust_info.netbios_name.string = local_lsa_info.name.string
2442 remote_trust_info.sid = local_lsa_info.sid
2445 lsaString.string = local_trust_info.domain_name.string
2446 local_old_netbios = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2447 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2448 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2449 except NTSTATUSError as error:
2450 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2451 raise self.LocalRuntimeError(self, error,
2452 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2456 lsaString.string = local_trust_info.netbios_name.string
2457 local_old_dns = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2458 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2459 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2460 except NTSTATUSError as error:
2461 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2462 raise self.LocalRuntimeError(self, error,
2463 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2466 if remote_trust_info:
2468 lsaString.string = remote_trust_info.domain_name.string
2469 remote_old_netbios = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2470 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2471 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2472 except NTSTATUSError as error:
2473 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2474 raise self.RemoteRuntimeError(self, error,
2475 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2479 lsaString.string = remote_trust_info.netbios_name.string
2480 remote_old_dns = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2481 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2482 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2483 except NTSTATUSError as error:
2484 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2485 raise self.RemoteRuntimeError(self, error,
2486 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2490 local_netlogon = self.new_local_netlogon_connection()
2491 except RuntimeError as error:
2492 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2495 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
2496 except RuntimeError as error:
2497 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
2499 if remote_trust_info:
2501 remote_netlogon = self.new_remote_netlogon_connection()
2502 except RuntimeError as error:
2503 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2506 remote_netlogon_info = self.get_netlogon_dc_info(remote_netlogon, remote_server)
2507 except RuntimeError as error:
2508 raise self.RemoteRuntimeError(self, error, "failed to get netlogon dc info")
2510 def generate_AuthInOutBlob(secret, update_time):
2512 blob = drsblobs.trustAuthInOutBlob()
2517 clear = drsblobs.AuthInfoClear()
2518 clear.size = len(secret)
2519 clear.password = secret
2521 info = drsblobs.AuthenticationInformation()
2522 info.LastUpdateTime = samba.unix2nttime(update_time)
2523 info.AuthType = lsa.TRUST_AUTH_TYPE_CLEAR
2524 info.AuthInfo = clear
2526 array = drsblobs.AuthenticationInformationArray()
2528 array.array = [info]
2530 blob = drsblobs.trustAuthInOutBlob()
2532 blob.current = array
2536 def generate_AuthInfoInternal(session_key, incoming=None, outgoing=None):
2537 confounder = [0] * 512
2538 for i in range(len(confounder)):
2539 confounder[i] = random.randint(0, 255)
2541 trustpass = drsblobs.trustDomainPasswords()
2543 trustpass.confounder = confounder
2544 trustpass.outgoing = outgoing
2545 trustpass.incoming = incoming
2547 trustpass_blob = ndr_pack(trustpass)
2549 encrypted_trustpass = arcfour_encrypt(session_key, trustpass_blob)
2551 auth_blob = lsa.DATA_BUF2()
2552 auth_blob.size = len(encrypted_trustpass)
2553 auth_blob.data = string_to_byte_array(encrypted_trustpass)
2555 auth_info = lsa.TrustDomainInfoAuthInfoInternal()
2556 auth_info.auth_blob = auth_blob
2560 update_time = samba.current_unix_time()
2561 incoming_blob = generate_AuthInOutBlob(incoming_secret, update_time)
2562 outgoing_blob = generate_AuthInOutBlob(outgoing_secret, update_time)
2564 local_tdo_handle = None
2565 remote_tdo_handle = None
2567 local_auth_info = generate_AuthInfoInternal(local_lsa.session_key,
2568 incoming=incoming_blob,
2569 outgoing=outgoing_blob)
2570 if remote_trust_info:
2571 remote_auth_info = generate_AuthInfoInternal(remote_lsa.session_key,
2572 incoming=outgoing_blob,
2573 outgoing=incoming_blob)
2576 if remote_trust_info:
2577 self.outf.write("Creating remote TDO.\n")
2578 current_request = { "location": "remote", "name": "CreateTrustedDomainEx2"}
2579 remote_tdo_handle = remote_lsa.CreateTrustedDomainEx2(remote_policy,
2582 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2583 self.outf.write("Remote TDO created.\n")
2585 self.outf.write("Setting supported encryption types on remote TDO.\n")
2586 current_request = { "location": "remote", "name": "SetInformationTrustedDomain"}
2587 remote_lsa.SetInformationTrustedDomain(remote_tdo_handle,
2588 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2591 self.outf.write("Creating local TDO.\n")
2592 current_request = { "location": "local", "name": "CreateTrustedDomainEx2"}
2593 local_tdo_handle = local_lsa.CreateTrustedDomainEx2(local_policy,
2596 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2597 self.outf.write("Local TDO created\n")
2599 self.outf.write("Setting supported encryption types on local TDO.\n")
2600 current_request = { "location": "local", "name": "SetInformationTrustedDomain"}
2601 local_lsa.SetInformationTrustedDomain(local_tdo_handle,
2602 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2604 except RuntimeError as error:
2605 self.outf.write("Error: %s failed %sly - cleaning up\n" % (
2606 current_request['name'], current_request['location']))
2607 if remote_tdo_handle:
2608 self.outf.write("Deleting remote TDO.\n")
2609 remote_lsa.DeleteObject(remote_tdo_handle)
2610 remote_tdo_handle = None
2611 if local_tdo_handle:
2612 self.outf.write("Deleting local TDO.\n")
2613 local_lsa.DeleteObject(local_tdo_handle)
2614 local_tdo_handle = None
2615 if current_request['location'] is "remote":
2616 raise self.RemoteRuntimeError(self, error, "%s" % (
2617 current_request['name']))
2618 raise self.LocalRuntimeError(self, error, "%s" % (
2619 current_request['name']))
2622 if local_trust_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2623 self.outf.write("Setup local forest trust information...\n")
2625 # get all information about the remote trust
2626 # this triggers netr_GetForestTrustInformation to the remote domain
2627 # and lsaRSetForestTrustInformation() locally, but new top level
2628 # names are disabled by default.
2629 local_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
2630 remote_lsa_info.dns_domain.string,
2631 netlogon.DS_GFTI_UPDATE_TDO)
2632 except RuntimeError as error:
2633 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2636 # here we try to enable all top level names
2637 local_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
2638 remote_lsa_info.dns_domain,
2639 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2642 except RuntimeError as error:
2643 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2645 self.write_forest_trust_info(local_forest_info,
2646 tln=remote_lsa_info.dns_domain.string,
2647 collisions=local_forest_collision)
2649 if remote_trust_info:
2650 self.outf.write("Setup remote forest trust information...\n")
2652 # get all information about the local trust (from the perspective of the remote domain)
2653 # this triggers netr_GetForestTrustInformation to our domain.
2654 # and lsaRSetForestTrustInformation() remotely, but new top level
2655 # names are disabled by default.
2656 remote_forest_info = remote_netlogon.netr_DsRGetForestTrustInformation(remote_netlogon_info.dc_unc,
2657 local_lsa_info.dns_domain.string,
2658 netlogon.DS_GFTI_UPDATE_TDO)
2659 except RuntimeError as error:
2660 raise self.RemoteRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2663 # here we try to enable all top level names
2664 remote_forest_collision = remote_lsa.lsaRSetForestTrustInformation(remote_policy,
2665 local_lsa_info.dns_domain,
2666 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2669 except RuntimeError as error:
2670 raise self.RemoteRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2672 self.write_forest_trust_info(remote_forest_info,
2673 tln=local_lsa_info.dns_domain.string,
2674 collisions=remote_forest_collision)
2676 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2677 self.outf.write("Validating outgoing trust...\n")
2679 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_netlogon_info.dc_unc,
2680 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2682 remote_lsa_info.dns_domain.string)
2683 except RuntimeError as error:
2684 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2686 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2687 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2689 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2690 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2691 local_trust_verify.trusted_dc_name,
2692 local_trust_verify.tc_connection_status[1],
2693 local_trust_verify.pdc_connection_status[1])
2695 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2696 local_trust_verify.trusted_dc_name,
2697 local_trust_verify.tc_connection_status[1],
2698 local_trust_verify.pdc_connection_status[1])
2700 if local_trust_status != werror.WERR_SUCCESS or local_conn_status != werror.WERR_SUCCESS:
2701 raise CommandError(local_validation)
2703 self.outf.write("OK: %s\n" % local_validation)
2705 if remote_trust_info:
2706 if remote_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2707 self.outf.write("Validating incoming trust...\n")
2709 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_netlogon_info.dc_unc,
2710 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2712 local_lsa_info.dns_domain.string)
2713 except RuntimeError as error:
2714 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2716 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2717 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2719 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2720 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2721 remote_trust_verify.trusted_dc_name,
2722 remote_trust_verify.tc_connection_status[1],
2723 remote_trust_verify.pdc_connection_status[1])
2725 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2726 remote_trust_verify.trusted_dc_name,
2727 remote_trust_verify.tc_connection_status[1],
2728 remote_trust_verify.pdc_connection_status[1])
2730 if remote_trust_status != werror.WERR_SUCCESS or remote_conn_status != werror.WERR_SUCCESS:
2731 raise CommandError(remote_validation)
2733 self.outf.write("OK: %s\n" % remote_validation)
2735 if remote_tdo_handle is not None:
2737 remote_lsa.Close(remote_tdo_handle)
2738 except RuntimeError as error:
2740 remote_tdo_handle = None
2741 if local_tdo_handle is not None:
2743 local_lsa.Close(local_tdo_handle)
2744 except RuntimeError as error:
2746 local_tdo_handle = None
2748 self.outf.write("Success.\n")
2751 class cmd_domain_trust_delete(DomainTrustCommand):
2752 """Delete a domain trust."""
2754 synopsis = "%prog DOMAIN [options]"
2756 takes_optiongroups = {
2757 "sambaopts": options.SambaOptions,
2758 "versionopts": options.VersionOptions,
2759 "credopts": options.CredentialsOptions,
2760 "localdcopts": LocalDCCredentialsOptions,
2764 Option("--delete-location", type="choice", metavar="LOCATION",
2765 choices=["local", "both"],
2766 help="Where to delete the trusted domain object: 'local' or 'both'.",
2767 dest='delete_location',
2771 takes_args = ["domain"]
2773 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2774 delete_location=None):
2776 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2777 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2778 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2780 if delete_location == "local":
2781 remote_policy_access = None
2783 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2784 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2785 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2787 local_server = self.setup_local_server(sambaopts, localdcopts)
2789 local_lsa = self.new_local_lsa_connection()
2790 except RuntimeError as error:
2791 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2794 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2795 except RuntimeError as error:
2796 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2798 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2799 local_lsa_info.name.string,
2800 local_lsa_info.dns_domain.string,
2801 local_lsa_info.sid))
2803 local_tdo_info = None
2804 local_tdo_handle = None
2805 remote_tdo_info = None
2806 remote_tdo_handle = None
2808 lsaString = lsa.String()
2810 lsaString.string = domain
2811 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2812 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2813 except NTSTATUSError as error:
2814 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2815 raise CommandError("Failed to find trust for domain '%s'" % domain)
2816 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2819 if remote_policy_access is not None:
2821 remote_server = self.setup_remote_server(credopts, domain)
2822 except RuntimeError as error:
2823 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2826 remote_lsa = self.new_remote_lsa_connection()
2827 except RuntimeError as error:
2828 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2831 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2832 except RuntimeError as error:
2833 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2835 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2836 remote_lsa_info.name.string,
2837 remote_lsa_info.dns_domain.string,
2838 remote_lsa_info.sid))
2840 if remote_lsa_info.sid != local_tdo_info.sid or \
2841 remote_lsa_info.name.string != local_tdo_info.netbios_name.string or \
2842 remote_lsa_info.dns_domain.string != local_tdo_info.domain_name.string:
2843 raise CommandError("LocalTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2844 local_tdo_info.netbios_name.string,
2845 local_tdo_info.domain_name.string,
2846 local_tdo_info.sid))
2849 lsaString.string = local_lsa_info.dns_domain.string
2850 remote_tdo_info = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2851 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2852 except NTSTATUSError as error:
2853 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2854 raise self.RemoteRuntimeError(self, error, "QueryTrustedDomainInfoByName(%s)" % (
2858 if remote_tdo_info is not None:
2859 if local_lsa_info.sid != remote_tdo_info.sid or \
2860 local_lsa_info.name.string != remote_tdo_info.netbios_name.string or \
2861 local_lsa_info.dns_domain.string != remote_tdo_info.domain_name.string:
2862 raise CommandError("RemoteTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2863 remote_tdo_info.netbios_name.string,
2864 remote_tdo_info.domain_name.string,
2865 remote_tdo_info.sid))
2867 if local_tdo_info is not None:
2869 lsaString.string = local_tdo_info.domain_name.string
2870 local_tdo_handle = local_lsa.OpenTrustedDomainByName(local_policy,
2872 security.SEC_STD_DELETE)
2873 except RuntimeError as error:
2874 raise self.LocalRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2877 local_lsa.DeleteObject(local_tdo_handle)
2878 local_tdo_handle = None
2880 if remote_tdo_info is not None:
2882 lsaString.string = remote_tdo_info.domain_name.string
2883 remote_tdo_handle = remote_lsa.OpenTrustedDomainByName(remote_policy,
2885 security.SEC_STD_DELETE)
2886 except RuntimeError as error:
2887 raise self.RemoteRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2890 if remote_tdo_handle is not None:
2892 remote_lsa.DeleteObject(remote_tdo_handle)
2893 remote_tdo_handle = None
2894 self.outf.write("RemoteTDO deleted.\n")
2895 except RuntimeError as error:
2896 self.outf.write("%s\n" % self.RemoteRuntimeError(self, error, "DeleteObject() failed"))
2898 if local_tdo_handle is not None:
2900 local_lsa.DeleteObject(local_tdo_handle)
2901 local_tdo_handle = None
2902 self.outf.write("LocalTDO deleted.\n")
2903 except RuntimeError as error:
2904 self.outf.write("%s\n" % self.LocalRuntimeError(self, error, "DeleteObject() failed"))
2908 class cmd_domain_trust_validate(DomainTrustCommand):
2909 """Validate a domain trust."""
2911 synopsis = "%prog DOMAIN [options]"
2913 takes_optiongroups = {
2914 "sambaopts": options.SambaOptions,
2915 "versionopts": options.VersionOptions,
2916 "credopts": options.CredentialsOptions,
2917 "localdcopts": LocalDCCredentialsOptions,
2921 Option("--validate-location", type="choice", metavar="LOCATION",
2922 choices=["local", "both"],
2923 help="Where to validate the trusted domain object: 'local' or 'both'.",
2924 dest='validate_location',
2928 takes_args = ["domain"]
2930 def run(self, domain, sambaopts=None, versionopts=None, credopts=None, localdcopts=None,
2931 validate_location=None):
2933 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2935 local_server = self.setup_local_server(sambaopts, localdcopts)
2937 local_lsa = self.new_local_lsa_connection()
2938 except RuntimeError as error:
2939 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2942 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2943 except RuntimeError as error:
2944 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2946 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2947 local_lsa_info.name.string,
2948 local_lsa_info.dns_domain.string,
2949 local_lsa_info.sid))
2952 lsaString = lsa.String()
2953 lsaString.string = domain
2954 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2955 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2956 except NTSTATUSError as error:
2957 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2958 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2960 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
2962 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
2963 local_tdo_info.netbios_name.string,
2964 local_tdo_info.domain_name.string,
2965 local_tdo_info.sid))
2968 local_netlogon = self.new_local_netlogon_connection()
2969 except RuntimeError as error:
2970 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2973 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_server,
2974 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2976 local_tdo_info.domain_name.string)
2977 except RuntimeError as error:
2978 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2980 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2981 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2983 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2984 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2985 local_trust_verify.trusted_dc_name,
2986 local_trust_verify.tc_connection_status[1],
2987 local_trust_verify.pdc_connection_status[1])
2989 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2990 local_trust_verify.trusted_dc_name,
2991 local_trust_verify.tc_connection_status[1],
2992 local_trust_verify.pdc_connection_status[1])
2994 if local_trust_status != werror.WERR_SUCCESS or local_conn_status != werror.WERR_SUCCESS:
2995 raise CommandError(local_validation)
2997 self.outf.write("OK: %s\n" % local_validation)
3000 server = local_trust_verify.trusted_dc_name.replace('\\', '')
3001 domain_and_server = "%s\\%s" % (local_tdo_info.domain_name.string, server)
3002 local_trust_rediscover = local_netlogon.netr_LogonControl2Ex(local_server,
3003 netlogon.NETLOGON_CONTROL_REDISCOVER,
3006 except RuntimeError as error:
3007 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
3009 local_conn_status = self._uint32(local_trust_rediscover.tc_connection_status[0])
3010 local_rediscover = "LocalRediscover: DC[%s] CONNECTION[%s]" % (
3011 local_trust_rediscover.trusted_dc_name,
3012 local_trust_rediscover.tc_connection_status[1])
3014 if local_conn_status != werror.WERR_SUCCESS:
3015 raise CommandError(local_rediscover)
3017 self.outf.write("OK: %s\n" % local_rediscover)
3019 if validate_location != "local":
3021 remote_server = self.setup_remote_server(credopts, domain, require_pdc=False)
3022 except RuntimeError as error:
3023 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
3026 remote_netlogon = self.new_remote_netlogon_connection()
3027 except RuntimeError as error:
3028 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
3031 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_server,
3032 netlogon.NETLOGON_CONTROL_TC_VERIFY,
3034 local_lsa_info.dns_domain.string)
3035 except RuntimeError as error:
3036 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
3038 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
3039 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
3041 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
3042 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
3043 remote_trust_verify.trusted_dc_name,
3044 remote_trust_verify.tc_connection_status[1],
3045 remote_trust_verify.pdc_connection_status[1])
3047 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
3048 remote_trust_verify.trusted_dc_name,
3049 remote_trust_verify.tc_connection_status[1],
3050 remote_trust_verify.pdc_connection_status[1])
3052 if remote_trust_status != werror.WERR_SUCCESS or remote_conn_status != werror.WERR_SUCCESS:
3053 raise CommandError(remote_validation)
3055 self.outf.write("OK: %s\n" % remote_validation)
3058 server = remote_trust_verify.trusted_dc_name.replace('\\', '')
3059 domain_and_server = "%s\\%s" % (local_lsa_info.dns_domain.string, server)
3060 remote_trust_rediscover = remote_netlogon.netr_LogonControl2Ex(remote_server,
3061 netlogon.NETLOGON_CONTROL_REDISCOVER,
3064 except RuntimeError as error:
3065 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
3067 remote_conn_status = self._uint32(remote_trust_rediscover.tc_connection_status[0])
3069 remote_rediscover = "RemoteRediscover: DC[%s] CONNECTION[%s]" % (
3070 remote_trust_rediscover.trusted_dc_name,
3071 remote_trust_rediscover.tc_connection_status[1])
3073 if remote_conn_status != werror.WERR_SUCCESS:
3074 raise CommandError(remote_rediscover)
3076 self.outf.write("OK: %s\n" % remote_rediscover)
3080 class cmd_domain_trust_namespaces(DomainTrustCommand):
3081 """Manage forest trust namespaces."""
3083 synopsis = "%prog [DOMAIN] [options]"
3085 takes_optiongroups = {
3086 "sambaopts": options.SambaOptions,
3087 "versionopts": options.VersionOptions,
3088 "localdcopts": LocalDCCredentialsOptions,
3092 Option("--refresh", type="choice", metavar="check|store",
3093 choices=["check", "store", None],
3094 help="List and maybe store refreshed forest trust information: 'check' or 'store'.",
3097 Option("--enable-all", action="store_true",
3098 help="Try to update disabled entries, not allowed with --refresh=check.",
3101 Option("--enable-tln", action="append", metavar='DNSDOMAIN',
3102 help="Enable a top level name entry. Can be specified multiple times.",
3105 Option("--disable-tln", action="append", metavar='DNSDOMAIN',
3106 help="Disable a top level name entry. Can be specified multiple times.",
3109 Option("--add-tln-ex", action="append", metavar='DNSDOMAIN',
3110 help="Add a top level exclusion entry. Can be specified multiple times.",
3113 Option("--delete-tln-ex", action="append", metavar='DNSDOMAIN',
3114 help="Delete a top level exclusion entry. Can be specified multiple times.",
3115 dest='delete_tln_ex',
3117 Option("--enable-nb", action="append", metavar='NETBIOSDOMAIN',
3118 help="Enable a netbios name in a domain entry. Can be specified multiple times.",
3121 Option("--disable-nb", action="append", metavar='NETBIOSDOMAIN',
3122 help="Disable a netbios name in a domain entry. Can be specified multiple times.",
3125 Option("--enable-sid", action="append", metavar='DOMAINSID',
3126 help="Enable a SID in a domain entry. Can be specified multiple times.",
3127 dest='enable_sid_str',
3129 Option("--disable-sid", action="append", metavar='DOMAINSID',
3130 help="Disable a SID in a domain entry. Can be specified multiple times.",
3131 dest='disable_sid_str',
3133 Option("--add-upn-suffix", action="append", metavar='DNSDOMAIN',
3134 help="Add a new uPNSuffixes attribute for the local forest. Can be specified multiple times.",
3137 Option("--delete-upn-suffix", action="append", metavar='DNSDOMAIN',
3138 help="Delete an existing uPNSuffixes attribute of the local forest. Can be specified multiple times.",
3141 Option("--add-spn-suffix", action="append", metavar='DNSDOMAIN',
3142 help="Add a new msDS-SPNSuffixes attribute for the local forest. Can be specified multiple times.",
3145 Option("--delete-spn-suffix", action="append", metavar='DNSDOMAIN',
3146 help="Delete an existing msDS-SPNSuffixes attribute of the local forest. Can be specified multiple times.",
3151 takes_args = ["domain?"]
3153 def run(self, domain=None, sambaopts=None, localdcopts=None, versionopts=None,
3154 refresh=None, enable_all=False,
3155 enable_tln=[], disable_tln=[], add_tln_ex=[], delete_tln_ex=[],
3156 enable_sid_str=[], disable_sid_str=[], enable_nb=[], disable_nb=[],
3157 add_upn=[], delete_upn=[], add_spn=[], delete_spn=[]):
3159 require_update = False
3162 if refresh == "store":
3163 raise CommandError("--refresh=%s not allowed without DOMAIN" % refresh)
3166 raise CommandError("--enable-all not allowed without DOMAIN")
3168 if len(enable_tln) > 0:
3169 raise CommandError("--enable-tln not allowed without DOMAIN")
3170 if len(disable_tln) > 0:
3171 raise CommandError("--disable-tln not allowed without DOMAIN")
3173 if len(add_tln_ex) > 0:
3174 raise CommandError("--add-tln-ex not allowed without DOMAIN")
3175 if len(delete_tln_ex) > 0:
3176 raise CommandError("--delete-tln-ex not allowed without DOMAIN")
3178 if len(enable_nb) > 0:
3179 raise CommandError("--enable-nb not allowed without DOMAIN")
3180 if len(disable_nb) > 0:
3181 raise CommandError("--disable-nb not allowed without DOMAIN")
3183 if len(enable_sid_str) > 0:
3184 raise CommandError("--enable-sid not allowed without DOMAIN")
3185 if len(disable_sid_str) > 0:
3186 raise CommandError("--disable-sid not allowed without DOMAIN")
3188 if len(add_upn) > 0:
3190 if not n.startswith("*."):
3192 raise CommandError("value[%s] specified for --add-upn-suffix should not include with '*.'" % n)
3193 require_update = True
3194 if len(delete_upn) > 0:
3195 for n in delete_upn:
3196 if not n.startswith("*."):
3198 raise CommandError("value[%s] specified for --delete-upn-suffix should not include with '*.'" % n)
3199 require_update = True
3201 for d in delete_upn:
3202 if a.lower() != d.lower():
3204 raise CommandError("value[%s] specified for --add-upn-suffix and --delete-upn-suffix" % a)
3206 if len(add_spn) > 0:
3208 if not n.startswith("*."):
3210 raise CommandError("value[%s] specified for --add-spn-suffix should not include with '*.'" % n)
3211 require_update = True
3212 if len(delete_spn) > 0:
3213 for n in delete_spn:
3214 if not n.startswith("*."):
3216 raise CommandError("value[%s] specified for --delete-spn-suffix should not include with '*.'" % n)
3217 require_update = True
3219 for d in delete_spn:
3220 if a.lower() != d.lower():
3222 raise CommandError("value[%s] specified for --add-spn-suffix and --delete-spn-suffix" % a)
3224 if len(add_upn) > 0:
3225 raise CommandError("--add-upn-suffix not allowed together with DOMAIN")
3226 if len(delete_upn) > 0:
3227 raise CommandError("--delete-upn-suffix not allowed together with DOMAIN")
3228 if len(add_spn) > 0:
3229 raise CommandError("--add-spn-suffix not allowed together with DOMAIN")
3230 if len(delete_spn) > 0:
3231 raise CommandError("--delete-spn-suffix not allowed together with DOMAIN")
3233 if refresh is not None:
3234 if refresh == "store":
3235 require_update = True
3237 if enable_all and refresh != "store":
3238 raise CommandError("--enable-all not allowed together with --refresh=%s" % refresh)
3240 if len(enable_tln) > 0:
3241 raise CommandError("--enable-tln not allowed together with --refresh")
3242 if len(disable_tln) > 0:
3243 raise CommandError("--disable-tln not allowed together with --refresh")
3245 if len(add_tln_ex) > 0:
3246 raise CommandError("--add-tln-ex not allowed together with --refresh")
3247 if len(delete_tln_ex) > 0:
3248 raise CommandError("--delete-tln-ex not allowed together with --refresh")
3250 if len(enable_nb) > 0:
3251 raise CommandError("--enable-nb not allowed together with --refresh")
3252 if len(disable_nb) > 0:
3253 raise CommandError("--disable-nb not allowed together with --refresh")
3255 if len(enable_sid_str) > 0:
3256 raise CommandError("--enable-sid not allowed together with --refresh")
3257 if len(disable_sid_str) > 0:
3258 raise CommandError("--disable-sid not allowed together with --refresh")
3261 require_update = True
3263 if len(enable_tln) > 0:
3264 raise CommandError("--enable-tln not allowed together with --enable-all")
3266 if len(enable_nb) > 0:
3267 raise CommandError("--enable-nb not allowed together with --enable-all")
3269 if len(enable_sid_str) > 0:
3270 raise CommandError("--enable-sid not allowed together with --enable-all")
3272 if len(enable_tln) > 0:
3273 require_update = True
3274 if len(disable_tln) > 0:
3275 require_update = True
3276 for e in enable_tln:
3277 for d in disable_tln:
3278 if e.lower() != d.lower():
3280 raise CommandError("value[%s] specified for --enable-tln and --disable-tln" % e)
3282 if len(add_tln_ex) > 0:
3283 for n in add_tln_ex:
3284 if not n.startswith("*."):
3286 raise CommandError("value[%s] specified for --add-tln-ex should not include with '*.'" % n)
3287 require_update = True
3288 if len(delete_tln_ex) > 0:
3289 for n in delete_tln_ex:
3290 if not n.startswith("*."):
3292 raise CommandError("value[%s] specified for --delete-tln-ex should not include with '*.'" % n)
3293 require_update = True
3294 for a in add_tln_ex:
3295 for d in delete_tln_ex:
3296 if a.lower() != d.lower():
3298 raise CommandError("value[%s] specified for --add-tln-ex and --delete-tln-ex" % a)
3300 if len(enable_nb) > 0:
3301 require_update = True
3302 if len(disable_nb) > 0:
3303 require_update = True
3305 for d in disable_nb:
3306 if e.upper() != d.upper():
3308 raise CommandError("value[%s] specified for --enable-nb and --disable-nb" % e)
3311 for s in enable_sid_str:
3313 sid = security.dom_sid(s)
3314 except TypeError as error:
3315 raise CommandError("value[%s] specified for --enable-sid is not a valid SID" % s)
3316 enable_sid.append(sid)
3318 for s in disable_sid_str:
3320 sid = security.dom_sid(s)
3321 except TypeError as error:
3322 raise CommandError("value[%s] specified for --disable-sid is not a valid SID" % s)
3323 disable_sid.append(sid)
3324 if len(enable_sid) > 0:
3325 require_update = True
3326 if len(disable_sid) > 0:
3327 require_update = True
3328 for e in enable_sid:
3329 for d in disable_sid:
3332 raise CommandError("value[%s] specified for --enable-sid and --disable-sid" % e)
3334 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
3336 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
3338 local_server = self.setup_local_server(sambaopts, localdcopts)
3340 local_lsa = self.new_local_lsa_connection()
3341 except RuntimeError as error:
3342 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
3345 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
3346 except RuntimeError as error:
3347 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
3349 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
3350 local_lsa_info.name.string,
3351 local_lsa_info.dns_domain.string,
3352 local_lsa_info.sid))
3356 local_netlogon = self.new_local_netlogon_connection()
3357 except RuntimeError as error:
3358 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3361 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3362 except RuntimeError as error:
3363 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3365 if local_netlogon_info.domain_name != local_netlogon_info.forest_name:
3366 raise CommandError("The local domain [%s] is not the forest root [%s]" % (
3367 local_netlogon_info.domain_name,
3368 local_netlogon_info.forest_name))
3371 # get all information about our own forest
3372 own_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3374 except RuntimeError as error:
3375 if self.check_runtime_error(error, werror.WERR_RPC_S_PROCNUM_OUT_OF_RANGE):
3376 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3379 if self.check_runtime_error(error, werror.WERR_INVALID_FUNCTION):
3380 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3383 if self.check_runtime_error(error, werror.WERR_NERR_ACFNOTLOADED):
3384 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3387 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3389 self.outf.write("Own forest trust information...\n")
3390 self.write_forest_trust_info(own_forest_info,
3391 tln=local_lsa_info.dns_domain.string)
3394 local_samdb = self.new_local_ldap_connection()
3395 except RuntimeError as error:
3396 raise self.LocalRuntimeError(self, error, "failed to connect to SamDB")
3398 local_partitions_dn = "CN=Partitions,%s" % str(local_samdb.get_config_basedn())
3399 attrs = ['uPNSuffixes', 'msDS-SPNSuffixes']
3401 msgs = local_samdb.search(base=local_partitions_dn,
3402 scope=ldb.SCOPE_BASE,
3403 expression="(objectClass=crossRefContainer)",
3405 stored_msg = msgs[0]
3406 except ldb.LdbError as error:
3407 raise self.LocalLdbError(self, error, "failed to search partition dn")
3409 stored_upn_vals = []
3410 if 'uPNSuffixes' in stored_msg:
3411 stored_upn_vals.extend(stored_msg['uPNSuffixes'])
3413 stored_spn_vals = []
3414 if 'msDS-SPNSuffixes' in stored_msg:
3415 stored_spn_vals.extend(stored_msg['msDS-SPNSuffixes'])
3417 self.outf.write("Stored uPNSuffixes attributes[%d]:\n" % len(stored_upn_vals))
3418 for v in stored_upn_vals:
3419 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3420 self.outf.write("Stored msDS-SPNSuffixes attributes[%d]:\n" % len(stored_spn_vals))
3421 for v in stored_spn_vals:
3422 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3424 if not require_update:
3428 update_upn_vals = []
3429 update_upn_vals.extend(stored_upn_vals)
3432 update_spn_vals = []
3433 update_spn_vals.extend(stored_spn_vals)
3437 for i in xrange(0, len(update_upn_vals)):
3438 v = update_upn_vals[i]
3439 if v.lower() != upn.lower():
3444 raise CommandError("Entry already present for value[%s] specified for --add-upn-suffix" % upn)
3445 update_upn_vals.append(upn)
3448 for upn in delete_upn:
3450 for i in xrange(0, len(update_upn_vals)):
3451 v = update_upn_vals[i]
3452 if v.lower() != upn.lower():
3457 raise CommandError("Entry not found for value[%s] specified for --delete-upn-suffix" % upn)
3459 update_upn_vals.pop(idx)
3464 for i in xrange(0, len(update_spn_vals)):
3465 v = update_spn_vals[i]
3466 if v.lower() != spn.lower():
3471 raise CommandError("Entry already present for value[%s] specified for --add-spn-suffix" % spn)
3472 update_spn_vals.append(spn)
3475 for spn in delete_spn:
3477 for i in xrange(0, len(update_spn_vals)):
3478 v = update_spn_vals[i]
3479 if v.lower() != spn.lower():
3484 raise CommandError("Entry not found for value[%s] specified for --delete-spn-suffix" % spn)
3486 update_spn_vals.pop(idx)
3489 self.outf.write("Update uPNSuffixes attributes[%d]:\n" % len(update_upn_vals))
3490 for v in update_upn_vals:
3491 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3492 self.outf.write("Update msDS-SPNSuffixes attributes[%d]:\n" % len(update_spn_vals))
3493 for v in update_spn_vals:
3494 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3496 update_msg = ldb.Message()
3497 update_msg.dn = stored_msg.dn
3500 update_msg['uPNSuffixes'] = ldb.MessageElement(update_upn_vals,
3501 ldb.FLAG_MOD_REPLACE,
3504 update_msg['msDS-SPNSuffixes'] = ldb.MessageElement(update_spn_vals,
3505 ldb.FLAG_MOD_REPLACE,
3508 local_samdb.modify(update_msg)
3509 except ldb.LdbError as error:
3510 raise self.LocalLdbError(self, error, "failed to update partition dn")
3513 stored_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3515 except RuntimeError as error:
3516 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3518 self.outf.write("Stored forest trust information...\n")
3519 self.write_forest_trust_info(stored_forest_info,
3520 tln=local_lsa_info.dns_domain.string)
3524 lsaString = lsa.String()
3525 lsaString.string = domain
3526 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
3527 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
3528 except NTSTATUSError as error:
3529 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
3530 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
3532 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
3534 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
3535 local_tdo_info.netbios_name.string,
3536 local_tdo_info.domain_name.string,
3537 local_tdo_info.sid))
3539 if not local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
3540 raise CommandError("trusted domain object for domain [%s] is not marked as FOREST_TRANSITIVE." % domain)
3542 if refresh is not None:
3544 local_netlogon = self.new_local_netlogon_connection()
3545 except RuntimeError as error:
3546 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3549 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3550 except RuntimeError as error:
3551 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3553 lsa_update_check = 1
3554 if refresh == "store":
3555 netlogon_update_tdo = netlogon.DS_GFTI_UPDATE_TDO
3557 lsa_update_check = 0
3559 netlogon_update_tdo = 0
3562 # get all information about the remote trust
3563 # this triggers netr_GetForestTrustInformation to the remote domain
3564 # and lsaRSetForestTrustInformation() locally, but new top level
3565 # names are disabled by default.
3566 fresh_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3567 local_tdo_info.domain_name.string,
3568 netlogon_update_tdo)
3569 except RuntimeError as error:
3570 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3573 fresh_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3574 local_tdo_info.domain_name,
3575 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3578 except RuntimeError as error:
3579 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3581 self.outf.write("Fresh forest trust information...\n")
3582 self.write_forest_trust_info(fresh_forest_info,
3583 tln=local_tdo_info.domain_name.string,
3584 collisions=fresh_forest_collision)
3586 if refresh == "store":
3588 lsaString = lsa.String()
3589 lsaString.string = local_tdo_info.domain_name.string
3590 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3592 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3593 except RuntimeError as error:
3594 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3596 self.outf.write("Stored forest trust information...\n")
3597 self.write_forest_trust_info(stored_forest_info,
3598 tln=local_tdo_info.domain_name.string)
3603 # The none --refresh path
3607 lsaString = lsa.String()
3608 lsaString.string = local_tdo_info.domain_name.string
3609 local_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3611 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3612 except RuntimeError as error:
3613 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3615 self.outf.write("Local forest trust information...\n")
3616 self.write_forest_trust_info(local_forest_info,
3617 tln=local_tdo_info.domain_name.string)
3619 if not require_update:
3623 entries.extend(local_forest_info.entries)
3624 update_forest_info = lsa.ForestTrustInformation()
3625 update_forest_info.count = len(entries)
3626 update_forest_info.entries = entries
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_TOP_LEVEL_NAME:
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_TLN_DISABLED_MASK
3637 for i in xrange(0, len(update_forest_info.entries)):
3638 r = update_forest_info.entries[i]
3639 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3641 if update_forest_info.entries[i].flags == 0:
3643 update_forest_info.entries[i].time = 0
3644 update_forest_info.entries[i].flags &= ~lsa.LSA_NB_DISABLED_MASK
3645 update_forest_info.entries[i].flags &= ~lsa.LSA_SID_DISABLED_MASK
3647 for tln in enable_tln:
3649 for i in xrange(0, len(update_forest_info.entries)):
3650 r = update_forest_info.entries[i]
3651 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3653 if r.forest_trust_data.string.lower() != tln.lower():
3658 raise CommandError("Entry not found for value[%s] specified for --enable-tln" % tln)
3659 if not update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_MASK:
3660 raise CommandError("Entry found for value[%s] specified for --enable-tln is already enabled" % tln)
3661 update_forest_info.entries[idx].time = 0
3662 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3664 for tln in disable_tln:
3666 for i in xrange(0, len(update_forest_info.entries)):
3667 r = update_forest_info.entries[i]
3668 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3670 if r.forest_trust_data.string.lower() != tln.lower():
3675 raise CommandError("Entry not found for value[%s] specified for --disable-tln" % tln)
3676 if update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_ADMIN:
3677 raise CommandError("Entry found for value[%s] specified for --disable-tln is already disabled" % tln)
3678 update_forest_info.entries[idx].time = 0
3679 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3680 update_forest_info.entries[idx].flags |= lsa.LSA_TLN_DISABLED_ADMIN
3682 for tln_ex in add_tln_ex:
3684 for i in xrange(0, len(update_forest_info.entries)):
3685 r = update_forest_info.entries[i]
3686 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3688 if r.forest_trust_data.string.lower() != tln_ex.lower():
3693 raise CommandError("Entry already present for value[%s] specified for --add-tln-ex" % tln_ex)
3695 tln_dot = ".%s" % tln_ex.lower()
3697 for i in xrange(0, len(update_forest_info.entries)):
3698 r = update_forest_info.entries[i]
3699 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3701 r_dot = ".%s" % r.forest_trust_data.string.lower()
3702 if tln_dot == r_dot:
3703 raise CommandError("TLN entry present for value[%s] specified for --add-tln-ex" % tln_ex)
3704 if not tln_dot.endswith(r_dot):
3710 raise CommandError("No TLN parent present for value[%s] specified for --add-tln-ex" % tln_ex)
3712 r = lsa.ForestTrustRecord()
3713 r.type = lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX
3716 r.forest_trust_data.string = tln_ex
3719 entries.extend(update_forest_info.entries)
3720 entries.insert(idx + 1, r)
3721 update_forest_info.count = len(entries)
3722 update_forest_info.entries = entries
3724 for tln_ex in delete_tln_ex:
3726 for i in xrange(0, len(update_forest_info.entries)):
3727 r = update_forest_info.entries[i]
3728 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3730 if r.forest_trust_data.string.lower() != tln_ex.lower():
3735 raise CommandError("Entry not found for value[%s] specified for --delete-tln-ex" % tln_ex)
3738 entries.extend(update_forest_info.entries)
3740 update_forest_info.count = len(entries)
3741 update_forest_info.entries = entries
3743 for nb in enable_nb:
3745 for i in xrange(0, len(update_forest_info.entries)):
3746 r = update_forest_info.entries[i]
3747 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3749 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3754 raise CommandError("Entry not found for value[%s] specified for --enable-nb" % nb)
3755 if not update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_MASK:
3756 raise CommandError("Entry found for value[%s] specified for --enable-nb is already enabled" % nb)
3757 update_forest_info.entries[idx].time = 0
3758 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3760 for nb in disable_nb:
3762 for i in xrange(0, len(update_forest_info.entries)):
3763 r = update_forest_info.entries[i]
3764 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3766 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3771 raise CommandError("Entry not found for value[%s] specified for --delete-nb" % nb)
3772 if update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_ADMIN:
3773 raise CommandError("Entry found for value[%s] specified for --disable-nb is already disabled" % nb)
3774 update_forest_info.entries[idx].time = 0
3775 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3776 update_forest_info.entries[idx].flags |= lsa.LSA_NB_DISABLED_ADMIN
3778 for sid in enable_sid:
3780 for i in xrange(0, len(update_forest_info.entries)):
3781 r = update_forest_info.entries[i]
3782 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3784 if r.forest_trust_data.domain_sid != sid:
3789 raise CommandError("Entry not found for value[%s] specified for --enable-sid" % sid)
3790 if not update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_MASK:
3791 raise CommandError("Entry found for value[%s] specified for --enable-sid is already enabled" % nb)
3792 update_forest_info.entries[idx].time = 0
3793 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3795 for sid in disable_sid:
3797 for i in xrange(0, len(update_forest_info.entries)):
3798 r = update_forest_info.entries[i]
3799 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3801 if r.forest_trust_data.domain_sid != sid:
3806 raise CommandError("Entry not found for value[%s] specified for --delete-sid" % sid)
3807 if update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_ADMIN:
3808 raise CommandError("Entry found for value[%s] specified for --disable-sid is already disabled" % nb)
3809 update_forest_info.entries[idx].time = 0
3810 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3811 update_forest_info.entries[idx].flags |= lsa.LSA_SID_DISABLED_ADMIN
3814 update_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3815 local_tdo_info.domain_name,
3816 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3817 update_forest_info, 0)
3818 except RuntimeError as error:
3819 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3821 self.outf.write("Updated forest trust information...\n")
3822 self.write_forest_trust_info(update_forest_info,
3823 tln=local_tdo_info.domain_name.string,
3824 collisions=update_forest_collision)
3827 lsaString = lsa.String()
3828 lsaString.string = local_tdo_info.domain_name.string
3829 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3831 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3832 except RuntimeError as error:
3833 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3835 self.outf.write("Stored forest trust information...\n")
3836 self.write_forest_trust_info(stored_forest_info,
3837 tln=local_tdo_info.domain_name.string)
3840 class cmd_domain_tombstones_expunge(Command):
3841 """Expunge tombstones from the database.
3843 This command expunges tombstones from the database."""
3844 synopsis = "%prog NC [NC [...]] [options]"
3847 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
3848 metavar="URL", dest="H"),
3849 Option("--current-time",
3850 help="The current time to evaluate the tombstone lifetime from, expressed as YYYY-MM-DD",
3852 Option("--tombstone-lifetime", help="Number of days a tombstone should be preserved for", type=int),
3855 takes_args = ["nc*"]
3857 takes_optiongroups = {
3858 "sambaopts": options.SambaOptions,
3859 "credopts": options.CredentialsOptions,
3860 "versionopts": options.VersionOptions,
3863 def run(self, *ncs, **kwargs):
3864 sambaopts = kwargs.get("sambaopts")
3865 credopts = kwargs.get("credopts")
3866 versionpts = kwargs.get("versionopts")
3868 current_time_string = kwargs.get("current_time")
3869 tombstone_lifetime = kwargs.get("tombstone_lifetime")
3870 lp = sambaopts.get_loadparm()
3871 creds = credopts.get_credentials(lp)
3872 samdb = SamDB(url=H, session_info=system_session(),
3873 credentials=creds, lp=lp)
3875 if current_time_string is not None:
3876 current_time_obj = time.strptime(current_time_string, "%Y-%m-%d")
3877 current_time = long(time.mktime(current_time_obj))
3880 current_time = long(time.time())
3883 res = samdb.search(expression="", base="", scope=ldb.SCOPE_BASE,
3884 attrs=["namingContexts"])
3887 for nc in res[0]["namingContexts"]:
3892 started_transaction = False
3894 samdb.transaction_start()
3895 started_transaction = True
3897 removed_links) = samdb.garbage_collect_tombstones(ncs,
3898 current_time=current_time,
3899 tombstone_lifetime=tombstone_lifetime)
3901 except Exception as err:
3902 if started_transaction:
3903 samdb.transaction_cancel()
3904 raise CommandError("Failed to expunge / garbage collect tombstones", err)
3906 samdb.transaction_commit()
3908 self.outf.write("Removed %d objects and %d links successfully\n"
3909 % (removed_objects, removed_links))
3913 class cmd_domain_trust(SuperCommand):
3914 """Domain and forest trust management."""
3917 subcommands["list"] = cmd_domain_trust_list()
3918 subcommands["show"] = cmd_domain_trust_show()
3919 subcommands["create"] = cmd_domain_trust_create()
3920 subcommands["delete"] = cmd_domain_trust_delete()
3921 subcommands["validate"] = cmd_domain_trust_validate()
3922 subcommands["namespaces"] = cmd_domain_trust_namespaces()
3924 class cmd_domain_tombstones(SuperCommand):
3925 """Domain tombstone and recycled object management."""
3928 subcommands["expunge"] = cmd_domain_tombstones_expunge()
3930 class ldif_schema_update:
3931 """Helper class for applying LDIF schema updates"""
3934 self.is_defunct = False
3935 self.unknown_oid = None
3939 def _ldap_schemaUpdateNow(self, samdb):
3943 add: schemaUpdateNow
3946 samdb.modify_ldif(ldif)
3948 def can_ignore_failure(self, error):
3949 """Checks if we can safely ignore failure to apply an LDIF update"""
3950 (num, errstr) = error.args
3952 # Microsoft has marked objects as defunct that Samba doesn't know about
3953 if num == ldb.ERR_NO_SUCH_OBJECT and self.is_defunct:
3954 print("Defunct object %s doesn't exist, skipping" % self.dn)
3956 elif self.unknown_oid is not None:
3957 print("Skipping unknown OID %s for object %s" %(self.unknown_oid, self.dn))
3962 def apply(self, samdb):
3963 """Applies a single LDIF update to the schema"""
3967 samdb.modify_ldif(self.ldif, controls=['relax:0'])
3968 except ldb.LdbError as e:
3969 if e.args[0] == ldb.ERR_INVALID_ATTRIBUTE_SYNTAX:
3971 # REFRESH after a failed change
3973 # Otherwise the OID-to-attribute mapping in
3974 # _apply_updates_in_file() won't work, because it
3975 # can't lookup the new OID in the schema
3976 self._ldap_schemaUpdateNow(samdb)
3978 samdb.modify_ldif(self.ldif, controls=['relax:0'])
3981 except ldb.LdbError as e:
3982 if self.can_ignore_failure(e):
3985 print("Exception: %s" % e)
3986 print("Encountered while trying to apply the following LDIF")
3987 print("----------------------------------------------------")
3988 print("%s" % self.ldif)
3994 class cmd_domain_schema_upgrade(Command):
3995 """Domain schema upgrading"""
3997 synopsis = "%prog [options]"
3999 takes_optiongroups = {
4000 "sambaopts": options.SambaOptions,
4001 "versionopts": options.VersionOptions,
4002 "credopts": options.CredentialsOptions,
4006 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
4007 metavar="URL", dest="H"),
4008 Option("--quiet", help="Be quiet", action="store_true"),
4009 Option("--verbose", help="Be verbose", action="store_true"),
4010 Option("--schema", type="choice", metavar="SCHEMA",
4011 choices=["2012", "2012_R2"],
4012 help="The schema file to upgrade to. Default is (Windows) 2012_R2.",
4014 Option("--ldf-file", type=str, default=None,
4015 help="Just apply the schema updates in the adprep/.LDF file(s) specified"),
4016 Option("--base-dir", type=str, default=None,
4017 help="Location of ldf files Default is ${SETUPDIR}/adprep.")
4020 def _apply_updates_in_file(self, samdb, ldif_file):
4022 Applies a series of updates specified in an .LDIF file. The .LDIF file
4023 is based on the adprep Schema updates provided by Microsoft.
4026 ldif_op = ldif_schema_update()
4028 # parse the file line by line and work out each update operation to apply
4029 for line in ldif_file:
4031 line = line.rstrip()
4033 # the operations in the .LDIF file are separated by blank lines. If
4034 # we hit a blank line, try to apply the update we've parsed so far
4037 # keep going if we haven't parsed anything yet
4038 if ldif_op.ldif == '':
4041 # Apply the individual change
4042 count += ldif_op.apply(samdb)
4044 # start storing the next operation from scratch again
4045 ldif_op = ldif_schema_update()
4048 # replace the placeholder domain name in the .ldif file with the real domain
4049 if line.upper().endswith('DC=X'):
4050 line = line[:-len('DC=X')] + str(samdb.get_default_basedn())
4051 elif line.upper().endswith('CN=X'):
4052 line = line[:-len('CN=X')] + str(samdb.get_default_basedn())
4054 values = line.split(':')
4056 if values[0].lower() == 'dn':
4057 ldif_op.dn = values[1].strip()
4059 # replace the Windows-specific operation with the Samba one
4060 if values[0].lower() == 'changetype':
4061 line = line.lower().replace(': ntdsschemaadd',
4063 line = line.lower().replace(': ntdsschemamodify',
4066 if values[0].lower() in ['rdnattid', 'subclassof',
4067 'systemposssuperiors',
4069 'systemauxiliaryclass']:
4072 # The Microsoft updates contain some OIDs we don't recognize.
4073 # Query the DB to see if we can work out the OID this update is
4074 # referring to. If we find a match, then replace the OID with
4075 # the ldapDisplayname
4077 res = samdb.search(base=samdb.get_schema_basedn(),
4078 expression="(|(attributeId=%s)(governsId=%s))" %
4080 attrs=['ldapDisplayName'])
4083 ldif_op.unknown_oid = value
4085 display_name = res[0]['ldapDisplayName'][0]
4086 line = line.replace(value, ' ' + display_name)
4088 # Microsoft has marked objects as defunct that Samba doesn't know about
4089 if values[0].lower() == 'isdefunct' and values[1].strip().lower() == 'true':
4090 ldif_op.is_defunct = True
4092 # Samba has added the showInAdvancedViewOnly attribute to all objects,
4093 # so rather than doing an add, we need to do a replace
4094 if values[0].lower() == 'add' and values[1].strip().lower() == 'showinadvancedviewonly':
4095 line = 'replace: showInAdvancedViewOnly'
4097 # Add the line to the current LDIF operation (including the newline
4098 # we stripped off at the start of the loop)
4099 ldif_op.ldif += line + '\n'
4104 def _apply_update(self, samdb, update_file, base_dir):
4105 """Wrapper function for parsing an LDIF file and applying the updates"""
4107 print("Applying %s updates..." % update_file)
4111 ldif_file = open(os.path.join(base_dir, update_file))
4113 count = self._apply_updates_in_file(samdb, ldif_file)
4119 print("%u changes applied" % count)
4123 def run(self, **kwargs):
4124 from samba.ms_schema_markdown import read_ms_markdown
4125 from samba.schema import Schema
4127 updates_allowed_overriden = False
4128 sambaopts = kwargs.get("sambaopts")
4129 credopts = kwargs.get("credopts")
4130 versionpts = kwargs.get("versionopts")
4131 lp = sambaopts.get_loadparm()
4132 creds = credopts.get_credentials(lp)
4134 target_schema = kwargs.get("schema")
4135 ldf_files = kwargs.get("ldf_file")
4136 base_dir = kwargs.get("base_dir")
4140 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
4142 # we're not going to get far if the config doesn't allow schema updates
4143 if lp.get("dsdb:schema update allowed") is None:
4144 lp.set("dsdb:schema update allowed", "yes")
4145 print("Temporarily overriding 'dsdb:schema update allowed' setting")
4146 updates_allowed_overriden = True
4148 own_dn = ldb.Dn(samdb, samdb.get_dsServiceName())
4149 master = get_fsmo_roleowner(samdb, str(samdb.get_schema_basedn()),
4151 if own_dn != master:
4152 raise CommandError("This server is not the schema master.")
4154 # if specific LDIF files were specified, just apply them
4156 schema_updates = ldf_files.split(",")
4160 # work out the version of the target schema we're upgrading to
4161 end = Schema.get_version(target_schema)
4163 # work out the version of the schema we're currently using
4164 res = samdb.search(base=samdb.get_schema_basedn(),
4165 scope=ldb.SCOPE_BASE, attrs=['objectVersion'])
4168 raise CommandError('Could not determine current schema version')
4169 start = int(res[0]['objectVersion'][0]) + 1
4171 diff_dir = setup_path("adprep/WindowsServerDocs")
4172 if base_dir is None:
4173 # Read from the Schema-Updates.md file
4174 temp_folder = tempfile.mkdtemp()
4176 update_file = setup_path("adprep/WindowsServerDocs/Schema-Updates.md")
4179 read_ms_markdown(update_file, temp_folder)
4180 except Exception as e:
4181 print("Exception in markdown parsing: %s" % e)
4182 shutil.rmtree(temp_folder)
4183 raise CommandError('Failed to upgrade schema')
4185 base_dir = temp_folder
4187 for version in range(start, end + 1):
4188 update = 'Sch%d.ldf' % version
4189 schema_updates.append(update)
4191 # Apply patches if we parsed the Schema-Updates.md file
4192 diff = os.path.abspath(os.path.join(diff_dir, update + '.diff'))
4193 if temp_folder and os.path.exists(diff):
4195 p = subprocess.Popen(['patch', update, '-i', diff],
4196 stdout=subprocess.PIPE,
4197 stderr=subprocess.PIPE, cwd=temp_folder)
4198 except (OSError, IOError):
4199 shutil.rmtree(temp_folder)
4200 raise CommandError("Failed to upgrade schema. Check if 'patch' is installed.")
4202 stdout, stderr = p.communicate()
4205 print("Exception in patch: %s\n%s" % (stdout, stderr))
4206 shutil.rmtree(temp_folder)
4207 raise CommandError('Failed to upgrade schema')
4209 print("Patched %s using %s" % (update, diff))
4211 if base_dir is None:
4212 base_dir = setup_path("adprep")
4214 samdb.transaction_start()
4216 error_encountered = False
4219 # Apply the schema updates needed to move to the new schema version
4220 for ldif_file in schema_updates:
4221 count += self._apply_update(samdb, ldif_file, base_dir)
4224 samdb.transaction_commit()
4225 print("Schema successfully updated")
4227 print("No changes applied to schema")
4228 samdb.transaction_cancel()
4229 except Exception as e:
4230 print("Exception: %s" % e)
4231 print("Error encountered, aborting schema upgrade")
4232 samdb.transaction_cancel()
4233 error_encountered = True
4235 if updates_allowed_overriden:
4236 lp.set("dsdb:schema update allowed", "no")
4239 shutil.rmtree(temp_folder)
4241 if error_encountered:
4242 raise CommandError('Failed to upgrade schema')
4244 class cmd_domain_functional_prep(Command):
4245 """Domain functional level preparation"""
4247 synopsis = "%prog [options]"
4249 takes_optiongroups = {
4250 "sambaopts": options.SambaOptions,
4251 "versionopts": options.VersionOptions,
4252 "credopts": options.CredentialsOptions,
4256 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
4257 metavar="URL", dest="H"),
4258 Option("--quiet", help="Be quiet", action="store_true"),
4259 Option("--verbose", help="Be verbose", action="store_true"),
4260 Option("--function-level", type="choice", metavar="FUNCTION_LEVEL",
4261 choices=["2008_R2", "2012", "2012_R2"],
4262 help="The schema file to upgrade to. Default is (Windows) 2012_R2.",
4264 Option("--forest-prep", action="store_true",
4265 help="Run the forest prep (by default, both the domain and forest prep are run)."),
4266 Option("--domain-prep", action="store_true",
4267 help="Run the domain prep (by default, both the domain and forest prep are run).")
4270 def run(self, **kwargs):
4271 updates_allowed_overriden = False
4272 sambaopts = kwargs.get("sambaopts")
4273 credopts = kwargs.get("credopts")
4274 versionpts = kwargs.get("versionopts")
4275 lp = sambaopts.get_loadparm()
4276 creds = credopts.get_credentials(lp)
4278 target_level = string_version_to_constant[kwargs.get("function_level")]
4279 forest_prep = kwargs.get("forest_prep")
4280 domain_prep = kwargs.get("domain_prep")
4282 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
4284 # we're not going to get far if the config doesn't allow schema updates
4285 if lp.get("dsdb:schema update allowed") is None:
4286 lp.set("dsdb:schema update allowed", "yes")
4287 print("Temporarily overriding 'dsdb:schema update allowed' setting")
4288 updates_allowed_overriden = True
4290 if forest_prep is None and domain_prep is None:
4294 own_dn = ldb.Dn(samdb, samdb.get_dsServiceName())
4296 master = get_fsmo_roleowner(samdb, str(samdb.get_schema_basedn()),
4298 if own_dn != master:
4299 raise CommandError("This server is not the schema master.")
4302 domain_dn = samdb.domain_dn()
4303 infrastructure_dn = "CN=Infrastructure," + domain_dn
4304 master = get_fsmo_roleowner(samdb, infrastructure_dn,
4306 if own_dn != master:
4307 raise CommandError("This server is not the infrastructure master.")
4310 samdb.transaction_start()
4311 error_encountered = False
4313 from samba.forest_update import ForestUpdate
4314 forest = ForestUpdate(samdb, fix=True)
4316 forest.check_updates_iterator([53, 79, 80, 81, 82, 83])
4317 forest.check_updates_functional_level(target_level,
4318 DS_DOMAIN_FUNCTION_2008_R2,
4319 update_revision=True)
4321 samdb.transaction_commit()
4322 except Exception as e:
4323 print("Exception: %s" % e)
4324 samdb.transaction_cancel()
4325 error_encountered = True
4328 samdb.transaction_start()
4329 error_encountered = False
4331 from samba.domain_update import DomainUpdate
4333 domain = DomainUpdate(samdb, fix=True)
4334 domain.check_updates_functional_level(target_level,
4335 DS_DOMAIN_FUNCTION_2008,
4336 update_revision=True)
4338 samdb.transaction_commit()
4339 except Exception as e:
4340 print("Exception: %s" % e)
4341 samdb.transaction_cancel()
4342 error_encountered = True
4344 if updates_allowed_overriden:
4345 lp.set("dsdb:schema update allowed", "no")
4347 if error_encountered:
4348 raise CommandError('Failed to perform functional prep')
4350 class cmd_domain(SuperCommand):
4351 """Domain management."""
4354 subcommands["demote"] = cmd_domain_demote()
4355 if cmd_domain_export_keytab is not None:
4356 subcommands["exportkeytab"] = cmd_domain_export_keytab()
4357 subcommands["info"] = cmd_domain_info()
4358 subcommands["provision"] = cmd_domain_provision()
4359 subcommands["join"] = cmd_domain_join()
4360 subcommands["dcpromo"] = cmd_domain_dcpromo()
4361 subcommands["level"] = cmd_domain_level()
4362 subcommands["passwordsettings"] = cmd_domain_passwordsettings()
4363 subcommands["classicupgrade"] = cmd_domain_classicupgrade()
4364 subcommands["samba3upgrade"] = cmd_domain_samba3upgrade()
4365 subcommands["trust"] = cmd_domain_trust()
4366 subcommands["tombstones"] = cmd_domain_tombstones()
4367 subcommands["schemaupgrade"] = cmd_domain_schema_upgrade()
4368 subcommands["functionalprep"] = cmd_domain_functional_prep()