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("--targetdir", metavar="DIR",
110 help="Set target directory (where to store provision)", type=str),
111 Option("--quiet", help="Be quiet", action="store_true"),
114 def get_testparm_var(testparm, smbconf, varname):
115 errfile = open(os.devnull, 'w')
116 p = subprocess.Popen([testparm, '-s', '-l',
117 '--parameter-name=%s' % varname, smbconf],
118 stdout=subprocess.PIPE, stderr=errfile)
119 (out,err) = p.communicate()
121 lines = out.split('\n')
123 return lines[0].strip()
127 import samba.dckeytab
129 cmd_domain_export_keytab = None
131 class cmd_domain_export_keytab(Command):
132 """Dump Kerberos keys of the domain into a keytab."""
134 synopsis = "%prog <keytab> [options]"
136 takes_optiongroups = {
137 "sambaopts": options.SambaOptions,
138 "credopts": options.CredentialsOptions,
139 "versionopts": options.VersionOptions,
143 Option("--principal", help="extract only this principal", type=str),
146 takes_args = ["keytab"]
148 def run(self, keytab, credopts=None, sambaopts=None, versionopts=None, principal=None):
149 lp = sambaopts.get_loadparm()
151 net.export_keytab(keytab=keytab, principal=principal)
154 class cmd_domain_info(Command):
155 """Print basic info about a domain and the DC passed as parameter."""
157 synopsis = "%prog <ip_address> [options]"
162 takes_optiongroups = {
163 "sambaopts": options.SambaOptions,
164 "credopts": options.CredentialsOptions,
165 "versionopts": options.VersionOptions,
168 takes_args = ["address"]
170 def run(self, address, credopts=None, sambaopts=None, versionopts=None):
171 lp = sambaopts.get_loadparm()
173 res = netcmd_get_domain_infos_via_cldap(lp, None, address)
175 raise CommandError("Invalid IP address '" + address + "'!")
176 self.outf.write("Forest : %s\n" % res.forest)
177 self.outf.write("Domain : %s\n" % res.dns_domain)
178 self.outf.write("Netbios domain : %s\n" % res.domain_name)
179 self.outf.write("DC name : %s\n" % res.pdc_dns_name)
180 self.outf.write("DC netbios name : %s\n" % res.pdc_name)
181 self.outf.write("Server site : %s\n" % res.server_site)
182 self.outf.write("Client site : %s\n" % res.client_site)
185 class cmd_domain_provision(Command):
186 """Provision a domain."""
188 synopsis = "%prog [options]"
190 takes_optiongroups = {
191 "sambaopts": options.SambaOptions,
192 "versionopts": options.VersionOptions,
196 Option("--interactive", help="Ask for names", action="store_true"),
197 Option("--domain", type="string", metavar="DOMAIN",
198 help="NetBIOS domain name to use"),
199 Option("--domain-guid", type="string", metavar="GUID",
200 help="set domainguid (otherwise random)"),
201 Option("--domain-sid", type="string", metavar="SID",
202 help="set domainsid (otherwise random)"),
203 Option("--ntds-guid", type="string", metavar="GUID",
204 help="set NTDS object GUID (otherwise random)"),
205 Option("--invocationid", type="string", metavar="GUID",
206 help="set invocationid (otherwise random)"),
207 Option("--host-name", type="string", metavar="HOSTNAME",
208 help="set hostname"),
209 Option("--host-ip", type="string", metavar="IPADDRESS",
210 help="set IPv4 ipaddress"),
211 Option("--host-ip6", type="string", metavar="IP6ADDRESS",
212 help="set IPv6 ipaddress"),
213 Option("--site", type="string", metavar="SITENAME",
214 help="set site name"),
215 Option("--adminpass", type="string", metavar="PASSWORD",
216 help="choose admin password (otherwise random)"),
217 Option("--krbtgtpass", type="string", metavar="PASSWORD",
218 help="choose krbtgt password (otherwise random)"),
219 Option("--machinepass", type="string", metavar="PASSWORD",
220 help="choose machine password (otherwise random)"),
221 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
222 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
223 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
224 "BIND9_FLATFILE uses bind9 text database to store zone information, "
225 "BIND9_DLZ uses samba4 AD to store zone information, "
226 "NONE skips the DNS setup entirely (not recommended)",
227 default="SAMBA_INTERNAL"),
228 Option("--dnspass", type="string", metavar="PASSWORD",
229 help="choose dns password (otherwise random)"),
230 Option("--root", type="string", metavar="USERNAME",
231 help="choose 'root' unix username"),
232 Option("--nobody", type="string", metavar="USERNAME",
233 help="choose 'nobody' user"),
234 Option("--users", type="string", metavar="GROUPNAME",
235 help="choose 'users' group"),
236 Option("--blank", action="store_true",
237 help="do not add users or groups, just the structure"),
238 Option("--server-role", type="choice", metavar="ROLE",
239 choices=["domain controller", "dc", "member server", "member", "standalone"],
240 help="The server role (domain controller | dc | member server | member | standalone). Default is dc.",
241 default="domain controller"),
242 Option("--function-level", type="choice", metavar="FOR-FUN-LEVEL",
243 choices=["2000", "2003", "2008", "2008_R2"],
244 help="The domain and forest function level (2000 | 2003 | 2008 | 2008_R2 - always native). Default is (Windows) 2008_R2 Native.",
246 Option("--base-schema", type="choice", metavar="BASE-SCHEMA",
247 choices=["2008_R2", "2008_R2_old", "2012", "2012_R2"],
248 help="The base schema files to use. Default is (Windows) 2008_R2.",
250 Option("--next-rid", type="int", metavar="NEXTRID", default=1000,
251 help="The initial nextRid value (only needed for upgrades). Default is 1000."),
252 Option("--partitions-only",
253 help="Configure Samba's partitions, but do not modify them (ie, join a BDC)", action="store_true"),
254 Option("--use-rfc2307", action="store_true", help="Use AD to store posix attributes (default = no)"),
255 Option("--plaintext-secrets", action="store_true",
256 help="Store secret/sensitive values as plain text on disk" +
257 "(default is to encrypt secret/ensitive values)"),
258 Option("--backend-store", type="choice", metavar="BACKENDSTORE",
259 choices=["tdb", "mdb"],
260 help="Specify the database backend to be used "
261 "(default is %s)" % get_default_backend_store()),
265 Option("--ldapadminpass", type="string", metavar="PASSWORD",
266 help="choose password to set between Samba and its LDAP backend (otherwise random)"),
267 Option("--ldap-backend-type", type="choice", metavar="LDAP-BACKEND-TYPE",
268 help="Test initialisation support for unsupported LDAP backend type (fedora-ds or openldap) DO NOT USE",
269 choices=["fedora-ds", "openldap"]),
270 Option("--ol-mmr-urls", type="string", metavar="LDAPSERVER",
271 help="List of LDAP-URLS [ ldap://<FQHN>:<PORT>/ (where <PORT> has to be different than 389!) ] separated with comma (\",\") for use with OpenLDAP-MMR (Multi-Master-Replication), e.g.: \"ldap://s4dc1:9000,ldap://s4dc2:9000\""),
272 Option("--ldap-dryrun-mode", help="Configure LDAP backend, but do not run any binaries and exit early. Used only for the test environment. DO NOT USE",
273 action="store_true"),
274 Option("--slapd-path", type="string", metavar="SLAPD-PATH",
275 help="Path to slapd for LDAP backend [e.g.:'/usr/local/libexec/slapd']. Required for Setup with LDAP-Backend. OpenLDAP Version >= 2.4.17 should be used."),
276 Option("--ldap-backend-extra-port", type="int", metavar="LDAP-BACKEND-EXTRA-PORT", help="Additional TCP port for LDAP backend server (to use for replication)"),
277 Option("--ldap-backend-forced-uri", type="string", metavar="LDAP-BACKEND-FORCED-URI",
278 help="Force the LDAP backend connection to be to a particular URI. Use this ONLY for 'existing' backends, or when debugging the interaction with the LDAP backend and you need to intercept the LDA"),
279 Option("--ldap-backend-nosync", help="Configure LDAP backend not to call fsync() (for performance in test environments)", action="store_true"),
283 Option("--use-ntvfs", action="store_true", help="Use NTVFS for the fileserver (default = no)"),
284 Option("--use-xattrs", type="choice", choices=["yes","no","auto"],
285 metavar="[yes|no|auto]",
286 help="Define if we should use the native fs capabilities or a tdb file for "
287 "storing attributes likes ntacl when --use-ntvfs is set. "
288 "auto tries to make an inteligent guess based on the user rights and system capabilities",
292 takes_options.extend(common_provision_join_options)
294 if os.getenv('TEST_LDAP', "no") == "yes":
295 takes_options.extend(openldap_options)
297 if samba.is_ntvfs_fileserver_built():
298 takes_options.extend(ntvfs_options)
302 def run(self, sambaopts=None, versionopts=None,
325 ldap_backend_type=None,
329 partitions_only=None,
336 ldap_backend_nosync=None,
337 ldap_backend_extra_port=None,
338 ldap_backend_forced_uri=None,
339 ldap_dryrun_mode=None,
341 plaintext_secrets=False,
344 self.logger = self.get_logger("provision")
346 self.logger.setLevel(logging.WARNING)
348 self.logger.setLevel(logging.INFO)
350 lp = sambaopts.get_loadparm()
351 smbconf = lp.configfile
353 if dns_forwarder is not None:
354 suggested_forwarder = dns_forwarder
356 suggested_forwarder = self._get_nameserver_ip()
357 if suggested_forwarder is None:
358 suggested_forwarder = "none"
360 if len(self.raw_argv) == 1:
364 from getpass import getpass
367 def ask(prompt, default=None):
368 if default is not None:
369 print("%s [%s]: " % (prompt, default), end=' ')
371 print("%s: " % (prompt,), end=' ')
372 return sys.stdin.readline().rstrip("\n") or default
375 default = socket.getfqdn().split(".", 1)[1].upper()
378 realm = ask("Realm", default)
379 if realm in (None, ""):
380 raise CommandError("No realm set!")
383 default = realm.split(".")[0]
386 domain = ask("Domain", default)
388 raise CommandError("No domain set!")
390 server_role = ask("Server Role (dc, member, standalone)", "dc")
392 dns_backend = ask("DNS backend (SAMBA_INTERNAL, BIND9_FLATFILE, BIND9_DLZ, NONE)", "SAMBA_INTERNAL")
393 if dns_backend in (None, ''):
394 raise CommandError("No DNS backend set!")
396 if dns_backend == "SAMBA_INTERNAL":
397 dns_forwarder = ask("DNS forwarder IP address (write 'none' to disable forwarding)", suggested_forwarder)
398 if dns_forwarder.lower() in (None, 'none'):
399 suggested_forwarder = None
403 adminpassplain = getpass("Administrator password: ")
404 issue = self._adminpass_issue(adminpassplain)
406 self.errf.write("%s.\n" % issue)
408 adminpassverify = getpass("Retype password: ")
409 if not adminpassplain == adminpassverify:
410 self.errf.write("Sorry, passwords do not match.\n")
412 adminpass = adminpassplain
416 realm = sambaopts._lp.get('realm')
418 raise CommandError("No realm set!")
420 raise CommandError("No domain set!")
423 issue = self._adminpass_issue(adminpass)
425 raise CommandError(issue)
427 self.logger.info("Administrator password will be set randomly!")
429 if function_level == "2000":
430 dom_for_fun_level = DS_DOMAIN_FUNCTION_2000
431 elif function_level == "2003":
432 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
433 elif function_level == "2008":
434 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008
435 elif function_level == "2008_R2":
436 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
438 if dns_backend == "SAMBA_INTERNAL" and dns_forwarder is None:
439 dns_forwarder = suggested_forwarder
441 samdb_fill = FILL_FULL
443 samdb_fill = FILL_NT4SYNC
444 elif partitions_only:
445 samdb_fill = FILL_DRS
447 if targetdir is not None:
448 if not os.path.isdir(targetdir):
453 if use_xattrs == "yes":
455 elif use_xattrs == "auto" and use_ntvfs == False:
457 elif use_ntvfs == False:
458 raise CommandError("--use-xattrs=no requires --use-ntvfs (not supported for production use). "
459 "Please re-run with --use-xattrs omitted.")
460 elif use_xattrs == "auto" and not lp.get("posix:eadb"):
462 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
464 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
467 samba.ntacls.setntacl(lp, file.name,
468 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
471 self.logger.info("You are not root or your system does not support xattr, using tdb backend for attributes. ")
476 self.logger.info("not using extended attributes to store ACLs and other metadata. If you intend to use this provision in production, rerun the script as root on a system supporting xattrs.")
477 if ldap_backend_type == "existing":
478 if ldap_backend_forced_uri is not None:
479 self.logger.warn("You have specified to use an existing LDAP server as the backend, please make sure an LDAP server is running at %s" % ldap_backend_forced_uri)
481 self.logger.info("You have specified to use an existing LDAP server as the backend, please make sure an LDAP server is running at the default location")
483 if ldap_backend_forced_uri is not None:
484 self.logger.warn("You have specified to use an fixed URI %s for connecting to your LDAP server backend. This is NOT RECOMMENDED, as our default communiation over ldapi:// is more secure and much less")
486 if domain_sid is not None:
487 domain_sid = security.dom_sid(domain_sid)
489 session = system_session()
490 if backend_store is None:
491 backend_store = get_default_backend_store()
493 result = provision(self.logger,
494 session, smbconf=smbconf, targetdir=targetdir,
495 samdb_fill=samdb_fill, realm=realm, domain=domain,
496 domainguid=domain_guid, domainsid=domain_sid,
498 hostip=host_ip, hostip6=host_ip6,
499 sitename=site, ntdsguid=ntds_guid,
500 invocationid=invocationid, adminpass=adminpass,
501 krbtgtpass=krbtgtpass, machinepass=machinepass,
502 dns_backend=dns_backend, dns_forwarder=dns_forwarder,
503 dnspass=dnspass, root=root, nobody=nobody,
505 serverrole=server_role, dom_for_fun_level=dom_for_fun_level,
506 backend_type=ldap_backend_type,
507 ldapadminpass=ldapadminpass, ol_mmr_urls=ol_mmr_urls, slapd_path=slapd_path,
508 useeadb=eadb, next_rid=next_rid, lp=lp, use_ntvfs=use_ntvfs,
509 use_rfc2307=use_rfc2307, skip_sysvolacl=False,
510 ldap_backend_extra_port=ldap_backend_extra_port,
511 ldap_backend_forced_uri=ldap_backend_forced_uri,
512 nosync=ldap_backend_nosync, ldap_dryrun_mode=ldap_dryrun_mode,
513 base_schema=base_schema,
514 plaintext_secrets=plaintext_secrets,
515 backend_store=backend_store)
517 except ProvisioningError as e:
518 raise CommandError("Provision failed", e)
520 result.report_logger(self.logger)
522 def _get_nameserver_ip(self):
523 """Grab the nameserver IP address from /etc/resolv.conf."""
525 RESOLV_CONF="/etc/resolv.conf"
527 if not path.isfile(RESOLV_CONF):
528 self.logger.warning("Failed to locate %s" % RESOLV_CONF)
533 handle = open(RESOLV_CONF, 'r')
535 if not line.startswith('nameserver'):
537 # we want the last non-space continuous string of the line
538 return line.strip().split()[-1]
540 if handle is not None:
543 self.logger.warning("No nameserver found in %s" % RESOLV_CONF)
545 def _adminpass_issue(self, adminpass):
546 """Returns error string for a bad administrator password,
547 or None if acceptable"""
549 if len(adminpass.decode('utf-8')) < DEFAULT_MIN_PWD_LENGTH:
550 return "Administrator password does not meet the default minimum" \
551 " password length requirement (%d characters)" \
552 % DEFAULT_MIN_PWD_LENGTH
553 elif not samba.check_password_quality(adminpass):
554 return "Administrator password does not meet the default" \
560 class cmd_domain_dcpromo(Command):
561 """Promote an existing domain member or NT4 PDC to an AD DC."""
563 synopsis = "%prog <dnsdomain> [DC|RODC] [options]"
565 takes_optiongroups = {
566 "sambaopts": options.SambaOptions,
567 "versionopts": options.VersionOptions,
568 "credopts": options.CredentialsOptions,
572 Option("--server", help="DC to join", type=str),
573 Option("--site", help="site to join", type=str),
574 Option("--domain-critical-only",
575 help="only replicate critical domain objects",
576 action="store_true"),
577 Option("--machinepass", type=str, metavar="PASSWORD",
578 help="choose machine password (otherwise random)"),
579 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
580 choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
581 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
582 "BIND9_DLZ uses samba4 AD to store zone information, "
583 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
584 default="SAMBA_INTERNAL"),
585 Option("--verbose", help="Be verbose", action="store_true")
588 takes_options.extend(common_provision_join_options)
591 Option("--use-ntvfs", action="store_true", help="Use NTVFS for the fileserver (default = no)"),
594 if samba.is_ntvfs_fileserver_built():
595 takes_options.extend(ntvfs_options)
598 takes_args = ["domain", "role?"]
600 def run(self, domain, role=None, sambaopts=None, credopts=None,
601 versionopts=None, server=None, site=None, targetdir=None,
602 domain_critical_only=False, parent_domain=None, machinepass=None,
603 use_ntvfs=False, dns_backend=None,
604 quiet=False, verbose=False):
605 lp = sambaopts.get_loadparm()
606 creds = credopts.get_credentials(lp)
607 net = Net(creds, lp, server=credopts.ipaddress)
609 logger = self.get_logger()
611 logger.setLevel(logging.DEBUG)
613 logger.setLevel(logging.WARNING)
615 logger.setLevel(logging.INFO)
617 netbios_name = lp.get("netbios name")
623 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
624 site=site, netbios_name=netbios_name, targetdir=targetdir,
625 domain_critical_only=domain_critical_only,
626 machinepass=machinepass, use_ntvfs=use_ntvfs,
627 dns_backend=dns_backend,
628 promote_existing=True)
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)
636 raise CommandError("Invalid role '%s' (possible values: DC, RODC)" % role)
639 class cmd_domain_join(Command):
640 """Join domain as either member or backup domain controller."""
642 synopsis = "%prog <dnsdomain> [DC|RODC|MEMBER|SUBDOMAIN] [options]"
644 takes_optiongroups = {
645 "sambaopts": options.SambaOptions,
646 "versionopts": options.VersionOptions,
647 "credopts": options.CredentialsOptions,
651 Option("--server", help="DC to join", type=str),
652 Option("--site", help="site to join", type=str),
653 Option("--parent-domain", help="parent domain to create subdomain under", type=str),
654 Option("--domain-critical-only",
655 help="only replicate critical domain objects",
656 action="store_true"),
657 Option("--machinepass", type=str, metavar="PASSWORD",
658 help="choose machine password (otherwise random)"),
659 Option("--adminpass", type="string", metavar="PASSWORD",
660 help="choose adminstrator password when joining as a subdomain (otherwise random)"),
661 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
662 choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
663 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
664 "BIND9_DLZ uses samba4 AD to store zone information, "
665 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
666 default="SAMBA_INTERNAL"),
667 Option("--plaintext-secrets", action="store_true",
668 help="Store secret/sensitive values as plain text on disk" +
669 "(default is to encrypt secret/ensitive values)"),
670 Option("--verbose", help="Be verbose", action="store_true")
674 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
677 takes_options.extend(common_provision_join_options)
679 if samba.is_ntvfs_fileserver_built():
680 takes_options.extend(ntvfs_options)
682 takes_args = ["domain", "role?"]
684 def run(self, domain, role=None, sambaopts=None, credopts=None,
685 versionopts=None, server=None, site=None, targetdir=None,
686 domain_critical_only=False, parent_domain=None, machinepass=None,
687 use_ntvfs=False, dns_backend=None, adminpass=None,
688 quiet=False, verbose=False, plaintext_secrets=False):
689 lp = sambaopts.get_loadparm()
690 creds = credopts.get_credentials(lp)
691 net = Net(creds, lp, server=credopts.ipaddress)
694 site = "Default-First-Site-Name"
696 logger = self.get_logger()
698 logger.setLevel(logging.DEBUG)
700 logger.setLevel(logging.WARNING)
702 logger.setLevel(logging.INFO)
704 netbios_name = lp.get("netbios name")
709 if role is None or role == "MEMBER":
710 (join_password, sid, domain_name) = net.join_member(
711 domain, netbios_name, LIBNET_JOIN_AUTOMATIC,
712 machinepass=machinepass)
714 self.errf.write("Joined domain %s (%s)\n" % (domain_name, sid))
716 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
717 site=site, netbios_name=netbios_name, targetdir=targetdir,
718 domain_critical_only=domain_critical_only,
719 machinepass=machinepass, use_ntvfs=use_ntvfs,
720 dns_backend=dns_backend,
721 plaintext_secrets=plaintext_secrets)
723 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
724 site=site, netbios_name=netbios_name, targetdir=targetdir,
725 domain_critical_only=domain_critical_only,
726 machinepass=machinepass, use_ntvfs=use_ntvfs,
727 dns_backend=dns_backend,
728 plaintext_secrets=plaintext_secrets)
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)
744 raise CommandError("Invalid role '%s' (possible values: MEMBER, DC, RODC, SUBDOMAIN)" % role)
747 class cmd_domain_demote(Command):
748 """Demote ourselves from the role of Domain Controller."""
750 synopsis = "%prog [options]"
753 Option("--server", help="writable DC to write demotion changes on", type=str),
754 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
755 metavar="URL", dest="H"),
756 Option("--remove-other-dead-server", help="Dead DC (name or NTDS GUID) "
757 "to remove ALL references to (rather than this DC)", type=str),
758 Option("--quiet", help="Be quiet", action="store_true"),
759 Option("--verbose", help="Be verbose", action="store_true"),
762 takes_optiongroups = {
763 "sambaopts": options.SambaOptions,
764 "credopts": options.CredentialsOptions,
765 "versionopts": options.VersionOptions,
768 def run(self, sambaopts=None, credopts=None,
769 versionopts=None, server=None,
770 remove_other_dead_server=None, H=None,
771 verbose=False, quiet=False):
772 lp = sambaopts.get_loadparm()
773 creds = credopts.get_credentials(lp)
774 net = Net(creds, lp, server=credopts.ipaddress)
776 logger = self.get_logger()
778 logger.setLevel(logging.DEBUG)
780 logger.setLevel(logging.WARNING)
782 logger.setLevel(logging.INFO)
784 if remove_other_dead_server is not None:
785 if server is not None:
786 samdb = SamDB(url="ldap://%s" % server,
787 session_info=system_session(),
788 credentials=creds, lp=lp)
790 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
792 remove_dc.remove_dc(samdb, logger, remove_other_dead_server)
793 except remove_dc.DemoteException as err:
794 raise CommandError("Demote failed: %s" % err)
797 netbios_name = lp.get("netbios name")
798 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
800 res = samdb.search(expression='(&(objectClass=computer)(serverReferenceBL=*))', attrs=["dnsHostName", "name"])
802 raise CommandError("Unable to search for servers")
805 raise CommandError("You are the latest server in the domain")
809 if str(e["name"]).lower() != netbios_name.lower():
810 server = e["dnsHostName"]
813 ntds_guid = samdb.get_ntds_GUID()
814 msg = samdb.search(base=str(samdb.get_config_basedn()),
815 scope=ldb.SCOPE_SUBTREE, expression="(objectGUID=%s)" % ntds_guid,
817 if len(msg) == 0 or "options" not in msg[0]:
818 raise CommandError("Failed to find options on %s" % ntds_guid)
821 dsa_options = int(str(msg[0]['options']))
823 res = samdb.search(expression="(fSMORoleOwner=%s)" % str(ntds_dn),
824 controls=["search_options:1:2"])
827 raise CommandError("Current DC is still the owner of %d role(s), use the role command to transfer roles to another DC" % len(res))
829 self.errf.write("Using %s as partner server for the demotion\n" %
831 (drsuapiBind, drsuapi_handle, supportedExtensions) = drsuapi_connect(server, lp, creds)
833 self.errf.write("Deactivating inbound replication\n")
838 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
839 dsa_options |= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
840 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
844 self.errf.write("Asking partner server %s to synchronize from us\n"
846 for part in (samdb.get_schema_basedn(),
847 samdb.get_config_basedn(),
848 samdb.get_root_basedn()):
849 nc = drsuapi.DsReplicaObjectIdentifier()
852 req1 = drsuapi.DsReplicaSyncRequest1()
853 req1.naming_context = nc;
854 req1.options = drsuapi.DRSUAPI_DRS_WRIT_REP
855 req1.source_dsa_guid = misc.GUID(ntds_guid)
858 drsuapiBind.DsReplicaSync(drsuapi_handle, 1, req1)
859 except RuntimeError as e1:
860 (werr, string) = e1.args
861 if werr == werror.WERR_DS_DRA_NO_REPLICA:
865 "Error while replicating out last local changes from '%s' for demotion, "
866 "re-enabling inbound replication\n" % part)
867 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
868 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
870 raise CommandError("Error while sending a DsReplicaSync for partition '%s'" % str(part), string)
872 remote_samdb = SamDB(url="ldap://%s" % server,
873 session_info=system_session(),
874 credentials=creds, lp=lp)
876 self.errf.write("Changing userControl and container\n")
877 res = remote_samdb.search(base=str(remote_samdb.domain_dn()),
878 expression="(&(objectClass=user)(sAMAccountName=%s$))" %
879 netbios_name.upper(),
880 attrs=["userAccountControl"])
882 uac = int(str(res[0]["userAccountControl"]))
884 except Exception as e:
885 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
887 "Error while demoting, re-enabling inbound replication\n")
888 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
889 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
891 raise CommandError("Error while changing account control", e)
894 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
896 "Error while demoting, re-enabling inbound replication")
897 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
898 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
900 raise CommandError("Unable to find object with samaccountName = %s$"
901 " in the remote dc" % netbios_name.upper())
905 uac &= ~(UF_SERVER_TRUST_ACCOUNT|UF_TRUSTED_FOR_DELEGATION|UF_PARTIAL_SECRETS_ACCOUNT)
906 uac |= UF_WORKSTATION_TRUST_ACCOUNT
911 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
912 ldb.FLAG_MOD_REPLACE,
913 "userAccountControl")
915 remote_samdb.modify(msg)
916 except Exception as e:
917 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
919 "Error while demoting, re-enabling inbound replication")
920 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
921 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
924 raise CommandError("Error while changing account control", e)
926 parent = msg.dn.parent()
927 dc_name = res[0].dn.get_rdn_value()
928 rdn = "CN=%s" % dc_name
930 # Let's move to the Computer container
934 computer_dn = ldb.Dn(remote_samdb, "CN=Computers,%s" % str(remote_samdb.domain_dn()))
935 res = remote_samdb.search(base=computer_dn, expression=rdn, scope=ldb.SCOPE_ONELEVEL)
938 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
939 scope=ldb.SCOPE_ONELEVEL)
940 while(len(res) != 0 and i < 100):
942 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
943 scope=ldb.SCOPE_ONELEVEL)
946 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
948 "Error while demoting, re-enabling inbound replication\n")
949 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
950 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
956 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
957 ldb.FLAG_MOD_REPLACE,
958 "userAccountControl")
960 remote_samdb.modify(msg)
962 raise CommandError("Unable to find a slot for renaming %s,"
963 " all names from %s-1 to %s-%d seemed used" %
964 (str(dc_dn), rdn, rdn, i - 9))
966 newrdn = "%s-%d" % (rdn, i)
969 newdn = ldb.Dn(remote_samdb, "%s,%s" % (newrdn, str(computer_dn)))
970 remote_samdb.rename(dc_dn, newdn)
971 except Exception as e:
972 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
974 "Error while demoting, re-enabling inbound replication\n")
975 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
976 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
982 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
983 ldb.FLAG_MOD_REPLACE,
984 "userAccountControl")
986 remote_samdb.modify(msg)
987 raise CommandError("Error while renaming %s to %s" % (str(dc_dn), str(newdn)), e)
990 server_dsa_dn = samdb.get_serverName()
991 domain = remote_samdb.get_root_basedn()
994 req1 = drsuapi.DsRemoveDSServerRequest1()
995 req1.server_dn = str(server_dsa_dn)
996 req1.domain_dn = str(domain)
999 drsuapiBind.DsRemoveDSServer(drsuapi_handle, 1, req1)
1000 except RuntimeError as e3:
1001 (werr, string) = e3.args
1002 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
1004 "Error while demoting, re-enabling inbound replication\n")
1005 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
1006 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
1012 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
1013 ldb.FLAG_MOD_REPLACE,
1014 "userAccountControl")
1015 remote_samdb.modify(msg)
1016 remote_samdb.rename(newdn, dc_dn)
1017 if werr == werror.WERR_DS_DRA_NO_REPLICA:
1018 raise CommandError("The DC %s is not present on (already removed from) the remote server: " % server_dsa_dn, e)
1020 raise CommandError("Error while sending a removeDsServer of %s: " % server_dsa_dn, e)
1022 remove_dc.remove_sysvol_references(remote_samdb, logger, dc_name)
1024 # These are objects under the computer account that should be deleted
1025 for s in ("CN=Enterprise,CN=NTFRS Subscriptions",
1026 "CN=%s, CN=NTFRS Subscriptions" % lp.get("realm"),
1027 "CN=Domain system Volumes (SYSVOL Share), CN=NTFRS Subscriptions",
1028 "CN=NTFRS Subscriptions"):
1030 remote_samdb.delete(ldb.Dn(remote_samdb,
1031 "%s,%s" % (s, str(newdn))))
1032 except ldb.LdbError as l:
1035 self.errf.write("Demote successful\n")
1038 class cmd_domain_level(Command):
1039 """Raise domain and forest function levels."""
1041 synopsis = "%prog (show|raise <options>) [options]"
1043 takes_optiongroups = {
1044 "sambaopts": options.SambaOptions,
1045 "credopts": options.CredentialsOptions,
1046 "versionopts": options.VersionOptions,
1050 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1051 metavar="URL", dest="H"),
1052 Option("--quiet", help="Be quiet", action="store_true"),
1053 Option("--forest-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
1054 help="The forest function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)"),
1055 Option("--domain-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
1056 help="The domain function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)")
1059 takes_args = ["subcommand"]
1061 def run(self, subcommand, H=None, forest_level=None, domain_level=None,
1062 quiet=False, credopts=None, sambaopts=None, versionopts=None):
1063 lp = sambaopts.get_loadparm()
1064 creds = credopts.get_credentials(lp, fallback_machine=True)
1066 samdb = SamDB(url=H, session_info=system_session(),
1067 credentials=creds, lp=lp)
1069 domain_dn = samdb.domain_dn()
1071 res_forest = samdb.search("CN=Partitions,%s" % samdb.get_config_basedn(),
1072 scope=ldb.SCOPE_BASE, attrs=["msDS-Behavior-Version"])
1073 assert len(res_forest) == 1
1075 res_domain = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1076 attrs=["msDS-Behavior-Version", "nTMixedDomain"])
1077 assert len(res_domain) == 1
1079 res_dc_s = samdb.search("CN=Sites,%s" % samdb.get_config_basedn(),
1080 scope=ldb.SCOPE_SUBTREE, expression="(objectClass=nTDSDSA)",
1081 attrs=["msDS-Behavior-Version"])
1082 assert len(res_dc_s) >= 1
1084 # default values, since "msDS-Behavior-Version" does not exist on Windows 2000 AD
1085 level_forest = DS_DOMAIN_FUNCTION_2000
1086 level_domain = DS_DOMAIN_FUNCTION_2000
1088 if "msDS-Behavior-Version" in res_forest[0]:
1089 level_forest = int(res_forest[0]["msDS-Behavior-Version"][0])
1090 if "msDS-Behavior-Version" in res_domain[0]:
1091 level_domain = int(res_domain[0]["msDS-Behavior-Version"][0])
1092 level_domain_mixed = int(res_domain[0]["nTMixedDomain"][0])
1095 for msg in res_dc_s:
1096 if "msDS-Behavior-Version" in msg:
1097 if min_level_dc is None or int(msg["msDS-Behavior-Version"][0]) < min_level_dc:
1098 min_level_dc = int(msg["msDS-Behavior-Version"][0])
1100 min_level_dc = DS_DOMAIN_FUNCTION_2000
1101 # well, this is the least
1104 if level_forest < DS_DOMAIN_FUNCTION_2000 or level_domain < DS_DOMAIN_FUNCTION_2000:
1105 raise CommandError("Domain and/or forest function level(s) is/are invalid. Correct them or reprovision!")
1106 if min_level_dc < DS_DOMAIN_FUNCTION_2000:
1107 raise CommandError("Lowest function level of a DC is invalid. Correct this or reprovision!")
1108 if level_forest > level_domain:
1109 raise CommandError("Forest function level is higher than the domain level(s). Correct this or reprovision!")
1110 if level_domain > min_level_dc:
1111 raise CommandError("Domain function level is higher than the lowest function level of a DC. Correct this or reprovision!")
1113 if subcommand == "show":
1114 self.message("Domain and forest function level for domain '%s'" % domain_dn)
1115 if level_forest == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1116 self.message("\nATTENTION: You run SAMBA 4 on a forest function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1117 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1118 self.message("\nATTENTION: You run SAMBA 4 on a domain function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1119 if min_level_dc == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1120 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)!")
1124 if level_forest == DS_DOMAIN_FUNCTION_2000:
1126 elif level_forest == DS_DOMAIN_FUNCTION_2003_MIXED:
1127 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1128 elif level_forest == DS_DOMAIN_FUNCTION_2003:
1130 elif level_forest == DS_DOMAIN_FUNCTION_2008:
1132 elif level_forest == DS_DOMAIN_FUNCTION_2008_R2:
1134 elif level_forest == DS_DOMAIN_FUNCTION_2012:
1136 elif level_forest == DS_DOMAIN_FUNCTION_2012_R2:
1139 outstr = "higher than 2012 R2"
1140 self.message("Forest function level: (Windows) " + outstr)
1142 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1143 outstr = "2000 mixed (NT4 DC support)"
1144 elif level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed == 0:
1146 elif level_domain == DS_DOMAIN_FUNCTION_2003_MIXED:
1147 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1148 elif level_domain == DS_DOMAIN_FUNCTION_2003:
1150 elif level_domain == DS_DOMAIN_FUNCTION_2008:
1152 elif level_domain == DS_DOMAIN_FUNCTION_2008_R2:
1154 elif level_domain == DS_DOMAIN_FUNCTION_2012:
1156 elif level_domain == DS_DOMAIN_FUNCTION_2012_R2:
1159 outstr = "higher than 2012 R2"
1160 self.message("Domain function level: (Windows) " + outstr)
1162 if min_level_dc == DS_DOMAIN_FUNCTION_2000:
1164 elif min_level_dc == DS_DOMAIN_FUNCTION_2003:
1166 elif min_level_dc == DS_DOMAIN_FUNCTION_2008:
1168 elif min_level_dc == DS_DOMAIN_FUNCTION_2008_R2:
1170 elif min_level_dc == DS_DOMAIN_FUNCTION_2012:
1172 elif min_level_dc == DS_DOMAIN_FUNCTION_2012_R2:
1175 outstr = "higher than 2012 R2"
1176 self.message("Lowest function level of a DC: (Windows) " + outstr)
1178 elif subcommand == "raise":
1181 if domain_level is not None:
1182 if domain_level == "2003":
1183 new_level_domain = DS_DOMAIN_FUNCTION_2003
1184 elif domain_level == "2008":
1185 new_level_domain = DS_DOMAIN_FUNCTION_2008
1186 elif domain_level == "2008_R2":
1187 new_level_domain = DS_DOMAIN_FUNCTION_2008_R2
1188 elif domain_level == "2012":
1189 new_level_domain = DS_DOMAIN_FUNCTION_2012
1190 elif domain_level == "2012_R2":
1191 new_level_domain = DS_DOMAIN_FUNCTION_2012_R2
1193 if new_level_domain <= level_domain and level_domain_mixed == 0:
1194 raise CommandError("Domain function level can't be smaller than or equal to the actual one!")
1195 if new_level_domain > min_level_dc:
1196 raise CommandError("Domain function level can't be higher than the lowest function level of a DC!")
1198 # Deactivate mixed/interim domain support
1199 if level_domain_mixed != 0:
1200 # Directly on the base DN
1202 m.dn = ldb.Dn(samdb, domain_dn)
1203 m["nTMixedDomain"] = ldb.MessageElement("0",
1204 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1208 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup") + ",CN=Partitions,%s" % samdb.get_config_basedn())
1209 m["nTMixedDomain"] = ldb.MessageElement("0",
1210 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1213 except ldb.LdbError as e:
1214 (enum, emsg) = e.args
1215 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1218 # Directly on the base DN
1220 m.dn = ldb.Dn(samdb, domain_dn)
1221 m["msDS-Behavior-Version"]= ldb.MessageElement(
1222 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1223 "msDS-Behavior-Version")
1227 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup")
1228 + ",CN=Partitions,%s" % samdb.get_config_basedn())
1229 m["msDS-Behavior-Version"]= ldb.MessageElement(
1230 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1231 "msDS-Behavior-Version")
1234 except ldb.LdbError as e2:
1235 (enum, emsg) = e2.args
1236 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1239 level_domain = new_level_domain
1240 msgs.append("Domain function level changed!")
1242 if forest_level is not None:
1243 if forest_level == "2003":
1244 new_level_forest = DS_DOMAIN_FUNCTION_2003
1245 elif forest_level == "2008":
1246 new_level_forest = DS_DOMAIN_FUNCTION_2008
1247 elif forest_level == "2008_R2":
1248 new_level_forest = DS_DOMAIN_FUNCTION_2008_R2
1249 elif forest_level == "2012":
1250 new_level_forest = DS_DOMAIN_FUNCTION_2012
1251 elif forest_level == "2012_R2":
1252 new_level_forest = DS_DOMAIN_FUNCTION_2012_R2
1254 if new_level_forest <= level_forest:
1255 raise CommandError("Forest function level can't be smaller than or equal to the actual one!")
1256 if new_level_forest > level_domain:
1257 raise CommandError("Forest function level can't be higher than the domain function level(s). Please raise it/them first!")
1260 m.dn = ldb.Dn(samdb, "CN=Partitions,%s" % samdb.get_config_basedn())
1261 m["msDS-Behavior-Version"]= ldb.MessageElement(
1262 str(new_level_forest), ldb.FLAG_MOD_REPLACE,
1263 "msDS-Behavior-Version")
1265 msgs.append("Forest function level changed!")
1266 msgs.append("All changes applied successfully!")
1267 self.message("\n".join(msgs))
1269 raise CommandError("invalid argument: '%s' (choose from 'show', 'raise')" % subcommand)
1271 class cmd_domain_passwordsettings_show(Command):
1272 """Display current password settings for the domain."""
1274 synopsis = "%prog [options]"
1276 takes_optiongroups = {
1277 "sambaopts": options.SambaOptions,
1278 "versionopts": options.VersionOptions,
1279 "credopts": options.CredentialsOptions,
1283 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1284 metavar="URL", dest="H"),
1287 def run(self, H=None, credopts=None, sambaopts=None, versionopts=None):
1288 lp = sambaopts.get_loadparm()
1289 creds = credopts.get_credentials(lp)
1291 samdb = SamDB(url=H, session_info=system_session(),
1292 credentials=creds, lp=lp)
1294 domain_dn = samdb.domain_dn()
1295 res = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1296 attrs=["pwdProperties", "pwdHistoryLength", "minPwdLength",
1297 "minPwdAge", "maxPwdAge", "lockoutDuration", "lockoutThreshold",
1298 "lockOutObservationWindow"])
1299 assert(len(res) == 1)
1301 pwd_props = int(res[0]["pwdProperties"][0])
1302 pwd_hist_len = int(res[0]["pwdHistoryLength"][0])
1303 cur_min_pwd_len = int(res[0]["minPwdLength"][0])
1305 cur_min_pwd_age = int(abs(int(res[0]["minPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1306 if int(res[0]["maxPwdAge"][0]) == -0x8000000000000000:
1309 cur_max_pwd_age = int(abs(int(res[0]["maxPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1310 cur_account_lockout_threshold = int(res[0]["lockoutThreshold"][0])
1312 if int(res[0]["lockoutDuration"][0]) == -0x8000000000000000:
1313 cur_account_lockout_duration = 0
1315 cur_account_lockout_duration = abs(int(res[0]["lockoutDuration"][0])) / (1e7 * 60)
1316 cur_reset_account_lockout_after = abs(int(res[0]["lockOutObservationWindow"][0])) / (1e7 * 60)
1317 except Exception as e:
1318 raise CommandError("Could not retrieve password properties!", e)
1320 self.message("Password informations for domain '%s'" % domain_dn)
1322 if pwd_props & DOMAIN_PASSWORD_COMPLEX != 0:
1323 self.message("Password complexity: on")
1325 self.message("Password complexity: off")
1326 if pwd_props & DOMAIN_PASSWORD_STORE_CLEARTEXT != 0:
1327 self.message("Store plaintext passwords: on")
1329 self.message("Store plaintext passwords: off")
1330 self.message("Password history length: %d" % pwd_hist_len)
1331 self.message("Minimum password length: %d" % cur_min_pwd_len)
1332 self.message("Minimum password age (days): %d" % cur_min_pwd_age)
1333 self.message("Maximum password age (days): %d" % cur_max_pwd_age)
1334 self.message("Account lockout duration (mins): %d" % cur_account_lockout_duration)
1335 self.message("Account lockout threshold (attempts): %d" % cur_account_lockout_threshold)
1336 self.message("Reset account lockout after (mins): %d" % cur_reset_account_lockout_after)
1338 class cmd_domain_passwordsettings_set(Command):
1339 """Set password settings.
1341 Password complexity, password lockout policy, history length,
1342 minimum password length, the minimum and maximum password age) on
1343 a Samba AD DC server.
1345 Use against a Windows DC is possible, but group policy will override it.
1348 synopsis = "%prog <options> [options]"
1350 takes_optiongroups = {
1351 "sambaopts": options.SambaOptions,
1352 "versionopts": options.VersionOptions,
1353 "credopts": options.CredentialsOptions,
1357 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1358 metavar="URL", dest="H"),
1359 Option("--quiet", help="Be quiet", action="store_true"),
1360 Option("--complexity", type="choice", choices=["on","off","default"],
1361 help="The password complexity (on | off | default). Default is 'on'"),
1362 Option("--store-plaintext", type="choice", choices=["on","off","default"],
1363 help="Store plaintext passwords where account have 'store passwords with reversible encryption' set (on | off | default). Default is 'off'"),
1364 Option("--history-length",
1365 help="The password history length (<integer> | default). Default is 24.", type=str),
1366 Option("--min-pwd-length",
1367 help="The minimum password length (<integer> | default). Default is 7.", type=str),
1368 Option("--min-pwd-age",
1369 help="The minimum password age (<integer in days> | default). Default is 1.", type=str),
1370 Option("--max-pwd-age",
1371 help="The maximum password age (<integer in days> | default). Default is 43.", type=str),
1372 Option("--account-lockout-duration",
1373 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),
1374 Option("--account-lockout-threshold",
1375 help="The number of bad password attempts allowed before locking out the account (<integer> | default). Default is 0 (never lock out).", type=str),
1376 Option("--reset-account-lockout-after",
1377 help="After this time is elapsed, the recorded number of attempts restarts from zero (<integer> | default). Default is 30.", type=str),
1380 def run(self, H=None, min_pwd_age=None, max_pwd_age=None,
1381 quiet=False, complexity=None, store_plaintext=None, history_length=None,
1382 min_pwd_length=None, account_lockout_duration=None, account_lockout_threshold=None,
1383 reset_account_lockout_after=None, credopts=None, sambaopts=None,
1385 lp = sambaopts.get_loadparm()
1386 creds = credopts.get_credentials(lp)
1388 samdb = SamDB(url=H, session_info=system_session(),
1389 credentials=creds, lp=lp)
1391 domain_dn = samdb.domain_dn()
1394 m.dn = ldb.Dn(samdb, domain_dn)
1395 pwd_props = int(samdb.get_pwdProperties())
1397 if complexity is not None:
1398 if complexity == "on" or complexity == "default":
1399 pwd_props = pwd_props | DOMAIN_PASSWORD_COMPLEX
1400 msgs.append("Password complexity activated!")
1401 elif complexity == "off":
1402 pwd_props = pwd_props & (~DOMAIN_PASSWORD_COMPLEX)
1403 msgs.append("Password complexity deactivated!")
1405 if store_plaintext is not None:
1406 if store_plaintext == "on" or store_plaintext == "default":
1407 pwd_props = pwd_props | DOMAIN_PASSWORD_STORE_CLEARTEXT
1408 msgs.append("Plaintext password storage for changed passwords activated!")
1409 elif store_plaintext == "off":
1410 pwd_props = pwd_props & (~DOMAIN_PASSWORD_STORE_CLEARTEXT)
1411 msgs.append("Plaintext password storage for changed passwords deactivated!")
1413 if complexity is not None or store_plaintext is not None:
1414 m["pwdProperties"] = ldb.MessageElement(str(pwd_props),
1415 ldb.FLAG_MOD_REPLACE, "pwdProperties")
1417 if history_length is not None:
1418 if history_length == "default":
1421 pwd_hist_len = int(history_length)
1423 if pwd_hist_len < 0 or pwd_hist_len > 24:
1424 raise CommandError("Password history length must be in the range of 0 to 24!")
1426 m["pwdHistoryLength"] = ldb.MessageElement(str(pwd_hist_len),
1427 ldb.FLAG_MOD_REPLACE, "pwdHistoryLength")
1428 msgs.append("Password history length changed!")
1430 if min_pwd_length is not None:
1431 if min_pwd_length == "default":
1434 min_pwd_len = int(min_pwd_length)
1436 if min_pwd_len < 0 or min_pwd_len > 14:
1437 raise CommandError("Minimum password length must be in the range of 0 to 14!")
1439 m["minPwdLength"] = ldb.MessageElement(str(min_pwd_len),
1440 ldb.FLAG_MOD_REPLACE, "minPwdLength")
1441 msgs.append("Minimum password length changed!")
1443 if min_pwd_age is not None:
1444 if min_pwd_age == "default":
1447 min_pwd_age = int(min_pwd_age)
1449 if min_pwd_age < 0 or min_pwd_age > 998:
1450 raise CommandError("Minimum password age must be in the range of 0 to 998!")
1453 min_pwd_age_ticks = -int(min_pwd_age * (24 * 60 * 60 * 1e7))
1455 m["minPwdAge"] = ldb.MessageElement(str(min_pwd_age_ticks),
1456 ldb.FLAG_MOD_REPLACE, "minPwdAge")
1457 msgs.append("Minimum password age changed!")
1459 if max_pwd_age is not None:
1460 if max_pwd_age == "default":
1463 max_pwd_age = int(max_pwd_age)
1465 if max_pwd_age < 0 or max_pwd_age > 999:
1466 raise CommandError("Maximum password age must be in the range of 0 to 999!")
1469 if max_pwd_age == 0:
1470 max_pwd_age_ticks = -0x8000000000000000
1472 max_pwd_age_ticks = -int(max_pwd_age * (24 * 60 * 60 * 1e7))
1474 m["maxPwdAge"] = ldb.MessageElement(str(max_pwd_age_ticks),
1475 ldb.FLAG_MOD_REPLACE, "maxPwdAge")
1476 msgs.append("Maximum password age changed!")
1478 if account_lockout_duration is not None:
1479 if account_lockout_duration == "default":
1480 account_lockout_duration = 30
1482 account_lockout_duration = int(account_lockout_duration)
1484 if account_lockout_duration < 0 or account_lockout_duration > 99999:
1485 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1488 if account_lockout_duration == 0:
1489 account_lockout_duration_ticks = -0x8000000000000000
1491 account_lockout_duration_ticks = -int(account_lockout_duration * (60 * 1e7))
1493 m["lockoutDuration"] = ldb.MessageElement(str(account_lockout_duration_ticks),
1494 ldb.FLAG_MOD_REPLACE, "lockoutDuration")
1495 msgs.append("Account lockout duration changed!")
1497 if account_lockout_threshold is not None:
1498 if account_lockout_threshold == "default":
1499 account_lockout_threshold = 0
1501 account_lockout_threshold = int(account_lockout_threshold)
1503 m["lockoutThreshold"] = ldb.MessageElement(str(account_lockout_threshold),
1504 ldb.FLAG_MOD_REPLACE, "lockoutThreshold")
1505 msgs.append("Account lockout threshold changed!")
1507 if reset_account_lockout_after is not None:
1508 if reset_account_lockout_after == "default":
1509 reset_account_lockout_after = 30
1511 reset_account_lockout_after = int(reset_account_lockout_after)
1513 if reset_account_lockout_after < 0 or reset_account_lockout_after > 99999:
1514 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1517 if reset_account_lockout_after == 0:
1518 reset_account_lockout_after_ticks = -0x8000000000000000
1520 reset_account_lockout_after_ticks = -int(reset_account_lockout_after * (60 * 1e7))
1522 m["lockOutObservationWindow"] = ldb.MessageElement(str(reset_account_lockout_after_ticks),
1523 ldb.FLAG_MOD_REPLACE, "lockOutObservationWindow")
1524 msgs.append("Duration to reset account lockout after changed!")
1526 if max_pwd_age > 0 and min_pwd_age >= max_pwd_age:
1527 raise CommandError("Maximum password age (%d) must be greater than minimum password age (%d)!" % (max_pwd_age, min_pwd_age))
1530 raise CommandError("You must specify at least one option to set. Try --help")
1532 msgs.append("All changes applied successfully!")
1533 self.message("\n".join(msgs))
1535 class cmd_domain_passwordsettings(SuperCommand):
1536 """Manage password policy settings."""
1539 subcommands["show"] = cmd_domain_passwordsettings_show()
1540 subcommands["set"] = cmd_domain_passwordsettings_set()
1542 class cmd_domain_classicupgrade(Command):
1543 """Upgrade from Samba classic (NT4-like) database to Samba AD DC database.
1545 Specify either a directory with all Samba classic DC databases and state files (with --dbdir) or
1546 the testparm utility from your classic installation (with --testparm).
1549 synopsis = "%prog [options] <classic_smb_conf>"
1551 takes_optiongroups = {
1552 "sambaopts": options.SambaOptions,
1553 "versionopts": options.VersionOptions
1557 Option("--dbdir", type="string", metavar="DIR",
1558 help="Path to samba classic DC database directory"),
1559 Option("--testparm", type="string", metavar="PATH",
1560 help="Path to samba classic DC testparm utility from the previous installation. This allows the default paths of the previous installation to be followed"),
1561 Option("--targetdir", type="string", metavar="DIR",
1562 help="Path prefix where the new Samba 4.0 AD domain should be initialised"),
1563 Option("--quiet", help="Be quiet", action="store_true"),
1564 Option("--verbose", help="Be verbose", action="store_true"),
1565 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
1566 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
1567 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
1568 "BIND9_FLATFILE uses bind9 text database to store zone information, "
1569 "BIND9_DLZ uses samba4 AD to store zone information, "
1570 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
1571 default="SAMBA_INTERNAL")
1575 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
1576 action="store_true"),
1577 Option("--use-xattrs", type="choice", choices=["yes","no","auto"],
1578 metavar="[yes|no|auto]",
1579 help="Define if we should use the native fs capabilities or a tdb file for "
1580 "storing attributes likes ntacl when --use-ntvfs is set. "
1581 "auto tries to make an inteligent guess based on the user rights and system capabilities",
1584 if samba.is_ntvfs_fileserver_built():
1585 takes_options.extend(ntvfs_options)
1587 takes_args = ["smbconf"]
1589 def run(self, smbconf=None, targetdir=None, dbdir=None, testparm=None,
1590 quiet=False, verbose=False, use_xattrs="auto", sambaopts=None, versionopts=None,
1591 dns_backend=None, use_ntvfs=False):
1593 if not os.path.exists(smbconf):
1594 raise CommandError("File %s does not exist" % smbconf)
1596 if testparm and not os.path.exists(testparm):
1597 raise CommandError("Testparm utility %s does not exist" % testparm)
1599 if dbdir and not os.path.exists(dbdir):
1600 raise CommandError("Directory %s does not exist" % dbdir)
1602 if not dbdir and not testparm:
1603 raise CommandError("Please specify either dbdir or testparm")
1605 logger = self.get_logger()
1607 logger.setLevel(logging.DEBUG)
1609 logger.setLevel(logging.WARNING)
1611 logger.setLevel(logging.INFO)
1613 if dbdir and testparm:
1614 logger.warning("both dbdir and testparm specified, ignoring dbdir.")
1617 lp = sambaopts.get_loadparm()
1619 s3conf = s3param.get_context()
1622 s3conf.set("realm", sambaopts.realm)
1624 if targetdir is not None:
1625 if not os.path.isdir(targetdir):
1629 if use_xattrs == "yes":
1631 elif use_xattrs == "auto" and use_ntvfs == False:
1633 elif use_ntvfs == False:
1634 raise CommandError("--use-xattrs=no requires --use-ntvfs (not supported for production use). "
1635 "Please re-run with --use-xattrs omitted.")
1636 elif use_xattrs == "auto" and not s3conf.get("posix:eadb"):
1638 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
1640 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
1643 samba.ntacls.setntacl(lp, tmpfile.name,
1644 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
1647 # FIXME: Don't catch all exceptions here
1648 logger.info("You are not root or your system does not support xattr, using tdb backend for attributes. "
1649 "If you intend to use this provision in production, rerun the script as root on a system supporting xattrs.")
1653 # Set correct default values from dbdir or testparm
1656 paths["state directory"] = dbdir
1657 paths["private dir"] = dbdir
1658 paths["lock directory"] = dbdir
1659 paths["smb passwd file"] = dbdir + "/smbpasswd"
1661 paths["state directory"] = get_testparm_var(testparm, smbconf, "state directory")
1662 paths["private dir"] = get_testparm_var(testparm, smbconf, "private dir")
1663 paths["smb passwd file"] = get_testparm_var(testparm, smbconf, "smb passwd file")
1664 paths["lock directory"] = get_testparm_var(testparm, smbconf, "lock directory")
1665 # "testparm" from Samba 3 < 3.4.x is not aware of the parameter
1666 # "state directory", instead make use of "lock directory"
1667 if len(paths["state directory"]) == 0:
1668 paths["state directory"] = paths["lock directory"]
1671 s3conf.set(p, paths[p])
1673 # load smb.conf parameters
1674 logger.info("Reading smb.conf")
1675 s3conf.load(smbconf)
1676 samba3 = Samba3(smbconf, s3conf)
1678 logger.info("Provisioning")
1679 upgrade_from_samba3(samba3, logger, targetdir, session_info=system_session(),
1680 useeadb=eadb, dns_backend=dns_backend, use_ntvfs=use_ntvfs)
1683 class cmd_domain_samba3upgrade(cmd_domain_classicupgrade):
1684 __doc__ = cmd_domain_classicupgrade.__doc__
1686 # This command is present for backwards compatibility only,
1687 # and should not be shown.
1691 class LocalDCCredentialsOptions(options.CredentialsOptions):
1692 def __init__(self, parser):
1693 options.CredentialsOptions.__init__(self, parser, special_name="local-dc")
1695 class DomainTrustCommand(Command):
1696 """List domain trusts."""
1699 Command.__init__(self)
1700 self.local_lp = None
1702 self.local_server = None
1703 self.local_binding_string = None
1704 self.local_creds = None
1706 self.remote_server = None
1707 self.remote_binding_string = None
1708 self.remote_creds = None
1710 def _uint32(self, v):
1711 return ctypes.c_uint32(v).value
1713 def check_runtime_error(self, runtime, val):
1717 err32 = self._uint32(runtime[0])
1723 class LocalRuntimeError(CommandError):
1724 def __init__(exception_self, self, runtime, message):
1725 err32 = self._uint32(runtime[0])
1727 msg = "LOCAL_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1728 self.local_server, message, err32, errstr)
1729 CommandError.__init__(exception_self, msg)
1731 class RemoteRuntimeError(CommandError):
1732 def __init__(exception_self, self, runtime, message):
1733 err32 = self._uint32(runtime[0])
1735 msg = "REMOTE_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1736 self.remote_server, message, err32, errstr)
1737 CommandError.__init__(exception_self, msg)
1739 class LocalLdbError(CommandError):
1740 def __init__(exception_self, self, ldb_error, message):
1741 errval = ldb_error[0]
1742 errstr = ldb_error[1]
1743 msg = "LOCAL_DC[%s]: %s - ERROR(%d) - %s" % (
1744 self.local_server, message, errval, errstr)
1745 CommandError.__init__(exception_self, msg)
1747 def setup_local_server(self, sambaopts, localdcopts):
1748 if self.local_server is not None:
1749 return self.local_server
1751 lp = sambaopts.get_loadparm()
1753 local_server = localdcopts.ipaddress
1754 if local_server is None:
1755 server_role = lp.server_role()
1756 if server_role != "ROLE_ACTIVE_DIRECTORY_DC":
1757 raise CommandError("Invalid server_role %s" % (server_role))
1758 local_server = lp.get('netbios name')
1759 local_transport = "ncalrpc"
1760 local_binding_options = ""
1761 local_binding_options += ",auth_type=ncalrpc_as_system"
1762 local_ldap_url = None
1765 local_transport = "ncacn_np"
1766 local_binding_options = ""
1767 local_ldap_url = "ldap://%s" % local_server
1768 local_creds = localdcopts.get_credentials(lp)
1772 self.local_server = local_server
1773 self.local_binding_string = "%s:%s[%s]" % (local_transport, local_server, local_binding_options)
1774 self.local_ldap_url = local_ldap_url
1775 self.local_creds = local_creds
1776 return self.local_server
1778 def new_local_lsa_connection(self):
1779 return lsa.lsarpc(self.local_binding_string, self.local_lp, self.local_creds)
1781 def new_local_netlogon_connection(self):
1782 return netlogon.netlogon(self.local_binding_string, self.local_lp, self.local_creds)
1784 def new_local_ldap_connection(self):
1785 return SamDB(url=self.local_ldap_url,
1786 session_info=system_session(),
1787 credentials=self.local_creds,
1790 def setup_remote_server(self, credopts, domain,
1792 require_writable=True):
1795 assert require_writable
1797 if self.remote_server is not None:
1798 return self.remote_server
1800 self.remote_server = "__unknown__remote_server__.%s" % domain
1801 assert self.local_server is not None
1803 remote_creds = credopts.get_credentials(self.local_lp)
1804 remote_server = credopts.ipaddress
1805 remote_binding_options = ""
1807 # TODO: we should also support NT4 domains
1808 # we could use local_netlogon.netr_DsRGetDCNameEx2() with the remote domain name
1809 # and delegate NBT or CLDAP to the local netlogon server
1811 remote_net = Net(remote_creds, self.local_lp, server=remote_server)
1812 remote_flags = nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS
1813 if require_writable:
1814 remote_flags |= nbt.NBT_SERVER_WRITABLE
1816 remote_flags |= nbt.NBT_SERVER_PDC
1817 remote_info = remote_net.finddc(flags=remote_flags, domain=domain, address=remote_server)
1818 except NTSTATUSError as error:
1819 raise CommandError("Failed to find a writeable DC for domain '%s': %s" %
1822 raise CommandError("Failed to find a writeable DC for domain '%s'" % domain)
1824 nbt.NBT_SERVER_PDC: "PDC",
1825 nbt.NBT_SERVER_GC: "GC",
1826 nbt.NBT_SERVER_LDAP: "LDAP",
1827 nbt.NBT_SERVER_DS: "DS",
1828 nbt.NBT_SERVER_KDC: "KDC",
1829 nbt.NBT_SERVER_TIMESERV: "TIMESERV",
1830 nbt.NBT_SERVER_CLOSEST: "CLOSEST",
1831 nbt.NBT_SERVER_WRITABLE: "WRITABLE",
1832 nbt.NBT_SERVER_GOOD_TIMESERV: "GOOD_TIMESERV",
1833 nbt.NBT_SERVER_NDNC: "NDNC",
1834 nbt.NBT_SERVER_SELECT_SECRET_DOMAIN_6: "SELECT_SECRET_DOMAIN_6",
1835 nbt.NBT_SERVER_FULL_SECRET_DOMAIN_6: "FULL_SECRET_DOMAIN_6",
1836 nbt.NBT_SERVER_ADS_WEB_SERVICE: "ADS_WEB_SERVICE",
1837 nbt.NBT_SERVER_DS_8: "DS_8",
1838 nbt.NBT_SERVER_HAS_DNS_NAME: "HAS_DNS_NAME",
1839 nbt.NBT_SERVER_IS_DEFAULT_NC: "IS_DEFAULT_NC",
1840 nbt.NBT_SERVER_FOREST_ROOT: "FOREST_ROOT",
1842 server_type_string = self.generic_bitmap_to_string(flag_map,
1843 remote_info.server_type, names_only=True)
1844 self.outf.write("RemoteDC Netbios[%s] DNS[%s] ServerType[%s]\n" % (
1845 remote_info.pdc_name,
1846 remote_info.pdc_dns_name,
1847 server_type_string))
1849 self.remote_server = remote_info.pdc_dns_name
1850 self.remote_binding_string="ncacn_np:%s[%s]" % (self.remote_server, remote_binding_options)
1851 self.remote_creds = remote_creds
1852 return self.remote_server
1854 def new_remote_lsa_connection(self):
1855 return lsa.lsarpc(self.remote_binding_string, self.local_lp, self.remote_creds)
1857 def new_remote_netlogon_connection(self):
1858 return netlogon.netlogon(self.remote_binding_string, self.local_lp, self.remote_creds)
1860 def get_lsa_info(self, conn, policy_access):
1861 objectAttr = lsa.ObjectAttribute()
1862 objectAttr.sec_qos = lsa.QosInfo()
1864 policy = conn.OpenPolicy2(''.decode('utf-8'),
1865 objectAttr, policy_access)
1867 info = conn.QueryInfoPolicy2(policy, lsa.LSA_POLICY_INFO_DNS)
1869 return (policy, info)
1871 def get_netlogon_dc_info(self, conn, server):
1872 info = conn.netr_DsRGetDCNameEx2(server,
1873 None, 0, None, None, None,
1874 netlogon.DS_RETURN_DNS_NAME)
1877 def netr_DomainTrust_to_name(self, t):
1878 if t.trust_type == lsa.LSA_TRUST_TYPE_DOWNLEVEL:
1879 return t.netbios_name
1883 def netr_DomainTrust_to_type(self, a, t):
1885 primary_parent = None
1887 if _t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
1889 if not _t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1890 primary_parent = a[_t.parent_index]
1893 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1894 if t is primary_parent:
1897 if t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1900 parent = a[t.parent_index]
1901 if parent is primary:
1906 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1911 def netr_DomainTrust_to_transitive(self, t):
1912 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1915 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE:
1918 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1923 def netr_DomainTrust_to_direction(self, t):
1924 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND and \
1925 t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1928 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND:
1931 if t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1936 def generic_enum_to_string(self, e_dict, v, names_only=False):
1940 v32 = self._uint32(v)
1941 w = "__unknown__%08X__" % v32
1943 r = "0x%x (%s)" % (v, w)
1946 def generic_bitmap_to_string(self, b_dict, v, names_only=False):
1951 for b in sorted(b_dict.keys()):
1958 c32 = self._uint32(c)
1959 s += ["__unknown_%08X__" % c32]
1964 r = "0x%x (%s)" % (v, w)
1967 def trustType_string(self, v):
1969 lsa.LSA_TRUST_TYPE_DOWNLEVEL : "DOWNLEVEL",
1970 lsa.LSA_TRUST_TYPE_UPLEVEL : "UPLEVEL",
1971 lsa.LSA_TRUST_TYPE_MIT : "MIT",
1972 lsa.LSA_TRUST_TYPE_DCE : "DCE",
1974 return self.generic_enum_to_string(types, v)
1976 def trustDirection_string(self, v):
1978 lsa.LSA_TRUST_DIRECTION_INBOUND |
1979 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "BOTH",
1980 lsa.LSA_TRUST_DIRECTION_INBOUND : "INBOUND",
1981 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "OUTBOUND",
1983 return self.generic_enum_to_string(directions, v)
1985 def trustAttributes_string(self, v):
1987 lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE : "NON_TRANSITIVE",
1988 lsa.LSA_TRUST_ATTRIBUTE_UPLEVEL_ONLY : "UPLEVEL_ONLY",
1989 lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN : "QUARANTINED_DOMAIN",
1990 lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE : "FOREST_TRANSITIVE",
1991 lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION : "CROSS_ORGANIZATION",
1992 lsa.LSA_TRUST_ATTRIBUTE_WITHIN_FOREST : "WITHIN_FOREST",
1993 lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL : "TREAT_AS_EXTERNAL",
1994 lsa.LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION : "USES_RC4_ENCRYPTION",
1996 return self.generic_bitmap_to_string(attributes, v)
1998 def kerb_EncTypes_string(self, v):
2000 security.KERB_ENCTYPE_DES_CBC_CRC : "DES_CBC_CRC",
2001 security.KERB_ENCTYPE_DES_CBC_MD5 : "DES_CBC_MD5",
2002 security.KERB_ENCTYPE_RC4_HMAC_MD5 : "RC4_HMAC_MD5",
2003 security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96 : "AES128_CTS_HMAC_SHA1_96",
2004 security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96 : "AES256_CTS_HMAC_SHA1_96",
2005 security.KERB_ENCTYPE_FAST_SUPPORTED : "FAST_SUPPORTED",
2006 security.KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED : "COMPOUND_IDENTITY_SUPPORTED",
2007 security.KERB_ENCTYPE_CLAIMS_SUPPORTED : "CLAIMS_SUPPORTED",
2008 security.KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED : "RESOURCE_SID_COMPRESSION_DISABLED",
2010 return self.generic_bitmap_to_string(enctypes, v)
2012 def entry_tln_status(self, e_flags, ):
2014 return "Status[Enabled]"
2017 lsa.LSA_TLN_DISABLED_NEW : "Disabled-New",
2018 lsa.LSA_TLN_DISABLED_ADMIN : "Disabled",
2019 lsa.LSA_TLN_DISABLED_CONFLICT : "Disabled-Conflicting",
2021 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
2023 def entry_dom_status(self, e_flags):
2025 return "Status[Enabled]"
2028 lsa.LSA_SID_DISABLED_ADMIN : "Disabled-SID",
2029 lsa.LSA_SID_DISABLED_CONFLICT : "Disabled-SID-Conflicting",
2030 lsa.LSA_NB_DISABLED_ADMIN : "Disabled-NB",
2031 lsa.LSA_NB_DISABLED_CONFLICT : "Disabled-NB-Conflicting",
2033 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
2035 def write_forest_trust_info(self, fti, tln=None, collisions=None):
2037 tln_string = " TDO[%s]" % tln
2041 self.outf.write("Namespaces[%d]%s:\n" % (
2042 len(fti.entries), tln_string))
2044 for i in xrange(0, len(fti.entries)):
2048 collision_string = ""
2050 if collisions is not None:
2051 for c in collisions.entries:
2055 collision_string = " Collision[%s]" % (c.name.string)
2057 d = e.forest_trust_data
2058 if e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
2059 self.outf.write("TLN: %-32s DNS[*.%s]%s\n" % (
2060 self.entry_tln_status(flags),
2061 d.string, collision_string))
2062 elif e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
2063 self.outf.write("TLN_EX: %-29s DNS[*.%s]\n" % (
2065 elif e.type == lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
2066 self.outf.write("DOM: %-32s DNS[%s] Netbios[%s] SID[%s]%s\n" % (
2067 self.entry_dom_status(flags),
2068 d.dns_domain_name.string,
2069 d.netbios_domain_name.string,
2070 d.domain_sid, collision_string))
2073 class cmd_domain_trust_list(DomainTrustCommand):
2074 """List domain trusts."""
2076 synopsis = "%prog [options]"
2078 takes_optiongroups = {
2079 "sambaopts": options.SambaOptions,
2080 "versionopts": options.VersionOptions,
2081 "localdcopts": LocalDCCredentialsOptions,
2087 def run(self, sambaopts=None, versionopts=None, localdcopts=None):
2089 local_server = self.setup_local_server(sambaopts, localdcopts)
2091 local_netlogon = self.new_local_netlogon_connection()
2092 except RuntimeError as error:
2093 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2096 local_netlogon_trusts = local_netlogon.netr_DsrEnumerateDomainTrusts(local_server,
2097 netlogon.NETR_TRUST_FLAG_IN_FOREST |
2098 netlogon.NETR_TRUST_FLAG_OUTBOUND |
2099 netlogon.NETR_TRUST_FLAG_INBOUND)
2100 except RuntimeError as error:
2101 if self.check_runtime_error(error, werror.WERR_RPC_S_PROCNUM_OUT_OF_RANGE):
2102 # TODO: we could implement a fallback to lsa.EnumTrustDom()
2103 raise CommandError("LOCAL_DC[%s]: netr_DsrEnumerateDomainTrusts not supported." % (
2105 raise self.LocalRuntimeError(self, error, "netr_DsrEnumerateDomainTrusts failed")
2107 a = local_netlogon_trusts.array
2109 if t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
2111 self.outf.write("%-14s %-15s %-19s %s\n" % (
2112 "Type[%s]" % self.netr_DomainTrust_to_type(a, t),
2113 "Transitive[%s]" % self.netr_DomainTrust_to_transitive(t),
2114 "Direction[%s]" % self.netr_DomainTrust_to_direction(t),
2115 "Name[%s]" % self.netr_DomainTrust_to_name(t)))
2118 class cmd_domain_trust_show(DomainTrustCommand):
2119 """Show trusted domain details."""
2121 synopsis = "%prog NAME [options]"
2123 takes_optiongroups = {
2124 "sambaopts": options.SambaOptions,
2125 "versionopts": options.VersionOptions,
2126 "localdcopts": LocalDCCredentialsOptions,
2132 takes_args = ["domain"]
2134 def run(self, domain, sambaopts=None, versionopts=None, localdcopts=None):
2136 local_server = self.setup_local_server(sambaopts, localdcopts)
2138 local_lsa = self.new_local_lsa_connection()
2139 except RuntimeError as error:
2140 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2143 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2144 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2145 except RuntimeError as error:
2146 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2148 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2149 local_lsa_info.name.string,
2150 local_lsa_info.dns_domain.string,
2151 local_lsa_info.sid))
2153 lsaString = lsa.String()
2154 lsaString.string = domain
2156 local_tdo_full = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2157 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2158 local_tdo_info = local_tdo_full.info_ex
2159 local_tdo_posix = local_tdo_full.posix_offset
2160 except NTSTATUSError as error:
2161 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2162 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2164 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(FULL_INFO) failed")
2167 local_tdo_enctypes = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2168 lsaString, lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES)
2169 except NTSTATUSError as error:
2170 if self.check_runtime_error(error, ntstatus.NT_STATUS_INVALID_PARAMETER):
2172 if self.check_runtime_error(error, ntstatus.NT_STATUS_INVALID_INFO_CLASS):
2175 if error is not None:
2176 raise self.LocalRuntimeError(self, error,
2177 "QueryTrustedDomainInfoByName(SUPPORTED_ENCRYPTION_TYPES) failed")
2179 local_tdo_enctypes = lsa.TrustDomainInfoSupportedEncTypes()
2180 local_tdo_enctypes.enc_types = 0
2183 local_tdo_forest = None
2184 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2185 local_tdo_forest = local_lsa.lsaRQueryForestTrustInformation(local_policy,
2186 lsaString, lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
2187 except RuntimeError as error:
2188 if self.check_runtime_error(error, ntstatus.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
2190 if self.check_runtime_error(error, ntstatus.NT_STATUS_NOT_FOUND):
2192 if error is not None:
2193 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation failed")
2195 local_tdo_forest = lsa.ForestTrustInformation()
2196 local_tdo_forest.count = 0
2197 local_tdo_forest.entries = []
2199 self.outf.write("TrustedDomain:\n\n");
2200 self.outf.write("NetbiosName: %s\n" % local_tdo_info.netbios_name.string)
2201 if local_tdo_info.netbios_name.string != local_tdo_info.domain_name.string:
2202 self.outf.write("DnsName: %s\n" % local_tdo_info.domain_name.string)
2203 self.outf.write("SID: %s\n" % local_tdo_info.sid)
2204 self.outf.write("Type: %s\n" % self.trustType_string(local_tdo_info.trust_type))
2205 self.outf.write("Direction: %s\n" % self.trustDirection_string(local_tdo_info.trust_direction))
2206 self.outf.write("Attributes: %s\n" % self.trustAttributes_string(local_tdo_info.trust_attributes))
2207 posix_offset_u32 = ctypes.c_uint32(local_tdo_posix.posix_offset).value
2208 posix_offset_i32 = ctypes.c_int32(local_tdo_posix.posix_offset).value
2209 self.outf.write("PosixOffset: 0x%08X (%d)\n" % (posix_offset_u32, posix_offset_i32))
2210 self.outf.write("kerb_EncTypes: %s\n" % self.kerb_EncTypes_string(local_tdo_enctypes.enc_types))
2212 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2213 self.write_forest_trust_info(local_tdo_forest,
2214 tln=local_tdo_info.domain_name.string)
2218 class cmd_domain_trust_create(DomainTrustCommand):
2219 """Create a domain or forest trust."""
2221 synopsis = "%prog DOMAIN [options]"
2223 takes_optiongroups = {
2224 "sambaopts": options.SambaOptions,
2225 "versionopts": options.VersionOptions,
2226 "credopts": options.CredentialsOptions,
2227 "localdcopts": LocalDCCredentialsOptions,
2231 Option("--type", type="choice", metavar="TYPE",
2232 choices=["external", "forest"],
2233 help="The type of the trust: 'external' or 'forest'.",
2235 default="external"),
2236 Option("--direction", type="choice", metavar="DIRECTION",
2237 choices=["incoming", "outgoing", "both"],
2238 help="The trust direction: 'incoming', 'outgoing' or 'both'.",
2239 dest='trust_direction',
2241 Option("--create-location", type="choice", metavar="LOCATION",
2242 choices=["local", "both"],
2243 help="Where to create the trusted domain object: 'local' or 'both'.",
2244 dest='create_location',
2246 Option("--cross-organisation", action="store_true",
2247 help="The related domains does not belong to the same organisation.",
2248 dest='cross_organisation',
2250 Option("--quarantined", type="choice", metavar="yes|no",
2251 choices=["yes", "no", None],
2252 help="Special SID filtering rules are applied to the trust. "
2253 "With --type=external the default is yes. "
2254 "With --type=forest the default is no.",
2255 dest='quarantined_arg',
2257 Option("--not-transitive", action="store_true",
2258 help="The forest trust is not transitive.",
2259 dest='not_transitive',
2261 Option("--treat-as-external", action="store_true",
2262 help="The treat the forest trust as external.",
2263 dest='treat_as_external',
2265 Option("--no-aes-keys", action="store_false",
2266 help="The trust uses aes kerberos keys.",
2267 dest='use_aes_keys',
2269 Option("--skip-validation", action="store_false",
2270 help="Skip validation of the trust.",
2275 takes_args = ["domain"]
2277 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2278 trust_type=None, trust_direction=None, create_location=None,
2279 cross_organisation=False, quarantined_arg=None,
2280 not_transitive=False, treat_as_external=False,
2281 use_aes_keys=False, validate=True):
2283 lsaString = lsa.String()
2286 if quarantined_arg is None:
2287 if trust_type == 'external':
2289 elif quarantined_arg == 'yes':
2292 if trust_type != 'forest':
2294 raise CommandError("--not-transitive requires --type=forest")
2295 if treat_as_external:
2296 raise CommandError("--treat-as-external requires --type=forest")
2300 enc_types = lsa.TrustDomainInfoSupportedEncTypes()
2301 enc_types.enc_types = security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96
2302 enc_types.enc_types |= security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96
2304 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2305 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2306 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2308 local_trust_info = lsa.TrustDomainInfoInfoEx()
2309 local_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2310 local_trust_info.trust_direction = 0
2311 if trust_direction == "both":
2312 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2313 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2314 elif trust_direction == "incoming":
2315 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2316 elif trust_direction == "outgoing":
2317 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2318 local_trust_info.trust_attributes = 0
2319 if cross_organisation:
2320 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2322 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2323 if trust_type == "forest":
2324 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2326 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2327 if treat_as_external:
2328 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2330 def get_password(name):
2333 if password is not None and password is not '':
2335 password = getpass("New %s Password: " % name)
2336 passwordverify = getpass("Retype %s Password: " % name)
2337 if not password == passwordverify:
2339 self.outf.write("Sorry, passwords do not match.\n")
2341 incoming_secret = None
2342 outgoing_secret = None
2343 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2344 if create_location == "local":
2345 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2346 incoming_password = get_password("Incoming Trust")
2347 incoming_secret = string_to_byte_array(incoming_password.encode('utf-16-le'))
2348 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2349 outgoing_password = get_password("Outgoing Trust")
2350 outgoing_secret = string_to_byte_array(outgoing_password.encode('utf-16-le'))
2352 remote_trust_info = None
2354 # We use 240 random bytes.
2355 # Windows uses 28 or 240 random bytes. I guess it's
2356 # based on the trust type external vs. forest.
2358 # The initial trust password can be up to 512 bytes
2359 # while the versioned passwords used for periodic updates
2360 # can only be up to 498 bytes, as netr_ServerPasswordSet2()
2361 # needs to pass the NL_PASSWORD_VERSION structure within the
2362 # 512 bytes and a 2 bytes confounder is required.
2364 def random_trust_secret(length):
2365 pw = samba.generate_random_machine_password(length//2, length//2)
2366 return string_to_byte_array(pw.encode('utf-16-le'))
2368 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2369 incoming_secret = random_trust_secret(240)
2370 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2371 outgoing_secret = random_trust_secret(240)
2373 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2374 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2376 remote_trust_info = lsa.TrustDomainInfoInfoEx()
2377 remote_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2378 remote_trust_info.trust_direction = 0
2379 if trust_direction == "both":
2380 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2381 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2382 elif trust_direction == "incoming":
2383 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2384 elif trust_direction == "outgoing":
2385 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2386 remote_trust_info.trust_attributes = 0
2387 if cross_organisation:
2388 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2390 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2391 if trust_type == "forest":
2392 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2394 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2395 if treat_as_external:
2396 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2398 local_server = self.setup_local_server(sambaopts, localdcopts)
2400 local_lsa = self.new_local_lsa_connection()
2401 except RuntimeError as error:
2402 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2405 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2406 except RuntimeError as error:
2407 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2409 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2410 local_lsa_info.name.string,
2411 local_lsa_info.dns_domain.string,
2412 local_lsa_info.sid))
2415 remote_server = self.setup_remote_server(credopts, domain)
2416 except RuntimeError as error:
2417 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2420 remote_lsa = self.new_remote_lsa_connection()
2421 except RuntimeError as error:
2422 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2425 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2426 except RuntimeError as error:
2427 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2429 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2430 remote_lsa_info.name.string,
2431 remote_lsa_info.dns_domain.string,
2432 remote_lsa_info.sid))
2434 local_trust_info.domain_name.string = remote_lsa_info.dns_domain.string
2435 local_trust_info.netbios_name.string = remote_lsa_info.name.string
2436 local_trust_info.sid = remote_lsa_info.sid
2438 if remote_trust_info:
2439 remote_trust_info.domain_name.string = local_lsa_info.dns_domain.string
2440 remote_trust_info.netbios_name.string = local_lsa_info.name.string
2441 remote_trust_info.sid = local_lsa_info.sid
2444 lsaString.string = local_trust_info.domain_name.string
2445 local_old_netbios = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2446 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2447 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2448 except NTSTATUSError as error:
2449 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2450 raise self.LocalRuntimeError(self, error,
2451 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2455 lsaString.string = local_trust_info.netbios_name.string
2456 local_old_dns = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2457 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2458 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2459 except NTSTATUSError as error:
2460 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2461 raise self.LocalRuntimeError(self, error,
2462 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2465 if remote_trust_info:
2467 lsaString.string = remote_trust_info.domain_name.string
2468 remote_old_netbios = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2469 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2470 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2471 except NTSTATUSError as error:
2472 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2473 raise self.RemoteRuntimeError(self, error,
2474 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2478 lsaString.string = remote_trust_info.netbios_name.string
2479 remote_old_dns = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2480 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2481 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2482 except NTSTATUSError as error:
2483 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2484 raise self.RemoteRuntimeError(self, error,
2485 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2489 local_netlogon = self.new_local_netlogon_connection()
2490 except RuntimeError as error:
2491 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2494 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
2495 except RuntimeError as error:
2496 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
2498 if remote_trust_info:
2500 remote_netlogon = self.new_remote_netlogon_connection()
2501 except RuntimeError as error:
2502 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2505 remote_netlogon_info = self.get_netlogon_dc_info(remote_netlogon, remote_server)
2506 except RuntimeError as error:
2507 raise self.RemoteRuntimeError(self, error, "failed to get netlogon dc info")
2509 def generate_AuthInOutBlob(secret, update_time):
2511 blob = drsblobs.trustAuthInOutBlob()
2516 clear = drsblobs.AuthInfoClear()
2517 clear.size = len(secret)
2518 clear.password = secret
2520 info = drsblobs.AuthenticationInformation()
2521 info.LastUpdateTime = samba.unix2nttime(update_time)
2522 info.AuthType = lsa.TRUST_AUTH_TYPE_CLEAR
2523 info.AuthInfo = clear
2525 array = drsblobs.AuthenticationInformationArray()
2527 array.array = [info]
2529 blob = drsblobs.trustAuthInOutBlob()
2531 blob.current = array
2535 def generate_AuthInfoInternal(session_key, incoming=None, outgoing=None):
2536 confounder = [0] * 512
2537 for i in range(len(confounder)):
2538 confounder[i] = random.randint(0, 255)
2540 trustpass = drsblobs.trustDomainPasswords()
2542 trustpass.confounder = confounder
2543 trustpass.outgoing = outgoing
2544 trustpass.incoming = incoming
2546 trustpass_blob = ndr_pack(trustpass)
2548 encrypted_trustpass = arcfour_encrypt(session_key, trustpass_blob)
2550 auth_blob = lsa.DATA_BUF2()
2551 auth_blob.size = len(encrypted_trustpass)
2552 auth_blob.data = string_to_byte_array(encrypted_trustpass)
2554 auth_info = lsa.TrustDomainInfoAuthInfoInternal()
2555 auth_info.auth_blob = auth_blob
2559 update_time = samba.current_unix_time()
2560 incoming_blob = generate_AuthInOutBlob(incoming_secret, update_time)
2561 outgoing_blob = generate_AuthInOutBlob(outgoing_secret, update_time)
2563 local_tdo_handle = None
2564 remote_tdo_handle = None
2566 local_auth_info = generate_AuthInfoInternal(local_lsa.session_key,
2567 incoming=incoming_blob,
2568 outgoing=outgoing_blob)
2569 if remote_trust_info:
2570 remote_auth_info = generate_AuthInfoInternal(remote_lsa.session_key,
2571 incoming=outgoing_blob,
2572 outgoing=incoming_blob)
2575 if remote_trust_info:
2576 self.outf.write("Creating remote TDO.\n")
2577 current_request = { "location": "remote", "name": "CreateTrustedDomainEx2"}
2578 remote_tdo_handle = remote_lsa.CreateTrustedDomainEx2(remote_policy,
2581 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2582 self.outf.write("Remote TDO created.\n")
2584 self.outf.write("Setting supported encryption types on remote TDO.\n")
2585 current_request = { "location": "remote", "name": "SetInformationTrustedDomain"}
2586 remote_lsa.SetInformationTrustedDomain(remote_tdo_handle,
2587 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2590 self.outf.write("Creating local TDO.\n")
2591 current_request = { "location": "local", "name": "CreateTrustedDomainEx2"}
2592 local_tdo_handle = local_lsa.CreateTrustedDomainEx2(local_policy,
2595 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2596 self.outf.write("Local TDO created\n")
2598 self.outf.write("Setting supported encryption types on local TDO.\n")
2599 current_request = { "location": "local", "name": "SetInformationTrustedDomain"}
2600 local_lsa.SetInformationTrustedDomain(local_tdo_handle,
2601 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2603 except RuntimeError as error:
2604 self.outf.write("Error: %s failed %sly - cleaning up\n" % (
2605 current_request['name'], current_request['location']))
2606 if remote_tdo_handle:
2607 self.outf.write("Deleting remote TDO.\n")
2608 remote_lsa.DeleteObject(remote_tdo_handle)
2609 remote_tdo_handle = None
2610 if local_tdo_handle:
2611 self.outf.write("Deleting local TDO.\n")
2612 local_lsa.DeleteObject(local_tdo_handle)
2613 local_tdo_handle = None
2614 if current_request['location'] is "remote":
2615 raise self.RemoteRuntimeError(self, error, "%s" % (
2616 current_request['name']))
2617 raise self.LocalRuntimeError(self, error, "%s" % (
2618 current_request['name']))
2621 if local_trust_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2622 self.outf.write("Setup local forest trust information...\n")
2624 # get all information about the remote trust
2625 # this triggers netr_GetForestTrustInformation to the remote domain
2626 # and lsaRSetForestTrustInformation() locally, but new top level
2627 # names are disabled by default.
2628 local_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
2629 remote_lsa_info.dns_domain.string,
2630 netlogon.DS_GFTI_UPDATE_TDO)
2631 except RuntimeError as error:
2632 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2635 # here we try to enable all top level names
2636 local_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
2637 remote_lsa_info.dns_domain,
2638 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2641 except RuntimeError as error:
2642 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2644 self.write_forest_trust_info(local_forest_info,
2645 tln=remote_lsa_info.dns_domain.string,
2646 collisions=local_forest_collision)
2648 if remote_trust_info:
2649 self.outf.write("Setup remote forest trust information...\n")
2651 # get all information about the local trust (from the perspective of the remote domain)
2652 # this triggers netr_GetForestTrustInformation to our domain.
2653 # and lsaRSetForestTrustInformation() remotely, but new top level
2654 # names are disabled by default.
2655 remote_forest_info = remote_netlogon.netr_DsRGetForestTrustInformation(remote_netlogon_info.dc_unc,
2656 local_lsa_info.dns_domain.string,
2657 netlogon.DS_GFTI_UPDATE_TDO)
2658 except RuntimeError as error:
2659 raise self.RemoteRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2662 # here we try to enable all top level names
2663 remote_forest_collision = remote_lsa.lsaRSetForestTrustInformation(remote_policy,
2664 local_lsa_info.dns_domain,
2665 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2668 except RuntimeError as error:
2669 raise self.RemoteRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2671 self.write_forest_trust_info(remote_forest_info,
2672 tln=local_lsa_info.dns_domain.string,
2673 collisions=remote_forest_collision)
2675 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2676 self.outf.write("Validating outgoing trust...\n")
2678 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_netlogon_info.dc_unc,
2679 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2681 remote_lsa_info.dns_domain.string)
2682 except RuntimeError as error:
2683 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2685 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2686 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2688 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2689 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2690 local_trust_verify.trusted_dc_name,
2691 local_trust_verify.tc_connection_status[1],
2692 local_trust_verify.pdc_connection_status[1])
2694 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2695 local_trust_verify.trusted_dc_name,
2696 local_trust_verify.tc_connection_status[1],
2697 local_trust_verify.pdc_connection_status[1])
2699 if local_trust_status != werror.WERR_SUCCESS or local_conn_status != werror.WERR_SUCCESS:
2700 raise CommandError(local_validation)
2702 self.outf.write("OK: %s\n" % local_validation)
2704 if remote_trust_info:
2705 if remote_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2706 self.outf.write("Validating incoming trust...\n")
2708 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_netlogon_info.dc_unc,
2709 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2711 local_lsa_info.dns_domain.string)
2712 except RuntimeError as error:
2713 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2715 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2716 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2718 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2719 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2720 remote_trust_verify.trusted_dc_name,
2721 remote_trust_verify.tc_connection_status[1],
2722 remote_trust_verify.pdc_connection_status[1])
2724 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2725 remote_trust_verify.trusted_dc_name,
2726 remote_trust_verify.tc_connection_status[1],
2727 remote_trust_verify.pdc_connection_status[1])
2729 if remote_trust_status != werror.WERR_SUCCESS or remote_conn_status != werror.WERR_SUCCESS:
2730 raise CommandError(remote_validation)
2732 self.outf.write("OK: %s\n" % remote_validation)
2734 if remote_tdo_handle is not None:
2736 remote_lsa.Close(remote_tdo_handle)
2737 except RuntimeError as error:
2739 remote_tdo_handle = None
2740 if local_tdo_handle is not None:
2742 local_lsa.Close(local_tdo_handle)
2743 except RuntimeError as error:
2745 local_tdo_handle = None
2747 self.outf.write("Success.\n")
2750 class cmd_domain_trust_delete(DomainTrustCommand):
2751 """Delete a domain trust."""
2753 synopsis = "%prog DOMAIN [options]"
2755 takes_optiongroups = {
2756 "sambaopts": options.SambaOptions,
2757 "versionopts": options.VersionOptions,
2758 "credopts": options.CredentialsOptions,
2759 "localdcopts": LocalDCCredentialsOptions,
2763 Option("--delete-location", type="choice", metavar="LOCATION",
2764 choices=["local", "both"],
2765 help="Where to delete the trusted domain object: 'local' or 'both'.",
2766 dest='delete_location',
2770 takes_args = ["domain"]
2772 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2773 delete_location=None):
2775 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2776 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2777 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2779 if delete_location == "local":
2780 remote_policy_access = None
2782 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2783 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2784 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2786 local_server = self.setup_local_server(sambaopts, localdcopts)
2788 local_lsa = self.new_local_lsa_connection()
2789 except RuntimeError as error:
2790 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2793 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2794 except RuntimeError as error:
2795 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2797 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2798 local_lsa_info.name.string,
2799 local_lsa_info.dns_domain.string,
2800 local_lsa_info.sid))
2802 local_tdo_info = None
2803 local_tdo_handle = None
2804 remote_tdo_info = None
2805 remote_tdo_handle = None
2807 lsaString = lsa.String()
2809 lsaString.string = domain
2810 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2811 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2812 except NTSTATUSError as error:
2813 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2814 raise CommandError("Failed to find trust for domain '%s'" % domain)
2815 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2818 if remote_policy_access is not None:
2820 remote_server = self.setup_remote_server(credopts, domain)
2821 except RuntimeError as error:
2822 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2825 remote_lsa = self.new_remote_lsa_connection()
2826 except RuntimeError as error:
2827 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2830 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2831 except RuntimeError as error:
2832 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2834 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2835 remote_lsa_info.name.string,
2836 remote_lsa_info.dns_domain.string,
2837 remote_lsa_info.sid))
2839 if remote_lsa_info.sid != local_tdo_info.sid or \
2840 remote_lsa_info.name.string != local_tdo_info.netbios_name.string or \
2841 remote_lsa_info.dns_domain.string != local_tdo_info.domain_name.string:
2842 raise CommandError("LocalTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2843 local_tdo_info.netbios_name.string,
2844 local_tdo_info.domain_name.string,
2845 local_tdo_info.sid))
2848 lsaString.string = local_lsa_info.dns_domain.string
2849 remote_tdo_info = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2850 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2851 except NTSTATUSError as error:
2852 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2853 raise self.RemoteRuntimeError(self, error, "QueryTrustedDomainInfoByName(%s)" % (
2857 if remote_tdo_info is not None:
2858 if local_lsa_info.sid != remote_tdo_info.sid or \
2859 local_lsa_info.name.string != remote_tdo_info.netbios_name.string or \
2860 local_lsa_info.dns_domain.string != remote_tdo_info.domain_name.string:
2861 raise CommandError("RemoteTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2862 remote_tdo_info.netbios_name.string,
2863 remote_tdo_info.domain_name.string,
2864 remote_tdo_info.sid))
2866 if local_tdo_info is not None:
2868 lsaString.string = local_tdo_info.domain_name.string
2869 local_tdo_handle = local_lsa.OpenTrustedDomainByName(local_policy,
2871 security.SEC_STD_DELETE)
2872 except RuntimeError as error:
2873 raise self.LocalRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2876 local_lsa.DeleteObject(local_tdo_handle)
2877 local_tdo_handle = None
2879 if remote_tdo_info is not None:
2881 lsaString.string = remote_tdo_info.domain_name.string
2882 remote_tdo_handle = remote_lsa.OpenTrustedDomainByName(remote_policy,
2884 security.SEC_STD_DELETE)
2885 except RuntimeError as error:
2886 raise self.RemoteRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2889 if remote_tdo_handle is not None:
2891 remote_lsa.DeleteObject(remote_tdo_handle)
2892 remote_tdo_handle = None
2893 self.outf.write("RemoteTDO deleted.\n")
2894 except RuntimeError as error:
2895 self.outf.write("%s\n" % self.RemoteRuntimeError(self, error, "DeleteObject() failed"))
2897 if local_tdo_handle is not None:
2899 local_lsa.DeleteObject(local_tdo_handle)
2900 local_tdo_handle = None
2901 self.outf.write("LocalTDO deleted.\n")
2902 except RuntimeError as error:
2903 self.outf.write("%s\n" % self.LocalRuntimeError(self, error, "DeleteObject() failed"))
2907 class cmd_domain_trust_validate(DomainTrustCommand):
2908 """Validate a domain trust."""
2910 synopsis = "%prog DOMAIN [options]"
2912 takes_optiongroups = {
2913 "sambaopts": options.SambaOptions,
2914 "versionopts": options.VersionOptions,
2915 "credopts": options.CredentialsOptions,
2916 "localdcopts": LocalDCCredentialsOptions,
2920 Option("--validate-location", type="choice", metavar="LOCATION",
2921 choices=["local", "both"],
2922 help="Where to validate the trusted domain object: 'local' or 'both'.",
2923 dest='validate_location',
2927 takes_args = ["domain"]
2929 def run(self, domain, sambaopts=None, versionopts=None, credopts=None, localdcopts=None,
2930 validate_location=None):
2932 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2934 local_server = self.setup_local_server(sambaopts, localdcopts)
2936 local_lsa = self.new_local_lsa_connection()
2937 except RuntimeError as error:
2938 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2941 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2942 except RuntimeError as error:
2943 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2945 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2946 local_lsa_info.name.string,
2947 local_lsa_info.dns_domain.string,
2948 local_lsa_info.sid))
2951 lsaString = lsa.String()
2952 lsaString.string = domain
2953 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2954 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2955 except NTSTATUSError as error:
2956 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2957 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2959 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
2961 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
2962 local_tdo_info.netbios_name.string,
2963 local_tdo_info.domain_name.string,
2964 local_tdo_info.sid))
2967 local_netlogon = self.new_local_netlogon_connection()
2968 except RuntimeError as error:
2969 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2972 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_server,
2973 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2975 local_tdo_info.domain_name.string)
2976 except RuntimeError as error:
2977 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2979 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2980 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2982 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2983 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2984 local_trust_verify.trusted_dc_name,
2985 local_trust_verify.tc_connection_status[1],
2986 local_trust_verify.pdc_connection_status[1])
2988 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2989 local_trust_verify.trusted_dc_name,
2990 local_trust_verify.tc_connection_status[1],
2991 local_trust_verify.pdc_connection_status[1])
2993 if local_trust_status != werror.WERR_SUCCESS or local_conn_status != werror.WERR_SUCCESS:
2994 raise CommandError(local_validation)
2996 self.outf.write("OK: %s\n" % local_validation)
2999 server = local_trust_verify.trusted_dc_name.replace('\\', '')
3000 domain_and_server = "%s\\%s" % (local_tdo_info.domain_name.string, server)
3001 local_trust_rediscover = local_netlogon.netr_LogonControl2Ex(local_server,
3002 netlogon.NETLOGON_CONTROL_REDISCOVER,
3005 except RuntimeError as error:
3006 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
3008 local_conn_status = self._uint32(local_trust_rediscover.tc_connection_status[0])
3009 local_rediscover = "LocalRediscover: DC[%s] CONNECTION[%s]" % (
3010 local_trust_rediscover.trusted_dc_name,
3011 local_trust_rediscover.tc_connection_status[1])
3013 if local_conn_status != werror.WERR_SUCCESS:
3014 raise CommandError(local_rediscover)
3016 self.outf.write("OK: %s\n" % local_rediscover)
3018 if validate_location != "local":
3020 remote_server = self.setup_remote_server(credopts, domain, require_pdc=False)
3021 except RuntimeError as error:
3022 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
3025 remote_netlogon = self.new_remote_netlogon_connection()
3026 except RuntimeError as error:
3027 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
3030 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_server,
3031 netlogon.NETLOGON_CONTROL_TC_VERIFY,
3033 local_lsa_info.dns_domain.string)
3034 except RuntimeError as error:
3035 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
3037 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
3038 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
3040 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
3041 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
3042 remote_trust_verify.trusted_dc_name,
3043 remote_trust_verify.tc_connection_status[1],
3044 remote_trust_verify.pdc_connection_status[1])
3046 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
3047 remote_trust_verify.trusted_dc_name,
3048 remote_trust_verify.tc_connection_status[1],
3049 remote_trust_verify.pdc_connection_status[1])
3051 if remote_trust_status != werror.WERR_SUCCESS or remote_conn_status != werror.WERR_SUCCESS:
3052 raise CommandError(remote_validation)
3054 self.outf.write("OK: %s\n" % remote_validation)
3057 server = remote_trust_verify.trusted_dc_name.replace('\\', '')
3058 domain_and_server = "%s\\%s" % (local_lsa_info.dns_domain.string, server)
3059 remote_trust_rediscover = remote_netlogon.netr_LogonControl2Ex(remote_server,
3060 netlogon.NETLOGON_CONTROL_REDISCOVER,
3063 except RuntimeError as error:
3064 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
3066 remote_conn_status = self._uint32(remote_trust_rediscover.tc_connection_status[0])
3068 remote_rediscover = "RemoteRediscover: DC[%s] CONNECTION[%s]" % (
3069 remote_trust_rediscover.trusted_dc_name,
3070 remote_trust_rediscover.tc_connection_status[1])
3072 if remote_conn_status != werror.WERR_SUCCESS:
3073 raise CommandError(remote_rediscover)
3075 self.outf.write("OK: %s\n" % remote_rediscover)
3079 class cmd_domain_trust_namespaces(DomainTrustCommand):
3080 """Manage forest trust namespaces."""
3082 synopsis = "%prog [DOMAIN] [options]"
3084 takes_optiongroups = {
3085 "sambaopts": options.SambaOptions,
3086 "versionopts": options.VersionOptions,
3087 "localdcopts": LocalDCCredentialsOptions,
3091 Option("--refresh", type="choice", metavar="check|store",
3092 choices=["check", "store", None],
3093 help="List and maybe store refreshed forest trust information: 'check' or 'store'.",
3096 Option("--enable-all", action="store_true",
3097 help="Try to update disabled entries, not allowed with --refresh=check.",
3100 Option("--enable-tln", action="append", metavar='DNSDOMAIN',
3101 help="Enable a top level name entry. Can be specified multiple times.",
3104 Option("--disable-tln", action="append", metavar='DNSDOMAIN',
3105 help="Disable a top level name entry. Can be specified multiple times.",
3108 Option("--add-tln-ex", action="append", metavar='DNSDOMAIN',
3109 help="Add a top level exclusion entry. Can be specified multiple times.",
3112 Option("--delete-tln-ex", action="append", metavar='DNSDOMAIN',
3113 help="Delete a top level exclusion entry. Can be specified multiple times.",
3114 dest='delete_tln_ex',
3116 Option("--enable-nb", action="append", metavar='NETBIOSDOMAIN',
3117 help="Enable a netbios name in a domain entry. Can be specified multiple times.",
3120 Option("--disable-nb", action="append", metavar='NETBIOSDOMAIN',
3121 help="Disable a netbios name in a domain entry. Can be specified multiple times.",
3124 Option("--enable-sid", action="append", metavar='DOMAINSID',
3125 help="Enable a SID in a domain entry. Can be specified multiple times.",
3126 dest='enable_sid_str',
3128 Option("--disable-sid", action="append", metavar='DOMAINSID',
3129 help="Disable a SID in a domain entry. Can be specified multiple times.",
3130 dest='disable_sid_str',
3132 Option("--add-upn-suffix", action="append", metavar='DNSDOMAIN',
3133 help="Add a new uPNSuffixes attribute for the local forest. Can be specified multiple times.",
3136 Option("--delete-upn-suffix", action="append", metavar='DNSDOMAIN',
3137 help="Delete an existing uPNSuffixes attribute of the local forest. Can be specified multiple times.",
3140 Option("--add-spn-suffix", action="append", metavar='DNSDOMAIN',
3141 help="Add a new msDS-SPNSuffixes attribute for the local forest. Can be specified multiple times.",
3144 Option("--delete-spn-suffix", action="append", metavar='DNSDOMAIN',
3145 help="Delete an existing msDS-SPNSuffixes attribute of the local forest. Can be specified multiple times.",
3150 takes_args = ["domain?"]
3152 def run(self, domain=None, sambaopts=None, localdcopts=None, versionopts=None,
3153 refresh=None, enable_all=False,
3154 enable_tln=[], disable_tln=[], add_tln_ex=[], delete_tln_ex=[],
3155 enable_sid_str=[], disable_sid_str=[], enable_nb=[], disable_nb=[],
3156 add_upn=[], delete_upn=[], add_spn=[], delete_spn=[]):
3158 require_update = False
3161 if refresh == "store":
3162 raise CommandError("--refresh=%s not allowed without DOMAIN" % refresh)
3165 raise CommandError("--enable-all not allowed without DOMAIN")
3167 if len(enable_tln) > 0:
3168 raise CommandError("--enable-tln not allowed without DOMAIN")
3169 if len(disable_tln) > 0:
3170 raise CommandError("--disable-tln not allowed without DOMAIN")
3172 if len(add_tln_ex) > 0:
3173 raise CommandError("--add-tln-ex not allowed without DOMAIN")
3174 if len(delete_tln_ex) > 0:
3175 raise CommandError("--delete-tln-ex not allowed without DOMAIN")
3177 if len(enable_nb) > 0:
3178 raise CommandError("--enable-nb not allowed without DOMAIN")
3179 if len(disable_nb) > 0:
3180 raise CommandError("--disable-nb not allowed without DOMAIN")
3182 if len(enable_sid_str) > 0:
3183 raise CommandError("--enable-sid not allowed without DOMAIN")
3184 if len(disable_sid_str) > 0:
3185 raise CommandError("--disable-sid not allowed without DOMAIN")
3187 if len(add_upn) > 0:
3189 if not n.startswith("*."):
3191 raise CommandError("value[%s] specified for --add-upn-suffix should not include with '*.'" % n)
3192 require_update = True
3193 if len(delete_upn) > 0:
3194 for n in delete_upn:
3195 if not n.startswith("*."):
3197 raise CommandError("value[%s] specified for --delete-upn-suffix should not include with '*.'" % n)
3198 require_update = True
3200 for d in delete_upn:
3201 if a.lower() != d.lower():
3203 raise CommandError("value[%s] specified for --add-upn-suffix and --delete-upn-suffix" % a)
3205 if len(add_spn) > 0:
3207 if not n.startswith("*."):
3209 raise CommandError("value[%s] specified for --add-spn-suffix should not include with '*.'" % n)
3210 require_update = True
3211 if len(delete_spn) > 0:
3212 for n in delete_spn:
3213 if not n.startswith("*."):
3215 raise CommandError("value[%s] specified for --delete-spn-suffix should not include with '*.'" % n)
3216 require_update = True
3218 for d in delete_spn:
3219 if a.lower() != d.lower():
3221 raise CommandError("value[%s] specified for --add-spn-suffix and --delete-spn-suffix" % a)
3223 if len(add_upn) > 0:
3224 raise CommandError("--add-upn-suffix not allowed together with DOMAIN")
3225 if len(delete_upn) > 0:
3226 raise CommandError("--delete-upn-suffix not allowed together with DOMAIN")
3227 if len(add_spn) > 0:
3228 raise CommandError("--add-spn-suffix not allowed together with DOMAIN")
3229 if len(delete_spn) > 0:
3230 raise CommandError("--delete-spn-suffix not allowed together with DOMAIN")
3232 if refresh is not None:
3233 if refresh == "store":
3234 require_update = True
3236 if enable_all and refresh != "store":
3237 raise CommandError("--enable-all not allowed together with --refresh=%s" % refresh)
3239 if len(enable_tln) > 0:
3240 raise CommandError("--enable-tln not allowed together with --refresh")
3241 if len(disable_tln) > 0:
3242 raise CommandError("--disable-tln not allowed together with --refresh")
3244 if len(add_tln_ex) > 0:
3245 raise CommandError("--add-tln-ex not allowed together with --refresh")
3246 if len(delete_tln_ex) > 0:
3247 raise CommandError("--delete-tln-ex not allowed together with --refresh")
3249 if len(enable_nb) > 0:
3250 raise CommandError("--enable-nb not allowed together with --refresh")
3251 if len(disable_nb) > 0:
3252 raise CommandError("--disable-nb not allowed together with --refresh")
3254 if len(enable_sid_str) > 0:
3255 raise CommandError("--enable-sid not allowed together with --refresh")
3256 if len(disable_sid_str) > 0:
3257 raise CommandError("--disable-sid not allowed together with --refresh")
3260 require_update = True
3262 if len(enable_tln) > 0:
3263 raise CommandError("--enable-tln not allowed together with --enable-all")
3265 if len(enable_nb) > 0:
3266 raise CommandError("--enable-nb not allowed together with --enable-all")
3268 if len(enable_sid_str) > 0:
3269 raise CommandError("--enable-sid not allowed together with --enable-all")
3271 if len(enable_tln) > 0:
3272 require_update = True
3273 if len(disable_tln) > 0:
3274 require_update = True
3275 for e in enable_tln:
3276 for d in disable_tln:
3277 if e.lower() != d.lower():
3279 raise CommandError("value[%s] specified for --enable-tln and --disable-tln" % e)
3281 if len(add_tln_ex) > 0:
3282 for n in add_tln_ex:
3283 if not n.startswith("*."):
3285 raise CommandError("value[%s] specified for --add-tln-ex should not include with '*.'" % n)
3286 require_update = True
3287 if len(delete_tln_ex) > 0:
3288 for n in delete_tln_ex:
3289 if not n.startswith("*."):
3291 raise CommandError("value[%s] specified for --delete-tln-ex should not include with '*.'" % n)
3292 require_update = True
3293 for a in add_tln_ex:
3294 for d in delete_tln_ex:
3295 if a.lower() != d.lower():
3297 raise CommandError("value[%s] specified for --add-tln-ex and --delete-tln-ex" % a)
3299 if len(enable_nb) > 0:
3300 require_update = True
3301 if len(disable_nb) > 0:
3302 require_update = True
3304 for d in disable_nb:
3305 if e.upper() != d.upper():
3307 raise CommandError("value[%s] specified for --enable-nb and --disable-nb" % e)
3310 for s in enable_sid_str:
3312 sid = security.dom_sid(s)
3313 except TypeError as error:
3314 raise CommandError("value[%s] specified for --enable-sid is not a valid SID" % s)
3315 enable_sid.append(sid)
3317 for s in disable_sid_str:
3319 sid = security.dom_sid(s)
3320 except TypeError as error:
3321 raise CommandError("value[%s] specified for --disable-sid is not a valid SID" % s)
3322 disable_sid.append(sid)
3323 if len(enable_sid) > 0:
3324 require_update = True
3325 if len(disable_sid) > 0:
3326 require_update = True
3327 for e in enable_sid:
3328 for d in disable_sid:
3331 raise CommandError("value[%s] specified for --enable-sid and --disable-sid" % e)
3333 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
3335 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
3337 local_server = self.setup_local_server(sambaopts, localdcopts)
3339 local_lsa = self.new_local_lsa_connection()
3340 except RuntimeError as error:
3341 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
3344 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
3345 except RuntimeError as error:
3346 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
3348 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
3349 local_lsa_info.name.string,
3350 local_lsa_info.dns_domain.string,
3351 local_lsa_info.sid))
3355 local_netlogon = self.new_local_netlogon_connection()
3356 except RuntimeError as error:
3357 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3360 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3361 except RuntimeError as error:
3362 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3364 if local_netlogon_info.domain_name != local_netlogon_info.forest_name:
3365 raise CommandError("The local domain [%s] is not the forest root [%s]" % (
3366 local_netlogon_info.domain_name,
3367 local_netlogon_info.forest_name))
3370 # get all information about our own forest
3371 own_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3373 except RuntimeError as error:
3374 if self.check_runtime_error(error, werror.WERR_RPC_S_PROCNUM_OUT_OF_RANGE):
3375 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3378 if self.check_runtime_error(error, werror.WERR_INVALID_FUNCTION):
3379 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3382 if self.check_runtime_error(error, werror.WERR_NERR_ACFNOTLOADED):
3383 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3386 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3388 self.outf.write("Own forest trust information...\n")
3389 self.write_forest_trust_info(own_forest_info,
3390 tln=local_lsa_info.dns_domain.string)
3393 local_samdb = self.new_local_ldap_connection()
3394 except RuntimeError as error:
3395 raise self.LocalRuntimeError(self, error, "failed to connect to SamDB")
3397 local_partitions_dn = "CN=Partitions,%s" % str(local_samdb.get_config_basedn())
3398 attrs = ['uPNSuffixes', 'msDS-SPNSuffixes']
3400 msgs = local_samdb.search(base=local_partitions_dn,
3401 scope=ldb.SCOPE_BASE,
3402 expression="(objectClass=crossRefContainer)",
3404 stored_msg = msgs[0]
3405 except ldb.LdbError as error:
3406 raise self.LocalLdbError(self, error, "failed to search partition dn")
3408 stored_upn_vals = []
3409 if 'uPNSuffixes' in stored_msg:
3410 stored_upn_vals.extend(stored_msg['uPNSuffixes'])
3412 stored_spn_vals = []
3413 if 'msDS-SPNSuffixes' in stored_msg:
3414 stored_spn_vals.extend(stored_msg['msDS-SPNSuffixes'])
3416 self.outf.write("Stored uPNSuffixes attributes[%d]:\n" % len(stored_upn_vals))
3417 for v in stored_upn_vals:
3418 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3419 self.outf.write("Stored msDS-SPNSuffixes attributes[%d]:\n" % len(stored_spn_vals))
3420 for v in stored_spn_vals:
3421 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3423 if not require_update:
3427 update_upn_vals = []
3428 update_upn_vals.extend(stored_upn_vals)
3431 update_spn_vals = []
3432 update_spn_vals.extend(stored_spn_vals)
3436 for i in xrange(0, len(update_upn_vals)):
3437 v = update_upn_vals[i]
3438 if v.lower() != upn.lower():
3443 raise CommandError("Entry already present for value[%s] specified for --add-upn-suffix" % upn)
3444 update_upn_vals.append(upn)
3447 for upn in delete_upn:
3449 for i in xrange(0, len(update_upn_vals)):
3450 v = update_upn_vals[i]
3451 if v.lower() != upn.lower():
3456 raise CommandError("Entry not found for value[%s] specified for --delete-upn-suffix" % upn)
3458 update_upn_vals.pop(idx)
3463 for i in xrange(0, len(update_spn_vals)):
3464 v = update_spn_vals[i]
3465 if v.lower() != spn.lower():
3470 raise CommandError("Entry already present for value[%s] specified for --add-spn-suffix" % spn)
3471 update_spn_vals.append(spn)
3474 for spn in delete_spn:
3476 for i in xrange(0, len(update_spn_vals)):
3477 v = update_spn_vals[i]
3478 if v.lower() != spn.lower():
3483 raise CommandError("Entry not found for value[%s] specified for --delete-spn-suffix" % spn)
3485 update_spn_vals.pop(idx)
3488 self.outf.write("Update uPNSuffixes attributes[%d]:\n" % len(update_upn_vals))
3489 for v in update_upn_vals:
3490 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3491 self.outf.write("Update msDS-SPNSuffixes attributes[%d]:\n" % len(update_spn_vals))
3492 for v in update_spn_vals:
3493 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3495 update_msg = ldb.Message()
3496 update_msg.dn = stored_msg.dn
3499 update_msg['uPNSuffixes'] = ldb.MessageElement(update_upn_vals,
3500 ldb.FLAG_MOD_REPLACE,
3503 update_msg['msDS-SPNSuffixes'] = ldb.MessageElement(update_spn_vals,
3504 ldb.FLAG_MOD_REPLACE,
3507 local_samdb.modify(update_msg)
3508 except ldb.LdbError as error:
3509 raise self.LocalLdbError(self, error, "failed to update partition dn")
3512 stored_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3514 except RuntimeError as error:
3515 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3517 self.outf.write("Stored forest trust information...\n")
3518 self.write_forest_trust_info(stored_forest_info,
3519 tln=local_lsa_info.dns_domain.string)
3523 lsaString = lsa.String()
3524 lsaString.string = domain
3525 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
3526 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
3527 except NTSTATUSError as error:
3528 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
3529 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
3531 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
3533 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
3534 local_tdo_info.netbios_name.string,
3535 local_tdo_info.domain_name.string,
3536 local_tdo_info.sid))
3538 if not local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
3539 raise CommandError("trusted domain object for domain [%s] is not marked as FOREST_TRANSITIVE." % domain)
3541 if refresh is not None:
3543 local_netlogon = self.new_local_netlogon_connection()
3544 except RuntimeError as error:
3545 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3548 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3549 except RuntimeError as error:
3550 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3552 lsa_update_check = 1
3553 if refresh == "store":
3554 netlogon_update_tdo = netlogon.DS_GFTI_UPDATE_TDO
3556 lsa_update_check = 0
3558 netlogon_update_tdo = 0
3561 # get all information about the remote trust
3562 # this triggers netr_GetForestTrustInformation to the remote domain
3563 # and lsaRSetForestTrustInformation() locally, but new top level
3564 # names are disabled by default.
3565 fresh_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3566 local_tdo_info.domain_name.string,
3567 netlogon_update_tdo)
3568 except RuntimeError as error:
3569 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3572 fresh_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3573 local_tdo_info.domain_name,
3574 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3577 except RuntimeError as error:
3578 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3580 self.outf.write("Fresh forest trust information...\n")
3581 self.write_forest_trust_info(fresh_forest_info,
3582 tln=local_tdo_info.domain_name.string,
3583 collisions=fresh_forest_collision)
3585 if refresh == "store":
3587 lsaString = lsa.String()
3588 lsaString.string = local_tdo_info.domain_name.string
3589 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3591 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3592 except RuntimeError as error:
3593 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3595 self.outf.write("Stored forest trust information...\n")
3596 self.write_forest_trust_info(stored_forest_info,
3597 tln=local_tdo_info.domain_name.string)
3602 # The none --refresh path
3606 lsaString = lsa.String()
3607 lsaString.string = local_tdo_info.domain_name.string
3608 local_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3610 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3611 except RuntimeError as error:
3612 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3614 self.outf.write("Local forest trust information...\n")
3615 self.write_forest_trust_info(local_forest_info,
3616 tln=local_tdo_info.domain_name.string)
3618 if not require_update:
3622 entries.extend(local_forest_info.entries)
3623 update_forest_info = lsa.ForestTrustInformation()
3624 update_forest_info.count = len(entries)
3625 update_forest_info.entries = entries
3628 for i in xrange(0, len(update_forest_info.entries)):
3629 r = update_forest_info.entries[i]
3630 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3632 if update_forest_info.entries[i].flags == 0:
3634 update_forest_info.entries[i].time = 0
3635 update_forest_info.entries[i].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3636 for i in xrange(0, len(update_forest_info.entries)):
3637 r = update_forest_info.entries[i]
3638 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3640 if update_forest_info.entries[i].flags == 0:
3642 update_forest_info.entries[i].time = 0
3643 update_forest_info.entries[i].flags &= ~lsa.LSA_NB_DISABLED_MASK
3644 update_forest_info.entries[i].flags &= ~lsa.LSA_SID_DISABLED_MASK
3646 for tln in enable_tln:
3648 for i in xrange(0, len(update_forest_info.entries)):
3649 r = update_forest_info.entries[i]
3650 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3652 if r.forest_trust_data.string.lower() != tln.lower():
3657 raise CommandError("Entry not found for value[%s] specified for --enable-tln" % tln)
3658 if not update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_MASK:
3659 raise CommandError("Entry found for value[%s] specified for --enable-tln is already enabled" % tln)
3660 update_forest_info.entries[idx].time = 0
3661 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3663 for tln in disable_tln:
3665 for i in xrange(0, len(update_forest_info.entries)):
3666 r = update_forest_info.entries[i]
3667 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3669 if r.forest_trust_data.string.lower() != tln.lower():
3674 raise CommandError("Entry not found for value[%s] specified for --disable-tln" % tln)
3675 if update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_ADMIN:
3676 raise CommandError("Entry found for value[%s] specified for --disable-tln is already disabled" % tln)
3677 update_forest_info.entries[idx].time = 0
3678 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3679 update_forest_info.entries[idx].flags |= lsa.LSA_TLN_DISABLED_ADMIN
3681 for tln_ex in add_tln_ex:
3683 for i in xrange(0, len(update_forest_info.entries)):
3684 r = update_forest_info.entries[i]
3685 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3687 if r.forest_trust_data.string.lower() != tln_ex.lower():
3692 raise CommandError("Entry already present for value[%s] specified for --add-tln-ex" % tln_ex)
3694 tln_dot = ".%s" % tln_ex.lower()
3696 for i in xrange(0, len(update_forest_info.entries)):
3697 r = update_forest_info.entries[i]
3698 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3700 r_dot = ".%s" % r.forest_trust_data.string.lower()
3701 if tln_dot == r_dot:
3702 raise CommandError("TLN entry present for value[%s] specified for --add-tln-ex" % tln_ex)
3703 if not tln_dot.endswith(r_dot):
3709 raise CommandError("No TLN parent present for value[%s] specified for --add-tln-ex" % tln_ex)
3711 r = lsa.ForestTrustRecord()
3712 r.type = lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX
3715 r.forest_trust_data.string = tln_ex
3718 entries.extend(update_forest_info.entries)
3719 entries.insert(idx + 1, r)
3720 update_forest_info.count = len(entries)
3721 update_forest_info.entries = entries
3723 for tln_ex in delete_tln_ex:
3725 for i in xrange(0, len(update_forest_info.entries)):
3726 r = update_forest_info.entries[i]
3727 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3729 if r.forest_trust_data.string.lower() != tln_ex.lower():
3734 raise CommandError("Entry not found for value[%s] specified for --delete-tln-ex" % tln_ex)
3737 entries.extend(update_forest_info.entries)
3739 update_forest_info.count = len(entries)
3740 update_forest_info.entries = entries
3742 for nb in enable_nb:
3744 for i in xrange(0, len(update_forest_info.entries)):
3745 r = update_forest_info.entries[i]
3746 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3748 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3753 raise CommandError("Entry not found for value[%s] specified for --enable-nb" % nb)
3754 if not update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_MASK:
3755 raise CommandError("Entry found for value[%s] specified for --enable-nb is already enabled" % nb)
3756 update_forest_info.entries[idx].time = 0
3757 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3759 for nb in disable_nb:
3761 for i in xrange(0, len(update_forest_info.entries)):
3762 r = update_forest_info.entries[i]
3763 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3765 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3770 raise CommandError("Entry not found for value[%s] specified for --delete-nb" % nb)
3771 if update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_ADMIN:
3772 raise CommandError("Entry found for value[%s] specified for --disable-nb is already disabled" % nb)
3773 update_forest_info.entries[idx].time = 0
3774 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3775 update_forest_info.entries[idx].flags |= lsa.LSA_NB_DISABLED_ADMIN
3777 for sid in enable_sid:
3779 for i in xrange(0, len(update_forest_info.entries)):
3780 r = update_forest_info.entries[i]
3781 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3783 if r.forest_trust_data.domain_sid != sid:
3788 raise CommandError("Entry not found for value[%s] specified for --enable-sid" % sid)
3789 if not update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_MASK:
3790 raise CommandError("Entry found for value[%s] specified for --enable-sid is already enabled" % nb)
3791 update_forest_info.entries[idx].time = 0
3792 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3794 for sid in disable_sid:
3796 for i in xrange(0, len(update_forest_info.entries)):
3797 r = update_forest_info.entries[i]
3798 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3800 if r.forest_trust_data.domain_sid != sid:
3805 raise CommandError("Entry not found for value[%s] specified for --delete-sid" % sid)
3806 if update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_ADMIN:
3807 raise CommandError("Entry found for value[%s] specified for --disable-sid is already disabled" % nb)
3808 update_forest_info.entries[idx].time = 0
3809 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3810 update_forest_info.entries[idx].flags |= lsa.LSA_SID_DISABLED_ADMIN
3813 update_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3814 local_tdo_info.domain_name,
3815 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3816 update_forest_info, 0)
3817 except RuntimeError as error:
3818 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3820 self.outf.write("Updated forest trust information...\n")
3821 self.write_forest_trust_info(update_forest_info,
3822 tln=local_tdo_info.domain_name.string,
3823 collisions=update_forest_collision)
3826 lsaString = lsa.String()
3827 lsaString.string = local_tdo_info.domain_name.string
3828 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3830 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3831 except RuntimeError as error:
3832 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3834 self.outf.write("Stored forest trust information...\n")
3835 self.write_forest_trust_info(stored_forest_info,
3836 tln=local_tdo_info.domain_name.string)
3839 class cmd_domain_tombstones_expunge(Command):
3840 """Expunge tombstones from the database.
3842 This command expunges tombstones from the database."""
3843 synopsis = "%prog NC [NC [...]] [options]"
3846 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
3847 metavar="URL", dest="H"),
3848 Option("--current-time",
3849 help="The current time to evaluate the tombstone lifetime from, expressed as YYYY-MM-DD",
3851 Option("--tombstone-lifetime", help="Number of days a tombstone should be preserved for", type=int),
3854 takes_args = ["nc*"]
3856 takes_optiongroups = {
3857 "sambaopts": options.SambaOptions,
3858 "credopts": options.CredentialsOptions,
3859 "versionopts": options.VersionOptions,
3862 def run(self, *ncs, **kwargs):
3863 sambaopts = kwargs.get("sambaopts")
3864 credopts = kwargs.get("credopts")
3865 versionpts = kwargs.get("versionopts")
3867 current_time_string = kwargs.get("current_time")
3868 tombstone_lifetime = kwargs.get("tombstone_lifetime")
3869 lp = sambaopts.get_loadparm()
3870 creds = credopts.get_credentials(lp)
3871 samdb = SamDB(url=H, session_info=system_session(),
3872 credentials=creds, lp=lp)
3874 if current_time_string is not None:
3875 current_time_obj = time.strptime(current_time_string, "%Y-%m-%d")
3876 current_time = long(time.mktime(current_time_obj))
3879 current_time = long(time.time())
3882 res = samdb.search(expression="", base="", scope=ldb.SCOPE_BASE,
3883 attrs=["namingContexts"])
3886 for nc in res[0]["namingContexts"]:
3891 started_transaction = False
3893 samdb.transaction_start()
3894 started_transaction = True
3896 removed_links) = samdb.garbage_collect_tombstones(ncs,
3897 current_time=current_time,
3898 tombstone_lifetime=tombstone_lifetime)
3900 except Exception as err:
3901 if started_transaction:
3902 samdb.transaction_cancel()
3903 raise CommandError("Failed to expunge / garbage collect tombstones", err)
3905 samdb.transaction_commit()
3907 self.outf.write("Removed %d objects and %d links successfully\n"
3908 % (removed_objects, removed_links))
3912 class cmd_domain_trust(SuperCommand):
3913 """Domain and forest trust management."""
3916 subcommands["list"] = cmd_domain_trust_list()
3917 subcommands["show"] = cmd_domain_trust_show()
3918 subcommands["create"] = cmd_domain_trust_create()
3919 subcommands["delete"] = cmd_domain_trust_delete()
3920 subcommands["validate"] = cmd_domain_trust_validate()
3921 subcommands["namespaces"] = cmd_domain_trust_namespaces()
3923 class cmd_domain_tombstones(SuperCommand):
3924 """Domain tombstone and recycled object management."""
3927 subcommands["expunge"] = cmd_domain_tombstones_expunge()
3929 class ldif_schema_update:
3930 """Helper class for applying LDIF schema updates"""
3933 self.is_defunct = False
3934 self.unknown_oid = None
3938 def _ldap_schemaUpdateNow(self, samdb):
3942 add: schemaUpdateNow
3945 samdb.modify_ldif(ldif)
3947 def can_ignore_failure(self, error):
3948 """Checks if we can safely ignore failure to apply an LDIF update"""
3949 (num, errstr) = error.args
3951 # Microsoft has marked objects as defunct that Samba doesn't know about
3952 if num == ldb.ERR_NO_SUCH_OBJECT and self.is_defunct:
3953 print("Defunct object %s doesn't exist, skipping" % self.dn)
3955 elif self.unknown_oid is not None:
3956 print("Skipping unknown OID %s for object %s" %(self.unknown_oid, self.dn))
3961 def apply(self, samdb):
3962 """Applies a single LDIF update to the schema"""
3966 samdb.modify_ldif(self.ldif, controls=['relax:0'])
3967 except ldb.LdbError as e:
3968 if e.args[0] == ldb.ERR_INVALID_ATTRIBUTE_SYNTAX:
3970 # REFRESH after a failed change
3972 # Otherwise the OID-to-attribute mapping in
3973 # _apply_updates_in_file() won't work, because it
3974 # can't lookup the new OID in the schema
3975 self._ldap_schemaUpdateNow(samdb)
3977 samdb.modify_ldif(self.ldif, controls=['relax:0'])
3980 except ldb.LdbError as e:
3981 if self.can_ignore_failure(e):
3984 print("Exception: %s" % e)
3985 print("Encountered while trying to apply the following LDIF")
3986 print("----------------------------------------------------")
3987 print("%s" % self.ldif)
3993 class cmd_domain_schema_upgrade(Command):
3994 """Domain schema upgrading"""
3996 synopsis = "%prog [options]"
3998 takes_optiongroups = {
3999 "sambaopts": options.SambaOptions,
4000 "versionopts": options.VersionOptions,
4001 "credopts": options.CredentialsOptions,
4005 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
4006 metavar="URL", dest="H"),
4007 Option("--quiet", help="Be quiet", action="store_true"),
4008 Option("--verbose", help="Be verbose", action="store_true"),
4009 Option("--schema", type="choice", metavar="SCHEMA",
4010 choices=["2012", "2012_R2"],
4011 help="The schema file to upgrade to. Default is (Windows) 2012_R2.",
4013 Option("--ldf-file", type=str, default=None,
4014 help="Just apply the schema updates in the adprep/.LDF file(s) specified"),
4015 Option("--base-dir", type=str, default=None,
4016 help="Location of ldf files Default is ${SETUPDIR}/adprep.")
4019 def _apply_updates_in_file(self, samdb, ldif_file):
4021 Applies a series of updates specified in an .LDIF file. The .LDIF file
4022 is based on the adprep Schema updates provided by Microsoft.
4025 ldif_op = ldif_schema_update()
4027 # parse the file line by line and work out each update operation to apply
4028 for line in ldif_file:
4030 line = line.rstrip()
4032 # the operations in the .LDIF file are separated by blank lines. If
4033 # we hit a blank line, try to apply the update we've parsed so far
4036 # keep going if we haven't parsed anything yet
4037 if ldif_op.ldif == '':
4040 # Apply the individual change
4041 count += ldif_op.apply(samdb)
4043 # start storing the next operation from scratch again
4044 ldif_op = ldif_schema_update()
4047 # replace the placeholder domain name in the .ldif file with the real domain
4048 if line.upper().endswith('DC=X'):
4049 line = line[:-len('DC=X')] + str(samdb.get_default_basedn())
4050 elif line.upper().endswith('CN=X'):
4051 line = line[:-len('CN=X')] + str(samdb.get_default_basedn())
4053 values = line.split(':')
4055 if values[0].lower() == 'dn':
4056 ldif_op.dn = values[1].strip()
4058 # replace the Windows-specific operation with the Samba one
4059 if values[0].lower() == 'changetype':
4060 line = line.lower().replace(': ntdsschemaadd',
4062 line = line.lower().replace(': ntdsschemamodify',
4065 if values[0].lower() in ['rdnattid', 'subclassof',
4066 'systemposssuperiors',
4068 'systemauxiliaryclass']:
4071 # The Microsoft updates contain some OIDs we don't recognize.
4072 # Query the DB to see if we can work out the OID this update is
4073 # referring to. If we find a match, then replace the OID with
4074 # the ldapDisplayname
4076 res = samdb.search(base=samdb.get_schema_basedn(),
4077 expression="(|(attributeId=%s)(governsId=%s))" %
4079 attrs=['ldapDisplayName'])
4082 ldif_op.unknown_oid = value
4084 display_name = res[0]['ldapDisplayName'][0]
4085 line = line.replace(value, ' ' + display_name)
4087 # Microsoft has marked objects as defunct that Samba doesn't know about
4088 if values[0].lower() == 'isdefunct' and values[1].strip().lower() == 'true':
4089 ldif_op.is_defunct = True
4091 # Samba has added the showInAdvancedViewOnly attribute to all objects,
4092 # so rather than doing an add, we need to do a replace
4093 if values[0].lower() == 'add' and values[1].strip().lower() == 'showinadvancedviewonly':
4094 line = 'replace: showInAdvancedViewOnly'
4096 # Add the line to the current LDIF operation (including the newline
4097 # we stripped off at the start of the loop)
4098 ldif_op.ldif += line + '\n'
4103 def _apply_update(self, samdb, update_file, base_dir):
4104 """Wrapper function for parsing an LDIF file and applying the updates"""
4106 print("Applying %s updates..." % update_file)
4110 ldif_file = open(os.path.join(base_dir, update_file))
4112 count = self._apply_updates_in_file(samdb, ldif_file)
4118 print("%u changes applied" % count)
4122 def run(self, **kwargs):
4123 from samba.ms_schema_markdown import read_ms_markdown
4124 from samba.schema import Schema
4126 updates_allowed_overriden = False
4127 sambaopts = kwargs.get("sambaopts")
4128 credopts = kwargs.get("credopts")
4129 versionpts = kwargs.get("versionopts")
4130 lp = sambaopts.get_loadparm()
4131 creds = credopts.get_credentials(lp)
4133 target_schema = kwargs.get("schema")
4134 ldf_files = kwargs.get("ldf_file")
4135 base_dir = kwargs.get("base_dir")
4139 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
4141 # we're not going to get far if the config doesn't allow schema updates
4142 if lp.get("dsdb:schema update allowed") is None:
4143 lp.set("dsdb:schema update allowed", "yes")
4144 print("Temporarily overriding 'dsdb:schema update allowed' setting")
4145 updates_allowed_overriden = True
4147 own_dn = ldb.Dn(samdb, samdb.get_dsServiceName())
4148 master = get_fsmo_roleowner(samdb, str(samdb.get_schema_basedn()),
4150 if own_dn != master:
4151 raise CommandError("This server is not the schema master.")
4153 # if specific LDIF files were specified, just apply them
4155 schema_updates = ldf_files.split(",")
4159 # work out the version of the target schema we're upgrading to
4160 end = Schema.get_version(target_schema)
4162 # work out the version of the schema we're currently using
4163 res = samdb.search(base=samdb.get_schema_basedn(),
4164 scope=ldb.SCOPE_BASE, attrs=['objectVersion'])
4167 raise CommandError('Could not determine current schema version')
4168 start = int(res[0]['objectVersion'][0]) + 1
4170 diff_dir = setup_path("adprep/WindowsServerDocs")
4171 if base_dir is None:
4172 # Read from the Schema-Updates.md file
4173 temp_folder = tempfile.mkdtemp()
4175 update_file = setup_path("adprep/WindowsServerDocs/Schema-Updates.md")
4178 read_ms_markdown(update_file, temp_folder)
4179 except Exception as e:
4180 print("Exception in markdown parsing: %s" % e)
4181 shutil.rmtree(temp_folder)
4182 raise CommandError('Failed to upgrade schema')
4184 base_dir = temp_folder
4186 for version in range(start, end + 1):
4187 update = 'Sch%d.ldf' % version
4188 schema_updates.append(update)
4190 # Apply patches if we parsed the Schema-Updates.md file
4191 diff = os.path.abspath(os.path.join(diff_dir, update + '.diff'))
4192 if temp_folder and os.path.exists(diff):
4194 p = subprocess.Popen(['patch', update, '-i', diff],
4195 stdout=subprocess.PIPE,
4196 stderr=subprocess.PIPE, cwd=temp_folder)
4197 except (OSError, IOError):
4198 shutil.rmtree(temp_folder)
4199 raise CommandError("Failed to upgrade schema. Check if 'patch' is installed.")
4201 stdout, stderr = p.communicate()
4204 print("Exception in patch: %s\n%s" % (stdout, stderr))
4205 shutil.rmtree(temp_folder)
4206 raise CommandError('Failed to upgrade schema')
4208 print("Patched %s using %s" % (update, diff))
4210 if base_dir is None:
4211 base_dir = setup_path("adprep")
4213 samdb.transaction_start()
4215 error_encountered = False
4218 # Apply the schema updates needed to move to the new schema version
4219 for ldif_file in schema_updates:
4220 count += self._apply_update(samdb, ldif_file, base_dir)
4223 samdb.transaction_commit()
4224 print("Schema successfully updated")
4226 print("No changes applied to schema")
4227 samdb.transaction_cancel()
4228 except Exception as e:
4229 print("Exception: %s" % e)
4230 print("Error encountered, aborting schema upgrade")
4231 samdb.transaction_cancel()
4232 error_encountered = True
4234 if updates_allowed_overriden:
4235 lp.set("dsdb:schema update allowed", "no")
4238 shutil.rmtree(temp_folder)
4240 if error_encountered:
4241 raise CommandError('Failed to upgrade schema')
4243 class cmd_domain_functional_prep(Command):
4244 """Domain functional level preparation"""
4246 synopsis = "%prog [options]"
4248 takes_optiongroups = {
4249 "sambaopts": options.SambaOptions,
4250 "versionopts": options.VersionOptions,
4251 "credopts": options.CredentialsOptions,
4255 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
4256 metavar="URL", dest="H"),
4257 Option("--quiet", help="Be quiet", action="store_true"),
4258 Option("--verbose", help="Be verbose", action="store_true"),
4259 Option("--function-level", type="choice", metavar="FUNCTION_LEVEL",
4260 choices=["2008_R2", "2012", "2012_R2"],
4261 help="The schema file to upgrade to. Default is (Windows) 2012_R2.",
4263 Option("--forest-prep", action="store_true",
4264 help="Run the forest prep (by default, both the domain and forest prep are run)."),
4265 Option("--domain-prep", action="store_true",
4266 help="Run the domain prep (by default, both the domain and forest prep are run).")
4269 def run(self, **kwargs):
4270 updates_allowed_overriden = False
4271 sambaopts = kwargs.get("sambaopts")
4272 credopts = kwargs.get("credopts")
4273 versionpts = kwargs.get("versionopts")
4274 lp = sambaopts.get_loadparm()
4275 creds = credopts.get_credentials(lp)
4277 target_level = string_version_to_constant[kwargs.get("function_level")]
4278 forest_prep = kwargs.get("forest_prep")
4279 domain_prep = kwargs.get("domain_prep")
4281 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
4283 # we're not going to get far if the config doesn't allow schema updates
4284 if lp.get("dsdb:schema update allowed") is None:
4285 lp.set("dsdb:schema update allowed", "yes")
4286 print("Temporarily overriding 'dsdb:schema update allowed' setting")
4287 updates_allowed_overriden = True
4289 if forest_prep is None and domain_prep is None:
4293 own_dn = ldb.Dn(samdb, samdb.get_dsServiceName())
4295 master = get_fsmo_roleowner(samdb, str(samdb.get_schema_basedn()),
4297 if own_dn != master:
4298 raise CommandError("This server is not the schema master.")
4301 domain_dn = samdb.domain_dn()
4302 infrastructure_dn = "CN=Infrastructure," + domain_dn
4303 master = get_fsmo_roleowner(samdb, infrastructure_dn,
4305 if own_dn != master:
4306 raise CommandError("This server is not the infrastructure master.")
4309 samdb.transaction_start()
4310 error_encountered = False
4312 from samba.forest_update import ForestUpdate
4313 forest = ForestUpdate(samdb, fix=True)
4315 forest.check_updates_iterator([53, 79, 80, 81, 82, 83])
4316 forest.check_updates_functional_level(target_level,
4317 DS_DOMAIN_FUNCTION_2008_R2,
4318 update_revision=True)
4320 samdb.transaction_commit()
4321 except Exception as e:
4322 print("Exception: %s" % e)
4323 samdb.transaction_cancel()
4324 error_encountered = True
4327 samdb.transaction_start()
4328 error_encountered = False
4330 from samba.domain_update import DomainUpdate
4332 domain = DomainUpdate(samdb, fix=True)
4333 domain.check_updates_functional_level(target_level,
4334 DS_DOMAIN_FUNCTION_2008,
4335 update_revision=True)
4337 samdb.transaction_commit()
4338 except Exception as e:
4339 print("Exception: %s" % e)
4340 samdb.transaction_cancel()
4341 error_encountered = True
4343 if updates_allowed_overriden:
4344 lp.set("dsdb:schema update allowed", "no")
4346 if error_encountered:
4347 raise CommandError('Failed to perform functional prep')
4349 class cmd_domain(SuperCommand):
4350 """Domain management."""
4353 subcommands["demote"] = cmd_domain_demote()
4354 if cmd_domain_export_keytab is not None:
4355 subcommands["exportkeytab"] = cmd_domain_export_keytab()
4356 subcommands["info"] = cmd_domain_info()
4357 subcommands["provision"] = cmd_domain_provision()
4358 subcommands["join"] = cmd_domain_join()
4359 subcommands["dcpromo"] = cmd_domain_dcpromo()
4360 subcommands["level"] = cmd_domain_level()
4361 subcommands["passwordsettings"] = cmd_domain_passwordsettings()
4362 subcommands["classicupgrade"] = cmd_domain_classicupgrade()
4363 subcommands["samba3upgrade"] = cmd_domain_samba3upgrade()
4364 subcommands["trust"] = cmd_domain_trust()
4365 subcommands["tombstones"] = cmd_domain_tombstones()
4366 subcommands["schemaupgrade"] = cmd_domain_schema_upgrade()
4367 subcommands["functionalprep"] = cmd_domain_functional_prep()