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 import samba.getopt as options
38 from samba import ntstatus
39 from samba import NTSTATUSError
40 from samba import werror
41 from getpass import getpass
42 from samba.net import Net, LIBNET_JOIN_AUTOMATIC
44 from samba.join import join_RODC, join_DC, join_subdomain
45 from samba.auth import system_session
46 from samba.samdb import SamDB, get_default_backend_store
47 from samba.ndr import ndr_unpack, ndr_pack, ndr_print
48 from samba.dcerpc import drsuapi
49 from samba.dcerpc import drsblobs
50 from samba.dcerpc import lsa
51 from samba.dcerpc import netlogon
52 from samba.dcerpc import security
53 from samba.dcerpc import nbt
54 from samba.dcerpc import misc
55 from samba.dcerpc.samr import DOMAIN_PASSWORD_COMPLEX, DOMAIN_PASSWORD_STORE_CLEARTEXT
56 from samba.netcmd import (
62 from samba.netcmd.fsmo import get_fsmo_roleowner
63 from samba.netcmd.common import netcmd_get_domain_infos_via_cldap
64 from samba.samba3 import Samba3
65 from samba.samba3 import param as s3param
66 from samba.upgrade import upgrade_from_samba3
67 from samba.drs_utils import (
68 sendDsReplicaSync, drsuapi_connect, drsException,
70 from samba import remove_dc, arcfour_encrypt, string_to_byte_array
72 from samba.dsdb import (
73 DS_DOMAIN_FUNCTION_2000,
74 DS_DOMAIN_FUNCTION_2003,
75 DS_DOMAIN_FUNCTION_2003_MIXED,
76 DS_DOMAIN_FUNCTION_2008,
77 DS_DOMAIN_FUNCTION_2008_R2,
78 DS_DOMAIN_FUNCTION_2012,
79 DS_DOMAIN_FUNCTION_2012_R2,
80 DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL,
81 DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL,
82 UF_WORKSTATION_TRUST_ACCOUNT,
83 UF_SERVER_TRUST_ACCOUNT,
84 UF_TRUSTED_FOR_DELEGATION,
85 UF_PARTIAL_SECRETS_ACCOUNT
88 from samba.provision import (
91 DEFAULT_MIN_PWD_LENGTH,
95 from samba.provision.common import (
101 string_version_to_constant = {
102 "2008_R2" : DS_DOMAIN_FUNCTION_2008_R2,
103 "2012": DS_DOMAIN_FUNCTION_2012,
104 "2012_R2": DS_DOMAIN_FUNCTION_2012_R2,
107 def get_testparm_var(testparm, smbconf, varname):
108 errfile = open(os.devnull, 'w')
109 p = subprocess.Popen([testparm, '-s', '-l',
110 '--parameter-name=%s' % varname, smbconf],
111 stdout=subprocess.PIPE, stderr=errfile)
112 (out,err) = p.communicate()
114 lines = out.split('\n')
116 return lines[0].strip()
120 import samba.dckeytab
122 cmd_domain_export_keytab = None
124 class cmd_domain_export_keytab(Command):
125 """Dump Kerberos keys of the domain into a keytab."""
127 synopsis = "%prog <keytab> [options]"
129 takes_optiongroups = {
130 "sambaopts": options.SambaOptions,
131 "credopts": options.CredentialsOptions,
132 "versionopts": options.VersionOptions,
136 Option("--principal", help="extract only this principal", type=str),
139 takes_args = ["keytab"]
141 def run(self, keytab, credopts=None, sambaopts=None, versionopts=None, principal=None):
142 lp = sambaopts.get_loadparm()
144 net.export_keytab(keytab=keytab, principal=principal)
147 class cmd_domain_info(Command):
148 """Print basic info about a domain and the DC passed as parameter."""
150 synopsis = "%prog <ip_address> [options]"
155 takes_optiongroups = {
156 "sambaopts": options.SambaOptions,
157 "credopts": options.CredentialsOptions,
158 "versionopts": options.VersionOptions,
161 takes_args = ["address"]
163 def run(self, address, credopts=None, sambaopts=None, versionopts=None):
164 lp = sambaopts.get_loadparm()
166 res = netcmd_get_domain_infos_via_cldap(lp, None, address)
168 raise CommandError("Invalid IP address '" + address + "'!")
169 self.outf.write("Forest : %s\n" % res.forest)
170 self.outf.write("Domain : %s\n" % res.dns_domain)
171 self.outf.write("Netbios domain : %s\n" % res.domain_name)
172 self.outf.write("DC name : %s\n" % res.pdc_dns_name)
173 self.outf.write("DC netbios name : %s\n" % res.pdc_name)
174 self.outf.write("Server site : %s\n" % res.server_site)
175 self.outf.write("Client site : %s\n" % res.client_site)
178 class cmd_domain_provision(Command):
179 """Provision a domain."""
181 synopsis = "%prog [options]"
183 takes_optiongroups = {
184 "sambaopts": options.SambaOptions,
185 "versionopts": options.VersionOptions,
189 Option("--interactive", help="Ask for names", action="store_true"),
190 Option("--domain", type="string", metavar="DOMAIN",
191 help="NetBIOS domain name to use"),
192 Option("--domain-guid", type="string", metavar="GUID",
193 help="set domainguid (otherwise random)"),
194 Option("--domain-sid", type="string", metavar="SID",
195 help="set domainsid (otherwise random)"),
196 Option("--ntds-guid", type="string", metavar="GUID",
197 help="set NTDS object GUID (otherwise random)"),
198 Option("--invocationid", type="string", metavar="GUID",
199 help="set invocationid (otherwise random)"),
200 Option("--host-name", type="string", metavar="HOSTNAME",
201 help="set hostname"),
202 Option("--host-ip", type="string", metavar="IPADDRESS",
203 help="set IPv4 ipaddress"),
204 Option("--host-ip6", type="string", metavar="IP6ADDRESS",
205 help="set IPv6 ipaddress"),
206 Option("--site", type="string", metavar="SITENAME",
207 help="set site name"),
208 Option("--adminpass", type="string", metavar="PASSWORD",
209 help="choose admin password (otherwise random)"),
210 Option("--krbtgtpass", type="string", metavar="PASSWORD",
211 help="choose krbtgt password (otherwise random)"),
212 Option("--machinepass", type="string", metavar="PASSWORD",
213 help="choose machine password (otherwise random)"),
214 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
215 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
216 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
217 "BIND9_FLATFILE uses bind9 text database to store zone information, "
218 "BIND9_DLZ uses samba4 AD to store zone information, "
219 "NONE skips the DNS setup entirely (not recommended)",
220 default="SAMBA_INTERNAL"),
221 Option("--dnspass", type="string", metavar="PASSWORD",
222 help="choose dns password (otherwise random)"),
223 Option("--ldapadminpass", type="string", metavar="PASSWORD",
224 help="choose password to set between Samba and its LDAP backend (otherwise random)"),
225 Option("--root", type="string", metavar="USERNAME",
226 help="choose 'root' unix username"),
227 Option("--nobody", type="string", metavar="USERNAME",
228 help="choose 'nobody' user"),
229 Option("--users", type="string", metavar="GROUPNAME",
230 help="choose 'users' group"),
231 Option("--quiet", help="Be quiet", action="store_true"),
232 Option("--blank", action="store_true",
233 help="do not add users or groups, just the structure"),
234 Option("--ldap-backend-type", type="choice", metavar="LDAP-BACKEND-TYPE",
235 help="Test initialisation support for unsupported LDAP backend type (fedora-ds or openldap) DO NOT USE",
236 choices=["fedora-ds", "openldap"]),
237 Option("--server-role", type="choice", metavar="ROLE",
238 choices=["domain controller", "dc", "member server", "member", "standalone"],
239 help="The server role (domain controller | dc | member server | member | standalone). Default is dc.",
240 default="domain controller"),
241 Option("--function-level", type="choice", metavar="FOR-FUN-LEVEL",
242 choices=["2000", "2003", "2008", "2008_R2"],
243 help="The domain and forest function level (2000 | 2003 | 2008 | 2008_R2 - always native). Default is (Windows) 2008_R2 Native.",
245 Option("--base-schema", type="choice", metavar="BASE-SCHEMA",
246 choices=["2008_R2", "2008_R2_old", "2012", "2012_R2"],
247 help="The base schema files to use. Default is (Windows) 2008_R2.",
249 Option("--next-rid", type="int", metavar="NEXTRID", default=1000,
250 help="The initial nextRid value (only needed for upgrades). Default is 1000."),
251 Option("--partitions-only",
252 help="Configure Samba's partitions, but do not modify them (ie, join a BDC)", action="store_true"),
253 Option("--targetdir", type="string", metavar="DIR",
254 help="Set target directory"),
255 Option("--ol-mmr-urls", type="string", metavar="LDAPSERVER",
256 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\""),
257 Option("--use-rfc2307", action="store_true", help="Use AD to store posix attributes (default = no)"),
258 Option("--plaintext-secrets", action="store_true",
259 help="Store secret/sensitive values as plain text on disk" +
260 "(default is to encrypt secret/ensitive values)"),
261 Option("--backend-store", type="choice", metavar="BACKENDSTORE",
262 choices=["tdb", "mdb"],
263 help="Specify the database backend to be used "
264 "(default is %s)" % get_default_backend_store()),
268 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",
269 action="store_true"),
270 Option("--slapd-path", type="string", metavar="SLAPD-PATH",
271 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."),
272 Option("--ldap-backend-extra-port", type="int", metavar="LDAP-BACKEND-EXTRA-PORT", help="Additional TCP port for LDAP backend server (to use for replication)"),
273 Option("--ldap-backend-forced-uri", type="string", metavar="LDAP-BACKEND-FORCED-URI",
274 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"),
275 Option("--ldap-backend-nosync", help="Configure LDAP backend not to call fsync() (for performance in test environments)", action="store_true"),
279 Option("--use-ntvfs", action="store_true", help="Use NTVFS for the fileserver (default = no)"),
280 Option("--use-xattrs", type="choice", choices=["yes","no","auto"],
281 metavar="[yes|no|auto]",
282 help="Define if we should use the native fs capabilities or a tdb file for "
283 "storing attributes likes ntacl when --use-ntvfs is set. "
284 "auto tries to make an inteligent guess based on the user rights and system capabilities",
288 if os.getenv('TEST_LDAP', "no") == "yes":
289 takes_options.extend(openldap_options)
291 if samba.is_ntvfs_fileserver_built():
292 takes_options.extend(ntvfs_options)
296 def run(self, sambaopts=None, versionopts=None,
319 ldap_backend_type=None,
323 partitions_only=None,
330 ldap_backend_nosync=None,
331 ldap_backend_extra_port=None,
332 ldap_backend_forced_uri=None,
333 ldap_dryrun_mode=None,
335 plaintext_secrets=False,
338 self.logger = self.get_logger("provision")
340 self.logger.setLevel(logging.WARNING)
342 self.logger.setLevel(logging.INFO)
344 lp = sambaopts.get_loadparm()
345 smbconf = lp.configfile
347 if dns_forwarder is not None:
348 suggested_forwarder = dns_forwarder
350 suggested_forwarder = self._get_nameserver_ip()
351 if suggested_forwarder is None:
352 suggested_forwarder = "none"
354 if len(self.raw_argv) == 1:
358 from getpass import getpass
361 def ask(prompt, default=None):
362 if default is not None:
363 print("%s [%s]: " % (prompt, default), end=' ')
365 print("%s: " % (prompt,), end=' ')
366 return sys.stdin.readline().rstrip("\n") or default
369 default = socket.getfqdn().split(".", 1)[1].upper()
372 realm = ask("Realm", default)
373 if realm in (None, ""):
374 raise CommandError("No realm set!")
377 default = realm.split(".")[0]
380 domain = ask("Domain", default)
382 raise CommandError("No domain set!")
384 server_role = ask("Server Role (dc, member, standalone)", "dc")
386 dns_backend = ask("DNS backend (SAMBA_INTERNAL, BIND9_FLATFILE, BIND9_DLZ, NONE)", "SAMBA_INTERNAL")
387 if dns_backend in (None, ''):
388 raise CommandError("No DNS backend set!")
390 if dns_backend == "SAMBA_INTERNAL":
391 dns_forwarder = ask("DNS forwarder IP address (write 'none' to disable forwarding)", suggested_forwarder)
392 if dns_forwarder.lower() in (None, 'none'):
393 suggested_forwarder = None
397 adminpassplain = getpass("Administrator password: ")
398 issue = self._adminpass_issue(adminpassplain)
400 self.errf.write("%s.\n" % issue)
402 adminpassverify = getpass("Retype password: ")
403 if not adminpassplain == adminpassverify:
404 self.errf.write("Sorry, passwords do not match.\n")
406 adminpass = adminpassplain
410 realm = sambaopts._lp.get('realm')
412 raise CommandError("No realm set!")
414 raise CommandError("No domain set!")
417 issue = self._adminpass_issue(adminpass)
419 raise CommandError(issue)
421 self.logger.info("Administrator password will be set randomly!")
423 if function_level == "2000":
424 dom_for_fun_level = DS_DOMAIN_FUNCTION_2000
425 elif function_level == "2003":
426 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
427 elif function_level == "2008":
428 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008
429 elif function_level == "2008_R2":
430 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
432 if dns_backend == "SAMBA_INTERNAL" and dns_forwarder is None:
433 dns_forwarder = suggested_forwarder
435 samdb_fill = FILL_FULL
437 samdb_fill = FILL_NT4SYNC
438 elif partitions_only:
439 samdb_fill = FILL_DRS
441 if targetdir is not None:
442 if not os.path.isdir(targetdir):
447 if use_xattrs == "yes":
449 elif use_xattrs == "auto" and use_ntvfs == False:
451 elif use_ntvfs == False:
452 raise CommandError("--use-xattrs=no requires --use-ntvfs (not supported for production use). "
453 "Please re-run with --use-xattrs omitted.")
454 elif use_xattrs == "auto" and not lp.get("posix:eadb"):
456 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
458 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
461 samba.ntacls.setntacl(lp, file.name,
462 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
465 self.logger.info("You are not root or your system does not support xattr, using tdb backend for attributes. ")
470 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.")
471 if ldap_backend_type == "existing":
472 if ldap_backend_forced_uri is not None:
473 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)
475 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")
477 if ldap_backend_forced_uri is not None:
478 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")
480 if domain_sid is not None:
481 domain_sid = security.dom_sid(domain_sid)
483 session = system_session()
484 if backend_store is None:
485 backend_store = get_default_backend_store()
487 result = provision(self.logger,
488 session, smbconf=smbconf, targetdir=targetdir,
489 samdb_fill=samdb_fill, realm=realm, domain=domain,
490 domainguid=domain_guid, domainsid=domain_sid,
492 hostip=host_ip, hostip6=host_ip6,
493 sitename=site, ntdsguid=ntds_guid,
494 invocationid=invocationid, adminpass=adminpass,
495 krbtgtpass=krbtgtpass, machinepass=machinepass,
496 dns_backend=dns_backend, dns_forwarder=dns_forwarder,
497 dnspass=dnspass, root=root, nobody=nobody,
499 serverrole=server_role, dom_for_fun_level=dom_for_fun_level,
500 backend_type=ldap_backend_type,
501 ldapadminpass=ldapadminpass, ol_mmr_urls=ol_mmr_urls, slapd_path=slapd_path,
502 useeadb=eadb, next_rid=next_rid, lp=lp, use_ntvfs=use_ntvfs,
503 use_rfc2307=use_rfc2307, skip_sysvolacl=False,
504 ldap_backend_extra_port=ldap_backend_extra_port,
505 ldap_backend_forced_uri=ldap_backend_forced_uri,
506 nosync=ldap_backend_nosync, ldap_dryrun_mode=ldap_dryrun_mode,
507 base_schema=base_schema,
508 plaintext_secrets=plaintext_secrets,
509 backend_store=backend_store)
511 except ProvisioningError as e:
512 raise CommandError("Provision failed", e)
514 result.report_logger(self.logger)
516 def _get_nameserver_ip(self):
517 """Grab the nameserver IP address from /etc/resolv.conf."""
519 RESOLV_CONF="/etc/resolv.conf"
521 if not path.isfile(RESOLV_CONF):
522 self.logger.warning("Failed to locate %s" % RESOLV_CONF)
527 handle = open(RESOLV_CONF, 'r')
529 if not line.startswith('nameserver'):
531 # we want the last non-space continuous string of the line
532 return line.strip().split()[-1]
534 if handle is not None:
537 self.logger.warning("No nameserver found in %s" % RESOLV_CONF)
539 def _adminpass_issue(self, adminpass):
540 """Returns error string for a bad administrator password,
541 or None if acceptable"""
543 if len(adminpass.decode('utf-8')) < DEFAULT_MIN_PWD_LENGTH:
544 return "Administrator password does not meet the default minimum" \
545 " password length requirement (%d characters)" \
546 % DEFAULT_MIN_PWD_LENGTH
547 elif not samba.check_password_quality(adminpass):
548 return "Administrator password does not meet the default" \
554 class cmd_domain_dcpromo(Command):
555 """Promote an existing domain member or NT4 PDC to an AD DC."""
557 synopsis = "%prog <dnsdomain> [DC|RODC] [options]"
559 takes_optiongroups = {
560 "sambaopts": options.SambaOptions,
561 "versionopts": options.VersionOptions,
562 "credopts": options.CredentialsOptions,
566 Option("--server", help="DC to join", type=str),
567 Option("--site", help="site to join", type=str),
568 Option("--targetdir", help="where to store provision", type=str),
569 Option("--domain-critical-only",
570 help="only replicate critical domain objects",
571 action="store_true"),
572 Option("--machinepass", type=str, metavar="PASSWORD",
573 help="choose machine password (otherwise random)"),
574 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
575 choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
576 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
577 "BIND9_DLZ uses samba4 AD to store zone information, "
578 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
579 default="SAMBA_INTERNAL"),
580 Option("--quiet", help="Be quiet", action="store_true"),
581 Option("--verbose", help="Be verbose", action="store_true")
585 Option("--use-ntvfs", action="store_true", help="Use NTVFS for the fileserver (default = no)"),
588 if samba.is_ntvfs_fileserver_built():
589 takes_options.extend(ntvfs_options)
592 takes_args = ["domain", "role?"]
594 def run(self, domain, role=None, sambaopts=None, credopts=None,
595 versionopts=None, server=None, site=None, targetdir=None,
596 domain_critical_only=False, parent_domain=None, machinepass=None,
597 use_ntvfs=False, dns_backend=None,
598 quiet=False, verbose=False):
599 lp = sambaopts.get_loadparm()
600 creds = credopts.get_credentials(lp)
601 net = Net(creds, lp, server=credopts.ipaddress)
603 logger = self.get_logger()
605 logger.setLevel(logging.DEBUG)
607 logger.setLevel(logging.WARNING)
609 logger.setLevel(logging.INFO)
611 netbios_name = lp.get("netbios name")
617 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
618 site=site, netbios_name=netbios_name, targetdir=targetdir,
619 domain_critical_only=domain_critical_only,
620 machinepass=machinepass, use_ntvfs=use_ntvfs,
621 dns_backend=dns_backend,
622 promote_existing=True)
624 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
625 site=site, netbios_name=netbios_name, targetdir=targetdir,
626 domain_critical_only=domain_critical_only,
627 machinepass=machinepass, use_ntvfs=use_ntvfs, dns_backend=dns_backend,
628 promote_existing=True)
630 raise CommandError("Invalid role '%s' (possible values: DC, RODC)" % role)
633 class cmd_domain_join(Command):
634 """Join domain as either member or backup domain controller."""
636 synopsis = "%prog <dnsdomain> [DC|RODC|MEMBER|SUBDOMAIN] [options]"
638 takes_optiongroups = {
639 "sambaopts": options.SambaOptions,
640 "versionopts": options.VersionOptions,
641 "credopts": options.CredentialsOptions,
645 Option("--server", help="DC to join", type=str),
646 Option("--site", help="site to join", type=str),
647 Option("--targetdir", help="where to store provision", type=str),
648 Option("--parent-domain", help="parent domain to create subdomain under", type=str),
649 Option("--domain-critical-only",
650 help="only replicate critical domain objects",
651 action="store_true"),
652 Option("--machinepass", type=str, metavar="PASSWORD",
653 help="choose machine password (otherwise random)"),
654 Option("--adminpass", type="string", metavar="PASSWORD",
655 help="choose adminstrator password when joining as a subdomain (otherwise random)"),
656 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
657 choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
658 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
659 "BIND9_DLZ uses samba4 AD to store zone information, "
660 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
661 default="SAMBA_INTERNAL"),
662 Option("--plaintext-secrets", action="store_true",
663 help="Store secret/sensitive values as plain text on disk" +
664 "(default is to encrypt secret/ensitive values)"),
665 Option("--quiet", help="Be quiet", action="store_true"),
666 Option("--verbose", help="Be verbose", action="store_true")
670 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
673 if samba.is_ntvfs_fileserver_built():
674 takes_options.extend(ntvfs_options)
676 takes_args = ["domain", "role?"]
678 def run(self, domain, role=None, sambaopts=None, credopts=None,
679 versionopts=None, server=None, site=None, targetdir=None,
680 domain_critical_only=False, parent_domain=None, machinepass=None,
681 use_ntvfs=False, dns_backend=None, adminpass=None,
682 quiet=False, verbose=False, plaintext_secrets=False):
683 lp = sambaopts.get_loadparm()
684 creds = credopts.get_credentials(lp)
685 net = Net(creds, lp, server=credopts.ipaddress)
688 site = "Default-First-Site-Name"
690 logger = self.get_logger()
692 logger.setLevel(logging.DEBUG)
694 logger.setLevel(logging.WARNING)
696 logger.setLevel(logging.INFO)
698 netbios_name = lp.get("netbios name")
703 if role is None or role == "MEMBER":
704 (join_password, sid, domain_name) = net.join_member(
705 domain, netbios_name, LIBNET_JOIN_AUTOMATIC,
706 machinepass=machinepass)
708 self.errf.write("Joined domain %s (%s)\n" % (domain_name, sid))
710 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
711 site=site, netbios_name=netbios_name, targetdir=targetdir,
712 domain_critical_only=domain_critical_only,
713 machinepass=machinepass, use_ntvfs=use_ntvfs,
714 dns_backend=dns_backend,
715 plaintext_secrets=plaintext_secrets)
717 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
718 site=site, netbios_name=netbios_name, targetdir=targetdir,
719 domain_critical_only=domain_critical_only,
720 machinepass=machinepass, use_ntvfs=use_ntvfs,
721 dns_backend=dns_backend,
722 plaintext_secrets=plaintext_secrets)
723 elif role == "SUBDOMAIN":
725 logger.info("Administrator password will be set randomly!")
727 netbios_domain = lp.get("workgroup")
728 if parent_domain is None:
729 parent_domain = ".".join(domain.split(".")[1:])
730 join_subdomain(logger=logger, server=server, creds=creds, lp=lp, dnsdomain=domain,
731 parent_domain=parent_domain, site=site,
732 netbios_name=netbios_name, netbios_domain=netbios_domain,
733 targetdir=targetdir, machinepass=machinepass,
734 use_ntvfs=use_ntvfs, dns_backend=dns_backend,
736 plaintext_secrets=plaintext_secrets)
738 raise CommandError("Invalid role '%s' (possible values: MEMBER, DC, RODC, SUBDOMAIN)" % role)
741 class cmd_domain_demote(Command):
742 """Demote ourselves from the role of Domain Controller."""
744 synopsis = "%prog [options]"
747 Option("--server", help="writable DC to write demotion changes on", type=str),
748 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
749 metavar="URL", dest="H"),
750 Option("--remove-other-dead-server", help="Dead DC (name or NTDS GUID) "
751 "to remove ALL references to (rather than this DC)", type=str),
752 Option("--quiet", help="Be quiet", action="store_true"),
753 Option("--verbose", help="Be verbose", action="store_true"),
756 takes_optiongroups = {
757 "sambaopts": options.SambaOptions,
758 "credopts": options.CredentialsOptions,
759 "versionopts": options.VersionOptions,
762 def run(self, sambaopts=None, credopts=None,
763 versionopts=None, server=None,
764 remove_other_dead_server=None, H=None,
765 verbose=False, quiet=False):
766 lp = sambaopts.get_loadparm()
767 creds = credopts.get_credentials(lp)
768 net = Net(creds, lp, server=credopts.ipaddress)
770 logger = self.get_logger()
772 logger.setLevel(logging.DEBUG)
774 logger.setLevel(logging.WARNING)
776 logger.setLevel(logging.INFO)
778 if remove_other_dead_server is not None:
779 if server is not None:
780 samdb = SamDB(url="ldap://%s" % server,
781 session_info=system_session(),
782 credentials=creds, lp=lp)
784 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
786 remove_dc.remove_dc(samdb, logger, remove_other_dead_server)
787 except remove_dc.DemoteException as err:
788 raise CommandError("Demote failed: %s" % err)
791 netbios_name = lp.get("netbios name")
792 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
794 res = samdb.search(expression='(&(objectClass=computer)(serverReferenceBL=*))', attrs=["dnsHostName", "name"])
796 raise CommandError("Unable to search for servers")
799 raise CommandError("You are the latest server in the domain")
803 if str(e["name"]).lower() != netbios_name.lower():
804 server = e["dnsHostName"]
807 ntds_guid = samdb.get_ntds_GUID()
808 msg = samdb.search(base=str(samdb.get_config_basedn()),
809 scope=ldb.SCOPE_SUBTREE, expression="(objectGUID=%s)" % ntds_guid,
811 if len(msg) == 0 or "options" not in msg[0]:
812 raise CommandError("Failed to find options on %s" % ntds_guid)
815 dsa_options = int(str(msg[0]['options']))
817 res = samdb.search(expression="(fSMORoleOwner=%s)" % str(ntds_dn),
818 controls=["search_options:1:2"])
821 raise CommandError("Current DC is still the owner of %d role(s), use the role command to transfer roles to another DC" % len(res))
823 self.errf.write("Using %s as partner server for the demotion\n" %
825 (drsuapiBind, drsuapi_handle, supportedExtensions) = drsuapi_connect(server, lp, creds)
827 self.errf.write("Deactivating inbound replication\n")
832 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
833 dsa_options |= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
834 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
838 self.errf.write("Asking partner server %s to synchronize from us\n"
840 for part in (samdb.get_schema_basedn(),
841 samdb.get_config_basedn(),
842 samdb.get_root_basedn()):
843 nc = drsuapi.DsReplicaObjectIdentifier()
846 req1 = drsuapi.DsReplicaSyncRequest1()
847 req1.naming_context = nc;
848 req1.options = drsuapi.DRSUAPI_DRS_WRIT_REP
849 req1.source_dsa_guid = misc.GUID(ntds_guid)
852 drsuapiBind.DsReplicaSync(drsuapi_handle, 1, req1)
853 except RuntimeError as e1:
854 (werr, string) = e1.args
855 if werr == werror.WERR_DS_DRA_NO_REPLICA:
859 "Error while replicating out last local changes from '%s' for demotion, "
860 "re-enabling inbound replication\n" % part)
861 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
862 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
864 raise CommandError("Error while sending a DsReplicaSync for partition '%s'" % str(part), string)
866 remote_samdb = SamDB(url="ldap://%s" % server,
867 session_info=system_session(),
868 credentials=creds, lp=lp)
870 self.errf.write("Changing userControl and container\n")
871 res = remote_samdb.search(base=str(remote_samdb.domain_dn()),
872 expression="(&(objectClass=user)(sAMAccountName=%s$))" %
873 netbios_name.upper(),
874 attrs=["userAccountControl"])
876 uac = int(str(res[0]["userAccountControl"]))
878 except Exception as e:
879 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
881 "Error while demoting, re-enabling inbound replication\n")
882 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
883 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
885 raise CommandError("Error while changing account control", e)
888 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
890 "Error while demoting, re-enabling inbound replication")
891 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
892 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
894 raise CommandError("Unable to find object with samaccountName = %s$"
895 " in the remote dc" % netbios_name.upper())
899 uac &= ~(UF_SERVER_TRUST_ACCOUNT|UF_TRUSTED_FOR_DELEGATION|UF_PARTIAL_SECRETS_ACCOUNT)
900 uac |= UF_WORKSTATION_TRUST_ACCOUNT
905 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
906 ldb.FLAG_MOD_REPLACE,
907 "userAccountControl")
909 remote_samdb.modify(msg)
910 except Exception as e:
911 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
913 "Error while demoting, re-enabling inbound replication")
914 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
915 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
918 raise CommandError("Error while changing account control", e)
920 parent = msg.dn.parent()
921 dc_name = res[0].dn.get_rdn_value()
922 rdn = "CN=%s" % dc_name
924 # Let's move to the Computer container
928 computer_dn = ldb.Dn(remote_samdb, "CN=Computers,%s" % str(remote_samdb.domain_dn()))
929 res = remote_samdb.search(base=computer_dn, expression=rdn, scope=ldb.SCOPE_ONELEVEL)
932 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
933 scope=ldb.SCOPE_ONELEVEL)
934 while(len(res) != 0 and i < 100):
936 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
937 scope=ldb.SCOPE_ONELEVEL)
940 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
942 "Error while demoting, re-enabling inbound replication\n")
943 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
944 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
950 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
951 ldb.FLAG_MOD_REPLACE,
952 "userAccountControl")
954 remote_samdb.modify(msg)
956 raise CommandError("Unable to find a slot for renaming %s,"
957 " all names from %s-1 to %s-%d seemed used" %
958 (str(dc_dn), rdn, rdn, i - 9))
960 newrdn = "%s-%d" % (rdn, i)
963 newdn = ldb.Dn(remote_samdb, "%s,%s" % (newrdn, str(computer_dn)))
964 remote_samdb.rename(dc_dn, newdn)
965 except Exception as e:
966 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
968 "Error while demoting, re-enabling inbound replication\n")
969 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
970 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
976 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
977 ldb.FLAG_MOD_REPLACE,
978 "userAccountControl")
980 remote_samdb.modify(msg)
981 raise CommandError("Error while renaming %s to %s" % (str(dc_dn), str(newdn)), e)
984 server_dsa_dn = samdb.get_serverName()
985 domain = remote_samdb.get_root_basedn()
988 req1 = drsuapi.DsRemoveDSServerRequest1()
989 req1.server_dn = str(server_dsa_dn)
990 req1.domain_dn = str(domain)
993 drsuapiBind.DsRemoveDSServer(drsuapi_handle, 1, req1)
994 except RuntimeError as e3:
995 (werr, string) = e3.args
996 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
998 "Error while demoting, re-enabling inbound replication\n")
999 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
1000 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
1006 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
1007 ldb.FLAG_MOD_REPLACE,
1008 "userAccountControl")
1009 remote_samdb.modify(msg)
1010 remote_samdb.rename(newdn, dc_dn)
1011 if werr == werror.WERR_DS_DRA_NO_REPLICA:
1012 raise CommandError("The DC %s is not present on (already removed from) the remote server: " % server_dsa_dn, e)
1014 raise CommandError("Error while sending a removeDsServer of %s: " % server_dsa_dn, e)
1016 remove_dc.remove_sysvol_references(remote_samdb, logger, dc_name)
1018 # These are objects under the computer account that should be deleted
1019 for s in ("CN=Enterprise,CN=NTFRS Subscriptions",
1020 "CN=%s, CN=NTFRS Subscriptions" % lp.get("realm"),
1021 "CN=Domain system Volumes (SYSVOL Share), CN=NTFRS Subscriptions",
1022 "CN=NTFRS Subscriptions"):
1024 remote_samdb.delete(ldb.Dn(remote_samdb,
1025 "%s,%s" % (s, str(newdn))))
1026 except ldb.LdbError as l:
1029 self.errf.write("Demote successful\n")
1032 class cmd_domain_level(Command):
1033 """Raise domain and forest function levels."""
1035 synopsis = "%prog (show|raise <options>) [options]"
1037 takes_optiongroups = {
1038 "sambaopts": options.SambaOptions,
1039 "credopts": options.CredentialsOptions,
1040 "versionopts": options.VersionOptions,
1044 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1045 metavar="URL", dest="H"),
1046 Option("--quiet", help="Be quiet", action="store_true"),
1047 Option("--forest-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
1048 help="The forest function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)"),
1049 Option("--domain-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
1050 help="The domain function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)")
1053 takes_args = ["subcommand"]
1055 def run(self, subcommand, H=None, forest_level=None, domain_level=None,
1056 quiet=False, credopts=None, sambaopts=None, versionopts=None):
1057 lp = sambaopts.get_loadparm()
1058 creds = credopts.get_credentials(lp, fallback_machine=True)
1060 samdb = SamDB(url=H, session_info=system_session(),
1061 credentials=creds, lp=lp)
1063 domain_dn = samdb.domain_dn()
1065 res_forest = samdb.search("CN=Partitions,%s" % samdb.get_config_basedn(),
1066 scope=ldb.SCOPE_BASE, attrs=["msDS-Behavior-Version"])
1067 assert len(res_forest) == 1
1069 res_domain = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1070 attrs=["msDS-Behavior-Version", "nTMixedDomain"])
1071 assert len(res_domain) == 1
1073 res_dc_s = samdb.search("CN=Sites,%s" % samdb.get_config_basedn(),
1074 scope=ldb.SCOPE_SUBTREE, expression="(objectClass=nTDSDSA)",
1075 attrs=["msDS-Behavior-Version"])
1076 assert len(res_dc_s) >= 1
1078 # default values, since "msDS-Behavior-Version" does not exist on Windows 2000 AD
1079 level_forest = DS_DOMAIN_FUNCTION_2000
1080 level_domain = DS_DOMAIN_FUNCTION_2000
1082 if "msDS-Behavior-Version" in res_forest[0]:
1083 level_forest = int(res_forest[0]["msDS-Behavior-Version"][0])
1084 if "msDS-Behavior-Version" in res_domain[0]:
1085 level_domain = int(res_domain[0]["msDS-Behavior-Version"][0])
1086 level_domain_mixed = int(res_domain[0]["nTMixedDomain"][0])
1089 for msg in res_dc_s:
1090 if "msDS-Behavior-Version" in msg:
1091 if min_level_dc is None or int(msg["msDS-Behavior-Version"][0]) < min_level_dc:
1092 min_level_dc = int(msg["msDS-Behavior-Version"][0])
1094 min_level_dc = DS_DOMAIN_FUNCTION_2000
1095 # well, this is the least
1098 if level_forest < DS_DOMAIN_FUNCTION_2000 or level_domain < DS_DOMAIN_FUNCTION_2000:
1099 raise CommandError("Domain and/or forest function level(s) is/are invalid. Correct them or reprovision!")
1100 if min_level_dc < DS_DOMAIN_FUNCTION_2000:
1101 raise CommandError("Lowest function level of a DC is invalid. Correct this or reprovision!")
1102 if level_forest > level_domain:
1103 raise CommandError("Forest function level is higher than the domain level(s). Correct this or reprovision!")
1104 if level_domain > min_level_dc:
1105 raise CommandError("Domain function level is higher than the lowest function level of a DC. Correct this or reprovision!")
1107 if subcommand == "show":
1108 self.message("Domain and forest function level for domain '%s'" % domain_dn)
1109 if level_forest == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1110 self.message("\nATTENTION: You run SAMBA 4 on a forest function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1111 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1112 self.message("\nATTENTION: You run SAMBA 4 on a domain function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1113 if min_level_dc == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1114 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)!")
1118 if level_forest == DS_DOMAIN_FUNCTION_2000:
1120 elif level_forest == DS_DOMAIN_FUNCTION_2003_MIXED:
1121 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1122 elif level_forest == DS_DOMAIN_FUNCTION_2003:
1124 elif level_forest == DS_DOMAIN_FUNCTION_2008:
1126 elif level_forest == DS_DOMAIN_FUNCTION_2008_R2:
1128 elif level_forest == DS_DOMAIN_FUNCTION_2012:
1130 elif level_forest == DS_DOMAIN_FUNCTION_2012_R2:
1133 outstr = "higher than 2012 R2"
1134 self.message("Forest function level: (Windows) " + outstr)
1136 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1137 outstr = "2000 mixed (NT4 DC support)"
1138 elif level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed == 0:
1140 elif level_domain == DS_DOMAIN_FUNCTION_2003_MIXED:
1141 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1142 elif level_domain == DS_DOMAIN_FUNCTION_2003:
1144 elif level_domain == DS_DOMAIN_FUNCTION_2008:
1146 elif level_domain == DS_DOMAIN_FUNCTION_2008_R2:
1148 elif level_domain == DS_DOMAIN_FUNCTION_2012:
1150 elif level_domain == DS_DOMAIN_FUNCTION_2012_R2:
1153 outstr = "higher than 2012 R2"
1154 self.message("Domain function level: (Windows) " + outstr)
1156 if min_level_dc == DS_DOMAIN_FUNCTION_2000:
1158 elif min_level_dc == DS_DOMAIN_FUNCTION_2003:
1160 elif min_level_dc == DS_DOMAIN_FUNCTION_2008:
1162 elif min_level_dc == DS_DOMAIN_FUNCTION_2008_R2:
1164 elif min_level_dc == DS_DOMAIN_FUNCTION_2012:
1166 elif min_level_dc == DS_DOMAIN_FUNCTION_2012_R2:
1169 outstr = "higher than 2012 R2"
1170 self.message("Lowest function level of a DC: (Windows) " + outstr)
1172 elif subcommand == "raise":
1175 if domain_level is not None:
1176 if domain_level == "2003":
1177 new_level_domain = DS_DOMAIN_FUNCTION_2003
1178 elif domain_level == "2008":
1179 new_level_domain = DS_DOMAIN_FUNCTION_2008
1180 elif domain_level == "2008_R2":
1181 new_level_domain = DS_DOMAIN_FUNCTION_2008_R2
1182 elif domain_level == "2012":
1183 new_level_domain = DS_DOMAIN_FUNCTION_2012
1184 elif domain_level == "2012_R2":
1185 new_level_domain = DS_DOMAIN_FUNCTION_2012_R2
1187 if new_level_domain <= level_domain and level_domain_mixed == 0:
1188 raise CommandError("Domain function level can't be smaller than or equal to the actual one!")
1189 if new_level_domain > min_level_dc:
1190 raise CommandError("Domain function level can't be higher than the lowest function level of a DC!")
1192 # Deactivate mixed/interim domain support
1193 if level_domain_mixed != 0:
1194 # Directly on the base DN
1196 m.dn = ldb.Dn(samdb, domain_dn)
1197 m["nTMixedDomain"] = ldb.MessageElement("0",
1198 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1202 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup") + ",CN=Partitions,%s" % samdb.get_config_basedn())
1203 m["nTMixedDomain"] = ldb.MessageElement("0",
1204 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1207 except ldb.LdbError as e:
1208 (enum, emsg) = e.args
1209 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1212 # Directly on the base DN
1214 m.dn = ldb.Dn(samdb, domain_dn)
1215 m["msDS-Behavior-Version"]= ldb.MessageElement(
1216 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1217 "msDS-Behavior-Version")
1221 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup")
1222 + ",CN=Partitions,%s" % samdb.get_config_basedn())
1223 m["msDS-Behavior-Version"]= ldb.MessageElement(
1224 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1225 "msDS-Behavior-Version")
1228 except ldb.LdbError as e2:
1229 (enum, emsg) = e2.args
1230 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1233 level_domain = new_level_domain
1234 msgs.append("Domain function level changed!")
1236 if forest_level is not None:
1237 if forest_level == "2003":
1238 new_level_forest = DS_DOMAIN_FUNCTION_2003
1239 elif forest_level == "2008":
1240 new_level_forest = DS_DOMAIN_FUNCTION_2008
1241 elif forest_level == "2008_R2":
1242 new_level_forest = DS_DOMAIN_FUNCTION_2008_R2
1243 elif forest_level == "2012":
1244 new_level_forest = DS_DOMAIN_FUNCTION_2012
1245 elif forest_level == "2012_R2":
1246 new_level_forest = DS_DOMAIN_FUNCTION_2012_R2
1248 if new_level_forest <= level_forest:
1249 raise CommandError("Forest function level can't be smaller than or equal to the actual one!")
1250 if new_level_forest > level_domain:
1251 raise CommandError("Forest function level can't be higher than the domain function level(s). Please raise it/them first!")
1254 m.dn = ldb.Dn(samdb, "CN=Partitions,%s" % samdb.get_config_basedn())
1255 m["msDS-Behavior-Version"]= ldb.MessageElement(
1256 str(new_level_forest), ldb.FLAG_MOD_REPLACE,
1257 "msDS-Behavior-Version")
1259 msgs.append("Forest function level changed!")
1260 msgs.append("All changes applied successfully!")
1261 self.message("\n".join(msgs))
1263 raise CommandError("invalid argument: '%s' (choose from 'show', 'raise')" % subcommand)
1266 class cmd_domain_passwordsettings(Command):
1267 """Set password settings.
1269 Password complexity, password lockout policy, history length,
1270 minimum password length, the minimum and maximum password age) on
1271 a Samba AD DC server.
1273 Use against a Windows DC is possible, but group policy will override it.
1276 synopsis = "%prog (show|set <options>) [options]"
1278 takes_optiongroups = {
1279 "sambaopts": options.SambaOptions,
1280 "versionopts": options.VersionOptions,
1281 "credopts": options.CredentialsOptions,
1285 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1286 metavar="URL", dest="H"),
1287 Option("--quiet", help="Be quiet", action="store_true"),
1288 Option("--complexity", type="choice", choices=["on","off","default"],
1289 help="The password complexity (on | off | default). Default is 'on'"),
1290 Option("--store-plaintext", type="choice", choices=["on","off","default"],
1291 help="Store plaintext passwords where account have 'store passwords with reversible encryption' set (on | off | default). Default is 'off'"),
1292 Option("--history-length",
1293 help="The password history length (<integer> | default). Default is 24.", type=str),
1294 Option("--min-pwd-length",
1295 help="The minimum password length (<integer> | default). Default is 7.", type=str),
1296 Option("--min-pwd-age",
1297 help="The minimum password age (<integer in days> | default). Default is 1.", type=str),
1298 Option("--max-pwd-age",
1299 help="The maximum password age (<integer in days> | default). Default is 43.", type=str),
1300 Option("--account-lockout-duration",
1301 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),
1302 Option("--account-lockout-threshold",
1303 help="The number of bad password attempts allowed before locking out the account (<integer> | default). Default is 0 (never lock out).", type=str),
1304 Option("--reset-account-lockout-after",
1305 help="After this time is elapsed, the recorded number of attempts restarts from zero (<integer> | default). Default is 30.", type=str),
1308 takes_args = ["subcommand"]
1310 def run(self, subcommand, H=None, min_pwd_age=None, max_pwd_age=None,
1311 quiet=False, complexity=None, store_plaintext=None, history_length=None,
1312 min_pwd_length=None, account_lockout_duration=None, account_lockout_threshold=None,
1313 reset_account_lockout_after=None, credopts=None, sambaopts=None,
1315 lp = sambaopts.get_loadparm()
1316 creds = credopts.get_credentials(lp)
1318 samdb = SamDB(url=H, session_info=system_session(),
1319 credentials=creds, lp=lp)
1321 domain_dn = samdb.domain_dn()
1322 res = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1323 attrs=["pwdProperties", "pwdHistoryLength", "minPwdLength",
1324 "minPwdAge", "maxPwdAge", "lockoutDuration", "lockoutThreshold",
1325 "lockOutObservationWindow"])
1326 assert(len(res) == 1)
1328 pwd_props = int(res[0]["pwdProperties"][0])
1329 pwd_hist_len = int(res[0]["pwdHistoryLength"][0])
1330 cur_min_pwd_len = int(res[0]["minPwdLength"][0])
1332 cur_min_pwd_age = int(abs(int(res[0]["minPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1333 if int(res[0]["maxPwdAge"][0]) == -0x8000000000000000:
1336 cur_max_pwd_age = int(abs(int(res[0]["maxPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1337 cur_account_lockout_threshold = int(res[0]["lockoutThreshold"][0])
1339 if int(res[0]["lockoutDuration"][0]) == -0x8000000000000000:
1340 cur_account_lockout_duration = 0
1342 cur_account_lockout_duration = abs(int(res[0]["lockoutDuration"][0])) / (1e7 * 60)
1343 cur_reset_account_lockout_after = abs(int(res[0]["lockOutObservationWindow"][0])) / (1e7 * 60)
1344 except Exception as e:
1345 raise CommandError("Could not retrieve password properties!", e)
1347 if subcommand == "show":
1348 self.message("Password informations for domain '%s'" % domain_dn)
1350 if pwd_props & DOMAIN_PASSWORD_COMPLEX != 0:
1351 self.message("Password complexity: on")
1353 self.message("Password complexity: off")
1354 if pwd_props & DOMAIN_PASSWORD_STORE_CLEARTEXT != 0:
1355 self.message("Store plaintext passwords: on")
1357 self.message("Store plaintext passwords: off")
1358 self.message("Password history length: %d" % pwd_hist_len)
1359 self.message("Minimum password length: %d" % cur_min_pwd_len)
1360 self.message("Minimum password age (days): %d" % cur_min_pwd_age)
1361 self.message("Maximum password age (days): %d" % cur_max_pwd_age)
1362 self.message("Account lockout duration (mins): %d" % cur_account_lockout_duration)
1363 self.message("Account lockout threshold (attempts): %d" % cur_account_lockout_threshold)
1364 self.message("Reset account lockout after (mins): %d" % cur_reset_account_lockout_after)
1365 elif subcommand == "set":
1368 m.dn = ldb.Dn(samdb, domain_dn)
1370 if complexity is not None:
1371 if complexity == "on" or complexity == "default":
1372 pwd_props = pwd_props | DOMAIN_PASSWORD_COMPLEX
1373 msgs.append("Password complexity activated!")
1374 elif complexity == "off":
1375 pwd_props = pwd_props & (~DOMAIN_PASSWORD_COMPLEX)
1376 msgs.append("Password complexity deactivated!")
1378 if store_plaintext is not None:
1379 if store_plaintext == "on" or store_plaintext == "default":
1380 pwd_props = pwd_props | DOMAIN_PASSWORD_STORE_CLEARTEXT
1381 msgs.append("Plaintext password storage for changed passwords activated!")
1382 elif store_plaintext == "off":
1383 pwd_props = pwd_props & (~DOMAIN_PASSWORD_STORE_CLEARTEXT)
1384 msgs.append("Plaintext password storage for changed passwords deactivated!")
1386 if complexity is not None or store_plaintext is not None:
1387 m["pwdProperties"] = ldb.MessageElement(str(pwd_props),
1388 ldb.FLAG_MOD_REPLACE, "pwdProperties")
1390 if history_length is not None:
1391 if history_length == "default":
1394 pwd_hist_len = int(history_length)
1396 if pwd_hist_len < 0 or pwd_hist_len > 24:
1397 raise CommandError("Password history length must be in the range of 0 to 24!")
1399 m["pwdHistoryLength"] = ldb.MessageElement(str(pwd_hist_len),
1400 ldb.FLAG_MOD_REPLACE, "pwdHistoryLength")
1401 msgs.append("Password history length changed!")
1403 if min_pwd_length is not None:
1404 if min_pwd_length == "default":
1407 min_pwd_len = int(min_pwd_length)
1409 if min_pwd_len < 0 or min_pwd_len > 14:
1410 raise CommandError("Minimum password length must be in the range of 0 to 14!")
1412 m["minPwdLength"] = ldb.MessageElement(str(min_pwd_len),
1413 ldb.FLAG_MOD_REPLACE, "minPwdLength")
1414 msgs.append("Minimum password length changed!")
1416 if min_pwd_age is not None:
1417 if min_pwd_age == "default":
1420 min_pwd_age = int(min_pwd_age)
1422 if min_pwd_age < 0 or min_pwd_age > 998:
1423 raise CommandError("Minimum password age must be in the range of 0 to 998!")
1426 min_pwd_age_ticks = -int(min_pwd_age * (24 * 60 * 60 * 1e7))
1428 m["minPwdAge"] = ldb.MessageElement(str(min_pwd_age_ticks),
1429 ldb.FLAG_MOD_REPLACE, "minPwdAge")
1430 msgs.append("Minimum password age changed!")
1432 if max_pwd_age is not None:
1433 if max_pwd_age == "default":
1436 max_pwd_age = int(max_pwd_age)
1438 if max_pwd_age < 0 or max_pwd_age > 999:
1439 raise CommandError("Maximum password age must be in the range of 0 to 999!")
1442 if max_pwd_age == 0:
1443 max_pwd_age_ticks = -0x8000000000000000
1445 max_pwd_age_ticks = -int(max_pwd_age * (24 * 60 * 60 * 1e7))
1447 m["maxPwdAge"] = ldb.MessageElement(str(max_pwd_age_ticks),
1448 ldb.FLAG_MOD_REPLACE, "maxPwdAge")
1449 msgs.append("Maximum password age changed!")
1451 if account_lockout_duration is not None:
1452 if account_lockout_duration == "default":
1453 account_lockout_duration = 30
1455 account_lockout_duration = int(account_lockout_duration)
1457 if account_lockout_duration < 0 or account_lockout_duration > 99999:
1458 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1461 if account_lockout_duration == 0:
1462 account_lockout_duration_ticks = -0x8000000000000000
1464 account_lockout_duration_ticks = -int(account_lockout_duration * (60 * 1e7))
1466 m["lockoutDuration"] = ldb.MessageElement(str(account_lockout_duration_ticks),
1467 ldb.FLAG_MOD_REPLACE, "lockoutDuration")
1468 msgs.append("Account lockout duration changed!")
1470 if account_lockout_threshold is not None:
1471 if account_lockout_threshold == "default":
1472 account_lockout_threshold = 0
1474 account_lockout_threshold = int(account_lockout_threshold)
1476 m["lockoutThreshold"] = ldb.MessageElement(str(account_lockout_threshold),
1477 ldb.FLAG_MOD_REPLACE, "lockoutThreshold")
1478 msgs.append("Account lockout threshold changed!")
1480 if reset_account_lockout_after is not None:
1481 if reset_account_lockout_after == "default":
1482 reset_account_lockout_after = 30
1484 reset_account_lockout_after = int(reset_account_lockout_after)
1486 if reset_account_lockout_after < 0 or reset_account_lockout_after > 99999:
1487 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1490 if reset_account_lockout_after == 0:
1491 reset_account_lockout_after_ticks = -0x8000000000000000
1493 reset_account_lockout_after_ticks = -int(reset_account_lockout_after * (60 * 1e7))
1495 m["lockOutObservationWindow"] = ldb.MessageElement(str(reset_account_lockout_after_ticks),
1496 ldb.FLAG_MOD_REPLACE, "lockOutObservationWindow")
1497 msgs.append("Duration to reset account lockout after changed!")
1499 if max_pwd_age > 0 and min_pwd_age >= max_pwd_age:
1500 raise CommandError("Maximum password age (%d) must be greater than minimum password age (%d)!" % (max_pwd_age, min_pwd_age))
1503 raise CommandError("You must specify at least one option to set. Try --help")
1505 msgs.append("All changes applied successfully!")
1506 self.message("\n".join(msgs))
1508 raise CommandError("Wrong argument '%s'!" % subcommand)
1511 class cmd_domain_classicupgrade(Command):
1512 """Upgrade from Samba classic (NT4-like) database to Samba AD DC database.
1514 Specify either a directory with all Samba classic DC databases and state files (with --dbdir) or
1515 the testparm utility from your classic installation (with --testparm).
1518 synopsis = "%prog [options] <classic_smb_conf>"
1520 takes_optiongroups = {
1521 "sambaopts": options.SambaOptions,
1522 "versionopts": options.VersionOptions
1526 Option("--dbdir", type="string", metavar="DIR",
1527 help="Path to samba classic DC database directory"),
1528 Option("--testparm", type="string", metavar="PATH",
1529 help="Path to samba classic DC testparm utility from the previous installation. This allows the default paths of the previous installation to be followed"),
1530 Option("--targetdir", type="string", metavar="DIR",
1531 help="Path prefix where the new Samba 4.0 AD domain should be initialised"),
1532 Option("--quiet", help="Be quiet", action="store_true"),
1533 Option("--verbose", help="Be verbose", action="store_true"),
1534 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
1535 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
1536 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
1537 "BIND9_FLATFILE uses bind9 text database to store zone information, "
1538 "BIND9_DLZ uses samba4 AD to store zone information, "
1539 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
1540 default="SAMBA_INTERNAL")
1544 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
1545 action="store_true"),
1546 Option("--use-xattrs", type="choice", choices=["yes","no","auto"],
1547 metavar="[yes|no|auto]",
1548 help="Define if we should use the native fs capabilities or a tdb file for "
1549 "storing attributes likes ntacl when --use-ntvfs is set. "
1550 "auto tries to make an inteligent guess based on the user rights and system capabilities",
1553 if samba.is_ntvfs_fileserver_built():
1554 takes_options.extend(ntvfs_options)
1556 takes_args = ["smbconf"]
1558 def run(self, smbconf=None, targetdir=None, dbdir=None, testparm=None,
1559 quiet=False, verbose=False, use_xattrs="auto", sambaopts=None, versionopts=None,
1560 dns_backend=None, use_ntvfs=False):
1562 if not os.path.exists(smbconf):
1563 raise CommandError("File %s does not exist" % smbconf)
1565 if testparm and not os.path.exists(testparm):
1566 raise CommandError("Testparm utility %s does not exist" % testparm)
1568 if dbdir and not os.path.exists(dbdir):
1569 raise CommandError("Directory %s does not exist" % dbdir)
1571 if not dbdir and not testparm:
1572 raise CommandError("Please specify either dbdir or testparm")
1574 logger = self.get_logger()
1576 logger.setLevel(logging.DEBUG)
1578 logger.setLevel(logging.WARNING)
1580 logger.setLevel(logging.INFO)
1582 if dbdir and testparm:
1583 logger.warning("both dbdir and testparm specified, ignoring dbdir.")
1586 lp = sambaopts.get_loadparm()
1588 s3conf = s3param.get_context()
1591 s3conf.set("realm", sambaopts.realm)
1593 if targetdir is not None:
1594 if not os.path.isdir(targetdir):
1598 if use_xattrs == "yes":
1600 elif use_xattrs == "auto" and use_ntvfs == False:
1602 elif use_ntvfs == False:
1603 raise CommandError("--use-xattrs=no requires --use-ntvfs (not supported for production use). "
1604 "Please re-run with --use-xattrs omitted.")
1605 elif use_xattrs == "auto" and not s3conf.get("posix:eadb"):
1607 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
1609 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
1612 samba.ntacls.setntacl(lp, tmpfile.name,
1613 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
1616 # FIXME: Don't catch all exceptions here
1617 logger.info("You are not root or your system does not support xattr, using tdb backend for attributes. "
1618 "If you intend to use this provision in production, rerun the script as root on a system supporting xattrs.")
1622 # Set correct default values from dbdir or testparm
1625 paths["state directory"] = dbdir
1626 paths["private dir"] = dbdir
1627 paths["lock directory"] = dbdir
1628 paths["smb passwd file"] = dbdir + "/smbpasswd"
1630 paths["state directory"] = get_testparm_var(testparm, smbconf, "state directory")
1631 paths["private dir"] = get_testparm_var(testparm, smbconf, "private dir")
1632 paths["smb passwd file"] = get_testparm_var(testparm, smbconf, "smb passwd file")
1633 paths["lock directory"] = get_testparm_var(testparm, smbconf, "lock directory")
1634 # "testparm" from Samba 3 < 3.4.x is not aware of the parameter
1635 # "state directory", instead make use of "lock directory"
1636 if len(paths["state directory"]) == 0:
1637 paths["state directory"] = paths["lock directory"]
1640 s3conf.set(p, paths[p])
1642 # load smb.conf parameters
1643 logger.info("Reading smb.conf")
1644 s3conf.load(smbconf)
1645 samba3 = Samba3(smbconf, s3conf)
1647 logger.info("Provisioning")
1648 upgrade_from_samba3(samba3, logger, targetdir, session_info=system_session(),
1649 useeadb=eadb, dns_backend=dns_backend, use_ntvfs=use_ntvfs)
1652 class cmd_domain_samba3upgrade(cmd_domain_classicupgrade):
1653 __doc__ = cmd_domain_classicupgrade.__doc__
1655 # This command is present for backwards compatibility only,
1656 # and should not be shown.
1660 class LocalDCCredentialsOptions(options.CredentialsOptions):
1661 def __init__(self, parser):
1662 options.CredentialsOptions.__init__(self, parser, special_name="local-dc")
1664 class DomainTrustCommand(Command):
1665 """List domain trusts."""
1668 Command.__init__(self)
1669 self.local_lp = None
1671 self.local_server = None
1672 self.local_binding_string = None
1673 self.local_creds = None
1675 self.remote_server = None
1676 self.remote_binding_string = None
1677 self.remote_creds = None
1679 def _uint32(self, v):
1680 return ctypes.c_uint32(v).value
1682 def check_runtime_error(self, runtime, val):
1686 err32 = self._uint32(runtime[0])
1692 class LocalRuntimeError(CommandError):
1693 def __init__(exception_self, self, runtime, message):
1694 err32 = self._uint32(runtime[0])
1696 msg = "LOCAL_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1697 self.local_server, message, err32, errstr)
1698 CommandError.__init__(exception_self, msg)
1700 class RemoteRuntimeError(CommandError):
1701 def __init__(exception_self, self, runtime, message):
1702 err32 = self._uint32(runtime[0])
1704 msg = "REMOTE_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1705 self.remote_server, message, err32, errstr)
1706 CommandError.__init__(exception_self, msg)
1708 class LocalLdbError(CommandError):
1709 def __init__(exception_self, self, ldb_error, message):
1710 errval = ldb_error[0]
1711 errstr = ldb_error[1]
1712 msg = "LOCAL_DC[%s]: %s - ERROR(%d) - %s" % (
1713 self.local_server, message, errval, errstr)
1714 CommandError.__init__(exception_self, msg)
1716 def setup_local_server(self, sambaopts, localdcopts):
1717 if self.local_server is not None:
1718 return self.local_server
1720 lp = sambaopts.get_loadparm()
1722 local_server = localdcopts.ipaddress
1723 if local_server is None:
1724 server_role = lp.server_role()
1725 if server_role != "ROLE_ACTIVE_DIRECTORY_DC":
1726 raise CommandError("Invalid server_role %s" % (server_role))
1727 local_server = lp.get('netbios name')
1728 local_transport = "ncalrpc"
1729 local_binding_options = ""
1730 local_binding_options += ",auth_type=ncalrpc_as_system"
1731 local_ldap_url = None
1734 local_transport = "ncacn_np"
1735 local_binding_options = ""
1736 local_ldap_url = "ldap://%s" % local_server
1737 local_creds = localdcopts.get_credentials(lp)
1741 self.local_server = local_server
1742 self.local_binding_string = "%s:%s[%s]" % (local_transport, local_server, local_binding_options)
1743 self.local_ldap_url = local_ldap_url
1744 self.local_creds = local_creds
1745 return self.local_server
1747 def new_local_lsa_connection(self):
1748 return lsa.lsarpc(self.local_binding_string, self.local_lp, self.local_creds)
1750 def new_local_netlogon_connection(self):
1751 return netlogon.netlogon(self.local_binding_string, self.local_lp, self.local_creds)
1753 def new_local_ldap_connection(self):
1754 return SamDB(url=self.local_ldap_url,
1755 session_info=system_session(),
1756 credentials=self.local_creds,
1759 def setup_remote_server(self, credopts, domain,
1761 require_writable=True):
1764 assert require_writable
1766 if self.remote_server is not None:
1767 return self.remote_server
1769 self.remote_server = "__unknown__remote_server__.%s" % domain
1770 assert self.local_server is not None
1772 remote_creds = credopts.get_credentials(self.local_lp)
1773 remote_server = credopts.ipaddress
1774 remote_binding_options = ""
1776 # TODO: we should also support NT4 domains
1777 # we could use local_netlogon.netr_DsRGetDCNameEx2() with the remote domain name
1778 # and delegate NBT or CLDAP to the local netlogon server
1780 remote_net = Net(remote_creds, self.local_lp, server=remote_server)
1781 remote_flags = nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS
1782 if require_writable:
1783 remote_flags |= nbt.NBT_SERVER_WRITABLE
1785 remote_flags |= nbt.NBT_SERVER_PDC
1786 remote_info = remote_net.finddc(flags=remote_flags, domain=domain, address=remote_server)
1787 except NTSTATUSError as error:
1788 raise CommandError("Failed to find a writeable DC for domain '%s': %s" %
1791 raise CommandError("Failed to find a writeable DC for domain '%s'" % domain)
1793 nbt.NBT_SERVER_PDC: "PDC",
1794 nbt.NBT_SERVER_GC: "GC",
1795 nbt.NBT_SERVER_LDAP: "LDAP",
1796 nbt.NBT_SERVER_DS: "DS",
1797 nbt.NBT_SERVER_KDC: "KDC",
1798 nbt.NBT_SERVER_TIMESERV: "TIMESERV",
1799 nbt.NBT_SERVER_CLOSEST: "CLOSEST",
1800 nbt.NBT_SERVER_WRITABLE: "WRITABLE",
1801 nbt.NBT_SERVER_GOOD_TIMESERV: "GOOD_TIMESERV",
1802 nbt.NBT_SERVER_NDNC: "NDNC",
1803 nbt.NBT_SERVER_SELECT_SECRET_DOMAIN_6: "SELECT_SECRET_DOMAIN_6",
1804 nbt.NBT_SERVER_FULL_SECRET_DOMAIN_6: "FULL_SECRET_DOMAIN_6",
1805 nbt.NBT_SERVER_ADS_WEB_SERVICE: "ADS_WEB_SERVICE",
1806 nbt.NBT_SERVER_DS_8: "DS_8",
1807 nbt.NBT_SERVER_HAS_DNS_NAME: "HAS_DNS_NAME",
1808 nbt.NBT_SERVER_IS_DEFAULT_NC: "IS_DEFAULT_NC",
1809 nbt.NBT_SERVER_FOREST_ROOT: "FOREST_ROOT",
1811 server_type_string = self.generic_bitmap_to_string(flag_map,
1812 remote_info.server_type, names_only=True)
1813 self.outf.write("RemoteDC Netbios[%s] DNS[%s] ServerType[%s]\n" % (
1814 remote_info.pdc_name,
1815 remote_info.pdc_dns_name,
1816 server_type_string))
1818 self.remote_server = remote_info.pdc_dns_name
1819 self.remote_binding_string="ncacn_np:%s[%s]" % (self.remote_server, remote_binding_options)
1820 self.remote_creds = remote_creds
1821 return self.remote_server
1823 def new_remote_lsa_connection(self):
1824 return lsa.lsarpc(self.remote_binding_string, self.local_lp, self.remote_creds)
1826 def new_remote_netlogon_connection(self):
1827 return netlogon.netlogon(self.remote_binding_string, self.local_lp, self.remote_creds)
1829 def get_lsa_info(self, conn, policy_access):
1830 objectAttr = lsa.ObjectAttribute()
1831 objectAttr.sec_qos = lsa.QosInfo()
1833 policy = conn.OpenPolicy2(''.decode('utf-8'),
1834 objectAttr, policy_access)
1836 info = conn.QueryInfoPolicy2(policy, lsa.LSA_POLICY_INFO_DNS)
1838 return (policy, info)
1840 def get_netlogon_dc_info(self, conn, server):
1841 info = conn.netr_DsRGetDCNameEx2(server,
1842 None, 0, None, None, None,
1843 netlogon.DS_RETURN_DNS_NAME)
1846 def netr_DomainTrust_to_name(self, t):
1847 if t.trust_type == lsa.LSA_TRUST_TYPE_DOWNLEVEL:
1848 return t.netbios_name
1852 def netr_DomainTrust_to_type(self, a, t):
1854 primary_parent = None
1856 if _t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
1858 if not _t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1859 primary_parent = a[_t.parent_index]
1862 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1863 if t is primary_parent:
1866 if t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1869 parent = a[t.parent_index]
1870 if parent is primary:
1875 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1880 def netr_DomainTrust_to_transitive(self, t):
1881 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1884 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE:
1887 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1892 def netr_DomainTrust_to_direction(self, t):
1893 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND and \
1894 t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1897 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND:
1900 if t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1905 def generic_enum_to_string(self, e_dict, v, names_only=False):
1909 v32 = self._uint32(v)
1910 w = "__unknown__%08X__" % v32
1912 r = "0x%x (%s)" % (v, w)
1915 def generic_bitmap_to_string(self, b_dict, v, names_only=False):
1920 for b in sorted(b_dict.keys()):
1927 c32 = self._uint32(c)
1928 s += ["__unknown_%08X__" % c32]
1933 r = "0x%x (%s)" % (v, w)
1936 def trustType_string(self, v):
1938 lsa.LSA_TRUST_TYPE_DOWNLEVEL : "DOWNLEVEL",
1939 lsa.LSA_TRUST_TYPE_UPLEVEL : "UPLEVEL",
1940 lsa.LSA_TRUST_TYPE_MIT : "MIT",
1941 lsa.LSA_TRUST_TYPE_DCE : "DCE",
1943 return self.generic_enum_to_string(types, v)
1945 def trustDirection_string(self, v):
1947 lsa.LSA_TRUST_DIRECTION_INBOUND |
1948 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "BOTH",
1949 lsa.LSA_TRUST_DIRECTION_INBOUND : "INBOUND",
1950 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "OUTBOUND",
1952 return self.generic_enum_to_string(directions, v)
1954 def trustAttributes_string(self, v):
1956 lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE : "NON_TRANSITIVE",
1957 lsa.LSA_TRUST_ATTRIBUTE_UPLEVEL_ONLY : "UPLEVEL_ONLY",
1958 lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN : "QUARANTINED_DOMAIN",
1959 lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE : "FOREST_TRANSITIVE",
1960 lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION : "CROSS_ORGANIZATION",
1961 lsa.LSA_TRUST_ATTRIBUTE_WITHIN_FOREST : "WITHIN_FOREST",
1962 lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL : "TREAT_AS_EXTERNAL",
1963 lsa.LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION : "USES_RC4_ENCRYPTION",
1965 return self.generic_bitmap_to_string(attributes, v)
1967 def kerb_EncTypes_string(self, v):
1969 security.KERB_ENCTYPE_DES_CBC_CRC : "DES_CBC_CRC",
1970 security.KERB_ENCTYPE_DES_CBC_MD5 : "DES_CBC_MD5",
1971 security.KERB_ENCTYPE_RC4_HMAC_MD5 : "RC4_HMAC_MD5",
1972 security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96 : "AES128_CTS_HMAC_SHA1_96",
1973 security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96 : "AES256_CTS_HMAC_SHA1_96",
1974 security.KERB_ENCTYPE_FAST_SUPPORTED : "FAST_SUPPORTED",
1975 security.KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED : "COMPOUND_IDENTITY_SUPPORTED",
1976 security.KERB_ENCTYPE_CLAIMS_SUPPORTED : "CLAIMS_SUPPORTED",
1977 security.KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED : "RESOURCE_SID_COMPRESSION_DISABLED",
1979 return self.generic_bitmap_to_string(enctypes, v)
1981 def entry_tln_status(self, e_flags, ):
1983 return "Status[Enabled]"
1986 lsa.LSA_TLN_DISABLED_NEW : "Disabled-New",
1987 lsa.LSA_TLN_DISABLED_ADMIN : "Disabled",
1988 lsa.LSA_TLN_DISABLED_CONFLICT : "Disabled-Conflicting",
1990 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
1992 def entry_dom_status(self, e_flags):
1994 return "Status[Enabled]"
1997 lsa.LSA_SID_DISABLED_ADMIN : "Disabled-SID",
1998 lsa.LSA_SID_DISABLED_CONFLICT : "Disabled-SID-Conflicting",
1999 lsa.LSA_NB_DISABLED_ADMIN : "Disabled-NB",
2000 lsa.LSA_NB_DISABLED_CONFLICT : "Disabled-NB-Conflicting",
2002 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
2004 def write_forest_trust_info(self, fti, tln=None, collisions=None):
2006 tln_string = " TDO[%s]" % tln
2010 self.outf.write("Namespaces[%d]%s:\n" % (
2011 len(fti.entries), tln_string))
2013 for i in xrange(0, len(fti.entries)):
2017 collision_string = ""
2019 if collisions is not None:
2020 for c in collisions.entries:
2024 collision_string = " Collision[%s]" % (c.name.string)
2026 d = e.forest_trust_data
2027 if e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
2028 self.outf.write("TLN: %-32s DNS[*.%s]%s\n" % (
2029 self.entry_tln_status(flags),
2030 d.string, collision_string))
2031 elif e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
2032 self.outf.write("TLN_EX: %-29s DNS[*.%s]\n" % (
2034 elif e.type == lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
2035 self.outf.write("DOM: %-32s DNS[%s] Netbios[%s] SID[%s]%s\n" % (
2036 self.entry_dom_status(flags),
2037 d.dns_domain_name.string,
2038 d.netbios_domain_name.string,
2039 d.domain_sid, collision_string))
2042 class cmd_domain_trust_list(DomainTrustCommand):
2043 """List domain trusts."""
2045 synopsis = "%prog [options]"
2047 takes_optiongroups = {
2048 "sambaopts": options.SambaOptions,
2049 "versionopts": options.VersionOptions,
2050 "localdcopts": LocalDCCredentialsOptions,
2056 def run(self, sambaopts=None, versionopts=None, localdcopts=None):
2058 local_server = self.setup_local_server(sambaopts, localdcopts)
2060 local_netlogon = self.new_local_netlogon_connection()
2061 except RuntimeError as error:
2062 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2065 local_netlogon_trusts = local_netlogon.netr_DsrEnumerateDomainTrusts(local_server,
2066 netlogon.NETR_TRUST_FLAG_IN_FOREST |
2067 netlogon.NETR_TRUST_FLAG_OUTBOUND |
2068 netlogon.NETR_TRUST_FLAG_INBOUND)
2069 except RuntimeError as error:
2070 if self.check_runtime_error(error, werror.WERR_RPC_S_PROCNUM_OUT_OF_RANGE):
2071 # TODO: we could implement a fallback to lsa.EnumTrustDom()
2072 raise CommandError("LOCAL_DC[%s]: netr_DsrEnumerateDomainTrusts not supported." % (
2074 raise self.LocalRuntimeError(self, error, "netr_DsrEnumerateDomainTrusts failed")
2076 a = local_netlogon_trusts.array
2078 if t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
2080 self.outf.write("%-14s %-15s %-19s %s\n" % (
2081 "Type[%s]" % self.netr_DomainTrust_to_type(a, t),
2082 "Transitive[%s]" % self.netr_DomainTrust_to_transitive(t),
2083 "Direction[%s]" % self.netr_DomainTrust_to_direction(t),
2084 "Name[%s]" % self.netr_DomainTrust_to_name(t)))
2087 class cmd_domain_trust_show(DomainTrustCommand):
2088 """Show trusted domain details."""
2090 synopsis = "%prog NAME [options]"
2092 takes_optiongroups = {
2093 "sambaopts": options.SambaOptions,
2094 "versionopts": options.VersionOptions,
2095 "localdcopts": LocalDCCredentialsOptions,
2101 takes_args = ["domain"]
2103 def run(self, domain, sambaopts=None, versionopts=None, localdcopts=None):
2105 local_server = self.setup_local_server(sambaopts, localdcopts)
2107 local_lsa = self.new_local_lsa_connection()
2108 except RuntimeError as error:
2109 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2112 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2113 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2114 except RuntimeError as error:
2115 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2117 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2118 local_lsa_info.name.string,
2119 local_lsa_info.dns_domain.string,
2120 local_lsa_info.sid))
2122 lsaString = lsa.String()
2123 lsaString.string = domain
2125 local_tdo_full = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2126 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2127 local_tdo_info = local_tdo_full.info_ex
2128 local_tdo_posix = local_tdo_full.posix_offset
2129 except NTSTATUSError as error:
2130 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2131 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2133 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(FULL_INFO) failed")
2136 local_tdo_enctypes = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2137 lsaString, lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES)
2138 except NTSTATUSError as error:
2139 if self.check_runtime_error(error, ntstatus.NT_STATUS_INVALID_PARAMETER):
2141 if self.check_runtime_error(error, ntstatus.NT_STATUS_INVALID_INFO_CLASS):
2144 if error is not None:
2145 raise self.LocalRuntimeError(self, error,
2146 "QueryTrustedDomainInfoByName(SUPPORTED_ENCRYPTION_TYPES) failed")
2148 local_tdo_enctypes = lsa.TrustDomainInfoSupportedEncTypes()
2149 local_tdo_enctypes.enc_types = 0
2152 local_tdo_forest = None
2153 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2154 local_tdo_forest = local_lsa.lsaRQueryForestTrustInformation(local_policy,
2155 lsaString, lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
2156 except RuntimeError as error:
2157 if self.check_runtime_error(error, ntstatus.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
2159 if self.check_runtime_error(error, ntstatus.NT_STATUS_NOT_FOUND):
2161 if error is not None:
2162 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation failed")
2164 local_tdo_forest = lsa.ForestTrustInformation()
2165 local_tdo_forest.count = 0
2166 local_tdo_forest.entries = []
2168 self.outf.write("TrusteDomain:\n\n");
2169 self.outf.write("NetbiosName: %s\n" % local_tdo_info.netbios_name.string)
2170 if local_tdo_info.netbios_name.string != local_tdo_info.domain_name.string:
2171 self.outf.write("DnsName: %s\n" % local_tdo_info.domain_name.string)
2172 self.outf.write("SID: %s\n" % local_tdo_info.sid)
2173 self.outf.write("Type: %s\n" % self.trustType_string(local_tdo_info.trust_type))
2174 self.outf.write("Direction: %s\n" % self.trustDirection_string(local_tdo_info.trust_direction))
2175 self.outf.write("Attributes: %s\n" % self.trustAttributes_string(local_tdo_info.trust_attributes))
2176 posix_offset_u32 = ctypes.c_uint32(local_tdo_posix.posix_offset).value
2177 posix_offset_i32 = ctypes.c_int32(local_tdo_posix.posix_offset).value
2178 self.outf.write("PosixOffset: 0x%08X (%d)\n" % (posix_offset_u32, posix_offset_i32))
2179 self.outf.write("kerb_EncTypes: %s\n" % self.kerb_EncTypes_string(local_tdo_enctypes.enc_types))
2181 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2182 self.write_forest_trust_info(local_tdo_forest,
2183 tln=local_tdo_info.domain_name.string)
2187 class cmd_domain_trust_create(DomainTrustCommand):
2188 """Create a domain or forest trust."""
2190 synopsis = "%prog DOMAIN [options]"
2192 takes_optiongroups = {
2193 "sambaopts": options.SambaOptions,
2194 "versionopts": options.VersionOptions,
2195 "credopts": options.CredentialsOptions,
2196 "localdcopts": LocalDCCredentialsOptions,
2200 Option("--type", type="choice", metavar="TYPE",
2201 choices=["external", "forest"],
2202 help="The type of the trust: 'external' or 'forest'.",
2204 default="external"),
2205 Option("--direction", type="choice", metavar="DIRECTION",
2206 choices=["incoming", "outgoing", "both"],
2207 help="The trust direction: 'incoming', 'outgoing' or 'both'.",
2208 dest='trust_direction',
2210 Option("--create-location", type="choice", metavar="LOCATION",
2211 choices=["local", "both"],
2212 help="Where to create the trusted domain object: 'local' or 'both'.",
2213 dest='create_location',
2215 Option("--cross-organisation", action="store_true",
2216 help="The related domains does not belong to the same organisation.",
2217 dest='cross_organisation',
2219 Option("--quarantined", type="choice", metavar="yes|no",
2220 choices=["yes", "no", None],
2221 help="Special SID filtering rules are applied to the trust. "
2222 "With --type=external the default is yes. "
2223 "With --type=forest the default is no.",
2224 dest='quarantined_arg',
2226 Option("--not-transitive", action="store_true",
2227 help="The forest trust is not transitive.",
2228 dest='not_transitive',
2230 Option("--treat-as-external", action="store_true",
2231 help="The treat the forest trust as external.",
2232 dest='treat_as_external',
2234 Option("--no-aes-keys", action="store_false",
2235 help="The trust uses aes kerberos keys.",
2236 dest='use_aes_keys',
2238 Option("--skip-validation", action="store_false",
2239 help="Skip validation of the trust.",
2244 takes_args = ["domain"]
2246 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2247 trust_type=None, trust_direction=None, create_location=None,
2248 cross_organisation=False, quarantined_arg=None,
2249 not_transitive=False, treat_as_external=False,
2250 use_aes_keys=False, validate=True):
2252 lsaString = lsa.String()
2255 if quarantined_arg is None:
2256 if trust_type == 'external':
2258 elif quarantined_arg == 'yes':
2261 if trust_type != 'forest':
2263 raise CommandError("--not-transitive requires --type=forest")
2264 if treat_as_external:
2265 raise CommandError("--treat-as-external requires --type=forest")
2269 enc_types = lsa.TrustDomainInfoSupportedEncTypes()
2270 enc_types.enc_types = security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96
2271 enc_types.enc_types |= security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96
2273 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2274 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2275 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2277 local_trust_info = lsa.TrustDomainInfoInfoEx()
2278 local_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2279 local_trust_info.trust_direction = 0
2280 if trust_direction == "both":
2281 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2282 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2283 elif trust_direction == "incoming":
2284 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2285 elif trust_direction == "outgoing":
2286 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2287 local_trust_info.trust_attributes = 0
2288 if cross_organisation:
2289 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2291 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2292 if trust_type == "forest":
2293 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2295 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2296 if treat_as_external:
2297 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2299 def get_password(name):
2302 if password is not None and password is not '':
2304 password = getpass("New %s Password: " % name)
2305 passwordverify = getpass("Retype %s Password: " % name)
2306 if not password == passwordverify:
2308 self.outf.write("Sorry, passwords do not match.\n")
2310 incoming_secret = None
2311 outgoing_secret = None
2312 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2313 if create_location == "local":
2314 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2315 incoming_password = get_password("Incoming Trust")
2316 incoming_secret = string_to_byte_array(incoming_password.encode('utf-16-le'))
2317 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2318 outgoing_password = get_password("Outgoing Trust")
2319 outgoing_secret = string_to_byte_array(outgoing_password.encode('utf-16-le'))
2321 remote_trust_info = None
2323 # We use 240 random bytes.
2324 # Windows uses 28 or 240 random bytes. I guess it's
2325 # based on the trust type external vs. forest.
2327 # The initial trust password can be up to 512 bytes
2328 # while the versioned passwords used for periodic updates
2329 # can only be up to 498 bytes, as netr_ServerPasswordSet2()
2330 # needs to pass the NL_PASSWORD_VERSION structure within the
2331 # 512 bytes and a 2 bytes confounder is required.
2333 def random_trust_secret(length):
2334 pw = samba.generate_random_machine_password(length/2, length/2)
2335 return string_to_byte_array(pw.encode('utf-16-le'))
2337 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2338 incoming_secret = random_trust_secret(240)
2339 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2340 outgoing_secret = random_trust_secret(240)
2342 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2343 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2345 remote_trust_info = lsa.TrustDomainInfoInfoEx()
2346 remote_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2347 remote_trust_info.trust_direction = 0
2348 if trust_direction == "both":
2349 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2350 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2351 elif trust_direction == "incoming":
2352 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2353 elif trust_direction == "outgoing":
2354 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2355 remote_trust_info.trust_attributes = 0
2356 if cross_organisation:
2357 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2359 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2360 if trust_type == "forest":
2361 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2363 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2364 if treat_as_external:
2365 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2367 local_server = self.setup_local_server(sambaopts, localdcopts)
2369 local_lsa = self.new_local_lsa_connection()
2370 except RuntimeError as error:
2371 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2374 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2375 except RuntimeError as error:
2376 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2378 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2379 local_lsa_info.name.string,
2380 local_lsa_info.dns_domain.string,
2381 local_lsa_info.sid))
2384 remote_server = self.setup_remote_server(credopts, domain)
2385 except RuntimeError as error:
2386 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2389 remote_lsa = self.new_remote_lsa_connection()
2390 except RuntimeError as error:
2391 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2394 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2395 except RuntimeError as error:
2396 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2398 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2399 remote_lsa_info.name.string,
2400 remote_lsa_info.dns_domain.string,
2401 remote_lsa_info.sid))
2403 local_trust_info.domain_name.string = remote_lsa_info.dns_domain.string
2404 local_trust_info.netbios_name.string = remote_lsa_info.name.string
2405 local_trust_info.sid = remote_lsa_info.sid
2407 if remote_trust_info:
2408 remote_trust_info.domain_name.string = local_lsa_info.dns_domain.string
2409 remote_trust_info.netbios_name.string = local_lsa_info.name.string
2410 remote_trust_info.sid = local_lsa_info.sid
2413 lsaString.string = local_trust_info.domain_name.string
2414 local_old_netbios = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2415 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2416 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2417 except NTSTATUSError as error:
2418 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2419 raise self.LocalRuntimeError(self, error,
2420 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2424 lsaString.string = local_trust_info.netbios_name.string
2425 local_old_dns = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2426 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2427 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2428 except NTSTATUSError as error:
2429 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2430 raise self.LocalRuntimeError(self, error,
2431 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2434 if remote_trust_info:
2436 lsaString.string = remote_trust_info.domain_name.string
2437 remote_old_netbios = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2438 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2439 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2440 except NTSTATUSError as error:
2441 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2442 raise self.RemoteRuntimeError(self, error,
2443 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2447 lsaString.string = remote_trust_info.netbios_name.string
2448 remote_old_dns = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2449 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2450 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2451 except NTSTATUSError as error:
2452 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2453 raise self.RemoteRuntimeError(self, error,
2454 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2458 local_netlogon = self.new_local_netlogon_connection()
2459 except RuntimeError as error:
2460 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2463 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
2464 except RuntimeError as error:
2465 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
2467 if remote_trust_info:
2469 remote_netlogon = self.new_remote_netlogon_connection()
2470 except RuntimeError as error:
2471 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2474 remote_netlogon_info = self.get_netlogon_dc_info(remote_netlogon, remote_server)
2475 except RuntimeError as error:
2476 raise self.RemoteRuntimeError(self, error, "failed to get netlogon dc info")
2478 def generate_AuthInOutBlob(secret, update_time):
2480 blob = drsblobs.trustAuthInOutBlob()
2485 clear = drsblobs.AuthInfoClear()
2486 clear.size = len(secret)
2487 clear.password = secret
2489 info = drsblobs.AuthenticationInformation()
2490 info.LastUpdateTime = samba.unix2nttime(update_time)
2491 info.AuthType = lsa.TRUST_AUTH_TYPE_CLEAR
2492 info.AuthInfo = clear
2494 array = drsblobs.AuthenticationInformationArray()
2496 array.array = [info]
2498 blob = drsblobs.trustAuthInOutBlob()
2500 blob.current = array
2504 def generate_AuthInfoInternal(session_key, incoming=None, outgoing=None):
2505 confounder = [0] * 512
2506 for i in range(len(confounder)):
2507 confounder[i] = random.randint(0, 255)
2509 trustpass = drsblobs.trustDomainPasswords()
2511 trustpass.confounder = confounder
2512 trustpass.outgoing = outgoing
2513 trustpass.incoming = incoming
2515 trustpass_blob = ndr_pack(trustpass)
2517 encrypted_trustpass = arcfour_encrypt(session_key, trustpass_blob)
2519 auth_blob = lsa.DATA_BUF2()
2520 auth_blob.size = len(encrypted_trustpass)
2521 auth_blob.data = string_to_byte_array(encrypted_trustpass)
2523 auth_info = lsa.TrustDomainInfoAuthInfoInternal()
2524 auth_info.auth_blob = auth_blob
2528 update_time = samba.current_unix_time()
2529 incoming_blob = generate_AuthInOutBlob(incoming_secret, update_time)
2530 outgoing_blob = generate_AuthInOutBlob(outgoing_secret, update_time)
2532 local_tdo_handle = None
2533 remote_tdo_handle = None
2535 local_auth_info = generate_AuthInfoInternal(local_lsa.session_key,
2536 incoming=incoming_blob,
2537 outgoing=outgoing_blob)
2538 if remote_trust_info:
2539 remote_auth_info = generate_AuthInfoInternal(remote_lsa.session_key,
2540 incoming=outgoing_blob,
2541 outgoing=incoming_blob)
2544 if remote_trust_info:
2545 self.outf.write("Creating remote TDO.\n")
2546 current_request = { "location": "remote", "name": "CreateTrustedDomainEx2"}
2547 remote_tdo_handle = remote_lsa.CreateTrustedDomainEx2(remote_policy,
2550 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2551 self.outf.write("Remote TDO created.\n")
2553 self.outf.write("Setting supported encryption types on remote TDO.\n")
2554 current_request = { "location": "remote", "name": "SetInformationTrustedDomain"}
2555 remote_lsa.SetInformationTrustedDomain(remote_tdo_handle,
2556 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2559 self.outf.write("Creating local TDO.\n")
2560 current_request = { "location": "local", "name": "CreateTrustedDomainEx2"}
2561 local_tdo_handle = local_lsa.CreateTrustedDomainEx2(local_policy,
2564 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2565 self.outf.write("Local TDO created\n")
2567 self.outf.write("Setting supported encryption types on local TDO.\n")
2568 current_request = { "location": "local", "name": "SetInformationTrustedDomain"}
2569 local_lsa.SetInformationTrustedDomain(local_tdo_handle,
2570 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2572 except RuntimeError as error:
2573 self.outf.write("Error: %s failed %sly - cleaning up\n" % (
2574 current_request['name'], current_request['location']))
2575 if remote_tdo_handle:
2576 self.outf.write("Deleting remote TDO.\n")
2577 remote_lsa.DeleteObject(remote_tdo_handle)
2578 remote_tdo_handle = None
2579 if local_tdo_handle:
2580 self.outf.write("Deleting local TDO.\n")
2581 local_lsa.DeleteObject(local_tdo_handle)
2582 local_tdo_handle = None
2583 if current_request['location'] is "remote":
2584 raise self.RemoteRuntimeError(self, error, "%s" % (
2585 current_request['name']))
2586 raise self.LocalRuntimeError(self, error, "%s" % (
2587 current_request['name']))
2590 if local_trust_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2591 self.outf.write("Setup local forest trust information...\n")
2593 # get all information about the remote trust
2594 # this triggers netr_GetForestTrustInformation to the remote domain
2595 # and lsaRSetForestTrustInformation() locally, but new top level
2596 # names are disabled by default.
2597 local_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
2598 remote_lsa_info.dns_domain.string,
2599 netlogon.DS_GFTI_UPDATE_TDO)
2600 except RuntimeError as error:
2601 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2604 # here we try to enable all top level names
2605 local_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
2606 remote_lsa_info.dns_domain,
2607 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2610 except RuntimeError as error:
2611 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2613 self.write_forest_trust_info(local_forest_info,
2614 tln=remote_lsa_info.dns_domain.string,
2615 collisions=local_forest_collision)
2617 if remote_trust_info:
2618 self.outf.write("Setup remote forest trust information...\n")
2620 # get all information about the local trust (from the perspective of the remote domain)
2621 # this triggers netr_GetForestTrustInformation to our domain.
2622 # and lsaRSetForestTrustInformation() remotely, but new top level
2623 # names are disabled by default.
2624 remote_forest_info = remote_netlogon.netr_DsRGetForestTrustInformation(remote_netlogon_info.dc_unc,
2625 local_lsa_info.dns_domain.string,
2626 netlogon.DS_GFTI_UPDATE_TDO)
2627 except RuntimeError as error:
2628 raise self.RemoteRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2631 # here we try to enable all top level names
2632 remote_forest_collision = remote_lsa.lsaRSetForestTrustInformation(remote_policy,
2633 local_lsa_info.dns_domain,
2634 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2637 except RuntimeError as error:
2638 raise self.RemoteRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2640 self.write_forest_trust_info(remote_forest_info,
2641 tln=local_lsa_info.dns_domain.string,
2642 collisions=remote_forest_collision)
2644 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2645 self.outf.write("Validating outgoing trust...\n")
2647 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_netlogon_info.dc_unc,
2648 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2650 remote_lsa_info.dns_domain.string)
2651 except RuntimeError as error:
2652 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2654 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2655 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2657 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2658 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2659 local_trust_verify.trusted_dc_name,
2660 local_trust_verify.tc_connection_status[1],
2661 local_trust_verify.pdc_connection_status[1])
2663 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2664 local_trust_verify.trusted_dc_name,
2665 local_trust_verify.tc_connection_status[1],
2666 local_trust_verify.pdc_connection_status[1])
2668 if local_trust_status != werror.WERR_SUCCESS or local_conn_status != werror.WERR_SUCCESS:
2669 raise CommandError(local_validation)
2671 self.outf.write("OK: %s\n" % local_validation)
2673 if remote_trust_info:
2674 if remote_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2675 self.outf.write("Validating incoming trust...\n")
2677 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_netlogon_info.dc_unc,
2678 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2680 local_lsa_info.dns_domain.string)
2681 except RuntimeError as error:
2682 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2684 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2685 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2687 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2688 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2689 remote_trust_verify.trusted_dc_name,
2690 remote_trust_verify.tc_connection_status[1],
2691 remote_trust_verify.pdc_connection_status[1])
2693 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2694 remote_trust_verify.trusted_dc_name,
2695 remote_trust_verify.tc_connection_status[1],
2696 remote_trust_verify.pdc_connection_status[1])
2698 if remote_trust_status != werror.WERR_SUCCESS or remote_conn_status != werror.WERR_SUCCESS:
2699 raise CommandError(remote_validation)
2701 self.outf.write("OK: %s\n" % remote_validation)
2703 if remote_tdo_handle is not None:
2705 remote_lsa.Close(remote_tdo_handle)
2706 except RuntimeError as error:
2708 remote_tdo_handle = None
2709 if local_tdo_handle is not None:
2711 local_lsa.Close(local_tdo_handle)
2712 except RuntimeError as error:
2714 local_tdo_handle = None
2716 self.outf.write("Success.\n")
2719 class cmd_domain_trust_delete(DomainTrustCommand):
2720 """Delete a domain trust."""
2722 synopsis = "%prog DOMAIN [options]"
2724 takes_optiongroups = {
2725 "sambaopts": options.SambaOptions,
2726 "versionopts": options.VersionOptions,
2727 "credopts": options.CredentialsOptions,
2728 "localdcopts": LocalDCCredentialsOptions,
2732 Option("--delete-location", type="choice", metavar="LOCATION",
2733 choices=["local", "both"],
2734 help="Where to delete the trusted domain object: 'local' or 'both'.",
2735 dest='delete_location',
2739 takes_args = ["domain"]
2741 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2742 delete_location=None):
2744 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2745 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2746 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2748 if delete_location == "local":
2749 remote_policy_access = None
2751 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2752 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2753 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2755 local_server = self.setup_local_server(sambaopts, localdcopts)
2757 local_lsa = self.new_local_lsa_connection()
2758 except RuntimeError as error:
2759 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2762 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2763 except RuntimeError as error:
2764 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2766 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2767 local_lsa_info.name.string,
2768 local_lsa_info.dns_domain.string,
2769 local_lsa_info.sid))
2771 local_tdo_info = None
2772 local_tdo_handle = None
2773 remote_tdo_info = None
2774 remote_tdo_handle = None
2776 lsaString = lsa.String()
2778 lsaString.string = domain
2779 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2780 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2781 except NTSTATUSError as error:
2782 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2783 raise CommandError("Failed to find trust for domain '%s'" % domain)
2784 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2787 if remote_policy_access is not None:
2789 remote_server = self.setup_remote_server(credopts, domain)
2790 except RuntimeError as error:
2791 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2794 remote_lsa = self.new_remote_lsa_connection()
2795 except RuntimeError as error:
2796 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2799 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2800 except RuntimeError as error:
2801 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2803 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2804 remote_lsa_info.name.string,
2805 remote_lsa_info.dns_domain.string,
2806 remote_lsa_info.sid))
2808 if remote_lsa_info.sid != local_tdo_info.sid or \
2809 remote_lsa_info.name.string != local_tdo_info.netbios_name.string or \
2810 remote_lsa_info.dns_domain.string != local_tdo_info.domain_name.string:
2811 raise CommandError("LocalTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2812 local_tdo_info.netbios_name.string,
2813 local_tdo_info.domain_name.string,
2814 local_tdo_info.sid))
2817 lsaString.string = local_lsa_info.dns_domain.string
2818 remote_tdo_info = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2819 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2820 except NTSTATUSError as error:
2821 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2822 raise self.RemoteRuntimeError(self, error, "QueryTrustedDomainInfoByName(%s)" % (
2826 if remote_tdo_info is not None:
2827 if local_lsa_info.sid != remote_tdo_info.sid or \
2828 local_lsa_info.name.string != remote_tdo_info.netbios_name.string or \
2829 local_lsa_info.dns_domain.string != remote_tdo_info.domain_name.string:
2830 raise CommandError("RemoteTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2831 remote_tdo_info.netbios_name.string,
2832 remote_tdo_info.domain_name.string,
2833 remote_tdo_info.sid))
2835 if local_tdo_info is not None:
2837 lsaString.string = local_tdo_info.domain_name.string
2838 local_tdo_handle = local_lsa.OpenTrustedDomainByName(local_policy,
2840 security.SEC_STD_DELETE)
2841 except RuntimeError as error:
2842 raise self.LocalRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2845 local_lsa.DeleteObject(local_tdo_handle)
2846 local_tdo_handle = None
2848 if remote_tdo_info is not None:
2850 lsaString.string = remote_tdo_info.domain_name.string
2851 remote_tdo_handle = remote_lsa.OpenTrustedDomainByName(remote_policy,
2853 security.SEC_STD_DELETE)
2854 except RuntimeError as error:
2855 raise self.RemoteRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2858 if remote_tdo_handle is not None:
2860 remote_lsa.DeleteObject(remote_tdo_handle)
2861 remote_tdo_handle = None
2862 self.outf.write("RemoteTDO deleted.\n")
2863 except RuntimeError as error:
2864 self.outf.write("%s\n" % self.RemoteRuntimeError(self, error, "DeleteObject() failed"))
2866 if local_tdo_handle is not None:
2868 local_lsa.DeleteObject(local_tdo_handle)
2869 local_tdo_handle = None
2870 self.outf.write("LocalTDO deleted.\n")
2871 except RuntimeError as error:
2872 self.outf.write("%s\n" % self.LocalRuntimeError(self, error, "DeleteObject() failed"))
2876 class cmd_domain_trust_validate(DomainTrustCommand):
2877 """Validate a domain trust."""
2879 synopsis = "%prog DOMAIN [options]"
2881 takes_optiongroups = {
2882 "sambaopts": options.SambaOptions,
2883 "versionopts": options.VersionOptions,
2884 "credopts": options.CredentialsOptions,
2885 "localdcopts": LocalDCCredentialsOptions,
2889 Option("--validate-location", type="choice", metavar="LOCATION",
2890 choices=["local", "both"],
2891 help="Where to validate the trusted domain object: 'local' or 'both'.",
2892 dest='validate_location',
2896 takes_args = ["domain"]
2898 def run(self, domain, sambaopts=None, versionopts=None, credopts=None, localdcopts=None,
2899 validate_location=None):
2901 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2903 local_server = self.setup_local_server(sambaopts, localdcopts)
2905 local_lsa = self.new_local_lsa_connection()
2906 except RuntimeError as error:
2907 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2910 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2911 except RuntimeError as error:
2912 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2914 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2915 local_lsa_info.name.string,
2916 local_lsa_info.dns_domain.string,
2917 local_lsa_info.sid))
2920 lsaString = lsa.String()
2921 lsaString.string = domain
2922 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2923 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2924 except NTSTATUSError as error:
2925 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2926 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2928 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
2930 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
2931 local_tdo_info.netbios_name.string,
2932 local_tdo_info.domain_name.string,
2933 local_tdo_info.sid))
2936 local_netlogon = self.new_local_netlogon_connection()
2937 except RuntimeError as error:
2938 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2941 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_server,
2942 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2944 local_tdo_info.domain_name.string)
2945 except RuntimeError as error:
2946 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2948 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2949 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2951 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2952 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2953 local_trust_verify.trusted_dc_name,
2954 local_trust_verify.tc_connection_status[1],
2955 local_trust_verify.pdc_connection_status[1])
2957 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2958 local_trust_verify.trusted_dc_name,
2959 local_trust_verify.tc_connection_status[1],
2960 local_trust_verify.pdc_connection_status[1])
2962 if local_trust_status != werror.WERR_SUCCESS or local_conn_status != werror.WERR_SUCCESS:
2963 raise CommandError(local_validation)
2965 self.outf.write("OK: %s\n" % local_validation)
2968 server = local_trust_verify.trusted_dc_name.replace('\\', '')
2969 domain_and_server = "%s\\%s" % (local_tdo_info.domain_name.string, server)
2970 local_trust_rediscover = local_netlogon.netr_LogonControl2Ex(local_server,
2971 netlogon.NETLOGON_CONTROL_REDISCOVER,
2974 except RuntimeError as error:
2975 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
2977 local_conn_status = self._uint32(local_trust_rediscover.tc_connection_status[0])
2978 local_rediscover = "LocalRediscover: DC[%s] CONNECTION[%s]" % (
2979 local_trust_rediscover.trusted_dc_name,
2980 local_trust_rediscover.tc_connection_status[1])
2982 if local_conn_status != werror.WERR_SUCCESS:
2983 raise CommandError(local_rediscover)
2985 self.outf.write("OK: %s\n" % local_rediscover)
2987 if validate_location != "local":
2989 remote_server = self.setup_remote_server(credopts, domain, require_pdc=False)
2990 except RuntimeError as error:
2991 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2994 remote_netlogon = self.new_remote_netlogon_connection()
2995 except RuntimeError as error:
2996 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2999 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_server,
3000 netlogon.NETLOGON_CONTROL_TC_VERIFY,
3002 local_lsa_info.dns_domain.string)
3003 except RuntimeError as error:
3004 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
3006 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
3007 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
3009 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
3010 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
3011 remote_trust_verify.trusted_dc_name,
3012 remote_trust_verify.tc_connection_status[1],
3013 remote_trust_verify.pdc_connection_status[1])
3015 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
3016 remote_trust_verify.trusted_dc_name,
3017 remote_trust_verify.tc_connection_status[1],
3018 remote_trust_verify.pdc_connection_status[1])
3020 if remote_trust_status != werror.WERR_SUCCESS or remote_conn_status != werror.WERR_SUCCESS:
3021 raise CommandError(remote_validation)
3023 self.outf.write("OK: %s\n" % remote_validation)
3026 server = remote_trust_verify.trusted_dc_name.replace('\\', '')
3027 domain_and_server = "%s\\%s" % (local_lsa_info.dns_domain.string, server)
3028 remote_trust_rediscover = remote_netlogon.netr_LogonControl2Ex(remote_server,
3029 netlogon.NETLOGON_CONTROL_REDISCOVER,
3032 except RuntimeError as error:
3033 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
3035 remote_conn_status = self._uint32(remote_trust_rediscover.tc_connection_status[0])
3037 remote_rediscover = "RemoteRediscover: DC[%s] CONNECTION[%s]" % (
3038 remote_trust_rediscover.trusted_dc_name,
3039 remote_trust_rediscover.tc_connection_status[1])
3041 if remote_conn_status != werror.WERR_SUCCESS:
3042 raise CommandError(remote_rediscover)
3044 self.outf.write("OK: %s\n" % remote_rediscover)
3048 class cmd_domain_trust_namespaces(DomainTrustCommand):
3049 """Manage forest trust namespaces."""
3051 synopsis = "%prog [DOMAIN] [options]"
3053 takes_optiongroups = {
3054 "sambaopts": options.SambaOptions,
3055 "versionopts": options.VersionOptions,
3056 "localdcopts": LocalDCCredentialsOptions,
3060 Option("--refresh", type="choice", metavar="check|store",
3061 choices=["check", "store", None],
3062 help="List and maybe store refreshed forest trust information: 'check' or 'store'.",
3065 Option("--enable-all", action="store_true",
3066 help="Try to update disabled entries, not allowed with --refresh=check.",
3069 Option("--enable-tln", action="append", metavar='DNSDOMAIN',
3070 help="Enable a top level name entry. Can be specified multiple times.",
3073 Option("--disable-tln", action="append", metavar='DNSDOMAIN',
3074 help="Disable a top level name entry. Can be specified multiple times.",
3077 Option("--add-tln-ex", action="append", metavar='DNSDOMAIN',
3078 help="Add a top level exclusion entry. Can be specified multiple times.",
3081 Option("--delete-tln-ex", action="append", metavar='DNSDOMAIN',
3082 help="Delete a top level exclusion entry. Can be specified multiple times.",
3083 dest='delete_tln_ex',
3085 Option("--enable-nb", action="append", metavar='NETBIOSDOMAIN',
3086 help="Enable a netbios name in a domain entry. Can be specified multiple times.",
3089 Option("--disable-nb", action="append", metavar='NETBIOSDOMAIN',
3090 help="Disable a netbios name in a domain entry. Can be specified multiple times.",
3093 Option("--enable-sid", action="append", metavar='DOMAINSID',
3094 help="Enable a SID in a domain entry. Can be specified multiple times.",
3095 dest='enable_sid_str',
3097 Option("--disable-sid", action="append", metavar='DOMAINSID',
3098 help="Disable a SID in a domain entry. Can be specified multiple times.",
3099 dest='disable_sid_str',
3101 Option("--add-upn-suffix", action="append", metavar='DNSDOMAIN',
3102 help="Add a new uPNSuffixes attribute for the local forest. Can be specified multiple times.",
3105 Option("--delete-upn-suffix", action="append", metavar='DNSDOMAIN',
3106 help="Delete an existing uPNSuffixes attribute of the local forest. Can be specified multiple times.",
3109 Option("--add-spn-suffix", action="append", metavar='DNSDOMAIN',
3110 help="Add a new msDS-SPNSuffixes attribute for the local forest. Can be specified multiple times.",
3113 Option("--delete-spn-suffix", action="append", metavar='DNSDOMAIN',
3114 help="Delete an existing msDS-SPNSuffixes attribute of the local forest. Can be specified multiple times.",
3119 takes_args = ["domain?"]
3121 def run(self, domain=None, sambaopts=None, localdcopts=None, versionopts=None,
3122 refresh=None, enable_all=False,
3123 enable_tln=[], disable_tln=[], add_tln_ex=[], delete_tln_ex=[],
3124 enable_sid_str=[], disable_sid_str=[], enable_nb=[], disable_nb=[],
3125 add_upn=[], delete_upn=[], add_spn=[], delete_spn=[]):
3127 require_update = False
3130 if refresh == "store":
3131 raise CommandError("--refresh=%s not allowed without DOMAIN" % refresh)
3134 raise CommandError("--enable-all not allowed without DOMAIN")
3136 if len(enable_tln) > 0:
3137 raise CommandError("--enable-tln not allowed without DOMAIN")
3138 if len(disable_tln) > 0:
3139 raise CommandError("--disable-tln not allowed without DOMAIN")
3141 if len(add_tln_ex) > 0:
3142 raise CommandError("--add-tln-ex not allowed without DOMAIN")
3143 if len(delete_tln_ex) > 0:
3144 raise CommandError("--delete-tln-ex not allowed without DOMAIN")
3146 if len(enable_nb) > 0:
3147 raise CommandError("--enable-nb not allowed without DOMAIN")
3148 if len(disable_nb) > 0:
3149 raise CommandError("--disable-nb not allowed without DOMAIN")
3151 if len(enable_sid_str) > 0:
3152 raise CommandError("--enable-sid not allowed without DOMAIN")
3153 if len(disable_sid_str) > 0:
3154 raise CommandError("--disable-sid not allowed without DOMAIN")
3156 if len(add_upn) > 0:
3158 if not n.startswith("*."):
3160 raise CommandError("value[%s] specified for --add-upn-suffix should not include with '*.'" % n)
3161 require_update = True
3162 if len(delete_upn) > 0:
3163 for n in delete_upn:
3164 if not n.startswith("*."):
3166 raise CommandError("value[%s] specified for --delete-upn-suffix should not include with '*.'" % n)
3167 require_update = True
3169 for d in delete_upn:
3170 if a.lower() != d.lower():
3172 raise CommandError("value[%s] specified for --add-upn-suffix and --delete-upn-suffix" % a)
3174 if len(add_spn) > 0:
3176 if not n.startswith("*."):
3178 raise CommandError("value[%s] specified for --add-spn-suffix should not include with '*.'" % n)
3179 require_update = True
3180 if len(delete_spn) > 0:
3181 for n in delete_spn:
3182 if not n.startswith("*."):
3184 raise CommandError("value[%s] specified for --delete-spn-suffix should not include with '*.'" % n)
3185 require_update = True
3187 for d in delete_spn:
3188 if a.lower() != d.lower():
3190 raise CommandError("value[%s] specified for --add-spn-suffix and --delete-spn-suffix" % a)
3192 if len(add_upn) > 0:
3193 raise CommandError("--add-upn-suffix not allowed together with DOMAIN")
3194 if len(delete_upn) > 0:
3195 raise CommandError("--delete-upn-suffix not allowed together with DOMAIN")
3196 if len(add_spn) > 0:
3197 raise CommandError("--add-spn-suffix not allowed together with DOMAIN")
3198 if len(delete_spn) > 0:
3199 raise CommandError("--delete-spn-suffix not allowed together with DOMAIN")
3201 if refresh is not None:
3202 if refresh == "store":
3203 require_update = True
3205 if enable_all and refresh != "store":
3206 raise CommandError("--enable-all not allowed together with --refresh=%s" % refresh)
3208 if len(enable_tln) > 0:
3209 raise CommandError("--enable-tln not allowed together with --refresh")
3210 if len(disable_tln) > 0:
3211 raise CommandError("--disable-tln not allowed together with --refresh")
3213 if len(add_tln_ex) > 0:
3214 raise CommandError("--add-tln-ex not allowed together with --refresh")
3215 if len(delete_tln_ex) > 0:
3216 raise CommandError("--delete-tln-ex not allowed together with --refresh")
3218 if len(enable_nb) > 0:
3219 raise CommandError("--enable-nb not allowed together with --refresh")
3220 if len(disable_nb) > 0:
3221 raise CommandError("--disable-nb not allowed together with --refresh")
3223 if len(enable_sid_str) > 0:
3224 raise CommandError("--enable-sid not allowed together with --refresh")
3225 if len(disable_sid_str) > 0:
3226 raise CommandError("--disable-sid not allowed together with --refresh")
3229 require_update = True
3231 if len(enable_tln) > 0:
3232 raise CommandError("--enable-tln not allowed together with --enable-all")
3234 if len(enable_nb) > 0:
3235 raise CommandError("--enable-nb not allowed together with --enable-all")
3237 if len(enable_sid_str) > 0:
3238 raise CommandError("--enable-sid not allowed together with --enable-all")
3240 if len(enable_tln) > 0:
3241 require_update = True
3242 if len(disable_tln) > 0:
3243 require_update = True
3244 for e in enable_tln:
3245 for d in disable_tln:
3246 if e.lower() != d.lower():
3248 raise CommandError("value[%s] specified for --enable-tln and --disable-tln" % e)
3250 if len(add_tln_ex) > 0:
3251 for n in add_tln_ex:
3252 if not n.startswith("*."):
3254 raise CommandError("value[%s] specified for --add-tln-ex should not include with '*.'" % n)
3255 require_update = True
3256 if len(delete_tln_ex) > 0:
3257 for n in delete_tln_ex:
3258 if not n.startswith("*."):
3260 raise CommandError("value[%s] specified for --delete-tln-ex should not include with '*.'" % n)
3261 require_update = True
3262 for a in add_tln_ex:
3263 for d in delete_tln_ex:
3264 if a.lower() != d.lower():
3266 raise CommandError("value[%s] specified for --add-tln-ex and --delete-tln-ex" % a)
3268 if len(enable_nb) > 0:
3269 require_update = True
3270 if len(disable_nb) > 0:
3271 require_update = True
3273 for d in disable_nb:
3274 if e.upper() != d.upper():
3276 raise CommandError("value[%s] specified for --enable-nb and --disable-nb" % e)
3279 for s in enable_sid_str:
3281 sid = security.dom_sid(s)
3282 except TypeError as error:
3283 raise CommandError("value[%s] specified for --enable-sid is not a valid SID" % s)
3284 enable_sid.append(sid)
3286 for s in disable_sid_str:
3288 sid = security.dom_sid(s)
3289 except TypeError as error:
3290 raise CommandError("value[%s] specified for --disable-sid is not a valid SID" % s)
3291 disable_sid.append(sid)
3292 if len(enable_sid) > 0:
3293 require_update = True
3294 if len(disable_sid) > 0:
3295 require_update = True
3296 for e in enable_sid:
3297 for d in disable_sid:
3300 raise CommandError("value[%s] specified for --enable-sid and --disable-sid" % e)
3302 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
3304 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
3306 local_server = self.setup_local_server(sambaopts, localdcopts)
3308 local_lsa = self.new_local_lsa_connection()
3309 except RuntimeError as error:
3310 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
3313 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
3314 except RuntimeError as error:
3315 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
3317 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
3318 local_lsa_info.name.string,
3319 local_lsa_info.dns_domain.string,
3320 local_lsa_info.sid))
3324 local_netlogon = self.new_local_netlogon_connection()
3325 except RuntimeError as error:
3326 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3329 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3330 except RuntimeError as error:
3331 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3333 if local_netlogon_info.domain_name != local_netlogon_info.forest_name:
3334 raise CommandError("The local domain [%s] is not the forest root [%s]" % (
3335 local_netlogon_info.domain_name,
3336 local_netlogon_info.forest_name))
3339 # get all information about our own forest
3340 own_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3342 except RuntimeError as error:
3343 if self.check_runtime_error(error, werror.WERR_RPC_S_PROCNUM_OUT_OF_RANGE):
3344 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3347 if self.check_runtime_error(error, werror.WERR_INVALID_FUNCTION):
3348 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3351 if self.check_runtime_error(error, werror.WERR_NERR_ACFNOTLOADED):
3352 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3355 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3357 self.outf.write("Own forest trust information...\n")
3358 self.write_forest_trust_info(own_forest_info,
3359 tln=local_lsa_info.dns_domain.string)
3362 local_samdb = self.new_local_ldap_connection()
3363 except RuntimeError as error:
3364 raise self.LocalRuntimeError(self, error, "failed to connect to SamDB")
3366 local_partitions_dn = "CN=Partitions,%s" % str(local_samdb.get_config_basedn())
3367 attrs = ['uPNSuffixes', 'msDS-SPNSuffixes']
3369 msgs = local_samdb.search(base=local_partitions_dn,
3370 scope=ldb.SCOPE_BASE,
3371 expression="(objectClass=crossRefContainer)",
3373 stored_msg = msgs[0]
3374 except ldb.LdbError as error:
3375 raise self.LocalLdbError(self, error, "failed to search partition dn")
3377 stored_upn_vals = []
3378 if 'uPNSuffixes' in stored_msg:
3379 stored_upn_vals.extend(stored_msg['uPNSuffixes'])
3381 stored_spn_vals = []
3382 if 'msDS-SPNSuffixes' in stored_msg:
3383 stored_spn_vals.extend(stored_msg['msDS-SPNSuffixes'])
3385 self.outf.write("Stored uPNSuffixes attributes[%d]:\n" % len(stored_upn_vals))
3386 for v in stored_upn_vals:
3387 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3388 self.outf.write("Stored msDS-SPNSuffixes attributes[%d]:\n" % len(stored_spn_vals))
3389 for v in stored_spn_vals:
3390 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3392 if not require_update:
3396 update_upn_vals = []
3397 update_upn_vals.extend(stored_upn_vals)
3400 update_spn_vals = []
3401 update_spn_vals.extend(stored_spn_vals)
3405 for i in xrange(0, len(update_upn_vals)):
3406 v = update_upn_vals[i]
3407 if v.lower() != upn.lower():
3412 raise CommandError("Entry already present for value[%s] specified for --add-upn-suffix" % upn)
3413 update_upn_vals.append(upn)
3416 for upn in delete_upn:
3418 for i in xrange(0, len(update_upn_vals)):
3419 v = update_upn_vals[i]
3420 if v.lower() != upn.lower():
3425 raise CommandError("Entry not found for value[%s] specified for --delete-upn-suffix" % upn)
3427 update_upn_vals.pop(idx)
3432 for i in xrange(0, len(update_spn_vals)):
3433 v = update_spn_vals[i]
3434 if v.lower() != spn.lower():
3439 raise CommandError("Entry already present for value[%s] specified for --add-spn-suffix" % spn)
3440 update_spn_vals.append(spn)
3443 for spn in delete_spn:
3445 for i in xrange(0, len(update_spn_vals)):
3446 v = update_spn_vals[i]
3447 if v.lower() != spn.lower():
3452 raise CommandError("Entry not found for value[%s] specified for --delete-spn-suffix" % spn)
3454 update_spn_vals.pop(idx)
3457 self.outf.write("Update uPNSuffixes attributes[%d]:\n" % len(update_upn_vals))
3458 for v in update_upn_vals:
3459 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3460 self.outf.write("Update msDS-SPNSuffixes attributes[%d]:\n" % len(update_spn_vals))
3461 for v in update_spn_vals:
3462 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3464 update_msg = ldb.Message()
3465 update_msg.dn = stored_msg.dn
3468 update_msg['uPNSuffixes'] = ldb.MessageElement(update_upn_vals,
3469 ldb.FLAG_MOD_REPLACE,
3472 update_msg['msDS-SPNSuffixes'] = ldb.MessageElement(update_spn_vals,
3473 ldb.FLAG_MOD_REPLACE,
3476 local_samdb.modify(update_msg)
3477 except ldb.LdbError as error:
3478 raise self.LocalLdbError(self, error, "failed to update partition dn")
3481 stored_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3483 except RuntimeError as error:
3484 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3486 self.outf.write("Stored forest trust information...\n")
3487 self.write_forest_trust_info(stored_forest_info,
3488 tln=local_lsa_info.dns_domain.string)
3492 lsaString = lsa.String()
3493 lsaString.string = domain
3494 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
3495 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
3496 except NTSTATUSError as error:
3497 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
3498 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
3500 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
3502 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
3503 local_tdo_info.netbios_name.string,
3504 local_tdo_info.domain_name.string,
3505 local_tdo_info.sid))
3507 if not local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
3508 raise CommandError("trusted domain object for domain [%s] is not marked as FOREST_TRANSITIVE." % domain)
3510 if refresh is not None:
3512 local_netlogon = self.new_local_netlogon_connection()
3513 except RuntimeError as error:
3514 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3517 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3518 except RuntimeError as error:
3519 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3521 lsa_update_check = 1
3522 if refresh == "store":
3523 netlogon_update_tdo = netlogon.DS_GFTI_UPDATE_TDO
3525 lsa_update_check = 0
3527 netlogon_update_tdo = 0
3530 # get all information about the remote trust
3531 # this triggers netr_GetForestTrustInformation to the remote domain
3532 # and lsaRSetForestTrustInformation() locally, but new top level
3533 # names are disabled by default.
3534 fresh_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3535 local_tdo_info.domain_name.string,
3536 netlogon_update_tdo)
3537 except RuntimeError as error:
3538 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3541 fresh_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3542 local_tdo_info.domain_name,
3543 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3546 except RuntimeError as error:
3547 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3549 self.outf.write("Fresh forest trust information...\n")
3550 self.write_forest_trust_info(fresh_forest_info,
3551 tln=local_tdo_info.domain_name.string,
3552 collisions=fresh_forest_collision)
3554 if refresh == "store":
3556 lsaString = lsa.String()
3557 lsaString.string = local_tdo_info.domain_name.string
3558 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3560 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3561 except RuntimeError as error:
3562 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3564 self.outf.write("Stored forest trust information...\n")
3565 self.write_forest_trust_info(stored_forest_info,
3566 tln=local_tdo_info.domain_name.string)
3571 # The none --refresh path
3575 lsaString = lsa.String()
3576 lsaString.string = local_tdo_info.domain_name.string
3577 local_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3579 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3580 except RuntimeError as error:
3581 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3583 self.outf.write("Local forest trust information...\n")
3584 self.write_forest_trust_info(local_forest_info,
3585 tln=local_tdo_info.domain_name.string)
3587 if not require_update:
3591 entries.extend(local_forest_info.entries)
3592 update_forest_info = lsa.ForestTrustInformation()
3593 update_forest_info.count = len(entries)
3594 update_forest_info.entries = entries
3597 for i in xrange(0, len(update_forest_info.entries)):
3598 r = update_forest_info.entries[i]
3599 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3601 if update_forest_info.entries[i].flags == 0:
3603 update_forest_info.entries[i].time = 0
3604 update_forest_info.entries[i].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3605 for i in xrange(0, len(update_forest_info.entries)):
3606 r = update_forest_info.entries[i]
3607 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3609 if update_forest_info.entries[i].flags == 0:
3611 update_forest_info.entries[i].time = 0
3612 update_forest_info.entries[i].flags &= ~lsa.LSA_NB_DISABLED_MASK
3613 update_forest_info.entries[i].flags &= ~lsa.LSA_SID_DISABLED_MASK
3615 for tln in enable_tln:
3617 for i in xrange(0, len(update_forest_info.entries)):
3618 r = update_forest_info.entries[i]
3619 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3621 if r.forest_trust_data.string.lower() != tln.lower():
3626 raise CommandError("Entry not found for value[%s] specified for --enable-tln" % tln)
3627 if not update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_MASK:
3628 raise CommandError("Entry found for value[%s] specified for --enable-tln is already enabled" % tln)
3629 update_forest_info.entries[idx].time = 0
3630 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3632 for tln in disable_tln:
3634 for i in xrange(0, len(update_forest_info.entries)):
3635 r = update_forest_info.entries[i]
3636 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3638 if r.forest_trust_data.string.lower() != tln.lower():
3643 raise CommandError("Entry not found for value[%s] specified for --disable-tln" % tln)
3644 if update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_ADMIN:
3645 raise CommandError("Entry found for value[%s] specified for --disable-tln is already disabled" % tln)
3646 update_forest_info.entries[idx].time = 0
3647 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3648 update_forest_info.entries[idx].flags |= lsa.LSA_TLN_DISABLED_ADMIN
3650 for tln_ex in add_tln_ex:
3652 for i in xrange(0, len(update_forest_info.entries)):
3653 r = update_forest_info.entries[i]
3654 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3656 if r.forest_trust_data.string.lower() != tln_ex.lower():
3661 raise CommandError("Entry already present for value[%s] specified for --add-tln-ex" % tln_ex)
3663 tln_dot = ".%s" % tln_ex.lower()
3665 for i in xrange(0, len(update_forest_info.entries)):
3666 r = update_forest_info.entries[i]
3667 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3669 r_dot = ".%s" % r.forest_trust_data.string.lower()
3670 if tln_dot == r_dot:
3671 raise CommandError("TLN entry present for value[%s] specified for --add-tln-ex" % tln_ex)
3672 if not tln_dot.endswith(r_dot):
3678 raise CommandError("No TLN parent present for value[%s] specified for --add-tln-ex" % tln_ex)
3680 r = lsa.ForestTrustRecord()
3681 r.type = lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX
3684 r.forest_trust_data.string = tln_ex
3687 entries.extend(update_forest_info.entries)
3688 entries.insert(idx + 1, r)
3689 update_forest_info.count = len(entries)
3690 update_forest_info.entries = entries
3692 for tln_ex in delete_tln_ex:
3694 for i in xrange(0, len(update_forest_info.entries)):
3695 r = update_forest_info.entries[i]
3696 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3698 if r.forest_trust_data.string.lower() != tln_ex.lower():
3703 raise CommandError("Entry not found for value[%s] specified for --delete-tln-ex" % tln_ex)
3706 entries.extend(update_forest_info.entries)
3708 update_forest_info.count = len(entries)
3709 update_forest_info.entries = entries
3711 for nb in enable_nb:
3713 for i in xrange(0, len(update_forest_info.entries)):
3714 r = update_forest_info.entries[i]
3715 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3717 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3722 raise CommandError("Entry not found for value[%s] specified for --enable-nb" % nb)
3723 if not update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_MASK:
3724 raise CommandError("Entry found for value[%s] specified for --enable-nb is already enabled" % nb)
3725 update_forest_info.entries[idx].time = 0
3726 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3728 for nb in disable_nb:
3730 for i in xrange(0, len(update_forest_info.entries)):
3731 r = update_forest_info.entries[i]
3732 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3734 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3739 raise CommandError("Entry not found for value[%s] specified for --delete-nb" % nb)
3740 if update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_ADMIN:
3741 raise CommandError("Entry found for value[%s] specified for --disable-nb is already disabled" % nb)
3742 update_forest_info.entries[idx].time = 0
3743 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3744 update_forest_info.entries[idx].flags |= lsa.LSA_NB_DISABLED_ADMIN
3746 for sid in enable_sid:
3748 for i in xrange(0, len(update_forest_info.entries)):
3749 r = update_forest_info.entries[i]
3750 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3752 if r.forest_trust_data.domain_sid != sid:
3757 raise CommandError("Entry not found for value[%s] specified for --enable-sid" % sid)
3758 if not update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_MASK:
3759 raise CommandError("Entry found for value[%s] specified for --enable-sid is already enabled" % nb)
3760 update_forest_info.entries[idx].time = 0
3761 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3763 for sid in disable_sid:
3765 for i in xrange(0, len(update_forest_info.entries)):
3766 r = update_forest_info.entries[i]
3767 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3769 if r.forest_trust_data.domain_sid != sid:
3774 raise CommandError("Entry not found for value[%s] specified for --delete-sid" % sid)
3775 if update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_ADMIN:
3776 raise CommandError("Entry found for value[%s] specified for --disable-sid is already disabled" % nb)
3777 update_forest_info.entries[idx].time = 0
3778 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3779 update_forest_info.entries[idx].flags |= lsa.LSA_SID_DISABLED_ADMIN
3782 update_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3783 local_tdo_info.domain_name,
3784 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3785 update_forest_info, 0)
3786 except RuntimeError as error:
3787 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3789 self.outf.write("Updated forest trust information...\n")
3790 self.write_forest_trust_info(update_forest_info,
3791 tln=local_tdo_info.domain_name.string,
3792 collisions=update_forest_collision)
3795 lsaString = lsa.String()
3796 lsaString.string = local_tdo_info.domain_name.string
3797 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3799 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3800 except RuntimeError as error:
3801 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3803 self.outf.write("Stored forest trust information...\n")
3804 self.write_forest_trust_info(stored_forest_info,
3805 tln=local_tdo_info.domain_name.string)
3808 class cmd_domain_tombstones_expunge(Command):
3809 """Expunge tombstones from the database.
3811 This command expunges tombstones from the database."""
3812 synopsis = "%prog NC [NC [...]] [options]"
3815 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
3816 metavar="URL", dest="H"),
3817 Option("--current-time",
3818 help="The current time to evaluate the tombstone lifetime from, expressed as YYYY-MM-DD",
3820 Option("--tombstone-lifetime", help="Number of days a tombstone should be preserved for", type=int),
3823 takes_args = ["nc*"]
3825 takes_optiongroups = {
3826 "sambaopts": options.SambaOptions,
3827 "credopts": options.CredentialsOptions,
3828 "versionopts": options.VersionOptions,
3831 def run(self, *ncs, **kwargs):
3832 sambaopts = kwargs.get("sambaopts")
3833 credopts = kwargs.get("credopts")
3834 versionpts = kwargs.get("versionopts")
3836 current_time_string = kwargs.get("current_time")
3837 tombstone_lifetime = kwargs.get("tombstone_lifetime")
3838 lp = sambaopts.get_loadparm()
3839 creds = credopts.get_credentials(lp)
3840 samdb = SamDB(url=H, session_info=system_session(),
3841 credentials=creds, lp=lp)
3843 if current_time_string is not None:
3844 current_time_obj = time.strptime(current_time_string, "%Y-%m-%d")
3845 current_time = long(time.mktime(current_time_obj))
3848 current_time = long(time.time())
3851 res = samdb.search(expression="", base="", scope=ldb.SCOPE_BASE,
3852 attrs=["namingContexts"])
3855 for nc in res[0]["namingContexts"]:
3860 started_transaction = False
3862 samdb.transaction_start()
3863 started_transaction = True
3865 removed_links) = samdb.garbage_collect_tombstones(ncs,
3866 current_time=current_time,
3867 tombstone_lifetime=tombstone_lifetime)
3869 except Exception as err:
3870 if started_transaction:
3871 samdb.transaction_cancel()
3872 raise CommandError("Failed to expunge / garbage collect tombstones", err)
3874 samdb.transaction_commit()
3876 self.outf.write("Removed %d objects and %d links successfully\n"
3877 % (removed_objects, removed_links))
3881 class cmd_domain_trust(SuperCommand):
3882 """Domain and forest trust management."""
3885 subcommands["list"] = cmd_domain_trust_list()
3886 subcommands["show"] = cmd_domain_trust_show()
3887 subcommands["create"] = cmd_domain_trust_create()
3888 subcommands["delete"] = cmd_domain_trust_delete()
3889 subcommands["validate"] = cmd_domain_trust_validate()
3890 subcommands["namespaces"] = cmd_domain_trust_namespaces()
3892 class cmd_domain_tombstones(SuperCommand):
3893 """Domain tombstone and recycled object management."""
3896 subcommands["expunge"] = cmd_domain_tombstones_expunge()
3898 class ldif_schema_update:
3899 """Helper class for applying LDIF schema updates"""
3902 self.is_defunct = False
3903 self.unknown_oid = None
3907 def _ldap_schemaUpdateNow(self, samdb):
3911 add: schemaUpdateNow
3914 samdb.modify_ldif(ldif)
3916 def can_ignore_failure(self, error):
3917 """Checks if we can safely ignore failure to apply an LDIF update"""
3918 (num, errstr) = error.args
3920 # Microsoft has marked objects as defunct that Samba doesn't know about
3921 if num == ldb.ERR_NO_SUCH_OBJECT and self.is_defunct:
3922 print("Defunct object %s doesn't exist, skipping" % self.dn)
3924 elif self.unknown_oid is not None:
3925 print("Skipping unknown OID %s for object %s" %(self.unknown_oid, self.dn))
3930 def apply(self, samdb):
3931 """Applies a single LDIF update to the schema"""
3935 samdb.modify_ldif(self.ldif, controls=['relax:0'])
3936 except ldb.LdbError as e:
3937 if e.args[0] == ldb.ERR_INVALID_ATTRIBUTE_SYNTAX:
3939 # REFRESH after a failed change
3941 # Otherwise the OID-to-attribute mapping in
3942 # _apply_updates_in_file() won't work, because it
3943 # can't lookup the new OID in the schema
3944 self._ldap_schemaUpdateNow(samdb)
3946 samdb.modify_ldif(self.ldif, controls=['relax:0'])
3949 except ldb.LdbError as e:
3950 if self.can_ignore_failure(e):
3953 print("Exception: %s" % e)
3954 print("Encountered while trying to apply the following LDIF")
3955 print("----------------------------------------------------")
3956 print("%s" % self.ldif)
3962 class cmd_domain_schema_upgrade(Command):
3963 """Domain schema upgrading"""
3965 synopsis = "%prog [options]"
3967 takes_optiongroups = {
3968 "sambaopts": options.SambaOptions,
3969 "versionopts": options.VersionOptions,
3970 "credopts": options.CredentialsOptions,
3974 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
3975 metavar="URL", dest="H"),
3976 Option("--quiet", help="Be quiet", action="store_true"),
3977 Option("--verbose", help="Be verbose", action="store_true"),
3978 Option("--schema", type="choice", metavar="SCHEMA",
3979 choices=["2012", "2012_R2"],
3980 help="The schema file to upgrade to. Default is (Windows) 2012_R2.",
3982 Option("--ldf-file", type=str, default=None,
3983 help="Just apply the schema updates in the adprep/.LDF file(s) specified"),
3984 Option("--base-dir", type=str, default=None,
3985 help="Location of ldf files Default is ${SETUPDIR}/adprep.")
3988 def _apply_updates_in_file(self, samdb, ldif_file):
3990 Applies a series of updates specified in an .LDIF file. The .LDIF file
3991 is based on the adprep Schema updates provided by Microsoft.
3994 ldif_op = ldif_schema_update()
3996 # parse the file line by line and work out each update operation to apply
3997 for line in ldif_file:
3999 line = line.rstrip()
4001 # the operations in the .LDIF file are separated by blank lines. If
4002 # we hit a blank line, try to apply the update we've parsed so far
4005 # keep going if we haven't parsed anything yet
4006 if ldif_op.ldif == '':
4009 # Apply the individual change
4010 count += ldif_op.apply(samdb)
4012 # start storing the next operation from scratch again
4013 ldif_op = ldif_schema_update()
4016 # replace the placeholder domain name in the .ldif file with the real domain
4017 if line.upper().endswith('DC=X'):
4018 line = line[:-len('DC=X')] + str(samdb.get_default_basedn())
4019 elif line.upper().endswith('CN=X'):
4020 line = line[:-len('CN=X')] + str(samdb.get_default_basedn())
4022 values = line.split(':')
4024 if values[0].lower() == 'dn':
4025 ldif_op.dn = values[1].strip()
4027 # replace the Windows-specific operation with the Samba one
4028 if values[0].lower() == 'changetype':
4029 line = line.lower().replace(': ntdsschemaadd',
4031 line = line.lower().replace(': ntdsschemamodify',
4034 if values[0].lower() in ['rdnattid', 'subclassof',
4035 'systemposssuperiors',
4037 'systemauxiliaryclass']:
4040 # The Microsoft updates contain some OIDs we don't recognize.
4041 # Query the DB to see if we can work out the OID this update is
4042 # referring to. If we find a match, then replace the OID with
4043 # the ldapDisplayname
4045 res = samdb.search(base=samdb.get_schema_basedn(),
4046 expression="(|(attributeId=%s)(governsId=%s))" %
4048 attrs=['ldapDisplayName'])
4051 ldif_op.unknown_oid = value
4053 display_name = res[0]['ldapDisplayName'][0]
4054 line = line.replace(value, ' ' + display_name)
4056 # Microsoft has marked objects as defunct that Samba doesn't know about
4057 if values[0].lower() == 'isdefunct' and values[1].strip().lower() == 'true':
4058 ldif_op.is_defunct = True
4060 # Samba has added the showInAdvancedViewOnly attribute to all objects,
4061 # so rather than doing an add, we need to do a replace
4062 if values[0].lower() == 'add' and values[1].strip().lower() == 'showinadvancedviewonly':
4063 line = 'replace: showInAdvancedViewOnly'
4065 # Add the line to the current LDIF operation (including the newline
4066 # we stripped off at the start of the loop)
4067 ldif_op.ldif += line + '\n'
4072 def _apply_update(self, samdb, update_file, base_dir):
4073 """Wrapper function for parsing an LDIF file and applying the updates"""
4075 print("Applying %s updates..." % update_file)
4079 ldif_file = open(os.path.join(base_dir, update_file))
4081 count = self._apply_updates_in_file(samdb, ldif_file)
4087 print("%u changes applied" % count)
4091 def run(self, **kwargs):
4092 from samba.ms_schema_markdown import read_ms_markdown
4093 from samba.schema import Schema
4095 updates_allowed_overriden = False
4096 sambaopts = kwargs.get("sambaopts")
4097 credopts = kwargs.get("credopts")
4098 versionpts = kwargs.get("versionopts")
4099 lp = sambaopts.get_loadparm()
4100 creds = credopts.get_credentials(lp)
4102 target_schema = kwargs.get("schema")
4103 ldf_files = kwargs.get("ldf_file")
4104 base_dir = kwargs.get("base_dir")
4108 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
4110 # we're not going to get far if the config doesn't allow schema updates
4111 if lp.get("dsdb:schema update allowed") is None:
4112 lp.set("dsdb:schema update allowed", "yes")
4113 print("Temporarily overriding 'dsdb:schema update allowed' setting")
4114 updates_allowed_overriden = True
4116 own_dn = ldb.Dn(samdb, samdb.get_dsServiceName())
4117 master = get_fsmo_roleowner(samdb, str(samdb.get_schema_basedn()),
4119 if own_dn != master:
4120 raise CommandError("This server is not the schema master.")
4122 # if specific LDIF files were specified, just apply them
4124 schema_updates = ldf_files.split(",")
4128 # work out the version of the target schema we're upgrading to
4129 end = Schema.get_version(target_schema)
4131 # work out the version of the schema we're currently using
4132 res = samdb.search(base=samdb.get_schema_basedn(),
4133 scope=ldb.SCOPE_BASE, attrs=['objectVersion'])
4136 raise CommandError('Could not determine current schema version')
4137 start = int(res[0]['objectVersion'][0]) + 1
4139 diff_dir = setup_path("adprep/WindowsServerDocs")
4140 if base_dir is None:
4141 # Read from the Schema-Updates.md file
4142 temp_folder = tempfile.mkdtemp()
4144 update_file = setup_path("adprep/WindowsServerDocs/Schema-Updates.md")
4147 read_ms_markdown(update_file, temp_folder)
4148 except Exception as e:
4149 print("Exception in markdown parsing: %s" % e)
4150 shutil.rmtree(temp_folder)
4151 raise CommandError('Failed to upgrade schema')
4153 base_dir = temp_folder
4155 for version in range(start, end + 1):
4156 update = 'Sch%d.ldf' % version
4157 schema_updates.append(update)
4159 # Apply patches if we parsed the Schema-Updates.md file
4160 diff = os.path.abspath(os.path.join(diff_dir, update + '.diff'))
4161 if temp_folder and os.path.exists(diff):
4163 p = subprocess.Popen(['patch', update, '-i', diff],
4164 stdout=subprocess.PIPE,
4165 stderr=subprocess.PIPE, cwd=temp_folder)
4166 except (OSError, IOError):
4167 shutil.rmtree(temp_folder)
4168 raise CommandError("Failed to upgrade schema. Check if 'patch' is installed.")
4170 stdout, stderr = p.communicate()
4173 print("Exception in patch: %s\n%s" % (stdout, stderr))
4174 shutil.rmtree(temp_folder)
4175 raise CommandError('Failed to upgrade schema')
4177 print("Patched %s using %s" % (update, diff))
4179 if base_dir is None:
4180 base_dir = setup_path("adprep")
4182 samdb.transaction_start()
4184 error_encountered = False
4187 # Apply the schema updates needed to move to the new schema version
4188 for ldif_file in schema_updates:
4189 count += self._apply_update(samdb, ldif_file, base_dir)
4192 samdb.transaction_commit()
4193 print("Schema successfully updated")
4195 print("No changes applied to schema")
4196 samdb.transaction_cancel()
4197 except Exception as e:
4198 print("Exception: %s" % e)
4199 print("Error encountered, aborting schema upgrade")
4200 samdb.transaction_cancel()
4201 error_encountered = True
4203 if updates_allowed_overriden:
4204 lp.set("dsdb:schema update allowed", "no")
4207 shutil.rmtree(temp_folder)
4209 if error_encountered:
4210 raise CommandError('Failed to upgrade schema')
4212 class cmd_domain_functional_prep(Command):
4213 """Domain functional level preparation"""
4215 synopsis = "%prog [options]"
4217 takes_optiongroups = {
4218 "sambaopts": options.SambaOptions,
4219 "versionopts": options.VersionOptions,
4220 "credopts": options.CredentialsOptions,
4224 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
4225 metavar="URL", dest="H"),
4226 Option("--quiet", help="Be quiet", action="store_true"),
4227 Option("--verbose", help="Be verbose", action="store_true"),
4228 Option("--function-level", type="choice", metavar="FUNCTION_LEVEL",
4229 choices=["2008_R2", "2012", "2012_R2"],
4230 help="The schema file to upgrade to. Default is (Windows) 2012_R2.",
4232 Option("--forest-prep", action="store_true",
4233 help="Run the forest prep (by default, both the domain and forest prep are run)."),
4234 Option("--domain-prep", action="store_true",
4235 help="Run the domain prep (by default, both the domain and forest prep are run).")
4238 def run(self, **kwargs):
4239 updates_allowed_overriden = False
4240 sambaopts = kwargs.get("sambaopts")
4241 credopts = kwargs.get("credopts")
4242 versionpts = kwargs.get("versionopts")
4243 lp = sambaopts.get_loadparm()
4244 creds = credopts.get_credentials(lp)
4246 target_level = string_version_to_constant[kwargs.get("function_level")]
4247 forest_prep = kwargs.get("forest_prep")
4248 domain_prep = kwargs.get("domain_prep")
4250 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
4252 # we're not going to get far if the config doesn't allow schema updates
4253 if lp.get("dsdb:schema update allowed") is None:
4254 lp.set("dsdb:schema update allowed", "yes")
4255 print("Temporarily overriding 'dsdb:schema update allowed' setting")
4256 updates_allowed_overriden = True
4258 if forest_prep is None and domain_prep is None:
4262 own_dn = ldb.Dn(samdb, samdb.get_dsServiceName())
4264 master = get_fsmo_roleowner(samdb, str(samdb.get_schema_basedn()),
4266 if own_dn != master:
4267 raise CommandError("This server is not the schema master.")
4270 domain_dn = samdb.domain_dn()
4271 infrastructure_dn = "CN=Infrastructure," + domain_dn
4272 master = get_fsmo_roleowner(samdb, infrastructure_dn,
4274 if own_dn != master:
4275 raise CommandError("This server is not the infrastructure master.")
4278 samdb.transaction_start()
4279 error_encountered = False
4281 from samba.forest_update import ForestUpdate
4282 forest = ForestUpdate(samdb, fix=True)
4284 forest.check_updates_iterator([53, 79, 80, 81, 82, 83])
4285 forest.check_updates_functional_level(target_level,
4286 DS_DOMAIN_FUNCTION_2008_R2,
4287 update_revision=True)
4289 samdb.transaction_commit()
4290 except Exception as e:
4291 print("Exception: %s" % e)
4292 samdb.transaction_cancel()
4293 error_encountered = True
4296 samdb.transaction_start()
4297 error_encountered = False
4299 from samba.domain_update import DomainUpdate
4301 domain = DomainUpdate(samdb, fix=True)
4302 domain.check_updates_functional_level(target_level,
4303 DS_DOMAIN_FUNCTION_2008,
4304 update_revision=True)
4306 samdb.transaction_commit()
4307 except Exception as e:
4308 print("Exception: %s" % e)
4309 samdb.transaction_cancel()
4310 error_encountered = True
4312 if updates_allowed_overriden:
4313 lp.set("dsdb:schema update allowed", "no")
4315 if error_encountered:
4316 raise CommandError('Failed to perform functional prep')
4318 class cmd_domain(SuperCommand):
4319 """Domain management."""
4322 subcommands["demote"] = cmd_domain_demote()
4323 if cmd_domain_export_keytab is not None:
4324 subcommands["exportkeytab"] = cmd_domain_export_keytab()
4325 subcommands["info"] = cmd_domain_info()
4326 subcommands["provision"] = cmd_domain_provision()
4327 subcommands["join"] = cmd_domain_join()
4328 subcommands["dcpromo"] = cmd_domain_dcpromo()
4329 subcommands["level"] = cmd_domain_level()
4330 subcommands["passwordsettings"] = cmd_domain_passwordsettings()
4331 subcommands["classicupgrade"] = cmd_domain_classicupgrade()
4332 subcommands["samba3upgrade"] = cmd_domain_samba3upgrade()
4333 subcommands["trust"] = cmd_domain_trust()
4334 subcommands["tombstones"] = cmd_domain_tombstones()
4335 subcommands["schemaupgrade"] = cmd_domain_schema_upgrade()
4336 subcommands["functionalprep"] = cmd_domain_functional_prep()