3 # Copyright Matthias Dieter Wallnoefer 2009
4 # Copyright Andrew Kroeger 2009
5 # Copyright Jelmer Vernooij 2007-2012
6 # Copyright Giampaolo Lauria 2011
7 # Copyright Matthieu Patou <mat@matws.net> 2011
8 # Copyright Andrew Bartlett 2008-2015
9 # Copyright Stefan Metzmacher 2012
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 3 of the License, or
14 # (at your option) any later version.
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 from __future__ import print_function
26 from __future__ import division
27 import samba.getopt as options
39 from samba import ntstatus
40 from samba import NTSTATUSError
41 from samba import werror
42 from getpass import getpass
43 from samba.net import Net, LIBNET_JOIN_AUTOMATIC
45 from samba.join import join_RODC, join_DC, join_subdomain
46 from samba.auth import system_session
47 from samba.samdb import SamDB, get_default_backend_store
48 from samba.ndr import ndr_unpack, ndr_pack, ndr_print
49 from samba.dcerpc import drsuapi
50 from samba.dcerpc import drsblobs
51 from samba.dcerpc import lsa
52 from samba.dcerpc import netlogon
53 from samba.dcerpc import security
54 from samba.dcerpc import nbt
55 from samba.dcerpc import misc
56 from samba.dcerpc.samr import DOMAIN_PASSWORD_COMPLEX, DOMAIN_PASSWORD_STORE_CLEARTEXT
57 from samba.netcmd import (
63 from samba.netcmd.fsmo import get_fsmo_roleowner
64 from samba.netcmd.common import netcmd_get_domain_infos_via_cldap
65 from samba.samba3 import Samba3
66 from samba.samba3 import param as s3param
67 from samba.upgrade import upgrade_from_samba3
68 from samba.drs_utils import (
69 sendDsReplicaSync, drsuapi_connect, drsException,
71 from samba import remove_dc, arcfour_encrypt, string_to_byte_array
73 from samba.dsdb import (
74 DS_DOMAIN_FUNCTION_2000,
75 DS_DOMAIN_FUNCTION_2003,
76 DS_DOMAIN_FUNCTION_2003_MIXED,
77 DS_DOMAIN_FUNCTION_2008,
78 DS_DOMAIN_FUNCTION_2008_R2,
79 DS_DOMAIN_FUNCTION_2012,
80 DS_DOMAIN_FUNCTION_2012_R2,
81 DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL,
82 DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL,
83 UF_WORKSTATION_TRUST_ACCOUNT,
84 UF_SERVER_TRUST_ACCOUNT,
85 UF_TRUSTED_FOR_DELEGATION,
86 UF_PARTIAL_SECRETS_ACCOUNT
89 from samba.provision import (
92 DEFAULT_MIN_PWD_LENGTH,
96 from samba.provision.common import (
102 from samba.netcmd.pso import cmd_domain_passwordsettings_pso
103 from samba.netcmd.domain_backup import cmd_domain_backup
105 string_version_to_constant = {
106 "2008_R2": DS_DOMAIN_FUNCTION_2008_R2,
107 "2012": DS_DOMAIN_FUNCTION_2012,
108 "2012_R2": DS_DOMAIN_FUNCTION_2012_R2,
111 common_provision_join_options = [
112 Option("--machinepass", type="string", metavar="PASSWORD",
113 help="choose machine password (otherwise random)"),
114 Option("--plaintext-secrets", action="store_true",
115 help="Store secret/sensitive values as plain text on disk" +
116 "(default is to encrypt secret/ensitive values)"),
117 Option("--backend-store", type="choice", metavar="BACKENDSTORE",
118 choices=["tdb", "mdb"],
119 help="Specify the database backend to be used "
120 "(default is %s)" % get_default_backend_store()),
121 Option("--targetdir", metavar="DIR",
122 help="Set target directory (where to store provision)", type=str),
123 Option("-q", "--quiet", help="Be quiet", action="store_true"),
126 common_join_options = [
127 Option("--server", help="DC to join", type=str),
128 Option("--site", help="site to join", type=str),
129 Option("--domain-critical-only",
130 help="only replicate critical domain objects",
131 action="store_true"),
132 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
133 choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
134 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
135 "BIND9_DLZ uses samba4 AD to store zone information, "
136 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
137 default="SAMBA_INTERNAL"),
138 Option("-v", "--verbose", help="Be verbose", action="store_true")
141 common_ntvfs_options = [
142 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
147 def get_testparm_var(testparm, smbconf, varname):
148 errfile = open(os.devnull, 'w')
149 p = subprocess.Popen([testparm, '-s', '-l',
150 '--parameter-name=%s' % varname, smbconf],
151 stdout=subprocess.PIPE, stderr=errfile)
152 (out, err) = p.communicate()
154 lines = out.split('\n')
156 return lines[0].strip()
160 import samba.dckeytab
162 cmd_domain_export_keytab = None
164 class cmd_domain_export_keytab(Command):
165 """Dump Kerberos keys of the domain into a keytab."""
167 synopsis = "%prog <keytab> [options]"
169 takes_optiongroups = {
170 "sambaopts": options.SambaOptions,
171 "credopts": options.CredentialsOptions,
172 "versionopts": options.VersionOptions,
176 Option("--principal", help="extract only this principal", type=str),
179 takes_args = ["keytab"]
181 def run(self, keytab, credopts=None, sambaopts=None, versionopts=None, principal=None):
182 lp = sambaopts.get_loadparm()
184 net.export_keytab(keytab=keytab, principal=principal)
187 class cmd_domain_info(Command):
188 """Print basic info about a domain and the DC passed as parameter."""
190 synopsis = "%prog <ip_address> [options]"
195 takes_optiongroups = {
196 "sambaopts": options.SambaOptions,
197 "credopts": options.CredentialsOptions,
198 "versionopts": options.VersionOptions,
201 takes_args = ["address"]
203 def run(self, address, credopts=None, sambaopts=None, versionopts=None):
204 lp = sambaopts.get_loadparm()
206 res = netcmd_get_domain_infos_via_cldap(lp, None, address)
208 raise CommandError("Invalid IP address '" + address + "'!")
209 self.outf.write("Forest : %s\n" % res.forest)
210 self.outf.write("Domain : %s\n" % res.dns_domain)
211 self.outf.write("Netbios domain : %s\n" % res.domain_name)
212 self.outf.write("DC name : %s\n" % res.pdc_dns_name)
213 self.outf.write("DC netbios name : %s\n" % res.pdc_name)
214 self.outf.write("Server site : %s\n" % res.server_site)
215 self.outf.write("Client site : %s\n" % res.client_site)
218 class cmd_domain_provision(Command):
219 """Provision a domain."""
221 synopsis = "%prog [options]"
223 takes_optiongroups = {
224 "sambaopts": options.SambaOptions,
225 "versionopts": options.VersionOptions,
229 Option("--interactive", help="Ask for names", action="store_true"),
230 Option("--domain", type="string", metavar="DOMAIN",
231 help="NetBIOS domain name to use"),
232 Option("--domain-guid", type="string", metavar="GUID",
233 help="set domainguid (otherwise random)"),
234 Option("--domain-sid", type="string", metavar="SID",
235 help="set domainsid (otherwise random)"),
236 Option("--ntds-guid", type="string", metavar="GUID",
237 help="set NTDS object GUID (otherwise random)"),
238 Option("--invocationid", type="string", metavar="GUID",
239 help="set invocationid (otherwise random)"),
240 Option("--host-name", type="string", metavar="HOSTNAME",
241 help="set hostname"),
242 Option("--host-ip", type="string", metavar="IPADDRESS",
243 help="set IPv4 ipaddress"),
244 Option("--host-ip6", type="string", metavar="IP6ADDRESS",
245 help="set IPv6 ipaddress"),
246 Option("--site", type="string", metavar="SITENAME",
247 help="set site name"),
248 Option("--adminpass", type="string", metavar="PASSWORD",
249 help="choose admin password (otherwise random)"),
250 Option("--krbtgtpass", type="string", metavar="PASSWORD",
251 help="choose krbtgt password (otherwise random)"),
252 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
253 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
254 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
255 "BIND9_FLATFILE uses bind9 text database to store zone information, "
256 "BIND9_DLZ uses samba4 AD to store zone information, "
257 "NONE skips the DNS setup entirely (not recommended)",
258 default="SAMBA_INTERNAL"),
259 Option("--dnspass", type="string", metavar="PASSWORD",
260 help="choose dns password (otherwise random)"),
261 Option("--root", type="string", metavar="USERNAME",
262 help="choose 'root' unix username"),
263 Option("--nobody", type="string", metavar="USERNAME",
264 help="choose 'nobody' user"),
265 Option("--users", type="string", metavar="GROUPNAME",
266 help="choose 'users' group"),
267 Option("--blank", action="store_true",
268 help="do not add users or groups, just the structure"),
269 Option("--server-role", type="choice", metavar="ROLE",
270 choices=["domain controller", "dc", "member server", "member", "standalone"],
271 help="The server role (domain controller | dc | member server | member | standalone). Default is dc.",
272 default="domain controller"),
273 Option("--function-level", type="choice", metavar="FOR-FUN-LEVEL",
274 choices=["2000", "2003", "2008", "2008_R2"],
275 help="The domain and forest function level (2000 | 2003 | 2008 | 2008_R2 - always native). Default is (Windows) 2008_R2 Native.",
277 Option("--base-schema", type="choice", metavar="BASE-SCHEMA",
278 choices=["2008_R2", "2008_R2_old", "2012", "2012_R2"],
279 help="The base schema files to use. Default is (Windows) 2008_R2.",
281 Option("--next-rid", type="int", metavar="NEXTRID", default=1000,
282 help="The initial nextRid value (only needed for upgrades). Default is 1000."),
283 Option("--partitions-only",
284 help="Configure Samba's partitions, but do not modify them (ie, join a BDC)", action="store_true"),
285 Option("--use-rfc2307", action="store_true", help="Use AD to store posix attributes (default = no)"),
289 Option("--ldapadminpass", type="string", metavar="PASSWORD",
290 help="choose password to set between Samba and its LDAP backend (otherwise random)"),
291 Option("--ldap-backend-type", type="choice", metavar="LDAP-BACKEND-TYPE",
292 help="Test initialisation support for unsupported LDAP backend type (fedora-ds or openldap) DO NOT USE",
293 choices=["fedora-ds", "openldap"]),
294 Option("--ol-mmr-urls", type="string", metavar="LDAPSERVER",
295 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\""),
296 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",
297 action="store_true"),
298 Option("--slapd-path", type="string", metavar="SLAPD-PATH",
299 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."),
300 Option("--ldap-backend-extra-port", type="int", metavar="LDAP-BACKEND-EXTRA-PORT", help="Additional TCP port for LDAP backend server (to use for replication)"),
301 Option("--ldap-backend-forced-uri", type="string", metavar="LDAP-BACKEND-FORCED-URI",
302 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"),
303 Option("--ldap-backend-nosync", help="Configure LDAP backend not to call fsync() (for performance in test environments)", action="store_true"),
307 Option("--use-xattrs", type="choice", choices=["yes", "no", "auto"],
308 metavar="[yes|no|auto]",
309 help="Define if we should use the native fs capabilities or a tdb file for "
310 "storing attributes likes ntacl when --use-ntvfs is set. "
311 "auto tries to make an inteligent guess based on the user rights and system capabilities",
315 takes_options.extend(common_provision_join_options)
317 if os.getenv('TEST_LDAP', "no") == "yes":
318 takes_options.extend(openldap_options)
320 if samba.is_ntvfs_fileserver_built():
321 takes_options.extend(common_ntvfs_options)
322 takes_options.extend(ntvfs_options)
326 def run(self, sambaopts=None, versionopts=None,
349 ldap_backend_type=None,
353 partitions_only=None,
360 ldap_backend_nosync=None,
361 ldap_backend_extra_port=None,
362 ldap_backend_forced_uri=None,
363 ldap_dryrun_mode=None,
365 plaintext_secrets=False,
368 self.logger = self.get_logger("provision")
370 self.logger.setLevel(logging.WARNING)
372 self.logger.setLevel(logging.INFO)
374 lp = sambaopts.get_loadparm()
375 smbconf = lp.configfile
377 if dns_forwarder is not None:
378 suggested_forwarder = dns_forwarder
380 suggested_forwarder = self._get_nameserver_ip()
381 if suggested_forwarder is None:
382 suggested_forwarder = "none"
384 if len(self.raw_argv) == 1:
388 from getpass import getpass
391 def ask(prompt, default=None):
392 if default is not None:
393 print("%s [%s]: " % (prompt, default), end=' ')
395 print("%s: " % (prompt,), end=' ')
396 return sys.stdin.readline().rstrip("\n") or default
399 default = socket.getfqdn().split(".", 1)[1].upper()
402 realm = ask("Realm", default)
403 if realm in (None, ""):
404 raise CommandError("No realm set!")
407 default = realm.split(".")[0]
410 domain = ask("Domain", default)
412 raise CommandError("No domain set!")
414 server_role = ask("Server Role (dc, member, standalone)", "dc")
416 dns_backend = ask("DNS backend (SAMBA_INTERNAL, BIND9_FLATFILE, BIND9_DLZ, NONE)", "SAMBA_INTERNAL")
417 if dns_backend in (None, ''):
418 raise CommandError("No DNS backend set!")
420 if dns_backend == "SAMBA_INTERNAL":
421 dns_forwarder = ask("DNS forwarder IP address (write 'none' to disable forwarding)", suggested_forwarder)
422 if dns_forwarder.lower() in (None, 'none'):
423 suggested_forwarder = None
427 adminpassplain = getpass("Administrator password: ")
428 issue = self._adminpass_issue(adminpassplain)
430 self.errf.write("%s.\n" % issue)
432 adminpassverify = getpass("Retype password: ")
433 if not adminpassplain == adminpassverify:
434 self.errf.write("Sorry, passwords do not match.\n")
436 adminpass = adminpassplain
440 realm = sambaopts._lp.get('realm')
442 raise CommandError("No realm set!")
444 raise CommandError("No domain set!")
447 issue = self._adminpass_issue(adminpass)
449 raise CommandError(issue)
451 self.logger.info("Administrator password will be set randomly!")
453 if function_level == "2000":
454 dom_for_fun_level = DS_DOMAIN_FUNCTION_2000
455 elif function_level == "2003":
456 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
457 elif function_level == "2008":
458 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008
459 elif function_level == "2008_R2":
460 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
462 if dns_backend == "SAMBA_INTERNAL" and dns_forwarder is None:
463 dns_forwarder = suggested_forwarder
465 samdb_fill = FILL_FULL
467 samdb_fill = FILL_NT4SYNC
468 elif partitions_only:
469 samdb_fill = FILL_DRS
471 if targetdir is not None:
472 if not os.path.isdir(targetdir):
477 if use_xattrs == "yes":
479 elif use_xattrs == "auto" and use_ntvfs == False:
481 elif use_ntvfs == False:
482 raise CommandError("--use-xattrs=no requires --use-ntvfs (not supported for production use). "
483 "Please re-run with --use-xattrs omitted.")
484 elif use_xattrs == "auto" and not lp.get("posix:eadb"):
486 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
488 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
491 samba.ntacls.setntacl(lp, file.name,
492 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
495 self.logger.info("You are not root or your system does not support xattr, using tdb backend for attributes. ")
500 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.")
501 if ldap_backend_type == "existing":
502 if ldap_backend_forced_uri is not None:
503 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)
505 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")
507 if ldap_backend_forced_uri is not None:
508 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")
510 if domain_sid is not None:
511 domain_sid = security.dom_sid(domain_sid)
513 session = system_session()
514 if backend_store is None:
515 backend_store = get_default_backend_store()
517 result = provision(self.logger,
518 session, smbconf=smbconf, targetdir=targetdir,
519 samdb_fill=samdb_fill, realm=realm, domain=domain,
520 domainguid=domain_guid, domainsid=domain_sid,
522 hostip=host_ip, hostip6=host_ip6,
523 sitename=site, ntdsguid=ntds_guid,
524 invocationid=invocationid, adminpass=adminpass,
525 krbtgtpass=krbtgtpass, machinepass=machinepass,
526 dns_backend=dns_backend, dns_forwarder=dns_forwarder,
527 dnspass=dnspass, root=root, nobody=nobody,
529 serverrole=server_role, dom_for_fun_level=dom_for_fun_level,
530 backend_type=ldap_backend_type,
531 ldapadminpass=ldapadminpass, ol_mmr_urls=ol_mmr_urls, slapd_path=slapd_path,
532 useeadb=eadb, next_rid=next_rid, lp=lp, use_ntvfs=use_ntvfs,
533 use_rfc2307=use_rfc2307, skip_sysvolacl=False,
534 ldap_backend_extra_port=ldap_backend_extra_port,
535 ldap_backend_forced_uri=ldap_backend_forced_uri,
536 nosync=ldap_backend_nosync, ldap_dryrun_mode=ldap_dryrun_mode,
537 base_schema=base_schema,
538 plaintext_secrets=plaintext_secrets,
539 backend_store=backend_store)
541 except ProvisioningError as e:
542 raise CommandError("Provision failed", e)
544 result.report_logger(self.logger)
546 def _get_nameserver_ip(self):
547 """Grab the nameserver IP address from /etc/resolv.conf."""
549 RESOLV_CONF = "/etc/resolv.conf"
551 if not path.isfile(RESOLV_CONF):
552 self.logger.warning("Failed to locate %s" % RESOLV_CONF)
557 handle = open(RESOLV_CONF, 'r')
559 if not line.startswith('nameserver'):
561 # we want the last non-space continuous string of the line
562 return line.strip().split()[-1]
564 if handle is not None:
567 self.logger.warning("No nameserver found in %s" % RESOLV_CONF)
569 def _adminpass_issue(self, adminpass):
570 """Returns error string for a bad administrator password,
571 or None if acceptable"""
573 if len(adminpass.decode('utf-8')) < DEFAULT_MIN_PWD_LENGTH:
574 return "Administrator password does not meet the default minimum" \
575 " password length requirement (%d characters)" \
576 % DEFAULT_MIN_PWD_LENGTH
577 elif not samba.check_password_quality(adminpass):
578 return "Administrator password does not meet the default" \
584 class cmd_domain_dcpromo(Command):
585 """Promote an existing domain member or NT4 PDC to an AD DC."""
587 synopsis = "%prog <dnsdomain> [DC|RODC] [options]"
589 takes_optiongroups = {
590 "sambaopts": options.SambaOptions,
591 "versionopts": options.VersionOptions,
592 "credopts": options.CredentialsOptions,
596 takes_options.extend(common_join_options)
598 takes_options.extend(common_provision_join_options)
600 if samba.is_ntvfs_fileserver_built():
601 takes_options.extend(common_ntvfs_options)
603 takes_args = ["domain", "role?"]
605 def run(self, domain, role=None, sambaopts=None, credopts=None,
606 versionopts=None, server=None, site=None, targetdir=None,
607 domain_critical_only=False, parent_domain=None, machinepass=None,
608 use_ntvfs=False, dns_backend=None,
609 quiet=False, verbose=False, plaintext_secrets=False,
611 lp = sambaopts.get_loadparm()
612 creds = credopts.get_credentials(lp)
613 net = Net(creds, lp, server=credopts.ipaddress)
615 logger = self.get_logger()
617 logger.setLevel(logging.DEBUG)
619 logger.setLevel(logging.WARNING)
621 logger.setLevel(logging.INFO)
623 netbios_name = lp.get("netbios name")
629 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
630 site=site, netbios_name=netbios_name, targetdir=targetdir,
631 domain_critical_only=domain_critical_only,
632 machinepass=machinepass, use_ntvfs=use_ntvfs,
633 dns_backend=dns_backend,
634 promote_existing=True, plaintext_secrets=plaintext_secrets,
635 backend_store=backend_store)
637 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
638 site=site, netbios_name=netbios_name, targetdir=targetdir,
639 domain_critical_only=domain_critical_only,
640 machinepass=machinepass, use_ntvfs=use_ntvfs, dns_backend=dns_backend,
641 promote_existing=True, plaintext_secrets=plaintext_secrets,
642 backend_store=backend_store)
644 raise CommandError("Invalid role '%s' (possible values: DC, RODC)" % role)
647 class cmd_domain_join(Command):
648 """Join domain as either member or backup domain controller."""
650 synopsis = "%prog <dnsdomain> [DC|RODC|MEMBER|SUBDOMAIN] [options]"
652 takes_optiongroups = {
653 "sambaopts": options.SambaOptions,
654 "versionopts": options.VersionOptions,
655 "credopts": options.CredentialsOptions,
659 Option("--parent-domain", help="parent domain to create subdomain under", type=str),
660 Option("--adminpass", type="string", metavar="PASSWORD",
661 help="choose adminstrator password when joining as a subdomain (otherwise random)"),
665 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
668 takes_options.extend(common_join_options)
669 takes_options.extend(common_provision_join_options)
671 if samba.is_ntvfs_fileserver_built():
672 takes_options.extend(ntvfs_options)
674 takes_args = ["domain", "role?"]
676 def run(self, domain, role=None, sambaopts=None, credopts=None,
677 versionopts=None, server=None, site=None, targetdir=None,
678 domain_critical_only=False, parent_domain=None, machinepass=None,
679 use_ntvfs=False, dns_backend=None, adminpass=None,
680 quiet=False, verbose=False,
681 plaintext_secrets=False,
683 lp = sambaopts.get_loadparm()
684 creds = credopts.get_credentials(lp)
685 net = Net(creds, lp, server=credopts.ipaddress)
688 site = "Default-First-Site-Name"
690 logger = self.get_logger()
692 logger.setLevel(logging.DEBUG)
694 logger.setLevel(logging.WARNING)
696 logger.setLevel(logging.INFO)
698 netbios_name = lp.get("netbios name")
703 if role is None or role == "MEMBER":
704 (join_password, sid, domain_name) = net.join_member(
705 domain, netbios_name, LIBNET_JOIN_AUTOMATIC,
706 machinepass=machinepass)
708 self.errf.write("Joined domain %s (%s)\n" % (domain_name, sid))
710 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
711 site=site, netbios_name=netbios_name, targetdir=targetdir,
712 domain_critical_only=domain_critical_only,
713 machinepass=machinepass, use_ntvfs=use_ntvfs,
714 dns_backend=dns_backend,
715 plaintext_secrets=plaintext_secrets,
716 backend_store=backend_store)
718 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
719 site=site, netbios_name=netbios_name, targetdir=targetdir,
720 domain_critical_only=domain_critical_only,
721 machinepass=machinepass, use_ntvfs=use_ntvfs,
722 dns_backend=dns_backend,
723 plaintext_secrets=plaintext_secrets,
724 backend_store=backend_store)
725 elif role == "SUBDOMAIN":
727 logger.info("Administrator password will be set randomly!")
729 netbios_domain = lp.get("workgroup")
730 if parent_domain is None:
731 parent_domain = ".".join(domain.split(".")[1:])
732 join_subdomain(logger=logger, server=server, creds=creds, lp=lp, dnsdomain=domain,
733 parent_domain=parent_domain, site=site,
734 netbios_name=netbios_name, netbios_domain=netbios_domain,
735 targetdir=targetdir, machinepass=machinepass,
736 use_ntvfs=use_ntvfs, dns_backend=dns_backend,
738 plaintext_secrets=plaintext_secrets,
739 backend_store=backend_store)
741 raise CommandError("Invalid role '%s' (possible values: MEMBER, DC, RODC, SUBDOMAIN)" % role)
744 class cmd_domain_demote(Command):
745 """Demote ourselves from the role of Domain Controller."""
747 synopsis = "%prog [options]"
750 Option("--server", help="writable DC to write demotion changes on", type=str),
751 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
752 metavar="URL", dest="H"),
753 Option("--remove-other-dead-server", help="Dead DC (name or NTDS GUID) "
754 "to remove ALL references to (rather than this DC)", type=str),
755 Option("-q", "--quiet", help="Be quiet", action="store_true"),
756 Option("-v", "--verbose", help="Be verbose", action="store_true"),
759 takes_optiongroups = {
760 "sambaopts": options.SambaOptions,
761 "credopts": options.CredentialsOptions,
762 "versionopts": options.VersionOptions,
765 def run(self, sambaopts=None, credopts=None,
766 versionopts=None, server=None,
767 remove_other_dead_server=None, H=None,
768 verbose=False, quiet=False):
769 lp = sambaopts.get_loadparm()
770 creds = credopts.get_credentials(lp)
771 net = Net(creds, lp, server=credopts.ipaddress)
773 logger = self.get_logger()
775 logger.setLevel(logging.DEBUG)
777 logger.setLevel(logging.WARNING)
779 logger.setLevel(logging.INFO)
781 if remove_other_dead_server is not None:
782 if server is not None:
783 samdb = SamDB(url="ldap://%s" % server,
784 session_info=system_session(),
785 credentials=creds, lp=lp)
787 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
789 remove_dc.remove_dc(samdb, logger, remove_other_dead_server)
790 except remove_dc.DemoteException as err:
791 raise CommandError("Demote failed: %s" % err)
794 netbios_name = lp.get("netbios name")
795 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
797 res = samdb.search(expression='(&(objectClass=computer)(serverReferenceBL=*))', attrs=["dnsHostName", "name"])
799 raise CommandError("Unable to search for servers")
802 raise CommandError("You are the last server in the domain")
806 if str(e["name"]).lower() != netbios_name.lower():
807 server = e["dnsHostName"]
810 ntds_guid = samdb.get_ntds_GUID()
811 msg = samdb.search(base=str(samdb.get_config_basedn()),
812 scope=ldb.SCOPE_SUBTREE, expression="(objectGUID=%s)" % ntds_guid,
814 if len(msg) == 0 or "options" not in msg[0]:
815 raise CommandError("Failed to find options on %s" % ntds_guid)
818 dsa_options = int(str(msg[0]['options']))
820 res = samdb.search(expression="(fSMORoleOwner=%s)" % str(ntds_dn),
821 controls=["search_options:1:2"])
824 raise CommandError("Current DC is still the owner of %d role(s), "
825 "use the role command to transfer roles to "
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")
843 self.errf.write("Asking partner server %s to synchronize from us\n"
845 for part in (samdb.get_schema_basedn(),
846 samdb.get_config_basedn(),
847 samdb.get_root_basedn()):
848 nc = drsuapi.DsReplicaObjectIdentifier()
851 req1 = drsuapi.DsReplicaSyncRequest1()
852 req1.naming_context = nc;
853 req1.options = drsuapi.DRSUAPI_DRS_WRIT_REP
854 req1.source_dsa_guid = misc.GUID(ntds_guid)
857 drsuapiBind.DsReplicaSync(drsuapi_handle, 1, req1)
858 except RuntimeError as e1:
859 (werr, string) = e1.args
860 if werr == werror.WERR_DS_DRA_NO_REPLICA:
864 "Error while replicating out last local changes from '%s' for demotion, "
865 "re-enabling inbound replication\n" % part)
866 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
867 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
869 raise CommandError("Error while sending a DsReplicaSync for partition '%s'" % str(part), string)
871 remote_samdb = SamDB(url="ldap://%s" % server,
872 session_info=system_session(),
873 credentials=creds, lp=lp)
875 self.errf.write("Changing userControl and container\n")
876 res = remote_samdb.search(base=str(remote_samdb.domain_dn()),
877 expression="(&(objectClass=user)(sAMAccountName=%s$))" %
878 netbios_name.upper(),
879 attrs=["userAccountControl"])
881 uac = int(str(res[0]["userAccountControl"]))
883 except Exception as e:
884 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
886 "Error while demoting, re-enabling inbound replication\n")
887 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
888 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
890 raise CommandError("Error while changing account control", e)
893 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
895 "Error while demoting, re-enabling inbound replication")
896 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
897 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
899 raise CommandError("Unable to find object with samaccountName = %s$"
900 " in the remote dc" % netbios_name.upper())
904 uac &= ~(UF_SERVER_TRUST_ACCOUNT |UF_TRUSTED_FOR_DELEGATION |UF_PARTIAL_SECRETS_ACCOUNT)
905 uac |= UF_WORKSTATION_TRUST_ACCOUNT
910 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
911 ldb.FLAG_MOD_REPLACE,
912 "userAccountControl")
914 remote_samdb.modify(msg)
915 except Exception as e:
916 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
918 "Error while demoting, re-enabling inbound replication")
919 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
920 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
923 raise CommandError("Error while changing account control", e)
925 parent = msg.dn.parent()
926 dc_name = res[0].dn.get_rdn_value()
927 rdn = "CN=%s" % dc_name
929 # Let's move to the Computer container
933 computer_dn = ldb.Dn(remote_samdb, "CN=Computers,%s" % str(remote_samdb.domain_dn()))
934 res = remote_samdb.search(base=computer_dn, expression=rdn, scope=ldb.SCOPE_ONELEVEL)
937 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
938 scope=ldb.SCOPE_ONELEVEL)
939 while(len(res) != 0 and i < 100):
941 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
942 scope=ldb.SCOPE_ONELEVEL)
945 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
947 "Error while demoting, re-enabling inbound replication\n")
948 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
949 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
955 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
956 ldb.FLAG_MOD_REPLACE,
957 "userAccountControl")
959 remote_samdb.modify(msg)
961 raise CommandError("Unable to find a slot for renaming %s,"
962 " all names from %s-1 to %s-%d seemed used" %
963 (str(dc_dn), rdn, rdn, i - 9))
965 newrdn = "%s-%d" % (rdn, i)
968 newdn = ldb.Dn(remote_samdb, "%s,%s" % (newrdn, str(computer_dn)))
969 remote_samdb.rename(dc_dn, newdn)
970 except Exception as e:
971 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
973 "Error while demoting, re-enabling inbound replication\n")
974 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
975 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
981 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
982 ldb.FLAG_MOD_REPLACE,
983 "userAccountControl")
985 remote_samdb.modify(msg)
986 raise CommandError("Error while renaming %s to %s" % (str(dc_dn), str(newdn)), e)
988 server_dsa_dn = samdb.get_serverName()
989 domain = remote_samdb.get_root_basedn()
992 req1 = drsuapi.DsRemoveDSServerRequest1()
993 req1.server_dn = str(server_dsa_dn)
994 req1.domain_dn = str(domain)
997 drsuapiBind.DsRemoveDSServer(drsuapi_handle, 1, req1)
998 except RuntimeError as e3:
999 (werr, string) = e3.args
1000 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
1002 "Error while demoting, re-enabling inbound replication\n")
1003 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
1004 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
1010 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
1011 ldb.FLAG_MOD_REPLACE,
1012 "userAccountControl")
1013 remote_samdb.modify(msg)
1014 remote_samdb.rename(newdn, dc_dn)
1015 if werr == werror.WERR_DS_DRA_NO_REPLICA:
1016 raise CommandError("The DC %s is not present on (already "
1017 "removed from) the remote server: %s" %
1018 (server_dsa_dn, e3))
1020 raise CommandError("Error while sending a removeDsServer "
1022 (server_dsa_dn, e3))
1024 remove_dc.remove_sysvol_references(remote_samdb, logger, dc_name)
1026 # These are objects under the computer account that should be deleted
1027 for s in ("CN=Enterprise,CN=NTFRS Subscriptions",
1028 "CN=%s, CN=NTFRS Subscriptions" % lp.get("realm"),
1029 "CN=Domain system Volumes (SYSVOL Share), CN=NTFRS Subscriptions",
1030 "CN=NTFRS Subscriptions"):
1032 remote_samdb.delete(ldb.Dn(remote_samdb,
1033 "%s,%s" % (s, str(newdn))))
1034 except ldb.LdbError as l:
1037 # get dns host name for target server to demote, remove dns references
1038 remove_dc.remove_dns_references(remote_samdb, logger, samdb.host_dns_name(),
1039 ignore_no_name=True)
1041 self.errf.write("Demote successful\n")
1044 class cmd_domain_level(Command):
1045 """Raise domain and forest function levels."""
1047 synopsis = "%prog (show|raise <options>) [options]"
1049 takes_optiongroups = {
1050 "sambaopts": options.SambaOptions,
1051 "credopts": options.CredentialsOptions,
1052 "versionopts": options.VersionOptions,
1056 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1057 metavar="URL", dest="H"),
1058 Option("-q", "--quiet", help="Be quiet", action="store_true"), # unused
1059 Option("--forest-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
1060 help="The forest function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)"),
1061 Option("--domain-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
1062 help="The domain function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)")
1065 takes_args = ["subcommand"]
1067 def run(self, subcommand, H=None, forest_level=None, domain_level=None,
1068 quiet=False, credopts=None, sambaopts=None, versionopts=None):
1069 lp = sambaopts.get_loadparm()
1070 creds = credopts.get_credentials(lp, fallback_machine=True)
1072 samdb = SamDB(url=H, session_info=system_session(),
1073 credentials=creds, lp=lp)
1075 domain_dn = samdb.domain_dn()
1077 res_forest = samdb.search("CN=Partitions,%s" % samdb.get_config_basedn(),
1078 scope=ldb.SCOPE_BASE, attrs=["msDS-Behavior-Version"])
1079 assert len(res_forest) == 1
1081 res_domain = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1082 attrs=["msDS-Behavior-Version", "nTMixedDomain"])
1083 assert len(res_domain) == 1
1085 res_dc_s = samdb.search("CN=Sites,%s" % samdb.get_config_basedn(),
1086 scope=ldb.SCOPE_SUBTREE, expression="(objectClass=nTDSDSA)",
1087 attrs=["msDS-Behavior-Version"])
1088 assert len(res_dc_s) >= 1
1090 # default values, since "msDS-Behavior-Version" does not exist on Windows 2000 AD
1091 level_forest = DS_DOMAIN_FUNCTION_2000
1092 level_domain = DS_DOMAIN_FUNCTION_2000
1094 if "msDS-Behavior-Version" in res_forest[0]:
1095 level_forest = int(res_forest[0]["msDS-Behavior-Version"][0])
1096 if "msDS-Behavior-Version" in res_domain[0]:
1097 level_domain = int(res_domain[0]["msDS-Behavior-Version"][0])
1098 level_domain_mixed = int(res_domain[0]["nTMixedDomain"][0])
1101 for msg in res_dc_s:
1102 if "msDS-Behavior-Version" in msg:
1103 if min_level_dc is None or int(msg["msDS-Behavior-Version"][0]) < min_level_dc:
1104 min_level_dc = int(msg["msDS-Behavior-Version"][0])
1106 min_level_dc = DS_DOMAIN_FUNCTION_2000
1107 # well, this is the least
1110 if level_forest < DS_DOMAIN_FUNCTION_2000 or level_domain < DS_DOMAIN_FUNCTION_2000:
1111 raise CommandError("Domain and/or forest function level(s) is/are invalid. Correct them or reprovision!")
1112 if min_level_dc < DS_DOMAIN_FUNCTION_2000:
1113 raise CommandError("Lowest function level of a DC is invalid. Correct this or reprovision!")
1114 if level_forest > level_domain:
1115 raise CommandError("Forest function level is higher than the domain level(s). Correct this or reprovision!")
1116 if level_domain > min_level_dc:
1117 raise CommandError("Domain function level is higher than the lowest function level of a DC. Correct this or reprovision!")
1119 if subcommand == "show":
1120 self.message("Domain and forest function level for domain '%s'" % domain_dn)
1121 if level_forest == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1122 self.message("\nATTENTION: You run SAMBA 4 on a forest function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1123 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1124 self.message("\nATTENTION: You run SAMBA 4 on a domain function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1125 if min_level_dc == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1126 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)!")
1130 if level_forest == DS_DOMAIN_FUNCTION_2000:
1132 elif level_forest == DS_DOMAIN_FUNCTION_2003_MIXED:
1133 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1134 elif level_forest == DS_DOMAIN_FUNCTION_2003:
1136 elif level_forest == DS_DOMAIN_FUNCTION_2008:
1138 elif level_forest == DS_DOMAIN_FUNCTION_2008_R2:
1140 elif level_forest == DS_DOMAIN_FUNCTION_2012:
1142 elif level_forest == DS_DOMAIN_FUNCTION_2012_R2:
1145 outstr = "higher than 2012 R2"
1146 self.message("Forest function level: (Windows) " + outstr)
1148 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1149 outstr = "2000 mixed (NT4 DC support)"
1150 elif level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed == 0:
1152 elif level_domain == DS_DOMAIN_FUNCTION_2003_MIXED:
1153 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1154 elif level_domain == DS_DOMAIN_FUNCTION_2003:
1156 elif level_domain == DS_DOMAIN_FUNCTION_2008:
1158 elif level_domain == DS_DOMAIN_FUNCTION_2008_R2:
1160 elif level_domain == DS_DOMAIN_FUNCTION_2012:
1162 elif level_domain == DS_DOMAIN_FUNCTION_2012_R2:
1165 outstr = "higher than 2012 R2"
1166 self.message("Domain function level: (Windows) " + outstr)
1168 if min_level_dc == DS_DOMAIN_FUNCTION_2000:
1170 elif min_level_dc == DS_DOMAIN_FUNCTION_2003:
1172 elif min_level_dc == DS_DOMAIN_FUNCTION_2008:
1174 elif min_level_dc == DS_DOMAIN_FUNCTION_2008_R2:
1176 elif min_level_dc == DS_DOMAIN_FUNCTION_2012:
1178 elif min_level_dc == DS_DOMAIN_FUNCTION_2012_R2:
1181 outstr = "higher than 2012 R2"
1182 self.message("Lowest function level of a DC: (Windows) " + outstr)
1184 elif subcommand == "raise":
1187 if domain_level is not None:
1188 if domain_level == "2003":
1189 new_level_domain = DS_DOMAIN_FUNCTION_2003
1190 elif domain_level == "2008":
1191 new_level_domain = DS_DOMAIN_FUNCTION_2008
1192 elif domain_level == "2008_R2":
1193 new_level_domain = DS_DOMAIN_FUNCTION_2008_R2
1194 elif domain_level == "2012":
1195 new_level_domain = DS_DOMAIN_FUNCTION_2012
1196 elif domain_level == "2012_R2":
1197 new_level_domain = DS_DOMAIN_FUNCTION_2012_R2
1199 if new_level_domain <= level_domain and level_domain_mixed == 0:
1200 raise CommandError("Domain function level can't be smaller than or equal to the actual one!")
1201 if new_level_domain > min_level_dc:
1202 raise CommandError("Domain function level can't be higher than the lowest function level of a DC!")
1204 # Deactivate mixed/interim domain support
1205 if level_domain_mixed != 0:
1206 # Directly on the base DN
1208 m.dn = ldb.Dn(samdb, domain_dn)
1209 m["nTMixedDomain"] = ldb.MessageElement("0",
1210 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1214 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup") + ",CN=Partitions,%s" % samdb.get_config_basedn())
1215 m["nTMixedDomain"] = ldb.MessageElement("0",
1216 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1219 except ldb.LdbError as e:
1220 (enum, emsg) = e.args
1221 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1224 # Directly on the base DN
1226 m.dn = ldb.Dn(samdb, domain_dn)
1227 m["msDS-Behavior-Version"] = ldb.MessageElement(
1228 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1229 "msDS-Behavior-Version")
1233 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup")
1234 + ",CN=Partitions,%s" % samdb.get_config_basedn())
1235 m["msDS-Behavior-Version"] = ldb.MessageElement(
1236 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1237 "msDS-Behavior-Version")
1240 except ldb.LdbError as e2:
1241 (enum, emsg) = e2.args
1242 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1245 level_domain = new_level_domain
1246 msgs.append("Domain function level changed!")
1248 if forest_level is not None:
1249 if forest_level == "2003":
1250 new_level_forest = DS_DOMAIN_FUNCTION_2003
1251 elif forest_level == "2008":
1252 new_level_forest = DS_DOMAIN_FUNCTION_2008
1253 elif forest_level == "2008_R2":
1254 new_level_forest = DS_DOMAIN_FUNCTION_2008_R2
1255 elif forest_level == "2012":
1256 new_level_forest = DS_DOMAIN_FUNCTION_2012
1257 elif forest_level == "2012_R2":
1258 new_level_forest = DS_DOMAIN_FUNCTION_2012_R2
1260 if new_level_forest <= level_forest:
1261 raise CommandError("Forest function level can't be smaller than or equal to the actual one!")
1262 if new_level_forest > level_domain:
1263 raise CommandError("Forest function level can't be higher than the domain function level(s). Please raise it/them first!")
1266 m.dn = ldb.Dn(samdb, "CN=Partitions,%s" % samdb.get_config_basedn())
1267 m["msDS-Behavior-Version"] = ldb.MessageElement(
1268 str(new_level_forest), ldb.FLAG_MOD_REPLACE,
1269 "msDS-Behavior-Version")
1271 msgs.append("Forest function level changed!")
1272 msgs.append("All changes applied successfully!")
1273 self.message("\n".join(msgs))
1275 raise CommandError("invalid argument: '%s' (choose from 'show', 'raise')" % subcommand)
1278 class cmd_domain_passwordsettings_show(Command):
1279 """Display current password settings for the domain."""
1281 synopsis = "%prog [options]"
1283 takes_optiongroups = {
1284 "sambaopts": options.SambaOptions,
1285 "versionopts": options.VersionOptions,
1286 "credopts": options.CredentialsOptions,
1290 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1291 metavar="URL", dest="H"),
1294 def run(self, H=None, credopts=None, sambaopts=None, versionopts=None):
1295 lp = sambaopts.get_loadparm()
1296 creds = credopts.get_credentials(lp)
1298 samdb = SamDB(url=H, session_info=system_session(),
1299 credentials=creds, lp=lp)
1301 domain_dn = samdb.domain_dn()
1302 res = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1303 attrs=["pwdProperties", "pwdHistoryLength", "minPwdLength",
1304 "minPwdAge", "maxPwdAge", "lockoutDuration", "lockoutThreshold",
1305 "lockOutObservationWindow"])
1306 assert(len(res) == 1)
1308 pwd_props = int(res[0]["pwdProperties"][0])
1309 pwd_hist_len = int(res[0]["pwdHistoryLength"][0])
1310 cur_min_pwd_len = int(res[0]["minPwdLength"][0])
1312 cur_min_pwd_age = int(abs(int(res[0]["minPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1313 if int(res[0]["maxPwdAge"][0]) == -0x8000000000000000:
1316 cur_max_pwd_age = int(abs(int(res[0]["maxPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1317 cur_account_lockout_threshold = int(res[0]["lockoutThreshold"][0])
1319 if int(res[0]["lockoutDuration"][0]) == -0x8000000000000000:
1320 cur_account_lockout_duration = 0
1322 cur_account_lockout_duration = abs(int(res[0]["lockoutDuration"][0])) / (1e7 * 60)
1323 cur_reset_account_lockout_after = abs(int(res[0]["lockOutObservationWindow"][0])) / (1e7 * 60)
1324 except Exception as e:
1325 raise CommandError("Could not retrieve password properties!", e)
1327 self.message("Password informations for domain '%s'" % domain_dn)
1329 if pwd_props & DOMAIN_PASSWORD_COMPLEX != 0:
1330 self.message("Password complexity: on")
1332 self.message("Password complexity: off")
1333 if pwd_props & DOMAIN_PASSWORD_STORE_CLEARTEXT != 0:
1334 self.message("Store plaintext passwords: on")
1336 self.message("Store plaintext passwords: off")
1337 self.message("Password history length: %d" % pwd_hist_len)
1338 self.message("Minimum password length: %d" % cur_min_pwd_len)
1339 self.message("Minimum password age (days): %d" % cur_min_pwd_age)
1340 self.message("Maximum password age (days): %d" % cur_max_pwd_age)
1341 self.message("Account lockout duration (mins): %d" % cur_account_lockout_duration)
1342 self.message("Account lockout threshold (attempts): %d" % cur_account_lockout_threshold)
1343 self.message("Reset account lockout after (mins): %d" % cur_reset_account_lockout_after)
1346 class cmd_domain_passwordsettings_set(Command):
1347 """Set password settings.
1349 Password complexity, password lockout policy, history length,
1350 minimum password length, the minimum and maximum password age) on
1351 a Samba AD DC server.
1353 Use against a Windows DC is possible, but group policy will override it.
1356 synopsis = "%prog <options> [options]"
1358 takes_optiongroups = {
1359 "sambaopts": options.SambaOptions,
1360 "versionopts": options.VersionOptions,
1361 "credopts": options.CredentialsOptions,
1365 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1366 metavar="URL", dest="H"),
1367 Option("-q", "--quiet", help="Be quiet", action="store_true"), # unused
1368 Option("--complexity", type="choice", choices=["on", "off", "default"],
1369 help="The password complexity (on | off | default). Default is 'on'"),
1370 Option("--store-plaintext", type="choice", choices=["on", "off", "default"],
1371 help="Store plaintext passwords where account have 'store passwords with reversible encryption' set (on | off | default). Default is 'off'"),
1372 Option("--history-length",
1373 help="The password history length (<integer> | default). Default is 24.", type=str),
1374 Option("--min-pwd-length",
1375 help="The minimum password length (<integer> | default). Default is 7.", type=str),
1376 Option("--min-pwd-age",
1377 help="The minimum password age (<integer in days> | default). Default is 1.", type=str),
1378 Option("--max-pwd-age",
1379 help="The maximum password age (<integer in days> | default). Default is 43.", type=str),
1380 Option("--account-lockout-duration",
1381 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),
1382 Option("--account-lockout-threshold",
1383 help="The number of bad password attempts allowed before locking out the account (<integer> | default). Default is 0 (never lock out).", type=str),
1384 Option("--reset-account-lockout-after",
1385 help="After this time is elapsed, the recorded number of attempts restarts from zero (<integer> | default). Default is 30.", type=str),
1388 def run(self, H=None, min_pwd_age=None, max_pwd_age=None,
1389 quiet=False, complexity=None, store_plaintext=None, history_length=None,
1390 min_pwd_length=None, account_lockout_duration=None, account_lockout_threshold=None,
1391 reset_account_lockout_after=None, credopts=None, sambaopts=None,
1393 lp = sambaopts.get_loadparm()
1394 creds = credopts.get_credentials(lp)
1396 samdb = SamDB(url=H, session_info=system_session(),
1397 credentials=creds, lp=lp)
1399 domain_dn = samdb.domain_dn()
1402 m.dn = ldb.Dn(samdb, domain_dn)
1403 pwd_props = int(samdb.get_pwdProperties())
1405 if complexity is not None:
1406 if complexity == "on" or complexity == "default":
1407 pwd_props = pwd_props | DOMAIN_PASSWORD_COMPLEX
1408 msgs.append("Password complexity activated!")
1409 elif complexity == "off":
1410 pwd_props = pwd_props & (~DOMAIN_PASSWORD_COMPLEX)
1411 msgs.append("Password complexity deactivated!")
1413 if store_plaintext is not None:
1414 if store_plaintext == "on" or store_plaintext == "default":
1415 pwd_props = pwd_props | DOMAIN_PASSWORD_STORE_CLEARTEXT
1416 msgs.append("Plaintext password storage for changed passwords activated!")
1417 elif store_plaintext == "off":
1418 pwd_props = pwd_props & (~DOMAIN_PASSWORD_STORE_CLEARTEXT)
1419 msgs.append("Plaintext password storage for changed passwords deactivated!")
1421 if complexity is not None or store_plaintext is not None:
1422 m["pwdProperties"] = ldb.MessageElement(str(pwd_props),
1423 ldb.FLAG_MOD_REPLACE, "pwdProperties")
1425 if history_length is not None:
1426 if history_length == "default":
1429 pwd_hist_len = int(history_length)
1431 if pwd_hist_len < 0 or pwd_hist_len > 24:
1432 raise CommandError("Password history length must be in the range of 0 to 24!")
1434 m["pwdHistoryLength"] = ldb.MessageElement(str(pwd_hist_len),
1435 ldb.FLAG_MOD_REPLACE, "pwdHistoryLength")
1436 msgs.append("Password history length changed!")
1438 if min_pwd_length is not None:
1439 if min_pwd_length == "default":
1442 min_pwd_len = int(min_pwd_length)
1444 if min_pwd_len < 0 or min_pwd_len > 14:
1445 raise CommandError("Minimum password length must be in the range of 0 to 14!")
1447 m["minPwdLength"] = ldb.MessageElement(str(min_pwd_len),
1448 ldb.FLAG_MOD_REPLACE, "minPwdLength")
1449 msgs.append("Minimum password length changed!")
1451 if min_pwd_age is not None:
1452 if min_pwd_age == "default":
1455 min_pwd_age = int(min_pwd_age)
1457 if min_pwd_age < 0 or min_pwd_age > 998:
1458 raise CommandError("Minimum password age must be in the range of 0 to 998!")
1461 min_pwd_age_ticks = -int(min_pwd_age * (24 * 60 * 60 * 1e7))
1463 m["minPwdAge"] = ldb.MessageElement(str(min_pwd_age_ticks),
1464 ldb.FLAG_MOD_REPLACE, "minPwdAge")
1465 msgs.append("Minimum password age changed!")
1467 if max_pwd_age is not None:
1468 if max_pwd_age == "default":
1471 max_pwd_age = int(max_pwd_age)
1473 if max_pwd_age < 0 or max_pwd_age > 999:
1474 raise CommandError("Maximum password age must be in the range of 0 to 999!")
1477 if max_pwd_age == 0:
1478 max_pwd_age_ticks = -0x8000000000000000
1480 max_pwd_age_ticks = -int(max_pwd_age * (24 * 60 * 60 * 1e7))
1482 m["maxPwdAge"] = ldb.MessageElement(str(max_pwd_age_ticks),
1483 ldb.FLAG_MOD_REPLACE, "maxPwdAge")
1484 msgs.append("Maximum password age changed!")
1486 if account_lockout_duration is not None:
1487 if account_lockout_duration == "default":
1488 account_lockout_duration = 30
1490 account_lockout_duration = int(account_lockout_duration)
1492 if account_lockout_duration < 0 or account_lockout_duration > 99999:
1493 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1496 if account_lockout_duration == 0:
1497 account_lockout_duration_ticks = -0x8000000000000000
1499 account_lockout_duration_ticks = -int(account_lockout_duration * (60 * 1e7))
1501 m["lockoutDuration"] = ldb.MessageElement(str(account_lockout_duration_ticks),
1502 ldb.FLAG_MOD_REPLACE, "lockoutDuration")
1503 msgs.append("Account lockout duration changed!")
1505 if account_lockout_threshold is not None:
1506 if account_lockout_threshold == "default":
1507 account_lockout_threshold = 0
1509 account_lockout_threshold = int(account_lockout_threshold)
1511 m["lockoutThreshold"] = ldb.MessageElement(str(account_lockout_threshold),
1512 ldb.FLAG_MOD_REPLACE, "lockoutThreshold")
1513 msgs.append("Account lockout threshold changed!")
1515 if reset_account_lockout_after is not None:
1516 if reset_account_lockout_after == "default":
1517 reset_account_lockout_after = 30
1519 reset_account_lockout_after = int(reset_account_lockout_after)
1521 if reset_account_lockout_after < 0 or reset_account_lockout_after > 99999:
1522 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1525 if reset_account_lockout_after == 0:
1526 reset_account_lockout_after_ticks = -0x8000000000000000
1528 reset_account_lockout_after_ticks = -int(reset_account_lockout_after * (60 * 1e7))
1530 m["lockOutObservationWindow"] = ldb.MessageElement(str(reset_account_lockout_after_ticks),
1531 ldb.FLAG_MOD_REPLACE, "lockOutObservationWindow")
1532 msgs.append("Duration to reset account lockout after changed!")
1534 if max_pwd_age and max_pwd_age > 0 and min_pwd_age >= max_pwd_age:
1535 raise CommandError("Maximum password age (%d) must be greater than minimum password age (%d)!" % (max_pwd_age, min_pwd_age))
1538 raise CommandError("You must specify at least one option to set. Try --help")
1540 msgs.append("All changes applied successfully!")
1541 self.message("\n".join(msgs))
1544 class cmd_domain_passwordsettings(SuperCommand):
1545 """Manage password policy settings."""
1548 subcommands["pso"] = cmd_domain_passwordsettings_pso()
1549 subcommands["show"] = cmd_domain_passwordsettings_show()
1550 subcommands["set"] = cmd_domain_passwordsettings_set()
1553 class cmd_domain_classicupgrade(Command):
1554 """Upgrade from Samba classic (NT4-like) database to Samba AD DC database.
1556 Specify either a directory with all Samba classic DC databases and state files (with --dbdir) or
1557 the testparm utility from your classic installation (with --testparm).
1560 synopsis = "%prog [options] <classic_smb_conf>"
1562 takes_optiongroups = {
1563 "sambaopts": options.SambaOptions,
1564 "versionopts": options.VersionOptions
1568 Option("--dbdir", type="string", metavar="DIR",
1569 help="Path to samba classic DC database directory"),
1570 Option("--testparm", type="string", metavar="PATH",
1571 help="Path to samba classic DC testparm utility from the previous installation. This allows the default paths of the previous installation to be followed"),
1572 Option("--targetdir", type="string", metavar="DIR",
1573 help="Path prefix where the new Samba 4.0 AD domain should be initialised"),
1574 Option("-q", "--quiet", help="Be quiet", action="store_true"),
1575 Option("-v", "--verbose", help="Be verbose", action="store_true"),
1576 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
1577 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
1578 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
1579 "BIND9_FLATFILE uses bind9 text database to store zone information, "
1580 "BIND9_DLZ uses samba4 AD to store zone information, "
1581 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
1582 default="SAMBA_INTERNAL")
1586 Option("--use-xattrs", type="choice", choices=["yes", "no", "auto"],
1587 metavar="[yes|no|auto]",
1588 help="Define if we should use the native fs capabilities or a tdb file for "
1589 "storing attributes likes ntacl when --use-ntvfs is set. "
1590 "auto tries to make an inteligent guess based on the user rights and system capabilities",
1593 if samba.is_ntvfs_fileserver_built():
1594 takes_options.extend(common_ntvfs_options)
1595 takes_options.extend(ntvfs_options)
1597 takes_args = ["smbconf"]
1599 def run(self, smbconf=None, targetdir=None, dbdir=None, testparm=None,
1600 quiet=False, verbose=False, use_xattrs="auto", sambaopts=None, versionopts=None,
1601 dns_backend=None, use_ntvfs=False):
1603 if not os.path.exists(smbconf):
1604 raise CommandError("File %s does not exist" % smbconf)
1606 if testparm and not os.path.exists(testparm):
1607 raise CommandError("Testparm utility %s does not exist" % testparm)
1609 if dbdir and not os.path.exists(dbdir):
1610 raise CommandError("Directory %s does not exist" % dbdir)
1612 if not dbdir and not testparm:
1613 raise CommandError("Please specify either dbdir or testparm")
1615 logger = self.get_logger()
1617 logger.setLevel(logging.DEBUG)
1619 logger.setLevel(logging.WARNING)
1621 logger.setLevel(logging.INFO)
1623 if dbdir and testparm:
1624 logger.warning("both dbdir and testparm specified, ignoring dbdir.")
1627 lp = sambaopts.get_loadparm()
1629 s3conf = s3param.get_context()
1632 s3conf.set("realm", sambaopts.realm)
1634 if targetdir is not None:
1635 if not os.path.isdir(targetdir):
1639 if use_xattrs == "yes":
1641 elif use_xattrs == "auto" and use_ntvfs == False:
1643 elif use_ntvfs == False:
1644 raise CommandError("--use-xattrs=no requires --use-ntvfs (not supported for production use). "
1645 "Please re-run with --use-xattrs omitted.")
1646 elif use_xattrs == "auto" and not s3conf.get("posix:eadb"):
1648 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
1650 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
1653 samba.ntacls.setntacl(lp, tmpfile.name,
1654 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
1657 # FIXME: Don't catch all exceptions here
1658 logger.info("You are not root or your system does not support xattr, using tdb backend for attributes. "
1659 "If you intend to use this provision in production, rerun the script as root on a system supporting xattrs.")
1663 # Set correct default values from dbdir or testparm
1666 paths["state directory"] = dbdir
1667 paths["private dir"] = dbdir
1668 paths["lock directory"] = dbdir
1669 paths["smb passwd file"] = dbdir + "/smbpasswd"
1671 paths["state directory"] = get_testparm_var(testparm, smbconf, "state directory")
1672 paths["private dir"] = get_testparm_var(testparm, smbconf, "private dir")
1673 paths["smb passwd file"] = get_testparm_var(testparm, smbconf, "smb passwd file")
1674 paths["lock directory"] = get_testparm_var(testparm, smbconf, "lock directory")
1675 # "testparm" from Samba 3 < 3.4.x is not aware of the parameter
1676 # "state directory", instead make use of "lock directory"
1677 if len(paths["state directory"]) == 0:
1678 paths["state directory"] = paths["lock directory"]
1681 s3conf.set(p, paths[p])
1683 # load smb.conf parameters
1684 logger.info("Reading smb.conf")
1685 s3conf.load(smbconf)
1686 samba3 = Samba3(smbconf, s3conf)
1688 logger.info("Provisioning")
1689 upgrade_from_samba3(samba3, logger, targetdir, session_info=system_session(),
1690 useeadb=eadb, dns_backend=dns_backend, use_ntvfs=use_ntvfs)
1693 class cmd_domain_samba3upgrade(cmd_domain_classicupgrade):
1694 __doc__ = cmd_domain_classicupgrade.__doc__
1696 # This command is present for backwards compatibility only,
1697 # and should not be shown.
1702 class LocalDCCredentialsOptions(options.CredentialsOptions):
1703 def __init__(self, parser):
1704 options.CredentialsOptions.__init__(self, parser, special_name="local-dc")
1707 class DomainTrustCommand(Command):
1708 """List domain trusts."""
1711 Command.__init__(self)
1712 self.local_lp = None
1714 self.local_server = None
1715 self.local_binding_string = None
1716 self.local_creds = None
1718 self.remote_server = None
1719 self.remote_binding_string = None
1720 self.remote_creds = None
1722 def _uint32(self, v):
1723 return ctypes.c_uint32(v).value
1725 def check_runtime_error(self, runtime, val):
1729 err32 = self._uint32(runtime.args[0])
1735 class LocalRuntimeError(CommandError):
1736 def __init__(exception_self, self, runtime, message):
1737 err32 = self._uint32(runtime.args[0])
1738 errstr = runtime.args[1]
1739 msg = "LOCAL_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1740 self.local_server, message, err32, errstr)
1741 CommandError.__init__(exception_self, msg)
1743 class RemoteRuntimeError(CommandError):
1744 def __init__(exception_self, self, runtime, message):
1745 err32 = self._uint32(runtime.args[0])
1746 errstr = runtime.args[1]
1747 msg = "REMOTE_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1748 self.remote_server, message, err32, errstr)
1749 CommandError.__init__(exception_self, msg)
1751 class LocalLdbError(CommandError):
1752 def __init__(exception_self, self, ldb_error, message):
1753 errval = ldb_error.args[0]
1754 errstr = ldb_error.args[1]
1755 msg = "LOCAL_DC[%s]: %s - ERROR(%d) - %s" % (
1756 self.local_server, message, errval, errstr)
1757 CommandError.__init__(exception_self, msg)
1759 def setup_local_server(self, sambaopts, localdcopts):
1760 if self.local_server is not None:
1761 return self.local_server
1763 lp = sambaopts.get_loadparm()
1765 local_server = localdcopts.ipaddress
1766 if local_server is None:
1767 server_role = lp.server_role()
1768 if server_role != "ROLE_ACTIVE_DIRECTORY_DC":
1769 raise CommandError("Invalid server_role %s" % (server_role))
1770 local_server = lp.get('netbios name')
1771 local_transport = "ncalrpc"
1772 local_binding_options = ""
1773 local_binding_options += ",auth_type=ncalrpc_as_system"
1774 local_ldap_url = None
1777 local_transport = "ncacn_np"
1778 local_binding_options = ""
1779 local_ldap_url = "ldap://%s" % local_server
1780 local_creds = localdcopts.get_credentials(lp)
1784 self.local_server = local_server
1785 self.local_binding_string = "%s:%s[%s]" % (local_transport, local_server, local_binding_options)
1786 self.local_ldap_url = local_ldap_url
1787 self.local_creds = local_creds
1788 return self.local_server
1790 def new_local_lsa_connection(self):
1791 return lsa.lsarpc(self.local_binding_string, self.local_lp, self.local_creds)
1793 def new_local_netlogon_connection(self):
1794 return netlogon.netlogon(self.local_binding_string, self.local_lp, self.local_creds)
1796 def new_local_ldap_connection(self):
1797 return SamDB(url=self.local_ldap_url,
1798 session_info=system_session(),
1799 credentials=self.local_creds,
1802 def setup_remote_server(self, credopts, domain,
1804 require_writable=True):
1807 assert require_writable
1809 if self.remote_server is not None:
1810 return self.remote_server
1812 self.remote_server = "__unknown__remote_server__.%s" % domain
1813 assert self.local_server is not None
1815 remote_creds = credopts.get_credentials(self.local_lp)
1816 remote_server = credopts.ipaddress
1817 remote_binding_options = ""
1819 # TODO: we should also support NT4 domains
1820 # we could use local_netlogon.netr_DsRGetDCNameEx2() with the remote domain name
1821 # and delegate NBT or CLDAP to the local netlogon server
1823 remote_net = Net(remote_creds, self.local_lp, server=remote_server)
1824 remote_flags = nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS
1825 if require_writable:
1826 remote_flags |= nbt.NBT_SERVER_WRITABLE
1828 remote_flags |= nbt.NBT_SERVER_PDC
1829 remote_info = remote_net.finddc(flags=remote_flags, domain=domain, address=remote_server)
1830 except NTSTATUSError as error:
1831 raise CommandError("Failed to find a writeable DC for domain '%s': %s" %
1834 raise CommandError("Failed to find a writeable DC for domain '%s'" % domain)
1836 nbt.NBT_SERVER_PDC: "PDC",
1837 nbt.NBT_SERVER_GC: "GC",
1838 nbt.NBT_SERVER_LDAP: "LDAP",
1839 nbt.NBT_SERVER_DS: "DS",
1840 nbt.NBT_SERVER_KDC: "KDC",
1841 nbt.NBT_SERVER_TIMESERV: "TIMESERV",
1842 nbt.NBT_SERVER_CLOSEST: "CLOSEST",
1843 nbt.NBT_SERVER_WRITABLE: "WRITABLE",
1844 nbt.NBT_SERVER_GOOD_TIMESERV: "GOOD_TIMESERV",
1845 nbt.NBT_SERVER_NDNC: "NDNC",
1846 nbt.NBT_SERVER_SELECT_SECRET_DOMAIN_6: "SELECT_SECRET_DOMAIN_6",
1847 nbt.NBT_SERVER_FULL_SECRET_DOMAIN_6: "FULL_SECRET_DOMAIN_6",
1848 nbt.NBT_SERVER_ADS_WEB_SERVICE: "ADS_WEB_SERVICE",
1849 nbt.NBT_SERVER_DS_8: "DS_8",
1850 nbt.NBT_SERVER_HAS_DNS_NAME: "HAS_DNS_NAME",
1851 nbt.NBT_SERVER_IS_DEFAULT_NC: "IS_DEFAULT_NC",
1852 nbt.NBT_SERVER_FOREST_ROOT: "FOREST_ROOT",
1854 server_type_string = self.generic_bitmap_to_string(flag_map,
1855 remote_info.server_type, names_only=True)
1856 self.outf.write("RemoteDC Netbios[%s] DNS[%s] ServerType[%s]\n" % (
1857 remote_info.pdc_name,
1858 remote_info.pdc_dns_name,
1859 server_type_string))
1861 self.remote_server = remote_info.pdc_dns_name
1862 self.remote_binding_string = "ncacn_np:%s[%s]" % (self.remote_server, remote_binding_options)
1863 self.remote_creds = remote_creds
1864 return self.remote_server
1866 def new_remote_lsa_connection(self):
1867 return lsa.lsarpc(self.remote_binding_string, self.local_lp, self.remote_creds)
1869 def new_remote_netlogon_connection(self):
1870 return netlogon.netlogon(self.remote_binding_string, self.local_lp, self.remote_creds)
1872 def get_lsa_info(self, conn, policy_access):
1873 objectAttr = lsa.ObjectAttribute()
1874 objectAttr.sec_qos = lsa.QosInfo()
1876 policy = conn.OpenPolicy2(''.decode('utf-8'),
1877 objectAttr, policy_access)
1879 info = conn.QueryInfoPolicy2(policy, lsa.LSA_POLICY_INFO_DNS)
1881 return (policy, info)
1883 def get_netlogon_dc_unc(self, conn, server, domain):
1885 info = conn.netr_DsRGetDCNameEx2(server,
1886 None, 0, None, None, None,
1887 netlogon.DS_RETURN_DNS_NAME)
1889 except RuntimeError:
1890 return conn.netr_GetDcName(server, domain)
1892 def get_netlogon_dc_info(self, conn, server):
1893 info = conn.netr_DsRGetDCNameEx2(server,
1894 None, 0, None, None, None,
1895 netlogon.DS_RETURN_DNS_NAME)
1898 def netr_DomainTrust_to_name(self, t):
1899 if t.trust_type == lsa.LSA_TRUST_TYPE_DOWNLEVEL:
1900 return t.netbios_name
1904 def netr_DomainTrust_to_type(self, a, t):
1906 primary_parent = None
1908 if _t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
1910 if not _t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1911 primary_parent = a[_t.parent_index]
1914 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1915 if t is primary_parent:
1918 if t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1921 parent = a[t.parent_index]
1922 if parent is primary:
1927 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1932 def netr_DomainTrust_to_transitive(self, t):
1933 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1936 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE:
1939 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1944 def netr_DomainTrust_to_direction(self, t):
1945 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND and \
1946 t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1949 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND:
1952 if t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1957 def generic_enum_to_string(self, e_dict, v, names_only=False):
1961 v32 = self._uint32(v)
1962 w = "__unknown__%08X__" % v32
1964 r = "0x%x (%s)" % (v, w)
1967 def generic_bitmap_to_string(self, b_dict, v, names_only=False):
1972 for b in sorted(b_dict.keys()):
1979 c32 = self._uint32(c)
1980 s += ["__unknown_%08X__" % c32]
1985 r = "0x%x (%s)" % (v, w)
1988 def trustType_string(self, v):
1990 lsa.LSA_TRUST_TYPE_DOWNLEVEL: "DOWNLEVEL",
1991 lsa.LSA_TRUST_TYPE_UPLEVEL: "UPLEVEL",
1992 lsa.LSA_TRUST_TYPE_MIT: "MIT",
1993 lsa.LSA_TRUST_TYPE_DCE: "DCE",
1995 return self.generic_enum_to_string(types, v)
1997 def trustDirection_string(self, v):
1999 lsa.LSA_TRUST_DIRECTION_INBOUND |
2000 lsa.LSA_TRUST_DIRECTION_OUTBOUND: "BOTH",
2001 lsa.LSA_TRUST_DIRECTION_INBOUND: "INBOUND",
2002 lsa.LSA_TRUST_DIRECTION_OUTBOUND: "OUTBOUND",
2004 return self.generic_enum_to_string(directions, v)
2006 def trustAttributes_string(self, v):
2008 lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE: "NON_TRANSITIVE",
2009 lsa.LSA_TRUST_ATTRIBUTE_UPLEVEL_ONLY: "UPLEVEL_ONLY",
2010 lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN: "QUARANTINED_DOMAIN",
2011 lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE: "FOREST_TRANSITIVE",
2012 lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION: "CROSS_ORGANIZATION",
2013 lsa.LSA_TRUST_ATTRIBUTE_WITHIN_FOREST: "WITHIN_FOREST",
2014 lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL: "TREAT_AS_EXTERNAL",
2015 lsa.LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION: "USES_RC4_ENCRYPTION",
2017 return self.generic_bitmap_to_string(attributes, v)
2019 def kerb_EncTypes_string(self, v):
2021 security.KERB_ENCTYPE_DES_CBC_CRC: "DES_CBC_CRC",
2022 security.KERB_ENCTYPE_DES_CBC_MD5: "DES_CBC_MD5",
2023 security.KERB_ENCTYPE_RC4_HMAC_MD5: "RC4_HMAC_MD5",
2024 security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96: "AES128_CTS_HMAC_SHA1_96",
2025 security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96: "AES256_CTS_HMAC_SHA1_96",
2026 security.KERB_ENCTYPE_FAST_SUPPORTED: "FAST_SUPPORTED",
2027 security.KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED: "COMPOUND_IDENTITY_SUPPORTED",
2028 security.KERB_ENCTYPE_CLAIMS_SUPPORTED: "CLAIMS_SUPPORTED",
2029 security.KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED: "RESOURCE_SID_COMPRESSION_DISABLED",
2031 return self.generic_bitmap_to_string(enctypes, v)
2033 def entry_tln_status(self, e_flags, ):
2035 return "Status[Enabled]"
2038 lsa.LSA_TLN_DISABLED_NEW: "Disabled-New",
2039 lsa.LSA_TLN_DISABLED_ADMIN: "Disabled",
2040 lsa.LSA_TLN_DISABLED_CONFLICT: "Disabled-Conflicting",
2042 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
2044 def entry_dom_status(self, e_flags):
2046 return "Status[Enabled]"
2049 lsa.LSA_SID_DISABLED_ADMIN: "Disabled-SID",
2050 lsa.LSA_SID_DISABLED_CONFLICT: "Disabled-SID-Conflicting",
2051 lsa.LSA_NB_DISABLED_ADMIN: "Disabled-NB",
2052 lsa.LSA_NB_DISABLED_CONFLICT: "Disabled-NB-Conflicting",
2054 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
2056 def write_forest_trust_info(self, fti, tln=None, collisions=None):
2058 tln_string = " TDO[%s]" % tln
2062 self.outf.write("Namespaces[%d]%s:\n" % (
2063 len(fti.entries), tln_string))
2065 for i, e in enumerate(fti.entries):
2068 collision_string = ""
2070 if collisions is not None:
2071 for c in collisions.entries:
2075 collision_string = " Collision[%s]" % (c.name.string)
2077 d = e.forest_trust_data
2078 if e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
2079 self.outf.write("TLN: %-32s DNS[*.%s]%s\n" % (
2080 self.entry_tln_status(flags),
2081 d.string, collision_string))
2082 elif e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
2083 self.outf.write("TLN_EX: %-29s DNS[*.%s]\n" % (
2085 elif e.type == lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
2086 self.outf.write("DOM: %-32s DNS[%s] Netbios[%s] SID[%s]%s\n" % (
2087 self.entry_dom_status(flags),
2088 d.dns_domain_name.string,
2089 d.netbios_domain_name.string,
2090 d.domain_sid, collision_string))
2094 class cmd_domain_trust_list(DomainTrustCommand):
2095 """List domain trusts."""
2097 synopsis = "%prog [options]"
2099 takes_optiongroups = {
2100 "sambaopts": options.SambaOptions,
2101 "versionopts": options.VersionOptions,
2102 "localdcopts": LocalDCCredentialsOptions,
2108 def run(self, sambaopts=None, versionopts=None, localdcopts=None):
2110 local_server = self.setup_local_server(sambaopts, localdcopts)
2112 local_netlogon = self.new_local_netlogon_connection()
2113 except RuntimeError as error:
2114 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2117 local_netlogon_trusts = \
2118 local_netlogon.netr_DsrEnumerateDomainTrusts(local_server,
2119 netlogon.NETR_TRUST_FLAG_IN_FOREST |
2120 netlogon.NETR_TRUST_FLAG_OUTBOUND |
2121 netlogon.NETR_TRUST_FLAG_INBOUND)
2122 except RuntimeError as error:
2123 if self.check_runtime_error(error, werror.WERR_RPC_S_PROCNUM_OUT_OF_RANGE):
2124 # TODO: we could implement a fallback to lsa.EnumTrustDom()
2125 raise CommandError("LOCAL_DC[%s]: netr_DsrEnumerateDomainTrusts not supported." % (
2127 raise self.LocalRuntimeError(self, error, "netr_DsrEnumerateDomainTrusts failed")
2129 a = local_netlogon_trusts.array
2131 if t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
2133 self.outf.write("%-14s %-15s %-19s %s\n" % (
2134 "Type[%s]" % self.netr_DomainTrust_to_type(a, t),
2135 "Transitive[%s]" % self.netr_DomainTrust_to_transitive(t),
2136 "Direction[%s]" % self.netr_DomainTrust_to_direction(t),
2137 "Name[%s]" % self.netr_DomainTrust_to_name(t)))
2141 class cmd_domain_trust_show(DomainTrustCommand):
2142 """Show trusted domain details."""
2144 synopsis = "%prog NAME [options]"
2146 takes_optiongroups = {
2147 "sambaopts": options.SambaOptions,
2148 "versionopts": options.VersionOptions,
2149 "localdcopts": LocalDCCredentialsOptions,
2155 takes_args = ["domain"]
2157 def run(self, domain, sambaopts=None, versionopts=None, localdcopts=None):
2159 local_server = self.setup_local_server(sambaopts, localdcopts)
2161 local_lsa = self.new_local_lsa_connection()
2162 except RuntimeError as error:
2163 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2166 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2167 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2168 except RuntimeError as error:
2169 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2171 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2172 local_lsa_info.name.string,
2173 local_lsa_info.dns_domain.string,
2174 local_lsa_info.sid))
2176 lsaString = lsa.String()
2177 lsaString.string = domain
2180 local_lsa.QueryTrustedDomainInfoByName(local_policy,
2182 lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2183 local_tdo_info = local_tdo_full.info_ex
2184 local_tdo_posix = local_tdo_full.posix_offset
2185 except NTSTATUSError as error:
2186 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2187 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2189 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(FULL_INFO) failed")
2192 local_tdo_enctypes = \
2193 local_lsa.QueryTrustedDomainInfoByName(local_policy,
2195 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES)
2196 except NTSTATUSError as error:
2197 if self.check_runtime_error(error, ntstatus.NT_STATUS_INVALID_PARAMETER):
2199 if self.check_runtime_error(error, ntstatus.NT_STATUS_INVALID_INFO_CLASS):
2202 if error is not None:
2203 raise self.LocalRuntimeError(self, error,
2204 "QueryTrustedDomainInfoByName(SUPPORTED_ENCRYPTION_TYPES) failed")
2206 local_tdo_enctypes = lsa.TrustDomainInfoSupportedEncTypes()
2207 local_tdo_enctypes.enc_types = 0
2210 local_tdo_forest = None
2211 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2212 local_tdo_forest = \
2213 local_lsa.lsaRQueryForestTrustInformation(local_policy,
2215 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
2216 except RuntimeError as error:
2217 if self.check_runtime_error(error, ntstatus.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
2219 if self.check_runtime_error(error, ntstatus.NT_STATUS_NOT_FOUND):
2221 if error is not None:
2222 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation failed")
2224 local_tdo_forest = lsa.ForestTrustInformation()
2225 local_tdo_forest.count = 0
2226 local_tdo_forest.entries = []
2228 self.outf.write("TrustedDomain:\n\n");
2229 self.outf.write("NetbiosName: %s\n" % local_tdo_info.netbios_name.string)
2230 if local_tdo_info.netbios_name.string != local_tdo_info.domain_name.string:
2231 self.outf.write("DnsName: %s\n" % local_tdo_info.domain_name.string)
2232 self.outf.write("SID: %s\n" % local_tdo_info.sid)
2233 self.outf.write("Type: %s\n" % self.trustType_string(local_tdo_info.trust_type))
2234 self.outf.write("Direction: %s\n" % self.trustDirection_string(local_tdo_info.trust_direction))
2235 self.outf.write("Attributes: %s\n" % self.trustAttributes_string(local_tdo_info.trust_attributes))
2236 posix_offset_u32 = ctypes.c_uint32(local_tdo_posix.posix_offset).value
2237 posix_offset_i32 = ctypes.c_int32(local_tdo_posix.posix_offset).value
2238 self.outf.write("PosixOffset: 0x%08X (%d)\n" % (posix_offset_u32, posix_offset_i32))
2239 self.outf.write("kerb_EncTypes: %s\n" % self.kerb_EncTypes_string(local_tdo_enctypes.enc_types))
2241 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2242 self.write_forest_trust_info(local_tdo_forest,
2243 tln=local_tdo_info.domain_name.string)
2248 class cmd_domain_trust_create(DomainTrustCommand):
2249 """Create a domain or forest trust."""
2251 synopsis = "%prog DOMAIN [options]"
2253 takes_optiongroups = {
2254 "sambaopts": options.SambaOptions,
2255 "versionopts": options.VersionOptions,
2256 "credopts": options.CredentialsOptions,
2257 "localdcopts": LocalDCCredentialsOptions,
2261 Option("--type", type="choice", metavar="TYPE",
2262 choices=["external", "forest"],
2263 help="The type of the trust: 'external' or 'forest'.",
2265 default="external"),
2266 Option("--direction", type="choice", metavar="DIRECTION",
2267 choices=["incoming", "outgoing", "both"],
2268 help="The trust direction: 'incoming', 'outgoing' or 'both'.",
2269 dest='trust_direction',
2271 Option("--create-location", type="choice", metavar="LOCATION",
2272 choices=["local", "both"],
2273 help="Where to create the trusted domain object: 'local' or 'both'.",
2274 dest='create_location',
2276 Option("--cross-organisation", action="store_true",
2277 help="The related domains does not belong to the same organisation.",
2278 dest='cross_organisation',
2280 Option("--quarantined", type="choice", metavar="yes|no",
2281 choices=["yes", "no", None],
2282 help="Special SID filtering rules are applied to the trust. "
2283 "With --type=external the default is yes. "
2284 "With --type=forest the default is no.",
2285 dest='quarantined_arg',
2287 Option("--not-transitive", action="store_true",
2288 help="The forest trust is not transitive.",
2289 dest='not_transitive',
2291 Option("--treat-as-external", action="store_true",
2292 help="The treat the forest trust as external.",
2293 dest='treat_as_external',
2295 Option("--no-aes-keys", action="store_false",
2296 help="The trust uses aes kerberos keys.",
2297 dest='use_aes_keys',
2299 Option("--skip-validation", action="store_false",
2300 help="Skip validation of the trust.",
2305 takes_args = ["domain"]
2307 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2308 trust_type=None, trust_direction=None, create_location=None,
2309 cross_organisation=False, quarantined_arg=None,
2310 not_transitive=False, treat_as_external=False,
2311 use_aes_keys=False, validate=True):
2313 lsaString = lsa.String()
2316 if quarantined_arg is None:
2317 if trust_type == 'external':
2319 elif quarantined_arg == 'yes':
2322 if trust_type != 'forest':
2324 raise CommandError("--not-transitive requires --type=forest")
2325 if treat_as_external:
2326 raise CommandError("--treat-as-external requires --type=forest")
2330 enc_types = lsa.TrustDomainInfoSupportedEncTypes()
2331 enc_types.enc_types = security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96
2332 enc_types.enc_types |= security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96
2334 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2335 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2336 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2338 local_trust_info = lsa.TrustDomainInfoInfoEx()
2339 local_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2340 local_trust_info.trust_direction = 0
2341 if trust_direction == "both":
2342 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2343 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2344 elif trust_direction == "incoming":
2345 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2346 elif trust_direction == "outgoing":
2347 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2348 local_trust_info.trust_attributes = 0
2349 if cross_organisation:
2350 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2352 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2353 if trust_type == "forest":
2354 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2356 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2357 if treat_as_external:
2358 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2360 def get_password(name):
2363 if password is not None and password is not '':
2365 password = getpass("New %s Password: " % name)
2366 passwordverify = getpass("Retype %s Password: " % name)
2367 if not password == passwordverify:
2369 self.outf.write("Sorry, passwords do not match.\n")
2371 incoming_secret = None
2372 outgoing_secret = None
2373 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2374 if create_location == "local":
2375 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2376 incoming_password = get_password("Incoming Trust")
2377 incoming_secret = string_to_byte_array(incoming_password.encode('utf-16-le'))
2378 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2379 outgoing_password = get_password("Outgoing Trust")
2380 outgoing_secret = string_to_byte_array(outgoing_password.encode('utf-16-le'))
2382 remote_trust_info = None
2384 # We use 240 random bytes.
2385 # Windows uses 28 or 240 random bytes. I guess it's
2386 # based on the trust type external vs. forest.
2388 # The initial trust password can be up to 512 bytes
2389 # while the versioned passwords used for periodic updates
2390 # can only be up to 498 bytes, as netr_ServerPasswordSet2()
2391 # needs to pass the NL_PASSWORD_VERSION structure within the
2392 # 512 bytes and a 2 bytes confounder is required.
2394 def random_trust_secret(length):
2395 pw = samba.generate_random_machine_password(length // 2, length // 2)
2396 return string_to_byte_array(pw.encode('utf-16-le'))
2398 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2399 incoming_secret = random_trust_secret(240)
2400 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2401 outgoing_secret = random_trust_secret(240)
2403 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2404 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2406 remote_trust_info = lsa.TrustDomainInfoInfoEx()
2407 remote_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2408 remote_trust_info.trust_direction = 0
2409 if trust_direction == "both":
2410 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2411 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2412 elif trust_direction == "incoming":
2413 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2414 elif trust_direction == "outgoing":
2415 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2416 remote_trust_info.trust_attributes = 0
2417 if cross_organisation:
2418 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2420 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2421 if trust_type == "forest":
2422 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2424 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2425 if treat_as_external:
2426 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2428 local_server = self.setup_local_server(sambaopts, localdcopts)
2430 local_lsa = self.new_local_lsa_connection()
2431 except RuntimeError as error:
2432 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2435 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2436 except RuntimeError as error:
2437 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2439 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2440 local_lsa_info.name.string,
2441 local_lsa_info.dns_domain.string,
2442 local_lsa_info.sid))
2445 remote_server = self.setup_remote_server(credopts, domain)
2446 except RuntimeError as error:
2447 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2450 remote_lsa = self.new_remote_lsa_connection()
2451 except RuntimeError as error:
2452 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2455 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2456 except RuntimeError as error:
2457 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2459 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2460 remote_lsa_info.name.string,
2461 remote_lsa_info.dns_domain.string,
2462 remote_lsa_info.sid))
2464 local_trust_info.domain_name.string = remote_lsa_info.dns_domain.string
2465 local_trust_info.netbios_name.string = remote_lsa_info.name.string
2466 local_trust_info.sid = remote_lsa_info.sid
2468 if remote_trust_info:
2469 remote_trust_info.domain_name.string = local_lsa_info.dns_domain.string
2470 remote_trust_info.netbios_name.string = local_lsa_info.name.string
2471 remote_trust_info.sid = local_lsa_info.sid
2474 lsaString.string = local_trust_info.domain_name.string
2475 local_old_netbios = \
2476 local_lsa.QueryTrustedDomainInfoByName(local_policy,
2478 lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2479 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2480 except NTSTATUSError as error:
2481 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2482 raise self.LocalRuntimeError(self, error,
2483 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2487 lsaString.string = local_trust_info.netbios_name.string
2489 local_lsa.QueryTrustedDomainInfoByName(local_policy,
2491 lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2492 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2493 except NTSTATUSError as error:
2494 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2495 raise self.LocalRuntimeError(self, error,
2496 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2499 if remote_trust_info:
2501 lsaString.string = remote_trust_info.domain_name.string
2502 remote_old_netbios = \
2503 remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2505 lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2506 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2507 except NTSTATUSError as error:
2508 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2509 raise self.RemoteRuntimeError(self, error,
2510 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2514 lsaString.string = remote_trust_info.netbios_name.string
2516 remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2518 lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2519 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2520 except NTSTATUSError as error:
2521 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2522 raise self.RemoteRuntimeError(self, error,
2523 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2527 local_netlogon = self.new_local_netlogon_connection()
2528 except RuntimeError as error:
2529 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2532 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
2533 except RuntimeError as error:
2534 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
2536 if remote_trust_info:
2538 remote_netlogon = self.new_remote_netlogon_connection()
2539 except RuntimeError as error:
2540 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2543 remote_netlogon_dc_unc = self.get_netlogon_dc_unc(remote_netlogon,
2544 remote_server, domain)
2545 except RuntimeError as error:
2546 raise self.RemoteRuntimeError(self, error, "failed to get netlogon dc info")
2548 def generate_AuthInOutBlob(secret, update_time):
2550 blob = drsblobs.trustAuthInOutBlob()
2555 clear = drsblobs.AuthInfoClear()
2556 clear.size = len(secret)
2557 clear.password = secret
2559 info = drsblobs.AuthenticationInformation()
2560 info.LastUpdateTime = samba.unix2nttime(update_time)
2561 info.AuthType = lsa.TRUST_AUTH_TYPE_CLEAR
2562 info.AuthInfo = clear
2564 array = drsblobs.AuthenticationInformationArray()
2566 array.array = [info]
2568 blob = drsblobs.trustAuthInOutBlob()
2570 blob.current = array
2574 def generate_AuthInfoInternal(session_key, incoming=None, outgoing=None):
2575 confounder = [0] * 512
2576 for i in range(len(confounder)):
2577 confounder[i] = random.randint(0, 255)
2579 trustpass = drsblobs.trustDomainPasswords()
2581 trustpass.confounder = confounder
2582 trustpass.outgoing = outgoing
2583 trustpass.incoming = incoming
2585 trustpass_blob = ndr_pack(trustpass)
2587 encrypted_trustpass = arcfour_encrypt(session_key, trustpass_blob)
2589 auth_blob = lsa.DATA_BUF2()
2590 auth_blob.size = len(encrypted_trustpass)
2591 auth_blob.data = string_to_byte_array(encrypted_trustpass)
2593 auth_info = lsa.TrustDomainInfoAuthInfoInternal()
2594 auth_info.auth_blob = auth_blob
2598 update_time = samba.current_unix_time()
2599 incoming_blob = generate_AuthInOutBlob(incoming_secret, update_time)
2600 outgoing_blob = generate_AuthInOutBlob(outgoing_secret, update_time)
2602 local_tdo_handle = None
2603 remote_tdo_handle = None
2605 local_auth_info = generate_AuthInfoInternal(local_lsa.session_key,
2606 incoming=incoming_blob,
2607 outgoing=outgoing_blob)
2608 if remote_trust_info:
2609 remote_auth_info = generate_AuthInfoInternal(remote_lsa.session_key,
2610 incoming=outgoing_blob,
2611 outgoing=incoming_blob)
2614 if remote_trust_info:
2615 self.outf.write("Creating remote TDO.\n")
2616 current_request = {"location": "remote", "name": "CreateTrustedDomainEx2"}
2617 remote_tdo_handle = \
2618 remote_lsa.CreateTrustedDomainEx2(remote_policy,
2621 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2622 self.outf.write("Remote TDO created.\n")
2624 self.outf.write("Setting supported encryption types on remote TDO.\n")
2625 current_request = {"location": "remote", "name": "SetInformationTrustedDomain"}
2626 remote_lsa.SetInformationTrustedDomain(remote_tdo_handle,
2627 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2630 self.outf.write("Creating local TDO.\n")
2631 current_request = {"location": "local", "name": "CreateTrustedDomainEx2"}
2632 local_tdo_handle = local_lsa.CreateTrustedDomainEx2(local_policy,
2635 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2636 self.outf.write("Local TDO created\n")
2638 self.outf.write("Setting supported encryption types on local TDO.\n")
2639 current_request = {"location": "local", "name": "SetInformationTrustedDomain"}
2640 local_lsa.SetInformationTrustedDomain(local_tdo_handle,
2641 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2643 except RuntimeError as error:
2644 self.outf.write("Error: %s failed %sly - cleaning up\n" % (
2645 current_request['name'], current_request['location']))
2646 if remote_tdo_handle:
2647 self.outf.write("Deleting remote TDO.\n")
2648 remote_lsa.DeleteObject(remote_tdo_handle)
2649 remote_tdo_handle = None
2650 if local_tdo_handle:
2651 self.outf.write("Deleting local TDO.\n")
2652 local_lsa.DeleteObject(local_tdo_handle)
2653 local_tdo_handle = None
2654 if current_request['location'] is "remote":
2655 raise self.RemoteRuntimeError(self, error, "%s" % (
2656 current_request['name']))
2657 raise self.LocalRuntimeError(self, error, "%s" % (
2658 current_request['name']))
2661 if local_trust_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2662 self.outf.write("Setup local forest trust information...\n")
2664 # get all information about the remote trust
2665 # this triggers netr_GetForestTrustInformation to the remote domain
2666 # and lsaRSetForestTrustInformation() locally, but new top level
2667 # names are disabled by default.
2668 local_forest_info = \
2669 local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
2670 remote_lsa_info.dns_domain.string,
2671 netlogon.DS_GFTI_UPDATE_TDO)
2672 except RuntimeError as error:
2673 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2676 # here we try to enable all top level names
2677 local_forest_collision = \
2678 local_lsa.lsaRSetForestTrustInformation(local_policy,
2679 remote_lsa_info.dns_domain,
2680 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2683 except RuntimeError as error:
2684 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2686 self.write_forest_trust_info(local_forest_info,
2687 tln=remote_lsa_info.dns_domain.string,
2688 collisions=local_forest_collision)
2690 if remote_trust_info:
2691 self.outf.write("Setup remote forest trust information...\n")
2693 # get all information about the local trust (from the perspective of the remote domain)
2694 # this triggers netr_GetForestTrustInformation to our domain.
2695 # and lsaRSetForestTrustInformation() remotely, but new top level
2696 # names are disabled by default.
2697 remote_forest_info = \
2698 remote_netlogon.netr_DsRGetForestTrustInformation(remote_netlogon_dc_unc,
2699 local_lsa_info.dns_domain.string,
2700 netlogon.DS_GFTI_UPDATE_TDO)
2701 except RuntimeError as error:
2702 raise self.RemoteRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2705 # here we try to enable all top level names
2706 remote_forest_collision = \
2707 remote_lsa.lsaRSetForestTrustInformation(remote_policy,
2708 local_lsa_info.dns_domain,
2709 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2712 except RuntimeError as error:
2713 raise self.RemoteRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2715 self.write_forest_trust_info(remote_forest_info,
2716 tln=local_lsa_info.dns_domain.string,
2717 collisions=remote_forest_collision)
2719 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2720 self.outf.write("Validating outgoing trust...\n")
2722 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_netlogon_info.dc_unc,
2723 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2725 remote_lsa_info.dns_domain.string)
2726 except RuntimeError as error:
2727 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2729 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2730 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2732 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2733 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2734 local_trust_verify.trusted_dc_name,
2735 local_trust_verify.tc_connection_status[1],
2736 local_trust_verify.pdc_connection_status[1])
2738 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2739 local_trust_verify.trusted_dc_name,
2740 local_trust_verify.tc_connection_status[1],
2741 local_trust_verify.pdc_connection_status[1])
2743 if local_trust_status != werror.WERR_SUCCESS or local_conn_status != werror.WERR_SUCCESS:
2744 raise CommandError(local_validation)
2746 self.outf.write("OK: %s\n" % local_validation)
2748 if remote_trust_info:
2749 if remote_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2750 self.outf.write("Validating incoming trust...\n")
2752 remote_trust_verify = \
2753 remote_netlogon.netr_LogonControl2Ex(remote_netlogon_dc_unc,
2754 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2756 local_lsa_info.dns_domain.string)
2757 except RuntimeError as error:
2758 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2760 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2761 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2763 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2764 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2765 remote_trust_verify.trusted_dc_name,
2766 remote_trust_verify.tc_connection_status[1],
2767 remote_trust_verify.pdc_connection_status[1])
2769 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2770 remote_trust_verify.trusted_dc_name,
2771 remote_trust_verify.tc_connection_status[1],
2772 remote_trust_verify.pdc_connection_status[1])
2774 if remote_trust_status != werror.WERR_SUCCESS or remote_conn_status != werror.WERR_SUCCESS:
2775 raise CommandError(remote_validation)
2777 self.outf.write("OK: %s\n" % remote_validation)
2779 if remote_tdo_handle is not None:
2781 remote_lsa.Close(remote_tdo_handle)
2782 except RuntimeError as error:
2784 remote_tdo_handle = None
2785 if local_tdo_handle is not None:
2787 local_lsa.Close(local_tdo_handle)
2788 except RuntimeError as error:
2790 local_tdo_handle = None
2792 self.outf.write("Success.\n")
2796 class cmd_domain_trust_delete(DomainTrustCommand):
2797 """Delete a domain trust."""
2799 synopsis = "%prog DOMAIN [options]"
2801 takes_optiongroups = {
2802 "sambaopts": options.SambaOptions,
2803 "versionopts": options.VersionOptions,
2804 "credopts": options.CredentialsOptions,
2805 "localdcopts": LocalDCCredentialsOptions,
2809 Option("--delete-location", type="choice", metavar="LOCATION",
2810 choices=["local", "both"],
2811 help="Where to delete the trusted domain object: 'local' or 'both'.",
2812 dest='delete_location',
2816 takes_args = ["domain"]
2818 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2819 delete_location=None):
2821 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2822 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2823 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2825 if delete_location == "local":
2826 remote_policy_access = None
2828 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2829 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2830 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2832 local_server = self.setup_local_server(sambaopts, localdcopts)
2834 local_lsa = self.new_local_lsa_connection()
2835 except RuntimeError as error:
2836 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2839 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2840 except RuntimeError as error:
2841 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2843 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2844 local_lsa_info.name.string,
2845 local_lsa_info.dns_domain.string,
2846 local_lsa_info.sid))
2848 local_tdo_info = None
2849 local_tdo_handle = None
2850 remote_tdo_info = None
2851 remote_tdo_handle = None
2853 lsaString = lsa.String()
2855 lsaString.string = domain
2856 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2857 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2858 except NTSTATUSError as error:
2859 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2860 raise CommandError("Failed to find trust for domain '%s'" % domain)
2861 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2863 if remote_policy_access is not None:
2865 remote_server = self.setup_remote_server(credopts, domain)
2866 except RuntimeError as error:
2867 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2870 remote_lsa = self.new_remote_lsa_connection()
2871 except RuntimeError as error:
2872 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2875 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2876 except RuntimeError as error:
2877 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2879 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2880 remote_lsa_info.name.string,
2881 remote_lsa_info.dns_domain.string,
2882 remote_lsa_info.sid))
2884 if remote_lsa_info.sid != local_tdo_info.sid or \
2885 remote_lsa_info.name.string != local_tdo_info.netbios_name.string or \
2886 remote_lsa_info.dns_domain.string != local_tdo_info.domain_name.string:
2887 raise CommandError("LocalTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2888 local_tdo_info.netbios_name.string,
2889 local_tdo_info.domain_name.string,
2890 local_tdo_info.sid))
2893 lsaString.string = local_lsa_info.dns_domain.string
2895 remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2897 lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2898 except NTSTATUSError as error:
2899 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2900 raise self.RemoteRuntimeError(self, error, "QueryTrustedDomainInfoByName(%s)" % (
2904 if remote_tdo_info is not None:
2905 if local_lsa_info.sid != remote_tdo_info.sid or \
2906 local_lsa_info.name.string != remote_tdo_info.netbios_name.string or \
2907 local_lsa_info.dns_domain.string != remote_tdo_info.domain_name.string:
2908 raise CommandError("RemoteTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2909 remote_tdo_info.netbios_name.string,
2910 remote_tdo_info.domain_name.string,
2911 remote_tdo_info.sid))
2913 if local_tdo_info is not None:
2915 lsaString.string = local_tdo_info.domain_name.string
2916 local_tdo_handle = \
2917 local_lsa.OpenTrustedDomainByName(local_policy,
2919 security.SEC_STD_DELETE)
2920 except RuntimeError as error:
2921 raise self.LocalRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2924 local_lsa.DeleteObject(local_tdo_handle)
2925 local_tdo_handle = None
2927 if remote_tdo_info is not None:
2929 lsaString.string = remote_tdo_info.domain_name.string
2930 remote_tdo_handle = \
2931 remote_lsa.OpenTrustedDomainByName(remote_policy,
2933 security.SEC_STD_DELETE)
2934 except RuntimeError as error:
2935 raise self.RemoteRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2938 if remote_tdo_handle is not None:
2940 remote_lsa.DeleteObject(remote_tdo_handle)
2941 remote_tdo_handle = None
2942 self.outf.write("RemoteTDO deleted.\n")
2943 except RuntimeError as error:
2944 self.outf.write("%s\n" % self.RemoteRuntimeError(self, error, "DeleteObject() failed"))
2946 if local_tdo_handle is not None:
2948 local_lsa.DeleteObject(local_tdo_handle)
2949 local_tdo_handle = None
2950 self.outf.write("LocalTDO deleted.\n")
2951 except RuntimeError as error:
2952 self.outf.write("%s\n" % self.LocalRuntimeError(self, error, "DeleteObject() failed"))
2957 class cmd_domain_trust_validate(DomainTrustCommand):
2958 """Validate a domain trust."""
2960 synopsis = "%prog DOMAIN [options]"
2962 takes_optiongroups = {
2963 "sambaopts": options.SambaOptions,
2964 "versionopts": options.VersionOptions,
2965 "credopts": options.CredentialsOptions,
2966 "localdcopts": LocalDCCredentialsOptions,
2970 Option("--validate-location", type="choice", metavar="LOCATION",
2971 choices=["local", "both"],
2972 help="Where to validate the trusted domain object: 'local' or 'both'.",
2973 dest='validate_location',
2977 takes_args = ["domain"]
2979 def run(self, domain, sambaopts=None, versionopts=None, credopts=None, localdcopts=None,
2980 validate_location=None):
2982 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2984 local_server = self.setup_local_server(sambaopts, localdcopts)
2986 local_lsa = self.new_local_lsa_connection()
2987 except RuntimeError as error:
2988 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2991 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2992 except RuntimeError as error:
2993 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2995 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2996 local_lsa_info.name.string,
2997 local_lsa_info.dns_domain.string,
2998 local_lsa_info.sid))
3001 lsaString = lsa.String()
3002 lsaString.string = domain
3004 local_lsa.QueryTrustedDomainInfoByName(local_policy,
3006 lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
3007 except NTSTATUSError as error:
3008 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
3009 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
3011 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
3013 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
3014 local_tdo_info.netbios_name.string,
3015 local_tdo_info.domain_name.string,
3016 local_tdo_info.sid))
3019 local_netlogon = self.new_local_netlogon_connection()
3020 except RuntimeError as error:
3021 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3024 local_trust_verify = \
3025 local_netlogon.netr_LogonControl2Ex(local_server,
3026 netlogon.NETLOGON_CONTROL_TC_VERIFY,
3028 local_tdo_info.domain_name.string)
3029 except RuntimeError as error:
3030 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
3032 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
3033 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
3035 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
3036 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
3037 local_trust_verify.trusted_dc_name,
3038 local_trust_verify.tc_connection_status[1],
3039 local_trust_verify.pdc_connection_status[1])
3041 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
3042 local_trust_verify.trusted_dc_name,
3043 local_trust_verify.tc_connection_status[1],
3044 local_trust_verify.pdc_connection_status[1])
3046 if local_trust_status != werror.WERR_SUCCESS or local_conn_status != werror.WERR_SUCCESS:
3047 raise CommandError(local_validation)
3049 self.outf.write("OK: %s\n" % local_validation)
3052 server = local_trust_verify.trusted_dc_name.replace('\\', '')
3053 domain_and_server = "%s\\%s" % (local_tdo_info.domain_name.string, server)
3054 local_trust_rediscover = \
3055 local_netlogon.netr_LogonControl2Ex(local_server,
3056 netlogon.NETLOGON_CONTROL_REDISCOVER,
3059 except RuntimeError as error:
3060 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
3062 local_conn_status = self._uint32(local_trust_rediscover.tc_connection_status[0])
3063 local_rediscover = "LocalRediscover: DC[%s] CONNECTION[%s]" % (
3064 local_trust_rediscover.trusted_dc_name,
3065 local_trust_rediscover.tc_connection_status[1])
3067 if local_conn_status != werror.WERR_SUCCESS:
3068 raise CommandError(local_rediscover)
3070 self.outf.write("OK: %s\n" % local_rediscover)
3072 if validate_location != "local":
3074 remote_server = self.setup_remote_server(credopts, domain, require_pdc=False)
3075 except RuntimeError as error:
3076 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
3079 remote_netlogon = self.new_remote_netlogon_connection()
3080 except RuntimeError as error:
3081 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
3084 remote_trust_verify = \
3085 remote_netlogon.netr_LogonControl2Ex(remote_server,
3086 netlogon.NETLOGON_CONTROL_TC_VERIFY,
3088 local_lsa_info.dns_domain.string)
3089 except RuntimeError as error:
3090 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
3092 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
3093 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
3095 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
3096 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
3097 remote_trust_verify.trusted_dc_name,
3098 remote_trust_verify.tc_connection_status[1],
3099 remote_trust_verify.pdc_connection_status[1])
3101 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
3102 remote_trust_verify.trusted_dc_name,
3103 remote_trust_verify.tc_connection_status[1],
3104 remote_trust_verify.pdc_connection_status[1])
3106 if remote_trust_status != werror.WERR_SUCCESS or remote_conn_status != werror.WERR_SUCCESS:
3107 raise CommandError(remote_validation)
3109 self.outf.write("OK: %s\n" % remote_validation)
3112 server = remote_trust_verify.trusted_dc_name.replace('\\', '')
3113 domain_and_server = "%s\\%s" % (local_lsa_info.dns_domain.string, server)
3114 remote_trust_rediscover = \
3115 remote_netlogon.netr_LogonControl2Ex(remote_server,
3116 netlogon.NETLOGON_CONTROL_REDISCOVER,
3119 except RuntimeError as error:
3120 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
3122 remote_conn_status = self._uint32(remote_trust_rediscover.tc_connection_status[0])
3124 remote_rediscover = "RemoteRediscover: DC[%s] CONNECTION[%s]" % (
3125 remote_trust_rediscover.trusted_dc_name,
3126 remote_trust_rediscover.tc_connection_status[1])
3128 if remote_conn_status != werror.WERR_SUCCESS:
3129 raise CommandError(remote_rediscover)
3131 self.outf.write("OK: %s\n" % remote_rediscover)
3136 class cmd_domain_trust_namespaces(DomainTrustCommand):
3137 """Manage forest trust namespaces."""
3139 synopsis = "%prog [DOMAIN] [options]"
3141 takes_optiongroups = {
3142 "sambaopts": options.SambaOptions,
3143 "versionopts": options.VersionOptions,
3144 "localdcopts": LocalDCCredentialsOptions,
3148 Option("--refresh", type="choice", metavar="check|store",
3149 choices=["check", "store", None],
3150 help="List and maybe store refreshed forest trust information: 'check' or 'store'.",
3153 Option("--enable-all", action="store_true",
3154 help="Try to update disabled entries, not allowed with --refresh=check.",
3157 Option("--enable-tln", action="append", metavar='DNSDOMAIN',
3158 help="Enable a top level name entry. Can be specified multiple times.",
3161 Option("--disable-tln", action="append", metavar='DNSDOMAIN',
3162 help="Disable a top level name entry. Can be specified multiple times.",
3165 Option("--add-tln-ex", action="append", metavar='DNSDOMAIN',
3166 help="Add a top level exclusion entry. Can be specified multiple times.",
3169 Option("--delete-tln-ex", action="append", metavar='DNSDOMAIN',
3170 help="Delete a top level exclusion entry. Can be specified multiple times.",
3171 dest='delete_tln_ex',
3173 Option("--enable-nb", action="append", metavar='NETBIOSDOMAIN',
3174 help="Enable a netbios name in a domain entry. Can be specified multiple times.",
3177 Option("--disable-nb", action="append", metavar='NETBIOSDOMAIN',
3178 help="Disable a netbios name in a domain entry. Can be specified multiple times.",
3181 Option("--enable-sid", action="append", metavar='DOMAINSID',
3182 help="Enable a SID in a domain entry. Can be specified multiple times.",
3183 dest='enable_sid_str',
3185 Option("--disable-sid", action="append", metavar='DOMAINSID',
3186 help="Disable a SID in a domain entry. Can be specified multiple times.",
3187 dest='disable_sid_str',
3189 Option("--add-upn-suffix", action="append", metavar='DNSDOMAIN',
3190 help="Add a new uPNSuffixes attribute for the local forest. Can be specified multiple times.",
3193 Option("--delete-upn-suffix", action="append", metavar='DNSDOMAIN',
3194 help="Delete an existing uPNSuffixes attribute of the local forest. Can be specified multiple times.",
3197 Option("--add-spn-suffix", action="append", metavar='DNSDOMAIN',
3198 help="Add a new msDS-SPNSuffixes attribute for the local forest. Can be specified multiple times.",
3201 Option("--delete-spn-suffix", action="append", metavar='DNSDOMAIN',
3202 help="Delete an existing msDS-SPNSuffixes attribute of the local forest. Can be specified multiple times.",
3207 takes_args = ["domain?"]
3209 def run(self, domain=None, sambaopts=None, localdcopts=None, versionopts=None,
3210 refresh=None, enable_all=False,
3211 enable_tln=[], disable_tln=[], add_tln_ex=[], delete_tln_ex=[],
3212 enable_sid_str=[], disable_sid_str=[], enable_nb=[], disable_nb=[],
3213 add_upn=[], delete_upn=[], add_spn=[], delete_spn=[]):
3215 require_update = False
3218 if refresh == "store":
3219 raise CommandError("--refresh=%s not allowed without DOMAIN" % refresh)
3222 raise CommandError("--enable-all not allowed without DOMAIN")
3224 if len(enable_tln) > 0:
3225 raise CommandError("--enable-tln not allowed without DOMAIN")
3226 if len(disable_tln) > 0:
3227 raise CommandError("--disable-tln not allowed without DOMAIN")
3229 if len(add_tln_ex) > 0:
3230 raise CommandError("--add-tln-ex not allowed without DOMAIN")
3231 if len(delete_tln_ex) > 0:
3232 raise CommandError("--delete-tln-ex not allowed without DOMAIN")
3234 if len(enable_nb) > 0:
3235 raise CommandError("--enable-nb not allowed without DOMAIN")
3236 if len(disable_nb) > 0:
3237 raise CommandError("--disable-nb not allowed without DOMAIN")
3239 if len(enable_sid_str) > 0:
3240 raise CommandError("--enable-sid not allowed without DOMAIN")
3241 if len(disable_sid_str) > 0:
3242 raise CommandError("--disable-sid not allowed without DOMAIN")
3244 if len(add_upn) > 0:
3246 if not n.startswith("*."):
3248 raise CommandError("value[%s] specified for --add-upn-suffix should not include with '*.'" % n)
3249 require_update = True
3250 if len(delete_upn) > 0:
3251 for n in delete_upn:
3252 if not n.startswith("*."):
3254 raise CommandError("value[%s] specified for --delete-upn-suffix should not include with '*.'" % n)
3255 require_update = True
3257 for d in delete_upn:
3258 if a.lower() != d.lower():
3260 raise CommandError("value[%s] specified for --add-upn-suffix and --delete-upn-suffix" % a)
3262 if len(add_spn) > 0:
3264 if not n.startswith("*."):
3266 raise CommandError("value[%s] specified for --add-spn-suffix should not include with '*.'" % n)
3267 require_update = True
3268 if len(delete_spn) > 0:
3269 for n in delete_spn:
3270 if not n.startswith("*."):
3272 raise CommandError("value[%s] specified for --delete-spn-suffix should not include with '*.'" % n)
3273 require_update = True
3275 for d in delete_spn:
3276 if a.lower() != d.lower():
3278 raise CommandError("value[%s] specified for --add-spn-suffix and --delete-spn-suffix" % a)
3280 if len(add_upn) > 0:
3281 raise CommandError("--add-upn-suffix not allowed together with DOMAIN")
3282 if len(delete_upn) > 0:
3283 raise CommandError("--delete-upn-suffix not allowed together with DOMAIN")
3284 if len(add_spn) > 0:
3285 raise CommandError("--add-spn-suffix not allowed together with DOMAIN")
3286 if len(delete_spn) > 0:
3287 raise CommandError("--delete-spn-suffix not allowed together with DOMAIN")
3289 if refresh is not None:
3290 if refresh == "store":
3291 require_update = True
3293 if enable_all and refresh != "store":
3294 raise CommandError("--enable-all not allowed together with --refresh=%s" % refresh)
3296 if len(enable_tln) > 0:
3297 raise CommandError("--enable-tln not allowed together with --refresh")
3298 if len(disable_tln) > 0:
3299 raise CommandError("--disable-tln not allowed together with --refresh")
3301 if len(add_tln_ex) > 0:
3302 raise CommandError("--add-tln-ex not allowed together with --refresh")
3303 if len(delete_tln_ex) > 0:
3304 raise CommandError("--delete-tln-ex not allowed together with --refresh")
3306 if len(enable_nb) > 0:
3307 raise CommandError("--enable-nb not allowed together with --refresh")
3308 if len(disable_nb) > 0:
3309 raise CommandError("--disable-nb not allowed together with --refresh")
3311 if len(enable_sid_str) > 0:
3312 raise CommandError("--enable-sid not allowed together with --refresh")
3313 if len(disable_sid_str) > 0:
3314 raise CommandError("--disable-sid not allowed together with --refresh")
3317 require_update = True
3319 if len(enable_tln) > 0:
3320 raise CommandError("--enable-tln not allowed together with --enable-all")
3322 if len(enable_nb) > 0:
3323 raise CommandError("--enable-nb not allowed together with --enable-all")
3325 if len(enable_sid_str) > 0:
3326 raise CommandError("--enable-sid not allowed together with --enable-all")
3328 if len(enable_tln) > 0:
3329 require_update = True
3330 if len(disable_tln) > 0:
3331 require_update = True
3332 for e in enable_tln:
3333 for d in disable_tln:
3334 if e.lower() != d.lower():
3336 raise CommandError("value[%s] specified for --enable-tln and --disable-tln" % e)
3338 if len(add_tln_ex) > 0:
3339 for n in add_tln_ex:
3340 if not n.startswith("*."):
3342 raise CommandError("value[%s] specified for --add-tln-ex should not include with '*.'" % n)
3343 require_update = True
3344 if len(delete_tln_ex) > 0:
3345 for n in delete_tln_ex:
3346 if not n.startswith("*."):
3348 raise CommandError("value[%s] specified for --delete-tln-ex should not include with '*.'" % n)
3349 require_update = True
3350 for a in add_tln_ex:
3351 for d in delete_tln_ex:
3352 if a.lower() != d.lower():
3354 raise CommandError("value[%s] specified for --add-tln-ex and --delete-tln-ex" % a)
3356 if len(enable_nb) > 0:
3357 require_update = True
3358 if len(disable_nb) > 0:
3359 require_update = True
3361 for d in disable_nb:
3362 if e.upper() != d.upper():
3364 raise CommandError("value[%s] specified for --enable-nb and --disable-nb" % e)
3367 for s in enable_sid_str:
3369 sid = security.dom_sid(s)
3370 except TypeError as error:
3371 raise CommandError("value[%s] specified for --enable-sid is not a valid SID" % s)
3372 enable_sid.append(sid)
3374 for s in disable_sid_str:
3376 sid = security.dom_sid(s)
3377 except TypeError as error:
3378 raise CommandError("value[%s] specified for --disable-sid is not a valid SID" % s)
3379 disable_sid.append(sid)
3380 if len(enable_sid) > 0:
3381 require_update = True
3382 if len(disable_sid) > 0:
3383 require_update = True
3384 for e in enable_sid:
3385 for d in disable_sid:
3388 raise CommandError("value[%s] specified for --enable-sid and --disable-sid" % e)
3390 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
3392 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
3394 local_server = self.setup_local_server(sambaopts, localdcopts)
3396 local_lsa = self.new_local_lsa_connection()
3397 except RuntimeError as error:
3398 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
3401 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
3402 except RuntimeError as error:
3403 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
3405 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
3406 local_lsa_info.name.string,
3407 local_lsa_info.dns_domain.string,
3408 local_lsa_info.sid))
3412 local_netlogon = self.new_local_netlogon_connection()
3413 except RuntimeError as error:
3414 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3417 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3418 except RuntimeError as error:
3419 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3421 if local_netlogon_info.domain_name != local_netlogon_info.forest_name:
3422 raise CommandError("The local domain [%s] is not the forest root [%s]" % (
3423 local_netlogon_info.domain_name,
3424 local_netlogon_info.forest_name))
3427 # get all information about our own forest
3428 own_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3430 except RuntimeError as error:
3431 if self.check_runtime_error(error, werror.WERR_RPC_S_PROCNUM_OUT_OF_RANGE):
3432 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3435 if self.check_runtime_error(error, werror.WERR_INVALID_FUNCTION):
3436 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3439 if self.check_runtime_error(error, werror.WERR_NERR_ACFNOTLOADED):
3440 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3443 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3445 self.outf.write("Own forest trust information...\n")
3446 self.write_forest_trust_info(own_forest_info,
3447 tln=local_lsa_info.dns_domain.string)
3450 local_samdb = self.new_local_ldap_connection()
3451 except RuntimeError as error:
3452 raise self.LocalRuntimeError(self, error, "failed to connect to SamDB")
3454 local_partitions_dn = "CN=Partitions,%s" % str(local_samdb.get_config_basedn())
3455 attrs = ['uPNSuffixes', 'msDS-SPNSuffixes']
3457 msgs = local_samdb.search(base=local_partitions_dn,
3458 scope=ldb.SCOPE_BASE,
3459 expression="(objectClass=crossRefContainer)",
3461 stored_msg = msgs[0]
3462 except ldb.LdbError as error:
3463 raise self.LocalLdbError(self, error, "failed to search partition dn")
3465 stored_upn_vals = []
3466 if 'uPNSuffixes' in stored_msg:
3467 stored_upn_vals.extend(stored_msg['uPNSuffixes'])
3469 stored_spn_vals = []
3470 if 'msDS-SPNSuffixes' in stored_msg:
3471 stored_spn_vals.extend(stored_msg['msDS-SPNSuffixes'])
3473 self.outf.write("Stored uPNSuffixes attributes[%d]:\n" % len(stored_upn_vals))
3474 for v in stored_upn_vals:
3475 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3476 self.outf.write("Stored msDS-SPNSuffixes attributes[%d]:\n" % len(stored_spn_vals))
3477 for v in stored_spn_vals:
3478 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3480 if not require_update:
3484 update_upn_vals = []
3485 update_upn_vals.extend(stored_upn_vals)
3488 update_spn_vals = []
3489 update_spn_vals.extend(stored_spn_vals)
3492 for i, v in enumerate(update_upn_vals):
3493 if v.lower() == upn.lower():
3494 raise CommandError("Entry already present for "
3495 "value[%s] specified for "
3496 "--add-upn-suffix" % upn)
3497 update_upn_vals.append(upn)
3500 for upn in delete_upn:
3502 for i, v in enumerate(update_upn_vals):
3503 if v.lower() != upn.lower():
3508 raise CommandError("Entry not found for value[%s] specified for --delete-upn-suffix" % upn)
3510 update_upn_vals.pop(idx)
3514 for i, v in enumerate(update_spn_vals):
3515 if v.lower() == spn.lower():
3516 raise CommandError("Entry already present for "
3517 "value[%s] specified for "
3518 "--add-spn-suffix" % spn)
3519 update_spn_vals.append(spn)
3522 for spn in delete_spn:
3524 for i, v in enumerate(update_spn_vals):
3525 if v.lower() != spn.lower():
3530 raise CommandError("Entry not found for value[%s] specified for --delete-spn-suffix" % spn)
3532 update_spn_vals.pop(idx)
3535 self.outf.write("Update uPNSuffixes attributes[%d]:\n" % len(update_upn_vals))
3536 for v in update_upn_vals:
3537 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3538 self.outf.write("Update msDS-SPNSuffixes attributes[%d]:\n" % len(update_spn_vals))
3539 for v in update_spn_vals:
3540 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3542 update_msg = ldb.Message()
3543 update_msg.dn = stored_msg.dn
3546 update_msg['uPNSuffixes'] = ldb.MessageElement(update_upn_vals,
3547 ldb.FLAG_MOD_REPLACE,
3550 update_msg['msDS-SPNSuffixes'] = ldb.MessageElement(update_spn_vals,
3551 ldb.FLAG_MOD_REPLACE,
3554 local_samdb.modify(update_msg)
3555 except ldb.LdbError as error:
3556 raise self.LocalLdbError(self, error, "failed to update partition dn")
3559 stored_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3561 except RuntimeError as error:
3562 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3564 self.outf.write("Stored forest trust information...\n")
3565 self.write_forest_trust_info(stored_forest_info,
3566 tln=local_lsa_info.dns_domain.string)
3570 lsaString = lsa.String()
3571 lsaString.string = domain
3573 local_lsa.QueryTrustedDomainInfoByName(local_policy,
3575 lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
3576 except NTSTATUSError as error:
3577 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
3578 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
3580 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
3582 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
3583 local_tdo_info.netbios_name.string,
3584 local_tdo_info.domain_name.string,
3585 local_tdo_info.sid))
3587 if not local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
3588 raise CommandError("trusted domain object for domain [%s] is not marked as FOREST_TRANSITIVE." % domain)
3590 if refresh is not None:
3592 local_netlogon = self.new_local_netlogon_connection()
3593 except RuntimeError as error:
3594 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3597 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3598 except RuntimeError as error:
3599 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3601 lsa_update_check = 1
3602 if refresh == "store":
3603 netlogon_update_tdo = netlogon.DS_GFTI_UPDATE_TDO
3605 lsa_update_check = 0
3607 netlogon_update_tdo = 0
3610 # get all information about the remote trust
3611 # this triggers netr_GetForestTrustInformation to the remote domain
3612 # and lsaRSetForestTrustInformation() locally, but new top level
3613 # names are disabled by default.
3614 fresh_forest_info = \
3615 local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3616 local_tdo_info.domain_name.string,
3617 netlogon_update_tdo)
3618 except RuntimeError as error:
3619 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3622 fresh_forest_collision = \
3623 local_lsa.lsaRSetForestTrustInformation(local_policy,
3624 local_tdo_info.domain_name,
3625 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3628 except RuntimeError as error:
3629 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3631 self.outf.write("Fresh forest trust information...\n")
3632 self.write_forest_trust_info(fresh_forest_info,
3633 tln=local_tdo_info.domain_name.string,
3634 collisions=fresh_forest_collision)
3636 if refresh == "store":
3638 lsaString = lsa.String()
3639 lsaString.string = local_tdo_info.domain_name.string
3640 stored_forest_info = \
3641 local_lsa.lsaRQueryForestTrustInformation(local_policy,
3643 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3644 except RuntimeError as error:
3645 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3647 self.outf.write("Stored forest trust information...\n")
3648 self.write_forest_trust_info(stored_forest_info,
3649 tln=local_tdo_info.domain_name.string)
3654 # The none --refresh path
3658 lsaString = lsa.String()
3659 lsaString.string = local_tdo_info.domain_name.string
3660 local_forest_info = \
3661 local_lsa.lsaRQueryForestTrustInformation(local_policy,
3663 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3664 except RuntimeError as error:
3665 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3667 self.outf.write("Local forest trust information...\n")
3668 self.write_forest_trust_info(local_forest_info,
3669 tln=local_tdo_info.domain_name.string)
3671 if not require_update:
3675 entries.extend(local_forest_info.entries)
3676 update_forest_info = lsa.ForestTrustInformation()
3677 update_forest_info.count = len(entries)
3678 update_forest_info.entries = entries
3681 for i, r in enumerate(update_forest_info.entries):
3682 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3684 if update_forest_info.entries[i].flags == 0:
3686 update_forest_info.entries[i].time = 0
3687 update_forest_info.entries[i].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3688 for i, r in enumerate(update_forest_info.entries):
3689 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3691 if update_forest_info.entries[i].flags == 0:
3693 update_forest_info.entries[i].time = 0
3694 update_forest_info.entries[i].flags &= ~lsa.LSA_NB_DISABLED_MASK
3695 update_forest_info.entries[i].flags &= ~lsa.LSA_SID_DISABLED_MASK
3697 for tln in enable_tln:
3699 for i, r in enumerate(update_forest_info.entries):
3700 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3702 if r.forest_trust_data.string.lower() != tln.lower():
3707 raise CommandError("Entry not found for value[%s] specified for --enable-tln" % tln)
3708 if not update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_MASK:
3709 raise CommandError("Entry found for value[%s] specified for --enable-tln is already enabled" % tln)
3710 update_forest_info.entries[idx].time = 0
3711 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3713 for tln in disable_tln:
3715 for i, r in enumerate(update_forest_info.entries):
3716 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3718 if r.forest_trust_data.string.lower() != tln.lower():
3723 raise CommandError("Entry not found for value[%s] specified for --disable-tln" % tln)
3724 if update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_ADMIN:
3725 raise CommandError("Entry found for value[%s] specified for --disable-tln is already disabled" % tln)
3726 update_forest_info.entries[idx].time = 0
3727 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3728 update_forest_info.entries[idx].flags |= lsa.LSA_TLN_DISABLED_ADMIN
3730 for tln_ex in add_tln_ex:
3732 for i, r in enumerate(update_forest_info.entries):
3733 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3735 if r.forest_trust_data.string.lower() != tln_ex.lower():
3740 raise CommandError("Entry already present for value[%s] specified for --add-tln-ex" % tln_ex)
3742 tln_dot = ".%s" % tln_ex.lower()
3744 for i, r in enumerate(update_forest_info.entries):
3745 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3747 r_dot = ".%s" % r.forest_trust_data.string.lower()
3748 if tln_dot == r_dot:
3749 raise CommandError("TLN entry present for value[%s] specified for --add-tln-ex" % tln_ex)
3750 if not tln_dot.endswith(r_dot):
3756 raise CommandError("No TLN parent present for value[%s] specified for --add-tln-ex" % tln_ex)
3758 r = lsa.ForestTrustRecord()
3759 r.type = lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX
3762 r.forest_trust_data.string = tln_ex
3765 entries.extend(update_forest_info.entries)
3766 entries.insert(idx + 1, r)
3767 update_forest_info.count = len(entries)
3768 update_forest_info.entries = entries
3770 for tln_ex in delete_tln_ex:
3772 for i, r in enumerate(update_forest_info.entries):
3773 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3775 if r.forest_trust_data.string.lower() != tln_ex.lower():
3780 raise CommandError("Entry not found for value[%s] specified for --delete-tln-ex" % tln_ex)
3783 entries.extend(update_forest_info.entries)
3785 update_forest_info.count = len(entries)
3786 update_forest_info.entries = entries
3788 for nb in enable_nb:
3790 for i, r in enumerate(update_forest_info.entries):
3791 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3793 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3798 raise CommandError("Entry not found for value[%s] specified for --enable-nb" % nb)
3799 if not update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_MASK:
3800 raise CommandError("Entry found for value[%s] specified for --enable-nb is already enabled" % nb)
3801 update_forest_info.entries[idx].time = 0
3802 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3804 for nb in disable_nb:
3806 for i, r in enumerate(update_forest_info.entries):
3807 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3809 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3814 raise CommandError("Entry not found for value[%s] specified for --delete-nb" % nb)
3815 if update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_ADMIN:
3816 raise CommandError("Entry found for value[%s] specified for --disable-nb is already disabled" % nb)
3817 update_forest_info.entries[idx].time = 0
3818 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3819 update_forest_info.entries[idx].flags |= lsa.LSA_NB_DISABLED_ADMIN
3821 for sid in enable_sid:
3823 for i, r in enumerate(update_forest_info.entries):
3824 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3826 if r.forest_trust_data.domain_sid != sid:
3831 raise CommandError("Entry not found for value[%s] specified for --enable-sid" % sid)
3832 if not update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_MASK:
3833 raise CommandError("Entry found for value[%s] specified for --enable-sid is already enabled" % nb)
3834 update_forest_info.entries[idx].time = 0
3835 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3837 for sid in disable_sid:
3839 for i, r in enumerate(update_forest_info.entries):
3840 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3842 if r.forest_trust_data.domain_sid != sid:
3847 raise CommandError("Entry not found for value[%s] specified for --delete-sid" % sid)
3848 if update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_ADMIN:
3849 raise CommandError("Entry found for value[%s] specified for --disable-sid is already disabled" % nb)
3850 update_forest_info.entries[idx].time = 0
3851 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3852 update_forest_info.entries[idx].flags |= lsa.LSA_SID_DISABLED_ADMIN
3855 update_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3856 local_tdo_info.domain_name,
3857 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3858 update_forest_info, 0)
3859 except RuntimeError as error:
3860 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3862 self.outf.write("Updated forest trust information...\n")
3863 self.write_forest_trust_info(update_forest_info,
3864 tln=local_tdo_info.domain_name.string,
3865 collisions=update_forest_collision)
3868 lsaString = lsa.String()
3869 lsaString.string = local_tdo_info.domain_name.string
3870 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3872 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3873 except RuntimeError as error:
3874 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3876 self.outf.write("Stored forest trust information...\n")
3877 self.write_forest_trust_info(stored_forest_info,
3878 tln=local_tdo_info.domain_name.string)
3882 class cmd_domain_tombstones_expunge(Command):
3883 """Expunge tombstones from the database.
3885 This command expunges tombstones from the database."""
3886 synopsis = "%prog NC [NC [...]] [options]"
3889 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
3890 metavar="URL", dest="H"),
3891 Option("--current-time",
3892 help="The current time to evaluate the tombstone lifetime from, expressed as YYYY-MM-DD",
3894 Option("--tombstone-lifetime", help="Number of days a tombstone should be preserved for", type=int),
3897 takes_args = ["nc*"]
3899 takes_optiongroups = {
3900 "sambaopts": options.SambaOptions,
3901 "credopts": options.CredentialsOptions,
3902 "versionopts": options.VersionOptions,
3905 def run(self, *ncs, **kwargs):
3906 sambaopts = kwargs.get("sambaopts")
3907 credopts = kwargs.get("credopts")
3908 versionpts = kwargs.get("versionopts")
3910 current_time_string = kwargs.get("current_time")
3911 tombstone_lifetime = kwargs.get("tombstone_lifetime")
3912 lp = sambaopts.get_loadparm()
3913 creds = credopts.get_credentials(lp)
3914 samdb = SamDB(url=H, session_info=system_session(),
3915 credentials=creds, lp=lp)
3917 if current_time_string is not None:
3918 current_time_obj = time.strptime(current_time_string, "%Y-%m-%d")
3919 current_time = long(time.mktime(current_time_obj))
3922 current_time = long(time.time())
3925 res = samdb.search(expression="", base="", scope=ldb.SCOPE_BASE,
3926 attrs=["namingContexts"])
3929 for nc in res[0]["namingContexts"]:
3934 started_transaction = False
3936 samdb.transaction_start()
3937 started_transaction = True
3939 removed_links) = samdb.garbage_collect_tombstones(ncs,
3940 current_time=current_time,
3941 tombstone_lifetime=tombstone_lifetime)
3943 except Exception as err:
3944 if started_transaction:
3945 samdb.transaction_cancel()
3946 raise CommandError("Failed to expunge / garbage collect tombstones", err)
3948 samdb.transaction_commit()
3950 self.outf.write("Removed %d objects and %d links successfully\n"
3951 % (removed_objects, removed_links))
3954 class cmd_domain_trust(SuperCommand):
3955 """Domain and forest trust management."""
3958 subcommands["list"] = cmd_domain_trust_list()
3959 subcommands["show"] = cmd_domain_trust_show()
3960 subcommands["create"] = cmd_domain_trust_create()
3961 subcommands["delete"] = cmd_domain_trust_delete()
3962 subcommands["validate"] = cmd_domain_trust_validate()
3963 subcommands["namespaces"] = cmd_domain_trust_namespaces()
3966 class cmd_domain_tombstones(SuperCommand):
3967 """Domain tombstone and recycled object management."""
3970 subcommands["expunge"] = cmd_domain_tombstones_expunge()
3973 class ldif_schema_update:
3974 """Helper class for applying LDIF schema updates"""
3977 self.is_defunct = False
3978 self.unknown_oid = None
3982 def can_ignore_failure(self, error):
3983 """Checks if we can safely ignore failure to apply an LDIF update"""
3984 (num, errstr) = error.args
3986 # Microsoft has marked objects as defunct that Samba doesn't know about
3987 if num == ldb.ERR_NO_SUCH_OBJECT and self.is_defunct:
3988 print("Defunct object %s doesn't exist, skipping" % self.dn)
3990 elif self.unknown_oid is not None:
3991 print("Skipping unknown OID %s for object %s" % (self.unknown_oid, self.dn))
3996 def apply(self, samdb):
3997 """Applies a single LDIF update to the schema"""
4001 samdb.modify_ldif(self.ldif, controls=['relax:0'])
4002 except ldb.LdbError as e:
4003 if e.args[0] == ldb.ERR_INVALID_ATTRIBUTE_SYNTAX:
4005 # REFRESH after a failed change
4007 # Otherwise the OID-to-attribute mapping in
4008 # _apply_updates_in_file() won't work, because it
4009 # can't lookup the new OID in the schema
4010 samdb.set_schema_update_now()
4012 samdb.modify_ldif(self.ldif, controls=['relax:0'])
4015 except ldb.LdbError as e:
4016 if self.can_ignore_failure(e):
4019 print("Exception: %s" % e)
4020 print("Encountered while trying to apply the following LDIF")
4021 print("----------------------------------------------------")
4022 print("%s" % self.ldif)
4029 class cmd_domain_schema_upgrade(Command):
4030 """Domain schema upgrading"""
4032 synopsis = "%prog [options]"
4034 takes_optiongroups = {
4035 "sambaopts": options.SambaOptions,
4036 "versionopts": options.VersionOptions,
4037 "credopts": options.CredentialsOptions,
4041 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
4042 metavar="URL", dest="H"),
4043 Option("-q", "--quiet", help="Be quiet", action="store_true"), # unused
4044 Option("-v", "--verbose", help="Be verbose", action="store_true"),
4045 Option("--schema", type="choice", metavar="SCHEMA",
4046 choices=["2012", "2012_R2"],
4047 help="The schema file to upgrade to. Default is (Windows) 2012_R2.",
4049 Option("--ldf-file", type=str, default=None,
4050 help="Just apply the schema updates in the adprep/.LDF file(s) specified"),
4051 Option("--base-dir", type=str, default=None,
4052 help="Location of ldf files Default is ${SETUPDIR}/adprep.")
4055 def _apply_updates_in_file(self, samdb, ldif_file):
4057 Applies a series of updates specified in an .LDIF file. The .LDIF file
4058 is based on the adprep Schema updates provided by Microsoft.
4061 ldif_op = ldif_schema_update()
4063 # parse the file line by line and work out each update operation to apply
4064 for line in ldif_file:
4066 line = line.rstrip()
4068 # the operations in the .LDIF file are separated by blank lines. If
4069 # we hit a blank line, try to apply the update we've parsed so far
4072 # keep going if we haven't parsed anything yet
4073 if ldif_op.ldif == '':
4076 # Apply the individual change
4077 count += ldif_op.apply(samdb)
4079 # start storing the next operation from scratch again
4080 ldif_op = ldif_schema_update()
4083 # replace the placeholder domain name in the .ldif file with the real domain
4084 if line.upper().endswith('DC=X'):
4085 line = line[:-len('DC=X')] + str(samdb.get_default_basedn())
4086 elif line.upper().endswith('CN=X'):
4087 line = line[:-len('CN=X')] + str(samdb.get_default_basedn())
4089 values = line.split(':')
4091 if values[0].lower() == 'dn':
4092 ldif_op.dn = values[1].strip()
4094 # replace the Windows-specific operation with the Samba one
4095 if values[0].lower() == 'changetype':
4096 line = line.lower().replace(': ntdsschemaadd',
4098 line = line.lower().replace(': ntdsschemamodify',
4101 if values[0].lower() in ['rdnattid', 'subclassof',
4102 'systemposssuperiors',
4104 'systemauxiliaryclass']:
4107 # The Microsoft updates contain some OIDs we don't recognize.
4108 # Query the DB to see if we can work out the OID this update is
4109 # referring to. If we find a match, then replace the OID with
4110 # the ldapDisplayname
4112 res = samdb.search(base=samdb.get_schema_basedn(),
4113 expression="(|(attributeId=%s)(governsId=%s))" %
4115 attrs=['ldapDisplayName'])
4118 ldif_op.unknown_oid = value
4120 display_name = res[0]['ldapDisplayName'][0]
4121 line = line.replace(value, ' ' + display_name)
4123 # Microsoft has marked objects as defunct that Samba doesn't know about
4124 if values[0].lower() == 'isdefunct' and values[1].strip().lower() == 'true':
4125 ldif_op.is_defunct = True
4127 # Samba has added the showInAdvancedViewOnly attribute to all objects,
4128 # so rather than doing an add, we need to do a replace
4129 if values[0].lower() == 'add' and values[1].strip().lower() == 'showinadvancedviewonly':
4130 line = 'replace: showInAdvancedViewOnly'
4132 # Add the line to the current LDIF operation (including the newline
4133 # we stripped off at the start of the loop)
4134 ldif_op.ldif += line + '\n'
4138 def _apply_update(self, samdb, update_file, base_dir):
4139 """Wrapper function for parsing an LDIF file and applying the updates"""
4141 print("Applying %s updates..." % update_file)
4145 ldif_file = open(os.path.join(base_dir, update_file))
4147 count = self._apply_updates_in_file(samdb, ldif_file)
4153 print("%u changes applied" % count)
4157 def run(self, **kwargs):
4158 from samba.ms_schema_markdown import read_ms_markdown
4159 from samba.schema import Schema
4161 updates_allowed_overriden = False
4162 sambaopts = kwargs.get("sambaopts")
4163 credopts = kwargs.get("credopts")
4164 versionpts = kwargs.get("versionopts")
4165 lp = sambaopts.get_loadparm()
4166 creds = credopts.get_credentials(lp)
4168 target_schema = kwargs.get("schema")
4169 ldf_files = kwargs.get("ldf_file")
4170 base_dir = kwargs.get("base_dir")
4174 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
4176 # we're not going to get far if the config doesn't allow schema updates
4177 if lp.get("dsdb:schema update allowed") is None:
4178 lp.set("dsdb:schema update allowed", "yes")
4179 print("Temporarily overriding 'dsdb:schema update allowed' setting")
4180 updates_allowed_overriden = True
4182 own_dn = ldb.Dn(samdb, samdb.get_dsServiceName())
4183 master = get_fsmo_roleowner(samdb, str(samdb.get_schema_basedn()),
4185 if own_dn != master:
4186 raise CommandError("This server is not the schema master.")
4188 # if specific LDIF files were specified, just apply them
4190 schema_updates = ldf_files.split(",")
4194 # work out the version of the target schema we're upgrading to
4195 end = Schema.get_version(target_schema)
4197 # work out the version of the schema we're currently using
4198 res = samdb.search(base=samdb.get_schema_basedn(),
4199 scope=ldb.SCOPE_BASE, attrs=['objectVersion'])
4202 raise CommandError('Could not determine current schema version')
4203 start = int(res[0]['objectVersion'][0]) + 1
4205 diff_dir = setup_path("adprep/WindowsServerDocs")
4206 if base_dir is None:
4207 # Read from the Schema-Updates.md file
4208 temp_folder = tempfile.mkdtemp()
4210 update_file = setup_path("adprep/WindowsServerDocs/Schema-Updates.md")
4213 read_ms_markdown(update_file, temp_folder)
4214 except Exception as e:
4215 print("Exception in markdown parsing: %s" % e)
4216 shutil.rmtree(temp_folder)
4217 raise CommandError('Failed to upgrade schema')
4219 base_dir = temp_folder
4221 for version in range(start, end + 1):
4222 update = 'Sch%d.ldf' % version
4223 schema_updates.append(update)
4225 # Apply patches if we parsed the Schema-Updates.md file
4226 diff = os.path.abspath(os.path.join(diff_dir, update + '.diff'))
4227 if temp_folder and os.path.exists(diff):
4229 p = subprocess.Popen(['patch', update, '-i', diff],
4230 stdout=subprocess.PIPE,
4231 stderr=subprocess.PIPE, cwd=temp_folder)
4232 except (OSError, IOError):
4233 shutil.rmtree(temp_folder)
4234 raise CommandError("Failed to upgrade schema. Check if 'patch' is installed.")
4236 stdout, stderr = p.communicate()
4239 print("Exception in patch: %s\n%s" % (stdout, stderr))
4240 shutil.rmtree(temp_folder)
4241 raise CommandError('Failed to upgrade schema')
4243 print("Patched %s using %s" % (update, diff))
4245 if base_dir is None:
4246 base_dir = setup_path("adprep")
4248 samdb.transaction_start()
4250 error_encountered = False
4253 # Apply the schema updates needed to move to the new schema version
4254 for ldif_file in schema_updates:
4255 count += self._apply_update(samdb, ldif_file, base_dir)
4258 samdb.transaction_commit()
4259 print("Schema successfully updated")
4261 print("No changes applied to schema")
4262 samdb.transaction_cancel()
4263 except Exception as e:
4264 print("Exception: %s" % e)
4265 print("Error encountered, aborting schema upgrade")
4266 samdb.transaction_cancel()
4267 error_encountered = True
4269 if updates_allowed_overriden:
4270 lp.set("dsdb:schema update allowed", "no")
4273 shutil.rmtree(temp_folder)
4275 if error_encountered:
4276 raise CommandError('Failed to upgrade schema')
4279 class cmd_domain_functional_prep(Command):
4280 """Domain functional level preparation"""
4282 synopsis = "%prog [options]"
4284 takes_optiongroups = {
4285 "sambaopts": options.SambaOptions,
4286 "versionopts": options.VersionOptions,
4287 "credopts": options.CredentialsOptions,
4291 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
4292 metavar="URL", dest="H"),
4293 Option("-q", "--quiet", help="Be quiet", action="store_true"),
4294 Option("-v", "--verbose", help="Be verbose", action="store_true"),
4295 Option("--function-level", type="choice", metavar="FUNCTION_LEVEL",
4296 choices=["2008_R2", "2012", "2012_R2"],
4297 help="The schema file to upgrade to. Default is (Windows) 2012_R2.",
4299 Option("--forest-prep", action="store_true",
4300 help="Run the forest prep (by default, both the domain and forest prep are run)."),
4301 Option("--domain-prep", action="store_true",
4302 help="Run the domain prep (by default, both the domain and forest prep are run).")
4305 def run(self, **kwargs):
4306 updates_allowed_overriden = False
4307 sambaopts = kwargs.get("sambaopts")
4308 credopts = kwargs.get("credopts")
4309 versionpts = kwargs.get("versionopts")
4310 lp = sambaopts.get_loadparm()
4311 creds = credopts.get_credentials(lp)
4313 target_level = string_version_to_constant[kwargs.get("function_level")]
4314 forest_prep = kwargs.get("forest_prep")
4315 domain_prep = kwargs.get("domain_prep")
4317 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
4319 # we're not going to get far if the config doesn't allow schema updates
4320 if lp.get("dsdb:schema update allowed") is None:
4321 lp.set("dsdb:schema update allowed", "yes")
4322 print("Temporarily overriding 'dsdb:schema update allowed' setting")
4323 updates_allowed_overriden = True
4325 if forest_prep is None and domain_prep is None:
4329 own_dn = ldb.Dn(samdb, samdb.get_dsServiceName())
4331 master = get_fsmo_roleowner(samdb, str(samdb.get_schema_basedn()),
4333 if own_dn != master:
4334 raise CommandError("This server is not the schema master.")
4337 domain_dn = samdb.domain_dn()
4338 infrastructure_dn = "CN=Infrastructure," + domain_dn
4339 master = get_fsmo_roleowner(samdb, infrastructure_dn,
4341 if own_dn != master:
4342 raise CommandError("This server is not the infrastructure master.")
4345 samdb.transaction_start()
4346 error_encountered = False
4348 from samba.forest_update import ForestUpdate
4349 forest = ForestUpdate(samdb, fix=True)
4351 forest.check_updates_iterator([53, 79, 80, 81, 82, 83])
4352 forest.check_updates_functional_level(target_level,
4353 DS_DOMAIN_FUNCTION_2008_R2,
4354 update_revision=True)
4356 samdb.transaction_commit()
4357 except Exception as e:
4358 print("Exception: %s" % e)
4359 samdb.transaction_cancel()
4360 error_encountered = True
4363 samdb.transaction_start()
4364 error_encountered = False
4366 from samba.domain_update import DomainUpdate
4368 domain = DomainUpdate(samdb, fix=True)
4369 domain.check_updates_functional_level(target_level,
4370 DS_DOMAIN_FUNCTION_2008,
4371 update_revision=True)
4373 samdb.transaction_commit()
4374 except Exception as e:
4375 print("Exception: %s" % e)
4376 samdb.transaction_cancel()
4377 error_encountered = True
4379 if updates_allowed_overriden:
4380 lp.set("dsdb:schema update allowed", "no")
4382 if error_encountered:
4383 raise CommandError('Failed to perform functional prep')
4386 class cmd_domain(SuperCommand):
4387 """Domain management."""
4390 subcommands["demote"] = cmd_domain_demote()
4391 if cmd_domain_export_keytab is not None:
4392 subcommands["exportkeytab"] = cmd_domain_export_keytab()
4393 subcommands["info"] = cmd_domain_info()
4394 subcommands["provision"] = cmd_domain_provision()
4395 subcommands["join"] = cmd_domain_join()
4396 subcommands["dcpromo"] = cmd_domain_dcpromo()
4397 subcommands["level"] = cmd_domain_level()
4398 subcommands["passwordsettings"] = cmd_domain_passwordsettings()
4399 subcommands["classicupgrade"] = cmd_domain_classicupgrade()
4400 subcommands["samba3upgrade"] = cmd_domain_samba3upgrade()
4401 subcommands["trust"] = cmd_domain_trust()
4402 subcommands["tombstones"] = cmd_domain_tombstones()
4403 subcommands["schemaupgrade"] = cmd_domain_schema_upgrade()
4404 subcommands["functionalprep"] = cmd_domain_functional_prep()
4405 subcommands["backup"] = cmd_domain_backup()