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 from samba.compat import binary_type
106 from samba.compat import get_string
108 string_version_to_constant = {
109 "2008_R2": DS_DOMAIN_FUNCTION_2008_R2,
110 "2012": DS_DOMAIN_FUNCTION_2012,
111 "2012_R2": DS_DOMAIN_FUNCTION_2012_R2,
114 common_provision_join_options = [
115 Option("--machinepass", type="string", metavar="PASSWORD",
116 help="choose machine password (otherwise random)"),
117 Option("--plaintext-secrets", action="store_true",
118 help="Store secret/sensitive values as plain text on disk" +
119 "(default is to encrypt secret/ensitive values)"),
120 Option("--backend-store", type="choice", metavar="BACKENDSTORE",
121 choices=["tdb", "mdb"],
122 help="Specify the database backend to be used "
123 "(default is %s)" % get_default_backend_store()),
124 Option("--targetdir", metavar="DIR",
125 help="Set target directory (where to store provision)", type=str),
126 Option("-q", "--quiet", help="Be quiet", action="store_true"),
129 common_join_options = [
130 Option("--server", help="DC to join", type=str),
131 Option("--site", help="site to join", type=str),
132 Option("--domain-critical-only",
133 help="only replicate critical domain objects",
134 action="store_true"),
135 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
136 choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
137 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
138 "BIND9_DLZ uses samba4 AD to store zone information, "
139 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
140 default="SAMBA_INTERNAL"),
141 Option("-v", "--verbose", help="Be verbose", action="store_true")
144 common_ntvfs_options = [
145 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
150 def get_testparm_var(testparm, smbconf, varname):
151 errfile = open(os.devnull, 'w')
152 p = subprocess.Popen([testparm, '-s', '-l',
153 '--parameter-name=%s' % varname, smbconf],
154 stdout=subprocess.PIPE, stderr=errfile)
155 (out, err) = p.communicate()
157 lines = out.split(b'\n')
159 return get_string(lines[0]).strip()
164 import samba.dckeytab
166 cmd_domain_export_keytab = None
168 class cmd_domain_export_keytab(Command):
169 """Dump Kerberos keys of the domain into a keytab."""
171 synopsis = "%prog <keytab> [options]"
173 takes_optiongroups = {
174 "sambaopts": options.SambaOptions,
175 "credopts": options.CredentialsOptions,
176 "versionopts": options.VersionOptions,
180 Option("--principal", help="extract only this principal", type=str),
183 takes_args = ["keytab"]
185 def run(self, keytab, credopts=None, sambaopts=None, versionopts=None, principal=None):
186 lp = sambaopts.get_loadparm()
188 net.export_keytab(keytab=keytab, principal=principal)
191 class cmd_domain_info(Command):
192 """Print basic info about a domain and the DC passed as parameter."""
194 synopsis = "%prog <ip_address> [options]"
199 takes_optiongroups = {
200 "sambaopts": options.SambaOptions,
201 "credopts": options.CredentialsOptions,
202 "versionopts": options.VersionOptions,
205 takes_args = ["address"]
207 def run(self, address, credopts=None, sambaopts=None, versionopts=None):
208 lp = sambaopts.get_loadparm()
210 res = netcmd_get_domain_infos_via_cldap(lp, None, address)
212 raise CommandError("Invalid IP address '" + address + "'!")
213 self.outf.write("Forest : %s\n" % res.forest)
214 self.outf.write("Domain : %s\n" % res.dns_domain)
215 self.outf.write("Netbios domain : %s\n" % res.domain_name)
216 self.outf.write("DC name : %s\n" % res.pdc_dns_name)
217 self.outf.write("DC netbios name : %s\n" % res.pdc_name)
218 self.outf.write("Server site : %s\n" % res.server_site)
219 self.outf.write("Client site : %s\n" % res.client_site)
222 class cmd_domain_provision(Command):
223 """Provision a domain."""
225 synopsis = "%prog [options]"
227 takes_optiongroups = {
228 "sambaopts": options.SambaOptions,
229 "versionopts": options.VersionOptions,
233 Option("--interactive", help="Ask for names", action="store_true"),
234 Option("--domain", type="string", metavar="DOMAIN",
235 help="NetBIOS domain name to use"),
236 Option("--domain-guid", type="string", metavar="GUID",
237 help="set domainguid (otherwise random)"),
238 Option("--domain-sid", type="string", metavar="SID",
239 help="set domainsid (otherwise random)"),
240 Option("--ntds-guid", type="string", metavar="GUID",
241 help="set NTDS object GUID (otherwise random)"),
242 Option("--invocationid", type="string", metavar="GUID",
243 help="set invocationid (otherwise random)"),
244 Option("--host-name", type="string", metavar="HOSTNAME",
245 help="set hostname"),
246 Option("--host-ip", type="string", metavar="IPADDRESS",
247 help="set IPv4 ipaddress"),
248 Option("--host-ip6", type="string", metavar="IP6ADDRESS",
249 help="set IPv6 ipaddress"),
250 Option("--site", type="string", metavar="SITENAME",
251 help="set site name"),
252 Option("--adminpass", type="string", metavar="PASSWORD",
253 help="choose admin password (otherwise random)"),
254 Option("--krbtgtpass", type="string", metavar="PASSWORD",
255 help="choose krbtgt password (otherwise random)"),
256 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
257 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
258 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
259 "BIND9_FLATFILE uses bind9 text database to store zone information, "
260 "BIND9_DLZ uses samba4 AD to store zone information, "
261 "NONE skips the DNS setup entirely (not recommended)",
262 default="SAMBA_INTERNAL"),
263 Option("--dnspass", type="string", metavar="PASSWORD",
264 help="choose dns password (otherwise random)"),
265 Option("--root", type="string", metavar="USERNAME",
266 help="choose 'root' unix username"),
267 Option("--nobody", type="string", metavar="USERNAME",
268 help="choose 'nobody' user"),
269 Option("--users", type="string", metavar="GROUPNAME",
270 help="choose 'users' group"),
271 Option("--blank", action="store_true",
272 help="do not add users or groups, just the structure"),
273 Option("--server-role", type="choice", metavar="ROLE",
274 choices=["domain controller", "dc", "member server", "member", "standalone"],
275 help="The server role (domain controller | dc | member server | member | standalone). Default is dc.",
276 default="domain controller"),
277 Option("--function-level", type="choice", metavar="FOR-FUN-LEVEL",
278 choices=["2000", "2003", "2008", "2008_R2"],
279 help="The domain and forest function level (2000 | 2003 | 2008 | 2008_R2 - always native). Default is (Windows) 2008_R2 Native.",
281 Option("--base-schema", type="choice", metavar="BASE-SCHEMA",
282 choices=["2008_R2", "2008_R2_old", "2012", "2012_R2"],
283 help="The base schema files to use. Default is (Windows) 2008_R2.",
285 Option("--next-rid", type="int", metavar="NEXTRID", default=1000,
286 help="The initial nextRid value (only needed for upgrades). Default is 1000."),
287 Option("--partitions-only",
288 help="Configure Samba's partitions, but do not modify them (ie, join a BDC)", action="store_true"),
289 Option("--use-rfc2307", action="store_true", help="Use AD to store posix attributes (default = no)"),
293 Option("--ldapadminpass", type="string", metavar="PASSWORD",
294 help="choose password to set between Samba and its LDAP backend (otherwise random)"),
295 Option("--ldap-backend-type", type="choice", metavar="LDAP-BACKEND-TYPE",
296 help="Test initialisation support for unsupported LDAP backend type (fedora-ds or openldap) DO NOT USE",
297 choices=["fedora-ds", "openldap"]),
298 Option("--ol-mmr-urls", type="string", metavar="LDAPSERVER",
299 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\""),
300 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",
301 action="store_true"),
302 Option("--slapd-path", type="string", metavar="SLAPD-PATH",
303 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."),
304 Option("--ldap-backend-extra-port", type="int", metavar="LDAP-BACKEND-EXTRA-PORT", help="Additional TCP port for LDAP backend server (to use for replication)"),
305 Option("--ldap-backend-forced-uri", type="string", metavar="LDAP-BACKEND-FORCED-URI",
306 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"),
307 Option("--ldap-backend-nosync", help="Configure LDAP backend not to call fsync() (for performance in test environments)", action="store_true"),
311 Option("--use-xattrs", type="choice", choices=["yes", "no", "auto"],
312 metavar="[yes|no|auto]",
313 help="Define if we should use the native fs capabilities or a tdb file for "
314 "storing attributes likes ntacl when --use-ntvfs is set. "
315 "auto tries to make an inteligent guess based on the user rights and system capabilities",
319 takes_options.extend(common_provision_join_options)
321 if os.getenv('TEST_LDAP', "no") == "yes":
322 takes_options.extend(openldap_options)
324 if samba.is_ntvfs_fileserver_built():
325 takes_options.extend(common_ntvfs_options)
326 takes_options.extend(ntvfs_options)
330 def run(self, sambaopts=None, versionopts=None,
353 ldap_backend_type=None,
357 partitions_only=None,
364 ldap_backend_nosync=None,
365 ldap_backend_extra_port=None,
366 ldap_backend_forced_uri=None,
367 ldap_dryrun_mode=None,
369 plaintext_secrets=False,
372 self.logger = self.get_logger(name="provision", quiet=quiet)
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"""
572 if isinstance(adminpass, binary_type):
573 adminpass = adminpass.decode('utf8')
574 if len(adminpass) < DEFAULT_MIN_PWD_LENGTH:
575 return "Administrator password does not meet the default minimum" \
576 " password length requirement (%d characters)" \
577 % DEFAULT_MIN_PWD_LENGTH
578 elif not samba.check_password_quality(adminpass):
579 return "Administrator password does not meet the default" \
585 class cmd_domain_dcpromo(Command):
586 """Promote an existing domain member or NT4 PDC to an AD DC."""
588 synopsis = "%prog <dnsdomain> [DC|RODC] [options]"
590 takes_optiongroups = {
591 "sambaopts": options.SambaOptions,
592 "versionopts": options.VersionOptions,
593 "credopts": options.CredentialsOptions,
597 takes_options.extend(common_join_options)
599 takes_options.extend(common_provision_join_options)
601 if samba.is_ntvfs_fileserver_built():
602 takes_options.extend(common_ntvfs_options)
604 takes_args = ["domain", "role?"]
606 def run(self, domain, role=None, sambaopts=None, credopts=None,
607 versionopts=None, server=None, site=None, targetdir=None,
608 domain_critical_only=False, parent_domain=None, machinepass=None,
609 use_ntvfs=False, dns_backend=None,
610 quiet=False, verbose=False, plaintext_secrets=False,
612 lp = sambaopts.get_loadparm()
613 creds = credopts.get_credentials(lp)
614 net = Net(creds, lp, server=credopts.ipaddress)
616 logger = self.get_logger(verbose=verbose, quiet=quiet)
618 netbios_name = lp.get("netbios name")
624 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
625 site=site, netbios_name=netbios_name, targetdir=targetdir,
626 domain_critical_only=domain_critical_only,
627 machinepass=machinepass, use_ntvfs=use_ntvfs,
628 dns_backend=dns_backend,
629 promote_existing=True, plaintext_secrets=plaintext_secrets,
630 backend_store=backend_store)
632 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
633 site=site, netbios_name=netbios_name, targetdir=targetdir,
634 domain_critical_only=domain_critical_only,
635 machinepass=machinepass, use_ntvfs=use_ntvfs, dns_backend=dns_backend,
636 promote_existing=True, plaintext_secrets=plaintext_secrets,
637 backend_store=backend_store)
639 raise CommandError("Invalid role '%s' (possible values: DC, RODC)" % role)
642 class cmd_domain_join(Command):
643 """Join domain as either member or backup domain controller."""
645 synopsis = "%prog <dnsdomain> [DC|RODC|MEMBER|SUBDOMAIN] [options]"
647 takes_optiongroups = {
648 "sambaopts": options.SambaOptions,
649 "versionopts": options.VersionOptions,
650 "credopts": options.CredentialsOptions,
654 Option("--parent-domain", help="parent domain to create subdomain under", type=str),
655 Option("--adminpass", type="string", metavar="PASSWORD",
656 help="choose adminstrator password when joining as a subdomain (otherwise random)"),
660 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
663 takes_options.extend(common_join_options)
664 takes_options.extend(common_provision_join_options)
666 if samba.is_ntvfs_fileserver_built():
667 takes_options.extend(ntvfs_options)
669 takes_args = ["domain", "role?"]
671 def run(self, domain, role=None, sambaopts=None, credopts=None,
672 versionopts=None, server=None, site=None, targetdir=None,
673 domain_critical_only=False, parent_domain=None, machinepass=None,
674 use_ntvfs=False, dns_backend=None, adminpass=None,
675 quiet=False, verbose=False,
676 plaintext_secrets=False,
678 lp = sambaopts.get_loadparm()
679 creds = credopts.get_credentials(lp)
680 net = Net(creds, lp, server=credopts.ipaddress)
682 logger = self.get_logger(verbose=verbose, quiet=quiet)
684 netbios_name = lp.get("netbios name")
689 if role is None or role == "MEMBER":
690 (join_password, sid, domain_name) = net.join_member(
691 domain, netbios_name, LIBNET_JOIN_AUTOMATIC,
692 machinepass=machinepass)
694 self.errf.write("Joined domain %s (%s)\n" % (domain_name, sid))
696 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
697 site=site, netbios_name=netbios_name, targetdir=targetdir,
698 domain_critical_only=domain_critical_only,
699 machinepass=machinepass, use_ntvfs=use_ntvfs,
700 dns_backend=dns_backend,
701 plaintext_secrets=plaintext_secrets,
702 backend_store=backend_store)
704 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
705 site=site, netbios_name=netbios_name, targetdir=targetdir,
706 domain_critical_only=domain_critical_only,
707 machinepass=machinepass, use_ntvfs=use_ntvfs,
708 dns_backend=dns_backend,
709 plaintext_secrets=plaintext_secrets,
710 backend_store=backend_store)
711 elif role == "SUBDOMAIN":
713 logger.info("Administrator password will be set randomly!")
715 netbios_domain = lp.get("workgroup")
716 if parent_domain is None:
717 parent_domain = ".".join(domain.split(".")[1:])
718 join_subdomain(logger=logger, server=server, creds=creds, lp=lp, dnsdomain=domain,
719 parent_domain=parent_domain, site=site,
720 netbios_name=netbios_name, netbios_domain=netbios_domain,
721 targetdir=targetdir, machinepass=machinepass,
722 use_ntvfs=use_ntvfs, dns_backend=dns_backend,
724 plaintext_secrets=plaintext_secrets,
725 backend_store=backend_store)
727 raise CommandError("Invalid role '%s' (possible values: MEMBER, DC, RODC, SUBDOMAIN)" % role)
730 class cmd_domain_demote(Command):
731 """Demote ourselves from the role of Domain Controller."""
733 synopsis = "%prog [options]"
736 Option("--server", help="writable DC to write demotion changes on", type=str),
737 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
738 metavar="URL", dest="H"),
739 Option("--remove-other-dead-server", help="Dead DC (name or NTDS GUID) "
740 "to remove ALL references to (rather than this DC)", type=str),
741 Option("-q", "--quiet", help="Be quiet", action="store_true"),
742 Option("-v", "--verbose", help="Be verbose", action="store_true"),
745 takes_optiongroups = {
746 "sambaopts": options.SambaOptions,
747 "credopts": options.CredentialsOptions,
748 "versionopts": options.VersionOptions,
751 def run(self, sambaopts=None, credopts=None,
752 versionopts=None, server=None,
753 remove_other_dead_server=None, H=None,
754 verbose=False, quiet=False):
755 lp = sambaopts.get_loadparm()
756 creds = credopts.get_credentials(lp)
757 net = Net(creds, lp, server=credopts.ipaddress)
759 logger = self.get_logger(verbose=verbose, quiet=quiet)
761 if remove_other_dead_server is not None:
762 if server is not None:
763 samdb = SamDB(url="ldap://%s" % server,
764 session_info=system_session(),
765 credentials=creds, lp=lp)
767 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
769 remove_dc.remove_dc(samdb, logger, remove_other_dead_server)
770 except remove_dc.DemoteException as err:
771 raise CommandError("Demote failed: %s" % err)
774 netbios_name = lp.get("netbios name")
775 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
777 res = samdb.search(expression='(&(objectClass=computer)(serverReferenceBL=*))', attrs=["dnsHostName", "name"])
779 raise CommandError("Unable to search for servers")
782 raise CommandError("You are the last server in the domain")
786 if str(e["name"]).lower() != netbios_name.lower():
787 server = e["dnsHostName"]
790 ntds_guid = samdb.get_ntds_GUID()
791 msg = samdb.search(base=str(samdb.get_config_basedn()),
792 scope=ldb.SCOPE_SUBTREE, expression="(objectGUID=%s)" % ntds_guid,
794 if len(msg) == 0 or "options" not in msg[0]:
795 raise CommandError("Failed to find options on %s" % ntds_guid)
798 dsa_options = int(str(msg[0]['options']))
800 res = samdb.search(expression="(fSMORoleOwner=%s)" % str(ntds_dn),
801 controls=["search_options:1:2"])
804 raise CommandError("Current DC is still the owner of %d role(s), "
805 "use the role command to transfer roles to "
809 self.errf.write("Using %s as partner server for the demotion\n" %
811 (drsuapiBind, drsuapi_handle, supportedExtensions) = drsuapi_connect(server, lp, creds)
813 self.errf.write("Deactivating inbound replication\n")
818 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
819 dsa_options |= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
820 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
823 self.errf.write("Asking partner server %s to synchronize from us\n"
825 for part in (samdb.get_schema_basedn(),
826 samdb.get_config_basedn(),
827 samdb.get_root_basedn()):
828 nc = drsuapi.DsReplicaObjectIdentifier()
831 req1 = drsuapi.DsReplicaSyncRequest1()
832 req1.naming_context = nc
833 req1.options = drsuapi.DRSUAPI_DRS_WRIT_REP
834 req1.source_dsa_guid = misc.GUID(ntds_guid)
837 drsuapiBind.DsReplicaSync(drsuapi_handle, 1, req1)
838 except RuntimeError as e1:
839 (werr, string) = e1.args
840 if werr == werror.WERR_DS_DRA_NO_REPLICA:
844 "Error while replicating out last local changes from '%s' for demotion, "
845 "re-enabling inbound replication\n" % part)
846 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
847 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
849 raise CommandError("Error while sending a DsReplicaSync for partition '%s'" % str(part), string)
851 remote_samdb = SamDB(url="ldap://%s" % server,
852 session_info=system_session(),
853 credentials=creds, lp=lp)
855 self.errf.write("Changing userControl and container\n")
856 res = remote_samdb.search(base=str(remote_samdb.domain_dn()),
857 expression="(&(objectClass=user)(sAMAccountName=%s$))" %
858 netbios_name.upper(),
859 attrs=["userAccountControl"])
861 uac = int(str(res[0]["userAccountControl"]))
863 except Exception as e:
864 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
866 "Error while demoting, re-enabling inbound replication\n")
867 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
868 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
870 raise CommandError("Error while changing account control", e)
873 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
875 "Error while demoting, re-enabling inbound replication")
876 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
877 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
879 raise CommandError("Unable to find object with samaccountName = %s$"
880 " in the remote dc" % netbios_name.upper())
884 uac &= ~(UF_SERVER_TRUST_ACCOUNT |
885 UF_TRUSTED_FOR_DELEGATION |
886 UF_PARTIAL_SECRETS_ACCOUNT)
887 uac |= UF_WORKSTATION_TRUST_ACCOUNT
892 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
893 ldb.FLAG_MOD_REPLACE,
894 "userAccountControl")
896 remote_samdb.modify(msg)
897 except Exception as e:
898 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
900 "Error while demoting, re-enabling inbound replication")
901 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
902 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
905 raise CommandError("Error while changing account control", e)
907 parent = msg.dn.parent()
908 dc_name = res[0].dn.get_rdn_value()
909 rdn = "CN=%s" % dc_name
911 # Let's move to the Computer container
915 computer_dn = ldb.Dn(remote_samdb, "CN=Computers,%s" % str(remote_samdb.domain_dn()))
916 res = remote_samdb.search(base=computer_dn, expression=rdn, scope=ldb.SCOPE_ONELEVEL)
919 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
920 scope=ldb.SCOPE_ONELEVEL)
921 while(len(res) != 0 and i < 100):
923 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
924 scope=ldb.SCOPE_ONELEVEL)
927 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
929 "Error while demoting, re-enabling inbound replication\n")
930 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
931 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
937 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
938 ldb.FLAG_MOD_REPLACE,
939 "userAccountControl")
941 remote_samdb.modify(msg)
943 raise CommandError("Unable to find a slot for renaming %s,"
944 " all names from %s-1 to %s-%d seemed used" %
945 (str(dc_dn), rdn, rdn, i - 9))
947 newrdn = "%s-%d" % (rdn, i)
950 newdn = ldb.Dn(remote_samdb, "%s,%s" % (newrdn, str(computer_dn)))
951 remote_samdb.rename(dc_dn, newdn)
952 except Exception as e:
953 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
955 "Error while demoting, re-enabling inbound replication\n")
956 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
957 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
963 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
964 ldb.FLAG_MOD_REPLACE,
965 "userAccountControl")
967 remote_samdb.modify(msg)
968 raise CommandError("Error while renaming %s to %s" % (str(dc_dn), str(newdn)), e)
970 server_dsa_dn = samdb.get_serverName()
971 domain = remote_samdb.get_root_basedn()
974 req1 = drsuapi.DsRemoveDSServerRequest1()
975 req1.server_dn = str(server_dsa_dn)
976 req1.domain_dn = str(domain)
979 drsuapiBind.DsRemoveDSServer(drsuapi_handle, 1, req1)
980 except RuntimeError as e3:
981 (werr, string) = e3.args
982 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
984 "Error while demoting, re-enabling inbound replication\n")
985 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
986 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
992 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
993 ldb.FLAG_MOD_REPLACE,
994 "userAccountControl")
995 remote_samdb.modify(msg)
996 remote_samdb.rename(newdn, dc_dn)
997 if werr == werror.WERR_DS_DRA_NO_REPLICA:
998 raise CommandError("The DC %s is not present on (already "
999 "removed from) the remote server: %s" %
1000 (server_dsa_dn, e3))
1002 raise CommandError("Error while sending a removeDsServer "
1004 (server_dsa_dn, e3))
1006 remove_dc.remove_sysvol_references(remote_samdb, logger, dc_name)
1008 # These are objects under the computer account that should be deleted
1009 for s in ("CN=Enterprise,CN=NTFRS Subscriptions",
1010 "CN=%s, CN=NTFRS Subscriptions" % lp.get("realm"),
1011 "CN=Domain system Volumes (SYSVOL Share), CN=NTFRS Subscriptions",
1012 "CN=NTFRS Subscriptions"):
1014 remote_samdb.delete(ldb.Dn(remote_samdb,
1015 "%s,%s" % (s, str(newdn))))
1016 except ldb.LdbError as l:
1019 # get dns host name for target server to demote, remove dns references
1020 remove_dc.remove_dns_references(remote_samdb, logger, samdb.host_dns_name(),
1021 ignore_no_name=True)
1023 self.errf.write("Demote successful\n")
1026 class cmd_domain_level(Command):
1027 """Raise domain and forest function levels."""
1029 synopsis = "%prog (show|raise <options>) [options]"
1031 takes_optiongroups = {
1032 "sambaopts": options.SambaOptions,
1033 "credopts": options.CredentialsOptions,
1034 "versionopts": options.VersionOptions,
1038 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1039 metavar="URL", dest="H"),
1040 Option("-q", "--quiet", help="Be quiet", action="store_true"), # unused
1041 Option("--forest-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
1042 help="The forest function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)"),
1043 Option("--domain-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
1044 help="The domain function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)")
1047 takes_args = ["subcommand"]
1049 def run(self, subcommand, H=None, forest_level=None, domain_level=None,
1050 quiet=False, credopts=None, sambaopts=None, versionopts=None):
1051 lp = sambaopts.get_loadparm()
1052 creds = credopts.get_credentials(lp, fallback_machine=True)
1054 samdb = SamDB(url=H, session_info=system_session(),
1055 credentials=creds, lp=lp)
1057 domain_dn = samdb.domain_dn()
1059 res_forest = samdb.search("CN=Partitions,%s" % samdb.get_config_basedn(),
1060 scope=ldb.SCOPE_BASE, attrs=["msDS-Behavior-Version"])
1061 assert len(res_forest) == 1
1063 res_domain = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1064 attrs=["msDS-Behavior-Version", "nTMixedDomain"])
1065 assert len(res_domain) == 1
1067 res_dc_s = samdb.search("CN=Sites,%s" % samdb.get_config_basedn(),
1068 scope=ldb.SCOPE_SUBTREE, expression="(objectClass=nTDSDSA)",
1069 attrs=["msDS-Behavior-Version"])
1070 assert len(res_dc_s) >= 1
1072 # default values, since "msDS-Behavior-Version" does not exist on Windows 2000 AD
1073 level_forest = DS_DOMAIN_FUNCTION_2000
1074 level_domain = DS_DOMAIN_FUNCTION_2000
1076 if "msDS-Behavior-Version" in res_forest[0]:
1077 level_forest = int(res_forest[0]["msDS-Behavior-Version"][0])
1078 if "msDS-Behavior-Version" in res_domain[0]:
1079 level_domain = int(res_domain[0]["msDS-Behavior-Version"][0])
1080 level_domain_mixed = int(res_domain[0]["nTMixedDomain"][0])
1083 for msg in res_dc_s:
1084 if "msDS-Behavior-Version" in msg:
1085 if min_level_dc is None or int(msg["msDS-Behavior-Version"][0]) < min_level_dc:
1086 min_level_dc = int(msg["msDS-Behavior-Version"][0])
1088 min_level_dc = DS_DOMAIN_FUNCTION_2000
1089 # well, this is the least
1092 if level_forest < DS_DOMAIN_FUNCTION_2000 or level_domain < DS_DOMAIN_FUNCTION_2000:
1093 raise CommandError("Domain and/or forest function level(s) is/are invalid. Correct them or reprovision!")
1094 if min_level_dc < DS_DOMAIN_FUNCTION_2000:
1095 raise CommandError("Lowest function level of a DC is invalid. Correct this or reprovision!")
1096 if level_forest > level_domain:
1097 raise CommandError("Forest function level is higher than the domain level(s). Correct this or reprovision!")
1098 if level_domain > min_level_dc:
1099 raise CommandError("Domain function level is higher than the lowest function level of a DC. Correct this or reprovision!")
1101 if subcommand == "show":
1102 self.message("Domain and forest function level for domain '%s'" % domain_dn)
1103 if level_forest == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1104 self.message("\nATTENTION: You run SAMBA 4 on a forest function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1105 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1106 self.message("\nATTENTION: You run SAMBA 4 on a domain function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1107 if min_level_dc == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1108 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)!")
1112 if level_forest == DS_DOMAIN_FUNCTION_2000:
1114 elif level_forest == DS_DOMAIN_FUNCTION_2003_MIXED:
1115 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1116 elif level_forest == DS_DOMAIN_FUNCTION_2003:
1118 elif level_forest == DS_DOMAIN_FUNCTION_2008:
1120 elif level_forest == DS_DOMAIN_FUNCTION_2008_R2:
1122 elif level_forest == DS_DOMAIN_FUNCTION_2012:
1124 elif level_forest == DS_DOMAIN_FUNCTION_2012_R2:
1127 outstr = "higher than 2012 R2"
1128 self.message("Forest function level: (Windows) " + outstr)
1130 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1131 outstr = "2000 mixed (NT4 DC support)"
1132 elif level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed == 0:
1134 elif level_domain == DS_DOMAIN_FUNCTION_2003_MIXED:
1135 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1136 elif level_domain == DS_DOMAIN_FUNCTION_2003:
1138 elif level_domain == DS_DOMAIN_FUNCTION_2008:
1140 elif level_domain == DS_DOMAIN_FUNCTION_2008_R2:
1142 elif level_domain == DS_DOMAIN_FUNCTION_2012:
1144 elif level_domain == DS_DOMAIN_FUNCTION_2012_R2:
1147 outstr = "higher than 2012 R2"
1148 self.message("Domain function level: (Windows) " + outstr)
1150 if min_level_dc == DS_DOMAIN_FUNCTION_2000:
1152 elif min_level_dc == DS_DOMAIN_FUNCTION_2003:
1154 elif min_level_dc == DS_DOMAIN_FUNCTION_2008:
1156 elif min_level_dc == DS_DOMAIN_FUNCTION_2008_R2:
1158 elif min_level_dc == DS_DOMAIN_FUNCTION_2012:
1160 elif min_level_dc == DS_DOMAIN_FUNCTION_2012_R2:
1163 outstr = "higher than 2012 R2"
1164 self.message("Lowest function level of a DC: (Windows) " + outstr)
1166 elif subcommand == "raise":
1169 if domain_level is not None:
1170 if domain_level == "2003":
1171 new_level_domain = DS_DOMAIN_FUNCTION_2003
1172 elif domain_level == "2008":
1173 new_level_domain = DS_DOMAIN_FUNCTION_2008
1174 elif domain_level == "2008_R2":
1175 new_level_domain = DS_DOMAIN_FUNCTION_2008_R2
1176 elif domain_level == "2012":
1177 new_level_domain = DS_DOMAIN_FUNCTION_2012
1178 elif domain_level == "2012_R2":
1179 new_level_domain = DS_DOMAIN_FUNCTION_2012_R2
1181 if new_level_domain <= level_domain and level_domain_mixed == 0:
1182 raise CommandError("Domain function level can't be smaller than or equal to the actual one!")
1183 if new_level_domain > min_level_dc:
1184 raise CommandError("Domain function level can't be higher than the lowest function level of a DC!")
1186 # Deactivate mixed/interim domain support
1187 if level_domain_mixed != 0:
1188 # Directly on the base DN
1190 m.dn = ldb.Dn(samdb, domain_dn)
1191 m["nTMixedDomain"] = ldb.MessageElement("0",
1192 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1196 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup") + ",CN=Partitions,%s" % samdb.get_config_basedn())
1197 m["nTMixedDomain"] = ldb.MessageElement("0",
1198 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1201 except ldb.LdbError as e:
1202 (enum, emsg) = e.args
1203 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1206 # Directly on the base DN
1208 m.dn = ldb.Dn(samdb, domain_dn)
1209 m["msDS-Behavior-Version"] = ldb.MessageElement(
1210 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1211 "msDS-Behavior-Version")
1215 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup")
1216 + ",CN=Partitions,%s" % samdb.get_config_basedn())
1217 m["msDS-Behavior-Version"] = ldb.MessageElement(
1218 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1219 "msDS-Behavior-Version")
1222 except ldb.LdbError as e2:
1223 (enum, emsg) = e2.args
1224 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1227 level_domain = new_level_domain
1228 msgs.append("Domain function level changed!")
1230 if forest_level is not None:
1231 if forest_level == "2003":
1232 new_level_forest = DS_DOMAIN_FUNCTION_2003
1233 elif forest_level == "2008":
1234 new_level_forest = DS_DOMAIN_FUNCTION_2008
1235 elif forest_level == "2008_R2":
1236 new_level_forest = DS_DOMAIN_FUNCTION_2008_R2
1237 elif forest_level == "2012":
1238 new_level_forest = DS_DOMAIN_FUNCTION_2012
1239 elif forest_level == "2012_R2":
1240 new_level_forest = DS_DOMAIN_FUNCTION_2012_R2
1242 if new_level_forest <= level_forest:
1243 raise CommandError("Forest function level can't be smaller than or equal to the actual one!")
1244 if new_level_forest > level_domain:
1245 raise CommandError("Forest function level can't be higher than the domain function level(s). Please raise it/them first!")
1248 m.dn = ldb.Dn(samdb, "CN=Partitions,%s" % samdb.get_config_basedn())
1249 m["msDS-Behavior-Version"] = ldb.MessageElement(
1250 str(new_level_forest), ldb.FLAG_MOD_REPLACE,
1251 "msDS-Behavior-Version")
1253 msgs.append("Forest function level changed!")
1254 msgs.append("All changes applied successfully!")
1255 self.message("\n".join(msgs))
1257 raise CommandError("invalid argument: '%s' (choose from 'show', 'raise')" % subcommand)
1260 class cmd_domain_passwordsettings_show(Command):
1261 """Display current password settings for the domain."""
1263 synopsis = "%prog [options]"
1265 takes_optiongroups = {
1266 "sambaopts": options.SambaOptions,
1267 "versionopts": options.VersionOptions,
1268 "credopts": options.CredentialsOptions,
1272 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1273 metavar="URL", dest="H"),
1276 def run(self, H=None, credopts=None, sambaopts=None, versionopts=None):
1277 lp = sambaopts.get_loadparm()
1278 creds = credopts.get_credentials(lp)
1280 samdb = SamDB(url=H, session_info=system_session(),
1281 credentials=creds, lp=lp)
1283 domain_dn = samdb.domain_dn()
1284 res = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1285 attrs=["pwdProperties", "pwdHistoryLength", "minPwdLength",
1286 "minPwdAge", "maxPwdAge", "lockoutDuration", "lockoutThreshold",
1287 "lockOutObservationWindow"])
1288 assert(len(res) == 1)
1290 pwd_props = int(res[0]["pwdProperties"][0])
1291 pwd_hist_len = int(res[0]["pwdHistoryLength"][0])
1292 cur_min_pwd_len = int(res[0]["minPwdLength"][0])
1294 cur_min_pwd_age = int(abs(int(res[0]["minPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1295 if int(res[0]["maxPwdAge"][0]) == -0x8000000000000000:
1298 cur_max_pwd_age = int(abs(int(res[0]["maxPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1299 cur_account_lockout_threshold = int(res[0]["lockoutThreshold"][0])
1301 if int(res[0]["lockoutDuration"][0]) == -0x8000000000000000:
1302 cur_account_lockout_duration = 0
1304 cur_account_lockout_duration = abs(int(res[0]["lockoutDuration"][0])) / (1e7 * 60)
1305 cur_reset_account_lockout_after = abs(int(res[0]["lockOutObservationWindow"][0])) / (1e7 * 60)
1306 except Exception as e:
1307 raise CommandError("Could not retrieve password properties!", e)
1309 self.message("Password informations for domain '%s'" % domain_dn)
1311 if pwd_props & DOMAIN_PASSWORD_COMPLEX != 0:
1312 self.message("Password complexity: on")
1314 self.message("Password complexity: off")
1315 if pwd_props & DOMAIN_PASSWORD_STORE_CLEARTEXT != 0:
1316 self.message("Store plaintext passwords: on")
1318 self.message("Store plaintext passwords: off")
1319 self.message("Password history length: %d" % pwd_hist_len)
1320 self.message("Minimum password length: %d" % cur_min_pwd_len)
1321 self.message("Minimum password age (days): %d" % cur_min_pwd_age)
1322 self.message("Maximum password age (days): %d" % cur_max_pwd_age)
1323 self.message("Account lockout duration (mins): %d" % cur_account_lockout_duration)
1324 self.message("Account lockout threshold (attempts): %d" % cur_account_lockout_threshold)
1325 self.message("Reset account lockout after (mins): %d" % cur_reset_account_lockout_after)
1328 class cmd_domain_passwordsettings_set(Command):
1329 """Set password settings.
1331 Password complexity, password lockout policy, history length,
1332 minimum password length, the minimum and maximum password age) on
1333 a Samba AD DC server.
1335 Use against a Windows DC is possible, but group policy will override it.
1338 synopsis = "%prog <options> [options]"
1340 takes_optiongroups = {
1341 "sambaopts": options.SambaOptions,
1342 "versionopts": options.VersionOptions,
1343 "credopts": options.CredentialsOptions,
1347 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1348 metavar="URL", dest="H"),
1349 Option("-q", "--quiet", help="Be quiet", action="store_true"), # unused
1350 Option("--complexity", type="choice", choices=["on", "off", "default"],
1351 help="The password complexity (on | off | default). Default is 'on'"),
1352 Option("--store-plaintext", type="choice", choices=["on", "off", "default"],
1353 help="Store plaintext passwords where account have 'store passwords with reversible encryption' set (on | off | default). Default is 'off'"),
1354 Option("--history-length",
1355 help="The password history length (<integer> | default). Default is 24.", type=str),
1356 Option("--min-pwd-length",
1357 help="The minimum password length (<integer> | default). Default is 7.", type=str),
1358 Option("--min-pwd-age",
1359 help="The minimum password age (<integer in days> | default). Default is 1.", type=str),
1360 Option("--max-pwd-age",
1361 help="The maximum password age (<integer in days> | default). Default is 43.", type=str),
1362 Option("--account-lockout-duration",
1363 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),
1364 Option("--account-lockout-threshold",
1365 help="The number of bad password attempts allowed before locking out the account (<integer> | default). Default is 0 (never lock out).", type=str),
1366 Option("--reset-account-lockout-after",
1367 help="After this time is elapsed, the recorded number of attempts restarts from zero (<integer> | default). Default is 30.", type=str),
1370 def run(self, H=None, min_pwd_age=None, max_pwd_age=None,
1371 quiet=False, complexity=None, store_plaintext=None, history_length=None,
1372 min_pwd_length=None, account_lockout_duration=None, account_lockout_threshold=None,
1373 reset_account_lockout_after=None, credopts=None, sambaopts=None,
1375 lp = sambaopts.get_loadparm()
1376 creds = credopts.get_credentials(lp)
1378 samdb = SamDB(url=H, session_info=system_session(),
1379 credentials=creds, lp=lp)
1381 domain_dn = samdb.domain_dn()
1384 m.dn = ldb.Dn(samdb, domain_dn)
1385 pwd_props = int(samdb.get_pwdProperties())
1387 if complexity is not None:
1388 if complexity == "on" or complexity == "default":
1389 pwd_props = pwd_props | DOMAIN_PASSWORD_COMPLEX
1390 msgs.append("Password complexity activated!")
1391 elif complexity == "off":
1392 pwd_props = pwd_props & (~DOMAIN_PASSWORD_COMPLEX)
1393 msgs.append("Password complexity deactivated!")
1395 if store_plaintext is not None:
1396 if store_plaintext == "on" or store_plaintext == "default":
1397 pwd_props = pwd_props | DOMAIN_PASSWORD_STORE_CLEARTEXT
1398 msgs.append("Plaintext password storage for changed passwords activated!")
1399 elif store_plaintext == "off":
1400 pwd_props = pwd_props & (~DOMAIN_PASSWORD_STORE_CLEARTEXT)
1401 msgs.append("Plaintext password storage for changed passwords deactivated!")
1403 if complexity is not None or store_plaintext is not None:
1404 m["pwdProperties"] = ldb.MessageElement(str(pwd_props),
1405 ldb.FLAG_MOD_REPLACE, "pwdProperties")
1407 if history_length is not None:
1408 if history_length == "default":
1411 pwd_hist_len = int(history_length)
1413 if pwd_hist_len < 0 or pwd_hist_len > 24:
1414 raise CommandError("Password history length must be in the range of 0 to 24!")
1416 m["pwdHistoryLength"] = ldb.MessageElement(str(pwd_hist_len),
1417 ldb.FLAG_MOD_REPLACE, "pwdHistoryLength")
1418 msgs.append("Password history length changed!")
1420 if min_pwd_length is not None:
1421 if min_pwd_length == "default":
1424 min_pwd_len = int(min_pwd_length)
1426 if min_pwd_len < 0 or min_pwd_len > 14:
1427 raise CommandError("Minimum password length must be in the range of 0 to 14!")
1429 m["minPwdLength"] = ldb.MessageElement(str(min_pwd_len),
1430 ldb.FLAG_MOD_REPLACE, "minPwdLength")
1431 msgs.append("Minimum password length changed!")
1433 if min_pwd_age is not None:
1434 if min_pwd_age == "default":
1437 min_pwd_age = int(min_pwd_age)
1439 if min_pwd_age < 0 or min_pwd_age > 998:
1440 raise CommandError("Minimum password age must be in the range of 0 to 998!")
1443 min_pwd_age_ticks = -int(min_pwd_age * (24 * 60 * 60 * 1e7))
1445 m["minPwdAge"] = ldb.MessageElement(str(min_pwd_age_ticks),
1446 ldb.FLAG_MOD_REPLACE, "minPwdAge")
1447 msgs.append("Minimum password age changed!")
1449 if max_pwd_age is not None:
1450 if max_pwd_age == "default":
1453 max_pwd_age = int(max_pwd_age)
1455 if max_pwd_age < 0 or max_pwd_age > 999:
1456 raise CommandError("Maximum password age must be in the range of 0 to 999!")
1459 if max_pwd_age == 0:
1460 max_pwd_age_ticks = -0x8000000000000000
1462 max_pwd_age_ticks = -int(max_pwd_age * (24 * 60 * 60 * 1e7))
1464 m["maxPwdAge"] = ldb.MessageElement(str(max_pwd_age_ticks),
1465 ldb.FLAG_MOD_REPLACE, "maxPwdAge")
1466 msgs.append("Maximum password age changed!")
1468 if account_lockout_duration is not None:
1469 if account_lockout_duration == "default":
1470 account_lockout_duration = 30
1472 account_lockout_duration = int(account_lockout_duration)
1474 if account_lockout_duration < 0 or account_lockout_duration > 99999:
1475 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1478 if account_lockout_duration == 0:
1479 account_lockout_duration_ticks = -0x8000000000000000
1481 account_lockout_duration_ticks = -int(account_lockout_duration * (60 * 1e7))
1483 m["lockoutDuration"] = ldb.MessageElement(str(account_lockout_duration_ticks),
1484 ldb.FLAG_MOD_REPLACE, "lockoutDuration")
1485 msgs.append("Account lockout duration changed!")
1487 if account_lockout_threshold is not None:
1488 if account_lockout_threshold == "default":
1489 account_lockout_threshold = 0
1491 account_lockout_threshold = int(account_lockout_threshold)
1493 m["lockoutThreshold"] = ldb.MessageElement(str(account_lockout_threshold),
1494 ldb.FLAG_MOD_REPLACE, "lockoutThreshold")
1495 msgs.append("Account lockout threshold changed!")
1497 if reset_account_lockout_after is not None:
1498 if reset_account_lockout_after == "default":
1499 reset_account_lockout_after = 30
1501 reset_account_lockout_after = int(reset_account_lockout_after)
1503 if reset_account_lockout_after < 0 or reset_account_lockout_after > 99999:
1504 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1507 if reset_account_lockout_after == 0:
1508 reset_account_lockout_after_ticks = -0x8000000000000000
1510 reset_account_lockout_after_ticks = -int(reset_account_lockout_after * (60 * 1e7))
1512 m["lockOutObservationWindow"] = ldb.MessageElement(str(reset_account_lockout_after_ticks),
1513 ldb.FLAG_MOD_REPLACE, "lockOutObservationWindow")
1514 msgs.append("Duration to reset account lockout after changed!")
1516 if max_pwd_age and max_pwd_age > 0 and min_pwd_age >= max_pwd_age:
1517 raise CommandError("Maximum password age (%d) must be greater than minimum password age (%d)!" % (max_pwd_age, min_pwd_age))
1520 raise CommandError("You must specify at least one option to set. Try --help")
1522 msgs.append("All changes applied successfully!")
1523 self.message("\n".join(msgs))
1526 class cmd_domain_passwordsettings(SuperCommand):
1527 """Manage password policy settings."""
1530 subcommands["pso"] = cmd_domain_passwordsettings_pso()
1531 subcommands["show"] = cmd_domain_passwordsettings_show()
1532 subcommands["set"] = cmd_domain_passwordsettings_set()
1535 class cmd_domain_classicupgrade(Command):
1536 """Upgrade from Samba classic (NT4-like) database to Samba AD DC database.
1538 Specify either a directory with all Samba classic DC databases and state files (with --dbdir) or
1539 the testparm utility from your classic installation (with --testparm).
1542 synopsis = "%prog [options] <classic_smb_conf>"
1544 takes_optiongroups = {
1545 "sambaopts": options.SambaOptions,
1546 "versionopts": options.VersionOptions
1550 Option("--dbdir", type="string", metavar="DIR",
1551 help="Path to samba classic DC database directory"),
1552 Option("--testparm", type="string", metavar="PATH",
1553 help="Path to samba classic DC testparm utility from the previous installation. This allows the default paths of the previous installation to be followed"),
1554 Option("--targetdir", type="string", metavar="DIR",
1555 help="Path prefix where the new Samba 4.0 AD domain should be initialised"),
1556 Option("-q", "--quiet", help="Be quiet", action="store_true"),
1557 Option("-v", "--verbose", help="Be verbose", action="store_true"),
1558 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
1559 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
1560 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
1561 "BIND9_FLATFILE uses bind9 text database to store zone information, "
1562 "BIND9_DLZ uses samba4 AD to store zone information, "
1563 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
1564 default="SAMBA_INTERNAL")
1568 Option("--use-xattrs", type="choice", choices=["yes", "no", "auto"],
1569 metavar="[yes|no|auto]",
1570 help="Define if we should use the native fs capabilities or a tdb file for "
1571 "storing attributes likes ntacl when --use-ntvfs is set. "
1572 "auto tries to make an inteligent guess based on the user rights and system capabilities",
1575 if samba.is_ntvfs_fileserver_built():
1576 takes_options.extend(common_ntvfs_options)
1577 takes_options.extend(ntvfs_options)
1579 takes_args = ["smbconf"]
1581 def run(self, smbconf=None, targetdir=None, dbdir=None, testparm=None,
1582 quiet=False, verbose=False, use_xattrs="auto", sambaopts=None, versionopts=None,
1583 dns_backend=None, use_ntvfs=False):
1585 if not os.path.exists(smbconf):
1586 raise CommandError("File %s does not exist" % smbconf)
1588 if testparm and not os.path.exists(testparm):
1589 raise CommandError("Testparm utility %s does not exist" % testparm)
1591 if dbdir and not os.path.exists(dbdir):
1592 raise CommandError("Directory %s does not exist" % dbdir)
1594 if not dbdir and not testparm:
1595 raise CommandError("Please specify either dbdir or testparm")
1597 logger = self.get_logger(verbose=verbose, quiet=quiet)
1599 if dbdir and testparm:
1600 logger.warning("both dbdir and testparm specified, ignoring dbdir.")
1603 lp = sambaopts.get_loadparm()
1605 s3conf = s3param.get_context()
1608 s3conf.set("realm", sambaopts.realm)
1610 if targetdir is not None:
1611 if not os.path.isdir(targetdir):
1615 if use_xattrs == "yes":
1617 elif use_xattrs == "auto" and use_ntvfs == False:
1619 elif use_ntvfs == False:
1620 raise CommandError("--use-xattrs=no requires --use-ntvfs (not supported for production use). "
1621 "Please re-run with --use-xattrs omitted.")
1622 elif use_xattrs == "auto" and not s3conf.get("posix:eadb"):
1624 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
1626 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
1629 samba.ntacls.setntacl(lp, tmpfile.name,
1630 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
1633 # FIXME: Don't catch all exceptions here
1634 logger.info("You are not root or your system does not support xattr, using tdb backend for attributes. "
1635 "If you intend to use this provision in production, rerun the script as root on a system supporting xattrs.")
1639 # Set correct default values from dbdir or testparm
1642 paths["state directory"] = dbdir
1643 paths["private dir"] = dbdir
1644 paths["lock directory"] = dbdir
1645 paths["smb passwd file"] = dbdir + "/smbpasswd"
1647 paths["state directory"] = get_testparm_var(testparm, smbconf, "state directory")
1648 paths["private dir"] = get_testparm_var(testparm, smbconf, "private dir")
1649 paths["smb passwd file"] = get_testparm_var(testparm, smbconf, "smb passwd file")
1650 paths["lock directory"] = get_testparm_var(testparm, smbconf, "lock directory")
1651 # "testparm" from Samba 3 < 3.4.x is not aware of the parameter
1652 # "state directory", instead make use of "lock directory"
1653 if len(paths["state directory"]) == 0:
1654 paths["state directory"] = paths["lock directory"]
1657 s3conf.set(p, paths[p])
1659 # load smb.conf parameters
1660 logger.info("Reading smb.conf")
1661 s3conf.load(smbconf)
1662 samba3 = Samba3(smbconf, s3conf)
1664 logger.info("Provisioning")
1665 upgrade_from_samba3(samba3, logger, targetdir, session_info=system_session(),
1666 useeadb=eadb, dns_backend=dns_backend, use_ntvfs=use_ntvfs)
1669 class cmd_domain_samba3upgrade(cmd_domain_classicupgrade):
1670 __doc__ = cmd_domain_classicupgrade.__doc__
1672 # This command is present for backwards compatibility only,
1673 # and should not be shown.
1678 class LocalDCCredentialsOptions(options.CredentialsOptions):
1679 def __init__(self, parser):
1680 options.CredentialsOptions.__init__(self, parser, special_name="local-dc")
1683 class DomainTrustCommand(Command):
1684 """List domain trusts."""
1687 Command.__init__(self)
1688 self.local_lp = None
1690 self.local_server = None
1691 self.local_binding_string = None
1692 self.local_creds = None
1694 self.remote_server = None
1695 self.remote_binding_string = None
1696 self.remote_creds = None
1698 def _uint32(self, v):
1699 return ctypes.c_uint32(v).value
1701 def check_runtime_error(self, runtime, val):
1705 err32 = self._uint32(runtime.args[0])
1711 class LocalRuntimeError(CommandError):
1712 def __init__(exception_self, self, runtime, message):
1713 err32 = self._uint32(runtime.args[0])
1714 errstr = runtime.args[1]
1715 msg = "LOCAL_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1716 self.local_server, message, err32, errstr)
1717 CommandError.__init__(exception_self, msg)
1719 class RemoteRuntimeError(CommandError):
1720 def __init__(exception_self, self, runtime, message):
1721 err32 = self._uint32(runtime.args[0])
1722 errstr = runtime.args[1]
1723 msg = "REMOTE_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1724 self.remote_server, message, err32, errstr)
1725 CommandError.__init__(exception_self, msg)
1727 class LocalLdbError(CommandError):
1728 def __init__(exception_self, self, ldb_error, message):
1729 errval = ldb_error.args[0]
1730 errstr = ldb_error.args[1]
1731 msg = "LOCAL_DC[%s]: %s - ERROR(%d) - %s" % (
1732 self.local_server, message, errval, errstr)
1733 CommandError.__init__(exception_self, msg)
1735 def setup_local_server(self, sambaopts, localdcopts):
1736 if self.local_server is not None:
1737 return self.local_server
1739 lp = sambaopts.get_loadparm()
1741 local_server = localdcopts.ipaddress
1742 if local_server is None:
1743 server_role = lp.server_role()
1744 if server_role != "ROLE_ACTIVE_DIRECTORY_DC":
1745 raise CommandError("Invalid server_role %s" % (server_role))
1746 local_server = lp.get('netbios name')
1747 local_transport = "ncalrpc"
1748 local_binding_options = ""
1749 local_binding_options += ",auth_type=ncalrpc_as_system"
1750 local_ldap_url = None
1753 local_transport = "ncacn_np"
1754 local_binding_options = ""
1755 local_ldap_url = "ldap://%s" % local_server
1756 local_creds = localdcopts.get_credentials(lp)
1760 self.local_server = local_server
1761 self.local_binding_string = "%s:%s[%s]" % (local_transport, local_server, local_binding_options)
1762 self.local_ldap_url = local_ldap_url
1763 self.local_creds = local_creds
1764 return self.local_server
1766 def new_local_lsa_connection(self):
1767 return lsa.lsarpc(self.local_binding_string, self.local_lp, self.local_creds)
1769 def new_local_netlogon_connection(self):
1770 return netlogon.netlogon(self.local_binding_string, self.local_lp, self.local_creds)
1772 def new_local_ldap_connection(self):
1773 return SamDB(url=self.local_ldap_url,
1774 session_info=system_session(),
1775 credentials=self.local_creds,
1778 def setup_remote_server(self, credopts, domain,
1780 require_writable=True):
1783 assert require_writable
1785 if self.remote_server is not None:
1786 return self.remote_server
1788 self.remote_server = "__unknown__remote_server__.%s" % domain
1789 assert self.local_server is not None
1791 remote_creds = credopts.get_credentials(self.local_lp)
1792 remote_server = credopts.ipaddress
1793 remote_binding_options = ""
1795 # TODO: we should also support NT4 domains
1796 # we could use local_netlogon.netr_DsRGetDCNameEx2() with the remote domain name
1797 # and delegate NBT or CLDAP to the local netlogon server
1799 remote_net = Net(remote_creds, self.local_lp, server=remote_server)
1800 remote_flags = nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS
1801 if require_writable:
1802 remote_flags |= nbt.NBT_SERVER_WRITABLE
1804 remote_flags |= nbt.NBT_SERVER_PDC
1805 remote_info = remote_net.finddc(flags=remote_flags, domain=domain, address=remote_server)
1806 except NTSTATUSError as error:
1807 raise CommandError("Failed to find a writeable DC for domain '%s': %s" %
1810 raise CommandError("Failed to find a writeable DC for domain '%s'" % domain)
1812 nbt.NBT_SERVER_PDC: "PDC",
1813 nbt.NBT_SERVER_GC: "GC",
1814 nbt.NBT_SERVER_LDAP: "LDAP",
1815 nbt.NBT_SERVER_DS: "DS",
1816 nbt.NBT_SERVER_KDC: "KDC",
1817 nbt.NBT_SERVER_TIMESERV: "TIMESERV",
1818 nbt.NBT_SERVER_CLOSEST: "CLOSEST",
1819 nbt.NBT_SERVER_WRITABLE: "WRITABLE",
1820 nbt.NBT_SERVER_GOOD_TIMESERV: "GOOD_TIMESERV",
1821 nbt.NBT_SERVER_NDNC: "NDNC",
1822 nbt.NBT_SERVER_SELECT_SECRET_DOMAIN_6: "SELECT_SECRET_DOMAIN_6",
1823 nbt.NBT_SERVER_FULL_SECRET_DOMAIN_6: "FULL_SECRET_DOMAIN_6",
1824 nbt.NBT_SERVER_ADS_WEB_SERVICE: "ADS_WEB_SERVICE",
1825 nbt.NBT_SERVER_DS_8: "DS_8",
1826 nbt.NBT_SERVER_HAS_DNS_NAME: "HAS_DNS_NAME",
1827 nbt.NBT_SERVER_IS_DEFAULT_NC: "IS_DEFAULT_NC",
1828 nbt.NBT_SERVER_FOREST_ROOT: "FOREST_ROOT",
1830 server_type_string = self.generic_bitmap_to_string(flag_map,
1831 remote_info.server_type, names_only=True)
1832 self.outf.write("RemoteDC Netbios[%s] DNS[%s] ServerType[%s]\n" % (
1833 remote_info.pdc_name,
1834 remote_info.pdc_dns_name,
1835 server_type_string))
1837 self.remote_server = remote_info.pdc_dns_name
1838 self.remote_binding_string = "ncacn_np:%s[%s]" % (self.remote_server, remote_binding_options)
1839 self.remote_creds = remote_creds
1840 return self.remote_server
1842 def new_remote_lsa_connection(self):
1843 return lsa.lsarpc(self.remote_binding_string, self.local_lp, self.remote_creds)
1845 def new_remote_netlogon_connection(self):
1846 return netlogon.netlogon(self.remote_binding_string, self.local_lp, self.remote_creds)
1848 def get_lsa_info(self, conn, policy_access):
1849 objectAttr = lsa.ObjectAttribute()
1850 objectAttr.sec_qos = lsa.QosInfo()
1852 policy = conn.OpenPolicy2(b''.decode('utf-8'),
1853 objectAttr, policy_access)
1855 info = conn.QueryInfoPolicy2(policy, lsa.LSA_POLICY_INFO_DNS)
1857 return (policy, info)
1859 def get_netlogon_dc_unc(self, conn, server, domain):
1861 info = conn.netr_DsRGetDCNameEx2(server,
1862 None, 0, None, None, None,
1863 netlogon.DS_RETURN_DNS_NAME)
1865 except RuntimeError:
1866 return conn.netr_GetDcName(server, domain)
1868 def get_netlogon_dc_info(self, conn, server):
1869 info = conn.netr_DsRGetDCNameEx2(server,
1870 None, 0, None, None, None,
1871 netlogon.DS_RETURN_DNS_NAME)
1874 def netr_DomainTrust_to_name(self, t):
1875 if t.trust_type == lsa.LSA_TRUST_TYPE_DOWNLEVEL:
1876 return t.netbios_name
1880 def netr_DomainTrust_to_type(self, a, t):
1882 primary_parent = None
1884 if _t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
1886 if not _t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1887 primary_parent = a[_t.parent_index]
1890 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1891 if t is primary_parent:
1894 if t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1897 parent = a[t.parent_index]
1898 if parent is primary:
1903 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1908 def netr_DomainTrust_to_transitive(self, t):
1909 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1912 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE:
1915 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1920 def netr_DomainTrust_to_direction(self, t):
1921 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND and \
1922 t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1925 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND:
1928 if t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1933 def generic_enum_to_string(self, e_dict, v, names_only=False):
1937 v32 = self._uint32(v)
1938 w = "__unknown__%08X__" % v32
1940 r = "0x%x (%s)" % (v, w)
1943 def generic_bitmap_to_string(self, b_dict, v, names_only=False):
1948 for b in sorted(b_dict.keys()):
1955 c32 = self._uint32(c)
1956 s += ["__unknown_%08X__" % c32]
1961 r = "0x%x (%s)" % (v, w)
1964 def trustType_string(self, v):
1966 lsa.LSA_TRUST_TYPE_DOWNLEVEL: "DOWNLEVEL",
1967 lsa.LSA_TRUST_TYPE_UPLEVEL: "UPLEVEL",
1968 lsa.LSA_TRUST_TYPE_MIT: "MIT",
1969 lsa.LSA_TRUST_TYPE_DCE: "DCE",
1971 return self.generic_enum_to_string(types, v)
1973 def trustDirection_string(self, v):
1975 lsa.LSA_TRUST_DIRECTION_INBOUND |
1976 lsa.LSA_TRUST_DIRECTION_OUTBOUND: "BOTH",
1977 lsa.LSA_TRUST_DIRECTION_INBOUND: "INBOUND",
1978 lsa.LSA_TRUST_DIRECTION_OUTBOUND: "OUTBOUND",
1980 return self.generic_enum_to_string(directions, v)
1982 def trustAttributes_string(self, v):
1984 lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE: "NON_TRANSITIVE",
1985 lsa.LSA_TRUST_ATTRIBUTE_UPLEVEL_ONLY: "UPLEVEL_ONLY",
1986 lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN: "QUARANTINED_DOMAIN",
1987 lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE: "FOREST_TRANSITIVE",
1988 lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION: "CROSS_ORGANIZATION",
1989 lsa.LSA_TRUST_ATTRIBUTE_WITHIN_FOREST: "WITHIN_FOREST",
1990 lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL: "TREAT_AS_EXTERNAL",
1991 lsa.LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION: "USES_RC4_ENCRYPTION",
1993 return self.generic_bitmap_to_string(attributes, v)
1995 def kerb_EncTypes_string(self, v):
1997 security.KERB_ENCTYPE_DES_CBC_CRC: "DES_CBC_CRC",
1998 security.KERB_ENCTYPE_DES_CBC_MD5: "DES_CBC_MD5",
1999 security.KERB_ENCTYPE_RC4_HMAC_MD5: "RC4_HMAC_MD5",
2000 security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96: "AES128_CTS_HMAC_SHA1_96",
2001 security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96: "AES256_CTS_HMAC_SHA1_96",
2002 security.KERB_ENCTYPE_FAST_SUPPORTED: "FAST_SUPPORTED",
2003 security.KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED: "COMPOUND_IDENTITY_SUPPORTED",
2004 security.KERB_ENCTYPE_CLAIMS_SUPPORTED: "CLAIMS_SUPPORTED",
2005 security.KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED: "RESOURCE_SID_COMPRESSION_DISABLED",
2007 return self.generic_bitmap_to_string(enctypes, v)
2009 def entry_tln_status(self, e_flags, ):
2011 return "Status[Enabled]"
2014 lsa.LSA_TLN_DISABLED_NEW: "Disabled-New",
2015 lsa.LSA_TLN_DISABLED_ADMIN: "Disabled",
2016 lsa.LSA_TLN_DISABLED_CONFLICT: "Disabled-Conflicting",
2018 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
2020 def entry_dom_status(self, e_flags):
2022 return "Status[Enabled]"
2025 lsa.LSA_SID_DISABLED_ADMIN: "Disabled-SID",
2026 lsa.LSA_SID_DISABLED_CONFLICT: "Disabled-SID-Conflicting",
2027 lsa.LSA_NB_DISABLED_ADMIN: "Disabled-NB",
2028 lsa.LSA_NB_DISABLED_CONFLICT: "Disabled-NB-Conflicting",
2030 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
2032 def write_forest_trust_info(self, fti, tln=None, collisions=None):
2034 tln_string = " TDO[%s]" % tln
2038 self.outf.write("Namespaces[%d]%s:\n" % (
2039 len(fti.entries), tln_string))
2041 for i, e in enumerate(fti.entries):
2044 collision_string = ""
2046 if collisions is not None:
2047 for c in collisions.entries:
2051 collision_string = " Collision[%s]" % (c.name.string)
2053 d = e.forest_trust_data
2054 if e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
2055 self.outf.write("TLN: %-32s DNS[*.%s]%s\n" % (
2056 self.entry_tln_status(flags),
2057 d.string, collision_string))
2058 elif e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
2059 self.outf.write("TLN_EX: %-29s DNS[*.%s]\n" % (
2061 elif e.type == lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
2062 self.outf.write("DOM: %-32s DNS[%s] Netbios[%s] SID[%s]%s\n" % (
2063 self.entry_dom_status(flags),
2064 d.dns_domain_name.string,
2065 d.netbios_domain_name.string,
2066 d.domain_sid, collision_string))
2070 class cmd_domain_trust_list(DomainTrustCommand):
2071 """List domain trusts."""
2073 synopsis = "%prog [options]"
2075 takes_optiongroups = {
2076 "sambaopts": options.SambaOptions,
2077 "versionopts": options.VersionOptions,
2078 "localdcopts": LocalDCCredentialsOptions,
2084 def run(self, sambaopts=None, versionopts=None, localdcopts=None):
2086 local_server = self.setup_local_server(sambaopts, localdcopts)
2088 local_netlogon = self.new_local_netlogon_connection()
2089 except RuntimeError as error:
2090 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2093 local_netlogon_trusts = \
2094 local_netlogon.netr_DsrEnumerateDomainTrusts(local_server,
2095 netlogon.NETR_TRUST_FLAG_IN_FOREST |
2096 netlogon.NETR_TRUST_FLAG_OUTBOUND |
2097 netlogon.NETR_TRUST_FLAG_INBOUND)
2098 except RuntimeError as error:
2099 if self.check_runtime_error(error, werror.WERR_RPC_S_PROCNUM_OUT_OF_RANGE):
2100 # TODO: we could implement a fallback to lsa.EnumTrustDom()
2101 raise CommandError("LOCAL_DC[%s]: netr_DsrEnumerateDomainTrusts not supported." % (
2103 raise self.LocalRuntimeError(self, error, "netr_DsrEnumerateDomainTrusts failed")
2105 a = local_netlogon_trusts.array
2107 if t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
2109 self.outf.write("%-14s %-15s %-19s %s\n" % (
2110 "Type[%s]" % self.netr_DomainTrust_to_type(a, t),
2111 "Transitive[%s]" % self.netr_DomainTrust_to_transitive(t),
2112 "Direction[%s]" % self.netr_DomainTrust_to_direction(t),
2113 "Name[%s]" % self.netr_DomainTrust_to_name(t)))
2117 class cmd_domain_trust_show(DomainTrustCommand):
2118 """Show trusted domain details."""
2120 synopsis = "%prog NAME [options]"
2122 takes_optiongroups = {
2123 "sambaopts": options.SambaOptions,
2124 "versionopts": options.VersionOptions,
2125 "localdcopts": LocalDCCredentialsOptions,
2131 takes_args = ["domain"]
2133 def run(self, domain, sambaopts=None, versionopts=None, localdcopts=None):
2135 local_server = self.setup_local_server(sambaopts, localdcopts)
2137 local_lsa = self.new_local_lsa_connection()
2138 except RuntimeError as error:
2139 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2142 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2143 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2144 except RuntimeError as error:
2145 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2147 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2148 local_lsa_info.name.string,
2149 local_lsa_info.dns_domain.string,
2150 local_lsa_info.sid))
2152 lsaString = lsa.String()
2153 lsaString.string = domain
2156 local_lsa.QueryTrustedDomainInfoByName(local_policy,
2158 lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2159 local_tdo_info = local_tdo_full.info_ex
2160 local_tdo_posix = local_tdo_full.posix_offset
2161 except NTSTATUSError as error:
2162 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2163 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2165 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(FULL_INFO) failed")
2168 local_tdo_enctypes = \
2169 local_lsa.QueryTrustedDomainInfoByName(local_policy,
2171 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES)
2172 except NTSTATUSError as error:
2173 if self.check_runtime_error(error, ntstatus.NT_STATUS_INVALID_PARAMETER):
2175 if self.check_runtime_error(error, ntstatus.NT_STATUS_INVALID_INFO_CLASS):
2178 if error is not None:
2179 raise self.LocalRuntimeError(self, error,
2180 "QueryTrustedDomainInfoByName(SUPPORTED_ENCRYPTION_TYPES) failed")
2182 local_tdo_enctypes = lsa.TrustDomainInfoSupportedEncTypes()
2183 local_tdo_enctypes.enc_types = 0
2186 local_tdo_forest = None
2187 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2188 local_tdo_forest = \
2189 local_lsa.lsaRQueryForestTrustInformation(local_policy,
2191 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
2192 except RuntimeError as error:
2193 if self.check_runtime_error(error, ntstatus.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
2195 if self.check_runtime_error(error, ntstatus.NT_STATUS_NOT_FOUND):
2197 if error is not None:
2198 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation failed")
2200 local_tdo_forest = lsa.ForestTrustInformation()
2201 local_tdo_forest.count = 0
2202 local_tdo_forest.entries = []
2204 self.outf.write("TrustedDomain:\n\n")
2205 self.outf.write("NetbiosName: %s\n" % local_tdo_info.netbios_name.string)
2206 if local_tdo_info.netbios_name.string != local_tdo_info.domain_name.string:
2207 self.outf.write("DnsName: %s\n" % local_tdo_info.domain_name.string)
2208 self.outf.write("SID: %s\n" % local_tdo_info.sid)
2209 self.outf.write("Type: %s\n" % self.trustType_string(local_tdo_info.trust_type))
2210 self.outf.write("Direction: %s\n" % self.trustDirection_string(local_tdo_info.trust_direction))
2211 self.outf.write("Attributes: %s\n" % self.trustAttributes_string(local_tdo_info.trust_attributes))
2212 posix_offset_u32 = ctypes.c_uint32(local_tdo_posix.posix_offset).value
2213 posix_offset_i32 = ctypes.c_int32(local_tdo_posix.posix_offset).value
2214 self.outf.write("PosixOffset: 0x%08X (%d)\n" % (posix_offset_u32, posix_offset_i32))
2215 self.outf.write("kerb_EncTypes: %s\n" % self.kerb_EncTypes_string(local_tdo_enctypes.enc_types))
2217 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2218 self.write_forest_trust_info(local_tdo_forest,
2219 tln=local_tdo_info.domain_name.string)
2224 class cmd_domain_trust_create(DomainTrustCommand):
2225 """Create a domain or forest trust."""
2227 synopsis = "%prog DOMAIN [options]"
2229 takes_optiongroups = {
2230 "sambaopts": options.SambaOptions,
2231 "versionopts": options.VersionOptions,
2232 "credopts": options.CredentialsOptions,
2233 "localdcopts": LocalDCCredentialsOptions,
2237 Option("--type", type="choice", metavar="TYPE",
2238 choices=["external", "forest"],
2239 help="The type of the trust: 'external' or 'forest'.",
2241 default="external"),
2242 Option("--direction", type="choice", metavar="DIRECTION",
2243 choices=["incoming", "outgoing", "both"],
2244 help="The trust direction: 'incoming', 'outgoing' or 'both'.",
2245 dest='trust_direction',
2247 Option("--create-location", type="choice", metavar="LOCATION",
2248 choices=["local", "both"],
2249 help="Where to create the trusted domain object: 'local' or 'both'.",
2250 dest='create_location',
2252 Option("--cross-organisation", action="store_true",
2253 help="The related domains does not belong to the same organisation.",
2254 dest='cross_organisation',
2256 Option("--quarantined", type="choice", metavar="yes|no",
2257 choices=["yes", "no", None],
2258 help="Special SID filtering rules are applied to the trust. "
2259 "With --type=external the default is yes. "
2260 "With --type=forest the default is no.",
2261 dest='quarantined_arg',
2263 Option("--not-transitive", action="store_true",
2264 help="The forest trust is not transitive.",
2265 dest='not_transitive',
2267 Option("--treat-as-external", action="store_true",
2268 help="The treat the forest trust as external.",
2269 dest='treat_as_external',
2271 Option("--no-aes-keys", action="store_false",
2272 help="The trust uses aes kerberos keys.",
2273 dest='use_aes_keys',
2275 Option("--skip-validation", action="store_false",
2276 help="Skip validation of the trust.",
2281 takes_args = ["domain"]
2283 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2284 trust_type=None, trust_direction=None, create_location=None,
2285 cross_organisation=False, quarantined_arg=None,
2286 not_transitive=False, treat_as_external=False,
2287 use_aes_keys=False, validate=True):
2289 lsaString = lsa.String()
2292 if quarantined_arg is None:
2293 if trust_type == 'external':
2295 elif quarantined_arg == 'yes':
2298 if trust_type != 'forest':
2300 raise CommandError("--not-transitive requires --type=forest")
2301 if treat_as_external:
2302 raise CommandError("--treat-as-external requires --type=forest")
2306 enc_types = lsa.TrustDomainInfoSupportedEncTypes()
2307 enc_types.enc_types = security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96
2308 enc_types.enc_types |= security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96
2310 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2311 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2312 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2314 local_trust_info = lsa.TrustDomainInfoInfoEx()
2315 local_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2316 local_trust_info.trust_direction = 0
2317 if trust_direction == "both":
2318 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2319 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2320 elif trust_direction == "incoming":
2321 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2322 elif trust_direction == "outgoing":
2323 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2324 local_trust_info.trust_attributes = 0
2325 if cross_organisation:
2326 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2328 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2329 if trust_type == "forest":
2330 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2332 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2333 if treat_as_external:
2334 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2336 def get_password(name):
2339 if password is not None and password is not '':
2341 password = getpass("New %s Password: " % name)
2342 passwordverify = getpass("Retype %s Password: " % name)
2343 if not password == passwordverify:
2345 self.outf.write("Sorry, passwords do not match.\n")
2347 incoming_secret = None
2348 outgoing_secret = None
2349 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2350 if create_location == "local":
2351 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2352 incoming_password = get_password("Incoming Trust")
2353 incoming_secret = string_to_byte_array(incoming_password.encode('utf-16-le'))
2354 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2355 outgoing_password = get_password("Outgoing Trust")
2356 outgoing_secret = string_to_byte_array(outgoing_password.encode('utf-16-le'))
2358 remote_trust_info = None
2360 # We use 240 random bytes.
2361 # Windows uses 28 or 240 random bytes. I guess it's
2362 # based on the trust type external vs. forest.
2364 # The initial trust password can be up to 512 bytes
2365 # while the versioned passwords used for periodic updates
2366 # can only be up to 498 bytes, as netr_ServerPasswordSet2()
2367 # needs to pass the NL_PASSWORD_VERSION structure within the
2368 # 512 bytes and a 2 bytes confounder is required.
2370 def random_trust_secret(length):
2371 pw = samba.generate_random_machine_password(length // 2, length // 2)
2372 return string_to_byte_array(pw.encode('utf-16-le'))
2374 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2375 incoming_secret = random_trust_secret(240)
2376 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2377 outgoing_secret = random_trust_secret(240)
2379 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2380 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2382 remote_trust_info = lsa.TrustDomainInfoInfoEx()
2383 remote_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2384 remote_trust_info.trust_direction = 0
2385 if trust_direction == "both":
2386 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2387 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2388 elif trust_direction == "incoming":
2389 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2390 elif trust_direction == "outgoing":
2391 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2392 remote_trust_info.trust_attributes = 0
2393 if cross_organisation:
2394 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2396 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2397 if trust_type == "forest":
2398 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2400 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2401 if treat_as_external:
2402 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2404 local_server = self.setup_local_server(sambaopts, localdcopts)
2406 local_lsa = self.new_local_lsa_connection()
2407 except RuntimeError as error:
2408 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2411 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2412 except RuntimeError as error:
2413 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2415 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2416 local_lsa_info.name.string,
2417 local_lsa_info.dns_domain.string,
2418 local_lsa_info.sid))
2421 remote_server = self.setup_remote_server(credopts, domain)
2422 except RuntimeError as error:
2423 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2426 remote_lsa = self.new_remote_lsa_connection()
2427 except RuntimeError as error:
2428 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2431 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2432 except RuntimeError as error:
2433 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2435 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2436 remote_lsa_info.name.string,
2437 remote_lsa_info.dns_domain.string,
2438 remote_lsa_info.sid))
2440 local_trust_info.domain_name.string = remote_lsa_info.dns_domain.string
2441 local_trust_info.netbios_name.string = remote_lsa_info.name.string
2442 local_trust_info.sid = remote_lsa_info.sid
2444 if remote_trust_info:
2445 remote_trust_info.domain_name.string = local_lsa_info.dns_domain.string
2446 remote_trust_info.netbios_name.string = local_lsa_info.name.string
2447 remote_trust_info.sid = local_lsa_info.sid
2450 lsaString.string = local_trust_info.domain_name.string
2451 local_old_netbios = \
2452 local_lsa.QueryTrustedDomainInfoByName(local_policy,
2454 lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2455 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2456 except NTSTATUSError as error:
2457 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2458 raise self.LocalRuntimeError(self, error,
2459 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2463 lsaString.string = local_trust_info.netbios_name.string
2465 local_lsa.QueryTrustedDomainInfoByName(local_policy,
2467 lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2468 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2469 except NTSTATUSError as error:
2470 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2471 raise self.LocalRuntimeError(self, error,
2472 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2475 if remote_trust_info:
2477 lsaString.string = remote_trust_info.domain_name.string
2478 remote_old_netbios = \
2479 remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2481 lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2482 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2483 except NTSTATUSError as error:
2484 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2485 raise self.RemoteRuntimeError(self, error,
2486 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2490 lsaString.string = remote_trust_info.netbios_name.string
2492 remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2494 lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2495 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2496 except NTSTATUSError as error:
2497 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2498 raise self.RemoteRuntimeError(self, error,
2499 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2503 local_netlogon = self.new_local_netlogon_connection()
2504 except RuntimeError as error:
2505 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2508 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
2509 except RuntimeError as error:
2510 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
2512 if remote_trust_info:
2514 remote_netlogon = self.new_remote_netlogon_connection()
2515 except RuntimeError as error:
2516 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2519 remote_netlogon_dc_unc = self.get_netlogon_dc_unc(remote_netlogon,
2520 remote_server, domain)
2521 except RuntimeError as error:
2522 raise self.RemoteRuntimeError(self, error, "failed to get netlogon dc info")
2524 def generate_AuthInOutBlob(secret, update_time):
2526 blob = drsblobs.trustAuthInOutBlob()
2531 clear = drsblobs.AuthInfoClear()
2532 clear.size = len(secret)
2533 clear.password = secret
2535 info = drsblobs.AuthenticationInformation()
2536 info.LastUpdateTime = samba.unix2nttime(update_time)
2537 info.AuthType = lsa.TRUST_AUTH_TYPE_CLEAR
2538 info.AuthInfo = clear
2540 array = drsblobs.AuthenticationInformationArray()
2542 array.array = [info]
2544 blob = drsblobs.trustAuthInOutBlob()
2546 blob.current = array
2550 def generate_AuthInfoInternal(session_key, incoming=None, outgoing=None):
2551 confounder = [0] * 512
2552 for i in range(len(confounder)):
2553 confounder[i] = random.randint(0, 255)
2555 trustpass = drsblobs.trustDomainPasswords()
2557 trustpass.confounder = confounder
2558 trustpass.outgoing = outgoing
2559 trustpass.incoming = incoming
2561 trustpass_blob = ndr_pack(trustpass)
2563 encrypted_trustpass = arcfour_encrypt(session_key, trustpass_blob)
2565 auth_blob = lsa.DATA_BUF2()
2566 auth_blob.size = len(encrypted_trustpass)
2567 auth_blob.data = string_to_byte_array(encrypted_trustpass)
2569 auth_info = lsa.TrustDomainInfoAuthInfoInternal()
2570 auth_info.auth_blob = auth_blob
2574 update_time = samba.current_unix_time()
2575 incoming_blob = generate_AuthInOutBlob(incoming_secret, update_time)
2576 outgoing_blob = generate_AuthInOutBlob(outgoing_secret, update_time)
2578 local_tdo_handle = None
2579 remote_tdo_handle = None
2581 local_auth_info = generate_AuthInfoInternal(local_lsa.session_key,
2582 incoming=incoming_blob,
2583 outgoing=outgoing_blob)
2584 if remote_trust_info:
2585 remote_auth_info = generate_AuthInfoInternal(remote_lsa.session_key,
2586 incoming=outgoing_blob,
2587 outgoing=incoming_blob)
2590 if remote_trust_info:
2591 self.outf.write("Creating remote TDO.\n")
2592 current_request = {"location": "remote", "name": "CreateTrustedDomainEx2"}
2593 remote_tdo_handle = \
2594 remote_lsa.CreateTrustedDomainEx2(remote_policy,
2597 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2598 self.outf.write("Remote TDO created.\n")
2600 self.outf.write("Setting supported encryption types on remote TDO.\n")
2601 current_request = {"location": "remote", "name": "SetInformationTrustedDomain"}
2602 remote_lsa.SetInformationTrustedDomain(remote_tdo_handle,
2603 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2606 self.outf.write("Creating local TDO.\n")
2607 current_request = {"location": "local", "name": "CreateTrustedDomainEx2"}
2608 local_tdo_handle = local_lsa.CreateTrustedDomainEx2(local_policy,
2611 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2612 self.outf.write("Local TDO created\n")
2614 self.outf.write("Setting supported encryption types on local TDO.\n")
2615 current_request = {"location": "local", "name": "SetInformationTrustedDomain"}
2616 local_lsa.SetInformationTrustedDomain(local_tdo_handle,
2617 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2619 except RuntimeError as error:
2620 self.outf.write("Error: %s failed %sly - cleaning up\n" % (
2621 current_request['name'], current_request['location']))
2622 if remote_tdo_handle:
2623 self.outf.write("Deleting remote TDO.\n")
2624 remote_lsa.DeleteObject(remote_tdo_handle)
2625 remote_tdo_handle = None
2626 if local_tdo_handle:
2627 self.outf.write("Deleting local TDO.\n")
2628 local_lsa.DeleteObject(local_tdo_handle)
2629 local_tdo_handle = None
2630 if current_request['location'] is "remote":
2631 raise self.RemoteRuntimeError(self, error, "%s" % (
2632 current_request['name']))
2633 raise self.LocalRuntimeError(self, error, "%s" % (
2634 current_request['name']))
2637 if local_trust_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2638 self.outf.write("Setup local forest trust information...\n")
2640 # get all information about the remote trust
2641 # this triggers netr_GetForestTrustInformation to the remote domain
2642 # and lsaRSetForestTrustInformation() locally, but new top level
2643 # names are disabled by default.
2644 local_forest_info = \
2645 local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
2646 remote_lsa_info.dns_domain.string,
2647 netlogon.DS_GFTI_UPDATE_TDO)
2648 except RuntimeError as error:
2649 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2652 # here we try to enable all top level names
2653 local_forest_collision = \
2654 local_lsa.lsaRSetForestTrustInformation(local_policy,
2655 remote_lsa_info.dns_domain,
2656 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2659 except RuntimeError as error:
2660 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2662 self.write_forest_trust_info(local_forest_info,
2663 tln=remote_lsa_info.dns_domain.string,
2664 collisions=local_forest_collision)
2666 if remote_trust_info:
2667 self.outf.write("Setup remote forest trust information...\n")
2669 # get all information about the local trust (from the perspective of the remote domain)
2670 # this triggers netr_GetForestTrustInformation to our domain.
2671 # and lsaRSetForestTrustInformation() remotely, but new top level
2672 # names are disabled by default.
2673 remote_forest_info = \
2674 remote_netlogon.netr_DsRGetForestTrustInformation(remote_netlogon_dc_unc,
2675 local_lsa_info.dns_domain.string,
2676 netlogon.DS_GFTI_UPDATE_TDO)
2677 except RuntimeError as error:
2678 raise self.RemoteRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2681 # here we try to enable all top level names
2682 remote_forest_collision = \
2683 remote_lsa.lsaRSetForestTrustInformation(remote_policy,
2684 local_lsa_info.dns_domain,
2685 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2688 except RuntimeError as error:
2689 raise self.RemoteRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2691 self.write_forest_trust_info(remote_forest_info,
2692 tln=local_lsa_info.dns_domain.string,
2693 collisions=remote_forest_collision)
2695 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2696 self.outf.write("Validating outgoing trust...\n")
2698 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_netlogon_info.dc_unc,
2699 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2701 remote_lsa_info.dns_domain.string)
2702 except RuntimeError as error:
2703 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2705 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2706 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2708 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2709 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2710 local_trust_verify.trusted_dc_name,
2711 local_trust_verify.tc_connection_status[1],
2712 local_trust_verify.pdc_connection_status[1])
2714 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2715 local_trust_verify.trusted_dc_name,
2716 local_trust_verify.tc_connection_status[1],
2717 local_trust_verify.pdc_connection_status[1])
2719 if local_trust_status != werror.WERR_SUCCESS or local_conn_status != werror.WERR_SUCCESS:
2720 raise CommandError(local_validation)
2722 self.outf.write("OK: %s\n" % local_validation)
2724 if remote_trust_info:
2725 if remote_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2726 self.outf.write("Validating incoming trust...\n")
2728 remote_trust_verify = \
2729 remote_netlogon.netr_LogonControl2Ex(remote_netlogon_dc_unc,
2730 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2732 local_lsa_info.dns_domain.string)
2733 except RuntimeError as error:
2734 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2736 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2737 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2739 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2740 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2741 remote_trust_verify.trusted_dc_name,
2742 remote_trust_verify.tc_connection_status[1],
2743 remote_trust_verify.pdc_connection_status[1])
2745 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2746 remote_trust_verify.trusted_dc_name,
2747 remote_trust_verify.tc_connection_status[1],
2748 remote_trust_verify.pdc_connection_status[1])
2750 if remote_trust_status != werror.WERR_SUCCESS or remote_conn_status != werror.WERR_SUCCESS:
2751 raise CommandError(remote_validation)
2753 self.outf.write("OK: %s\n" % remote_validation)
2755 if remote_tdo_handle is not None:
2757 remote_lsa.Close(remote_tdo_handle)
2758 except RuntimeError as error:
2760 remote_tdo_handle = None
2761 if local_tdo_handle is not None:
2763 local_lsa.Close(local_tdo_handle)
2764 except RuntimeError as error:
2766 local_tdo_handle = None
2768 self.outf.write("Success.\n")
2772 class cmd_domain_trust_delete(DomainTrustCommand):
2773 """Delete a domain trust."""
2775 synopsis = "%prog DOMAIN [options]"
2777 takes_optiongroups = {
2778 "sambaopts": options.SambaOptions,
2779 "versionopts": options.VersionOptions,
2780 "credopts": options.CredentialsOptions,
2781 "localdcopts": LocalDCCredentialsOptions,
2785 Option("--delete-location", type="choice", metavar="LOCATION",
2786 choices=["local", "both"],
2787 help="Where to delete the trusted domain object: 'local' or 'both'.",
2788 dest='delete_location',
2792 takes_args = ["domain"]
2794 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2795 delete_location=None):
2797 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2798 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2799 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2801 if delete_location == "local":
2802 remote_policy_access = None
2804 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2805 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2806 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2808 local_server = self.setup_local_server(sambaopts, localdcopts)
2810 local_lsa = self.new_local_lsa_connection()
2811 except RuntimeError as error:
2812 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2815 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2816 except RuntimeError as error:
2817 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2819 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2820 local_lsa_info.name.string,
2821 local_lsa_info.dns_domain.string,
2822 local_lsa_info.sid))
2824 local_tdo_info = None
2825 local_tdo_handle = None
2826 remote_tdo_info = None
2827 remote_tdo_handle = None
2829 lsaString = lsa.String()
2831 lsaString.string = domain
2832 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2833 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2834 except NTSTATUSError as error:
2835 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2836 raise CommandError("Failed to find trust for domain '%s'" % domain)
2837 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2839 if remote_policy_access is not None:
2841 remote_server = self.setup_remote_server(credopts, domain)
2842 except RuntimeError as error:
2843 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2846 remote_lsa = self.new_remote_lsa_connection()
2847 except RuntimeError as error:
2848 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2851 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2852 except RuntimeError as error:
2853 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2855 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2856 remote_lsa_info.name.string,
2857 remote_lsa_info.dns_domain.string,
2858 remote_lsa_info.sid))
2860 if remote_lsa_info.sid != local_tdo_info.sid or \
2861 remote_lsa_info.name.string != local_tdo_info.netbios_name.string or \
2862 remote_lsa_info.dns_domain.string != local_tdo_info.domain_name.string:
2863 raise CommandError("LocalTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2864 local_tdo_info.netbios_name.string,
2865 local_tdo_info.domain_name.string,
2866 local_tdo_info.sid))
2869 lsaString.string = local_lsa_info.dns_domain.string
2871 remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2873 lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2874 except NTSTATUSError as error:
2875 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2876 raise self.RemoteRuntimeError(self, error, "QueryTrustedDomainInfoByName(%s)" % (
2880 if remote_tdo_info is not None:
2881 if local_lsa_info.sid != remote_tdo_info.sid or \
2882 local_lsa_info.name.string != remote_tdo_info.netbios_name.string or \
2883 local_lsa_info.dns_domain.string != remote_tdo_info.domain_name.string:
2884 raise CommandError("RemoteTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2885 remote_tdo_info.netbios_name.string,
2886 remote_tdo_info.domain_name.string,
2887 remote_tdo_info.sid))
2889 if local_tdo_info is not None:
2891 lsaString.string = local_tdo_info.domain_name.string
2892 local_tdo_handle = \
2893 local_lsa.OpenTrustedDomainByName(local_policy,
2895 security.SEC_STD_DELETE)
2896 except RuntimeError as error:
2897 raise self.LocalRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2900 local_lsa.DeleteObject(local_tdo_handle)
2901 local_tdo_handle = None
2903 if remote_tdo_info is not None:
2905 lsaString.string = remote_tdo_info.domain_name.string
2906 remote_tdo_handle = \
2907 remote_lsa.OpenTrustedDomainByName(remote_policy,
2909 security.SEC_STD_DELETE)
2910 except RuntimeError as error:
2911 raise self.RemoteRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2914 if remote_tdo_handle is not None:
2916 remote_lsa.DeleteObject(remote_tdo_handle)
2917 remote_tdo_handle = None
2918 self.outf.write("RemoteTDO deleted.\n")
2919 except RuntimeError as error:
2920 self.outf.write("%s\n" % self.RemoteRuntimeError(self, error, "DeleteObject() failed"))
2922 if local_tdo_handle is not None:
2924 local_lsa.DeleteObject(local_tdo_handle)
2925 local_tdo_handle = None
2926 self.outf.write("LocalTDO deleted.\n")
2927 except RuntimeError as error:
2928 self.outf.write("%s\n" % self.LocalRuntimeError(self, error, "DeleteObject() failed"))
2933 class cmd_domain_trust_validate(DomainTrustCommand):
2934 """Validate a domain trust."""
2936 synopsis = "%prog DOMAIN [options]"
2938 takes_optiongroups = {
2939 "sambaopts": options.SambaOptions,
2940 "versionopts": options.VersionOptions,
2941 "credopts": options.CredentialsOptions,
2942 "localdcopts": LocalDCCredentialsOptions,
2946 Option("--validate-location", type="choice", metavar="LOCATION",
2947 choices=["local", "both"],
2948 help="Where to validate the trusted domain object: 'local' or 'both'.",
2949 dest='validate_location',
2953 takes_args = ["domain"]
2955 def run(self, domain, sambaopts=None, versionopts=None, credopts=None, localdcopts=None,
2956 validate_location=None):
2958 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2960 local_server = self.setup_local_server(sambaopts, localdcopts)
2962 local_lsa = self.new_local_lsa_connection()
2963 except RuntimeError as error:
2964 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2967 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2968 except RuntimeError as error:
2969 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2971 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2972 local_lsa_info.name.string,
2973 local_lsa_info.dns_domain.string,
2974 local_lsa_info.sid))
2977 lsaString = lsa.String()
2978 lsaString.string = domain
2980 local_lsa.QueryTrustedDomainInfoByName(local_policy,
2982 lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2983 except NTSTATUSError as error:
2984 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2985 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2987 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
2989 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
2990 local_tdo_info.netbios_name.string,
2991 local_tdo_info.domain_name.string,
2992 local_tdo_info.sid))
2995 local_netlogon = self.new_local_netlogon_connection()
2996 except RuntimeError as error:
2997 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3000 local_trust_verify = \
3001 local_netlogon.netr_LogonControl2Ex(local_server,
3002 netlogon.NETLOGON_CONTROL_TC_VERIFY,
3004 local_tdo_info.domain_name.string)
3005 except RuntimeError as error:
3006 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
3008 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
3009 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
3011 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
3012 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
3013 local_trust_verify.trusted_dc_name,
3014 local_trust_verify.tc_connection_status[1],
3015 local_trust_verify.pdc_connection_status[1])
3017 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
3018 local_trust_verify.trusted_dc_name,
3019 local_trust_verify.tc_connection_status[1],
3020 local_trust_verify.pdc_connection_status[1])
3022 if local_trust_status != werror.WERR_SUCCESS or local_conn_status != werror.WERR_SUCCESS:
3023 raise CommandError(local_validation)
3025 self.outf.write("OK: %s\n" % local_validation)
3028 server = local_trust_verify.trusted_dc_name.replace('\\', '')
3029 domain_and_server = "%s\\%s" % (local_tdo_info.domain_name.string, server)
3030 local_trust_rediscover = \
3031 local_netlogon.netr_LogonControl2Ex(local_server,
3032 netlogon.NETLOGON_CONTROL_REDISCOVER,
3035 except RuntimeError as error:
3036 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
3038 local_conn_status = self._uint32(local_trust_rediscover.tc_connection_status[0])
3039 local_rediscover = "LocalRediscover: DC[%s] CONNECTION[%s]" % (
3040 local_trust_rediscover.trusted_dc_name,
3041 local_trust_rediscover.tc_connection_status[1])
3043 if local_conn_status != werror.WERR_SUCCESS:
3044 raise CommandError(local_rediscover)
3046 self.outf.write("OK: %s\n" % local_rediscover)
3048 if validate_location != "local":
3050 remote_server = self.setup_remote_server(credopts, domain, require_pdc=False)
3051 except RuntimeError as error:
3052 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
3055 remote_netlogon = self.new_remote_netlogon_connection()
3056 except RuntimeError as error:
3057 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
3060 remote_trust_verify = \
3061 remote_netlogon.netr_LogonControl2Ex(remote_server,
3062 netlogon.NETLOGON_CONTROL_TC_VERIFY,
3064 local_lsa_info.dns_domain.string)
3065 except RuntimeError as error:
3066 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
3068 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
3069 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
3071 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
3072 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
3073 remote_trust_verify.trusted_dc_name,
3074 remote_trust_verify.tc_connection_status[1],
3075 remote_trust_verify.pdc_connection_status[1])
3077 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
3078 remote_trust_verify.trusted_dc_name,
3079 remote_trust_verify.tc_connection_status[1],
3080 remote_trust_verify.pdc_connection_status[1])
3082 if remote_trust_status != werror.WERR_SUCCESS or remote_conn_status != werror.WERR_SUCCESS:
3083 raise CommandError(remote_validation)
3085 self.outf.write("OK: %s\n" % remote_validation)
3088 server = remote_trust_verify.trusted_dc_name.replace('\\', '')
3089 domain_and_server = "%s\\%s" % (local_lsa_info.dns_domain.string, server)
3090 remote_trust_rediscover = \
3091 remote_netlogon.netr_LogonControl2Ex(remote_server,
3092 netlogon.NETLOGON_CONTROL_REDISCOVER,
3095 except RuntimeError as error:
3096 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
3098 remote_conn_status = self._uint32(remote_trust_rediscover.tc_connection_status[0])
3100 remote_rediscover = "RemoteRediscover: DC[%s] CONNECTION[%s]" % (
3101 remote_trust_rediscover.trusted_dc_name,
3102 remote_trust_rediscover.tc_connection_status[1])
3104 if remote_conn_status != werror.WERR_SUCCESS:
3105 raise CommandError(remote_rediscover)
3107 self.outf.write("OK: %s\n" % remote_rediscover)
3112 class cmd_domain_trust_namespaces(DomainTrustCommand):
3113 """Manage forest trust namespaces."""
3115 synopsis = "%prog [DOMAIN] [options]"
3117 takes_optiongroups = {
3118 "sambaopts": options.SambaOptions,
3119 "versionopts": options.VersionOptions,
3120 "localdcopts": LocalDCCredentialsOptions,
3124 Option("--refresh", type="choice", metavar="check|store",
3125 choices=["check", "store", None],
3126 help="List and maybe store refreshed forest trust information: 'check' or 'store'.",
3129 Option("--enable-all", action="store_true",
3130 help="Try to update disabled entries, not allowed with --refresh=check.",
3133 Option("--enable-tln", action="append", metavar='DNSDOMAIN',
3134 help="Enable a top level name entry. Can be specified multiple times.",
3137 Option("--disable-tln", action="append", metavar='DNSDOMAIN',
3138 help="Disable a top level name entry. Can be specified multiple times.",
3141 Option("--add-tln-ex", action="append", metavar='DNSDOMAIN',
3142 help="Add a top level exclusion entry. Can be specified multiple times.",
3145 Option("--delete-tln-ex", action="append", metavar='DNSDOMAIN',
3146 help="Delete a top level exclusion entry. Can be specified multiple times.",
3147 dest='delete_tln_ex',
3149 Option("--enable-nb", action="append", metavar='NETBIOSDOMAIN',
3150 help="Enable a netbios name in a domain entry. Can be specified multiple times.",
3153 Option("--disable-nb", action="append", metavar='NETBIOSDOMAIN',
3154 help="Disable a netbios name in a domain entry. Can be specified multiple times.",
3157 Option("--enable-sid", action="append", metavar='DOMAINSID',
3158 help="Enable a SID in a domain entry. Can be specified multiple times.",
3159 dest='enable_sid_str',
3161 Option("--disable-sid", action="append", metavar='DOMAINSID',
3162 help="Disable a SID in a domain entry. Can be specified multiple times.",
3163 dest='disable_sid_str',
3165 Option("--add-upn-suffix", action="append", metavar='DNSDOMAIN',
3166 help="Add a new uPNSuffixes attribute for the local forest. Can be specified multiple times.",
3169 Option("--delete-upn-suffix", action="append", metavar='DNSDOMAIN',
3170 help="Delete an existing uPNSuffixes attribute of the local forest. Can be specified multiple times.",
3173 Option("--add-spn-suffix", action="append", metavar='DNSDOMAIN',
3174 help="Add a new msDS-SPNSuffixes attribute for the local forest. Can be specified multiple times.",
3177 Option("--delete-spn-suffix", action="append", metavar='DNSDOMAIN',
3178 help="Delete an existing msDS-SPNSuffixes attribute of the local forest. Can be specified multiple times.",
3183 takes_args = ["domain?"]
3185 def run(self, domain=None, sambaopts=None, localdcopts=None, versionopts=None,
3186 refresh=None, enable_all=False,
3187 enable_tln=[], disable_tln=[], add_tln_ex=[], delete_tln_ex=[],
3188 enable_sid_str=[], disable_sid_str=[], enable_nb=[], disable_nb=[],
3189 add_upn=[], delete_upn=[], add_spn=[], delete_spn=[]):
3191 require_update = False
3194 if refresh == "store":
3195 raise CommandError("--refresh=%s not allowed without DOMAIN" % refresh)
3198 raise CommandError("--enable-all not allowed without DOMAIN")
3200 if len(enable_tln) > 0:
3201 raise CommandError("--enable-tln not allowed without DOMAIN")
3202 if len(disable_tln) > 0:
3203 raise CommandError("--disable-tln not allowed without DOMAIN")
3205 if len(add_tln_ex) > 0:
3206 raise CommandError("--add-tln-ex not allowed without DOMAIN")
3207 if len(delete_tln_ex) > 0:
3208 raise CommandError("--delete-tln-ex not allowed without DOMAIN")
3210 if len(enable_nb) > 0:
3211 raise CommandError("--enable-nb not allowed without DOMAIN")
3212 if len(disable_nb) > 0:
3213 raise CommandError("--disable-nb not allowed without DOMAIN")
3215 if len(enable_sid_str) > 0:
3216 raise CommandError("--enable-sid not allowed without DOMAIN")
3217 if len(disable_sid_str) > 0:
3218 raise CommandError("--disable-sid not allowed without DOMAIN")
3220 if len(add_upn) > 0:
3222 if not n.startswith("*."):
3224 raise CommandError("value[%s] specified for --add-upn-suffix should not include with '*.'" % n)
3225 require_update = True
3226 if len(delete_upn) > 0:
3227 for n in delete_upn:
3228 if not n.startswith("*."):
3230 raise CommandError("value[%s] specified for --delete-upn-suffix should not include with '*.'" % n)
3231 require_update = True
3233 for d in delete_upn:
3234 if a.lower() != d.lower():
3236 raise CommandError("value[%s] specified for --add-upn-suffix and --delete-upn-suffix" % a)
3238 if len(add_spn) > 0:
3240 if not n.startswith("*."):
3242 raise CommandError("value[%s] specified for --add-spn-suffix should not include with '*.'" % n)
3243 require_update = True
3244 if len(delete_spn) > 0:
3245 for n in delete_spn:
3246 if not n.startswith("*."):
3248 raise CommandError("value[%s] specified for --delete-spn-suffix should not include with '*.'" % n)
3249 require_update = True
3251 for d in delete_spn:
3252 if a.lower() != d.lower():
3254 raise CommandError("value[%s] specified for --add-spn-suffix and --delete-spn-suffix" % a)
3256 if len(add_upn) > 0:
3257 raise CommandError("--add-upn-suffix not allowed together with DOMAIN")
3258 if len(delete_upn) > 0:
3259 raise CommandError("--delete-upn-suffix not allowed together with DOMAIN")
3260 if len(add_spn) > 0:
3261 raise CommandError("--add-spn-suffix not allowed together with DOMAIN")
3262 if len(delete_spn) > 0:
3263 raise CommandError("--delete-spn-suffix not allowed together with DOMAIN")
3265 if refresh is not None:
3266 if refresh == "store":
3267 require_update = True
3269 if enable_all and refresh != "store":
3270 raise CommandError("--enable-all not allowed together with --refresh=%s" % refresh)
3272 if len(enable_tln) > 0:
3273 raise CommandError("--enable-tln not allowed together with --refresh")
3274 if len(disable_tln) > 0:
3275 raise CommandError("--disable-tln not allowed together with --refresh")
3277 if len(add_tln_ex) > 0:
3278 raise CommandError("--add-tln-ex not allowed together with --refresh")
3279 if len(delete_tln_ex) > 0:
3280 raise CommandError("--delete-tln-ex not allowed together with --refresh")
3282 if len(enable_nb) > 0:
3283 raise CommandError("--enable-nb not allowed together with --refresh")
3284 if len(disable_nb) > 0:
3285 raise CommandError("--disable-nb not allowed together with --refresh")
3287 if len(enable_sid_str) > 0:
3288 raise CommandError("--enable-sid not allowed together with --refresh")
3289 if len(disable_sid_str) > 0:
3290 raise CommandError("--disable-sid not allowed together with --refresh")
3293 require_update = True
3295 if len(enable_tln) > 0:
3296 raise CommandError("--enable-tln not allowed together with --enable-all")
3298 if len(enable_nb) > 0:
3299 raise CommandError("--enable-nb not allowed together with --enable-all")
3301 if len(enable_sid_str) > 0:
3302 raise CommandError("--enable-sid not allowed together with --enable-all")
3304 if len(enable_tln) > 0:
3305 require_update = True
3306 if len(disable_tln) > 0:
3307 require_update = True
3308 for e in enable_tln:
3309 for d in disable_tln:
3310 if e.lower() != d.lower():
3312 raise CommandError("value[%s] specified for --enable-tln and --disable-tln" % e)
3314 if len(add_tln_ex) > 0:
3315 for n in add_tln_ex:
3316 if not n.startswith("*."):
3318 raise CommandError("value[%s] specified for --add-tln-ex should not include with '*.'" % n)
3319 require_update = True
3320 if len(delete_tln_ex) > 0:
3321 for n in delete_tln_ex:
3322 if not n.startswith("*."):
3324 raise CommandError("value[%s] specified for --delete-tln-ex should not include with '*.'" % n)
3325 require_update = True
3326 for a in add_tln_ex:
3327 for d in delete_tln_ex:
3328 if a.lower() != d.lower():
3330 raise CommandError("value[%s] specified for --add-tln-ex and --delete-tln-ex" % a)
3332 if len(enable_nb) > 0:
3333 require_update = True
3334 if len(disable_nb) > 0:
3335 require_update = True
3337 for d in disable_nb:
3338 if e.upper() != d.upper():
3340 raise CommandError("value[%s] specified for --enable-nb and --disable-nb" % e)
3343 for s in enable_sid_str:
3345 sid = security.dom_sid(s)
3346 except TypeError as error:
3347 raise CommandError("value[%s] specified for --enable-sid is not a valid SID" % s)
3348 enable_sid.append(sid)
3350 for s in disable_sid_str:
3352 sid = security.dom_sid(s)
3353 except TypeError as error:
3354 raise CommandError("value[%s] specified for --disable-sid is not a valid SID" % s)
3355 disable_sid.append(sid)
3356 if len(enable_sid) > 0:
3357 require_update = True
3358 if len(disable_sid) > 0:
3359 require_update = True
3360 for e in enable_sid:
3361 for d in disable_sid:
3364 raise CommandError("value[%s] specified for --enable-sid and --disable-sid" % e)
3366 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
3368 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
3370 local_server = self.setup_local_server(sambaopts, localdcopts)
3372 local_lsa = self.new_local_lsa_connection()
3373 except RuntimeError as error:
3374 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
3377 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
3378 except RuntimeError as error:
3379 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
3381 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
3382 local_lsa_info.name.string,
3383 local_lsa_info.dns_domain.string,
3384 local_lsa_info.sid))
3388 local_netlogon = self.new_local_netlogon_connection()
3389 except RuntimeError as error:
3390 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3393 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3394 except RuntimeError as error:
3395 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3397 if local_netlogon_info.domain_name != local_netlogon_info.forest_name:
3398 raise CommandError("The local domain [%s] is not the forest root [%s]" % (
3399 local_netlogon_info.domain_name,
3400 local_netlogon_info.forest_name))
3403 # get all information about our own forest
3404 own_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3406 except RuntimeError as error:
3407 if self.check_runtime_error(error, werror.WERR_RPC_S_PROCNUM_OUT_OF_RANGE):
3408 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3411 if self.check_runtime_error(error, werror.WERR_INVALID_FUNCTION):
3412 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3415 if self.check_runtime_error(error, werror.WERR_NERR_ACFNOTLOADED):
3416 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3419 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3421 self.outf.write("Own forest trust information...\n")
3422 self.write_forest_trust_info(own_forest_info,
3423 tln=local_lsa_info.dns_domain.string)
3426 local_samdb = self.new_local_ldap_connection()
3427 except RuntimeError as error:
3428 raise self.LocalRuntimeError(self, error, "failed to connect to SamDB")
3430 local_partitions_dn = "CN=Partitions,%s" % str(local_samdb.get_config_basedn())
3431 attrs = ['uPNSuffixes', 'msDS-SPNSuffixes']
3433 msgs = local_samdb.search(base=local_partitions_dn,
3434 scope=ldb.SCOPE_BASE,
3435 expression="(objectClass=crossRefContainer)",
3437 stored_msg = msgs[0]
3438 except ldb.LdbError as error:
3439 raise self.LocalLdbError(self, error, "failed to search partition dn")
3441 stored_upn_vals = []
3442 if 'uPNSuffixes' in stored_msg:
3443 stored_upn_vals.extend(stored_msg['uPNSuffixes'])
3445 stored_spn_vals = []
3446 if 'msDS-SPNSuffixes' in stored_msg:
3447 stored_spn_vals.extend(stored_msg['msDS-SPNSuffixes'])
3449 self.outf.write("Stored uPNSuffixes attributes[%d]:\n" % len(stored_upn_vals))
3450 for v in stored_upn_vals:
3451 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3452 self.outf.write("Stored msDS-SPNSuffixes attributes[%d]:\n" % len(stored_spn_vals))
3453 for v in stored_spn_vals:
3454 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3456 if not require_update:
3460 update_upn_vals = []
3461 update_upn_vals.extend(stored_upn_vals)
3464 update_spn_vals = []
3465 update_spn_vals.extend(stored_spn_vals)
3468 for i, v in enumerate(update_upn_vals):
3469 if v.lower() == upn.lower():
3470 raise CommandError("Entry already present for "
3471 "value[%s] specified for "
3472 "--add-upn-suffix" % upn)
3473 update_upn_vals.append(upn)
3476 for upn in delete_upn:
3478 for i, v in enumerate(update_upn_vals):
3479 if v.lower() != upn.lower():
3484 raise CommandError("Entry not found for value[%s] specified for --delete-upn-suffix" % upn)
3486 update_upn_vals.pop(idx)
3490 for i, v in enumerate(update_spn_vals):
3491 if v.lower() == spn.lower():
3492 raise CommandError("Entry already present for "
3493 "value[%s] specified for "
3494 "--add-spn-suffix" % spn)
3495 update_spn_vals.append(spn)
3498 for spn in delete_spn:
3500 for i, v in enumerate(update_spn_vals):
3501 if v.lower() != spn.lower():
3506 raise CommandError("Entry not found for value[%s] specified for --delete-spn-suffix" % spn)
3508 update_spn_vals.pop(idx)
3511 self.outf.write("Update uPNSuffixes attributes[%d]:\n" % len(update_upn_vals))
3512 for v in update_upn_vals:
3513 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3514 self.outf.write("Update msDS-SPNSuffixes attributes[%d]:\n" % len(update_spn_vals))
3515 for v in update_spn_vals:
3516 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3518 update_msg = ldb.Message()
3519 update_msg.dn = stored_msg.dn
3522 update_msg['uPNSuffixes'] = ldb.MessageElement(update_upn_vals,
3523 ldb.FLAG_MOD_REPLACE,
3526 update_msg['msDS-SPNSuffixes'] = ldb.MessageElement(update_spn_vals,
3527 ldb.FLAG_MOD_REPLACE,
3530 local_samdb.modify(update_msg)
3531 except ldb.LdbError as error:
3532 raise self.LocalLdbError(self, error, "failed to update partition dn")
3535 stored_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3537 except RuntimeError as error:
3538 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3540 self.outf.write("Stored forest trust information...\n")
3541 self.write_forest_trust_info(stored_forest_info,
3542 tln=local_lsa_info.dns_domain.string)
3546 lsaString = lsa.String()
3547 lsaString.string = domain
3549 local_lsa.QueryTrustedDomainInfoByName(local_policy,
3551 lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
3552 except NTSTATUSError as error:
3553 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
3554 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
3556 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
3558 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
3559 local_tdo_info.netbios_name.string,
3560 local_tdo_info.domain_name.string,
3561 local_tdo_info.sid))
3563 if not local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
3564 raise CommandError("trusted domain object for domain [%s] is not marked as FOREST_TRANSITIVE." % domain)
3566 if refresh is not None:
3568 local_netlogon = self.new_local_netlogon_connection()
3569 except RuntimeError as error:
3570 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3573 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3574 except RuntimeError as error:
3575 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3577 lsa_update_check = 1
3578 if refresh == "store":
3579 netlogon_update_tdo = netlogon.DS_GFTI_UPDATE_TDO
3581 lsa_update_check = 0
3583 netlogon_update_tdo = 0
3586 # get all information about the remote trust
3587 # this triggers netr_GetForestTrustInformation to the remote domain
3588 # and lsaRSetForestTrustInformation() locally, but new top level
3589 # names are disabled by default.
3590 fresh_forest_info = \
3591 local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3592 local_tdo_info.domain_name.string,
3593 netlogon_update_tdo)
3594 except RuntimeError as error:
3595 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3598 fresh_forest_collision = \
3599 local_lsa.lsaRSetForestTrustInformation(local_policy,
3600 local_tdo_info.domain_name,
3601 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3604 except RuntimeError as error:
3605 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3607 self.outf.write("Fresh forest trust information...\n")
3608 self.write_forest_trust_info(fresh_forest_info,
3609 tln=local_tdo_info.domain_name.string,
3610 collisions=fresh_forest_collision)
3612 if refresh == "store":
3614 lsaString = lsa.String()
3615 lsaString.string = local_tdo_info.domain_name.string
3616 stored_forest_info = \
3617 local_lsa.lsaRQueryForestTrustInformation(local_policy,
3619 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3620 except RuntimeError as error:
3621 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3623 self.outf.write("Stored forest trust information...\n")
3624 self.write_forest_trust_info(stored_forest_info,
3625 tln=local_tdo_info.domain_name.string)
3630 # The none --refresh path
3634 lsaString = lsa.String()
3635 lsaString.string = local_tdo_info.domain_name.string
3636 local_forest_info = \
3637 local_lsa.lsaRQueryForestTrustInformation(local_policy,
3639 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3640 except RuntimeError as error:
3641 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3643 self.outf.write("Local forest trust information...\n")
3644 self.write_forest_trust_info(local_forest_info,
3645 tln=local_tdo_info.domain_name.string)
3647 if not require_update:
3651 entries.extend(local_forest_info.entries)
3652 update_forest_info = lsa.ForestTrustInformation()
3653 update_forest_info.count = len(entries)
3654 update_forest_info.entries = entries
3657 for i, r in enumerate(update_forest_info.entries):
3658 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3660 if update_forest_info.entries[i].flags == 0:
3662 update_forest_info.entries[i].time = 0
3663 update_forest_info.entries[i].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3664 for i, r in enumerate(update_forest_info.entries):
3665 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3667 if update_forest_info.entries[i].flags == 0:
3669 update_forest_info.entries[i].time = 0
3670 update_forest_info.entries[i].flags &= ~lsa.LSA_NB_DISABLED_MASK
3671 update_forest_info.entries[i].flags &= ~lsa.LSA_SID_DISABLED_MASK
3673 for tln in enable_tln:
3675 for i, r in enumerate(update_forest_info.entries):
3676 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3678 if r.forest_trust_data.string.lower() != tln.lower():
3683 raise CommandError("Entry not found for value[%s] specified for --enable-tln" % tln)
3684 if not update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_MASK:
3685 raise CommandError("Entry found for value[%s] specified for --enable-tln is already enabled" % tln)
3686 update_forest_info.entries[idx].time = 0
3687 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3689 for tln in disable_tln:
3691 for i, r in enumerate(update_forest_info.entries):
3692 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3694 if r.forest_trust_data.string.lower() != tln.lower():
3699 raise CommandError("Entry not found for value[%s] specified for --disable-tln" % tln)
3700 if update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_ADMIN:
3701 raise CommandError("Entry found for value[%s] specified for --disable-tln is already disabled" % tln)
3702 update_forest_info.entries[idx].time = 0
3703 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3704 update_forest_info.entries[idx].flags |= lsa.LSA_TLN_DISABLED_ADMIN
3706 for tln_ex in add_tln_ex:
3708 for i, r in enumerate(update_forest_info.entries):
3709 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3711 if r.forest_trust_data.string.lower() != tln_ex.lower():
3716 raise CommandError("Entry already present for value[%s] specified for --add-tln-ex" % tln_ex)
3718 tln_dot = ".%s" % tln_ex.lower()
3720 for i, r in enumerate(update_forest_info.entries):
3721 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3723 r_dot = ".%s" % r.forest_trust_data.string.lower()
3724 if tln_dot == r_dot:
3725 raise CommandError("TLN entry present for value[%s] specified for --add-tln-ex" % tln_ex)
3726 if not tln_dot.endswith(r_dot):
3732 raise CommandError("No TLN parent present for value[%s] specified for --add-tln-ex" % tln_ex)
3734 r = lsa.ForestTrustRecord()
3735 r.type = lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX
3738 r.forest_trust_data.string = tln_ex
3741 entries.extend(update_forest_info.entries)
3742 entries.insert(idx + 1, r)
3743 update_forest_info.count = len(entries)
3744 update_forest_info.entries = entries
3746 for tln_ex in delete_tln_ex:
3748 for i, r in enumerate(update_forest_info.entries):
3749 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3751 if r.forest_trust_data.string.lower() != tln_ex.lower():
3756 raise CommandError("Entry not found for value[%s] specified for --delete-tln-ex" % tln_ex)
3759 entries.extend(update_forest_info.entries)
3761 update_forest_info.count = len(entries)
3762 update_forest_info.entries = entries
3764 for nb in enable_nb:
3766 for i, r in enumerate(update_forest_info.entries):
3767 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3769 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3774 raise CommandError("Entry not found for value[%s] specified for --enable-nb" % nb)
3775 if not update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_MASK:
3776 raise CommandError("Entry found for value[%s] specified for --enable-nb is already enabled" % nb)
3777 update_forest_info.entries[idx].time = 0
3778 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3780 for nb in disable_nb:
3782 for i, r in enumerate(update_forest_info.entries):
3783 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3785 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3790 raise CommandError("Entry not found for value[%s] specified for --delete-nb" % nb)
3791 if update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_ADMIN:
3792 raise CommandError("Entry found for value[%s] specified for --disable-nb is already disabled" % nb)
3793 update_forest_info.entries[idx].time = 0
3794 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3795 update_forest_info.entries[idx].flags |= lsa.LSA_NB_DISABLED_ADMIN
3797 for sid in enable_sid:
3799 for i, r in enumerate(update_forest_info.entries):
3800 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3802 if r.forest_trust_data.domain_sid != sid:
3807 raise CommandError("Entry not found for value[%s] specified for --enable-sid" % sid)
3808 if not update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_MASK:
3809 raise CommandError("Entry found for value[%s] specified for --enable-sid is already enabled" % nb)
3810 update_forest_info.entries[idx].time = 0
3811 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3813 for sid in disable_sid:
3815 for i, r in enumerate(update_forest_info.entries):
3816 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3818 if r.forest_trust_data.domain_sid != sid:
3823 raise CommandError("Entry not found for value[%s] specified for --delete-sid" % sid)
3824 if update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_ADMIN:
3825 raise CommandError("Entry found for value[%s] specified for --disable-sid is already disabled" % nb)
3826 update_forest_info.entries[idx].time = 0
3827 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3828 update_forest_info.entries[idx].flags |= lsa.LSA_SID_DISABLED_ADMIN
3831 update_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3832 local_tdo_info.domain_name,
3833 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3834 update_forest_info, 0)
3835 except RuntimeError as error:
3836 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3838 self.outf.write("Updated forest trust information...\n")
3839 self.write_forest_trust_info(update_forest_info,
3840 tln=local_tdo_info.domain_name.string,
3841 collisions=update_forest_collision)
3844 lsaString = lsa.String()
3845 lsaString.string = local_tdo_info.domain_name.string
3846 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3848 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3849 except RuntimeError as error:
3850 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3852 self.outf.write("Stored forest trust information...\n")
3853 self.write_forest_trust_info(stored_forest_info,
3854 tln=local_tdo_info.domain_name.string)
3858 class cmd_domain_tombstones_expunge(Command):
3859 """Expunge tombstones from the database.
3861 This command expunges tombstones from the database."""
3862 synopsis = "%prog NC [NC [...]] [options]"
3865 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
3866 metavar="URL", dest="H"),
3867 Option("--current-time",
3868 help="The current time to evaluate the tombstone lifetime from, expressed as YYYY-MM-DD",
3870 Option("--tombstone-lifetime", help="Number of days a tombstone should be preserved for", type=int),
3873 takes_args = ["nc*"]
3875 takes_optiongroups = {
3876 "sambaopts": options.SambaOptions,
3877 "credopts": options.CredentialsOptions,
3878 "versionopts": options.VersionOptions,
3881 def run(self, *ncs, **kwargs):
3882 sambaopts = kwargs.get("sambaopts")
3883 credopts = kwargs.get("credopts")
3884 versionpts = kwargs.get("versionopts")
3886 current_time_string = kwargs.get("current_time")
3887 tombstone_lifetime = kwargs.get("tombstone_lifetime")
3888 lp = sambaopts.get_loadparm()
3889 creds = credopts.get_credentials(lp)
3890 samdb = SamDB(url=H, session_info=system_session(),
3891 credentials=creds, lp=lp)
3893 if current_time_string is not None:
3894 current_time_obj = time.strptime(current_time_string, "%Y-%m-%d")
3895 current_time = int(time.mktime(current_time_obj))
3898 current_time = int(time.time())
3901 res = samdb.search(expression="", base="", scope=ldb.SCOPE_BASE,
3902 attrs=["namingContexts"])
3905 for nc in res[0]["namingContexts"]:
3910 started_transaction = False
3912 samdb.transaction_start()
3913 started_transaction = True
3915 removed_links) = samdb.garbage_collect_tombstones(ncs,
3916 current_time=current_time,
3917 tombstone_lifetime=tombstone_lifetime)
3919 except Exception as err:
3920 if started_transaction:
3921 samdb.transaction_cancel()
3922 raise CommandError("Failed to expunge / garbage collect tombstones", err)
3924 samdb.transaction_commit()
3926 self.outf.write("Removed %d objects and %d links successfully\n"
3927 % (removed_objects, removed_links))
3930 class cmd_domain_trust(SuperCommand):
3931 """Domain and forest trust management."""
3934 subcommands["list"] = cmd_domain_trust_list()
3935 subcommands["show"] = cmd_domain_trust_show()
3936 subcommands["create"] = cmd_domain_trust_create()
3937 subcommands["delete"] = cmd_domain_trust_delete()
3938 subcommands["validate"] = cmd_domain_trust_validate()
3939 subcommands["namespaces"] = cmd_domain_trust_namespaces()
3942 class cmd_domain_tombstones(SuperCommand):
3943 """Domain tombstone and recycled object management."""
3946 subcommands["expunge"] = cmd_domain_tombstones_expunge()
3949 class ldif_schema_update:
3950 """Helper class for applying LDIF schema updates"""
3953 self.is_defunct = False
3954 self.unknown_oid = None
3958 def can_ignore_failure(self, error):
3959 """Checks if we can safely ignore failure to apply an LDIF update"""
3960 (num, errstr) = error.args
3962 # Microsoft has marked objects as defunct that Samba doesn't know about
3963 if num == ldb.ERR_NO_SUCH_OBJECT and self.is_defunct:
3964 print("Defunct object %s doesn't exist, skipping" % self.dn)
3966 elif self.unknown_oid is not None:
3967 print("Skipping unknown OID %s for object %s" % (self.unknown_oid, self.dn))
3972 def apply(self, samdb):
3973 """Applies a single LDIF update to the schema"""
3977 samdb.modify_ldif(self.ldif, controls=['relax:0'])
3978 except ldb.LdbError as e:
3979 if e.args[0] == ldb.ERR_INVALID_ATTRIBUTE_SYNTAX:
3981 # REFRESH after a failed change
3983 # Otherwise the OID-to-attribute mapping in
3984 # _apply_updates_in_file() won't work, because it
3985 # can't lookup the new OID in the schema
3986 samdb.set_schema_update_now()
3988 samdb.modify_ldif(self.ldif, controls=['relax:0'])
3991 except ldb.LdbError as e:
3992 if self.can_ignore_failure(e):
3995 print("Exception: %s" % e)
3996 print("Encountered while trying to apply the following LDIF")
3997 print("----------------------------------------------------")
3998 print("%s" % self.ldif)
4005 class cmd_domain_schema_upgrade(Command):
4006 """Domain schema upgrading"""
4008 synopsis = "%prog [options]"
4010 takes_optiongroups = {
4011 "sambaopts": options.SambaOptions,
4012 "versionopts": options.VersionOptions,
4013 "credopts": options.CredentialsOptions,
4017 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
4018 metavar="URL", dest="H"),
4019 Option("-q", "--quiet", help="Be quiet", action="store_true"), # unused
4020 Option("-v", "--verbose", help="Be verbose", action="store_true"),
4021 Option("--schema", type="choice", metavar="SCHEMA",
4022 choices=["2012", "2012_R2"],
4023 help="The schema file to upgrade to. Default is (Windows) 2012_R2.",
4025 Option("--ldf-file", type=str, default=None,
4026 help="Just apply the schema updates in the adprep/.LDF file(s) specified"),
4027 Option("--base-dir", type=str, default=None,
4028 help="Location of ldf files Default is ${SETUPDIR}/adprep.")
4031 def _apply_updates_in_file(self, samdb, ldif_file):
4033 Applies a series of updates specified in an .LDIF file. The .LDIF file
4034 is based on the adprep Schema updates provided by Microsoft.
4037 ldif_op = ldif_schema_update()
4039 # parse the file line by line and work out each update operation to apply
4040 for line in ldif_file:
4042 line = line.rstrip()
4044 # the operations in the .LDIF file are separated by blank lines. If
4045 # we hit a blank line, try to apply the update we've parsed so far
4048 # keep going if we haven't parsed anything yet
4049 if ldif_op.ldif == '':
4052 # Apply the individual change
4053 count += ldif_op.apply(samdb)
4055 # start storing the next operation from scratch again
4056 ldif_op = ldif_schema_update()
4059 # replace the placeholder domain name in the .ldif file with the real domain
4060 if line.upper().endswith('DC=X'):
4061 line = line[:-len('DC=X')] + str(samdb.get_default_basedn())
4062 elif line.upper().endswith('CN=X'):
4063 line = line[:-len('CN=X')] + str(samdb.get_default_basedn())
4065 values = line.split(':')
4067 if values[0].lower() == 'dn':
4068 ldif_op.dn = values[1].strip()
4070 # replace the Windows-specific operation with the Samba one
4071 if values[0].lower() == 'changetype':
4072 line = line.lower().replace(': ntdsschemaadd',
4074 line = line.lower().replace(': ntdsschemamodify',
4077 if values[0].lower() in ['rdnattid', 'subclassof',
4078 'systemposssuperiors',
4080 'systemauxiliaryclass']:
4083 # The Microsoft updates contain some OIDs we don't recognize.
4084 # Query the DB to see if we can work out the OID this update is
4085 # referring to. If we find a match, then replace the OID with
4086 # the ldapDisplayname
4088 res = samdb.search(base=samdb.get_schema_basedn(),
4089 expression="(|(attributeId=%s)(governsId=%s))" %
4091 attrs=['ldapDisplayName'])
4094 ldif_op.unknown_oid = value
4096 display_name = str(res[0]['ldapDisplayName'][0])
4097 line = line.replace(value, ' ' + display_name)
4099 # Microsoft has marked objects as defunct that Samba doesn't know about
4100 if values[0].lower() == 'isdefunct' and values[1].strip().lower() == 'true':
4101 ldif_op.is_defunct = True
4103 # Samba has added the showInAdvancedViewOnly attribute to all objects,
4104 # so rather than doing an add, we need to do a replace
4105 if values[0].lower() == 'add' and values[1].strip().lower() == 'showinadvancedviewonly':
4106 line = 'replace: showInAdvancedViewOnly'
4108 # Add the line to the current LDIF operation (including the newline
4109 # we stripped off at the start of the loop)
4110 ldif_op.ldif += line + '\n'
4114 def _apply_update(self, samdb, update_file, base_dir):
4115 """Wrapper function for parsing an LDIF file and applying the updates"""
4117 print("Applying %s updates..." % update_file)
4121 ldif_file = open(os.path.join(base_dir, update_file))
4123 count = self._apply_updates_in_file(samdb, ldif_file)
4129 print("%u changes applied" % count)
4133 def run(self, **kwargs):
4134 from samba.ms_schema_markdown import read_ms_markdown
4135 from samba.schema import Schema
4137 updates_allowed_overriden = False
4138 sambaopts = kwargs.get("sambaopts")
4139 credopts = kwargs.get("credopts")
4140 versionpts = kwargs.get("versionopts")
4141 lp = sambaopts.get_loadparm()
4142 creds = credopts.get_credentials(lp)
4144 target_schema = kwargs.get("schema")
4145 ldf_files = kwargs.get("ldf_file")
4146 base_dir = kwargs.get("base_dir")
4150 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
4152 # we're not going to get far if the config doesn't allow schema updates
4153 if lp.get("dsdb:schema update allowed") is None:
4154 lp.set("dsdb:schema update allowed", "yes")
4155 print("Temporarily overriding 'dsdb:schema update allowed' setting")
4156 updates_allowed_overriden = True
4158 own_dn = ldb.Dn(samdb, samdb.get_dsServiceName())
4159 master = get_fsmo_roleowner(samdb, str(samdb.get_schema_basedn()),
4161 if own_dn != master:
4162 raise CommandError("This server is not the schema master.")
4164 # if specific LDIF files were specified, just apply them
4166 schema_updates = ldf_files.split(",")
4170 # work out the version of the target schema we're upgrading to
4171 end = Schema.get_version(target_schema)
4173 # work out the version of the schema we're currently using
4174 res = samdb.search(base=samdb.get_schema_basedn(),
4175 scope=ldb.SCOPE_BASE, attrs=['objectVersion'])
4178 raise CommandError('Could not determine current schema version')
4179 start = int(res[0]['objectVersion'][0]) + 1
4181 diff_dir = setup_path("adprep/WindowsServerDocs")
4182 if base_dir is None:
4183 # Read from the Schema-Updates.md file
4184 temp_folder = tempfile.mkdtemp()
4186 update_file = setup_path("adprep/WindowsServerDocs/Schema-Updates.md")
4189 read_ms_markdown(update_file, temp_folder)
4190 except Exception as e:
4191 print("Exception in markdown parsing: %s" % e)
4192 shutil.rmtree(temp_folder)
4193 raise CommandError('Failed to upgrade schema')
4195 base_dir = temp_folder
4197 for version in range(start, end + 1):
4198 update = 'Sch%d.ldf' % version
4199 schema_updates.append(update)
4201 # Apply patches if we parsed the Schema-Updates.md file
4202 diff = os.path.abspath(os.path.join(diff_dir, update + '.diff'))
4203 if temp_folder and os.path.exists(diff):
4205 p = subprocess.Popen(['patch', update, '-i', diff],
4206 stdout=subprocess.PIPE,
4207 stderr=subprocess.PIPE, cwd=temp_folder)
4208 except (OSError, IOError):
4209 shutil.rmtree(temp_folder)
4210 raise CommandError("Failed to upgrade schema. Check if 'patch' is installed.")
4212 stdout, stderr = p.communicate()
4215 print("Exception in patch: %s\n%s" % (stdout, stderr))
4216 shutil.rmtree(temp_folder)
4217 raise CommandError('Failed to upgrade schema')
4219 print("Patched %s using %s" % (update, diff))
4221 if base_dir is None:
4222 base_dir = setup_path("adprep")
4224 samdb.transaction_start()
4226 error_encountered = False
4229 # Apply the schema updates needed to move to the new schema version
4230 for ldif_file in schema_updates:
4231 count += self._apply_update(samdb, ldif_file, base_dir)
4234 samdb.transaction_commit()
4235 print("Schema successfully updated")
4237 print("No changes applied to schema")
4238 samdb.transaction_cancel()
4239 except Exception as e:
4240 print("Exception: %s" % e)
4241 print("Error encountered, aborting schema upgrade")
4242 samdb.transaction_cancel()
4243 error_encountered = True
4245 if updates_allowed_overriden:
4246 lp.set("dsdb:schema update allowed", "no")
4249 shutil.rmtree(temp_folder)
4251 if error_encountered:
4252 raise CommandError('Failed to upgrade schema')
4255 class cmd_domain_functional_prep(Command):
4256 """Domain functional level preparation"""
4258 synopsis = "%prog [options]"
4260 takes_optiongroups = {
4261 "sambaopts": options.SambaOptions,
4262 "versionopts": options.VersionOptions,
4263 "credopts": options.CredentialsOptions,
4267 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
4268 metavar="URL", dest="H"),
4269 Option("-q", "--quiet", help="Be quiet", action="store_true"),
4270 Option("-v", "--verbose", help="Be verbose", action="store_true"),
4271 Option("--function-level", type="choice", metavar="FUNCTION_LEVEL",
4272 choices=["2008_R2", "2012", "2012_R2"],
4273 help="The schema file to upgrade to. Default is (Windows) 2012_R2.",
4275 Option("--forest-prep", action="store_true",
4276 help="Run the forest prep (by default, both the domain and forest prep are run)."),
4277 Option("--domain-prep", action="store_true",
4278 help="Run the domain prep (by default, both the domain and forest prep are run).")
4281 def run(self, **kwargs):
4282 updates_allowed_overriden = False
4283 sambaopts = kwargs.get("sambaopts")
4284 credopts = kwargs.get("credopts")
4285 versionpts = kwargs.get("versionopts")
4286 lp = sambaopts.get_loadparm()
4287 creds = credopts.get_credentials(lp)
4289 target_level = string_version_to_constant[kwargs.get("function_level")]
4290 forest_prep = kwargs.get("forest_prep")
4291 domain_prep = kwargs.get("domain_prep")
4293 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
4295 # we're not going to get far if the config doesn't allow schema updates
4296 if lp.get("dsdb:schema update allowed") is None:
4297 lp.set("dsdb:schema update allowed", "yes")
4298 print("Temporarily overriding 'dsdb:schema update allowed' setting")
4299 updates_allowed_overriden = True
4301 if forest_prep is None and domain_prep is None:
4305 own_dn = ldb.Dn(samdb, samdb.get_dsServiceName())
4307 master = get_fsmo_roleowner(samdb, str(samdb.get_schema_basedn()),
4309 if own_dn != master:
4310 raise CommandError("This server is not the schema master.")
4313 domain_dn = samdb.domain_dn()
4314 infrastructure_dn = "CN=Infrastructure," + domain_dn
4315 master = get_fsmo_roleowner(samdb, infrastructure_dn,
4317 if own_dn != master:
4318 raise CommandError("This server is not the infrastructure master.")
4321 samdb.transaction_start()
4322 error_encountered = False
4324 from samba.forest_update import ForestUpdate
4325 forest = ForestUpdate(samdb, fix=True)
4327 forest.check_updates_iterator([53, 79, 80, 81, 82, 83])
4328 forest.check_updates_functional_level(target_level,
4329 DS_DOMAIN_FUNCTION_2008_R2,
4330 update_revision=True)
4332 samdb.transaction_commit()
4333 except Exception as e:
4334 print("Exception: %s" % e)
4335 samdb.transaction_cancel()
4336 error_encountered = True
4339 samdb.transaction_start()
4340 error_encountered = False
4342 from samba.domain_update import DomainUpdate
4344 domain = DomainUpdate(samdb, fix=True)
4345 domain.check_updates_functional_level(target_level,
4346 DS_DOMAIN_FUNCTION_2008,
4347 update_revision=True)
4349 samdb.transaction_commit()
4350 except Exception as e:
4351 print("Exception: %s" % e)
4352 samdb.transaction_cancel()
4353 error_encountered = True
4355 if updates_allowed_overriden:
4356 lp.set("dsdb:schema update allowed", "no")
4358 if error_encountered:
4359 raise CommandError('Failed to perform functional prep')
4362 class cmd_domain(SuperCommand):
4363 """Domain management."""
4366 subcommands["demote"] = cmd_domain_demote()
4367 if cmd_domain_export_keytab is not None:
4368 subcommands["exportkeytab"] = cmd_domain_export_keytab()
4369 subcommands["info"] = cmd_domain_info()
4370 subcommands["provision"] = cmd_domain_provision()
4371 subcommands["join"] = cmd_domain_join()
4372 subcommands["dcpromo"] = cmd_domain_dcpromo()
4373 subcommands["level"] = cmd_domain_level()
4374 subcommands["passwordsettings"] = cmd_domain_passwordsettings()
4375 subcommands["classicupgrade"] = cmd_domain_classicupgrade()
4376 subcommands["samba3upgrade"] = cmd_domain_samba3upgrade()
4377 subcommands["trust"] = cmd_domain_trust()
4378 subcommands["tombstones"] = cmd_domain_tombstones()
4379 subcommands["schemaupgrade"] = cmd_domain_schema_upgrade()
4380 subcommands["functionalprep"] = cmd_domain_functional_prep()
4381 subcommands["backup"] = cmd_domain_backup()