3 # Copyright Matthias Dieter Wallnoefer 2009
4 # Copyright Andrew Kroeger 2009
5 # Copyright Jelmer Vernooij 2007-2012
6 # Copyright Giampaolo Lauria 2011
7 # Copyright Matthieu Patou <mat@matws.net> 2011
8 # Copyright Andrew Bartlett 2008-2015
9 # Copyright Stefan Metzmacher 2012
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 3 of the License, or
14 # (at your option) any later version.
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 from __future__ import print_function
26 from __future__ import division
27 import samba.getopt as options
39 from samba import ntstatus
40 from samba import NTSTATUSError
41 from samba import werror
42 from getpass import getpass
43 from samba.net import Net, LIBNET_JOIN_AUTOMATIC
45 from samba.join import join_RODC, join_DC, join_subdomain
46 from samba.auth import system_session
47 from samba.samdb import SamDB, get_default_backend_store
48 from samba.ndr import ndr_unpack, ndr_pack, ndr_print
49 from samba.dcerpc import drsuapi
50 from samba.dcerpc import drsblobs
51 from samba.dcerpc import lsa
52 from samba.dcerpc import netlogon
53 from samba.dcerpc import security
54 from samba.dcerpc import nbt
55 from samba.dcerpc import misc
56 from samba.dcerpc.samr import DOMAIN_PASSWORD_COMPLEX, DOMAIN_PASSWORD_STORE_CLEARTEXT
57 from samba.netcmd import (
63 from samba.netcmd.fsmo import get_fsmo_roleowner
64 from samba.netcmd.common import netcmd_get_domain_infos_via_cldap
65 from samba.samba3 import Samba3
66 from samba.samba3 import param as s3param
67 from samba.upgrade import upgrade_from_samba3
68 from samba.drs_utils import (
69 sendDsReplicaSync, drsuapi_connect, drsException,
71 from samba import remove_dc, arcfour_encrypt, string_to_byte_array
73 from samba.dsdb import (
74 DS_DOMAIN_FUNCTION_2000,
75 DS_DOMAIN_FUNCTION_2003,
76 DS_DOMAIN_FUNCTION_2003_MIXED,
77 DS_DOMAIN_FUNCTION_2008,
78 DS_DOMAIN_FUNCTION_2008_R2,
79 DS_DOMAIN_FUNCTION_2012,
80 DS_DOMAIN_FUNCTION_2012_R2,
81 DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL,
82 DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL,
83 UF_WORKSTATION_TRUST_ACCOUNT,
84 UF_SERVER_TRUST_ACCOUNT,
85 UF_TRUSTED_FOR_DELEGATION,
86 UF_PARTIAL_SECRETS_ACCOUNT
89 from samba.provision import (
92 DEFAULT_MIN_PWD_LENGTH,
96 from samba.provision.common import (
102 string_version_to_constant = {
103 "2008_R2" : DS_DOMAIN_FUNCTION_2008_R2,
104 "2012": DS_DOMAIN_FUNCTION_2012,
105 "2012_R2": DS_DOMAIN_FUNCTION_2012_R2,
108 def get_testparm_var(testparm, smbconf, varname):
109 errfile = open(os.devnull, 'w')
110 p = subprocess.Popen([testparm, '-s', '-l',
111 '--parameter-name=%s' % varname, smbconf],
112 stdout=subprocess.PIPE, stderr=errfile)
113 (out,err) = p.communicate()
115 lines = out.split('\n')
117 return lines[0].strip()
121 import samba.dckeytab
123 cmd_domain_export_keytab = None
125 class cmd_domain_export_keytab(Command):
126 """Dump Kerberos keys of the domain into a keytab."""
128 synopsis = "%prog <keytab> [options]"
130 takes_optiongroups = {
131 "sambaopts": options.SambaOptions,
132 "credopts": options.CredentialsOptions,
133 "versionopts": options.VersionOptions,
137 Option("--principal", help="extract only this principal", type=str),
140 takes_args = ["keytab"]
142 def run(self, keytab, credopts=None, sambaopts=None, versionopts=None, principal=None):
143 lp = sambaopts.get_loadparm()
145 net.export_keytab(keytab=keytab, principal=principal)
148 class cmd_domain_info(Command):
149 """Print basic info about a domain and the DC passed as parameter."""
151 synopsis = "%prog <ip_address> [options]"
156 takes_optiongroups = {
157 "sambaopts": options.SambaOptions,
158 "credopts": options.CredentialsOptions,
159 "versionopts": options.VersionOptions,
162 takes_args = ["address"]
164 def run(self, address, credopts=None, sambaopts=None, versionopts=None):
165 lp = sambaopts.get_loadparm()
167 res = netcmd_get_domain_infos_via_cldap(lp, None, address)
169 raise CommandError("Invalid IP address '" + address + "'!")
170 self.outf.write("Forest : %s\n" % res.forest)
171 self.outf.write("Domain : %s\n" % res.dns_domain)
172 self.outf.write("Netbios domain : %s\n" % res.domain_name)
173 self.outf.write("DC name : %s\n" % res.pdc_dns_name)
174 self.outf.write("DC netbios name : %s\n" % res.pdc_name)
175 self.outf.write("Server site : %s\n" % res.server_site)
176 self.outf.write("Client site : %s\n" % res.client_site)
179 class cmd_domain_provision(Command):
180 """Provision a domain."""
182 synopsis = "%prog [options]"
184 takes_optiongroups = {
185 "sambaopts": options.SambaOptions,
186 "versionopts": options.VersionOptions,
190 Option("--interactive", help="Ask for names", action="store_true"),
191 Option("--domain", type="string", metavar="DOMAIN",
192 help="NetBIOS domain name to use"),
193 Option("--domain-guid", type="string", metavar="GUID",
194 help="set domainguid (otherwise random)"),
195 Option("--domain-sid", type="string", metavar="SID",
196 help="set domainsid (otherwise random)"),
197 Option("--ntds-guid", type="string", metavar="GUID",
198 help="set NTDS object GUID (otherwise random)"),
199 Option("--invocationid", type="string", metavar="GUID",
200 help="set invocationid (otherwise random)"),
201 Option("--host-name", type="string", metavar="HOSTNAME",
202 help="set hostname"),
203 Option("--host-ip", type="string", metavar="IPADDRESS",
204 help="set IPv4 ipaddress"),
205 Option("--host-ip6", type="string", metavar="IP6ADDRESS",
206 help="set IPv6 ipaddress"),
207 Option("--site", type="string", metavar="SITENAME",
208 help="set site name"),
209 Option("--adminpass", type="string", metavar="PASSWORD",
210 help="choose admin password (otherwise random)"),
211 Option("--krbtgtpass", type="string", metavar="PASSWORD",
212 help="choose krbtgt password (otherwise random)"),
213 Option("--machinepass", type="string", metavar="PASSWORD",
214 help="choose machine password (otherwise random)"),
215 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
216 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
217 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
218 "BIND9_FLATFILE uses bind9 text database to store zone information, "
219 "BIND9_DLZ uses samba4 AD to store zone information, "
220 "NONE skips the DNS setup entirely (not recommended)",
221 default="SAMBA_INTERNAL"),
222 Option("--dnspass", type="string", metavar="PASSWORD",
223 help="choose dns password (otherwise random)"),
224 Option("--ldapadminpass", type="string", metavar="PASSWORD",
225 help="choose password to set between Samba and its LDAP backend (otherwise random)"),
226 Option("--root", type="string", metavar="USERNAME",
227 help="choose 'root' unix username"),
228 Option("--nobody", type="string", metavar="USERNAME",
229 help="choose 'nobody' user"),
230 Option("--users", type="string", metavar="GROUPNAME",
231 help="choose 'users' group"),
232 Option("--quiet", help="Be quiet", action="store_true"),
233 Option("--blank", action="store_true",
234 help="do not add users or groups, just the structure"),
235 Option("--ldap-backend-type", type="choice", metavar="LDAP-BACKEND-TYPE",
236 help="Test initialisation support for unsupported LDAP backend type (fedora-ds or openldap) DO NOT USE",
237 choices=["fedora-ds", "openldap"]),
238 Option("--server-role", type="choice", metavar="ROLE",
239 choices=["domain controller", "dc", "member server", "member", "standalone"],
240 help="The server role (domain controller | dc | member server | member | standalone). Default is dc.",
241 default="domain controller"),
242 Option("--function-level", type="choice", metavar="FOR-FUN-LEVEL",
243 choices=["2000", "2003", "2008", "2008_R2"],
244 help="The domain and forest function level (2000 | 2003 | 2008 | 2008_R2 - always native). Default is (Windows) 2008_R2 Native.",
246 Option("--base-schema", type="choice", metavar="BASE-SCHEMA",
247 choices=["2008_R2", "2008_R2_old", "2012", "2012_R2"],
248 help="The base schema files to use. Default is (Windows) 2008_R2.",
250 Option("--next-rid", type="int", metavar="NEXTRID", default=1000,
251 help="The initial nextRid value (only needed for upgrades). Default is 1000."),
252 Option("--partitions-only",
253 help="Configure Samba's partitions, but do not modify them (ie, join a BDC)", action="store_true"),
254 Option("--targetdir", type="string", metavar="DIR",
255 help="Set target directory"),
256 Option("--ol-mmr-urls", type="string", metavar="LDAPSERVER",
257 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\""),
258 Option("--use-rfc2307", action="store_true", help="Use AD to store posix attributes (default = no)"),
259 Option("--plaintext-secrets", action="store_true",
260 help="Store secret/sensitive values as plain text on disk" +
261 "(default is to encrypt secret/ensitive values)"),
262 Option("--backend-store", type="choice", metavar="BACKENDSTORE",
263 choices=["tdb", "mdb"],
264 help="Specify the database backend to be used "
265 "(default is %s)" % get_default_backend_store()),
269 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",
270 action="store_true"),
271 Option("--slapd-path", type="string", metavar="SLAPD-PATH",
272 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."),
273 Option("--ldap-backend-extra-port", type="int", metavar="LDAP-BACKEND-EXTRA-PORT", help="Additional TCP port for LDAP backend server (to use for replication)"),
274 Option("--ldap-backend-forced-uri", type="string", metavar="LDAP-BACKEND-FORCED-URI",
275 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"),
276 Option("--ldap-backend-nosync", help="Configure LDAP backend not to call fsync() (for performance in test environments)", action="store_true"),
280 Option("--use-ntvfs", action="store_true", help="Use NTVFS for the fileserver (default = no)"),
281 Option("--use-xattrs", type="choice", choices=["yes","no","auto"],
282 metavar="[yes|no|auto]",
283 help="Define if we should use the native fs capabilities or a tdb file for "
284 "storing attributes likes ntacl when --use-ntvfs is set. "
285 "auto tries to make an inteligent guess based on the user rights and system capabilities",
289 if os.getenv('TEST_LDAP', "no") == "yes":
290 takes_options.extend(openldap_options)
292 if samba.is_ntvfs_fileserver_built():
293 takes_options.extend(ntvfs_options)
297 def run(self, sambaopts=None, versionopts=None,
320 ldap_backend_type=None,
324 partitions_only=None,
331 ldap_backend_nosync=None,
332 ldap_backend_extra_port=None,
333 ldap_backend_forced_uri=None,
334 ldap_dryrun_mode=None,
336 plaintext_secrets=False,
339 self.logger = self.get_logger("provision")
341 self.logger.setLevel(logging.WARNING)
343 self.logger.setLevel(logging.INFO)
345 lp = sambaopts.get_loadparm()
346 smbconf = lp.configfile
348 if dns_forwarder is not None:
349 suggested_forwarder = dns_forwarder
351 suggested_forwarder = self._get_nameserver_ip()
352 if suggested_forwarder is None:
353 suggested_forwarder = "none"
355 if len(self.raw_argv) == 1:
359 from getpass import getpass
362 def ask(prompt, default=None):
363 if default is not None:
364 print("%s [%s]: " % (prompt, default), end=' ')
366 print("%s: " % (prompt,), end=' ')
367 return sys.stdin.readline().rstrip("\n") or default
370 default = socket.getfqdn().split(".", 1)[1].upper()
373 realm = ask("Realm", default)
374 if realm in (None, ""):
375 raise CommandError("No realm set!")
378 default = realm.split(".")[0]
381 domain = ask("Domain", default)
383 raise CommandError("No domain set!")
385 server_role = ask("Server Role (dc, member, standalone)", "dc")
387 dns_backend = ask("DNS backend (SAMBA_INTERNAL, BIND9_FLATFILE, BIND9_DLZ, NONE)", "SAMBA_INTERNAL")
388 if dns_backend in (None, ''):
389 raise CommandError("No DNS backend set!")
391 if dns_backend == "SAMBA_INTERNAL":
392 dns_forwarder = ask("DNS forwarder IP address (write 'none' to disable forwarding)", suggested_forwarder)
393 if dns_forwarder.lower() in (None, 'none'):
394 suggested_forwarder = None
398 adminpassplain = getpass("Administrator password: ")
399 issue = self._adminpass_issue(adminpassplain)
401 self.errf.write("%s.\n" % issue)
403 adminpassverify = getpass("Retype password: ")
404 if not adminpassplain == adminpassverify:
405 self.errf.write("Sorry, passwords do not match.\n")
407 adminpass = adminpassplain
411 realm = sambaopts._lp.get('realm')
413 raise CommandError("No realm set!")
415 raise CommandError("No domain set!")
418 issue = self._adminpass_issue(adminpass)
420 raise CommandError(issue)
422 self.logger.info("Administrator password will be set randomly!")
424 if function_level == "2000":
425 dom_for_fun_level = DS_DOMAIN_FUNCTION_2000
426 elif function_level == "2003":
427 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
428 elif function_level == "2008":
429 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008
430 elif function_level == "2008_R2":
431 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
433 if dns_backend == "SAMBA_INTERNAL" and dns_forwarder is None:
434 dns_forwarder = suggested_forwarder
436 samdb_fill = FILL_FULL
438 samdb_fill = FILL_NT4SYNC
439 elif partitions_only:
440 samdb_fill = FILL_DRS
442 if targetdir is not None:
443 if not os.path.isdir(targetdir):
448 if use_xattrs == "yes":
450 elif use_xattrs == "auto" and use_ntvfs == False:
452 elif use_ntvfs == False:
453 raise CommandError("--use-xattrs=no requires --use-ntvfs (not supported for production use). "
454 "Please re-run with --use-xattrs omitted.")
455 elif use_xattrs == "auto" and not lp.get("posix:eadb"):
457 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
459 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
462 samba.ntacls.setntacl(lp, file.name,
463 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
466 self.logger.info("You are not root or your system does not support xattr, using tdb backend for attributes. ")
471 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.")
472 if ldap_backend_type == "existing":
473 if ldap_backend_forced_uri is not None:
474 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)
476 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")
478 if ldap_backend_forced_uri is not None:
479 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")
481 if domain_sid is not None:
482 domain_sid = security.dom_sid(domain_sid)
484 session = system_session()
485 if backend_store is None:
486 backend_store = get_default_backend_store()
488 result = provision(self.logger,
489 session, smbconf=smbconf, targetdir=targetdir,
490 samdb_fill=samdb_fill, realm=realm, domain=domain,
491 domainguid=domain_guid, domainsid=domain_sid,
493 hostip=host_ip, hostip6=host_ip6,
494 sitename=site, ntdsguid=ntds_guid,
495 invocationid=invocationid, adminpass=adminpass,
496 krbtgtpass=krbtgtpass, machinepass=machinepass,
497 dns_backend=dns_backend, dns_forwarder=dns_forwarder,
498 dnspass=dnspass, root=root, nobody=nobody,
500 serverrole=server_role, dom_for_fun_level=dom_for_fun_level,
501 backend_type=ldap_backend_type,
502 ldapadminpass=ldapadminpass, ol_mmr_urls=ol_mmr_urls, slapd_path=slapd_path,
503 useeadb=eadb, next_rid=next_rid, lp=lp, use_ntvfs=use_ntvfs,
504 use_rfc2307=use_rfc2307, skip_sysvolacl=False,
505 ldap_backend_extra_port=ldap_backend_extra_port,
506 ldap_backend_forced_uri=ldap_backend_forced_uri,
507 nosync=ldap_backend_nosync, ldap_dryrun_mode=ldap_dryrun_mode,
508 base_schema=base_schema,
509 plaintext_secrets=plaintext_secrets,
510 backend_store=backend_store)
512 except ProvisioningError as e:
513 raise CommandError("Provision failed", e)
515 result.report_logger(self.logger)
517 def _get_nameserver_ip(self):
518 """Grab the nameserver IP address from /etc/resolv.conf."""
520 RESOLV_CONF="/etc/resolv.conf"
522 if not path.isfile(RESOLV_CONF):
523 self.logger.warning("Failed to locate %s" % RESOLV_CONF)
528 handle = open(RESOLV_CONF, 'r')
530 if not line.startswith('nameserver'):
532 # we want the last non-space continuous string of the line
533 return line.strip().split()[-1]
535 if handle is not None:
538 self.logger.warning("No nameserver found in %s" % RESOLV_CONF)
540 def _adminpass_issue(self, adminpass):
541 """Returns error string for a bad administrator password,
542 or None if acceptable"""
544 if len(adminpass.decode('utf-8')) < DEFAULT_MIN_PWD_LENGTH:
545 return "Administrator password does not meet the default minimum" \
546 " password length requirement (%d characters)" \
547 % DEFAULT_MIN_PWD_LENGTH
548 elif not samba.check_password_quality(adminpass):
549 return "Administrator password does not meet the default" \
555 class cmd_domain_dcpromo(Command):
556 """Promote an existing domain member or NT4 PDC to an AD DC."""
558 synopsis = "%prog <dnsdomain> [DC|RODC] [options]"
560 takes_optiongroups = {
561 "sambaopts": options.SambaOptions,
562 "versionopts": options.VersionOptions,
563 "credopts": options.CredentialsOptions,
567 Option("--server", help="DC to join", type=str),
568 Option("--site", help="site to join", type=str),
569 Option("--targetdir", help="where to store provision", type=str),
570 Option("--domain-critical-only",
571 help="only replicate critical domain objects",
572 action="store_true"),
573 Option("--machinepass", type=str, metavar="PASSWORD",
574 help="choose machine password (otherwise random)"),
575 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
576 choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
577 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
578 "BIND9_DLZ uses samba4 AD to store zone information, "
579 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
580 default="SAMBA_INTERNAL"),
581 Option("--quiet", help="Be quiet", action="store_true"),
582 Option("--verbose", help="Be verbose", action="store_true")
586 Option("--use-ntvfs", action="store_true", help="Use NTVFS for the fileserver (default = no)"),
589 if samba.is_ntvfs_fileserver_built():
590 takes_options.extend(ntvfs_options)
593 takes_args = ["domain", "role?"]
595 def run(self, domain, role=None, sambaopts=None, credopts=None,
596 versionopts=None, server=None, site=None, targetdir=None,
597 domain_critical_only=False, parent_domain=None, machinepass=None,
598 use_ntvfs=False, dns_backend=None,
599 quiet=False, verbose=False):
600 lp = sambaopts.get_loadparm()
601 creds = credopts.get_credentials(lp)
602 net = Net(creds, lp, server=credopts.ipaddress)
604 logger = self.get_logger()
606 logger.setLevel(logging.DEBUG)
608 logger.setLevel(logging.WARNING)
610 logger.setLevel(logging.INFO)
612 netbios_name = lp.get("netbios name")
618 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
619 site=site, netbios_name=netbios_name, targetdir=targetdir,
620 domain_critical_only=domain_critical_only,
621 machinepass=machinepass, use_ntvfs=use_ntvfs,
622 dns_backend=dns_backend,
623 promote_existing=True)
625 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
626 site=site, netbios_name=netbios_name, targetdir=targetdir,
627 domain_critical_only=domain_critical_only,
628 machinepass=machinepass, use_ntvfs=use_ntvfs, dns_backend=dns_backend,
629 promote_existing=True)
631 raise CommandError("Invalid role '%s' (possible values: DC, RODC)" % role)
634 class cmd_domain_join(Command):
635 """Join domain as either member or backup domain controller."""
637 synopsis = "%prog <dnsdomain> [DC|RODC|MEMBER|SUBDOMAIN] [options]"
639 takes_optiongroups = {
640 "sambaopts": options.SambaOptions,
641 "versionopts": options.VersionOptions,
642 "credopts": options.CredentialsOptions,
646 Option("--server", help="DC to join", type=str),
647 Option("--site", help="site to join", type=str),
648 Option("--targetdir", help="where to store provision", type=str),
649 Option("--parent-domain", help="parent domain to create subdomain under", type=str),
650 Option("--domain-critical-only",
651 help="only replicate critical domain objects",
652 action="store_true"),
653 Option("--machinepass", type=str, metavar="PASSWORD",
654 help="choose machine password (otherwise random)"),
655 Option("--adminpass", type="string", metavar="PASSWORD",
656 help="choose adminstrator password when joining as a subdomain (otherwise random)"),
657 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
658 choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
659 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
660 "BIND9_DLZ uses samba4 AD to store zone information, "
661 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
662 default="SAMBA_INTERNAL"),
663 Option("--plaintext-secrets", action="store_true",
664 help="Store secret/sensitive values as plain text on disk" +
665 "(default is to encrypt secret/ensitive values)"),
666 Option("--quiet", help="Be quiet", action="store_true"),
667 Option("--verbose", help="Be verbose", action="store_true")
671 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
674 if samba.is_ntvfs_fileserver_built():
675 takes_options.extend(ntvfs_options)
677 takes_args = ["domain", "role?"]
679 def run(self, domain, role=None, sambaopts=None, credopts=None,
680 versionopts=None, server=None, site=None, targetdir=None,
681 domain_critical_only=False, parent_domain=None, machinepass=None,
682 use_ntvfs=False, dns_backend=None, adminpass=None,
683 quiet=False, verbose=False, plaintext_secrets=False):
684 lp = sambaopts.get_loadparm()
685 creds = credopts.get_credentials(lp)
686 net = Net(creds, lp, server=credopts.ipaddress)
689 site = "Default-First-Site-Name"
691 logger = self.get_logger()
693 logger.setLevel(logging.DEBUG)
695 logger.setLevel(logging.WARNING)
697 logger.setLevel(logging.INFO)
699 netbios_name = lp.get("netbios name")
704 if role is None or role == "MEMBER":
705 (join_password, sid, domain_name) = net.join_member(
706 domain, netbios_name, LIBNET_JOIN_AUTOMATIC,
707 machinepass=machinepass)
709 self.errf.write("Joined domain %s (%s)\n" % (domain_name, sid))
711 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
712 site=site, netbios_name=netbios_name, targetdir=targetdir,
713 domain_critical_only=domain_critical_only,
714 machinepass=machinepass, use_ntvfs=use_ntvfs,
715 dns_backend=dns_backend,
716 plaintext_secrets=plaintext_secrets)
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 elif role == "SUBDOMAIN":
726 logger.info("Administrator password will be set randomly!")
728 netbios_domain = lp.get("workgroup")
729 if parent_domain is None:
730 parent_domain = ".".join(domain.split(".")[1:])
731 join_subdomain(logger=logger, server=server, creds=creds, lp=lp, dnsdomain=domain,
732 parent_domain=parent_domain, site=site,
733 netbios_name=netbios_name, netbios_domain=netbios_domain,
734 targetdir=targetdir, machinepass=machinepass,
735 use_ntvfs=use_ntvfs, dns_backend=dns_backend,
737 plaintext_secrets=plaintext_secrets)
739 raise CommandError("Invalid role '%s' (possible values: MEMBER, DC, RODC, SUBDOMAIN)" % role)
742 class cmd_domain_demote(Command):
743 """Demote ourselves from the role of Domain Controller."""
745 synopsis = "%prog [options]"
748 Option("--server", help="writable DC to write demotion changes on", type=str),
749 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
750 metavar="URL", dest="H"),
751 Option("--remove-other-dead-server", help="Dead DC (name or NTDS GUID) "
752 "to remove ALL references to (rather than this DC)", type=str),
753 Option("--quiet", help="Be quiet", action="store_true"),
754 Option("--verbose", help="Be verbose", action="store_true"),
757 takes_optiongroups = {
758 "sambaopts": options.SambaOptions,
759 "credopts": options.CredentialsOptions,
760 "versionopts": options.VersionOptions,
763 def run(self, sambaopts=None, credopts=None,
764 versionopts=None, server=None,
765 remove_other_dead_server=None, H=None,
766 verbose=False, quiet=False):
767 lp = sambaopts.get_loadparm()
768 creds = credopts.get_credentials(lp)
769 net = Net(creds, lp, server=credopts.ipaddress)
771 logger = self.get_logger()
773 logger.setLevel(logging.DEBUG)
775 logger.setLevel(logging.WARNING)
777 logger.setLevel(logging.INFO)
779 if remove_other_dead_server is not None:
780 if server is not None:
781 samdb = SamDB(url="ldap://%s" % server,
782 session_info=system_session(),
783 credentials=creds, lp=lp)
785 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
787 remove_dc.remove_dc(samdb, logger, remove_other_dead_server)
788 except remove_dc.DemoteException as err:
789 raise CommandError("Demote failed: %s" % err)
792 netbios_name = lp.get("netbios name")
793 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
795 res = samdb.search(expression='(&(objectClass=computer)(serverReferenceBL=*))', attrs=["dnsHostName", "name"])
797 raise CommandError("Unable to search for servers")
800 raise CommandError("You are the latest server in the domain")
804 if str(e["name"]).lower() != netbios_name.lower():
805 server = e["dnsHostName"]
808 ntds_guid = samdb.get_ntds_GUID()
809 msg = samdb.search(base=str(samdb.get_config_basedn()),
810 scope=ldb.SCOPE_SUBTREE, expression="(objectGUID=%s)" % ntds_guid,
812 if len(msg) == 0 or "options" not in msg[0]:
813 raise CommandError("Failed to find options on %s" % ntds_guid)
816 dsa_options = int(str(msg[0]['options']))
818 res = samdb.search(expression="(fSMORoleOwner=%s)" % str(ntds_dn),
819 controls=["search_options:1:2"])
822 raise CommandError("Current DC is still the owner of %d role(s), use the role command to transfer roles to another DC" % len(res))
824 self.errf.write("Using %s as partner server for the demotion\n" %
826 (drsuapiBind, drsuapi_handle, supportedExtensions) = drsuapi_connect(server, lp, creds)
828 self.errf.write("Deactivating inbound replication\n")
833 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
834 dsa_options |= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
835 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
839 self.errf.write("Asking partner server %s to synchronize from us\n"
841 for part in (samdb.get_schema_basedn(),
842 samdb.get_config_basedn(),
843 samdb.get_root_basedn()):
844 nc = drsuapi.DsReplicaObjectIdentifier()
847 req1 = drsuapi.DsReplicaSyncRequest1()
848 req1.naming_context = nc;
849 req1.options = drsuapi.DRSUAPI_DRS_WRIT_REP
850 req1.source_dsa_guid = misc.GUID(ntds_guid)
853 drsuapiBind.DsReplicaSync(drsuapi_handle, 1, req1)
854 except RuntimeError as e1:
855 (werr, string) = e1.args
856 if werr == werror.WERR_DS_DRA_NO_REPLICA:
860 "Error while replicating out last local changes from '%s' for demotion, "
861 "re-enabling inbound replication\n" % part)
862 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
863 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
865 raise CommandError("Error while sending a DsReplicaSync for partition '%s'" % str(part), string)
867 remote_samdb = SamDB(url="ldap://%s" % server,
868 session_info=system_session(),
869 credentials=creds, lp=lp)
871 self.errf.write("Changing userControl and container\n")
872 res = remote_samdb.search(base=str(remote_samdb.domain_dn()),
873 expression="(&(objectClass=user)(sAMAccountName=%s$))" %
874 netbios_name.upper(),
875 attrs=["userAccountControl"])
877 uac = int(str(res[0]["userAccountControl"]))
879 except Exception as e:
880 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
882 "Error while demoting, re-enabling inbound replication\n")
883 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
884 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
886 raise CommandError("Error while changing account control", e)
889 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
891 "Error while demoting, re-enabling inbound replication")
892 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
893 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
895 raise CommandError("Unable to find object with samaccountName = %s$"
896 " in the remote dc" % netbios_name.upper())
900 uac &= ~(UF_SERVER_TRUST_ACCOUNT|UF_TRUSTED_FOR_DELEGATION|UF_PARTIAL_SECRETS_ACCOUNT)
901 uac |= UF_WORKSTATION_TRUST_ACCOUNT
906 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
907 ldb.FLAG_MOD_REPLACE,
908 "userAccountControl")
910 remote_samdb.modify(msg)
911 except Exception as e:
912 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
914 "Error while demoting, re-enabling inbound replication")
915 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
916 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
919 raise CommandError("Error while changing account control", e)
921 parent = msg.dn.parent()
922 dc_name = res[0].dn.get_rdn_value()
923 rdn = "CN=%s" % dc_name
925 # Let's move to the Computer container
929 computer_dn = ldb.Dn(remote_samdb, "CN=Computers,%s" % str(remote_samdb.domain_dn()))
930 res = remote_samdb.search(base=computer_dn, expression=rdn, scope=ldb.SCOPE_ONELEVEL)
933 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
934 scope=ldb.SCOPE_ONELEVEL)
935 while(len(res) != 0 and i < 100):
937 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
938 scope=ldb.SCOPE_ONELEVEL)
941 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
943 "Error while demoting, re-enabling inbound replication\n")
944 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
945 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
951 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
952 ldb.FLAG_MOD_REPLACE,
953 "userAccountControl")
955 remote_samdb.modify(msg)
957 raise CommandError("Unable to find a slot for renaming %s,"
958 " all names from %s-1 to %s-%d seemed used" %
959 (str(dc_dn), rdn, rdn, i - 9))
961 newrdn = "%s-%d" % (rdn, i)
964 newdn = ldb.Dn(remote_samdb, "%s,%s" % (newrdn, str(computer_dn)))
965 remote_samdb.rename(dc_dn, newdn)
966 except Exception as e:
967 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
969 "Error while demoting, re-enabling inbound replication\n")
970 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
971 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
977 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
978 ldb.FLAG_MOD_REPLACE,
979 "userAccountControl")
981 remote_samdb.modify(msg)
982 raise CommandError("Error while renaming %s to %s" % (str(dc_dn), str(newdn)), e)
985 server_dsa_dn = samdb.get_serverName()
986 domain = remote_samdb.get_root_basedn()
989 req1 = drsuapi.DsRemoveDSServerRequest1()
990 req1.server_dn = str(server_dsa_dn)
991 req1.domain_dn = str(domain)
994 drsuapiBind.DsRemoveDSServer(drsuapi_handle, 1, req1)
995 except RuntimeError as e3:
996 (werr, string) = e3.args
997 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
999 "Error while demoting, re-enabling inbound replication\n")
1000 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
1001 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
1007 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
1008 ldb.FLAG_MOD_REPLACE,
1009 "userAccountControl")
1010 remote_samdb.modify(msg)
1011 remote_samdb.rename(newdn, dc_dn)
1012 if werr == werror.WERR_DS_DRA_NO_REPLICA:
1013 raise CommandError("The DC %s is not present on (already removed from) the remote server: " % server_dsa_dn, e)
1015 raise CommandError("Error while sending a removeDsServer of %s: " % server_dsa_dn, e)
1017 remove_dc.remove_sysvol_references(remote_samdb, logger, dc_name)
1019 # These are objects under the computer account that should be deleted
1020 for s in ("CN=Enterprise,CN=NTFRS Subscriptions",
1021 "CN=%s, CN=NTFRS Subscriptions" % lp.get("realm"),
1022 "CN=Domain system Volumes (SYSVOL Share), CN=NTFRS Subscriptions",
1023 "CN=NTFRS Subscriptions"):
1025 remote_samdb.delete(ldb.Dn(remote_samdb,
1026 "%s,%s" % (s, str(newdn))))
1027 except ldb.LdbError as l:
1030 self.errf.write("Demote successful\n")
1033 class cmd_domain_level(Command):
1034 """Raise domain and forest function levels."""
1036 synopsis = "%prog (show|raise <options>) [options]"
1038 takes_optiongroups = {
1039 "sambaopts": options.SambaOptions,
1040 "credopts": options.CredentialsOptions,
1041 "versionopts": options.VersionOptions,
1045 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1046 metavar="URL", dest="H"),
1047 Option("--quiet", help="Be quiet", action="store_true"),
1048 Option("--forest-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
1049 help="The forest function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)"),
1050 Option("--domain-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
1051 help="The domain function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)")
1054 takes_args = ["subcommand"]
1056 def run(self, subcommand, H=None, forest_level=None, domain_level=None,
1057 quiet=False, credopts=None, sambaopts=None, versionopts=None):
1058 lp = sambaopts.get_loadparm()
1059 creds = credopts.get_credentials(lp, fallback_machine=True)
1061 samdb = SamDB(url=H, session_info=system_session(),
1062 credentials=creds, lp=lp)
1064 domain_dn = samdb.domain_dn()
1066 res_forest = samdb.search("CN=Partitions,%s" % samdb.get_config_basedn(),
1067 scope=ldb.SCOPE_BASE, attrs=["msDS-Behavior-Version"])
1068 assert len(res_forest) == 1
1070 res_domain = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1071 attrs=["msDS-Behavior-Version", "nTMixedDomain"])
1072 assert len(res_domain) == 1
1074 res_dc_s = samdb.search("CN=Sites,%s" % samdb.get_config_basedn(),
1075 scope=ldb.SCOPE_SUBTREE, expression="(objectClass=nTDSDSA)",
1076 attrs=["msDS-Behavior-Version"])
1077 assert len(res_dc_s) >= 1
1079 # default values, since "msDS-Behavior-Version" does not exist on Windows 2000 AD
1080 level_forest = DS_DOMAIN_FUNCTION_2000
1081 level_domain = DS_DOMAIN_FUNCTION_2000
1083 if "msDS-Behavior-Version" in res_forest[0]:
1084 level_forest = int(res_forest[0]["msDS-Behavior-Version"][0])
1085 if "msDS-Behavior-Version" in res_domain[0]:
1086 level_domain = int(res_domain[0]["msDS-Behavior-Version"][0])
1087 level_domain_mixed = int(res_domain[0]["nTMixedDomain"][0])
1090 for msg in res_dc_s:
1091 if "msDS-Behavior-Version" in msg:
1092 if min_level_dc is None or int(msg["msDS-Behavior-Version"][0]) < min_level_dc:
1093 min_level_dc = int(msg["msDS-Behavior-Version"][0])
1095 min_level_dc = DS_DOMAIN_FUNCTION_2000
1096 # well, this is the least
1099 if level_forest < DS_DOMAIN_FUNCTION_2000 or level_domain < DS_DOMAIN_FUNCTION_2000:
1100 raise CommandError("Domain and/or forest function level(s) is/are invalid. Correct them or reprovision!")
1101 if min_level_dc < DS_DOMAIN_FUNCTION_2000:
1102 raise CommandError("Lowest function level of a DC is invalid. Correct this or reprovision!")
1103 if level_forest > level_domain:
1104 raise CommandError("Forest function level is higher than the domain level(s). Correct this or reprovision!")
1105 if level_domain > min_level_dc:
1106 raise CommandError("Domain function level is higher than the lowest function level of a DC. Correct this or reprovision!")
1108 if subcommand == "show":
1109 self.message("Domain and forest function level for domain '%s'" % domain_dn)
1110 if level_forest == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1111 self.message("\nATTENTION: You run SAMBA 4 on a forest function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1112 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1113 self.message("\nATTENTION: You run SAMBA 4 on a domain function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1114 if min_level_dc == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1115 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)!")
1119 if level_forest == DS_DOMAIN_FUNCTION_2000:
1121 elif level_forest == DS_DOMAIN_FUNCTION_2003_MIXED:
1122 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1123 elif level_forest == DS_DOMAIN_FUNCTION_2003:
1125 elif level_forest == DS_DOMAIN_FUNCTION_2008:
1127 elif level_forest == DS_DOMAIN_FUNCTION_2008_R2:
1129 elif level_forest == DS_DOMAIN_FUNCTION_2012:
1131 elif level_forest == DS_DOMAIN_FUNCTION_2012_R2:
1134 outstr = "higher than 2012 R2"
1135 self.message("Forest function level: (Windows) " + outstr)
1137 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1138 outstr = "2000 mixed (NT4 DC support)"
1139 elif level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed == 0:
1141 elif level_domain == DS_DOMAIN_FUNCTION_2003_MIXED:
1142 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1143 elif level_domain == DS_DOMAIN_FUNCTION_2003:
1145 elif level_domain == DS_DOMAIN_FUNCTION_2008:
1147 elif level_domain == DS_DOMAIN_FUNCTION_2008_R2:
1149 elif level_domain == DS_DOMAIN_FUNCTION_2012:
1151 elif level_domain == DS_DOMAIN_FUNCTION_2012_R2:
1154 outstr = "higher than 2012 R2"
1155 self.message("Domain function level: (Windows) " + outstr)
1157 if min_level_dc == DS_DOMAIN_FUNCTION_2000:
1159 elif min_level_dc == DS_DOMAIN_FUNCTION_2003:
1161 elif min_level_dc == DS_DOMAIN_FUNCTION_2008:
1163 elif min_level_dc == DS_DOMAIN_FUNCTION_2008_R2:
1165 elif min_level_dc == DS_DOMAIN_FUNCTION_2012:
1167 elif min_level_dc == DS_DOMAIN_FUNCTION_2012_R2:
1170 outstr = "higher than 2012 R2"
1171 self.message("Lowest function level of a DC: (Windows) " + outstr)
1173 elif subcommand == "raise":
1176 if domain_level is not None:
1177 if domain_level == "2003":
1178 new_level_domain = DS_DOMAIN_FUNCTION_2003
1179 elif domain_level == "2008":
1180 new_level_domain = DS_DOMAIN_FUNCTION_2008
1181 elif domain_level == "2008_R2":
1182 new_level_domain = DS_DOMAIN_FUNCTION_2008_R2
1183 elif domain_level == "2012":
1184 new_level_domain = DS_DOMAIN_FUNCTION_2012
1185 elif domain_level == "2012_R2":
1186 new_level_domain = DS_DOMAIN_FUNCTION_2012_R2
1188 if new_level_domain <= level_domain and level_domain_mixed == 0:
1189 raise CommandError("Domain function level can't be smaller than or equal to the actual one!")
1190 if new_level_domain > min_level_dc:
1191 raise CommandError("Domain function level can't be higher than the lowest function level of a DC!")
1193 # Deactivate mixed/interim domain support
1194 if level_domain_mixed != 0:
1195 # Directly on the base DN
1197 m.dn = ldb.Dn(samdb, domain_dn)
1198 m["nTMixedDomain"] = ldb.MessageElement("0",
1199 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1203 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup") + ",CN=Partitions,%s" % samdb.get_config_basedn())
1204 m["nTMixedDomain"] = ldb.MessageElement("0",
1205 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1208 except ldb.LdbError as e:
1209 (enum, emsg) = e.args
1210 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1213 # Directly on the base DN
1215 m.dn = ldb.Dn(samdb, domain_dn)
1216 m["msDS-Behavior-Version"]= ldb.MessageElement(
1217 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1218 "msDS-Behavior-Version")
1222 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup")
1223 + ",CN=Partitions,%s" % samdb.get_config_basedn())
1224 m["msDS-Behavior-Version"]= ldb.MessageElement(
1225 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1226 "msDS-Behavior-Version")
1229 except ldb.LdbError as e2:
1230 (enum, emsg) = e2.args
1231 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1234 level_domain = new_level_domain
1235 msgs.append("Domain function level changed!")
1237 if forest_level is not None:
1238 if forest_level == "2003":
1239 new_level_forest = DS_DOMAIN_FUNCTION_2003
1240 elif forest_level == "2008":
1241 new_level_forest = DS_DOMAIN_FUNCTION_2008
1242 elif forest_level == "2008_R2":
1243 new_level_forest = DS_DOMAIN_FUNCTION_2008_R2
1244 elif forest_level == "2012":
1245 new_level_forest = DS_DOMAIN_FUNCTION_2012
1246 elif forest_level == "2012_R2":
1247 new_level_forest = DS_DOMAIN_FUNCTION_2012_R2
1249 if new_level_forest <= level_forest:
1250 raise CommandError("Forest function level can't be smaller than or equal to the actual one!")
1251 if new_level_forest > level_domain:
1252 raise CommandError("Forest function level can't be higher than the domain function level(s). Please raise it/them first!")
1255 m.dn = ldb.Dn(samdb, "CN=Partitions,%s" % samdb.get_config_basedn())
1256 m["msDS-Behavior-Version"]= ldb.MessageElement(
1257 str(new_level_forest), ldb.FLAG_MOD_REPLACE,
1258 "msDS-Behavior-Version")
1260 msgs.append("Forest function level changed!")
1261 msgs.append("All changes applied successfully!")
1262 self.message("\n".join(msgs))
1264 raise CommandError("invalid argument: '%s' (choose from 'show', 'raise')" % subcommand)
1267 class cmd_domain_passwordsettings(Command):
1268 """Set password settings.
1270 Password complexity, password lockout policy, history length,
1271 minimum password length, the minimum and maximum password age) on
1272 a Samba AD DC server.
1274 Use against a Windows DC is possible, but group policy will override it.
1277 synopsis = "%prog (show|set <options>) [options]"
1279 takes_optiongroups = {
1280 "sambaopts": options.SambaOptions,
1281 "versionopts": options.VersionOptions,
1282 "credopts": options.CredentialsOptions,
1286 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1287 metavar="URL", dest="H"),
1288 Option("--quiet", help="Be quiet", action="store_true"),
1289 Option("--complexity", type="choice", choices=["on","off","default"],
1290 help="The password complexity (on | off | default). Default is 'on'"),
1291 Option("--store-plaintext", type="choice", choices=["on","off","default"],
1292 help="Store plaintext passwords where account have 'store passwords with reversible encryption' set (on | off | default). Default is 'off'"),
1293 Option("--history-length",
1294 help="The password history length (<integer> | default). Default is 24.", type=str),
1295 Option("--min-pwd-length",
1296 help="The minimum password length (<integer> | default). Default is 7.", type=str),
1297 Option("--min-pwd-age",
1298 help="The minimum password age (<integer in days> | default). Default is 1.", type=str),
1299 Option("--max-pwd-age",
1300 help="The maximum password age (<integer in days> | default). Default is 43.", type=str),
1301 Option("--account-lockout-duration",
1302 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),
1303 Option("--account-lockout-threshold",
1304 help="The number of bad password attempts allowed before locking out the account (<integer> | default). Default is 0 (never lock out).", type=str),
1305 Option("--reset-account-lockout-after",
1306 help="After this time is elapsed, the recorded number of attempts restarts from zero (<integer> | default). Default is 30.", type=str),
1309 takes_args = ["subcommand"]
1311 def run(self, subcommand, H=None, min_pwd_age=None, max_pwd_age=None,
1312 quiet=False, complexity=None, store_plaintext=None, history_length=None,
1313 min_pwd_length=None, account_lockout_duration=None, account_lockout_threshold=None,
1314 reset_account_lockout_after=None, credopts=None, sambaopts=None,
1316 lp = sambaopts.get_loadparm()
1317 creds = credopts.get_credentials(lp)
1319 samdb = SamDB(url=H, session_info=system_session(),
1320 credentials=creds, lp=lp)
1322 domain_dn = samdb.domain_dn()
1323 res = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1324 attrs=["pwdProperties", "pwdHistoryLength", "minPwdLength",
1325 "minPwdAge", "maxPwdAge", "lockoutDuration", "lockoutThreshold",
1326 "lockOutObservationWindow"])
1327 assert(len(res) == 1)
1329 pwd_props = int(res[0]["pwdProperties"][0])
1330 pwd_hist_len = int(res[0]["pwdHistoryLength"][0])
1331 cur_min_pwd_len = int(res[0]["minPwdLength"][0])
1333 cur_min_pwd_age = int(abs(int(res[0]["minPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1334 if int(res[0]["maxPwdAge"][0]) == -0x8000000000000000:
1337 cur_max_pwd_age = int(abs(int(res[0]["maxPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1338 cur_account_lockout_threshold = int(res[0]["lockoutThreshold"][0])
1340 if int(res[0]["lockoutDuration"][0]) == -0x8000000000000000:
1341 cur_account_lockout_duration = 0
1343 cur_account_lockout_duration = abs(int(res[0]["lockoutDuration"][0])) / (1e7 * 60)
1344 cur_reset_account_lockout_after = abs(int(res[0]["lockOutObservationWindow"][0])) / (1e7 * 60)
1345 except Exception as e:
1346 raise CommandError("Could not retrieve password properties!", e)
1348 if subcommand == "show":
1349 self.message("Password informations for domain '%s'" % domain_dn)
1351 if pwd_props & DOMAIN_PASSWORD_COMPLEX != 0:
1352 self.message("Password complexity: on")
1354 self.message("Password complexity: off")
1355 if pwd_props & DOMAIN_PASSWORD_STORE_CLEARTEXT != 0:
1356 self.message("Store plaintext passwords: on")
1358 self.message("Store plaintext passwords: off")
1359 self.message("Password history length: %d" % pwd_hist_len)
1360 self.message("Minimum password length: %d" % cur_min_pwd_len)
1361 self.message("Minimum password age (days): %d" % cur_min_pwd_age)
1362 self.message("Maximum password age (days): %d" % cur_max_pwd_age)
1363 self.message("Account lockout duration (mins): %d" % cur_account_lockout_duration)
1364 self.message("Account lockout threshold (attempts): %d" % cur_account_lockout_threshold)
1365 self.message("Reset account lockout after (mins): %d" % cur_reset_account_lockout_after)
1366 elif subcommand == "set":
1369 m.dn = ldb.Dn(samdb, domain_dn)
1371 if complexity is not None:
1372 if complexity == "on" or complexity == "default":
1373 pwd_props = pwd_props | DOMAIN_PASSWORD_COMPLEX
1374 msgs.append("Password complexity activated!")
1375 elif complexity == "off":
1376 pwd_props = pwd_props & (~DOMAIN_PASSWORD_COMPLEX)
1377 msgs.append("Password complexity deactivated!")
1379 if store_plaintext is not None:
1380 if store_plaintext == "on" or store_plaintext == "default":
1381 pwd_props = pwd_props | DOMAIN_PASSWORD_STORE_CLEARTEXT
1382 msgs.append("Plaintext password storage for changed passwords activated!")
1383 elif store_plaintext == "off":
1384 pwd_props = pwd_props & (~DOMAIN_PASSWORD_STORE_CLEARTEXT)
1385 msgs.append("Plaintext password storage for changed passwords deactivated!")
1387 if complexity is not None or store_plaintext is not None:
1388 m["pwdProperties"] = ldb.MessageElement(str(pwd_props),
1389 ldb.FLAG_MOD_REPLACE, "pwdProperties")
1391 if history_length is not None:
1392 if history_length == "default":
1395 pwd_hist_len = int(history_length)
1397 if pwd_hist_len < 0 or pwd_hist_len > 24:
1398 raise CommandError("Password history length must be in the range of 0 to 24!")
1400 m["pwdHistoryLength"] = ldb.MessageElement(str(pwd_hist_len),
1401 ldb.FLAG_MOD_REPLACE, "pwdHistoryLength")
1402 msgs.append("Password history length changed!")
1404 if min_pwd_length is not None:
1405 if min_pwd_length == "default":
1408 min_pwd_len = int(min_pwd_length)
1410 if min_pwd_len < 0 or min_pwd_len > 14:
1411 raise CommandError("Minimum password length must be in the range of 0 to 14!")
1413 m["minPwdLength"] = ldb.MessageElement(str(min_pwd_len),
1414 ldb.FLAG_MOD_REPLACE, "minPwdLength")
1415 msgs.append("Minimum password length changed!")
1417 if min_pwd_age is not None:
1418 if min_pwd_age == "default":
1421 min_pwd_age = int(min_pwd_age)
1423 if min_pwd_age < 0 or min_pwd_age > 998:
1424 raise CommandError("Minimum password age must be in the range of 0 to 998!")
1427 min_pwd_age_ticks = -int(min_pwd_age * (24 * 60 * 60 * 1e7))
1429 m["minPwdAge"] = ldb.MessageElement(str(min_pwd_age_ticks),
1430 ldb.FLAG_MOD_REPLACE, "minPwdAge")
1431 msgs.append("Minimum password age changed!")
1433 if max_pwd_age is not None:
1434 if max_pwd_age == "default":
1437 max_pwd_age = int(max_pwd_age)
1439 if max_pwd_age < 0 or max_pwd_age > 999:
1440 raise CommandError("Maximum password age must be in the range of 0 to 999!")
1443 if max_pwd_age == 0:
1444 max_pwd_age_ticks = -0x8000000000000000
1446 max_pwd_age_ticks = -int(max_pwd_age * (24 * 60 * 60 * 1e7))
1448 m["maxPwdAge"] = ldb.MessageElement(str(max_pwd_age_ticks),
1449 ldb.FLAG_MOD_REPLACE, "maxPwdAge")
1450 msgs.append("Maximum password age changed!")
1452 if account_lockout_duration is not None:
1453 if account_lockout_duration == "default":
1454 account_lockout_duration = 30
1456 account_lockout_duration = int(account_lockout_duration)
1458 if account_lockout_duration < 0 or account_lockout_duration > 99999:
1459 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1462 if account_lockout_duration == 0:
1463 account_lockout_duration_ticks = -0x8000000000000000
1465 account_lockout_duration_ticks = -int(account_lockout_duration * (60 * 1e7))
1467 m["lockoutDuration"] = ldb.MessageElement(str(account_lockout_duration_ticks),
1468 ldb.FLAG_MOD_REPLACE, "lockoutDuration")
1469 msgs.append("Account lockout duration changed!")
1471 if account_lockout_threshold is not None:
1472 if account_lockout_threshold == "default":
1473 account_lockout_threshold = 0
1475 account_lockout_threshold = int(account_lockout_threshold)
1477 m["lockoutThreshold"] = ldb.MessageElement(str(account_lockout_threshold),
1478 ldb.FLAG_MOD_REPLACE, "lockoutThreshold")
1479 msgs.append("Account lockout threshold changed!")
1481 if reset_account_lockout_after is not None:
1482 if reset_account_lockout_after == "default":
1483 reset_account_lockout_after = 30
1485 reset_account_lockout_after = int(reset_account_lockout_after)
1487 if reset_account_lockout_after < 0 or reset_account_lockout_after > 99999:
1488 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1491 if reset_account_lockout_after == 0:
1492 reset_account_lockout_after_ticks = -0x8000000000000000
1494 reset_account_lockout_after_ticks = -int(reset_account_lockout_after * (60 * 1e7))
1496 m["lockOutObservationWindow"] = ldb.MessageElement(str(reset_account_lockout_after_ticks),
1497 ldb.FLAG_MOD_REPLACE, "lockOutObservationWindow")
1498 msgs.append("Duration to reset account lockout after changed!")
1500 if max_pwd_age > 0 and min_pwd_age >= max_pwd_age:
1501 raise CommandError("Maximum password age (%d) must be greater than minimum password age (%d)!" % (max_pwd_age, min_pwd_age))
1504 raise CommandError("You must specify at least one option to set. Try --help")
1506 msgs.append("All changes applied successfully!")
1507 self.message("\n".join(msgs))
1509 raise CommandError("Wrong argument '%s'!" % subcommand)
1512 class cmd_domain_classicupgrade(Command):
1513 """Upgrade from Samba classic (NT4-like) database to Samba AD DC database.
1515 Specify either a directory with all Samba classic DC databases and state files (with --dbdir) or
1516 the testparm utility from your classic installation (with --testparm).
1519 synopsis = "%prog [options] <classic_smb_conf>"
1521 takes_optiongroups = {
1522 "sambaopts": options.SambaOptions,
1523 "versionopts": options.VersionOptions
1527 Option("--dbdir", type="string", metavar="DIR",
1528 help="Path to samba classic DC database directory"),
1529 Option("--testparm", type="string", metavar="PATH",
1530 help="Path to samba classic DC testparm utility from the previous installation. This allows the default paths of the previous installation to be followed"),
1531 Option("--targetdir", type="string", metavar="DIR",
1532 help="Path prefix where the new Samba 4.0 AD domain should be initialised"),
1533 Option("--quiet", help="Be quiet", action="store_true"),
1534 Option("--verbose", help="Be verbose", action="store_true"),
1535 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
1536 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
1537 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
1538 "BIND9_FLATFILE uses bind9 text database to store zone information, "
1539 "BIND9_DLZ uses samba4 AD to store zone information, "
1540 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
1541 default="SAMBA_INTERNAL")
1545 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
1546 action="store_true"),
1547 Option("--use-xattrs", type="choice", choices=["yes","no","auto"],
1548 metavar="[yes|no|auto]",
1549 help="Define if we should use the native fs capabilities or a tdb file for "
1550 "storing attributes likes ntacl when --use-ntvfs is set. "
1551 "auto tries to make an inteligent guess based on the user rights and system capabilities",
1554 if samba.is_ntvfs_fileserver_built():
1555 takes_options.extend(ntvfs_options)
1557 takes_args = ["smbconf"]
1559 def run(self, smbconf=None, targetdir=None, dbdir=None, testparm=None,
1560 quiet=False, verbose=False, use_xattrs="auto", sambaopts=None, versionopts=None,
1561 dns_backend=None, use_ntvfs=False):
1563 if not os.path.exists(smbconf):
1564 raise CommandError("File %s does not exist" % smbconf)
1566 if testparm and not os.path.exists(testparm):
1567 raise CommandError("Testparm utility %s does not exist" % testparm)
1569 if dbdir and not os.path.exists(dbdir):
1570 raise CommandError("Directory %s does not exist" % dbdir)
1572 if not dbdir and not testparm:
1573 raise CommandError("Please specify either dbdir or testparm")
1575 logger = self.get_logger()
1577 logger.setLevel(logging.DEBUG)
1579 logger.setLevel(logging.WARNING)
1581 logger.setLevel(logging.INFO)
1583 if dbdir and testparm:
1584 logger.warning("both dbdir and testparm specified, ignoring dbdir.")
1587 lp = sambaopts.get_loadparm()
1589 s3conf = s3param.get_context()
1592 s3conf.set("realm", sambaopts.realm)
1594 if targetdir is not None:
1595 if not os.path.isdir(targetdir):
1599 if use_xattrs == "yes":
1601 elif use_xattrs == "auto" and use_ntvfs == False:
1603 elif use_ntvfs == False:
1604 raise CommandError("--use-xattrs=no requires --use-ntvfs (not supported for production use). "
1605 "Please re-run with --use-xattrs omitted.")
1606 elif use_xattrs == "auto" and not s3conf.get("posix:eadb"):
1608 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
1610 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
1613 samba.ntacls.setntacl(lp, tmpfile.name,
1614 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
1617 # FIXME: Don't catch all exceptions here
1618 logger.info("You are not root or your system does not support xattr, using tdb backend for attributes. "
1619 "If you intend to use this provision in production, rerun the script as root on a system supporting xattrs.")
1623 # Set correct default values from dbdir or testparm
1626 paths["state directory"] = dbdir
1627 paths["private dir"] = dbdir
1628 paths["lock directory"] = dbdir
1629 paths["smb passwd file"] = dbdir + "/smbpasswd"
1631 paths["state directory"] = get_testparm_var(testparm, smbconf, "state directory")
1632 paths["private dir"] = get_testparm_var(testparm, smbconf, "private dir")
1633 paths["smb passwd file"] = get_testparm_var(testparm, smbconf, "smb passwd file")
1634 paths["lock directory"] = get_testparm_var(testparm, smbconf, "lock directory")
1635 # "testparm" from Samba 3 < 3.4.x is not aware of the parameter
1636 # "state directory", instead make use of "lock directory"
1637 if len(paths["state directory"]) == 0:
1638 paths["state directory"] = paths["lock directory"]
1641 s3conf.set(p, paths[p])
1643 # load smb.conf parameters
1644 logger.info("Reading smb.conf")
1645 s3conf.load(smbconf)
1646 samba3 = Samba3(smbconf, s3conf)
1648 logger.info("Provisioning")
1649 upgrade_from_samba3(samba3, logger, targetdir, session_info=system_session(),
1650 useeadb=eadb, dns_backend=dns_backend, use_ntvfs=use_ntvfs)
1653 class cmd_domain_samba3upgrade(cmd_domain_classicupgrade):
1654 __doc__ = cmd_domain_classicupgrade.__doc__
1656 # This command is present for backwards compatibility only,
1657 # and should not be shown.
1661 class LocalDCCredentialsOptions(options.CredentialsOptions):
1662 def __init__(self, parser):
1663 options.CredentialsOptions.__init__(self, parser, special_name="local-dc")
1665 class DomainTrustCommand(Command):
1666 """List domain trusts."""
1669 Command.__init__(self)
1670 self.local_lp = None
1672 self.local_server = None
1673 self.local_binding_string = None
1674 self.local_creds = None
1676 self.remote_server = None
1677 self.remote_binding_string = None
1678 self.remote_creds = None
1680 def _uint32(self, v):
1681 return ctypes.c_uint32(v).value
1683 def check_runtime_error(self, runtime, val):
1687 err32 = self._uint32(runtime[0])
1693 class LocalRuntimeError(CommandError):
1694 def __init__(exception_self, self, runtime, message):
1695 err32 = self._uint32(runtime[0])
1697 msg = "LOCAL_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1698 self.local_server, message, err32, errstr)
1699 CommandError.__init__(exception_self, msg)
1701 class RemoteRuntimeError(CommandError):
1702 def __init__(exception_self, self, runtime, message):
1703 err32 = self._uint32(runtime[0])
1705 msg = "REMOTE_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1706 self.remote_server, message, err32, errstr)
1707 CommandError.__init__(exception_self, msg)
1709 class LocalLdbError(CommandError):
1710 def __init__(exception_self, self, ldb_error, message):
1711 errval = ldb_error[0]
1712 errstr = ldb_error[1]
1713 msg = "LOCAL_DC[%s]: %s - ERROR(%d) - %s" % (
1714 self.local_server, message, errval, errstr)
1715 CommandError.__init__(exception_self, msg)
1717 def setup_local_server(self, sambaopts, localdcopts):
1718 if self.local_server is not None:
1719 return self.local_server
1721 lp = sambaopts.get_loadparm()
1723 local_server = localdcopts.ipaddress
1724 if local_server is None:
1725 server_role = lp.server_role()
1726 if server_role != "ROLE_ACTIVE_DIRECTORY_DC":
1727 raise CommandError("Invalid server_role %s" % (server_role))
1728 local_server = lp.get('netbios name')
1729 local_transport = "ncalrpc"
1730 local_binding_options = ""
1731 local_binding_options += ",auth_type=ncalrpc_as_system"
1732 local_ldap_url = None
1735 local_transport = "ncacn_np"
1736 local_binding_options = ""
1737 local_ldap_url = "ldap://%s" % local_server
1738 local_creds = localdcopts.get_credentials(lp)
1742 self.local_server = local_server
1743 self.local_binding_string = "%s:%s[%s]" % (local_transport, local_server, local_binding_options)
1744 self.local_ldap_url = local_ldap_url
1745 self.local_creds = local_creds
1746 return self.local_server
1748 def new_local_lsa_connection(self):
1749 return lsa.lsarpc(self.local_binding_string, self.local_lp, self.local_creds)
1751 def new_local_netlogon_connection(self):
1752 return netlogon.netlogon(self.local_binding_string, self.local_lp, self.local_creds)
1754 def new_local_ldap_connection(self):
1755 return SamDB(url=self.local_ldap_url,
1756 session_info=system_session(),
1757 credentials=self.local_creds,
1760 def setup_remote_server(self, credopts, domain,
1762 require_writable=True):
1765 assert require_writable
1767 if self.remote_server is not None:
1768 return self.remote_server
1770 self.remote_server = "__unknown__remote_server__.%s" % domain
1771 assert self.local_server is not None
1773 remote_creds = credopts.get_credentials(self.local_lp)
1774 remote_server = credopts.ipaddress
1775 remote_binding_options = ""
1777 # TODO: we should also support NT4 domains
1778 # we could use local_netlogon.netr_DsRGetDCNameEx2() with the remote domain name
1779 # and delegate NBT or CLDAP to the local netlogon server
1781 remote_net = Net(remote_creds, self.local_lp, server=remote_server)
1782 remote_flags = nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS
1783 if require_writable:
1784 remote_flags |= nbt.NBT_SERVER_WRITABLE
1786 remote_flags |= nbt.NBT_SERVER_PDC
1787 remote_info = remote_net.finddc(flags=remote_flags, domain=domain, address=remote_server)
1788 except NTSTATUSError as error:
1789 raise CommandError("Failed to find a writeable DC for domain '%s': %s" %
1792 raise CommandError("Failed to find a writeable DC for domain '%s'" % domain)
1794 nbt.NBT_SERVER_PDC: "PDC",
1795 nbt.NBT_SERVER_GC: "GC",
1796 nbt.NBT_SERVER_LDAP: "LDAP",
1797 nbt.NBT_SERVER_DS: "DS",
1798 nbt.NBT_SERVER_KDC: "KDC",
1799 nbt.NBT_SERVER_TIMESERV: "TIMESERV",
1800 nbt.NBT_SERVER_CLOSEST: "CLOSEST",
1801 nbt.NBT_SERVER_WRITABLE: "WRITABLE",
1802 nbt.NBT_SERVER_GOOD_TIMESERV: "GOOD_TIMESERV",
1803 nbt.NBT_SERVER_NDNC: "NDNC",
1804 nbt.NBT_SERVER_SELECT_SECRET_DOMAIN_6: "SELECT_SECRET_DOMAIN_6",
1805 nbt.NBT_SERVER_FULL_SECRET_DOMAIN_6: "FULL_SECRET_DOMAIN_6",
1806 nbt.NBT_SERVER_ADS_WEB_SERVICE: "ADS_WEB_SERVICE",
1807 nbt.NBT_SERVER_DS_8: "DS_8",
1808 nbt.NBT_SERVER_HAS_DNS_NAME: "HAS_DNS_NAME",
1809 nbt.NBT_SERVER_IS_DEFAULT_NC: "IS_DEFAULT_NC",
1810 nbt.NBT_SERVER_FOREST_ROOT: "FOREST_ROOT",
1812 server_type_string = self.generic_bitmap_to_string(flag_map,
1813 remote_info.server_type, names_only=True)
1814 self.outf.write("RemoteDC Netbios[%s] DNS[%s] ServerType[%s]\n" % (
1815 remote_info.pdc_name,
1816 remote_info.pdc_dns_name,
1817 server_type_string))
1819 self.remote_server = remote_info.pdc_dns_name
1820 self.remote_binding_string="ncacn_np:%s[%s]" % (self.remote_server, remote_binding_options)
1821 self.remote_creds = remote_creds
1822 return self.remote_server
1824 def new_remote_lsa_connection(self):
1825 return lsa.lsarpc(self.remote_binding_string, self.local_lp, self.remote_creds)
1827 def new_remote_netlogon_connection(self):
1828 return netlogon.netlogon(self.remote_binding_string, self.local_lp, self.remote_creds)
1830 def get_lsa_info(self, conn, policy_access):
1831 objectAttr = lsa.ObjectAttribute()
1832 objectAttr.sec_qos = lsa.QosInfo()
1834 policy = conn.OpenPolicy2(''.decode('utf-8'),
1835 objectAttr, policy_access)
1837 info = conn.QueryInfoPolicy2(policy, lsa.LSA_POLICY_INFO_DNS)
1839 return (policy, info)
1841 def get_netlogon_dc_info(self, conn, server):
1842 info = conn.netr_DsRGetDCNameEx2(server,
1843 None, 0, None, None, None,
1844 netlogon.DS_RETURN_DNS_NAME)
1847 def netr_DomainTrust_to_name(self, t):
1848 if t.trust_type == lsa.LSA_TRUST_TYPE_DOWNLEVEL:
1849 return t.netbios_name
1853 def netr_DomainTrust_to_type(self, a, t):
1855 primary_parent = None
1857 if _t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
1859 if not _t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1860 primary_parent = a[_t.parent_index]
1863 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1864 if t is primary_parent:
1867 if t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1870 parent = a[t.parent_index]
1871 if parent is primary:
1876 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1881 def netr_DomainTrust_to_transitive(self, t):
1882 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1885 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE:
1888 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1893 def netr_DomainTrust_to_direction(self, t):
1894 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND and \
1895 t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1898 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND:
1901 if t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1906 def generic_enum_to_string(self, e_dict, v, names_only=False):
1910 v32 = self._uint32(v)
1911 w = "__unknown__%08X__" % v32
1913 r = "0x%x (%s)" % (v, w)
1916 def generic_bitmap_to_string(self, b_dict, v, names_only=False):
1921 for b in sorted(b_dict.keys()):
1928 c32 = self._uint32(c)
1929 s += ["__unknown_%08X__" % c32]
1934 r = "0x%x (%s)" % (v, w)
1937 def trustType_string(self, v):
1939 lsa.LSA_TRUST_TYPE_DOWNLEVEL : "DOWNLEVEL",
1940 lsa.LSA_TRUST_TYPE_UPLEVEL : "UPLEVEL",
1941 lsa.LSA_TRUST_TYPE_MIT : "MIT",
1942 lsa.LSA_TRUST_TYPE_DCE : "DCE",
1944 return self.generic_enum_to_string(types, v)
1946 def trustDirection_string(self, v):
1948 lsa.LSA_TRUST_DIRECTION_INBOUND |
1949 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "BOTH",
1950 lsa.LSA_TRUST_DIRECTION_INBOUND : "INBOUND",
1951 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "OUTBOUND",
1953 return self.generic_enum_to_string(directions, v)
1955 def trustAttributes_string(self, v):
1957 lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE : "NON_TRANSITIVE",
1958 lsa.LSA_TRUST_ATTRIBUTE_UPLEVEL_ONLY : "UPLEVEL_ONLY",
1959 lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN : "QUARANTINED_DOMAIN",
1960 lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE : "FOREST_TRANSITIVE",
1961 lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION : "CROSS_ORGANIZATION",
1962 lsa.LSA_TRUST_ATTRIBUTE_WITHIN_FOREST : "WITHIN_FOREST",
1963 lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL : "TREAT_AS_EXTERNAL",
1964 lsa.LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION : "USES_RC4_ENCRYPTION",
1966 return self.generic_bitmap_to_string(attributes, v)
1968 def kerb_EncTypes_string(self, v):
1970 security.KERB_ENCTYPE_DES_CBC_CRC : "DES_CBC_CRC",
1971 security.KERB_ENCTYPE_DES_CBC_MD5 : "DES_CBC_MD5",
1972 security.KERB_ENCTYPE_RC4_HMAC_MD5 : "RC4_HMAC_MD5",
1973 security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96 : "AES128_CTS_HMAC_SHA1_96",
1974 security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96 : "AES256_CTS_HMAC_SHA1_96",
1975 security.KERB_ENCTYPE_FAST_SUPPORTED : "FAST_SUPPORTED",
1976 security.KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED : "COMPOUND_IDENTITY_SUPPORTED",
1977 security.KERB_ENCTYPE_CLAIMS_SUPPORTED : "CLAIMS_SUPPORTED",
1978 security.KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED : "RESOURCE_SID_COMPRESSION_DISABLED",
1980 return self.generic_bitmap_to_string(enctypes, v)
1982 def entry_tln_status(self, e_flags, ):
1984 return "Status[Enabled]"
1987 lsa.LSA_TLN_DISABLED_NEW : "Disabled-New",
1988 lsa.LSA_TLN_DISABLED_ADMIN : "Disabled",
1989 lsa.LSA_TLN_DISABLED_CONFLICT : "Disabled-Conflicting",
1991 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
1993 def entry_dom_status(self, e_flags):
1995 return "Status[Enabled]"
1998 lsa.LSA_SID_DISABLED_ADMIN : "Disabled-SID",
1999 lsa.LSA_SID_DISABLED_CONFLICT : "Disabled-SID-Conflicting",
2000 lsa.LSA_NB_DISABLED_ADMIN : "Disabled-NB",
2001 lsa.LSA_NB_DISABLED_CONFLICT : "Disabled-NB-Conflicting",
2003 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
2005 def write_forest_trust_info(self, fti, tln=None, collisions=None):
2007 tln_string = " TDO[%s]" % tln
2011 self.outf.write("Namespaces[%d]%s:\n" % (
2012 len(fti.entries), tln_string))
2014 for i in xrange(0, len(fti.entries)):
2018 collision_string = ""
2020 if collisions is not None:
2021 for c in collisions.entries:
2025 collision_string = " Collision[%s]" % (c.name.string)
2027 d = e.forest_trust_data
2028 if e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
2029 self.outf.write("TLN: %-32s DNS[*.%s]%s\n" % (
2030 self.entry_tln_status(flags),
2031 d.string, collision_string))
2032 elif e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
2033 self.outf.write("TLN_EX: %-29s DNS[*.%s]\n" % (
2035 elif e.type == lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
2036 self.outf.write("DOM: %-32s DNS[%s] Netbios[%s] SID[%s]%s\n" % (
2037 self.entry_dom_status(flags),
2038 d.dns_domain_name.string,
2039 d.netbios_domain_name.string,
2040 d.domain_sid, collision_string))
2043 class cmd_domain_trust_list(DomainTrustCommand):
2044 """List domain trusts."""
2046 synopsis = "%prog [options]"
2048 takes_optiongroups = {
2049 "sambaopts": options.SambaOptions,
2050 "versionopts": options.VersionOptions,
2051 "localdcopts": LocalDCCredentialsOptions,
2057 def run(self, sambaopts=None, versionopts=None, localdcopts=None):
2059 local_server = self.setup_local_server(sambaopts, localdcopts)
2061 local_netlogon = self.new_local_netlogon_connection()
2062 except RuntimeError as error:
2063 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2066 local_netlogon_trusts = local_netlogon.netr_DsrEnumerateDomainTrusts(local_server,
2067 netlogon.NETR_TRUST_FLAG_IN_FOREST |
2068 netlogon.NETR_TRUST_FLAG_OUTBOUND |
2069 netlogon.NETR_TRUST_FLAG_INBOUND)
2070 except RuntimeError as error:
2071 if self.check_runtime_error(error, werror.WERR_RPC_S_PROCNUM_OUT_OF_RANGE):
2072 # TODO: we could implement a fallback to lsa.EnumTrustDom()
2073 raise CommandError("LOCAL_DC[%s]: netr_DsrEnumerateDomainTrusts not supported." % (
2075 raise self.LocalRuntimeError(self, error, "netr_DsrEnumerateDomainTrusts failed")
2077 a = local_netlogon_trusts.array
2079 if t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
2081 self.outf.write("%-14s %-15s %-19s %s\n" % (
2082 "Type[%s]" % self.netr_DomainTrust_to_type(a, t),
2083 "Transitive[%s]" % self.netr_DomainTrust_to_transitive(t),
2084 "Direction[%s]" % self.netr_DomainTrust_to_direction(t),
2085 "Name[%s]" % self.netr_DomainTrust_to_name(t)))
2088 class cmd_domain_trust_show(DomainTrustCommand):
2089 """Show trusted domain details."""
2091 synopsis = "%prog NAME [options]"
2093 takes_optiongroups = {
2094 "sambaopts": options.SambaOptions,
2095 "versionopts": options.VersionOptions,
2096 "localdcopts": LocalDCCredentialsOptions,
2102 takes_args = ["domain"]
2104 def run(self, domain, sambaopts=None, versionopts=None, localdcopts=None):
2106 local_server = self.setup_local_server(sambaopts, localdcopts)
2108 local_lsa = self.new_local_lsa_connection()
2109 except RuntimeError as error:
2110 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2113 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2114 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2115 except RuntimeError as error:
2116 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2118 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2119 local_lsa_info.name.string,
2120 local_lsa_info.dns_domain.string,
2121 local_lsa_info.sid))
2123 lsaString = lsa.String()
2124 lsaString.string = domain
2126 local_tdo_full = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2127 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2128 local_tdo_info = local_tdo_full.info_ex
2129 local_tdo_posix = local_tdo_full.posix_offset
2130 except NTSTATUSError as error:
2131 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2132 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2134 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(FULL_INFO) failed")
2137 local_tdo_enctypes = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2138 lsaString, lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES)
2139 except NTSTATUSError as error:
2140 if self.check_runtime_error(error, ntstatus.NT_STATUS_INVALID_PARAMETER):
2142 if self.check_runtime_error(error, ntstatus.NT_STATUS_INVALID_INFO_CLASS):
2145 if error is not None:
2146 raise self.LocalRuntimeError(self, error,
2147 "QueryTrustedDomainInfoByName(SUPPORTED_ENCRYPTION_TYPES) failed")
2149 local_tdo_enctypes = lsa.TrustDomainInfoSupportedEncTypes()
2150 local_tdo_enctypes.enc_types = 0
2153 local_tdo_forest = None
2154 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2155 local_tdo_forest = local_lsa.lsaRQueryForestTrustInformation(local_policy,
2156 lsaString, lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
2157 except RuntimeError as error:
2158 if self.check_runtime_error(error, ntstatus.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
2160 if self.check_runtime_error(error, ntstatus.NT_STATUS_NOT_FOUND):
2162 if error is not None:
2163 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation failed")
2165 local_tdo_forest = lsa.ForestTrustInformation()
2166 local_tdo_forest.count = 0
2167 local_tdo_forest.entries = []
2169 self.outf.write("TrustedDomain:\n\n");
2170 self.outf.write("NetbiosName: %s\n" % local_tdo_info.netbios_name.string)
2171 if local_tdo_info.netbios_name.string != local_tdo_info.domain_name.string:
2172 self.outf.write("DnsName: %s\n" % local_tdo_info.domain_name.string)
2173 self.outf.write("SID: %s\n" % local_tdo_info.sid)
2174 self.outf.write("Type: %s\n" % self.trustType_string(local_tdo_info.trust_type))
2175 self.outf.write("Direction: %s\n" % self.trustDirection_string(local_tdo_info.trust_direction))
2176 self.outf.write("Attributes: %s\n" % self.trustAttributes_string(local_tdo_info.trust_attributes))
2177 posix_offset_u32 = ctypes.c_uint32(local_tdo_posix.posix_offset).value
2178 posix_offset_i32 = ctypes.c_int32(local_tdo_posix.posix_offset).value
2179 self.outf.write("PosixOffset: 0x%08X (%d)\n" % (posix_offset_u32, posix_offset_i32))
2180 self.outf.write("kerb_EncTypes: %s\n" % self.kerb_EncTypes_string(local_tdo_enctypes.enc_types))
2182 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2183 self.write_forest_trust_info(local_tdo_forest,
2184 tln=local_tdo_info.domain_name.string)
2188 class cmd_domain_trust_create(DomainTrustCommand):
2189 """Create a domain or forest trust."""
2191 synopsis = "%prog DOMAIN [options]"
2193 takes_optiongroups = {
2194 "sambaopts": options.SambaOptions,
2195 "versionopts": options.VersionOptions,
2196 "credopts": options.CredentialsOptions,
2197 "localdcopts": LocalDCCredentialsOptions,
2201 Option("--type", type="choice", metavar="TYPE",
2202 choices=["external", "forest"],
2203 help="The type of the trust: 'external' or 'forest'.",
2205 default="external"),
2206 Option("--direction", type="choice", metavar="DIRECTION",
2207 choices=["incoming", "outgoing", "both"],
2208 help="The trust direction: 'incoming', 'outgoing' or 'both'.",
2209 dest='trust_direction',
2211 Option("--create-location", type="choice", metavar="LOCATION",
2212 choices=["local", "both"],
2213 help="Where to create the trusted domain object: 'local' or 'both'.",
2214 dest='create_location',
2216 Option("--cross-organisation", action="store_true",
2217 help="The related domains does not belong to the same organisation.",
2218 dest='cross_organisation',
2220 Option("--quarantined", type="choice", metavar="yes|no",
2221 choices=["yes", "no", None],
2222 help="Special SID filtering rules are applied to the trust. "
2223 "With --type=external the default is yes. "
2224 "With --type=forest the default is no.",
2225 dest='quarantined_arg',
2227 Option("--not-transitive", action="store_true",
2228 help="The forest trust is not transitive.",
2229 dest='not_transitive',
2231 Option("--treat-as-external", action="store_true",
2232 help="The treat the forest trust as external.",
2233 dest='treat_as_external',
2235 Option("--no-aes-keys", action="store_false",
2236 help="The trust uses aes kerberos keys.",
2237 dest='use_aes_keys',
2239 Option("--skip-validation", action="store_false",
2240 help="Skip validation of the trust.",
2245 takes_args = ["domain"]
2247 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2248 trust_type=None, trust_direction=None, create_location=None,
2249 cross_organisation=False, quarantined_arg=None,
2250 not_transitive=False, treat_as_external=False,
2251 use_aes_keys=False, validate=True):
2253 lsaString = lsa.String()
2256 if quarantined_arg is None:
2257 if trust_type == 'external':
2259 elif quarantined_arg == 'yes':
2262 if trust_type != 'forest':
2264 raise CommandError("--not-transitive requires --type=forest")
2265 if treat_as_external:
2266 raise CommandError("--treat-as-external requires --type=forest")
2270 enc_types = lsa.TrustDomainInfoSupportedEncTypes()
2271 enc_types.enc_types = security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96
2272 enc_types.enc_types |= security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96
2274 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2275 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2276 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2278 local_trust_info = lsa.TrustDomainInfoInfoEx()
2279 local_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2280 local_trust_info.trust_direction = 0
2281 if trust_direction == "both":
2282 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2283 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2284 elif trust_direction == "incoming":
2285 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2286 elif trust_direction == "outgoing":
2287 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2288 local_trust_info.trust_attributes = 0
2289 if cross_organisation:
2290 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2292 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2293 if trust_type == "forest":
2294 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2296 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2297 if treat_as_external:
2298 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2300 def get_password(name):
2303 if password is not None and password is not '':
2305 password = getpass("New %s Password: " % name)
2306 passwordverify = getpass("Retype %s Password: " % name)
2307 if not password == passwordverify:
2309 self.outf.write("Sorry, passwords do not match.\n")
2311 incoming_secret = None
2312 outgoing_secret = None
2313 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2314 if create_location == "local":
2315 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2316 incoming_password = get_password("Incoming Trust")
2317 incoming_secret = string_to_byte_array(incoming_password.encode('utf-16-le'))
2318 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2319 outgoing_password = get_password("Outgoing Trust")
2320 outgoing_secret = string_to_byte_array(outgoing_password.encode('utf-16-le'))
2322 remote_trust_info = None
2324 # We use 240 random bytes.
2325 # Windows uses 28 or 240 random bytes. I guess it's
2326 # based on the trust type external vs. forest.
2328 # The initial trust password can be up to 512 bytes
2329 # while the versioned passwords used for periodic updates
2330 # can only be up to 498 bytes, as netr_ServerPasswordSet2()
2331 # needs to pass the NL_PASSWORD_VERSION structure within the
2332 # 512 bytes and a 2 bytes confounder is required.
2334 def random_trust_secret(length):
2335 pw = samba.generate_random_machine_password(length//2, length//2)
2336 return string_to_byte_array(pw.encode('utf-16-le'))
2338 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2339 incoming_secret = random_trust_secret(240)
2340 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2341 outgoing_secret = random_trust_secret(240)
2343 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2344 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2346 remote_trust_info = lsa.TrustDomainInfoInfoEx()
2347 remote_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2348 remote_trust_info.trust_direction = 0
2349 if trust_direction == "both":
2350 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2351 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2352 elif trust_direction == "incoming":
2353 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2354 elif trust_direction == "outgoing":
2355 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2356 remote_trust_info.trust_attributes = 0
2357 if cross_organisation:
2358 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2360 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2361 if trust_type == "forest":
2362 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2364 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2365 if treat_as_external:
2366 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2368 local_server = self.setup_local_server(sambaopts, localdcopts)
2370 local_lsa = self.new_local_lsa_connection()
2371 except RuntimeError as error:
2372 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2375 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2376 except RuntimeError as error:
2377 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2379 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2380 local_lsa_info.name.string,
2381 local_lsa_info.dns_domain.string,
2382 local_lsa_info.sid))
2385 remote_server = self.setup_remote_server(credopts, domain)
2386 except RuntimeError as error:
2387 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2390 remote_lsa = self.new_remote_lsa_connection()
2391 except RuntimeError as error:
2392 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2395 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2396 except RuntimeError as error:
2397 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2399 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2400 remote_lsa_info.name.string,
2401 remote_lsa_info.dns_domain.string,
2402 remote_lsa_info.sid))
2404 local_trust_info.domain_name.string = remote_lsa_info.dns_domain.string
2405 local_trust_info.netbios_name.string = remote_lsa_info.name.string
2406 local_trust_info.sid = remote_lsa_info.sid
2408 if remote_trust_info:
2409 remote_trust_info.domain_name.string = local_lsa_info.dns_domain.string
2410 remote_trust_info.netbios_name.string = local_lsa_info.name.string
2411 remote_trust_info.sid = local_lsa_info.sid
2414 lsaString.string = local_trust_info.domain_name.string
2415 local_old_netbios = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2416 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2417 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2418 except NTSTATUSError as error:
2419 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2420 raise self.LocalRuntimeError(self, error,
2421 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2425 lsaString.string = local_trust_info.netbios_name.string
2426 local_old_dns = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2427 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2428 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2429 except NTSTATUSError as error:
2430 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2431 raise self.LocalRuntimeError(self, error,
2432 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2435 if remote_trust_info:
2437 lsaString.string = remote_trust_info.domain_name.string
2438 remote_old_netbios = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2439 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2440 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2441 except NTSTATUSError as error:
2442 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2443 raise self.RemoteRuntimeError(self, error,
2444 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2448 lsaString.string = remote_trust_info.netbios_name.string
2449 remote_old_dns = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2450 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2451 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2452 except NTSTATUSError as error:
2453 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2454 raise self.RemoteRuntimeError(self, error,
2455 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2459 local_netlogon = self.new_local_netlogon_connection()
2460 except RuntimeError as error:
2461 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2464 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
2465 except RuntimeError as error:
2466 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
2468 if remote_trust_info:
2470 remote_netlogon = self.new_remote_netlogon_connection()
2471 except RuntimeError as error:
2472 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2475 remote_netlogon_info = self.get_netlogon_dc_info(remote_netlogon, remote_server)
2476 except RuntimeError as error:
2477 raise self.RemoteRuntimeError(self, error, "failed to get netlogon dc info")
2479 def generate_AuthInOutBlob(secret, update_time):
2481 blob = drsblobs.trustAuthInOutBlob()
2486 clear = drsblobs.AuthInfoClear()
2487 clear.size = len(secret)
2488 clear.password = secret
2490 info = drsblobs.AuthenticationInformation()
2491 info.LastUpdateTime = samba.unix2nttime(update_time)
2492 info.AuthType = lsa.TRUST_AUTH_TYPE_CLEAR
2493 info.AuthInfo = clear
2495 array = drsblobs.AuthenticationInformationArray()
2497 array.array = [info]
2499 blob = drsblobs.trustAuthInOutBlob()
2501 blob.current = array
2505 def generate_AuthInfoInternal(session_key, incoming=None, outgoing=None):
2506 confounder = [0] * 512
2507 for i in range(len(confounder)):
2508 confounder[i] = random.randint(0, 255)
2510 trustpass = drsblobs.trustDomainPasswords()
2512 trustpass.confounder = confounder
2513 trustpass.outgoing = outgoing
2514 trustpass.incoming = incoming
2516 trustpass_blob = ndr_pack(trustpass)
2518 encrypted_trustpass = arcfour_encrypt(session_key, trustpass_blob)
2520 auth_blob = lsa.DATA_BUF2()
2521 auth_blob.size = len(encrypted_trustpass)
2522 auth_blob.data = string_to_byte_array(encrypted_trustpass)
2524 auth_info = lsa.TrustDomainInfoAuthInfoInternal()
2525 auth_info.auth_blob = auth_blob
2529 update_time = samba.current_unix_time()
2530 incoming_blob = generate_AuthInOutBlob(incoming_secret, update_time)
2531 outgoing_blob = generate_AuthInOutBlob(outgoing_secret, update_time)
2533 local_tdo_handle = None
2534 remote_tdo_handle = None
2536 local_auth_info = generate_AuthInfoInternal(local_lsa.session_key,
2537 incoming=incoming_blob,
2538 outgoing=outgoing_blob)
2539 if remote_trust_info:
2540 remote_auth_info = generate_AuthInfoInternal(remote_lsa.session_key,
2541 incoming=outgoing_blob,
2542 outgoing=incoming_blob)
2545 if remote_trust_info:
2546 self.outf.write("Creating remote TDO.\n")
2547 current_request = { "location": "remote", "name": "CreateTrustedDomainEx2"}
2548 remote_tdo_handle = remote_lsa.CreateTrustedDomainEx2(remote_policy,
2551 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2552 self.outf.write("Remote TDO created.\n")
2554 self.outf.write("Setting supported encryption types on remote TDO.\n")
2555 current_request = { "location": "remote", "name": "SetInformationTrustedDomain"}
2556 remote_lsa.SetInformationTrustedDomain(remote_tdo_handle,
2557 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2560 self.outf.write("Creating local TDO.\n")
2561 current_request = { "location": "local", "name": "CreateTrustedDomainEx2"}
2562 local_tdo_handle = local_lsa.CreateTrustedDomainEx2(local_policy,
2565 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2566 self.outf.write("Local TDO created\n")
2568 self.outf.write("Setting supported encryption types on local TDO.\n")
2569 current_request = { "location": "local", "name": "SetInformationTrustedDomain"}
2570 local_lsa.SetInformationTrustedDomain(local_tdo_handle,
2571 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2573 except RuntimeError as error:
2574 self.outf.write("Error: %s failed %sly - cleaning up\n" % (
2575 current_request['name'], current_request['location']))
2576 if remote_tdo_handle:
2577 self.outf.write("Deleting remote TDO.\n")
2578 remote_lsa.DeleteObject(remote_tdo_handle)
2579 remote_tdo_handle = None
2580 if local_tdo_handle:
2581 self.outf.write("Deleting local TDO.\n")
2582 local_lsa.DeleteObject(local_tdo_handle)
2583 local_tdo_handle = None
2584 if current_request['location'] is "remote":
2585 raise self.RemoteRuntimeError(self, error, "%s" % (
2586 current_request['name']))
2587 raise self.LocalRuntimeError(self, error, "%s" % (
2588 current_request['name']))
2591 if local_trust_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2592 self.outf.write("Setup local forest trust information...\n")
2594 # get all information about the remote trust
2595 # this triggers netr_GetForestTrustInformation to the remote domain
2596 # and lsaRSetForestTrustInformation() locally, but new top level
2597 # names are disabled by default.
2598 local_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
2599 remote_lsa_info.dns_domain.string,
2600 netlogon.DS_GFTI_UPDATE_TDO)
2601 except RuntimeError as error:
2602 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2605 # here we try to enable all top level names
2606 local_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
2607 remote_lsa_info.dns_domain,
2608 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2611 except RuntimeError as error:
2612 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2614 self.write_forest_trust_info(local_forest_info,
2615 tln=remote_lsa_info.dns_domain.string,
2616 collisions=local_forest_collision)
2618 if remote_trust_info:
2619 self.outf.write("Setup remote forest trust information...\n")
2621 # get all information about the local trust (from the perspective of the remote domain)
2622 # this triggers netr_GetForestTrustInformation to our domain.
2623 # and lsaRSetForestTrustInformation() remotely, but new top level
2624 # names are disabled by default.
2625 remote_forest_info = remote_netlogon.netr_DsRGetForestTrustInformation(remote_netlogon_info.dc_unc,
2626 local_lsa_info.dns_domain.string,
2627 netlogon.DS_GFTI_UPDATE_TDO)
2628 except RuntimeError as error:
2629 raise self.RemoteRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2632 # here we try to enable all top level names
2633 remote_forest_collision = remote_lsa.lsaRSetForestTrustInformation(remote_policy,
2634 local_lsa_info.dns_domain,
2635 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2638 except RuntimeError as error:
2639 raise self.RemoteRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2641 self.write_forest_trust_info(remote_forest_info,
2642 tln=local_lsa_info.dns_domain.string,
2643 collisions=remote_forest_collision)
2645 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2646 self.outf.write("Validating outgoing trust...\n")
2648 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_netlogon_info.dc_unc,
2649 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2651 remote_lsa_info.dns_domain.string)
2652 except RuntimeError as error:
2653 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2655 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2656 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2658 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2659 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2660 local_trust_verify.trusted_dc_name,
2661 local_trust_verify.tc_connection_status[1],
2662 local_trust_verify.pdc_connection_status[1])
2664 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2665 local_trust_verify.trusted_dc_name,
2666 local_trust_verify.tc_connection_status[1],
2667 local_trust_verify.pdc_connection_status[1])
2669 if local_trust_status != werror.WERR_SUCCESS or local_conn_status != werror.WERR_SUCCESS:
2670 raise CommandError(local_validation)
2672 self.outf.write("OK: %s\n" % local_validation)
2674 if remote_trust_info:
2675 if remote_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2676 self.outf.write("Validating incoming trust...\n")
2678 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_netlogon_info.dc_unc,
2679 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2681 local_lsa_info.dns_domain.string)
2682 except RuntimeError as error:
2683 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2685 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2686 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2688 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2689 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2690 remote_trust_verify.trusted_dc_name,
2691 remote_trust_verify.tc_connection_status[1],
2692 remote_trust_verify.pdc_connection_status[1])
2694 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2695 remote_trust_verify.trusted_dc_name,
2696 remote_trust_verify.tc_connection_status[1],
2697 remote_trust_verify.pdc_connection_status[1])
2699 if remote_trust_status != werror.WERR_SUCCESS or remote_conn_status != werror.WERR_SUCCESS:
2700 raise CommandError(remote_validation)
2702 self.outf.write("OK: %s\n" % remote_validation)
2704 if remote_tdo_handle is not None:
2706 remote_lsa.Close(remote_tdo_handle)
2707 except RuntimeError as error:
2709 remote_tdo_handle = None
2710 if local_tdo_handle is not None:
2712 local_lsa.Close(local_tdo_handle)
2713 except RuntimeError as error:
2715 local_tdo_handle = None
2717 self.outf.write("Success.\n")
2720 class cmd_domain_trust_delete(DomainTrustCommand):
2721 """Delete a domain trust."""
2723 synopsis = "%prog DOMAIN [options]"
2725 takes_optiongroups = {
2726 "sambaopts": options.SambaOptions,
2727 "versionopts": options.VersionOptions,
2728 "credopts": options.CredentialsOptions,
2729 "localdcopts": LocalDCCredentialsOptions,
2733 Option("--delete-location", type="choice", metavar="LOCATION",
2734 choices=["local", "both"],
2735 help="Where to delete the trusted domain object: 'local' or 'both'.",
2736 dest='delete_location',
2740 takes_args = ["domain"]
2742 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2743 delete_location=None):
2745 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2746 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2747 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2749 if delete_location == "local":
2750 remote_policy_access = None
2752 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2753 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2754 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2756 local_server = self.setup_local_server(sambaopts, localdcopts)
2758 local_lsa = self.new_local_lsa_connection()
2759 except RuntimeError as error:
2760 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2763 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2764 except RuntimeError as error:
2765 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2767 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2768 local_lsa_info.name.string,
2769 local_lsa_info.dns_domain.string,
2770 local_lsa_info.sid))
2772 local_tdo_info = None
2773 local_tdo_handle = None
2774 remote_tdo_info = None
2775 remote_tdo_handle = None
2777 lsaString = lsa.String()
2779 lsaString.string = domain
2780 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2781 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2782 except NTSTATUSError as error:
2783 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2784 raise CommandError("Failed to find trust for domain '%s'" % domain)
2785 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2788 if remote_policy_access is not None:
2790 remote_server = self.setup_remote_server(credopts, domain)
2791 except RuntimeError as error:
2792 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2795 remote_lsa = self.new_remote_lsa_connection()
2796 except RuntimeError as error:
2797 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2800 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2801 except RuntimeError as error:
2802 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2804 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2805 remote_lsa_info.name.string,
2806 remote_lsa_info.dns_domain.string,
2807 remote_lsa_info.sid))
2809 if remote_lsa_info.sid != local_tdo_info.sid or \
2810 remote_lsa_info.name.string != local_tdo_info.netbios_name.string or \
2811 remote_lsa_info.dns_domain.string != local_tdo_info.domain_name.string:
2812 raise CommandError("LocalTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2813 local_tdo_info.netbios_name.string,
2814 local_tdo_info.domain_name.string,
2815 local_tdo_info.sid))
2818 lsaString.string = local_lsa_info.dns_domain.string
2819 remote_tdo_info = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2820 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2821 except NTSTATUSError as error:
2822 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2823 raise self.RemoteRuntimeError(self, error, "QueryTrustedDomainInfoByName(%s)" % (
2827 if remote_tdo_info is not None:
2828 if local_lsa_info.sid != remote_tdo_info.sid or \
2829 local_lsa_info.name.string != remote_tdo_info.netbios_name.string or \
2830 local_lsa_info.dns_domain.string != remote_tdo_info.domain_name.string:
2831 raise CommandError("RemoteTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2832 remote_tdo_info.netbios_name.string,
2833 remote_tdo_info.domain_name.string,
2834 remote_tdo_info.sid))
2836 if local_tdo_info is not None:
2838 lsaString.string = local_tdo_info.domain_name.string
2839 local_tdo_handle = local_lsa.OpenTrustedDomainByName(local_policy,
2841 security.SEC_STD_DELETE)
2842 except RuntimeError as error:
2843 raise self.LocalRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2846 local_lsa.DeleteObject(local_tdo_handle)
2847 local_tdo_handle = None
2849 if remote_tdo_info is not None:
2851 lsaString.string = remote_tdo_info.domain_name.string
2852 remote_tdo_handle = remote_lsa.OpenTrustedDomainByName(remote_policy,
2854 security.SEC_STD_DELETE)
2855 except RuntimeError as error:
2856 raise self.RemoteRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2859 if remote_tdo_handle is not None:
2861 remote_lsa.DeleteObject(remote_tdo_handle)
2862 remote_tdo_handle = None
2863 self.outf.write("RemoteTDO deleted.\n")
2864 except RuntimeError as error:
2865 self.outf.write("%s\n" % self.RemoteRuntimeError(self, error, "DeleteObject() failed"))
2867 if local_tdo_handle is not None:
2869 local_lsa.DeleteObject(local_tdo_handle)
2870 local_tdo_handle = None
2871 self.outf.write("LocalTDO deleted.\n")
2872 except RuntimeError as error:
2873 self.outf.write("%s\n" % self.LocalRuntimeError(self, error, "DeleteObject() failed"))
2877 class cmd_domain_trust_validate(DomainTrustCommand):
2878 """Validate a domain trust."""
2880 synopsis = "%prog DOMAIN [options]"
2882 takes_optiongroups = {
2883 "sambaopts": options.SambaOptions,
2884 "versionopts": options.VersionOptions,
2885 "credopts": options.CredentialsOptions,
2886 "localdcopts": LocalDCCredentialsOptions,
2890 Option("--validate-location", type="choice", metavar="LOCATION",
2891 choices=["local", "both"],
2892 help="Where to validate the trusted domain object: 'local' or 'both'.",
2893 dest='validate_location',
2897 takes_args = ["domain"]
2899 def run(self, domain, sambaopts=None, versionopts=None, credopts=None, localdcopts=None,
2900 validate_location=None):
2902 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2904 local_server = self.setup_local_server(sambaopts, localdcopts)
2906 local_lsa = self.new_local_lsa_connection()
2907 except RuntimeError as error:
2908 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2911 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2912 except RuntimeError as error:
2913 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2915 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2916 local_lsa_info.name.string,
2917 local_lsa_info.dns_domain.string,
2918 local_lsa_info.sid))
2921 lsaString = lsa.String()
2922 lsaString.string = domain
2923 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2924 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2925 except NTSTATUSError as error:
2926 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2927 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2929 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
2931 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
2932 local_tdo_info.netbios_name.string,
2933 local_tdo_info.domain_name.string,
2934 local_tdo_info.sid))
2937 local_netlogon = self.new_local_netlogon_connection()
2938 except RuntimeError as error:
2939 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2942 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_server,
2943 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2945 local_tdo_info.domain_name.string)
2946 except RuntimeError as error:
2947 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2949 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2950 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2952 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2953 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2954 local_trust_verify.trusted_dc_name,
2955 local_trust_verify.tc_connection_status[1],
2956 local_trust_verify.pdc_connection_status[1])
2958 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2959 local_trust_verify.trusted_dc_name,
2960 local_trust_verify.tc_connection_status[1],
2961 local_trust_verify.pdc_connection_status[1])
2963 if local_trust_status != werror.WERR_SUCCESS or local_conn_status != werror.WERR_SUCCESS:
2964 raise CommandError(local_validation)
2966 self.outf.write("OK: %s\n" % local_validation)
2969 server = local_trust_verify.trusted_dc_name.replace('\\', '')
2970 domain_and_server = "%s\\%s" % (local_tdo_info.domain_name.string, server)
2971 local_trust_rediscover = local_netlogon.netr_LogonControl2Ex(local_server,
2972 netlogon.NETLOGON_CONTROL_REDISCOVER,
2975 except RuntimeError as error:
2976 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
2978 local_conn_status = self._uint32(local_trust_rediscover.tc_connection_status[0])
2979 local_rediscover = "LocalRediscover: DC[%s] CONNECTION[%s]" % (
2980 local_trust_rediscover.trusted_dc_name,
2981 local_trust_rediscover.tc_connection_status[1])
2983 if local_conn_status != werror.WERR_SUCCESS:
2984 raise CommandError(local_rediscover)
2986 self.outf.write("OK: %s\n" % local_rediscover)
2988 if validate_location != "local":
2990 remote_server = self.setup_remote_server(credopts, domain, require_pdc=False)
2991 except RuntimeError as error:
2992 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2995 remote_netlogon = self.new_remote_netlogon_connection()
2996 except RuntimeError as error:
2997 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
3000 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_server,
3001 netlogon.NETLOGON_CONTROL_TC_VERIFY,
3003 local_lsa_info.dns_domain.string)
3004 except RuntimeError as error:
3005 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
3007 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
3008 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
3010 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
3011 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
3012 remote_trust_verify.trusted_dc_name,
3013 remote_trust_verify.tc_connection_status[1],
3014 remote_trust_verify.pdc_connection_status[1])
3016 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
3017 remote_trust_verify.trusted_dc_name,
3018 remote_trust_verify.tc_connection_status[1],
3019 remote_trust_verify.pdc_connection_status[1])
3021 if remote_trust_status != werror.WERR_SUCCESS or remote_conn_status != werror.WERR_SUCCESS:
3022 raise CommandError(remote_validation)
3024 self.outf.write("OK: %s\n" % remote_validation)
3027 server = remote_trust_verify.trusted_dc_name.replace('\\', '')
3028 domain_and_server = "%s\\%s" % (local_lsa_info.dns_domain.string, server)
3029 remote_trust_rediscover = remote_netlogon.netr_LogonControl2Ex(remote_server,
3030 netlogon.NETLOGON_CONTROL_REDISCOVER,
3033 except RuntimeError as error:
3034 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
3036 remote_conn_status = self._uint32(remote_trust_rediscover.tc_connection_status[0])
3038 remote_rediscover = "RemoteRediscover: DC[%s] CONNECTION[%s]" % (
3039 remote_trust_rediscover.trusted_dc_name,
3040 remote_trust_rediscover.tc_connection_status[1])
3042 if remote_conn_status != werror.WERR_SUCCESS:
3043 raise CommandError(remote_rediscover)
3045 self.outf.write("OK: %s\n" % remote_rediscover)
3049 class cmd_domain_trust_namespaces(DomainTrustCommand):
3050 """Manage forest trust namespaces."""
3052 synopsis = "%prog [DOMAIN] [options]"
3054 takes_optiongroups = {
3055 "sambaopts": options.SambaOptions,
3056 "versionopts": options.VersionOptions,
3057 "localdcopts": LocalDCCredentialsOptions,
3061 Option("--refresh", type="choice", metavar="check|store",
3062 choices=["check", "store", None],
3063 help="List and maybe store refreshed forest trust information: 'check' or 'store'.",
3066 Option("--enable-all", action="store_true",
3067 help="Try to update disabled entries, not allowed with --refresh=check.",
3070 Option("--enable-tln", action="append", metavar='DNSDOMAIN',
3071 help="Enable a top level name entry. Can be specified multiple times.",
3074 Option("--disable-tln", action="append", metavar='DNSDOMAIN',
3075 help="Disable a top level name entry. Can be specified multiple times.",
3078 Option("--add-tln-ex", action="append", metavar='DNSDOMAIN',
3079 help="Add a top level exclusion entry. Can be specified multiple times.",
3082 Option("--delete-tln-ex", action="append", metavar='DNSDOMAIN',
3083 help="Delete a top level exclusion entry. Can be specified multiple times.",
3084 dest='delete_tln_ex',
3086 Option("--enable-nb", action="append", metavar='NETBIOSDOMAIN',
3087 help="Enable a netbios name in a domain entry. Can be specified multiple times.",
3090 Option("--disable-nb", action="append", metavar='NETBIOSDOMAIN',
3091 help="Disable a netbios name in a domain entry. Can be specified multiple times.",
3094 Option("--enable-sid", action="append", metavar='DOMAINSID',
3095 help="Enable a SID in a domain entry. Can be specified multiple times.",
3096 dest='enable_sid_str',
3098 Option("--disable-sid", action="append", metavar='DOMAINSID',
3099 help="Disable a SID in a domain entry. Can be specified multiple times.",
3100 dest='disable_sid_str',
3102 Option("--add-upn-suffix", action="append", metavar='DNSDOMAIN',
3103 help="Add a new uPNSuffixes attribute for the local forest. Can be specified multiple times.",
3106 Option("--delete-upn-suffix", action="append", metavar='DNSDOMAIN',
3107 help="Delete an existing uPNSuffixes attribute of the local forest. Can be specified multiple times.",
3110 Option("--add-spn-suffix", action="append", metavar='DNSDOMAIN',
3111 help="Add a new msDS-SPNSuffixes attribute for the local forest. Can be specified multiple times.",
3114 Option("--delete-spn-suffix", action="append", metavar='DNSDOMAIN',
3115 help="Delete an existing msDS-SPNSuffixes attribute of the local forest. Can be specified multiple times.",
3120 takes_args = ["domain?"]
3122 def run(self, domain=None, sambaopts=None, localdcopts=None, versionopts=None,
3123 refresh=None, enable_all=False,
3124 enable_tln=[], disable_tln=[], add_tln_ex=[], delete_tln_ex=[],
3125 enable_sid_str=[], disable_sid_str=[], enable_nb=[], disable_nb=[],
3126 add_upn=[], delete_upn=[], add_spn=[], delete_spn=[]):
3128 require_update = False
3131 if refresh == "store":
3132 raise CommandError("--refresh=%s not allowed without DOMAIN" % refresh)
3135 raise CommandError("--enable-all not allowed without DOMAIN")
3137 if len(enable_tln) > 0:
3138 raise CommandError("--enable-tln not allowed without DOMAIN")
3139 if len(disable_tln) > 0:
3140 raise CommandError("--disable-tln not allowed without DOMAIN")
3142 if len(add_tln_ex) > 0:
3143 raise CommandError("--add-tln-ex not allowed without DOMAIN")
3144 if len(delete_tln_ex) > 0:
3145 raise CommandError("--delete-tln-ex not allowed without DOMAIN")
3147 if len(enable_nb) > 0:
3148 raise CommandError("--enable-nb not allowed without DOMAIN")
3149 if len(disable_nb) > 0:
3150 raise CommandError("--disable-nb not allowed without DOMAIN")
3152 if len(enable_sid_str) > 0:
3153 raise CommandError("--enable-sid not allowed without DOMAIN")
3154 if len(disable_sid_str) > 0:
3155 raise CommandError("--disable-sid not allowed without DOMAIN")
3157 if len(add_upn) > 0:
3159 if not n.startswith("*."):
3161 raise CommandError("value[%s] specified for --add-upn-suffix should not include with '*.'" % n)
3162 require_update = True
3163 if len(delete_upn) > 0:
3164 for n in delete_upn:
3165 if not n.startswith("*."):
3167 raise CommandError("value[%s] specified for --delete-upn-suffix should not include with '*.'" % n)
3168 require_update = True
3170 for d in delete_upn:
3171 if a.lower() != d.lower():
3173 raise CommandError("value[%s] specified for --add-upn-suffix and --delete-upn-suffix" % a)
3175 if len(add_spn) > 0:
3177 if not n.startswith("*."):
3179 raise CommandError("value[%s] specified for --add-spn-suffix should not include with '*.'" % n)
3180 require_update = True
3181 if len(delete_spn) > 0:
3182 for n in delete_spn:
3183 if not n.startswith("*."):
3185 raise CommandError("value[%s] specified for --delete-spn-suffix should not include with '*.'" % n)
3186 require_update = True
3188 for d in delete_spn:
3189 if a.lower() != d.lower():
3191 raise CommandError("value[%s] specified for --add-spn-suffix and --delete-spn-suffix" % a)
3193 if len(add_upn) > 0:
3194 raise CommandError("--add-upn-suffix not allowed together with DOMAIN")
3195 if len(delete_upn) > 0:
3196 raise CommandError("--delete-upn-suffix not allowed together with DOMAIN")
3197 if len(add_spn) > 0:
3198 raise CommandError("--add-spn-suffix not allowed together with DOMAIN")
3199 if len(delete_spn) > 0:
3200 raise CommandError("--delete-spn-suffix not allowed together with DOMAIN")
3202 if refresh is not None:
3203 if refresh == "store":
3204 require_update = True
3206 if enable_all and refresh != "store":
3207 raise CommandError("--enable-all not allowed together with --refresh=%s" % refresh)
3209 if len(enable_tln) > 0:
3210 raise CommandError("--enable-tln not allowed together with --refresh")
3211 if len(disable_tln) > 0:
3212 raise CommandError("--disable-tln not allowed together with --refresh")
3214 if len(add_tln_ex) > 0:
3215 raise CommandError("--add-tln-ex not allowed together with --refresh")
3216 if len(delete_tln_ex) > 0:
3217 raise CommandError("--delete-tln-ex not allowed together with --refresh")
3219 if len(enable_nb) > 0:
3220 raise CommandError("--enable-nb not allowed together with --refresh")
3221 if len(disable_nb) > 0:
3222 raise CommandError("--disable-nb not allowed together with --refresh")
3224 if len(enable_sid_str) > 0:
3225 raise CommandError("--enable-sid not allowed together with --refresh")
3226 if len(disable_sid_str) > 0:
3227 raise CommandError("--disable-sid not allowed together with --refresh")
3230 require_update = True
3232 if len(enable_tln) > 0:
3233 raise CommandError("--enable-tln not allowed together with --enable-all")
3235 if len(enable_nb) > 0:
3236 raise CommandError("--enable-nb not allowed together with --enable-all")
3238 if len(enable_sid_str) > 0:
3239 raise CommandError("--enable-sid not allowed together with --enable-all")
3241 if len(enable_tln) > 0:
3242 require_update = True
3243 if len(disable_tln) > 0:
3244 require_update = True
3245 for e in enable_tln:
3246 for d in disable_tln:
3247 if e.lower() != d.lower():
3249 raise CommandError("value[%s] specified for --enable-tln and --disable-tln" % e)
3251 if len(add_tln_ex) > 0:
3252 for n in add_tln_ex:
3253 if not n.startswith("*."):
3255 raise CommandError("value[%s] specified for --add-tln-ex should not include with '*.'" % n)
3256 require_update = True
3257 if len(delete_tln_ex) > 0:
3258 for n in delete_tln_ex:
3259 if not n.startswith("*."):
3261 raise CommandError("value[%s] specified for --delete-tln-ex should not include with '*.'" % n)
3262 require_update = True
3263 for a in add_tln_ex:
3264 for d in delete_tln_ex:
3265 if a.lower() != d.lower():
3267 raise CommandError("value[%s] specified for --add-tln-ex and --delete-tln-ex" % a)
3269 if len(enable_nb) > 0:
3270 require_update = True
3271 if len(disable_nb) > 0:
3272 require_update = True
3274 for d in disable_nb:
3275 if e.upper() != d.upper():
3277 raise CommandError("value[%s] specified for --enable-nb and --disable-nb" % e)
3280 for s in enable_sid_str:
3282 sid = security.dom_sid(s)
3283 except TypeError as error:
3284 raise CommandError("value[%s] specified for --enable-sid is not a valid SID" % s)
3285 enable_sid.append(sid)
3287 for s in disable_sid_str:
3289 sid = security.dom_sid(s)
3290 except TypeError as error:
3291 raise CommandError("value[%s] specified for --disable-sid is not a valid SID" % s)
3292 disable_sid.append(sid)
3293 if len(enable_sid) > 0:
3294 require_update = True
3295 if len(disable_sid) > 0:
3296 require_update = True
3297 for e in enable_sid:
3298 for d in disable_sid:
3301 raise CommandError("value[%s] specified for --enable-sid and --disable-sid" % e)
3303 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
3305 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
3307 local_server = self.setup_local_server(sambaopts, localdcopts)
3309 local_lsa = self.new_local_lsa_connection()
3310 except RuntimeError as error:
3311 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
3314 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
3315 except RuntimeError as error:
3316 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
3318 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
3319 local_lsa_info.name.string,
3320 local_lsa_info.dns_domain.string,
3321 local_lsa_info.sid))
3325 local_netlogon = self.new_local_netlogon_connection()
3326 except RuntimeError as error:
3327 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3330 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3331 except RuntimeError as error:
3332 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3334 if local_netlogon_info.domain_name != local_netlogon_info.forest_name:
3335 raise CommandError("The local domain [%s] is not the forest root [%s]" % (
3336 local_netlogon_info.domain_name,
3337 local_netlogon_info.forest_name))
3340 # get all information about our own forest
3341 own_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3343 except RuntimeError as error:
3344 if self.check_runtime_error(error, werror.WERR_RPC_S_PROCNUM_OUT_OF_RANGE):
3345 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3348 if self.check_runtime_error(error, werror.WERR_INVALID_FUNCTION):
3349 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3352 if self.check_runtime_error(error, werror.WERR_NERR_ACFNOTLOADED):
3353 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3356 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3358 self.outf.write("Own forest trust information...\n")
3359 self.write_forest_trust_info(own_forest_info,
3360 tln=local_lsa_info.dns_domain.string)
3363 local_samdb = self.new_local_ldap_connection()
3364 except RuntimeError as error:
3365 raise self.LocalRuntimeError(self, error, "failed to connect to SamDB")
3367 local_partitions_dn = "CN=Partitions,%s" % str(local_samdb.get_config_basedn())
3368 attrs = ['uPNSuffixes', 'msDS-SPNSuffixes']
3370 msgs = local_samdb.search(base=local_partitions_dn,
3371 scope=ldb.SCOPE_BASE,
3372 expression="(objectClass=crossRefContainer)",
3374 stored_msg = msgs[0]
3375 except ldb.LdbError as error:
3376 raise self.LocalLdbError(self, error, "failed to search partition dn")
3378 stored_upn_vals = []
3379 if 'uPNSuffixes' in stored_msg:
3380 stored_upn_vals.extend(stored_msg['uPNSuffixes'])
3382 stored_spn_vals = []
3383 if 'msDS-SPNSuffixes' in stored_msg:
3384 stored_spn_vals.extend(stored_msg['msDS-SPNSuffixes'])
3386 self.outf.write("Stored uPNSuffixes attributes[%d]:\n" % len(stored_upn_vals))
3387 for v in stored_upn_vals:
3388 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3389 self.outf.write("Stored msDS-SPNSuffixes attributes[%d]:\n" % len(stored_spn_vals))
3390 for v in stored_spn_vals:
3391 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3393 if not require_update:
3397 update_upn_vals = []
3398 update_upn_vals.extend(stored_upn_vals)
3401 update_spn_vals = []
3402 update_spn_vals.extend(stored_spn_vals)
3406 for i in xrange(0, len(update_upn_vals)):
3407 v = update_upn_vals[i]
3408 if v.lower() != upn.lower():
3413 raise CommandError("Entry already present for value[%s] specified for --add-upn-suffix" % upn)
3414 update_upn_vals.append(upn)
3417 for upn in delete_upn:
3419 for i in xrange(0, len(update_upn_vals)):
3420 v = update_upn_vals[i]
3421 if v.lower() != upn.lower():
3426 raise CommandError("Entry not found for value[%s] specified for --delete-upn-suffix" % upn)
3428 update_upn_vals.pop(idx)
3433 for i in xrange(0, len(update_spn_vals)):
3434 v = update_spn_vals[i]
3435 if v.lower() != spn.lower():
3440 raise CommandError("Entry already present for value[%s] specified for --add-spn-suffix" % spn)
3441 update_spn_vals.append(spn)
3444 for spn in delete_spn:
3446 for i in xrange(0, len(update_spn_vals)):
3447 v = update_spn_vals[i]
3448 if v.lower() != spn.lower():
3453 raise CommandError("Entry not found for value[%s] specified for --delete-spn-suffix" % spn)
3455 update_spn_vals.pop(idx)
3458 self.outf.write("Update uPNSuffixes attributes[%d]:\n" % len(update_upn_vals))
3459 for v in update_upn_vals:
3460 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3461 self.outf.write("Update msDS-SPNSuffixes attributes[%d]:\n" % len(update_spn_vals))
3462 for v in update_spn_vals:
3463 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3465 update_msg = ldb.Message()
3466 update_msg.dn = stored_msg.dn
3469 update_msg['uPNSuffixes'] = ldb.MessageElement(update_upn_vals,
3470 ldb.FLAG_MOD_REPLACE,
3473 update_msg['msDS-SPNSuffixes'] = ldb.MessageElement(update_spn_vals,
3474 ldb.FLAG_MOD_REPLACE,
3477 local_samdb.modify(update_msg)
3478 except ldb.LdbError as error:
3479 raise self.LocalLdbError(self, error, "failed to update partition dn")
3482 stored_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3484 except RuntimeError as error:
3485 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3487 self.outf.write("Stored forest trust information...\n")
3488 self.write_forest_trust_info(stored_forest_info,
3489 tln=local_lsa_info.dns_domain.string)
3493 lsaString = lsa.String()
3494 lsaString.string = domain
3495 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
3496 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
3497 except NTSTATUSError as error:
3498 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
3499 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
3501 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
3503 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
3504 local_tdo_info.netbios_name.string,
3505 local_tdo_info.domain_name.string,
3506 local_tdo_info.sid))
3508 if not local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
3509 raise CommandError("trusted domain object for domain [%s] is not marked as FOREST_TRANSITIVE." % domain)
3511 if refresh is not None:
3513 local_netlogon = self.new_local_netlogon_connection()
3514 except RuntimeError as error:
3515 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3518 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3519 except RuntimeError as error:
3520 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3522 lsa_update_check = 1
3523 if refresh == "store":
3524 netlogon_update_tdo = netlogon.DS_GFTI_UPDATE_TDO
3526 lsa_update_check = 0
3528 netlogon_update_tdo = 0
3531 # get all information about the remote trust
3532 # this triggers netr_GetForestTrustInformation to the remote domain
3533 # and lsaRSetForestTrustInformation() locally, but new top level
3534 # names are disabled by default.
3535 fresh_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3536 local_tdo_info.domain_name.string,
3537 netlogon_update_tdo)
3538 except RuntimeError as error:
3539 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3542 fresh_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3543 local_tdo_info.domain_name,
3544 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3547 except RuntimeError as error:
3548 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3550 self.outf.write("Fresh forest trust information...\n")
3551 self.write_forest_trust_info(fresh_forest_info,
3552 tln=local_tdo_info.domain_name.string,
3553 collisions=fresh_forest_collision)
3555 if refresh == "store":
3557 lsaString = lsa.String()
3558 lsaString.string = local_tdo_info.domain_name.string
3559 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3561 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3562 except RuntimeError as error:
3563 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3565 self.outf.write("Stored forest trust information...\n")
3566 self.write_forest_trust_info(stored_forest_info,
3567 tln=local_tdo_info.domain_name.string)
3572 # The none --refresh path
3576 lsaString = lsa.String()
3577 lsaString.string = local_tdo_info.domain_name.string
3578 local_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3580 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3581 except RuntimeError as error:
3582 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3584 self.outf.write("Local forest trust information...\n")
3585 self.write_forest_trust_info(local_forest_info,
3586 tln=local_tdo_info.domain_name.string)
3588 if not require_update:
3592 entries.extend(local_forest_info.entries)
3593 update_forest_info = lsa.ForestTrustInformation()
3594 update_forest_info.count = len(entries)
3595 update_forest_info.entries = entries
3598 for i in xrange(0, len(update_forest_info.entries)):
3599 r = update_forest_info.entries[i]
3600 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3602 if update_forest_info.entries[i].flags == 0:
3604 update_forest_info.entries[i].time = 0
3605 update_forest_info.entries[i].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3606 for i in xrange(0, len(update_forest_info.entries)):
3607 r = update_forest_info.entries[i]
3608 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3610 if update_forest_info.entries[i].flags == 0:
3612 update_forest_info.entries[i].time = 0
3613 update_forest_info.entries[i].flags &= ~lsa.LSA_NB_DISABLED_MASK
3614 update_forest_info.entries[i].flags &= ~lsa.LSA_SID_DISABLED_MASK
3616 for tln in enable_tln:
3618 for i in xrange(0, len(update_forest_info.entries)):
3619 r = update_forest_info.entries[i]
3620 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3622 if r.forest_trust_data.string.lower() != tln.lower():
3627 raise CommandError("Entry not found for value[%s] specified for --enable-tln" % tln)
3628 if not update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_MASK:
3629 raise CommandError("Entry found for value[%s] specified for --enable-tln is already enabled" % tln)
3630 update_forest_info.entries[idx].time = 0
3631 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3633 for tln in disable_tln:
3635 for i in xrange(0, len(update_forest_info.entries)):
3636 r = update_forest_info.entries[i]
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 --disable-tln" % tln)
3645 if update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_ADMIN:
3646 raise CommandError("Entry found for value[%s] specified for --disable-tln is already disabled" % tln)
3647 update_forest_info.entries[idx].time = 0
3648 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3649 update_forest_info.entries[idx].flags |= lsa.LSA_TLN_DISABLED_ADMIN
3651 for tln_ex in add_tln_ex:
3653 for i in xrange(0, len(update_forest_info.entries)):
3654 r = update_forest_info.entries[i]
3655 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3657 if r.forest_trust_data.string.lower() != tln_ex.lower():
3662 raise CommandError("Entry already present for value[%s] specified for --add-tln-ex" % tln_ex)
3664 tln_dot = ".%s" % tln_ex.lower()
3666 for i in xrange(0, len(update_forest_info.entries)):
3667 r = update_forest_info.entries[i]
3668 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3670 r_dot = ".%s" % r.forest_trust_data.string.lower()
3671 if tln_dot == r_dot:
3672 raise CommandError("TLN entry present for value[%s] specified for --add-tln-ex" % tln_ex)
3673 if not tln_dot.endswith(r_dot):
3679 raise CommandError("No TLN parent present for value[%s] specified for --add-tln-ex" % tln_ex)
3681 r = lsa.ForestTrustRecord()
3682 r.type = lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX
3685 r.forest_trust_data.string = tln_ex
3688 entries.extend(update_forest_info.entries)
3689 entries.insert(idx + 1, r)
3690 update_forest_info.count = len(entries)
3691 update_forest_info.entries = entries
3693 for tln_ex in delete_tln_ex:
3695 for i in xrange(0, len(update_forest_info.entries)):
3696 r = update_forest_info.entries[i]
3697 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3699 if r.forest_trust_data.string.lower() != tln_ex.lower():
3704 raise CommandError("Entry not found for value[%s] specified for --delete-tln-ex" % tln_ex)
3707 entries.extend(update_forest_info.entries)
3709 update_forest_info.count = len(entries)
3710 update_forest_info.entries = entries
3712 for nb in enable_nb:
3714 for i in xrange(0, len(update_forest_info.entries)):
3715 r = update_forest_info.entries[i]
3716 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3718 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3723 raise CommandError("Entry not found for value[%s] specified for --enable-nb" % nb)
3724 if not update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_MASK:
3725 raise CommandError("Entry found for value[%s] specified for --enable-nb is already enabled" % nb)
3726 update_forest_info.entries[idx].time = 0
3727 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3729 for nb in disable_nb:
3731 for i in xrange(0, len(update_forest_info.entries)):
3732 r = update_forest_info.entries[i]
3733 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3735 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3740 raise CommandError("Entry not found for value[%s] specified for --delete-nb" % nb)
3741 if update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_ADMIN:
3742 raise CommandError("Entry found for value[%s] specified for --disable-nb is already disabled" % nb)
3743 update_forest_info.entries[idx].time = 0
3744 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3745 update_forest_info.entries[idx].flags |= lsa.LSA_NB_DISABLED_ADMIN
3747 for sid in enable_sid:
3749 for i in xrange(0, len(update_forest_info.entries)):
3750 r = update_forest_info.entries[i]
3751 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3753 if r.forest_trust_data.domain_sid != sid:
3758 raise CommandError("Entry not found for value[%s] specified for --enable-sid" % sid)
3759 if not update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_MASK:
3760 raise CommandError("Entry found for value[%s] specified for --enable-sid is already enabled" % nb)
3761 update_forest_info.entries[idx].time = 0
3762 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3764 for sid in disable_sid:
3766 for i in xrange(0, len(update_forest_info.entries)):
3767 r = update_forest_info.entries[i]
3768 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3770 if r.forest_trust_data.domain_sid != sid:
3775 raise CommandError("Entry not found for value[%s] specified for --delete-sid" % sid)
3776 if update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_ADMIN:
3777 raise CommandError("Entry found for value[%s] specified for --disable-sid is already disabled" % nb)
3778 update_forest_info.entries[idx].time = 0
3779 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3780 update_forest_info.entries[idx].flags |= lsa.LSA_SID_DISABLED_ADMIN
3783 update_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3784 local_tdo_info.domain_name,
3785 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3786 update_forest_info, 0)
3787 except RuntimeError as error:
3788 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3790 self.outf.write("Updated forest trust information...\n")
3791 self.write_forest_trust_info(update_forest_info,
3792 tln=local_tdo_info.domain_name.string,
3793 collisions=update_forest_collision)
3796 lsaString = lsa.String()
3797 lsaString.string = local_tdo_info.domain_name.string
3798 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3800 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3801 except RuntimeError as error:
3802 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3804 self.outf.write("Stored forest trust information...\n")
3805 self.write_forest_trust_info(stored_forest_info,
3806 tln=local_tdo_info.domain_name.string)
3809 class cmd_domain_tombstones_expunge(Command):
3810 """Expunge tombstones from the database.
3812 This command expunges tombstones from the database."""
3813 synopsis = "%prog NC [NC [...]] [options]"
3816 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
3817 metavar="URL", dest="H"),
3818 Option("--current-time",
3819 help="The current time to evaluate the tombstone lifetime from, expressed as YYYY-MM-DD",
3821 Option("--tombstone-lifetime", help="Number of days a tombstone should be preserved for", type=int),
3824 takes_args = ["nc*"]
3826 takes_optiongroups = {
3827 "sambaopts": options.SambaOptions,
3828 "credopts": options.CredentialsOptions,
3829 "versionopts": options.VersionOptions,
3832 def run(self, *ncs, **kwargs):
3833 sambaopts = kwargs.get("sambaopts")
3834 credopts = kwargs.get("credopts")
3835 versionpts = kwargs.get("versionopts")
3837 current_time_string = kwargs.get("current_time")
3838 tombstone_lifetime = kwargs.get("tombstone_lifetime")
3839 lp = sambaopts.get_loadparm()
3840 creds = credopts.get_credentials(lp)
3841 samdb = SamDB(url=H, session_info=system_session(),
3842 credentials=creds, lp=lp)
3844 if current_time_string is not None:
3845 current_time_obj = time.strptime(current_time_string, "%Y-%m-%d")
3846 current_time = long(time.mktime(current_time_obj))
3849 current_time = long(time.time())
3852 res = samdb.search(expression="", base="", scope=ldb.SCOPE_BASE,
3853 attrs=["namingContexts"])
3856 for nc in res[0]["namingContexts"]:
3861 started_transaction = False
3863 samdb.transaction_start()
3864 started_transaction = True
3866 removed_links) = samdb.garbage_collect_tombstones(ncs,
3867 current_time=current_time,
3868 tombstone_lifetime=tombstone_lifetime)
3870 except Exception as err:
3871 if started_transaction:
3872 samdb.transaction_cancel()
3873 raise CommandError("Failed to expunge / garbage collect tombstones", err)
3875 samdb.transaction_commit()
3877 self.outf.write("Removed %d objects and %d links successfully\n"
3878 % (removed_objects, removed_links))
3882 class cmd_domain_trust(SuperCommand):
3883 """Domain and forest trust management."""
3886 subcommands["list"] = cmd_domain_trust_list()
3887 subcommands["show"] = cmd_domain_trust_show()
3888 subcommands["create"] = cmd_domain_trust_create()
3889 subcommands["delete"] = cmd_domain_trust_delete()
3890 subcommands["validate"] = cmd_domain_trust_validate()
3891 subcommands["namespaces"] = cmd_domain_trust_namespaces()
3893 class cmd_domain_tombstones(SuperCommand):
3894 """Domain tombstone and recycled object management."""
3897 subcommands["expunge"] = cmd_domain_tombstones_expunge()
3899 class ldif_schema_update:
3900 """Helper class for applying LDIF schema updates"""
3903 self.is_defunct = False
3904 self.unknown_oid = None
3908 def _ldap_schemaUpdateNow(self, samdb):
3912 add: schemaUpdateNow
3915 samdb.modify_ldif(ldif)
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 self._ldap_schemaUpdateNow(samdb)
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("--quiet", help="Be quiet", action="store_true"),
3978 Option("--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("--quiet", help="Be quiet", action="store_true"),
4228 Option("--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()