3 # Copyright Matthias Dieter Wallnoefer 2009
4 # Copyright Andrew Kroeger 2009
5 # Copyright Jelmer Vernooij 2007-2012
6 # Copyright Giampaolo Lauria 2011
7 # Copyright Matthieu Patou <mat@matws.net> 2011
8 # Copyright Andrew Bartlett 2008-2015
9 # Copyright Stefan Metzmacher 2012
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 3 of the License, or
14 # (at your option) any later version.
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 from __future__ import print_function
26 from __future__ import division
27 import samba.getopt as options
39 from samba import ntstatus
40 from samba import NTSTATUSError
41 from samba import werror
42 from getpass import getpass
43 from samba.net import Net, LIBNET_JOIN_AUTOMATIC
45 from samba.join import join_RODC, join_DC, join_subdomain
46 from samba.auth import system_session
47 from samba.samdb import SamDB, get_default_backend_store
48 from samba.ndr import ndr_unpack, ndr_pack, ndr_print
49 from samba.dcerpc import drsuapi
50 from samba.dcerpc import drsblobs
51 from samba.dcerpc import lsa
52 from samba.dcerpc import netlogon
53 from samba.dcerpc import security
54 from samba.dcerpc import nbt
55 from samba.dcerpc import misc
56 from samba.dcerpc.samr import DOMAIN_PASSWORD_COMPLEX, DOMAIN_PASSWORD_STORE_CLEARTEXT
57 from samba.netcmd import (
63 from samba.netcmd.fsmo import get_fsmo_roleowner
64 from samba.netcmd.common import netcmd_get_domain_infos_via_cldap
65 from samba.samba3 import Samba3
66 from samba.samba3 import param as s3param
67 from samba.upgrade import upgrade_from_samba3
68 from samba.drs_utils import (
69 sendDsReplicaSync, drsuapi_connect, drsException,
71 from samba import remove_dc, arcfour_encrypt, string_to_byte_array
73 from samba.dsdb import (
74 DS_DOMAIN_FUNCTION_2000,
75 DS_DOMAIN_FUNCTION_2003,
76 DS_DOMAIN_FUNCTION_2003_MIXED,
77 DS_DOMAIN_FUNCTION_2008,
78 DS_DOMAIN_FUNCTION_2008_R2,
79 DS_DOMAIN_FUNCTION_2012,
80 DS_DOMAIN_FUNCTION_2012_R2,
81 DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL,
82 DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL,
83 UF_WORKSTATION_TRUST_ACCOUNT,
84 UF_SERVER_TRUST_ACCOUNT,
85 UF_TRUSTED_FOR_DELEGATION,
86 UF_PARTIAL_SECRETS_ACCOUNT
89 from samba.provision import (
92 DEFAULT_MIN_PWD_LENGTH,
96 from samba.provision.common import (
102 from samba.netcmd.pso import cmd_domain_passwordsettings_pso
103 from samba.netcmd.domain_backup import cmd_domain_backup
105 string_version_to_constant = {
106 "2008_R2" : DS_DOMAIN_FUNCTION_2008_R2,
107 "2012": DS_DOMAIN_FUNCTION_2012,
108 "2012_R2": DS_DOMAIN_FUNCTION_2012_R2,
111 common_provision_join_options = [
112 Option("--machinepass", type="string", metavar="PASSWORD",
113 help="choose machine password (otherwise random)"),
114 Option("--plaintext-secrets", action="store_true",
115 help="Store secret/sensitive values as plain text on disk" +
116 "(default is to encrypt secret/ensitive values)"),
117 Option("--backend-store", type="choice", metavar="BACKENDSTORE",
118 choices=["tdb", "mdb"],
119 help="Specify the database backend to be used "
120 "(default is %s)" % get_default_backend_store()),
121 Option("--targetdir", metavar="DIR",
122 help="Set target directory (where to store provision)", type=str),
123 Option("-q", "--quiet", help="Be quiet", action="store_true"),
126 common_join_options = [
127 Option("--server", help="DC to join", type=str),
128 Option("--site", help="site to join", type=str),
129 Option("--domain-critical-only",
130 help="only replicate critical domain objects",
131 action="store_true"),
132 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
133 choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
134 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
135 "BIND9_DLZ uses samba4 AD to store zone information, "
136 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
137 default="SAMBA_INTERNAL"),
138 Option("-v", "--verbose", help="Be verbose", action="store_true")
141 common_ntvfs_options = [
142 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
146 def get_testparm_var(testparm, smbconf, varname):
147 errfile = open(os.devnull, 'w')
148 p = subprocess.Popen([testparm, '-s', '-l',
149 '--parameter-name=%s' % varname, smbconf],
150 stdout=subprocess.PIPE, stderr=errfile)
151 (out,err) = p.communicate()
153 lines = out.split('\n')
155 return lines[0].strip()
159 import samba.dckeytab
161 cmd_domain_export_keytab = None
163 class cmd_domain_export_keytab(Command):
164 """Dump Kerberos keys of the domain into a keytab."""
166 synopsis = "%prog <keytab> [options]"
168 takes_optiongroups = {
169 "sambaopts": options.SambaOptions,
170 "credopts": options.CredentialsOptions,
171 "versionopts": options.VersionOptions,
175 Option("--principal", help="extract only this principal", type=str),
178 takes_args = ["keytab"]
180 def run(self, keytab, credopts=None, sambaopts=None, versionopts=None, principal=None):
181 lp = sambaopts.get_loadparm()
183 net.export_keytab(keytab=keytab, principal=principal)
186 class cmd_domain_info(Command):
187 """Print basic info about a domain and the DC passed as parameter."""
189 synopsis = "%prog <ip_address> [options]"
194 takes_optiongroups = {
195 "sambaopts": options.SambaOptions,
196 "credopts": options.CredentialsOptions,
197 "versionopts": options.VersionOptions,
200 takes_args = ["address"]
202 def run(self, address, credopts=None, sambaopts=None, versionopts=None):
203 lp = sambaopts.get_loadparm()
205 res = netcmd_get_domain_infos_via_cldap(lp, None, address)
207 raise CommandError("Invalid IP address '" + address + "'!")
208 self.outf.write("Forest : %s\n" % res.forest)
209 self.outf.write("Domain : %s\n" % res.dns_domain)
210 self.outf.write("Netbios domain : %s\n" % res.domain_name)
211 self.outf.write("DC name : %s\n" % res.pdc_dns_name)
212 self.outf.write("DC netbios name : %s\n" % res.pdc_name)
213 self.outf.write("Server site : %s\n" % res.server_site)
214 self.outf.write("Client site : %s\n" % res.client_site)
217 class cmd_domain_provision(Command):
218 """Provision a domain."""
220 synopsis = "%prog [options]"
222 takes_optiongroups = {
223 "sambaopts": options.SambaOptions,
224 "versionopts": options.VersionOptions,
228 Option("--interactive", help="Ask for names", action="store_true"),
229 Option("--domain", type="string", metavar="DOMAIN",
230 help="NetBIOS domain name to use"),
231 Option("--domain-guid", type="string", metavar="GUID",
232 help="set domainguid (otherwise random)"),
233 Option("--domain-sid", type="string", metavar="SID",
234 help="set domainsid (otherwise random)"),
235 Option("--ntds-guid", type="string", metavar="GUID",
236 help="set NTDS object GUID (otherwise random)"),
237 Option("--invocationid", type="string", metavar="GUID",
238 help="set invocationid (otherwise random)"),
239 Option("--host-name", type="string", metavar="HOSTNAME",
240 help="set hostname"),
241 Option("--host-ip", type="string", metavar="IPADDRESS",
242 help="set IPv4 ipaddress"),
243 Option("--host-ip6", type="string", metavar="IP6ADDRESS",
244 help="set IPv6 ipaddress"),
245 Option("--site", type="string", metavar="SITENAME",
246 help="set site name"),
247 Option("--adminpass", type="string", metavar="PASSWORD",
248 help="choose admin password (otherwise random)"),
249 Option("--krbtgtpass", type="string", metavar="PASSWORD",
250 help="choose krbtgt password (otherwise random)"),
251 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
252 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
253 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
254 "BIND9_FLATFILE uses bind9 text database to store zone information, "
255 "BIND9_DLZ uses samba4 AD to store zone information, "
256 "NONE skips the DNS setup entirely (not recommended)",
257 default="SAMBA_INTERNAL"),
258 Option("--dnspass", type="string", metavar="PASSWORD",
259 help="choose dns password (otherwise random)"),
260 Option("--root", type="string", metavar="USERNAME",
261 help="choose 'root' unix username"),
262 Option("--nobody", type="string", metavar="USERNAME",
263 help="choose 'nobody' user"),
264 Option("--users", type="string", metavar="GROUPNAME",
265 help="choose 'users' group"),
266 Option("--blank", action="store_true",
267 help="do not add users or groups, just the structure"),
268 Option("--server-role", type="choice", metavar="ROLE",
269 choices=["domain controller", "dc", "member server", "member", "standalone"],
270 help="The server role (domain controller | dc | member server | member | standalone). Default is dc.",
271 default="domain controller"),
272 Option("--function-level", type="choice", metavar="FOR-FUN-LEVEL",
273 choices=["2000", "2003", "2008", "2008_R2"],
274 help="The domain and forest function level (2000 | 2003 | 2008 | 2008_R2 - always native). Default is (Windows) 2008_R2 Native.",
276 Option("--base-schema", type="choice", metavar="BASE-SCHEMA",
277 choices=["2008_R2", "2008_R2_old", "2012", "2012_R2"],
278 help="The base schema files to use. Default is (Windows) 2008_R2.",
280 Option("--next-rid", type="int", metavar="NEXTRID", default=1000,
281 help="The initial nextRid value (only needed for upgrades). Default is 1000."),
282 Option("--partitions-only",
283 help="Configure Samba's partitions, but do not modify them (ie, join a BDC)", action="store_true"),
284 Option("--use-rfc2307", action="store_true", help="Use AD to store posix attributes (default = no)"),
288 Option("--ldapadminpass", type="string", metavar="PASSWORD",
289 help="choose password to set between Samba and its LDAP backend (otherwise random)"),
290 Option("--ldap-backend-type", type="choice", metavar="LDAP-BACKEND-TYPE",
291 help="Test initialisation support for unsupported LDAP backend type (fedora-ds or openldap) DO NOT USE",
292 choices=["fedora-ds", "openldap"]),
293 Option("--ol-mmr-urls", type="string", metavar="LDAPSERVER",
294 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\""),
295 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",
296 action="store_true"),
297 Option("--slapd-path", type="string", metavar="SLAPD-PATH",
298 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."),
299 Option("--ldap-backend-extra-port", type="int", metavar="LDAP-BACKEND-EXTRA-PORT", help="Additional TCP port for LDAP backend server (to use for replication)"),
300 Option("--ldap-backend-forced-uri", type="string", metavar="LDAP-BACKEND-FORCED-URI",
301 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"),
302 Option("--ldap-backend-nosync", help="Configure LDAP backend not to call fsync() (for performance in test environments)", action="store_true"),
306 Option("--use-xattrs", type="choice", choices=["yes","no","auto"],
307 metavar="[yes|no|auto]",
308 help="Define if we should use the native fs capabilities or a tdb file for "
309 "storing attributes likes ntacl when --use-ntvfs is set. "
310 "auto tries to make an inteligent guess based on the user rights and system capabilities",
314 takes_options.extend(common_provision_join_options)
316 if os.getenv('TEST_LDAP', "no") == "yes":
317 takes_options.extend(openldap_options)
319 if samba.is_ntvfs_fileserver_built():
320 takes_options.extend(common_ntvfs_options)
321 takes_options.extend(ntvfs_options)
325 def run(self, sambaopts=None, versionopts=None,
348 ldap_backend_type=None,
352 partitions_only=None,
359 ldap_backend_nosync=None,
360 ldap_backend_extra_port=None,
361 ldap_backend_forced_uri=None,
362 ldap_dryrun_mode=None,
364 plaintext_secrets=False,
367 self.logger = self.get_logger("provision")
369 self.logger.setLevel(logging.WARNING)
371 self.logger.setLevel(logging.INFO)
373 lp = sambaopts.get_loadparm()
374 smbconf = lp.configfile
376 if dns_forwarder is not None:
377 suggested_forwarder = dns_forwarder
379 suggested_forwarder = self._get_nameserver_ip()
380 if suggested_forwarder is None:
381 suggested_forwarder = "none"
383 if len(self.raw_argv) == 1:
387 from getpass import getpass
390 def ask(prompt, default=None):
391 if default is not None:
392 print("%s [%s]: " % (prompt, default), end=' ')
394 print("%s: " % (prompt,), end=' ')
395 return sys.stdin.readline().rstrip("\n") or default
398 default = socket.getfqdn().split(".", 1)[1].upper()
401 realm = ask("Realm", default)
402 if realm in (None, ""):
403 raise CommandError("No realm set!")
406 default = realm.split(".")[0]
409 domain = ask("Domain", default)
411 raise CommandError("No domain set!")
413 server_role = ask("Server Role (dc, member, standalone)", "dc")
415 dns_backend = ask("DNS backend (SAMBA_INTERNAL, BIND9_FLATFILE, BIND9_DLZ, NONE)", "SAMBA_INTERNAL")
416 if dns_backend in (None, ''):
417 raise CommandError("No DNS backend set!")
419 if dns_backend == "SAMBA_INTERNAL":
420 dns_forwarder = ask("DNS forwarder IP address (write 'none' to disable forwarding)", suggested_forwarder)
421 if dns_forwarder.lower() in (None, 'none'):
422 suggested_forwarder = None
426 adminpassplain = getpass("Administrator password: ")
427 issue = self._adminpass_issue(adminpassplain)
429 self.errf.write("%s.\n" % issue)
431 adminpassverify = getpass("Retype password: ")
432 if not adminpassplain == adminpassverify:
433 self.errf.write("Sorry, passwords do not match.\n")
435 adminpass = adminpassplain
439 realm = sambaopts._lp.get('realm')
441 raise CommandError("No realm set!")
443 raise CommandError("No domain set!")
446 issue = self._adminpass_issue(adminpass)
448 raise CommandError(issue)
450 self.logger.info("Administrator password will be set randomly!")
452 if function_level == "2000":
453 dom_for_fun_level = DS_DOMAIN_FUNCTION_2000
454 elif function_level == "2003":
455 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
456 elif function_level == "2008":
457 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008
458 elif function_level == "2008_R2":
459 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
461 if dns_backend == "SAMBA_INTERNAL" and dns_forwarder is None:
462 dns_forwarder = suggested_forwarder
464 samdb_fill = FILL_FULL
466 samdb_fill = FILL_NT4SYNC
467 elif partitions_only:
468 samdb_fill = FILL_DRS
470 if targetdir is not None:
471 if not os.path.isdir(targetdir):
476 if use_xattrs == "yes":
478 elif use_xattrs == "auto" and use_ntvfs == False:
480 elif use_ntvfs == False:
481 raise CommandError("--use-xattrs=no requires --use-ntvfs (not supported for production use). "
482 "Please re-run with --use-xattrs omitted.")
483 elif use_xattrs == "auto" and not lp.get("posix:eadb"):
485 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
487 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
490 samba.ntacls.setntacl(lp, file.name,
491 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
494 self.logger.info("You are not root or your system does not support xattr, using tdb backend for attributes. ")
499 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.")
500 if ldap_backend_type == "existing":
501 if ldap_backend_forced_uri is not None:
502 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)
504 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")
506 if ldap_backend_forced_uri is not None:
507 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")
509 if domain_sid is not None:
510 domain_sid = security.dom_sid(domain_sid)
512 session = system_session()
513 if backend_store is None:
514 backend_store = get_default_backend_store()
516 result = provision(self.logger,
517 session, smbconf=smbconf, targetdir=targetdir,
518 samdb_fill=samdb_fill, realm=realm, domain=domain,
519 domainguid=domain_guid, domainsid=domain_sid,
521 hostip=host_ip, hostip6=host_ip6,
522 sitename=site, ntdsguid=ntds_guid,
523 invocationid=invocationid, adminpass=adminpass,
524 krbtgtpass=krbtgtpass, machinepass=machinepass,
525 dns_backend=dns_backend, dns_forwarder=dns_forwarder,
526 dnspass=dnspass, root=root, nobody=nobody,
528 serverrole=server_role, dom_for_fun_level=dom_for_fun_level,
529 backend_type=ldap_backend_type,
530 ldapadminpass=ldapadminpass, ol_mmr_urls=ol_mmr_urls, slapd_path=slapd_path,
531 useeadb=eadb, next_rid=next_rid, lp=lp, use_ntvfs=use_ntvfs,
532 use_rfc2307=use_rfc2307, skip_sysvolacl=False,
533 ldap_backend_extra_port=ldap_backend_extra_port,
534 ldap_backend_forced_uri=ldap_backend_forced_uri,
535 nosync=ldap_backend_nosync, ldap_dryrun_mode=ldap_dryrun_mode,
536 base_schema=base_schema,
537 plaintext_secrets=plaintext_secrets,
538 backend_store=backend_store)
540 except ProvisioningError as e:
541 raise CommandError("Provision failed", e)
543 result.report_logger(self.logger)
545 def _get_nameserver_ip(self):
546 """Grab the nameserver IP address from /etc/resolv.conf."""
548 RESOLV_CONF="/etc/resolv.conf"
550 if not path.isfile(RESOLV_CONF):
551 self.logger.warning("Failed to locate %s" % RESOLV_CONF)
556 handle = open(RESOLV_CONF, 'r')
558 if not line.startswith('nameserver'):
560 # we want the last non-space continuous string of the line
561 return line.strip().split()[-1]
563 if handle is not None:
566 self.logger.warning("No nameserver found in %s" % RESOLV_CONF)
568 def _adminpass_issue(self, adminpass):
569 """Returns error string for a bad administrator password,
570 or None if acceptable"""
572 if len(adminpass.decode('utf-8')) < DEFAULT_MIN_PWD_LENGTH:
573 return "Administrator password does not meet the default minimum" \
574 " password length requirement (%d characters)" \
575 % DEFAULT_MIN_PWD_LENGTH
576 elif not samba.check_password_quality(adminpass):
577 return "Administrator password does not meet the default" \
583 class cmd_domain_dcpromo(Command):
584 """Promote an existing domain member or NT4 PDC to an AD DC."""
586 synopsis = "%prog <dnsdomain> [DC|RODC] [options]"
588 takes_optiongroups = {
589 "sambaopts": options.SambaOptions,
590 "versionopts": options.VersionOptions,
591 "credopts": options.CredentialsOptions,
595 takes_options.extend(common_join_options)
597 takes_options.extend(common_provision_join_options)
599 if samba.is_ntvfs_fileserver_built():
600 takes_options.extend(common_ntvfs_options)
603 takes_args = ["domain", "role?"]
605 def run(self, domain, role=None, sambaopts=None, credopts=None,
606 versionopts=None, server=None, site=None, targetdir=None,
607 domain_critical_only=False, parent_domain=None, machinepass=None,
608 use_ntvfs=False, dns_backend=None,
609 quiet=False, verbose=False, plaintext_secrets=False,
611 lp = sambaopts.get_loadparm()
612 creds = credopts.get_credentials(lp)
613 net = Net(creds, lp, server=credopts.ipaddress)
615 logger = self.get_logger()
617 logger.setLevel(logging.DEBUG)
619 logger.setLevel(logging.WARNING)
621 logger.setLevel(logging.INFO)
623 netbios_name = lp.get("netbios name")
629 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
630 site=site, netbios_name=netbios_name, targetdir=targetdir,
631 domain_critical_only=domain_critical_only,
632 machinepass=machinepass, use_ntvfs=use_ntvfs,
633 dns_backend=dns_backend,
634 promote_existing=True, plaintext_secrets=plaintext_secrets,
635 backend_store=backend_store)
637 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
638 site=site, netbios_name=netbios_name, targetdir=targetdir,
639 domain_critical_only=domain_critical_only,
640 machinepass=machinepass, use_ntvfs=use_ntvfs, dns_backend=dns_backend,
641 promote_existing=True, plaintext_secrets=plaintext_secrets,
642 backend_store=backend_store)
644 raise CommandError("Invalid role '%s' (possible values: DC, RODC)" % role)
647 class cmd_domain_join(Command):
648 """Join domain as either member or backup domain controller."""
650 synopsis = "%prog <dnsdomain> [DC|RODC|MEMBER|SUBDOMAIN] [options]"
652 takes_optiongroups = {
653 "sambaopts": options.SambaOptions,
654 "versionopts": options.VersionOptions,
655 "credopts": options.CredentialsOptions,
659 Option("--parent-domain", help="parent domain to create subdomain under", type=str),
660 Option("--adminpass", type="string", metavar="PASSWORD",
661 help="choose adminstrator password when joining as a subdomain (otherwise random)"),
665 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
668 takes_options.extend(common_join_options)
669 takes_options.extend(common_provision_join_options)
671 if samba.is_ntvfs_fileserver_built():
672 takes_options.extend(ntvfs_options)
674 takes_args = ["domain", "role?"]
676 def run(self, domain, role=None, sambaopts=None, credopts=None,
677 versionopts=None, server=None, site=None, targetdir=None,
678 domain_critical_only=False, parent_domain=None, machinepass=None,
679 use_ntvfs=False, dns_backend=None, adminpass=None,
680 quiet=False, verbose=False,
681 plaintext_secrets=False,
683 lp = sambaopts.get_loadparm()
684 creds = credopts.get_credentials(lp)
685 net = Net(creds, lp, server=credopts.ipaddress)
688 site = "Default-First-Site-Name"
690 logger = self.get_logger()
692 logger.setLevel(logging.DEBUG)
694 logger.setLevel(logging.WARNING)
696 logger.setLevel(logging.INFO)
698 netbios_name = lp.get("netbios name")
703 if role is None or role == "MEMBER":
704 (join_password, sid, domain_name) = net.join_member(
705 domain, netbios_name, LIBNET_JOIN_AUTOMATIC,
706 machinepass=machinepass)
708 self.errf.write("Joined domain %s (%s)\n" % (domain_name, sid))
710 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
711 site=site, netbios_name=netbios_name, targetdir=targetdir,
712 domain_critical_only=domain_critical_only,
713 machinepass=machinepass, use_ntvfs=use_ntvfs,
714 dns_backend=dns_backend,
715 plaintext_secrets=plaintext_secrets,
716 backend_store=backend_store)
718 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
719 site=site, netbios_name=netbios_name, targetdir=targetdir,
720 domain_critical_only=domain_critical_only,
721 machinepass=machinepass, use_ntvfs=use_ntvfs,
722 dns_backend=dns_backend,
723 plaintext_secrets=plaintext_secrets,
724 backend_store=backend_store)
725 elif role == "SUBDOMAIN":
727 logger.info("Administrator password will be set randomly!")
729 netbios_domain = lp.get("workgroup")
730 if parent_domain is None:
731 parent_domain = ".".join(domain.split(".")[1:])
732 join_subdomain(logger=logger, server=server, creds=creds, lp=lp, dnsdomain=domain,
733 parent_domain=parent_domain, site=site,
734 netbios_name=netbios_name, netbios_domain=netbios_domain,
735 targetdir=targetdir, machinepass=machinepass,
736 use_ntvfs=use_ntvfs, dns_backend=dns_backend,
738 plaintext_secrets=plaintext_secrets,
739 backend_store=backend_store)
741 raise CommandError("Invalid role '%s' (possible values: MEMBER, DC, RODC, SUBDOMAIN)" % role)
744 class cmd_domain_demote(Command):
745 """Demote ourselves from the role of Domain Controller."""
747 synopsis = "%prog [options]"
750 Option("--server", help="writable DC to write demotion changes on", type=str),
751 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
752 metavar="URL", dest="H"),
753 Option("--remove-other-dead-server", help="Dead DC (name or NTDS GUID) "
754 "to remove ALL references to (rather than this DC)", type=str),
755 Option("-q", "--quiet", help="Be quiet", action="store_true"),
756 Option("-v", "--verbose", help="Be verbose", action="store_true"),
759 takes_optiongroups = {
760 "sambaopts": options.SambaOptions,
761 "credopts": options.CredentialsOptions,
762 "versionopts": options.VersionOptions,
765 def run(self, sambaopts=None, credopts=None,
766 versionopts=None, server=None,
767 remove_other_dead_server=None, H=None,
768 verbose=False, quiet=False):
769 lp = sambaopts.get_loadparm()
770 creds = credopts.get_credentials(lp)
771 net = Net(creds, lp, server=credopts.ipaddress)
773 logger = self.get_logger()
775 logger.setLevel(logging.DEBUG)
777 logger.setLevel(logging.WARNING)
779 logger.setLevel(logging.INFO)
781 if remove_other_dead_server is not None:
782 if server is not None:
783 samdb = SamDB(url="ldap://%s" % server,
784 session_info=system_session(),
785 credentials=creds, lp=lp)
787 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
789 remove_dc.remove_dc(samdb, logger, remove_other_dead_server)
790 except remove_dc.DemoteException as err:
791 raise CommandError("Demote failed: %s" % err)
794 netbios_name = lp.get("netbios name")
795 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
797 res = samdb.search(expression='(&(objectClass=computer)(serverReferenceBL=*))', attrs=["dnsHostName", "name"])
799 raise CommandError("Unable to search for servers")
802 raise CommandError("You are the last server in the domain")
806 if str(e["name"]).lower() != netbios_name.lower():
807 server = e["dnsHostName"]
810 ntds_guid = samdb.get_ntds_GUID()
811 msg = samdb.search(base=str(samdb.get_config_basedn()),
812 scope=ldb.SCOPE_SUBTREE, expression="(objectGUID=%s)" % ntds_guid,
814 if len(msg) == 0 or "options" not in msg[0]:
815 raise CommandError("Failed to find options on %s" % ntds_guid)
818 dsa_options = int(str(msg[0]['options']))
820 res = samdb.search(expression="(fSMORoleOwner=%s)" % str(ntds_dn),
821 controls=["search_options:1:2"])
824 raise CommandError("Current DC is still the owner of %d role(s), use the role command to transfer roles to another DC" % len(res))
826 self.errf.write("Using %s as partner server for the demotion\n" %
828 (drsuapiBind, drsuapi_handle, supportedExtensions) = drsuapi_connect(server, lp, creds)
830 self.errf.write("Deactivating inbound replication\n")
835 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
836 dsa_options |= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
837 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
841 self.errf.write("Asking partner server %s to synchronize from us\n"
843 for part in (samdb.get_schema_basedn(),
844 samdb.get_config_basedn(),
845 samdb.get_root_basedn()):
846 nc = drsuapi.DsReplicaObjectIdentifier()
849 req1 = drsuapi.DsReplicaSyncRequest1()
850 req1.naming_context = nc;
851 req1.options = drsuapi.DRSUAPI_DRS_WRIT_REP
852 req1.source_dsa_guid = misc.GUID(ntds_guid)
855 drsuapiBind.DsReplicaSync(drsuapi_handle, 1, req1)
856 except RuntimeError as e1:
857 (werr, string) = e1.args
858 if werr == werror.WERR_DS_DRA_NO_REPLICA:
862 "Error while replicating out last local changes from '%s' for demotion, "
863 "re-enabling inbound replication\n" % part)
864 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
865 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
867 raise CommandError("Error while sending a DsReplicaSync for partition '%s'" % str(part), string)
869 remote_samdb = SamDB(url="ldap://%s" % server,
870 session_info=system_session(),
871 credentials=creds, lp=lp)
873 self.errf.write("Changing userControl and container\n")
874 res = remote_samdb.search(base=str(remote_samdb.domain_dn()),
875 expression="(&(objectClass=user)(sAMAccountName=%s$))" %
876 netbios_name.upper(),
877 attrs=["userAccountControl"])
879 uac = int(str(res[0]["userAccountControl"]))
881 except Exception as e:
882 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
884 "Error while demoting, re-enabling inbound replication\n")
885 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
886 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
888 raise CommandError("Error while changing account control", e)
891 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
893 "Error while demoting, re-enabling inbound replication")
894 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
895 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
897 raise CommandError("Unable to find object with samaccountName = %s$"
898 " in the remote dc" % netbios_name.upper())
902 uac &= ~(UF_SERVER_TRUST_ACCOUNT|UF_TRUSTED_FOR_DELEGATION|UF_PARTIAL_SECRETS_ACCOUNT)
903 uac |= UF_WORKSTATION_TRUST_ACCOUNT
908 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
909 ldb.FLAG_MOD_REPLACE,
910 "userAccountControl")
912 remote_samdb.modify(msg)
913 except Exception as e:
914 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
916 "Error while demoting, re-enabling inbound replication")
917 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
918 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
921 raise CommandError("Error while changing account control", e)
923 parent = msg.dn.parent()
924 dc_name = res[0].dn.get_rdn_value()
925 rdn = "CN=%s" % dc_name
927 # Let's move to the Computer container
931 computer_dn = ldb.Dn(remote_samdb, "CN=Computers,%s" % str(remote_samdb.domain_dn()))
932 res = remote_samdb.search(base=computer_dn, expression=rdn, scope=ldb.SCOPE_ONELEVEL)
935 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
936 scope=ldb.SCOPE_ONELEVEL)
937 while(len(res) != 0 and i < 100):
939 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
940 scope=ldb.SCOPE_ONELEVEL)
943 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
945 "Error while demoting, re-enabling inbound replication\n")
946 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
947 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
953 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
954 ldb.FLAG_MOD_REPLACE,
955 "userAccountControl")
957 remote_samdb.modify(msg)
959 raise CommandError("Unable to find a slot for renaming %s,"
960 " all names from %s-1 to %s-%d seemed used" %
961 (str(dc_dn), rdn, rdn, i - 9))
963 newrdn = "%s-%d" % (rdn, i)
966 newdn = ldb.Dn(remote_samdb, "%s,%s" % (newrdn, str(computer_dn)))
967 remote_samdb.rename(dc_dn, newdn)
968 except Exception as e:
969 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
971 "Error while demoting, re-enabling inbound replication\n")
972 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
973 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
979 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
980 ldb.FLAG_MOD_REPLACE,
981 "userAccountControl")
983 remote_samdb.modify(msg)
984 raise CommandError("Error while renaming %s to %s" % (str(dc_dn), str(newdn)), e)
987 server_dsa_dn = samdb.get_serverName()
988 domain = remote_samdb.get_root_basedn()
991 req1 = drsuapi.DsRemoveDSServerRequest1()
992 req1.server_dn = str(server_dsa_dn)
993 req1.domain_dn = str(domain)
996 drsuapiBind.DsRemoveDSServer(drsuapi_handle, 1, req1)
997 except RuntimeError as e3:
998 (werr, string) = e3.args
999 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
1001 "Error while demoting, re-enabling inbound replication\n")
1002 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
1003 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
1009 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
1010 ldb.FLAG_MOD_REPLACE,
1011 "userAccountControl")
1012 remote_samdb.modify(msg)
1013 remote_samdb.rename(newdn, dc_dn)
1014 if werr == werror.WERR_DS_DRA_NO_REPLICA:
1015 raise CommandError("The DC %s is not present on (already removed from) the remote server: " % server_dsa_dn, e)
1017 raise CommandError("Error while sending a removeDsServer of %s: " % server_dsa_dn, e)
1019 remove_dc.remove_sysvol_references(remote_samdb, logger, dc_name)
1021 # These are objects under the computer account that should be deleted
1022 for s in ("CN=Enterprise,CN=NTFRS Subscriptions",
1023 "CN=%s, CN=NTFRS Subscriptions" % lp.get("realm"),
1024 "CN=Domain system Volumes (SYSVOL Share), CN=NTFRS Subscriptions",
1025 "CN=NTFRS Subscriptions"):
1027 remote_samdb.delete(ldb.Dn(remote_samdb,
1028 "%s,%s" % (s, str(newdn))))
1029 except ldb.LdbError as l:
1032 # get dns host name for target server to demote, remove dns references
1033 remove_dc.remove_dns_references(remote_samdb, logger, samdb.host_dns_name(),
1034 ignore_no_name=True)
1036 self.errf.write("Demote successful\n")
1039 class cmd_domain_level(Command):
1040 """Raise domain and forest function levels."""
1042 synopsis = "%prog (show|raise <options>) [options]"
1044 takes_optiongroups = {
1045 "sambaopts": options.SambaOptions,
1046 "credopts": options.CredentialsOptions,
1047 "versionopts": options.VersionOptions,
1051 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1052 metavar="URL", dest="H"),
1053 Option("-q", "--quiet", help="Be quiet", action="store_true"), # unused
1054 Option("--forest-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
1055 help="The forest function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)"),
1056 Option("--domain-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
1057 help="The domain function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)")
1060 takes_args = ["subcommand"]
1062 def run(self, subcommand, H=None, forest_level=None, domain_level=None,
1063 quiet=False, credopts=None, sambaopts=None, versionopts=None):
1064 lp = sambaopts.get_loadparm()
1065 creds = credopts.get_credentials(lp, fallback_machine=True)
1067 samdb = SamDB(url=H, session_info=system_session(),
1068 credentials=creds, lp=lp)
1070 domain_dn = samdb.domain_dn()
1072 res_forest = samdb.search("CN=Partitions,%s" % samdb.get_config_basedn(),
1073 scope=ldb.SCOPE_BASE, attrs=["msDS-Behavior-Version"])
1074 assert len(res_forest) == 1
1076 res_domain = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1077 attrs=["msDS-Behavior-Version", "nTMixedDomain"])
1078 assert len(res_domain) == 1
1080 res_dc_s = samdb.search("CN=Sites,%s" % samdb.get_config_basedn(),
1081 scope=ldb.SCOPE_SUBTREE, expression="(objectClass=nTDSDSA)",
1082 attrs=["msDS-Behavior-Version"])
1083 assert len(res_dc_s) >= 1
1085 # default values, since "msDS-Behavior-Version" does not exist on Windows 2000 AD
1086 level_forest = DS_DOMAIN_FUNCTION_2000
1087 level_domain = DS_DOMAIN_FUNCTION_2000
1089 if "msDS-Behavior-Version" in res_forest[0]:
1090 level_forest = int(res_forest[0]["msDS-Behavior-Version"][0])
1091 if "msDS-Behavior-Version" in res_domain[0]:
1092 level_domain = int(res_domain[0]["msDS-Behavior-Version"][0])
1093 level_domain_mixed = int(res_domain[0]["nTMixedDomain"][0])
1096 for msg in res_dc_s:
1097 if "msDS-Behavior-Version" in msg:
1098 if min_level_dc is None or int(msg["msDS-Behavior-Version"][0]) < min_level_dc:
1099 min_level_dc = int(msg["msDS-Behavior-Version"][0])
1101 min_level_dc = DS_DOMAIN_FUNCTION_2000
1102 # well, this is the least
1105 if level_forest < DS_DOMAIN_FUNCTION_2000 or level_domain < DS_DOMAIN_FUNCTION_2000:
1106 raise CommandError("Domain and/or forest function level(s) is/are invalid. Correct them or reprovision!")
1107 if min_level_dc < DS_DOMAIN_FUNCTION_2000:
1108 raise CommandError("Lowest function level of a DC is invalid. Correct this or reprovision!")
1109 if level_forest > level_domain:
1110 raise CommandError("Forest function level is higher than the domain level(s). Correct this or reprovision!")
1111 if level_domain > min_level_dc:
1112 raise CommandError("Domain function level is higher than the lowest function level of a DC. Correct this or reprovision!")
1114 if subcommand == "show":
1115 self.message("Domain and forest function level for domain '%s'" % domain_dn)
1116 if level_forest == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1117 self.message("\nATTENTION: You run SAMBA 4 on a forest function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1118 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1119 self.message("\nATTENTION: You run SAMBA 4 on a domain function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1120 if min_level_dc == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1121 self.message("\nATTENTION: You run SAMBA 4 on a lowest function level of a DC lower than Windows 2003. This isn't supported! Please step-up or upgrade the concerning DC(s)!")
1125 if level_forest == DS_DOMAIN_FUNCTION_2000:
1127 elif level_forest == DS_DOMAIN_FUNCTION_2003_MIXED:
1128 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1129 elif level_forest == DS_DOMAIN_FUNCTION_2003:
1131 elif level_forest == DS_DOMAIN_FUNCTION_2008:
1133 elif level_forest == DS_DOMAIN_FUNCTION_2008_R2:
1135 elif level_forest == DS_DOMAIN_FUNCTION_2012:
1137 elif level_forest == DS_DOMAIN_FUNCTION_2012_R2:
1140 outstr = "higher than 2012 R2"
1141 self.message("Forest function level: (Windows) " + outstr)
1143 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1144 outstr = "2000 mixed (NT4 DC support)"
1145 elif level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed == 0:
1147 elif level_domain == DS_DOMAIN_FUNCTION_2003_MIXED:
1148 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1149 elif level_domain == DS_DOMAIN_FUNCTION_2003:
1151 elif level_domain == DS_DOMAIN_FUNCTION_2008:
1153 elif level_domain == DS_DOMAIN_FUNCTION_2008_R2:
1155 elif level_domain == DS_DOMAIN_FUNCTION_2012:
1157 elif level_domain == DS_DOMAIN_FUNCTION_2012_R2:
1160 outstr = "higher than 2012 R2"
1161 self.message("Domain function level: (Windows) " + outstr)
1163 if min_level_dc == DS_DOMAIN_FUNCTION_2000:
1165 elif min_level_dc == DS_DOMAIN_FUNCTION_2003:
1167 elif min_level_dc == DS_DOMAIN_FUNCTION_2008:
1169 elif min_level_dc == DS_DOMAIN_FUNCTION_2008_R2:
1171 elif min_level_dc == DS_DOMAIN_FUNCTION_2012:
1173 elif min_level_dc == DS_DOMAIN_FUNCTION_2012_R2:
1176 outstr = "higher than 2012 R2"
1177 self.message("Lowest function level of a DC: (Windows) " + outstr)
1179 elif subcommand == "raise":
1182 if domain_level is not None:
1183 if domain_level == "2003":
1184 new_level_domain = DS_DOMAIN_FUNCTION_2003
1185 elif domain_level == "2008":
1186 new_level_domain = DS_DOMAIN_FUNCTION_2008
1187 elif domain_level == "2008_R2":
1188 new_level_domain = DS_DOMAIN_FUNCTION_2008_R2
1189 elif domain_level == "2012":
1190 new_level_domain = DS_DOMAIN_FUNCTION_2012
1191 elif domain_level == "2012_R2":
1192 new_level_domain = DS_DOMAIN_FUNCTION_2012_R2
1194 if new_level_domain <= level_domain and level_domain_mixed == 0:
1195 raise CommandError("Domain function level can't be smaller than or equal to the actual one!")
1196 if new_level_domain > min_level_dc:
1197 raise CommandError("Domain function level can't be higher than the lowest function level of a DC!")
1199 # Deactivate mixed/interim domain support
1200 if level_domain_mixed != 0:
1201 # Directly on the base DN
1203 m.dn = ldb.Dn(samdb, domain_dn)
1204 m["nTMixedDomain"] = ldb.MessageElement("0",
1205 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1209 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup") + ",CN=Partitions,%s" % samdb.get_config_basedn())
1210 m["nTMixedDomain"] = ldb.MessageElement("0",
1211 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1214 except ldb.LdbError as e:
1215 (enum, emsg) = e.args
1216 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1219 # Directly on the base DN
1221 m.dn = ldb.Dn(samdb, domain_dn)
1222 m["msDS-Behavior-Version"]= ldb.MessageElement(
1223 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1224 "msDS-Behavior-Version")
1228 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup")
1229 + ",CN=Partitions,%s" % samdb.get_config_basedn())
1230 m["msDS-Behavior-Version"]= ldb.MessageElement(
1231 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1232 "msDS-Behavior-Version")
1235 except ldb.LdbError as e2:
1236 (enum, emsg) = e2.args
1237 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1240 level_domain = new_level_domain
1241 msgs.append("Domain function level changed!")
1243 if forest_level is not None:
1244 if forest_level == "2003":
1245 new_level_forest = DS_DOMAIN_FUNCTION_2003
1246 elif forest_level == "2008":
1247 new_level_forest = DS_DOMAIN_FUNCTION_2008
1248 elif forest_level == "2008_R2":
1249 new_level_forest = DS_DOMAIN_FUNCTION_2008_R2
1250 elif forest_level == "2012":
1251 new_level_forest = DS_DOMAIN_FUNCTION_2012
1252 elif forest_level == "2012_R2":
1253 new_level_forest = DS_DOMAIN_FUNCTION_2012_R2
1255 if new_level_forest <= level_forest:
1256 raise CommandError("Forest function level can't be smaller than or equal to the actual one!")
1257 if new_level_forest > level_domain:
1258 raise CommandError("Forest function level can't be higher than the domain function level(s). Please raise it/them first!")
1261 m.dn = ldb.Dn(samdb, "CN=Partitions,%s" % samdb.get_config_basedn())
1262 m["msDS-Behavior-Version"]= ldb.MessageElement(
1263 str(new_level_forest), ldb.FLAG_MOD_REPLACE,
1264 "msDS-Behavior-Version")
1266 msgs.append("Forest function level changed!")
1267 msgs.append("All changes applied successfully!")
1268 self.message("\n".join(msgs))
1270 raise CommandError("invalid argument: '%s' (choose from 'show', 'raise')" % subcommand)
1272 class cmd_domain_passwordsettings_show(Command):
1273 """Display current password settings for the domain."""
1275 synopsis = "%prog [options]"
1277 takes_optiongroups = {
1278 "sambaopts": options.SambaOptions,
1279 "versionopts": options.VersionOptions,
1280 "credopts": options.CredentialsOptions,
1284 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1285 metavar="URL", dest="H"),
1288 def run(self, H=None, credopts=None, sambaopts=None, versionopts=None):
1289 lp = sambaopts.get_loadparm()
1290 creds = credopts.get_credentials(lp)
1292 samdb = SamDB(url=H, session_info=system_session(),
1293 credentials=creds, lp=lp)
1295 domain_dn = samdb.domain_dn()
1296 res = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1297 attrs=["pwdProperties", "pwdHistoryLength", "minPwdLength",
1298 "minPwdAge", "maxPwdAge", "lockoutDuration", "lockoutThreshold",
1299 "lockOutObservationWindow"])
1300 assert(len(res) == 1)
1302 pwd_props = int(res[0]["pwdProperties"][0])
1303 pwd_hist_len = int(res[0]["pwdHistoryLength"][0])
1304 cur_min_pwd_len = int(res[0]["minPwdLength"][0])
1306 cur_min_pwd_age = int(abs(int(res[0]["minPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1307 if int(res[0]["maxPwdAge"][0]) == -0x8000000000000000:
1310 cur_max_pwd_age = int(abs(int(res[0]["maxPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1311 cur_account_lockout_threshold = int(res[0]["lockoutThreshold"][0])
1313 if int(res[0]["lockoutDuration"][0]) == -0x8000000000000000:
1314 cur_account_lockout_duration = 0
1316 cur_account_lockout_duration = abs(int(res[0]["lockoutDuration"][0])) / (1e7 * 60)
1317 cur_reset_account_lockout_after = abs(int(res[0]["lockOutObservationWindow"][0])) / (1e7 * 60)
1318 except Exception as e:
1319 raise CommandError("Could not retrieve password properties!", e)
1321 self.message("Password informations for domain '%s'" % domain_dn)
1323 if pwd_props & DOMAIN_PASSWORD_COMPLEX != 0:
1324 self.message("Password complexity: on")
1326 self.message("Password complexity: off")
1327 if pwd_props & DOMAIN_PASSWORD_STORE_CLEARTEXT != 0:
1328 self.message("Store plaintext passwords: on")
1330 self.message("Store plaintext passwords: off")
1331 self.message("Password history length: %d" % pwd_hist_len)
1332 self.message("Minimum password length: %d" % cur_min_pwd_len)
1333 self.message("Minimum password age (days): %d" % cur_min_pwd_age)
1334 self.message("Maximum password age (days): %d" % cur_max_pwd_age)
1335 self.message("Account lockout duration (mins): %d" % cur_account_lockout_duration)
1336 self.message("Account lockout threshold (attempts): %d" % cur_account_lockout_threshold)
1337 self.message("Reset account lockout after (mins): %d" % cur_reset_account_lockout_after)
1339 class cmd_domain_passwordsettings_set(Command):
1340 """Set password settings.
1342 Password complexity, password lockout policy, history length,
1343 minimum password length, the minimum and maximum password age) on
1344 a Samba AD DC server.
1346 Use against a Windows DC is possible, but group policy will override it.
1349 synopsis = "%prog <options> [options]"
1351 takes_optiongroups = {
1352 "sambaopts": options.SambaOptions,
1353 "versionopts": options.VersionOptions,
1354 "credopts": options.CredentialsOptions,
1358 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1359 metavar="URL", dest="H"),
1360 Option("-q", "--quiet", help="Be quiet", action="store_true"), # unused
1361 Option("--complexity", type="choice", choices=["on","off","default"],
1362 help="The password complexity (on | off | default). Default is 'on'"),
1363 Option("--store-plaintext", type="choice", choices=["on","off","default"],
1364 help="Store plaintext passwords where account have 'store passwords with reversible encryption' set (on | off | default). Default is 'off'"),
1365 Option("--history-length",
1366 help="The password history length (<integer> | default). Default is 24.", type=str),
1367 Option("--min-pwd-length",
1368 help="The minimum password length (<integer> | default). Default is 7.", type=str),
1369 Option("--min-pwd-age",
1370 help="The minimum password age (<integer in days> | default). Default is 1.", type=str),
1371 Option("--max-pwd-age",
1372 help="The maximum password age (<integer in days> | default). Default is 43.", type=str),
1373 Option("--account-lockout-duration",
1374 help="The the length of time an account is locked out after exeeding the limit on bad password attempts (<integer in mins> | default). Default is 30 mins.", type=str),
1375 Option("--account-lockout-threshold",
1376 help="The number of bad password attempts allowed before locking out the account (<integer> | default). Default is 0 (never lock out).", type=str),
1377 Option("--reset-account-lockout-after",
1378 help="After this time is elapsed, the recorded number of attempts restarts from zero (<integer> | default). Default is 30.", type=str),
1381 def run(self, H=None, min_pwd_age=None, max_pwd_age=None,
1382 quiet=False, complexity=None, store_plaintext=None, history_length=None,
1383 min_pwd_length=None, account_lockout_duration=None, account_lockout_threshold=None,
1384 reset_account_lockout_after=None, credopts=None, sambaopts=None,
1386 lp = sambaopts.get_loadparm()
1387 creds = credopts.get_credentials(lp)
1389 samdb = SamDB(url=H, session_info=system_session(),
1390 credentials=creds, lp=lp)
1392 domain_dn = samdb.domain_dn()
1395 m.dn = ldb.Dn(samdb, domain_dn)
1396 pwd_props = int(samdb.get_pwdProperties())
1398 if complexity is not None:
1399 if complexity == "on" or complexity == "default":
1400 pwd_props = pwd_props | DOMAIN_PASSWORD_COMPLEX
1401 msgs.append("Password complexity activated!")
1402 elif complexity == "off":
1403 pwd_props = pwd_props & (~DOMAIN_PASSWORD_COMPLEX)
1404 msgs.append("Password complexity deactivated!")
1406 if store_plaintext is not None:
1407 if store_plaintext == "on" or store_plaintext == "default":
1408 pwd_props = pwd_props | DOMAIN_PASSWORD_STORE_CLEARTEXT
1409 msgs.append("Plaintext password storage for changed passwords activated!")
1410 elif store_plaintext == "off":
1411 pwd_props = pwd_props & (~DOMAIN_PASSWORD_STORE_CLEARTEXT)
1412 msgs.append("Plaintext password storage for changed passwords deactivated!")
1414 if complexity is not None or store_plaintext is not None:
1415 m["pwdProperties"] = ldb.MessageElement(str(pwd_props),
1416 ldb.FLAG_MOD_REPLACE, "pwdProperties")
1418 if history_length is not None:
1419 if history_length == "default":
1422 pwd_hist_len = int(history_length)
1424 if pwd_hist_len < 0 or pwd_hist_len > 24:
1425 raise CommandError("Password history length must be in the range of 0 to 24!")
1427 m["pwdHistoryLength"] = ldb.MessageElement(str(pwd_hist_len),
1428 ldb.FLAG_MOD_REPLACE, "pwdHistoryLength")
1429 msgs.append("Password history length changed!")
1431 if min_pwd_length is not None:
1432 if min_pwd_length == "default":
1435 min_pwd_len = int(min_pwd_length)
1437 if min_pwd_len < 0 or min_pwd_len > 14:
1438 raise CommandError("Minimum password length must be in the range of 0 to 14!")
1440 m["minPwdLength"] = ldb.MessageElement(str(min_pwd_len),
1441 ldb.FLAG_MOD_REPLACE, "minPwdLength")
1442 msgs.append("Minimum password length changed!")
1444 if min_pwd_age is not None:
1445 if min_pwd_age == "default":
1448 min_pwd_age = int(min_pwd_age)
1450 if min_pwd_age < 0 or min_pwd_age > 998:
1451 raise CommandError("Minimum password age must be in the range of 0 to 998!")
1454 min_pwd_age_ticks = -int(min_pwd_age * (24 * 60 * 60 * 1e7))
1456 m["minPwdAge"] = ldb.MessageElement(str(min_pwd_age_ticks),
1457 ldb.FLAG_MOD_REPLACE, "minPwdAge")
1458 msgs.append("Minimum password age changed!")
1460 if max_pwd_age is not None:
1461 if max_pwd_age == "default":
1464 max_pwd_age = int(max_pwd_age)
1466 if max_pwd_age < 0 or max_pwd_age > 999:
1467 raise CommandError("Maximum password age must be in the range of 0 to 999!")
1470 if max_pwd_age == 0:
1471 max_pwd_age_ticks = -0x8000000000000000
1473 max_pwd_age_ticks = -int(max_pwd_age * (24 * 60 * 60 * 1e7))
1475 m["maxPwdAge"] = ldb.MessageElement(str(max_pwd_age_ticks),
1476 ldb.FLAG_MOD_REPLACE, "maxPwdAge")
1477 msgs.append("Maximum password age changed!")
1479 if account_lockout_duration is not None:
1480 if account_lockout_duration == "default":
1481 account_lockout_duration = 30
1483 account_lockout_duration = int(account_lockout_duration)
1485 if account_lockout_duration < 0 or account_lockout_duration > 99999:
1486 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1489 if account_lockout_duration == 0:
1490 account_lockout_duration_ticks = -0x8000000000000000
1492 account_lockout_duration_ticks = -int(account_lockout_duration * (60 * 1e7))
1494 m["lockoutDuration"] = ldb.MessageElement(str(account_lockout_duration_ticks),
1495 ldb.FLAG_MOD_REPLACE, "lockoutDuration")
1496 msgs.append("Account lockout duration changed!")
1498 if account_lockout_threshold is not None:
1499 if account_lockout_threshold == "default":
1500 account_lockout_threshold = 0
1502 account_lockout_threshold = int(account_lockout_threshold)
1504 m["lockoutThreshold"] = ldb.MessageElement(str(account_lockout_threshold),
1505 ldb.FLAG_MOD_REPLACE, "lockoutThreshold")
1506 msgs.append("Account lockout threshold changed!")
1508 if reset_account_lockout_after is not None:
1509 if reset_account_lockout_after == "default":
1510 reset_account_lockout_after = 30
1512 reset_account_lockout_after = int(reset_account_lockout_after)
1514 if reset_account_lockout_after < 0 or reset_account_lockout_after > 99999:
1515 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1518 if reset_account_lockout_after == 0:
1519 reset_account_lockout_after_ticks = -0x8000000000000000
1521 reset_account_lockout_after_ticks = -int(reset_account_lockout_after * (60 * 1e7))
1523 m["lockOutObservationWindow"] = ldb.MessageElement(str(reset_account_lockout_after_ticks),
1524 ldb.FLAG_MOD_REPLACE, "lockOutObservationWindow")
1525 msgs.append("Duration to reset account lockout after changed!")
1527 if max_pwd_age and max_pwd_age > 0 and min_pwd_age >= max_pwd_age:
1528 raise CommandError("Maximum password age (%d) must be greater than minimum password age (%d)!" % (max_pwd_age, min_pwd_age))
1531 raise CommandError("You must specify at least one option to set. Try --help")
1533 msgs.append("All changes applied successfully!")
1534 self.message("\n".join(msgs))
1536 class cmd_domain_passwordsettings(SuperCommand):
1537 """Manage password policy settings."""
1540 subcommands["pso"] = cmd_domain_passwordsettings_pso()
1541 subcommands["show"] = cmd_domain_passwordsettings_show()
1542 subcommands["set"] = cmd_domain_passwordsettings_set()
1544 class cmd_domain_classicupgrade(Command):
1545 """Upgrade from Samba classic (NT4-like) database to Samba AD DC database.
1547 Specify either a directory with all Samba classic DC databases and state files (with --dbdir) or
1548 the testparm utility from your classic installation (with --testparm).
1551 synopsis = "%prog [options] <classic_smb_conf>"
1553 takes_optiongroups = {
1554 "sambaopts": options.SambaOptions,
1555 "versionopts": options.VersionOptions
1559 Option("--dbdir", type="string", metavar="DIR",
1560 help="Path to samba classic DC database directory"),
1561 Option("--testparm", type="string", metavar="PATH",
1562 help="Path to samba classic DC testparm utility from the previous installation. This allows the default paths of the previous installation to be followed"),
1563 Option("--targetdir", type="string", metavar="DIR",
1564 help="Path prefix where the new Samba 4.0 AD domain should be initialised"),
1565 Option("-q", "--quiet", help="Be quiet", action="store_true"),
1566 Option("-v", "--verbose", help="Be verbose", action="store_true"),
1567 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
1568 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
1569 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
1570 "BIND9_FLATFILE uses bind9 text database to store zone information, "
1571 "BIND9_DLZ uses samba4 AD to store zone information, "
1572 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
1573 default="SAMBA_INTERNAL")
1577 Option("--use-xattrs", type="choice", choices=["yes","no","auto"],
1578 metavar="[yes|no|auto]",
1579 help="Define if we should use the native fs capabilities or a tdb file for "
1580 "storing attributes likes ntacl when --use-ntvfs is set. "
1581 "auto tries to make an inteligent guess based on the user rights and system capabilities",
1584 if samba.is_ntvfs_fileserver_built():
1585 takes_options.extend(common_ntvfs_options)
1586 takes_options.extend(ntvfs_options)
1588 takes_args = ["smbconf"]
1590 def run(self, smbconf=None, targetdir=None, dbdir=None, testparm=None,
1591 quiet=False, verbose=False, use_xattrs="auto", sambaopts=None, versionopts=None,
1592 dns_backend=None, use_ntvfs=False):
1594 if not os.path.exists(smbconf):
1595 raise CommandError("File %s does not exist" % smbconf)
1597 if testparm and not os.path.exists(testparm):
1598 raise CommandError("Testparm utility %s does not exist" % testparm)
1600 if dbdir and not os.path.exists(dbdir):
1601 raise CommandError("Directory %s does not exist" % dbdir)
1603 if not dbdir and not testparm:
1604 raise CommandError("Please specify either dbdir or testparm")
1606 logger = self.get_logger()
1608 logger.setLevel(logging.DEBUG)
1610 logger.setLevel(logging.WARNING)
1612 logger.setLevel(logging.INFO)
1614 if dbdir and testparm:
1615 logger.warning("both dbdir and testparm specified, ignoring dbdir.")
1618 lp = sambaopts.get_loadparm()
1620 s3conf = s3param.get_context()
1623 s3conf.set("realm", sambaopts.realm)
1625 if targetdir is not None:
1626 if not os.path.isdir(targetdir):
1630 if use_xattrs == "yes":
1632 elif use_xattrs == "auto" and use_ntvfs == False:
1634 elif use_ntvfs == False:
1635 raise CommandError("--use-xattrs=no requires --use-ntvfs (not supported for production use). "
1636 "Please re-run with --use-xattrs omitted.")
1637 elif use_xattrs == "auto" and not s3conf.get("posix:eadb"):
1639 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
1641 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
1644 samba.ntacls.setntacl(lp, tmpfile.name,
1645 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
1648 # FIXME: Don't catch all exceptions here
1649 logger.info("You are not root or your system does not support xattr, using tdb backend for attributes. "
1650 "If you intend to use this provision in production, rerun the script as root on a system supporting xattrs.")
1654 # Set correct default values from dbdir or testparm
1657 paths["state directory"] = dbdir
1658 paths["private dir"] = dbdir
1659 paths["lock directory"] = dbdir
1660 paths["smb passwd file"] = dbdir + "/smbpasswd"
1662 paths["state directory"] = get_testparm_var(testparm, smbconf, "state directory")
1663 paths["private dir"] = get_testparm_var(testparm, smbconf, "private dir")
1664 paths["smb passwd file"] = get_testparm_var(testparm, smbconf, "smb passwd file")
1665 paths["lock directory"] = get_testparm_var(testparm, smbconf, "lock directory")
1666 # "testparm" from Samba 3 < 3.4.x is not aware of the parameter
1667 # "state directory", instead make use of "lock directory"
1668 if len(paths["state directory"]) == 0:
1669 paths["state directory"] = paths["lock directory"]
1672 s3conf.set(p, paths[p])
1674 # load smb.conf parameters
1675 logger.info("Reading smb.conf")
1676 s3conf.load(smbconf)
1677 samba3 = Samba3(smbconf, s3conf)
1679 logger.info("Provisioning")
1680 upgrade_from_samba3(samba3, logger, targetdir, session_info=system_session(),
1681 useeadb=eadb, dns_backend=dns_backend, use_ntvfs=use_ntvfs)
1684 class cmd_domain_samba3upgrade(cmd_domain_classicupgrade):
1685 __doc__ = cmd_domain_classicupgrade.__doc__
1687 # This command is present for backwards compatibility only,
1688 # and should not be shown.
1692 class LocalDCCredentialsOptions(options.CredentialsOptions):
1693 def __init__(self, parser):
1694 options.CredentialsOptions.__init__(self, parser, special_name="local-dc")
1696 class DomainTrustCommand(Command):
1697 """List domain trusts."""
1700 Command.__init__(self)
1701 self.local_lp = None
1703 self.local_server = None
1704 self.local_binding_string = None
1705 self.local_creds = None
1707 self.remote_server = None
1708 self.remote_binding_string = None
1709 self.remote_creds = None
1711 def _uint32(self, v):
1712 return ctypes.c_uint32(v).value
1714 def check_runtime_error(self, runtime, val):
1718 err32 = self._uint32(runtime.args[0])
1724 class LocalRuntimeError(CommandError):
1725 def __init__(exception_self, self, runtime, message):
1726 err32 = self._uint32(runtime.args[0])
1727 errstr = runtime.args[1]
1728 msg = "LOCAL_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1729 self.local_server, message, err32, errstr)
1730 CommandError.__init__(exception_self, msg)
1732 class RemoteRuntimeError(CommandError):
1733 def __init__(exception_self, self, runtime, message):
1734 err32 = self._uint32(runtime.args[0])
1735 errstr = runtime.args[1]
1736 msg = "REMOTE_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1737 self.remote_server, message, err32, errstr)
1738 CommandError.__init__(exception_self, msg)
1740 class LocalLdbError(CommandError):
1741 def __init__(exception_self, self, ldb_error, message):
1742 errval = ldb_error.args[0]
1743 errstr = ldb_error.args[1]
1744 msg = "LOCAL_DC[%s]: %s - ERROR(%d) - %s" % (
1745 self.local_server, message, errval, errstr)
1746 CommandError.__init__(exception_self, msg)
1748 def setup_local_server(self, sambaopts, localdcopts):
1749 if self.local_server is not None:
1750 return self.local_server
1752 lp = sambaopts.get_loadparm()
1754 local_server = localdcopts.ipaddress
1755 if local_server is None:
1756 server_role = lp.server_role()
1757 if server_role != "ROLE_ACTIVE_DIRECTORY_DC":
1758 raise CommandError("Invalid server_role %s" % (server_role))
1759 local_server = lp.get('netbios name')
1760 local_transport = "ncalrpc"
1761 local_binding_options = ""
1762 local_binding_options += ",auth_type=ncalrpc_as_system"
1763 local_ldap_url = None
1766 local_transport = "ncacn_np"
1767 local_binding_options = ""
1768 local_ldap_url = "ldap://%s" % local_server
1769 local_creds = localdcopts.get_credentials(lp)
1773 self.local_server = local_server
1774 self.local_binding_string = "%s:%s[%s]" % (local_transport, local_server, local_binding_options)
1775 self.local_ldap_url = local_ldap_url
1776 self.local_creds = local_creds
1777 return self.local_server
1779 def new_local_lsa_connection(self):
1780 return lsa.lsarpc(self.local_binding_string, self.local_lp, self.local_creds)
1782 def new_local_netlogon_connection(self):
1783 return netlogon.netlogon(self.local_binding_string, self.local_lp, self.local_creds)
1785 def new_local_ldap_connection(self):
1786 return SamDB(url=self.local_ldap_url,
1787 session_info=system_session(),
1788 credentials=self.local_creds,
1791 def setup_remote_server(self, credopts, domain,
1793 require_writable=True):
1796 assert require_writable
1798 if self.remote_server is not None:
1799 return self.remote_server
1801 self.remote_server = "__unknown__remote_server__.%s" % domain
1802 assert self.local_server is not None
1804 remote_creds = credopts.get_credentials(self.local_lp)
1805 remote_server = credopts.ipaddress
1806 remote_binding_options = ""
1808 # TODO: we should also support NT4 domains
1809 # we could use local_netlogon.netr_DsRGetDCNameEx2() with the remote domain name
1810 # and delegate NBT or CLDAP to the local netlogon server
1812 remote_net = Net(remote_creds, self.local_lp, server=remote_server)
1813 remote_flags = nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS
1814 if require_writable:
1815 remote_flags |= nbt.NBT_SERVER_WRITABLE
1817 remote_flags |= nbt.NBT_SERVER_PDC
1818 remote_info = remote_net.finddc(flags=remote_flags, domain=domain, address=remote_server)
1819 except NTSTATUSError as error:
1820 raise CommandError("Failed to find a writeable DC for domain '%s': %s" %
1823 raise CommandError("Failed to find a writeable DC for domain '%s'" % domain)
1825 nbt.NBT_SERVER_PDC: "PDC",
1826 nbt.NBT_SERVER_GC: "GC",
1827 nbt.NBT_SERVER_LDAP: "LDAP",
1828 nbt.NBT_SERVER_DS: "DS",
1829 nbt.NBT_SERVER_KDC: "KDC",
1830 nbt.NBT_SERVER_TIMESERV: "TIMESERV",
1831 nbt.NBT_SERVER_CLOSEST: "CLOSEST",
1832 nbt.NBT_SERVER_WRITABLE: "WRITABLE",
1833 nbt.NBT_SERVER_GOOD_TIMESERV: "GOOD_TIMESERV",
1834 nbt.NBT_SERVER_NDNC: "NDNC",
1835 nbt.NBT_SERVER_SELECT_SECRET_DOMAIN_6: "SELECT_SECRET_DOMAIN_6",
1836 nbt.NBT_SERVER_FULL_SECRET_DOMAIN_6: "FULL_SECRET_DOMAIN_6",
1837 nbt.NBT_SERVER_ADS_WEB_SERVICE: "ADS_WEB_SERVICE",
1838 nbt.NBT_SERVER_DS_8: "DS_8",
1839 nbt.NBT_SERVER_HAS_DNS_NAME: "HAS_DNS_NAME",
1840 nbt.NBT_SERVER_IS_DEFAULT_NC: "IS_DEFAULT_NC",
1841 nbt.NBT_SERVER_FOREST_ROOT: "FOREST_ROOT",
1843 server_type_string = self.generic_bitmap_to_string(flag_map,
1844 remote_info.server_type, names_only=True)
1845 self.outf.write("RemoteDC Netbios[%s] DNS[%s] ServerType[%s]\n" % (
1846 remote_info.pdc_name,
1847 remote_info.pdc_dns_name,
1848 server_type_string))
1850 self.remote_server = remote_info.pdc_dns_name
1851 self.remote_binding_string="ncacn_np:%s[%s]" % (self.remote_server, remote_binding_options)
1852 self.remote_creds = remote_creds
1853 return self.remote_server
1855 def new_remote_lsa_connection(self):
1856 return lsa.lsarpc(self.remote_binding_string, self.local_lp, self.remote_creds)
1858 def new_remote_netlogon_connection(self):
1859 return netlogon.netlogon(self.remote_binding_string, self.local_lp, self.remote_creds)
1861 def get_lsa_info(self, conn, policy_access):
1862 objectAttr = lsa.ObjectAttribute()
1863 objectAttr.sec_qos = lsa.QosInfo()
1865 policy = conn.OpenPolicy2(''.decode('utf-8'),
1866 objectAttr, policy_access)
1868 info = conn.QueryInfoPolicy2(policy, lsa.LSA_POLICY_INFO_DNS)
1870 return (policy, info)
1872 def get_netlogon_dc_info(self, conn, server):
1873 info = conn.netr_DsRGetDCNameEx2(server,
1874 None, 0, None, None, None,
1875 netlogon.DS_RETURN_DNS_NAME)
1878 def netr_DomainTrust_to_name(self, t):
1879 if t.trust_type == lsa.LSA_TRUST_TYPE_DOWNLEVEL:
1880 return t.netbios_name
1884 def netr_DomainTrust_to_type(self, a, t):
1886 primary_parent = None
1888 if _t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
1890 if not _t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1891 primary_parent = a[_t.parent_index]
1894 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1895 if t is primary_parent:
1898 if t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1901 parent = a[t.parent_index]
1902 if parent is primary:
1907 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1912 def netr_DomainTrust_to_transitive(self, t):
1913 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1916 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE:
1919 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1924 def netr_DomainTrust_to_direction(self, t):
1925 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND and \
1926 t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1929 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND:
1932 if t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1937 def generic_enum_to_string(self, e_dict, v, names_only=False):
1941 v32 = self._uint32(v)
1942 w = "__unknown__%08X__" % v32
1944 r = "0x%x (%s)" % (v, w)
1947 def generic_bitmap_to_string(self, b_dict, v, names_only=False):
1952 for b in sorted(b_dict.keys()):
1959 c32 = self._uint32(c)
1960 s += ["__unknown_%08X__" % c32]
1965 r = "0x%x (%s)" % (v, w)
1968 def trustType_string(self, v):
1970 lsa.LSA_TRUST_TYPE_DOWNLEVEL : "DOWNLEVEL",
1971 lsa.LSA_TRUST_TYPE_UPLEVEL : "UPLEVEL",
1972 lsa.LSA_TRUST_TYPE_MIT : "MIT",
1973 lsa.LSA_TRUST_TYPE_DCE : "DCE",
1975 return self.generic_enum_to_string(types, v)
1977 def trustDirection_string(self, v):
1979 lsa.LSA_TRUST_DIRECTION_INBOUND |
1980 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "BOTH",
1981 lsa.LSA_TRUST_DIRECTION_INBOUND : "INBOUND",
1982 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "OUTBOUND",
1984 return self.generic_enum_to_string(directions, v)
1986 def trustAttributes_string(self, v):
1988 lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE : "NON_TRANSITIVE",
1989 lsa.LSA_TRUST_ATTRIBUTE_UPLEVEL_ONLY : "UPLEVEL_ONLY",
1990 lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN : "QUARANTINED_DOMAIN",
1991 lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE : "FOREST_TRANSITIVE",
1992 lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION : "CROSS_ORGANIZATION",
1993 lsa.LSA_TRUST_ATTRIBUTE_WITHIN_FOREST : "WITHIN_FOREST",
1994 lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL : "TREAT_AS_EXTERNAL",
1995 lsa.LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION : "USES_RC4_ENCRYPTION",
1997 return self.generic_bitmap_to_string(attributes, v)
1999 def kerb_EncTypes_string(self, v):
2001 security.KERB_ENCTYPE_DES_CBC_CRC : "DES_CBC_CRC",
2002 security.KERB_ENCTYPE_DES_CBC_MD5 : "DES_CBC_MD5",
2003 security.KERB_ENCTYPE_RC4_HMAC_MD5 : "RC4_HMAC_MD5",
2004 security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96 : "AES128_CTS_HMAC_SHA1_96",
2005 security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96 : "AES256_CTS_HMAC_SHA1_96",
2006 security.KERB_ENCTYPE_FAST_SUPPORTED : "FAST_SUPPORTED",
2007 security.KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED : "COMPOUND_IDENTITY_SUPPORTED",
2008 security.KERB_ENCTYPE_CLAIMS_SUPPORTED : "CLAIMS_SUPPORTED",
2009 security.KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED : "RESOURCE_SID_COMPRESSION_DISABLED",
2011 return self.generic_bitmap_to_string(enctypes, v)
2013 def entry_tln_status(self, e_flags, ):
2015 return "Status[Enabled]"
2018 lsa.LSA_TLN_DISABLED_NEW : "Disabled-New",
2019 lsa.LSA_TLN_DISABLED_ADMIN : "Disabled",
2020 lsa.LSA_TLN_DISABLED_CONFLICT : "Disabled-Conflicting",
2022 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
2024 def entry_dom_status(self, e_flags):
2026 return "Status[Enabled]"
2029 lsa.LSA_SID_DISABLED_ADMIN : "Disabled-SID",
2030 lsa.LSA_SID_DISABLED_CONFLICT : "Disabled-SID-Conflicting",
2031 lsa.LSA_NB_DISABLED_ADMIN : "Disabled-NB",
2032 lsa.LSA_NB_DISABLED_CONFLICT : "Disabled-NB-Conflicting",
2034 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
2036 def write_forest_trust_info(self, fti, tln=None, collisions=None):
2038 tln_string = " TDO[%s]" % tln
2042 self.outf.write("Namespaces[%d]%s:\n" % (
2043 len(fti.entries), tln_string))
2045 for i, e in enumerate(fti.entries):
2048 collision_string = ""
2050 if collisions is not None:
2051 for c in collisions.entries:
2055 collision_string = " Collision[%s]" % (c.name.string)
2057 d = e.forest_trust_data
2058 if e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
2059 self.outf.write("TLN: %-32s DNS[*.%s]%s\n" % (
2060 self.entry_tln_status(flags),
2061 d.string, collision_string))
2062 elif e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
2063 self.outf.write("TLN_EX: %-29s DNS[*.%s]\n" % (
2065 elif e.type == lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
2066 self.outf.write("DOM: %-32s DNS[%s] Netbios[%s] SID[%s]%s\n" % (
2067 self.entry_dom_status(flags),
2068 d.dns_domain_name.string,
2069 d.netbios_domain_name.string,
2070 d.domain_sid, collision_string))
2073 class cmd_domain_trust_list(DomainTrustCommand):
2074 """List domain trusts."""
2076 synopsis = "%prog [options]"
2078 takes_optiongroups = {
2079 "sambaopts": options.SambaOptions,
2080 "versionopts": options.VersionOptions,
2081 "localdcopts": LocalDCCredentialsOptions,
2087 def run(self, sambaopts=None, versionopts=None, localdcopts=None):
2089 local_server = self.setup_local_server(sambaopts, localdcopts)
2091 local_netlogon = self.new_local_netlogon_connection()
2092 except RuntimeError as error:
2093 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2096 local_netlogon_trusts = local_netlogon.netr_DsrEnumerateDomainTrusts(local_server,
2097 netlogon.NETR_TRUST_FLAG_IN_FOREST |
2098 netlogon.NETR_TRUST_FLAG_OUTBOUND |
2099 netlogon.NETR_TRUST_FLAG_INBOUND)
2100 except RuntimeError as error:
2101 if self.check_runtime_error(error, werror.WERR_RPC_S_PROCNUM_OUT_OF_RANGE):
2102 # TODO: we could implement a fallback to lsa.EnumTrustDom()
2103 raise CommandError("LOCAL_DC[%s]: netr_DsrEnumerateDomainTrusts not supported." % (
2105 raise self.LocalRuntimeError(self, error, "netr_DsrEnumerateDomainTrusts failed")
2107 a = local_netlogon_trusts.array
2109 if t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
2111 self.outf.write("%-14s %-15s %-19s %s\n" % (
2112 "Type[%s]" % self.netr_DomainTrust_to_type(a, t),
2113 "Transitive[%s]" % self.netr_DomainTrust_to_transitive(t),
2114 "Direction[%s]" % self.netr_DomainTrust_to_direction(t),
2115 "Name[%s]" % self.netr_DomainTrust_to_name(t)))
2118 class cmd_domain_trust_show(DomainTrustCommand):
2119 """Show trusted domain details."""
2121 synopsis = "%prog NAME [options]"
2123 takes_optiongroups = {
2124 "sambaopts": options.SambaOptions,
2125 "versionopts": options.VersionOptions,
2126 "localdcopts": LocalDCCredentialsOptions,
2132 takes_args = ["domain"]
2134 def run(self, domain, sambaopts=None, versionopts=None, localdcopts=None):
2136 local_server = self.setup_local_server(sambaopts, localdcopts)
2138 local_lsa = self.new_local_lsa_connection()
2139 except RuntimeError as error:
2140 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2143 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2144 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2145 except RuntimeError as error:
2146 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2148 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2149 local_lsa_info.name.string,
2150 local_lsa_info.dns_domain.string,
2151 local_lsa_info.sid))
2153 lsaString = lsa.String()
2154 lsaString.string = domain
2156 local_tdo_full = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2157 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2158 local_tdo_info = local_tdo_full.info_ex
2159 local_tdo_posix = local_tdo_full.posix_offset
2160 except NTSTATUSError as error:
2161 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2162 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2164 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(FULL_INFO) failed")
2167 local_tdo_enctypes = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2168 lsaString, lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES)
2169 except NTSTATUSError as error:
2170 if self.check_runtime_error(error, ntstatus.NT_STATUS_INVALID_PARAMETER):
2172 if self.check_runtime_error(error, ntstatus.NT_STATUS_INVALID_INFO_CLASS):
2175 if error is not None:
2176 raise self.LocalRuntimeError(self, error,
2177 "QueryTrustedDomainInfoByName(SUPPORTED_ENCRYPTION_TYPES) failed")
2179 local_tdo_enctypes = lsa.TrustDomainInfoSupportedEncTypes()
2180 local_tdo_enctypes.enc_types = 0
2183 local_tdo_forest = None
2184 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2185 local_tdo_forest = local_lsa.lsaRQueryForestTrustInformation(local_policy,
2186 lsaString, lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
2187 except RuntimeError as error:
2188 if self.check_runtime_error(error, ntstatus.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
2190 if self.check_runtime_error(error, ntstatus.NT_STATUS_NOT_FOUND):
2192 if error is not None:
2193 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation failed")
2195 local_tdo_forest = lsa.ForestTrustInformation()
2196 local_tdo_forest.count = 0
2197 local_tdo_forest.entries = []
2199 self.outf.write("TrustedDomain:\n\n");
2200 self.outf.write("NetbiosName: %s\n" % local_tdo_info.netbios_name.string)
2201 if local_tdo_info.netbios_name.string != local_tdo_info.domain_name.string:
2202 self.outf.write("DnsName: %s\n" % local_tdo_info.domain_name.string)
2203 self.outf.write("SID: %s\n" % local_tdo_info.sid)
2204 self.outf.write("Type: %s\n" % self.trustType_string(local_tdo_info.trust_type))
2205 self.outf.write("Direction: %s\n" % self.trustDirection_string(local_tdo_info.trust_direction))
2206 self.outf.write("Attributes: %s\n" % self.trustAttributes_string(local_tdo_info.trust_attributes))
2207 posix_offset_u32 = ctypes.c_uint32(local_tdo_posix.posix_offset).value
2208 posix_offset_i32 = ctypes.c_int32(local_tdo_posix.posix_offset).value
2209 self.outf.write("PosixOffset: 0x%08X (%d)\n" % (posix_offset_u32, posix_offset_i32))
2210 self.outf.write("kerb_EncTypes: %s\n" % self.kerb_EncTypes_string(local_tdo_enctypes.enc_types))
2212 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2213 self.write_forest_trust_info(local_tdo_forest,
2214 tln=local_tdo_info.domain_name.string)
2218 class cmd_domain_trust_create(DomainTrustCommand):
2219 """Create a domain or forest trust."""
2221 synopsis = "%prog DOMAIN [options]"
2223 takes_optiongroups = {
2224 "sambaopts": options.SambaOptions,
2225 "versionopts": options.VersionOptions,
2226 "credopts": options.CredentialsOptions,
2227 "localdcopts": LocalDCCredentialsOptions,
2231 Option("--type", type="choice", metavar="TYPE",
2232 choices=["external", "forest"],
2233 help="The type of the trust: 'external' or 'forest'.",
2235 default="external"),
2236 Option("--direction", type="choice", metavar="DIRECTION",
2237 choices=["incoming", "outgoing", "both"],
2238 help="The trust direction: 'incoming', 'outgoing' or 'both'.",
2239 dest='trust_direction',
2241 Option("--create-location", type="choice", metavar="LOCATION",
2242 choices=["local", "both"],
2243 help="Where to create the trusted domain object: 'local' or 'both'.",
2244 dest='create_location',
2246 Option("--cross-organisation", action="store_true",
2247 help="The related domains does not belong to the same organisation.",
2248 dest='cross_organisation',
2250 Option("--quarantined", type="choice", metavar="yes|no",
2251 choices=["yes", "no", None],
2252 help="Special SID filtering rules are applied to the trust. "
2253 "With --type=external the default is yes. "
2254 "With --type=forest the default is no.",
2255 dest='quarantined_arg',
2257 Option("--not-transitive", action="store_true",
2258 help="The forest trust is not transitive.",
2259 dest='not_transitive',
2261 Option("--treat-as-external", action="store_true",
2262 help="The treat the forest trust as external.",
2263 dest='treat_as_external',
2265 Option("--no-aes-keys", action="store_false",
2266 help="The trust uses aes kerberos keys.",
2267 dest='use_aes_keys',
2269 Option("--skip-validation", action="store_false",
2270 help="Skip validation of the trust.",
2275 takes_args = ["domain"]
2277 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2278 trust_type=None, trust_direction=None, create_location=None,
2279 cross_organisation=False, quarantined_arg=None,
2280 not_transitive=False, treat_as_external=False,
2281 use_aes_keys=False, validate=True):
2283 lsaString = lsa.String()
2286 if quarantined_arg is None:
2287 if trust_type == 'external':
2289 elif quarantined_arg == 'yes':
2292 if trust_type != 'forest':
2294 raise CommandError("--not-transitive requires --type=forest")
2295 if treat_as_external:
2296 raise CommandError("--treat-as-external requires --type=forest")
2300 enc_types = lsa.TrustDomainInfoSupportedEncTypes()
2301 enc_types.enc_types = security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96
2302 enc_types.enc_types |= security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96
2304 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2305 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2306 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2308 local_trust_info = lsa.TrustDomainInfoInfoEx()
2309 local_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2310 local_trust_info.trust_direction = 0
2311 if trust_direction == "both":
2312 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2313 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2314 elif trust_direction == "incoming":
2315 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2316 elif trust_direction == "outgoing":
2317 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2318 local_trust_info.trust_attributes = 0
2319 if cross_organisation:
2320 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2322 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2323 if trust_type == "forest":
2324 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2326 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2327 if treat_as_external:
2328 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2330 def get_password(name):
2333 if password is not None and password is not '':
2335 password = getpass("New %s Password: " % name)
2336 passwordverify = getpass("Retype %s Password: " % name)
2337 if not password == passwordverify:
2339 self.outf.write("Sorry, passwords do not match.\n")
2341 incoming_secret = None
2342 outgoing_secret = None
2343 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2344 if create_location == "local":
2345 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2346 incoming_password = get_password("Incoming Trust")
2347 incoming_secret = string_to_byte_array(incoming_password.encode('utf-16-le'))
2348 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2349 outgoing_password = get_password("Outgoing Trust")
2350 outgoing_secret = string_to_byte_array(outgoing_password.encode('utf-16-le'))
2352 remote_trust_info = None
2354 # We use 240 random bytes.
2355 # Windows uses 28 or 240 random bytes. I guess it's
2356 # based on the trust type external vs. forest.
2358 # The initial trust password can be up to 512 bytes
2359 # while the versioned passwords used for periodic updates
2360 # can only be up to 498 bytes, as netr_ServerPasswordSet2()
2361 # needs to pass the NL_PASSWORD_VERSION structure within the
2362 # 512 bytes and a 2 bytes confounder is required.
2364 def random_trust_secret(length):
2365 pw = samba.generate_random_machine_password(length//2, length//2)
2366 return string_to_byte_array(pw.encode('utf-16-le'))
2368 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2369 incoming_secret = random_trust_secret(240)
2370 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2371 outgoing_secret = random_trust_secret(240)
2373 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2374 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2376 remote_trust_info = lsa.TrustDomainInfoInfoEx()
2377 remote_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2378 remote_trust_info.trust_direction = 0
2379 if trust_direction == "both":
2380 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2381 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2382 elif trust_direction == "incoming":
2383 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2384 elif trust_direction == "outgoing":
2385 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2386 remote_trust_info.trust_attributes = 0
2387 if cross_organisation:
2388 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2390 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2391 if trust_type == "forest":
2392 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2394 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2395 if treat_as_external:
2396 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2398 local_server = self.setup_local_server(sambaopts, localdcopts)
2400 local_lsa = self.new_local_lsa_connection()
2401 except RuntimeError as error:
2402 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2405 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2406 except RuntimeError as error:
2407 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2409 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2410 local_lsa_info.name.string,
2411 local_lsa_info.dns_domain.string,
2412 local_lsa_info.sid))
2415 remote_server = self.setup_remote_server(credopts, domain)
2416 except RuntimeError as error:
2417 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2420 remote_lsa = self.new_remote_lsa_connection()
2421 except RuntimeError as error:
2422 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2425 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2426 except RuntimeError as error:
2427 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2429 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2430 remote_lsa_info.name.string,
2431 remote_lsa_info.dns_domain.string,
2432 remote_lsa_info.sid))
2434 local_trust_info.domain_name.string = remote_lsa_info.dns_domain.string
2435 local_trust_info.netbios_name.string = remote_lsa_info.name.string
2436 local_trust_info.sid = remote_lsa_info.sid
2438 if remote_trust_info:
2439 remote_trust_info.domain_name.string = local_lsa_info.dns_domain.string
2440 remote_trust_info.netbios_name.string = local_lsa_info.name.string
2441 remote_trust_info.sid = local_lsa_info.sid
2444 lsaString.string = local_trust_info.domain_name.string
2445 local_old_netbios = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2446 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2447 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2448 except NTSTATUSError as error:
2449 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2450 raise self.LocalRuntimeError(self, error,
2451 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2455 lsaString.string = local_trust_info.netbios_name.string
2456 local_old_dns = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2457 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2458 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2459 except NTSTATUSError as error:
2460 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2461 raise self.LocalRuntimeError(self, error,
2462 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2465 if remote_trust_info:
2467 lsaString.string = remote_trust_info.domain_name.string
2468 remote_old_netbios = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2469 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2470 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2471 except NTSTATUSError as error:
2472 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2473 raise self.RemoteRuntimeError(self, error,
2474 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2478 lsaString.string = remote_trust_info.netbios_name.string
2479 remote_old_dns = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2480 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2481 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2482 except NTSTATUSError as error:
2483 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2484 raise self.RemoteRuntimeError(self, error,
2485 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2489 local_netlogon = self.new_local_netlogon_connection()
2490 except RuntimeError as error:
2491 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2494 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
2495 except RuntimeError as error:
2496 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
2498 if remote_trust_info:
2500 remote_netlogon = self.new_remote_netlogon_connection()
2501 except RuntimeError as error:
2502 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2505 remote_netlogon_info = self.get_netlogon_dc_info(remote_netlogon, remote_server)
2506 except RuntimeError as error:
2507 raise self.RemoteRuntimeError(self, error, "failed to get netlogon dc info")
2509 def generate_AuthInOutBlob(secret, update_time):
2511 blob = drsblobs.trustAuthInOutBlob()
2516 clear = drsblobs.AuthInfoClear()
2517 clear.size = len(secret)
2518 clear.password = secret
2520 info = drsblobs.AuthenticationInformation()
2521 info.LastUpdateTime = samba.unix2nttime(update_time)
2522 info.AuthType = lsa.TRUST_AUTH_TYPE_CLEAR
2523 info.AuthInfo = clear
2525 array = drsblobs.AuthenticationInformationArray()
2527 array.array = [info]
2529 blob = drsblobs.trustAuthInOutBlob()
2531 blob.current = array
2535 def generate_AuthInfoInternal(session_key, incoming=None, outgoing=None):
2536 confounder = [0] * 512
2537 for i in range(len(confounder)):
2538 confounder[i] = random.randint(0, 255)
2540 trustpass = drsblobs.trustDomainPasswords()
2542 trustpass.confounder = confounder
2543 trustpass.outgoing = outgoing
2544 trustpass.incoming = incoming
2546 trustpass_blob = ndr_pack(trustpass)
2548 encrypted_trustpass = arcfour_encrypt(session_key, trustpass_blob)
2550 auth_blob = lsa.DATA_BUF2()
2551 auth_blob.size = len(encrypted_trustpass)
2552 auth_blob.data = string_to_byte_array(encrypted_trustpass)
2554 auth_info = lsa.TrustDomainInfoAuthInfoInternal()
2555 auth_info.auth_blob = auth_blob
2559 update_time = samba.current_unix_time()
2560 incoming_blob = generate_AuthInOutBlob(incoming_secret, update_time)
2561 outgoing_blob = generate_AuthInOutBlob(outgoing_secret, update_time)
2563 local_tdo_handle = None
2564 remote_tdo_handle = None
2566 local_auth_info = generate_AuthInfoInternal(local_lsa.session_key,
2567 incoming=incoming_blob,
2568 outgoing=outgoing_blob)
2569 if remote_trust_info:
2570 remote_auth_info = generate_AuthInfoInternal(remote_lsa.session_key,
2571 incoming=outgoing_blob,
2572 outgoing=incoming_blob)
2575 if remote_trust_info:
2576 self.outf.write("Creating remote TDO.\n")
2577 current_request = { "location": "remote", "name": "CreateTrustedDomainEx2"}
2578 remote_tdo_handle = remote_lsa.CreateTrustedDomainEx2(remote_policy,
2581 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2582 self.outf.write("Remote TDO created.\n")
2584 self.outf.write("Setting supported encryption types on remote TDO.\n")
2585 current_request = { "location": "remote", "name": "SetInformationTrustedDomain"}
2586 remote_lsa.SetInformationTrustedDomain(remote_tdo_handle,
2587 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2590 self.outf.write("Creating local TDO.\n")
2591 current_request = { "location": "local", "name": "CreateTrustedDomainEx2"}
2592 local_tdo_handle = local_lsa.CreateTrustedDomainEx2(local_policy,
2595 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2596 self.outf.write("Local TDO created\n")
2598 self.outf.write("Setting supported encryption types on local TDO.\n")
2599 current_request = { "location": "local", "name": "SetInformationTrustedDomain"}
2600 local_lsa.SetInformationTrustedDomain(local_tdo_handle,
2601 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2603 except RuntimeError as error:
2604 self.outf.write("Error: %s failed %sly - cleaning up\n" % (
2605 current_request['name'], current_request['location']))
2606 if remote_tdo_handle:
2607 self.outf.write("Deleting remote TDO.\n")
2608 remote_lsa.DeleteObject(remote_tdo_handle)
2609 remote_tdo_handle = None
2610 if local_tdo_handle:
2611 self.outf.write("Deleting local TDO.\n")
2612 local_lsa.DeleteObject(local_tdo_handle)
2613 local_tdo_handle = None
2614 if current_request['location'] is "remote":
2615 raise self.RemoteRuntimeError(self, error, "%s" % (
2616 current_request['name']))
2617 raise self.LocalRuntimeError(self, error, "%s" % (
2618 current_request['name']))
2621 if local_trust_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2622 self.outf.write("Setup local forest trust information...\n")
2624 # get all information about the remote trust
2625 # this triggers netr_GetForestTrustInformation to the remote domain
2626 # and lsaRSetForestTrustInformation() locally, but new top level
2627 # names are disabled by default.
2628 local_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
2629 remote_lsa_info.dns_domain.string,
2630 netlogon.DS_GFTI_UPDATE_TDO)
2631 except RuntimeError as error:
2632 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2635 # here we try to enable all top level names
2636 local_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
2637 remote_lsa_info.dns_domain,
2638 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2641 except RuntimeError as error:
2642 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2644 self.write_forest_trust_info(local_forest_info,
2645 tln=remote_lsa_info.dns_domain.string,
2646 collisions=local_forest_collision)
2648 if remote_trust_info:
2649 self.outf.write("Setup remote forest trust information...\n")
2651 # get all information about the local trust (from the perspective of the remote domain)
2652 # this triggers netr_GetForestTrustInformation to our domain.
2653 # and lsaRSetForestTrustInformation() remotely, but new top level
2654 # names are disabled by default.
2655 remote_forest_info = remote_netlogon.netr_DsRGetForestTrustInformation(remote_netlogon_info.dc_unc,
2656 local_lsa_info.dns_domain.string,
2657 netlogon.DS_GFTI_UPDATE_TDO)
2658 except RuntimeError as error:
2659 raise self.RemoteRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2662 # here we try to enable all top level names
2663 remote_forest_collision = remote_lsa.lsaRSetForestTrustInformation(remote_policy,
2664 local_lsa_info.dns_domain,
2665 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2668 except RuntimeError as error:
2669 raise self.RemoteRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2671 self.write_forest_trust_info(remote_forest_info,
2672 tln=local_lsa_info.dns_domain.string,
2673 collisions=remote_forest_collision)
2675 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2676 self.outf.write("Validating outgoing trust...\n")
2678 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_netlogon_info.dc_unc,
2679 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2681 remote_lsa_info.dns_domain.string)
2682 except RuntimeError as error:
2683 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2685 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2686 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2688 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2689 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2690 local_trust_verify.trusted_dc_name,
2691 local_trust_verify.tc_connection_status[1],
2692 local_trust_verify.pdc_connection_status[1])
2694 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2695 local_trust_verify.trusted_dc_name,
2696 local_trust_verify.tc_connection_status[1],
2697 local_trust_verify.pdc_connection_status[1])
2699 if local_trust_status != werror.WERR_SUCCESS or local_conn_status != werror.WERR_SUCCESS:
2700 raise CommandError(local_validation)
2702 self.outf.write("OK: %s\n" % local_validation)
2704 if remote_trust_info:
2705 if remote_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2706 self.outf.write("Validating incoming trust...\n")
2708 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_netlogon_info.dc_unc,
2709 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2711 local_lsa_info.dns_domain.string)
2712 except RuntimeError as error:
2713 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2715 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2716 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2718 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2719 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2720 remote_trust_verify.trusted_dc_name,
2721 remote_trust_verify.tc_connection_status[1],
2722 remote_trust_verify.pdc_connection_status[1])
2724 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2725 remote_trust_verify.trusted_dc_name,
2726 remote_trust_verify.tc_connection_status[1],
2727 remote_trust_verify.pdc_connection_status[1])
2729 if remote_trust_status != werror.WERR_SUCCESS or remote_conn_status != werror.WERR_SUCCESS:
2730 raise CommandError(remote_validation)
2732 self.outf.write("OK: %s\n" % remote_validation)
2734 if remote_tdo_handle is not None:
2736 remote_lsa.Close(remote_tdo_handle)
2737 except RuntimeError as error:
2739 remote_tdo_handle = None
2740 if local_tdo_handle is not None:
2742 local_lsa.Close(local_tdo_handle)
2743 except RuntimeError as error:
2745 local_tdo_handle = None
2747 self.outf.write("Success.\n")
2750 class cmd_domain_trust_delete(DomainTrustCommand):
2751 """Delete a domain trust."""
2753 synopsis = "%prog DOMAIN [options]"
2755 takes_optiongroups = {
2756 "sambaopts": options.SambaOptions,
2757 "versionopts": options.VersionOptions,
2758 "credopts": options.CredentialsOptions,
2759 "localdcopts": LocalDCCredentialsOptions,
2763 Option("--delete-location", type="choice", metavar="LOCATION",
2764 choices=["local", "both"],
2765 help="Where to delete the trusted domain object: 'local' or 'both'.",
2766 dest='delete_location',
2770 takes_args = ["domain"]
2772 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2773 delete_location=None):
2775 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2776 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2777 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2779 if delete_location == "local":
2780 remote_policy_access = None
2782 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2783 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2784 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2786 local_server = self.setup_local_server(sambaopts, localdcopts)
2788 local_lsa = self.new_local_lsa_connection()
2789 except RuntimeError as error:
2790 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2793 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2794 except RuntimeError as error:
2795 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2797 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2798 local_lsa_info.name.string,
2799 local_lsa_info.dns_domain.string,
2800 local_lsa_info.sid))
2802 local_tdo_info = None
2803 local_tdo_handle = None
2804 remote_tdo_info = None
2805 remote_tdo_handle = None
2807 lsaString = lsa.String()
2809 lsaString.string = domain
2810 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2811 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2812 except NTSTATUSError as error:
2813 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2814 raise CommandError("Failed to find trust for domain '%s'" % domain)
2815 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2818 if remote_policy_access is not None:
2820 remote_server = self.setup_remote_server(credopts, domain)
2821 except RuntimeError as error:
2822 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2825 remote_lsa = self.new_remote_lsa_connection()
2826 except RuntimeError as error:
2827 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2830 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2831 except RuntimeError as error:
2832 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2834 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2835 remote_lsa_info.name.string,
2836 remote_lsa_info.dns_domain.string,
2837 remote_lsa_info.sid))
2839 if remote_lsa_info.sid != local_tdo_info.sid or \
2840 remote_lsa_info.name.string != local_tdo_info.netbios_name.string or \
2841 remote_lsa_info.dns_domain.string != local_tdo_info.domain_name.string:
2842 raise CommandError("LocalTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2843 local_tdo_info.netbios_name.string,
2844 local_tdo_info.domain_name.string,
2845 local_tdo_info.sid))
2848 lsaString.string = local_lsa_info.dns_domain.string
2849 remote_tdo_info = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2850 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2851 except NTSTATUSError as error:
2852 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2853 raise self.RemoteRuntimeError(self, error, "QueryTrustedDomainInfoByName(%s)" % (
2857 if remote_tdo_info is not None:
2858 if local_lsa_info.sid != remote_tdo_info.sid or \
2859 local_lsa_info.name.string != remote_tdo_info.netbios_name.string or \
2860 local_lsa_info.dns_domain.string != remote_tdo_info.domain_name.string:
2861 raise CommandError("RemoteTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2862 remote_tdo_info.netbios_name.string,
2863 remote_tdo_info.domain_name.string,
2864 remote_tdo_info.sid))
2866 if local_tdo_info is not None:
2868 lsaString.string = local_tdo_info.domain_name.string
2869 local_tdo_handle = local_lsa.OpenTrustedDomainByName(local_policy,
2871 security.SEC_STD_DELETE)
2872 except RuntimeError as error:
2873 raise self.LocalRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2876 local_lsa.DeleteObject(local_tdo_handle)
2877 local_tdo_handle = None
2879 if remote_tdo_info is not None:
2881 lsaString.string = remote_tdo_info.domain_name.string
2882 remote_tdo_handle = remote_lsa.OpenTrustedDomainByName(remote_policy,
2884 security.SEC_STD_DELETE)
2885 except RuntimeError as error:
2886 raise self.RemoteRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2889 if remote_tdo_handle is not None:
2891 remote_lsa.DeleteObject(remote_tdo_handle)
2892 remote_tdo_handle = None
2893 self.outf.write("RemoteTDO deleted.\n")
2894 except RuntimeError as error:
2895 self.outf.write("%s\n" % self.RemoteRuntimeError(self, error, "DeleteObject() failed"))
2897 if local_tdo_handle is not None:
2899 local_lsa.DeleteObject(local_tdo_handle)
2900 local_tdo_handle = None
2901 self.outf.write("LocalTDO deleted.\n")
2902 except RuntimeError as error:
2903 self.outf.write("%s\n" % self.LocalRuntimeError(self, error, "DeleteObject() failed"))
2907 class cmd_domain_trust_validate(DomainTrustCommand):
2908 """Validate a domain trust."""
2910 synopsis = "%prog DOMAIN [options]"
2912 takes_optiongroups = {
2913 "sambaopts": options.SambaOptions,
2914 "versionopts": options.VersionOptions,
2915 "credopts": options.CredentialsOptions,
2916 "localdcopts": LocalDCCredentialsOptions,
2920 Option("--validate-location", type="choice", metavar="LOCATION",
2921 choices=["local", "both"],
2922 help="Where to validate the trusted domain object: 'local' or 'both'.",
2923 dest='validate_location',
2927 takes_args = ["domain"]
2929 def run(self, domain, sambaopts=None, versionopts=None, credopts=None, localdcopts=None,
2930 validate_location=None):
2932 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2934 local_server = self.setup_local_server(sambaopts, localdcopts)
2936 local_lsa = self.new_local_lsa_connection()
2937 except RuntimeError as error:
2938 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2941 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2942 except RuntimeError as error:
2943 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2945 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2946 local_lsa_info.name.string,
2947 local_lsa_info.dns_domain.string,
2948 local_lsa_info.sid))
2951 lsaString = lsa.String()
2952 lsaString.string = domain
2953 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2954 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2955 except NTSTATUSError as error:
2956 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2957 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2959 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
2961 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
2962 local_tdo_info.netbios_name.string,
2963 local_tdo_info.domain_name.string,
2964 local_tdo_info.sid))
2967 local_netlogon = self.new_local_netlogon_connection()
2968 except RuntimeError as error:
2969 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2972 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_server,
2973 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2975 local_tdo_info.domain_name.string)
2976 except RuntimeError as error:
2977 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2979 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2980 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2982 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2983 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2984 local_trust_verify.trusted_dc_name,
2985 local_trust_verify.tc_connection_status[1],
2986 local_trust_verify.pdc_connection_status[1])
2988 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2989 local_trust_verify.trusted_dc_name,
2990 local_trust_verify.tc_connection_status[1],
2991 local_trust_verify.pdc_connection_status[1])
2993 if local_trust_status != werror.WERR_SUCCESS or local_conn_status != werror.WERR_SUCCESS:
2994 raise CommandError(local_validation)
2996 self.outf.write("OK: %s\n" % local_validation)
2999 server = local_trust_verify.trusted_dc_name.replace('\\', '')
3000 domain_and_server = "%s\\%s" % (local_tdo_info.domain_name.string, server)
3001 local_trust_rediscover = local_netlogon.netr_LogonControl2Ex(local_server,
3002 netlogon.NETLOGON_CONTROL_REDISCOVER,
3005 except RuntimeError as error:
3006 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
3008 local_conn_status = self._uint32(local_trust_rediscover.tc_connection_status[0])
3009 local_rediscover = "LocalRediscover: DC[%s] CONNECTION[%s]" % (
3010 local_trust_rediscover.trusted_dc_name,
3011 local_trust_rediscover.tc_connection_status[1])
3013 if local_conn_status != werror.WERR_SUCCESS:
3014 raise CommandError(local_rediscover)
3016 self.outf.write("OK: %s\n" % local_rediscover)
3018 if validate_location != "local":
3020 remote_server = self.setup_remote_server(credopts, domain, require_pdc=False)
3021 except RuntimeError as error:
3022 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
3025 remote_netlogon = self.new_remote_netlogon_connection()
3026 except RuntimeError as error:
3027 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
3030 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_server,
3031 netlogon.NETLOGON_CONTROL_TC_VERIFY,
3033 local_lsa_info.dns_domain.string)
3034 except RuntimeError as error:
3035 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
3037 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
3038 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
3040 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
3041 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
3042 remote_trust_verify.trusted_dc_name,
3043 remote_trust_verify.tc_connection_status[1],
3044 remote_trust_verify.pdc_connection_status[1])
3046 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
3047 remote_trust_verify.trusted_dc_name,
3048 remote_trust_verify.tc_connection_status[1],
3049 remote_trust_verify.pdc_connection_status[1])
3051 if remote_trust_status != werror.WERR_SUCCESS or remote_conn_status != werror.WERR_SUCCESS:
3052 raise CommandError(remote_validation)
3054 self.outf.write("OK: %s\n" % remote_validation)
3057 server = remote_trust_verify.trusted_dc_name.replace('\\', '')
3058 domain_and_server = "%s\\%s" % (local_lsa_info.dns_domain.string, server)
3059 remote_trust_rediscover = remote_netlogon.netr_LogonControl2Ex(remote_server,
3060 netlogon.NETLOGON_CONTROL_REDISCOVER,
3063 except RuntimeError as error:
3064 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
3066 remote_conn_status = self._uint32(remote_trust_rediscover.tc_connection_status[0])
3068 remote_rediscover = "RemoteRediscover: DC[%s] CONNECTION[%s]" % (
3069 remote_trust_rediscover.trusted_dc_name,
3070 remote_trust_rediscover.tc_connection_status[1])
3072 if remote_conn_status != werror.WERR_SUCCESS:
3073 raise CommandError(remote_rediscover)
3075 self.outf.write("OK: %s\n" % remote_rediscover)
3079 class cmd_domain_trust_namespaces(DomainTrustCommand):
3080 """Manage forest trust namespaces."""
3082 synopsis = "%prog [DOMAIN] [options]"
3084 takes_optiongroups = {
3085 "sambaopts": options.SambaOptions,
3086 "versionopts": options.VersionOptions,
3087 "localdcopts": LocalDCCredentialsOptions,
3091 Option("--refresh", type="choice", metavar="check|store",
3092 choices=["check", "store", None],
3093 help="List and maybe store refreshed forest trust information: 'check' or 'store'.",
3096 Option("--enable-all", action="store_true",
3097 help="Try to update disabled entries, not allowed with --refresh=check.",
3100 Option("--enable-tln", action="append", metavar='DNSDOMAIN',
3101 help="Enable a top level name entry. Can be specified multiple times.",
3104 Option("--disable-tln", action="append", metavar='DNSDOMAIN',
3105 help="Disable a top level name entry. Can be specified multiple times.",
3108 Option("--add-tln-ex", action="append", metavar='DNSDOMAIN',
3109 help="Add a top level exclusion entry. Can be specified multiple times.",
3112 Option("--delete-tln-ex", action="append", metavar='DNSDOMAIN',
3113 help="Delete a top level exclusion entry. Can be specified multiple times.",
3114 dest='delete_tln_ex',
3116 Option("--enable-nb", action="append", metavar='NETBIOSDOMAIN',
3117 help="Enable a netbios name in a domain entry. Can be specified multiple times.",
3120 Option("--disable-nb", action="append", metavar='NETBIOSDOMAIN',
3121 help="Disable a netbios name in a domain entry. Can be specified multiple times.",
3124 Option("--enable-sid", action="append", metavar='DOMAINSID',
3125 help="Enable a SID in a domain entry. Can be specified multiple times.",
3126 dest='enable_sid_str',
3128 Option("--disable-sid", action="append", metavar='DOMAINSID',
3129 help="Disable a SID in a domain entry. Can be specified multiple times.",
3130 dest='disable_sid_str',
3132 Option("--add-upn-suffix", action="append", metavar='DNSDOMAIN',
3133 help="Add a new uPNSuffixes attribute for the local forest. Can be specified multiple times.",
3136 Option("--delete-upn-suffix", action="append", metavar='DNSDOMAIN',
3137 help="Delete an existing uPNSuffixes attribute of the local forest. Can be specified multiple times.",
3140 Option("--add-spn-suffix", action="append", metavar='DNSDOMAIN',
3141 help="Add a new msDS-SPNSuffixes attribute for the local forest. Can be specified multiple times.",
3144 Option("--delete-spn-suffix", action="append", metavar='DNSDOMAIN',
3145 help="Delete an existing msDS-SPNSuffixes attribute of the local forest. Can be specified multiple times.",
3150 takes_args = ["domain?"]
3152 def run(self, domain=None, sambaopts=None, localdcopts=None, versionopts=None,
3153 refresh=None, enable_all=False,
3154 enable_tln=[], disable_tln=[], add_tln_ex=[], delete_tln_ex=[],
3155 enable_sid_str=[], disable_sid_str=[], enable_nb=[], disable_nb=[],
3156 add_upn=[], delete_upn=[], add_spn=[], delete_spn=[]):
3158 require_update = False
3161 if refresh == "store":
3162 raise CommandError("--refresh=%s not allowed without DOMAIN" % refresh)
3165 raise CommandError("--enable-all not allowed without DOMAIN")
3167 if len(enable_tln) > 0:
3168 raise CommandError("--enable-tln not allowed without DOMAIN")
3169 if len(disable_tln) > 0:
3170 raise CommandError("--disable-tln not allowed without DOMAIN")
3172 if len(add_tln_ex) > 0:
3173 raise CommandError("--add-tln-ex not allowed without DOMAIN")
3174 if len(delete_tln_ex) > 0:
3175 raise CommandError("--delete-tln-ex not allowed without DOMAIN")
3177 if len(enable_nb) > 0:
3178 raise CommandError("--enable-nb not allowed without DOMAIN")
3179 if len(disable_nb) > 0:
3180 raise CommandError("--disable-nb not allowed without DOMAIN")
3182 if len(enable_sid_str) > 0:
3183 raise CommandError("--enable-sid not allowed without DOMAIN")
3184 if len(disable_sid_str) > 0:
3185 raise CommandError("--disable-sid not allowed without DOMAIN")
3187 if len(add_upn) > 0:
3189 if not n.startswith("*."):
3191 raise CommandError("value[%s] specified for --add-upn-suffix should not include with '*.'" % n)
3192 require_update = True
3193 if len(delete_upn) > 0:
3194 for n in delete_upn:
3195 if not n.startswith("*."):
3197 raise CommandError("value[%s] specified for --delete-upn-suffix should not include with '*.'" % n)
3198 require_update = True
3200 for d in delete_upn:
3201 if a.lower() != d.lower():
3203 raise CommandError("value[%s] specified for --add-upn-suffix and --delete-upn-suffix" % a)
3205 if len(add_spn) > 0:
3207 if not n.startswith("*."):
3209 raise CommandError("value[%s] specified for --add-spn-suffix should not include with '*.'" % n)
3210 require_update = True
3211 if len(delete_spn) > 0:
3212 for n in delete_spn:
3213 if not n.startswith("*."):
3215 raise CommandError("value[%s] specified for --delete-spn-suffix should not include with '*.'" % n)
3216 require_update = True
3218 for d in delete_spn:
3219 if a.lower() != d.lower():
3221 raise CommandError("value[%s] specified for --add-spn-suffix and --delete-spn-suffix" % a)
3223 if len(add_upn) > 0:
3224 raise CommandError("--add-upn-suffix not allowed together with DOMAIN")
3225 if len(delete_upn) > 0:
3226 raise CommandError("--delete-upn-suffix not allowed together with DOMAIN")
3227 if len(add_spn) > 0:
3228 raise CommandError("--add-spn-suffix not allowed together with DOMAIN")
3229 if len(delete_spn) > 0:
3230 raise CommandError("--delete-spn-suffix not allowed together with DOMAIN")
3232 if refresh is not None:
3233 if refresh == "store":
3234 require_update = True
3236 if enable_all and refresh != "store":
3237 raise CommandError("--enable-all not allowed together with --refresh=%s" % refresh)
3239 if len(enable_tln) > 0:
3240 raise CommandError("--enable-tln not allowed together with --refresh")
3241 if len(disable_tln) > 0:
3242 raise CommandError("--disable-tln not allowed together with --refresh")
3244 if len(add_tln_ex) > 0:
3245 raise CommandError("--add-tln-ex not allowed together with --refresh")
3246 if len(delete_tln_ex) > 0:
3247 raise CommandError("--delete-tln-ex not allowed together with --refresh")
3249 if len(enable_nb) > 0:
3250 raise CommandError("--enable-nb not allowed together with --refresh")
3251 if len(disable_nb) > 0:
3252 raise CommandError("--disable-nb not allowed together with --refresh")
3254 if len(enable_sid_str) > 0:
3255 raise CommandError("--enable-sid not allowed together with --refresh")
3256 if len(disable_sid_str) > 0:
3257 raise CommandError("--disable-sid not allowed together with --refresh")
3260 require_update = True
3262 if len(enable_tln) > 0:
3263 raise CommandError("--enable-tln not allowed together with --enable-all")
3265 if len(enable_nb) > 0:
3266 raise CommandError("--enable-nb not allowed together with --enable-all")
3268 if len(enable_sid_str) > 0:
3269 raise CommandError("--enable-sid not allowed together with --enable-all")
3271 if len(enable_tln) > 0:
3272 require_update = True
3273 if len(disable_tln) > 0:
3274 require_update = True
3275 for e in enable_tln:
3276 for d in disable_tln:
3277 if e.lower() != d.lower():
3279 raise CommandError("value[%s] specified for --enable-tln and --disable-tln" % e)
3281 if len(add_tln_ex) > 0:
3282 for n in add_tln_ex:
3283 if not n.startswith("*."):
3285 raise CommandError("value[%s] specified for --add-tln-ex should not include with '*.'" % n)
3286 require_update = True
3287 if len(delete_tln_ex) > 0:
3288 for n in delete_tln_ex:
3289 if not n.startswith("*."):
3291 raise CommandError("value[%s] specified for --delete-tln-ex should not include with '*.'" % n)
3292 require_update = True
3293 for a in add_tln_ex:
3294 for d in delete_tln_ex:
3295 if a.lower() != d.lower():
3297 raise CommandError("value[%s] specified for --add-tln-ex and --delete-tln-ex" % a)
3299 if len(enable_nb) > 0:
3300 require_update = True
3301 if len(disable_nb) > 0:
3302 require_update = True
3304 for d in disable_nb:
3305 if e.upper() != d.upper():
3307 raise CommandError("value[%s] specified for --enable-nb and --disable-nb" % e)
3310 for s in enable_sid_str:
3312 sid = security.dom_sid(s)
3313 except TypeError as error:
3314 raise CommandError("value[%s] specified for --enable-sid is not a valid SID" % s)
3315 enable_sid.append(sid)
3317 for s in disable_sid_str:
3319 sid = security.dom_sid(s)
3320 except TypeError as error:
3321 raise CommandError("value[%s] specified for --disable-sid is not a valid SID" % s)
3322 disable_sid.append(sid)
3323 if len(enable_sid) > 0:
3324 require_update = True
3325 if len(disable_sid) > 0:
3326 require_update = True
3327 for e in enable_sid:
3328 for d in disable_sid:
3331 raise CommandError("value[%s] specified for --enable-sid and --disable-sid" % e)
3333 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
3335 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
3337 local_server = self.setup_local_server(sambaopts, localdcopts)
3339 local_lsa = self.new_local_lsa_connection()
3340 except RuntimeError as error:
3341 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
3344 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
3345 except RuntimeError as error:
3346 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
3348 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
3349 local_lsa_info.name.string,
3350 local_lsa_info.dns_domain.string,
3351 local_lsa_info.sid))
3355 local_netlogon = self.new_local_netlogon_connection()
3356 except RuntimeError as error:
3357 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3360 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3361 except RuntimeError as error:
3362 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3364 if local_netlogon_info.domain_name != local_netlogon_info.forest_name:
3365 raise CommandError("The local domain [%s] is not the forest root [%s]" % (
3366 local_netlogon_info.domain_name,
3367 local_netlogon_info.forest_name))
3370 # get all information about our own forest
3371 own_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3373 except RuntimeError as error:
3374 if self.check_runtime_error(error, werror.WERR_RPC_S_PROCNUM_OUT_OF_RANGE):
3375 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3378 if self.check_runtime_error(error, werror.WERR_INVALID_FUNCTION):
3379 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3382 if self.check_runtime_error(error, werror.WERR_NERR_ACFNOTLOADED):
3383 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3386 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3388 self.outf.write("Own forest trust information...\n")
3389 self.write_forest_trust_info(own_forest_info,
3390 tln=local_lsa_info.dns_domain.string)
3393 local_samdb = self.new_local_ldap_connection()
3394 except RuntimeError as error:
3395 raise self.LocalRuntimeError(self, error, "failed to connect to SamDB")
3397 local_partitions_dn = "CN=Partitions,%s" % str(local_samdb.get_config_basedn())
3398 attrs = ['uPNSuffixes', 'msDS-SPNSuffixes']
3400 msgs = local_samdb.search(base=local_partitions_dn,
3401 scope=ldb.SCOPE_BASE,
3402 expression="(objectClass=crossRefContainer)",
3404 stored_msg = msgs[0]
3405 except ldb.LdbError as error:
3406 raise self.LocalLdbError(self, error, "failed to search partition dn")
3408 stored_upn_vals = []
3409 if 'uPNSuffixes' in stored_msg:
3410 stored_upn_vals.extend(stored_msg['uPNSuffixes'])
3412 stored_spn_vals = []
3413 if 'msDS-SPNSuffixes' in stored_msg:
3414 stored_spn_vals.extend(stored_msg['msDS-SPNSuffixes'])
3416 self.outf.write("Stored uPNSuffixes attributes[%d]:\n" % len(stored_upn_vals))
3417 for v in stored_upn_vals:
3418 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3419 self.outf.write("Stored msDS-SPNSuffixes attributes[%d]:\n" % len(stored_spn_vals))
3420 for v in stored_spn_vals:
3421 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3423 if not require_update:
3427 update_upn_vals = []
3428 update_upn_vals.extend(stored_upn_vals)
3431 update_spn_vals = []
3432 update_spn_vals.extend(stored_spn_vals)
3435 for i, v in enumerate(update_upn_vals):
3436 if v.lower() == upn.lower():
3437 raise CommandError("Entry already present for "
3438 "value[%s] specified for "
3439 "--add-upn-suffix" % upn)
3440 update_upn_vals.append(upn)
3443 for upn in delete_upn:
3445 for i, v in enumerate(update_upn_vals):
3446 if v.lower() != upn.lower():
3451 raise CommandError("Entry not found for value[%s] specified for --delete-upn-suffix" % upn)
3453 update_upn_vals.pop(idx)
3457 for i, v in enumerate(update_spn_vals):
3458 if v.lower() == spn.lower():
3459 raise CommandError("Entry already present for "
3460 "value[%s] specified for "
3461 "--add-spn-suffix" % spn)
3462 update_spn_vals.append(spn)
3465 for spn in delete_spn:
3467 for i, v in enumerate(update_spn_vals):
3468 if v.lower() != spn.lower():
3473 raise CommandError("Entry not found for value[%s] specified for --delete-spn-suffix" % spn)
3475 update_spn_vals.pop(idx)
3478 self.outf.write("Update uPNSuffixes attributes[%d]:\n" % len(update_upn_vals))
3479 for v in update_upn_vals:
3480 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3481 self.outf.write("Update msDS-SPNSuffixes attributes[%d]:\n" % len(update_spn_vals))
3482 for v in update_spn_vals:
3483 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3485 update_msg = ldb.Message()
3486 update_msg.dn = stored_msg.dn
3489 update_msg['uPNSuffixes'] = ldb.MessageElement(update_upn_vals,
3490 ldb.FLAG_MOD_REPLACE,
3493 update_msg['msDS-SPNSuffixes'] = ldb.MessageElement(update_spn_vals,
3494 ldb.FLAG_MOD_REPLACE,
3497 local_samdb.modify(update_msg)
3498 except ldb.LdbError as error:
3499 raise self.LocalLdbError(self, error, "failed to update partition dn")
3502 stored_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3504 except RuntimeError as error:
3505 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3507 self.outf.write("Stored forest trust information...\n")
3508 self.write_forest_trust_info(stored_forest_info,
3509 tln=local_lsa_info.dns_domain.string)
3513 lsaString = lsa.String()
3514 lsaString.string = domain
3515 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
3516 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
3517 except NTSTATUSError as error:
3518 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
3519 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
3521 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
3523 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
3524 local_tdo_info.netbios_name.string,
3525 local_tdo_info.domain_name.string,
3526 local_tdo_info.sid))
3528 if not local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
3529 raise CommandError("trusted domain object for domain [%s] is not marked as FOREST_TRANSITIVE." % domain)
3531 if refresh is not None:
3533 local_netlogon = self.new_local_netlogon_connection()
3534 except RuntimeError as error:
3535 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3538 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3539 except RuntimeError as error:
3540 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3542 lsa_update_check = 1
3543 if refresh == "store":
3544 netlogon_update_tdo = netlogon.DS_GFTI_UPDATE_TDO
3546 lsa_update_check = 0
3548 netlogon_update_tdo = 0
3551 # get all information about the remote trust
3552 # this triggers netr_GetForestTrustInformation to the remote domain
3553 # and lsaRSetForestTrustInformation() locally, but new top level
3554 # names are disabled by default.
3555 fresh_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3556 local_tdo_info.domain_name.string,
3557 netlogon_update_tdo)
3558 except RuntimeError as error:
3559 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3562 fresh_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3563 local_tdo_info.domain_name,
3564 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3567 except RuntimeError as error:
3568 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3570 self.outf.write("Fresh forest trust information...\n")
3571 self.write_forest_trust_info(fresh_forest_info,
3572 tln=local_tdo_info.domain_name.string,
3573 collisions=fresh_forest_collision)
3575 if refresh == "store":
3577 lsaString = lsa.String()
3578 lsaString.string = local_tdo_info.domain_name.string
3579 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3581 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3582 except RuntimeError as error:
3583 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3585 self.outf.write("Stored forest trust information...\n")
3586 self.write_forest_trust_info(stored_forest_info,
3587 tln=local_tdo_info.domain_name.string)
3592 # The none --refresh path
3596 lsaString = lsa.String()
3597 lsaString.string = local_tdo_info.domain_name.string
3598 local_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3600 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3601 except RuntimeError as error:
3602 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3604 self.outf.write("Local forest trust information...\n")
3605 self.write_forest_trust_info(local_forest_info,
3606 tln=local_tdo_info.domain_name.string)
3608 if not require_update:
3612 entries.extend(local_forest_info.entries)
3613 update_forest_info = lsa.ForestTrustInformation()
3614 update_forest_info.count = len(entries)
3615 update_forest_info.entries = entries
3618 for i, r in enumerate(update_forest_info.entries):
3619 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3621 if update_forest_info.entries[i].flags == 0:
3623 update_forest_info.entries[i].time = 0
3624 update_forest_info.entries[i].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3625 for i, r in enumerate(update_forest_info.entries):
3626 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3628 if update_forest_info.entries[i].flags == 0:
3630 update_forest_info.entries[i].time = 0
3631 update_forest_info.entries[i].flags &= ~lsa.LSA_NB_DISABLED_MASK
3632 update_forest_info.entries[i].flags &= ~lsa.LSA_SID_DISABLED_MASK
3634 for tln in enable_tln:
3636 for i, r in enumerate(update_forest_info.entries):
3637 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3639 if r.forest_trust_data.string.lower() != tln.lower():
3644 raise CommandError("Entry not found for value[%s] specified for --enable-tln" % tln)
3645 if not update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_MASK:
3646 raise CommandError("Entry found for value[%s] specified for --enable-tln is already enabled" % tln)
3647 update_forest_info.entries[idx].time = 0
3648 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3650 for tln in disable_tln:
3652 for i, r in enumerate(update_forest_info.entries):
3653 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3655 if r.forest_trust_data.string.lower() != tln.lower():
3660 raise CommandError("Entry not found for value[%s] specified for --disable-tln" % tln)
3661 if update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_ADMIN:
3662 raise CommandError("Entry found for value[%s] specified for --disable-tln is already disabled" % tln)
3663 update_forest_info.entries[idx].time = 0
3664 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3665 update_forest_info.entries[idx].flags |= lsa.LSA_TLN_DISABLED_ADMIN
3667 for tln_ex in add_tln_ex:
3669 for i, r in enumerate(update_forest_info.entries):
3670 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3672 if r.forest_trust_data.string.lower() != tln_ex.lower():
3677 raise CommandError("Entry already present for value[%s] specified for --add-tln-ex" % tln_ex)
3679 tln_dot = ".%s" % tln_ex.lower()
3681 for i, r in enumerate(update_forest_info.entries):
3682 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3684 r_dot = ".%s" % r.forest_trust_data.string.lower()
3685 if tln_dot == r_dot:
3686 raise CommandError("TLN entry present for value[%s] specified for --add-tln-ex" % tln_ex)
3687 if not tln_dot.endswith(r_dot):
3693 raise CommandError("No TLN parent present for value[%s] specified for --add-tln-ex" % tln_ex)
3695 r = lsa.ForestTrustRecord()
3696 r.type = lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX
3699 r.forest_trust_data.string = tln_ex
3702 entries.extend(update_forest_info.entries)
3703 entries.insert(idx + 1, r)
3704 update_forest_info.count = len(entries)
3705 update_forest_info.entries = entries
3707 for tln_ex in delete_tln_ex:
3709 for i, r in enumerate(update_forest_info.entries):
3710 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3712 if r.forest_trust_data.string.lower() != tln_ex.lower():
3717 raise CommandError("Entry not found for value[%s] specified for --delete-tln-ex" % tln_ex)
3720 entries.extend(update_forest_info.entries)
3722 update_forest_info.count = len(entries)
3723 update_forest_info.entries = entries
3725 for nb in enable_nb:
3727 for i, r in enumerate(update_forest_info.entries):
3728 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3730 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3735 raise CommandError("Entry not found for value[%s] specified for --enable-nb" % nb)
3736 if not update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_MASK:
3737 raise CommandError("Entry found for value[%s] specified for --enable-nb is already enabled" % nb)
3738 update_forest_info.entries[idx].time = 0
3739 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3741 for nb in disable_nb:
3743 for i, r in enumerate(update_forest_info.entries):
3744 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3746 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3751 raise CommandError("Entry not found for value[%s] specified for --delete-nb" % nb)
3752 if update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_ADMIN:
3753 raise CommandError("Entry found for value[%s] specified for --disable-nb is already disabled" % nb)
3754 update_forest_info.entries[idx].time = 0
3755 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3756 update_forest_info.entries[idx].flags |= lsa.LSA_NB_DISABLED_ADMIN
3758 for sid in enable_sid:
3760 for i, r in enumerate(update_forest_info.entries):
3761 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3763 if r.forest_trust_data.domain_sid != sid:
3768 raise CommandError("Entry not found for value[%s] specified for --enable-sid" % sid)
3769 if not update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_MASK:
3770 raise CommandError("Entry found for value[%s] specified for --enable-sid is already enabled" % nb)
3771 update_forest_info.entries[idx].time = 0
3772 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3774 for sid in disable_sid:
3776 for i, r in enumerate(update_forest_info.entries):
3777 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3779 if r.forest_trust_data.domain_sid != sid:
3784 raise CommandError("Entry not found for value[%s] specified for --delete-sid" % sid)
3785 if update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_ADMIN:
3786 raise CommandError("Entry found for value[%s] specified for --disable-sid is already disabled" % nb)
3787 update_forest_info.entries[idx].time = 0
3788 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3789 update_forest_info.entries[idx].flags |= lsa.LSA_SID_DISABLED_ADMIN
3792 update_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3793 local_tdo_info.domain_name,
3794 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3795 update_forest_info, 0)
3796 except RuntimeError as error:
3797 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3799 self.outf.write("Updated forest trust information...\n")
3800 self.write_forest_trust_info(update_forest_info,
3801 tln=local_tdo_info.domain_name.string,
3802 collisions=update_forest_collision)
3805 lsaString = lsa.String()
3806 lsaString.string = local_tdo_info.domain_name.string
3807 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3809 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3810 except RuntimeError as error:
3811 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3813 self.outf.write("Stored forest trust information...\n")
3814 self.write_forest_trust_info(stored_forest_info,
3815 tln=local_tdo_info.domain_name.string)
3818 class cmd_domain_tombstones_expunge(Command):
3819 """Expunge tombstones from the database.
3821 This command expunges tombstones from the database."""
3822 synopsis = "%prog NC [NC [...]] [options]"
3825 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
3826 metavar="URL", dest="H"),
3827 Option("--current-time",
3828 help="The current time to evaluate the tombstone lifetime from, expressed as YYYY-MM-DD",
3830 Option("--tombstone-lifetime", help="Number of days a tombstone should be preserved for", type=int),
3833 takes_args = ["nc*"]
3835 takes_optiongroups = {
3836 "sambaopts": options.SambaOptions,
3837 "credopts": options.CredentialsOptions,
3838 "versionopts": options.VersionOptions,
3841 def run(self, *ncs, **kwargs):
3842 sambaopts = kwargs.get("sambaopts")
3843 credopts = kwargs.get("credopts")
3844 versionpts = kwargs.get("versionopts")
3846 current_time_string = kwargs.get("current_time")
3847 tombstone_lifetime = kwargs.get("tombstone_lifetime")
3848 lp = sambaopts.get_loadparm()
3849 creds = credopts.get_credentials(lp)
3850 samdb = SamDB(url=H, session_info=system_session(),
3851 credentials=creds, lp=lp)
3853 if current_time_string is not None:
3854 current_time_obj = time.strptime(current_time_string, "%Y-%m-%d")
3855 current_time = long(time.mktime(current_time_obj))
3858 current_time = long(time.time())
3861 res = samdb.search(expression="", base="", scope=ldb.SCOPE_BASE,
3862 attrs=["namingContexts"])
3865 for nc in res[0]["namingContexts"]:
3870 started_transaction = False
3872 samdb.transaction_start()
3873 started_transaction = True
3875 removed_links) = samdb.garbage_collect_tombstones(ncs,
3876 current_time=current_time,
3877 tombstone_lifetime=tombstone_lifetime)
3879 except Exception as err:
3880 if started_transaction:
3881 samdb.transaction_cancel()
3882 raise CommandError("Failed to expunge / garbage collect tombstones", err)
3884 samdb.transaction_commit()
3886 self.outf.write("Removed %d objects and %d links successfully\n"
3887 % (removed_objects, removed_links))
3891 class cmd_domain_trust(SuperCommand):
3892 """Domain and forest trust management."""
3895 subcommands["list"] = cmd_domain_trust_list()
3896 subcommands["show"] = cmd_domain_trust_show()
3897 subcommands["create"] = cmd_domain_trust_create()
3898 subcommands["delete"] = cmd_domain_trust_delete()
3899 subcommands["validate"] = cmd_domain_trust_validate()
3900 subcommands["namespaces"] = cmd_domain_trust_namespaces()
3902 class cmd_domain_tombstones(SuperCommand):
3903 """Domain tombstone and recycled object management."""
3906 subcommands["expunge"] = cmd_domain_tombstones_expunge()
3908 class ldif_schema_update:
3909 """Helper class for applying LDIF schema updates"""
3912 self.is_defunct = False
3913 self.unknown_oid = None
3917 def can_ignore_failure(self, error):
3918 """Checks if we can safely ignore failure to apply an LDIF update"""
3919 (num, errstr) = error.args
3921 # Microsoft has marked objects as defunct that Samba doesn't know about
3922 if num == ldb.ERR_NO_SUCH_OBJECT and self.is_defunct:
3923 print("Defunct object %s doesn't exist, skipping" % self.dn)
3925 elif self.unknown_oid is not None:
3926 print("Skipping unknown OID %s for object %s" %(self.unknown_oid, self.dn))
3931 def apply(self, samdb):
3932 """Applies a single LDIF update to the schema"""
3936 samdb.modify_ldif(self.ldif, controls=['relax:0'])
3937 except ldb.LdbError as e:
3938 if e.args[0] == ldb.ERR_INVALID_ATTRIBUTE_SYNTAX:
3940 # REFRESH after a failed change
3942 # Otherwise the OID-to-attribute mapping in
3943 # _apply_updates_in_file() won't work, because it
3944 # can't lookup the new OID in the schema
3945 samdb.set_schema_update_now()
3947 samdb.modify_ldif(self.ldif, controls=['relax:0'])
3950 except ldb.LdbError as e:
3951 if self.can_ignore_failure(e):
3954 print("Exception: %s" % e)
3955 print("Encountered while trying to apply the following LDIF")
3956 print("----------------------------------------------------")
3957 print("%s" % self.ldif)
3963 class cmd_domain_schema_upgrade(Command):
3964 """Domain schema upgrading"""
3966 synopsis = "%prog [options]"
3968 takes_optiongroups = {
3969 "sambaopts": options.SambaOptions,
3970 "versionopts": options.VersionOptions,
3971 "credopts": options.CredentialsOptions,
3975 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
3976 metavar="URL", dest="H"),
3977 Option("-q", "--quiet", help="Be quiet", action="store_true"), #unused
3978 Option("-v", "--verbose", help="Be verbose", action="store_true"),
3979 Option("--schema", type="choice", metavar="SCHEMA",
3980 choices=["2012", "2012_R2"],
3981 help="The schema file to upgrade to. Default is (Windows) 2012_R2.",
3983 Option("--ldf-file", type=str, default=None,
3984 help="Just apply the schema updates in the adprep/.LDF file(s) specified"),
3985 Option("--base-dir", type=str, default=None,
3986 help="Location of ldf files Default is ${SETUPDIR}/adprep.")
3989 def _apply_updates_in_file(self, samdb, ldif_file):
3991 Applies a series of updates specified in an .LDIF file. The .LDIF file
3992 is based on the adprep Schema updates provided by Microsoft.
3995 ldif_op = ldif_schema_update()
3997 # parse the file line by line and work out each update operation to apply
3998 for line in ldif_file:
4000 line = line.rstrip()
4002 # the operations in the .LDIF file are separated by blank lines. If
4003 # we hit a blank line, try to apply the update we've parsed so far
4006 # keep going if we haven't parsed anything yet
4007 if ldif_op.ldif == '':
4010 # Apply the individual change
4011 count += ldif_op.apply(samdb)
4013 # start storing the next operation from scratch again
4014 ldif_op = ldif_schema_update()
4017 # replace the placeholder domain name in the .ldif file with the real domain
4018 if line.upper().endswith('DC=X'):
4019 line = line[:-len('DC=X')] + str(samdb.get_default_basedn())
4020 elif line.upper().endswith('CN=X'):
4021 line = line[:-len('CN=X')] + str(samdb.get_default_basedn())
4023 values = line.split(':')
4025 if values[0].lower() == 'dn':
4026 ldif_op.dn = values[1].strip()
4028 # replace the Windows-specific operation with the Samba one
4029 if values[0].lower() == 'changetype':
4030 line = line.lower().replace(': ntdsschemaadd',
4032 line = line.lower().replace(': ntdsschemamodify',
4035 if values[0].lower() in ['rdnattid', 'subclassof',
4036 'systemposssuperiors',
4038 'systemauxiliaryclass']:
4041 # The Microsoft updates contain some OIDs we don't recognize.
4042 # Query the DB to see if we can work out the OID this update is
4043 # referring to. If we find a match, then replace the OID with
4044 # the ldapDisplayname
4046 res = samdb.search(base=samdb.get_schema_basedn(),
4047 expression="(|(attributeId=%s)(governsId=%s))" %
4049 attrs=['ldapDisplayName'])
4052 ldif_op.unknown_oid = value
4054 display_name = res[0]['ldapDisplayName'][0]
4055 line = line.replace(value, ' ' + display_name)
4057 # Microsoft has marked objects as defunct that Samba doesn't know about
4058 if values[0].lower() == 'isdefunct' and values[1].strip().lower() == 'true':
4059 ldif_op.is_defunct = True
4061 # Samba has added the showInAdvancedViewOnly attribute to all objects,
4062 # so rather than doing an add, we need to do a replace
4063 if values[0].lower() == 'add' and values[1].strip().lower() == 'showinadvancedviewonly':
4064 line = 'replace: showInAdvancedViewOnly'
4066 # Add the line to the current LDIF operation (including the newline
4067 # we stripped off at the start of the loop)
4068 ldif_op.ldif += line + '\n'
4073 def _apply_update(self, samdb, update_file, base_dir):
4074 """Wrapper function for parsing an LDIF file and applying the updates"""
4076 print("Applying %s updates..." % update_file)
4080 ldif_file = open(os.path.join(base_dir, update_file))
4082 count = self._apply_updates_in_file(samdb, ldif_file)
4088 print("%u changes applied" % count)
4092 def run(self, **kwargs):
4093 from samba.ms_schema_markdown import read_ms_markdown
4094 from samba.schema import Schema
4096 updates_allowed_overriden = False
4097 sambaopts = kwargs.get("sambaopts")
4098 credopts = kwargs.get("credopts")
4099 versionpts = kwargs.get("versionopts")
4100 lp = sambaopts.get_loadparm()
4101 creds = credopts.get_credentials(lp)
4103 target_schema = kwargs.get("schema")
4104 ldf_files = kwargs.get("ldf_file")
4105 base_dir = kwargs.get("base_dir")
4109 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
4111 # we're not going to get far if the config doesn't allow schema updates
4112 if lp.get("dsdb:schema update allowed") is None:
4113 lp.set("dsdb:schema update allowed", "yes")
4114 print("Temporarily overriding 'dsdb:schema update allowed' setting")
4115 updates_allowed_overriden = True
4117 own_dn = ldb.Dn(samdb, samdb.get_dsServiceName())
4118 master = get_fsmo_roleowner(samdb, str(samdb.get_schema_basedn()),
4120 if own_dn != master:
4121 raise CommandError("This server is not the schema master.")
4123 # if specific LDIF files were specified, just apply them
4125 schema_updates = ldf_files.split(",")
4129 # work out the version of the target schema we're upgrading to
4130 end = Schema.get_version(target_schema)
4132 # work out the version of the schema we're currently using
4133 res = samdb.search(base=samdb.get_schema_basedn(),
4134 scope=ldb.SCOPE_BASE, attrs=['objectVersion'])
4137 raise CommandError('Could not determine current schema version')
4138 start = int(res[0]['objectVersion'][0]) + 1
4140 diff_dir = setup_path("adprep/WindowsServerDocs")
4141 if base_dir is None:
4142 # Read from the Schema-Updates.md file
4143 temp_folder = tempfile.mkdtemp()
4145 update_file = setup_path("adprep/WindowsServerDocs/Schema-Updates.md")
4148 read_ms_markdown(update_file, temp_folder)
4149 except Exception as e:
4150 print("Exception in markdown parsing: %s" % e)
4151 shutil.rmtree(temp_folder)
4152 raise CommandError('Failed to upgrade schema')
4154 base_dir = temp_folder
4156 for version in range(start, end + 1):
4157 update = 'Sch%d.ldf' % version
4158 schema_updates.append(update)
4160 # Apply patches if we parsed the Schema-Updates.md file
4161 diff = os.path.abspath(os.path.join(diff_dir, update + '.diff'))
4162 if temp_folder and os.path.exists(diff):
4164 p = subprocess.Popen(['patch', update, '-i', diff],
4165 stdout=subprocess.PIPE,
4166 stderr=subprocess.PIPE, cwd=temp_folder)
4167 except (OSError, IOError):
4168 shutil.rmtree(temp_folder)
4169 raise CommandError("Failed to upgrade schema. Check if 'patch' is installed.")
4171 stdout, stderr = p.communicate()
4174 print("Exception in patch: %s\n%s" % (stdout, stderr))
4175 shutil.rmtree(temp_folder)
4176 raise CommandError('Failed to upgrade schema')
4178 print("Patched %s using %s" % (update, diff))
4180 if base_dir is None:
4181 base_dir = setup_path("adprep")
4183 samdb.transaction_start()
4185 error_encountered = False
4188 # Apply the schema updates needed to move to the new schema version
4189 for ldif_file in schema_updates:
4190 count += self._apply_update(samdb, ldif_file, base_dir)
4193 samdb.transaction_commit()
4194 print("Schema successfully updated")
4196 print("No changes applied to schema")
4197 samdb.transaction_cancel()
4198 except Exception as e:
4199 print("Exception: %s" % e)
4200 print("Error encountered, aborting schema upgrade")
4201 samdb.transaction_cancel()
4202 error_encountered = True
4204 if updates_allowed_overriden:
4205 lp.set("dsdb:schema update allowed", "no")
4208 shutil.rmtree(temp_folder)
4210 if error_encountered:
4211 raise CommandError('Failed to upgrade schema')
4213 class cmd_domain_functional_prep(Command):
4214 """Domain functional level preparation"""
4216 synopsis = "%prog [options]"
4218 takes_optiongroups = {
4219 "sambaopts": options.SambaOptions,
4220 "versionopts": options.VersionOptions,
4221 "credopts": options.CredentialsOptions,
4225 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
4226 metavar="URL", dest="H"),
4227 Option("-q", "--quiet", help="Be quiet", action="store_true"),
4228 Option("-v", "--verbose", help="Be verbose", action="store_true"),
4229 Option("--function-level", type="choice", metavar="FUNCTION_LEVEL",
4230 choices=["2008_R2", "2012", "2012_R2"],
4231 help="The schema file to upgrade to. Default is (Windows) 2012_R2.",
4233 Option("--forest-prep", action="store_true",
4234 help="Run the forest prep (by default, both the domain and forest prep are run)."),
4235 Option("--domain-prep", action="store_true",
4236 help="Run the domain prep (by default, both the domain and forest prep are run).")
4239 def run(self, **kwargs):
4240 updates_allowed_overriden = False
4241 sambaopts = kwargs.get("sambaopts")
4242 credopts = kwargs.get("credopts")
4243 versionpts = kwargs.get("versionopts")
4244 lp = sambaopts.get_loadparm()
4245 creds = credopts.get_credentials(lp)
4247 target_level = string_version_to_constant[kwargs.get("function_level")]
4248 forest_prep = kwargs.get("forest_prep")
4249 domain_prep = kwargs.get("domain_prep")
4251 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
4253 # we're not going to get far if the config doesn't allow schema updates
4254 if lp.get("dsdb:schema update allowed") is None:
4255 lp.set("dsdb:schema update allowed", "yes")
4256 print("Temporarily overriding 'dsdb:schema update allowed' setting")
4257 updates_allowed_overriden = True
4259 if forest_prep is None and domain_prep is None:
4263 own_dn = ldb.Dn(samdb, samdb.get_dsServiceName())
4265 master = get_fsmo_roleowner(samdb, str(samdb.get_schema_basedn()),
4267 if own_dn != master:
4268 raise CommandError("This server is not the schema master.")
4271 domain_dn = samdb.domain_dn()
4272 infrastructure_dn = "CN=Infrastructure," + domain_dn
4273 master = get_fsmo_roleowner(samdb, infrastructure_dn,
4275 if own_dn != master:
4276 raise CommandError("This server is not the infrastructure master.")
4279 samdb.transaction_start()
4280 error_encountered = False
4282 from samba.forest_update import ForestUpdate
4283 forest = ForestUpdate(samdb, fix=True)
4285 forest.check_updates_iterator([53, 79, 80, 81, 82, 83])
4286 forest.check_updates_functional_level(target_level,
4287 DS_DOMAIN_FUNCTION_2008_R2,
4288 update_revision=True)
4290 samdb.transaction_commit()
4291 except Exception as e:
4292 print("Exception: %s" % e)
4293 samdb.transaction_cancel()
4294 error_encountered = True
4297 samdb.transaction_start()
4298 error_encountered = False
4300 from samba.domain_update import DomainUpdate
4302 domain = DomainUpdate(samdb, fix=True)
4303 domain.check_updates_functional_level(target_level,
4304 DS_DOMAIN_FUNCTION_2008,
4305 update_revision=True)
4307 samdb.transaction_commit()
4308 except Exception as e:
4309 print("Exception: %s" % e)
4310 samdb.transaction_cancel()
4311 error_encountered = True
4313 if updates_allowed_overriden:
4314 lp.set("dsdb:schema update allowed", "no")
4316 if error_encountered:
4317 raise CommandError('Failed to perform functional prep')
4319 class cmd_domain(SuperCommand):
4320 """Domain management."""
4323 subcommands["demote"] = cmd_domain_demote()
4324 if cmd_domain_export_keytab is not None:
4325 subcommands["exportkeytab"] = cmd_domain_export_keytab()
4326 subcommands["info"] = cmd_domain_info()
4327 subcommands["provision"] = cmd_domain_provision()
4328 subcommands["join"] = cmd_domain_join()
4329 subcommands["dcpromo"] = cmd_domain_dcpromo()
4330 subcommands["level"] = cmd_domain_level()
4331 subcommands["passwordsettings"] = cmd_domain_passwordsettings()
4332 subcommands["classicupgrade"] = cmd_domain_classicupgrade()
4333 subcommands["samba3upgrade"] = cmd_domain_samba3upgrade()
4334 subcommands["trust"] = cmd_domain_trust()
4335 subcommands["tombstones"] = cmd_domain_tombstones()
4336 subcommands["schemaupgrade"] = cmd_domain_schema_upgrade()
4337 subcommands["functionalprep"] = cmd_domain_functional_prep()
4338 subcommands["backup"] = cmd_domain_backup()