3 # Copyright Matthias Dieter Wallnoefer 2009
4 # Copyright Andrew Kroeger 2009
5 # Copyright Jelmer Vernooij 2007-2012
6 # Copyright Giampaolo Lauria 2011
7 # Copyright Matthieu Patou <mat@matws.net> 2011
8 # Copyright Andrew Bartlett 2008-2015
9 # Copyright Stefan Metzmacher 2012
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 3 of the License, or
14 # (at your option) any later version.
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 from __future__ import print_function
26 from __future__ import division
27 import samba.getopt as options
39 from samba import ntstatus
40 from samba import NTSTATUSError
41 from samba import werror
42 from getpass import getpass
43 from samba.net import Net, LIBNET_JOIN_AUTOMATIC
45 from samba.join import join_RODC, join_DC, join_subdomain
46 from samba.auth import system_session
47 from samba.samdb import SamDB, get_default_backend_store
48 from samba.ndr import ndr_unpack, ndr_pack, ndr_print
49 from samba.dcerpc import drsuapi
50 from samba.dcerpc import drsblobs
51 from samba.dcerpc import lsa
52 from samba.dcerpc import netlogon
53 from samba.dcerpc import security
54 from samba.dcerpc import nbt
55 from samba.dcerpc import misc
56 from samba.dcerpc.samr import DOMAIN_PASSWORD_COMPLEX, DOMAIN_PASSWORD_STORE_CLEARTEXT
57 from samba.netcmd import (
63 from samba.netcmd.fsmo import get_fsmo_roleowner
64 from samba.netcmd.common import netcmd_get_domain_infos_via_cldap
65 from samba.samba3 import Samba3
66 from samba.samba3 import param as s3param
67 from samba.upgrade import upgrade_from_samba3
68 from samba.drs_utils import (
69 sendDsReplicaSync, drsuapi_connect, drsException,
71 from samba import remove_dc, arcfour_encrypt, string_to_byte_array
73 from samba.dsdb import (
74 DS_DOMAIN_FUNCTION_2000,
75 DS_DOMAIN_FUNCTION_2003,
76 DS_DOMAIN_FUNCTION_2003_MIXED,
77 DS_DOMAIN_FUNCTION_2008,
78 DS_DOMAIN_FUNCTION_2008_R2,
79 DS_DOMAIN_FUNCTION_2012,
80 DS_DOMAIN_FUNCTION_2012_R2,
81 DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL,
82 DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL,
83 UF_WORKSTATION_TRUST_ACCOUNT,
84 UF_SERVER_TRUST_ACCOUNT,
85 UF_TRUSTED_FOR_DELEGATION,
86 UF_PARTIAL_SECRETS_ACCOUNT
89 from samba.provision import (
92 DEFAULT_MIN_PWD_LENGTH,
96 from samba.provision.common import (
102 string_version_to_constant = {
103 "2008_R2" : DS_DOMAIN_FUNCTION_2008_R2,
104 "2012": DS_DOMAIN_FUNCTION_2012,
105 "2012_R2": DS_DOMAIN_FUNCTION_2012_R2,
108 common_provision_join_options = [
109 Option("--machinepass", type="string", metavar="PASSWORD",
110 help="choose machine password (otherwise random)"),
111 Option("--targetdir", metavar="DIR",
112 help="Set target directory (where to store provision)", type=str),
113 Option("--quiet", help="Be quiet", action="store_true"),
116 def get_testparm_var(testparm, smbconf, varname):
117 errfile = open(os.devnull, 'w')
118 p = subprocess.Popen([testparm, '-s', '-l',
119 '--parameter-name=%s' % varname, smbconf],
120 stdout=subprocess.PIPE, stderr=errfile)
121 (out,err) = p.communicate()
123 lines = out.split('\n')
125 return lines[0].strip()
129 import samba.dckeytab
131 cmd_domain_export_keytab = None
133 class cmd_domain_export_keytab(Command):
134 """Dump Kerberos keys of the domain into a keytab."""
136 synopsis = "%prog <keytab> [options]"
138 takes_optiongroups = {
139 "sambaopts": options.SambaOptions,
140 "credopts": options.CredentialsOptions,
141 "versionopts": options.VersionOptions,
145 Option("--principal", help="extract only this principal", type=str),
148 takes_args = ["keytab"]
150 def run(self, keytab, credopts=None, sambaopts=None, versionopts=None, principal=None):
151 lp = sambaopts.get_loadparm()
153 net.export_keytab(keytab=keytab, principal=principal)
156 class cmd_domain_info(Command):
157 """Print basic info about a domain and the DC passed as parameter."""
159 synopsis = "%prog <ip_address> [options]"
164 takes_optiongroups = {
165 "sambaopts": options.SambaOptions,
166 "credopts": options.CredentialsOptions,
167 "versionopts": options.VersionOptions,
170 takes_args = ["address"]
172 def run(self, address, credopts=None, sambaopts=None, versionopts=None):
173 lp = sambaopts.get_loadparm()
175 res = netcmd_get_domain_infos_via_cldap(lp, None, address)
177 raise CommandError("Invalid IP address '" + address + "'!")
178 self.outf.write("Forest : %s\n" % res.forest)
179 self.outf.write("Domain : %s\n" % res.dns_domain)
180 self.outf.write("Netbios domain : %s\n" % res.domain_name)
181 self.outf.write("DC name : %s\n" % res.pdc_dns_name)
182 self.outf.write("DC netbios name : %s\n" % res.pdc_name)
183 self.outf.write("Server site : %s\n" % res.server_site)
184 self.outf.write("Client site : %s\n" % res.client_site)
187 class cmd_domain_provision(Command):
188 """Provision a domain."""
190 synopsis = "%prog [options]"
192 takes_optiongroups = {
193 "sambaopts": options.SambaOptions,
194 "versionopts": options.VersionOptions,
198 Option("--interactive", help="Ask for names", action="store_true"),
199 Option("--domain", type="string", metavar="DOMAIN",
200 help="NetBIOS domain name to use"),
201 Option("--domain-guid", type="string", metavar="GUID",
202 help="set domainguid (otherwise random)"),
203 Option("--domain-sid", type="string", metavar="SID",
204 help="set domainsid (otherwise random)"),
205 Option("--ntds-guid", type="string", metavar="GUID",
206 help="set NTDS object GUID (otherwise random)"),
207 Option("--invocationid", type="string", metavar="GUID",
208 help="set invocationid (otherwise random)"),
209 Option("--host-name", type="string", metavar="HOSTNAME",
210 help="set hostname"),
211 Option("--host-ip", type="string", metavar="IPADDRESS",
212 help="set IPv4 ipaddress"),
213 Option("--host-ip6", type="string", metavar="IP6ADDRESS",
214 help="set IPv6 ipaddress"),
215 Option("--site", type="string", metavar="SITENAME",
216 help="set site name"),
217 Option("--adminpass", type="string", metavar="PASSWORD",
218 help="choose admin password (otherwise random)"),
219 Option("--krbtgtpass", type="string", metavar="PASSWORD",
220 help="choose krbtgt password (otherwise random)"),
221 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
222 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
223 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
224 "BIND9_FLATFILE uses bind9 text database to store zone information, "
225 "BIND9_DLZ uses samba4 AD to store zone information, "
226 "NONE skips the DNS setup entirely (not recommended)",
227 default="SAMBA_INTERNAL"),
228 Option("--dnspass", type="string", metavar="PASSWORD",
229 help="choose dns password (otherwise random)"),
230 Option("--root", type="string", metavar="USERNAME",
231 help="choose 'root' unix username"),
232 Option("--nobody", type="string", metavar="USERNAME",
233 help="choose 'nobody' user"),
234 Option("--users", type="string", metavar="GROUPNAME",
235 help="choose 'users' group"),
236 Option("--blank", action="store_true",
237 help="do not add users or groups, just the structure"),
238 Option("--server-role", type="choice", metavar="ROLE",
239 choices=["domain controller", "dc", "member server", "member", "standalone"],
240 help="The server role (domain controller | dc | member server | member | standalone). Default is dc.",
241 default="domain controller"),
242 Option("--function-level", type="choice", metavar="FOR-FUN-LEVEL",
243 choices=["2000", "2003", "2008", "2008_R2"],
244 help="The domain and forest function level (2000 | 2003 | 2008 | 2008_R2 - always native). Default is (Windows) 2008_R2 Native.",
246 Option("--base-schema", type="choice", metavar="BASE-SCHEMA",
247 choices=["2008_R2", "2008_R2_old", "2012", "2012_R2"],
248 help="The base schema files to use. Default is (Windows) 2008_R2.",
250 Option("--next-rid", type="int", metavar="NEXTRID", default=1000,
251 help="The initial nextRid value (only needed for upgrades). Default is 1000."),
252 Option("--partitions-only",
253 help="Configure Samba's partitions, but do not modify them (ie, join a BDC)", action="store_true"),
254 Option("--use-rfc2307", action="store_true", help="Use AD to store posix attributes (default = no)"),
255 Option("--plaintext-secrets", action="store_true",
256 help="Store secret/sensitive values as plain text on disk" +
257 "(default is to encrypt secret/ensitive values)"),
258 Option("--backend-store", type="choice", metavar="BACKENDSTORE",
259 choices=["tdb", "mdb"],
260 help="Specify the database backend to be used "
261 "(default is %s)" % get_default_backend_store()),
265 Option("--ldapadminpass", type="string", metavar="PASSWORD",
266 help="choose password to set between Samba and its LDAP backend (otherwise random)"),
267 Option("--ldap-backend-type", type="choice", metavar="LDAP-BACKEND-TYPE",
268 help="Test initialisation support for unsupported LDAP backend type (fedora-ds or openldap) DO NOT USE",
269 choices=["fedora-ds", "openldap"]),
270 Option("--ol-mmr-urls", type="string", metavar="LDAPSERVER",
271 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\""),
272 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",
273 action="store_true"),
274 Option("--slapd-path", type="string", metavar="SLAPD-PATH",
275 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."),
276 Option("--ldap-backend-extra-port", type="int", metavar="LDAP-BACKEND-EXTRA-PORT", help="Additional TCP port for LDAP backend server (to use for replication)"),
277 Option("--ldap-backend-forced-uri", type="string", metavar="LDAP-BACKEND-FORCED-URI",
278 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"),
279 Option("--ldap-backend-nosync", help="Configure LDAP backend not to call fsync() (for performance in test environments)", action="store_true"),
283 Option("--use-ntvfs", action="store_true", help="Use NTVFS for the fileserver (default = no)"),
284 Option("--use-xattrs", type="choice", choices=["yes","no","auto"],
285 metavar="[yes|no|auto]",
286 help="Define if we should use the native fs capabilities or a tdb file for "
287 "storing attributes likes ntacl when --use-ntvfs is set. "
288 "auto tries to make an inteligent guess based on the user rights and system capabilities",
292 takes_options.extend(common_provision_join_options)
294 if os.getenv('TEST_LDAP', "no") == "yes":
295 takes_options.extend(openldap_options)
297 if samba.is_ntvfs_fileserver_built():
298 takes_options.extend(ntvfs_options)
302 def run(self, sambaopts=None, versionopts=None,
325 ldap_backend_type=None,
329 partitions_only=None,
336 ldap_backend_nosync=None,
337 ldap_backend_extra_port=None,
338 ldap_backend_forced_uri=None,
339 ldap_dryrun_mode=None,
341 plaintext_secrets=False,
344 self.logger = self.get_logger("provision")
346 self.logger.setLevel(logging.WARNING)
348 self.logger.setLevel(logging.INFO)
350 lp = sambaopts.get_loadparm()
351 smbconf = lp.configfile
353 if dns_forwarder is not None:
354 suggested_forwarder = dns_forwarder
356 suggested_forwarder = self._get_nameserver_ip()
357 if suggested_forwarder is None:
358 suggested_forwarder = "none"
360 if len(self.raw_argv) == 1:
364 from getpass import getpass
367 def ask(prompt, default=None):
368 if default is not None:
369 print("%s [%s]: " % (prompt, default), end=' ')
371 print("%s: " % (prompt,), end=' ')
372 return sys.stdin.readline().rstrip("\n") or default
375 default = socket.getfqdn().split(".", 1)[1].upper()
378 realm = ask("Realm", default)
379 if realm in (None, ""):
380 raise CommandError("No realm set!")
383 default = realm.split(".")[0]
386 domain = ask("Domain", default)
388 raise CommandError("No domain set!")
390 server_role = ask("Server Role (dc, member, standalone)", "dc")
392 dns_backend = ask("DNS backend (SAMBA_INTERNAL, BIND9_FLATFILE, BIND9_DLZ, NONE)", "SAMBA_INTERNAL")
393 if dns_backend in (None, ''):
394 raise CommandError("No DNS backend set!")
396 if dns_backend == "SAMBA_INTERNAL":
397 dns_forwarder = ask("DNS forwarder IP address (write 'none' to disable forwarding)", suggested_forwarder)
398 if dns_forwarder.lower() in (None, 'none'):
399 suggested_forwarder = None
403 adminpassplain = getpass("Administrator password: ")
404 issue = self._adminpass_issue(adminpassplain)
406 self.errf.write("%s.\n" % issue)
408 adminpassverify = getpass("Retype password: ")
409 if not adminpassplain == adminpassverify:
410 self.errf.write("Sorry, passwords do not match.\n")
412 adminpass = adminpassplain
416 realm = sambaopts._lp.get('realm')
418 raise CommandError("No realm set!")
420 raise CommandError("No domain set!")
423 issue = self._adminpass_issue(adminpass)
425 raise CommandError(issue)
427 self.logger.info("Administrator password will be set randomly!")
429 if function_level == "2000":
430 dom_for_fun_level = DS_DOMAIN_FUNCTION_2000
431 elif function_level == "2003":
432 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
433 elif function_level == "2008":
434 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008
435 elif function_level == "2008_R2":
436 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
438 if dns_backend == "SAMBA_INTERNAL" and dns_forwarder is None:
439 dns_forwarder = suggested_forwarder
441 samdb_fill = FILL_FULL
443 samdb_fill = FILL_NT4SYNC
444 elif partitions_only:
445 samdb_fill = FILL_DRS
447 if targetdir is not None:
448 if not os.path.isdir(targetdir):
453 if use_xattrs == "yes":
455 elif use_xattrs == "auto" and use_ntvfs == False:
457 elif use_ntvfs == False:
458 raise CommandError("--use-xattrs=no requires --use-ntvfs (not supported for production use). "
459 "Please re-run with --use-xattrs omitted.")
460 elif use_xattrs == "auto" and not lp.get("posix:eadb"):
462 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
464 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
467 samba.ntacls.setntacl(lp, file.name,
468 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
471 self.logger.info("You are not root or your system does not support xattr, using tdb backend for attributes. ")
476 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.")
477 if ldap_backend_type == "existing":
478 if ldap_backend_forced_uri is not None:
479 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)
481 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")
483 if ldap_backend_forced_uri is not None:
484 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")
486 if domain_sid is not None:
487 domain_sid = security.dom_sid(domain_sid)
489 session = system_session()
490 if backend_store is None:
491 backend_store = get_default_backend_store()
493 result = provision(self.logger,
494 session, smbconf=smbconf, targetdir=targetdir,
495 samdb_fill=samdb_fill, realm=realm, domain=domain,
496 domainguid=domain_guid, domainsid=domain_sid,
498 hostip=host_ip, hostip6=host_ip6,
499 sitename=site, ntdsguid=ntds_guid,
500 invocationid=invocationid, adminpass=adminpass,
501 krbtgtpass=krbtgtpass, machinepass=machinepass,
502 dns_backend=dns_backend, dns_forwarder=dns_forwarder,
503 dnspass=dnspass, root=root, nobody=nobody,
505 serverrole=server_role, dom_for_fun_level=dom_for_fun_level,
506 backend_type=ldap_backend_type,
507 ldapadminpass=ldapadminpass, ol_mmr_urls=ol_mmr_urls, slapd_path=slapd_path,
508 useeadb=eadb, next_rid=next_rid, lp=lp, use_ntvfs=use_ntvfs,
509 use_rfc2307=use_rfc2307, skip_sysvolacl=False,
510 ldap_backend_extra_port=ldap_backend_extra_port,
511 ldap_backend_forced_uri=ldap_backend_forced_uri,
512 nosync=ldap_backend_nosync, ldap_dryrun_mode=ldap_dryrun_mode,
513 base_schema=base_schema,
514 plaintext_secrets=plaintext_secrets,
515 backend_store=backend_store)
517 except ProvisioningError as e:
518 raise CommandError("Provision failed", e)
520 result.report_logger(self.logger)
522 def _get_nameserver_ip(self):
523 """Grab the nameserver IP address from /etc/resolv.conf."""
525 RESOLV_CONF="/etc/resolv.conf"
527 if not path.isfile(RESOLV_CONF):
528 self.logger.warning("Failed to locate %s" % RESOLV_CONF)
533 handle = open(RESOLV_CONF, 'r')
535 if not line.startswith('nameserver'):
537 # we want the last non-space continuous string of the line
538 return line.strip().split()[-1]
540 if handle is not None:
543 self.logger.warning("No nameserver found in %s" % RESOLV_CONF)
545 def _adminpass_issue(self, adminpass):
546 """Returns error string for a bad administrator password,
547 or None if acceptable"""
549 if len(adminpass.decode('utf-8')) < DEFAULT_MIN_PWD_LENGTH:
550 return "Administrator password does not meet the default minimum" \
551 " password length requirement (%d characters)" \
552 % DEFAULT_MIN_PWD_LENGTH
553 elif not samba.check_password_quality(adminpass):
554 return "Administrator password does not meet the default" \
560 class cmd_domain_dcpromo(Command):
561 """Promote an existing domain member or NT4 PDC to an AD DC."""
563 synopsis = "%prog <dnsdomain> [DC|RODC] [options]"
565 takes_optiongroups = {
566 "sambaopts": options.SambaOptions,
567 "versionopts": options.VersionOptions,
568 "credopts": options.CredentialsOptions,
572 Option("--server", help="DC to join", type=str),
573 Option("--site", help="site to join", type=str),
574 Option("--domain-critical-only",
575 help="only replicate critical domain objects",
576 action="store_true"),
577 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
578 choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
579 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
580 "BIND9_DLZ uses samba4 AD to store zone information, "
581 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
582 default="SAMBA_INTERNAL"),
583 Option("--verbose", help="Be verbose", action="store_true")
586 takes_options.extend(common_provision_join_options)
589 Option("--use-ntvfs", action="store_true", help="Use NTVFS for the fileserver (default = no)"),
592 if samba.is_ntvfs_fileserver_built():
593 takes_options.extend(ntvfs_options)
596 takes_args = ["domain", "role?"]
598 def run(self, domain, role=None, sambaopts=None, credopts=None,
599 versionopts=None, server=None, site=None, targetdir=None,
600 domain_critical_only=False, parent_domain=None, machinepass=None,
601 use_ntvfs=False, dns_backend=None,
602 quiet=False, verbose=False):
603 lp = sambaopts.get_loadparm()
604 creds = credopts.get_credentials(lp)
605 net = Net(creds, lp, server=credopts.ipaddress)
607 logger = self.get_logger()
609 logger.setLevel(logging.DEBUG)
611 logger.setLevel(logging.WARNING)
613 logger.setLevel(logging.INFO)
615 netbios_name = lp.get("netbios name")
621 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
622 site=site, netbios_name=netbios_name, targetdir=targetdir,
623 domain_critical_only=domain_critical_only,
624 machinepass=machinepass, use_ntvfs=use_ntvfs,
625 dns_backend=dns_backend,
626 promote_existing=True)
628 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
629 site=site, netbios_name=netbios_name, targetdir=targetdir,
630 domain_critical_only=domain_critical_only,
631 machinepass=machinepass, use_ntvfs=use_ntvfs, dns_backend=dns_backend,
632 promote_existing=True)
634 raise CommandError("Invalid role '%s' (possible values: DC, RODC)" % role)
637 class cmd_domain_join(Command):
638 """Join domain as either member or backup domain controller."""
640 synopsis = "%prog <dnsdomain> [DC|RODC|MEMBER|SUBDOMAIN] [options]"
642 takes_optiongroups = {
643 "sambaopts": options.SambaOptions,
644 "versionopts": options.VersionOptions,
645 "credopts": options.CredentialsOptions,
649 Option("--server", help="DC to join", type=str),
650 Option("--site", help="site to join", type=str),
651 Option("--parent-domain", help="parent domain to create subdomain under", type=str),
652 Option("--domain-critical-only",
653 help="only replicate critical domain objects",
654 action="store_true"),
655 Option("--adminpass", type="string", metavar="PASSWORD",
656 help="choose adminstrator password when joining as a subdomain (otherwise random)"),
657 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
658 choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
659 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
660 "BIND9_DLZ uses samba4 AD to store zone information, "
661 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
662 default="SAMBA_INTERNAL"),
663 Option("--plaintext-secrets", action="store_true",
664 help="Store secret/sensitive values as plain text on disk" +
665 "(default is to encrypt secret/ensitive values)"),
666 Option("--verbose", help="Be verbose", action="store_true")
670 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
673 takes_options.extend(common_provision_join_options)
675 if samba.is_ntvfs_fileserver_built():
676 takes_options.extend(ntvfs_options)
678 takes_args = ["domain", "role?"]
680 def run(self, domain, role=None, sambaopts=None, credopts=None,
681 versionopts=None, server=None, site=None, targetdir=None,
682 domain_critical_only=False, parent_domain=None, machinepass=None,
683 use_ntvfs=False, dns_backend=None, adminpass=None,
684 quiet=False, verbose=False, plaintext_secrets=False):
685 lp = sambaopts.get_loadparm()
686 creds = credopts.get_credentials(lp)
687 net = Net(creds, lp, server=credopts.ipaddress)
690 site = "Default-First-Site-Name"
692 logger = self.get_logger()
694 logger.setLevel(logging.DEBUG)
696 logger.setLevel(logging.WARNING)
698 logger.setLevel(logging.INFO)
700 netbios_name = lp.get("netbios name")
705 if role is None or role == "MEMBER":
706 (join_password, sid, domain_name) = net.join_member(
707 domain, netbios_name, LIBNET_JOIN_AUTOMATIC,
708 machinepass=machinepass)
710 self.errf.write("Joined domain %s (%s)\n" % (domain_name, sid))
712 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
713 site=site, netbios_name=netbios_name, targetdir=targetdir,
714 domain_critical_only=domain_critical_only,
715 machinepass=machinepass, use_ntvfs=use_ntvfs,
716 dns_backend=dns_backend,
717 plaintext_secrets=plaintext_secrets)
719 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
720 site=site, netbios_name=netbios_name, targetdir=targetdir,
721 domain_critical_only=domain_critical_only,
722 machinepass=machinepass, use_ntvfs=use_ntvfs,
723 dns_backend=dns_backend,
724 plaintext_secrets=plaintext_secrets)
725 elif role == "SUBDOMAIN":
727 logger.info("Administrator password will be set randomly!")
729 netbios_domain = lp.get("workgroup")
730 if parent_domain is None:
731 parent_domain = ".".join(domain.split(".")[1:])
732 join_subdomain(logger=logger, server=server, creds=creds, lp=lp, dnsdomain=domain,
733 parent_domain=parent_domain, site=site,
734 netbios_name=netbios_name, netbios_domain=netbios_domain,
735 targetdir=targetdir, machinepass=machinepass,
736 use_ntvfs=use_ntvfs, dns_backend=dns_backend,
738 plaintext_secrets=plaintext_secrets)
740 raise CommandError("Invalid role '%s' (possible values: MEMBER, DC, RODC, SUBDOMAIN)" % role)
743 class cmd_domain_demote(Command):
744 """Demote ourselves from the role of Domain Controller."""
746 synopsis = "%prog [options]"
749 Option("--server", help="writable DC to write demotion changes on", type=str),
750 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
751 metavar="URL", dest="H"),
752 Option("--remove-other-dead-server", help="Dead DC (name or NTDS GUID) "
753 "to remove ALL references to (rather than this DC)", type=str),
754 Option("--quiet", help="Be quiet", action="store_true"),
755 Option("--verbose", help="Be verbose", action="store_true"),
758 takes_optiongroups = {
759 "sambaopts": options.SambaOptions,
760 "credopts": options.CredentialsOptions,
761 "versionopts": options.VersionOptions,
764 def run(self, sambaopts=None, credopts=None,
765 versionopts=None, server=None,
766 remove_other_dead_server=None, H=None,
767 verbose=False, quiet=False):
768 lp = sambaopts.get_loadparm()
769 creds = credopts.get_credentials(lp)
770 net = Net(creds, lp, server=credopts.ipaddress)
772 logger = self.get_logger()
774 logger.setLevel(logging.DEBUG)
776 logger.setLevel(logging.WARNING)
778 logger.setLevel(logging.INFO)
780 if remove_other_dead_server is not None:
781 if server is not None:
782 samdb = SamDB(url="ldap://%s" % server,
783 session_info=system_session(),
784 credentials=creds, lp=lp)
786 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
788 remove_dc.remove_dc(samdb, logger, remove_other_dead_server)
789 except remove_dc.DemoteException as err:
790 raise CommandError("Demote failed: %s" % err)
793 netbios_name = lp.get("netbios name")
794 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
796 res = samdb.search(expression='(&(objectClass=computer)(serverReferenceBL=*))', attrs=["dnsHostName", "name"])
798 raise CommandError("Unable to search for servers")
801 raise CommandError("You are the latest server in the domain")
805 if str(e["name"]).lower() != netbios_name.lower():
806 server = e["dnsHostName"]
809 ntds_guid = samdb.get_ntds_GUID()
810 msg = samdb.search(base=str(samdb.get_config_basedn()),
811 scope=ldb.SCOPE_SUBTREE, expression="(objectGUID=%s)" % ntds_guid,
813 if len(msg) == 0 or "options" not in msg[0]:
814 raise CommandError("Failed to find options on %s" % ntds_guid)
817 dsa_options = int(str(msg[0]['options']))
819 res = samdb.search(expression="(fSMORoleOwner=%s)" % str(ntds_dn),
820 controls=["search_options:1:2"])
823 raise CommandError("Current DC is still the owner of %d role(s), use the role command to transfer roles to another DC" % len(res))
825 self.errf.write("Using %s as partner server for the demotion\n" %
827 (drsuapiBind, drsuapi_handle, supportedExtensions) = drsuapi_connect(server, lp, creds)
829 self.errf.write("Deactivating inbound replication\n")
834 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
835 dsa_options |= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
836 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
840 self.errf.write("Asking partner server %s to synchronize from us\n"
842 for part in (samdb.get_schema_basedn(),
843 samdb.get_config_basedn(),
844 samdb.get_root_basedn()):
845 nc = drsuapi.DsReplicaObjectIdentifier()
848 req1 = drsuapi.DsReplicaSyncRequest1()
849 req1.naming_context = nc;
850 req1.options = drsuapi.DRSUAPI_DRS_WRIT_REP
851 req1.source_dsa_guid = misc.GUID(ntds_guid)
854 drsuapiBind.DsReplicaSync(drsuapi_handle, 1, req1)
855 except RuntimeError as e1:
856 (werr, string) = e1.args
857 if werr == werror.WERR_DS_DRA_NO_REPLICA:
861 "Error while replicating out last local changes from '%s' for demotion, "
862 "re-enabling inbound replication\n" % part)
863 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
864 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
866 raise CommandError("Error while sending a DsReplicaSync for partition '%s'" % str(part), string)
868 remote_samdb = SamDB(url="ldap://%s" % server,
869 session_info=system_session(),
870 credentials=creds, lp=lp)
872 self.errf.write("Changing userControl and container\n")
873 res = remote_samdb.search(base=str(remote_samdb.domain_dn()),
874 expression="(&(objectClass=user)(sAMAccountName=%s$))" %
875 netbios_name.upper(),
876 attrs=["userAccountControl"])
878 uac = int(str(res[0]["userAccountControl"]))
880 except Exception as e:
881 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
883 "Error while demoting, re-enabling inbound replication\n")
884 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
885 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
887 raise CommandError("Error while changing account control", e)
890 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
892 "Error while demoting, re-enabling inbound replication")
893 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
894 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
896 raise CommandError("Unable to find object with samaccountName = %s$"
897 " in the remote dc" % netbios_name.upper())
901 uac &= ~(UF_SERVER_TRUST_ACCOUNT|UF_TRUSTED_FOR_DELEGATION|UF_PARTIAL_SECRETS_ACCOUNT)
902 uac |= UF_WORKSTATION_TRUST_ACCOUNT
907 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
908 ldb.FLAG_MOD_REPLACE,
909 "userAccountControl")
911 remote_samdb.modify(msg)
912 except Exception as e:
913 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
915 "Error while demoting, re-enabling inbound replication")
916 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
917 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
920 raise CommandError("Error while changing account control", e)
922 parent = msg.dn.parent()
923 dc_name = res[0].dn.get_rdn_value()
924 rdn = "CN=%s" % dc_name
926 # Let's move to the Computer container
930 computer_dn = ldb.Dn(remote_samdb, "CN=Computers,%s" % str(remote_samdb.domain_dn()))
931 res = remote_samdb.search(base=computer_dn, expression=rdn, scope=ldb.SCOPE_ONELEVEL)
934 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
935 scope=ldb.SCOPE_ONELEVEL)
936 while(len(res) != 0 and i < 100):
938 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
939 scope=ldb.SCOPE_ONELEVEL)
942 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
944 "Error while demoting, re-enabling inbound replication\n")
945 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
946 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
952 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
953 ldb.FLAG_MOD_REPLACE,
954 "userAccountControl")
956 remote_samdb.modify(msg)
958 raise CommandError("Unable to find a slot for renaming %s,"
959 " all names from %s-1 to %s-%d seemed used" %
960 (str(dc_dn), rdn, rdn, i - 9))
962 newrdn = "%s-%d" % (rdn, i)
965 newdn = ldb.Dn(remote_samdb, "%s,%s" % (newrdn, str(computer_dn)))
966 remote_samdb.rename(dc_dn, newdn)
967 except Exception as e:
968 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
970 "Error while demoting, re-enabling inbound replication\n")
971 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
972 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
978 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
979 ldb.FLAG_MOD_REPLACE,
980 "userAccountControl")
982 remote_samdb.modify(msg)
983 raise CommandError("Error while renaming %s to %s" % (str(dc_dn), str(newdn)), e)
986 server_dsa_dn = samdb.get_serverName()
987 domain = remote_samdb.get_root_basedn()
990 req1 = drsuapi.DsRemoveDSServerRequest1()
991 req1.server_dn = str(server_dsa_dn)
992 req1.domain_dn = str(domain)
995 drsuapiBind.DsRemoveDSServer(drsuapi_handle, 1, req1)
996 except RuntimeError as e3:
997 (werr, string) = e3.args
998 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
1000 "Error while demoting, re-enabling inbound replication\n")
1001 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
1002 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
1008 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
1009 ldb.FLAG_MOD_REPLACE,
1010 "userAccountControl")
1011 remote_samdb.modify(msg)
1012 remote_samdb.rename(newdn, dc_dn)
1013 if werr == werror.WERR_DS_DRA_NO_REPLICA:
1014 raise CommandError("The DC %s is not present on (already removed from) the remote server: " % server_dsa_dn, e)
1016 raise CommandError("Error while sending a removeDsServer of %s: " % server_dsa_dn, e)
1018 remove_dc.remove_sysvol_references(remote_samdb, logger, dc_name)
1020 # These are objects under the computer account that should be deleted
1021 for s in ("CN=Enterprise,CN=NTFRS Subscriptions",
1022 "CN=%s, CN=NTFRS Subscriptions" % lp.get("realm"),
1023 "CN=Domain system Volumes (SYSVOL Share), CN=NTFRS Subscriptions",
1024 "CN=NTFRS Subscriptions"):
1026 remote_samdb.delete(ldb.Dn(remote_samdb,
1027 "%s,%s" % (s, str(newdn))))
1028 except ldb.LdbError as l:
1031 self.errf.write("Demote successful\n")
1034 class cmd_domain_level(Command):
1035 """Raise domain and forest function levels."""
1037 synopsis = "%prog (show|raise <options>) [options]"
1039 takes_optiongroups = {
1040 "sambaopts": options.SambaOptions,
1041 "credopts": options.CredentialsOptions,
1042 "versionopts": options.VersionOptions,
1046 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1047 metavar="URL", dest="H"),
1048 Option("--quiet", help="Be quiet", action="store_true"),
1049 Option("--forest-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
1050 help="The forest function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)"),
1051 Option("--domain-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
1052 help="The domain function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)")
1055 takes_args = ["subcommand"]
1057 def run(self, subcommand, H=None, forest_level=None, domain_level=None,
1058 quiet=False, credopts=None, sambaopts=None, versionopts=None):
1059 lp = sambaopts.get_loadparm()
1060 creds = credopts.get_credentials(lp, fallback_machine=True)
1062 samdb = SamDB(url=H, session_info=system_session(),
1063 credentials=creds, lp=lp)
1065 domain_dn = samdb.domain_dn()
1067 res_forest = samdb.search("CN=Partitions,%s" % samdb.get_config_basedn(),
1068 scope=ldb.SCOPE_BASE, attrs=["msDS-Behavior-Version"])
1069 assert len(res_forest) == 1
1071 res_domain = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1072 attrs=["msDS-Behavior-Version", "nTMixedDomain"])
1073 assert len(res_domain) == 1
1075 res_dc_s = samdb.search("CN=Sites,%s" % samdb.get_config_basedn(),
1076 scope=ldb.SCOPE_SUBTREE, expression="(objectClass=nTDSDSA)",
1077 attrs=["msDS-Behavior-Version"])
1078 assert len(res_dc_s) >= 1
1080 # default values, since "msDS-Behavior-Version" does not exist on Windows 2000 AD
1081 level_forest = DS_DOMAIN_FUNCTION_2000
1082 level_domain = DS_DOMAIN_FUNCTION_2000
1084 if "msDS-Behavior-Version" in res_forest[0]:
1085 level_forest = int(res_forest[0]["msDS-Behavior-Version"][0])
1086 if "msDS-Behavior-Version" in res_domain[0]:
1087 level_domain = int(res_domain[0]["msDS-Behavior-Version"][0])
1088 level_domain_mixed = int(res_domain[0]["nTMixedDomain"][0])
1091 for msg in res_dc_s:
1092 if "msDS-Behavior-Version" in msg:
1093 if min_level_dc is None or int(msg["msDS-Behavior-Version"][0]) < min_level_dc:
1094 min_level_dc = int(msg["msDS-Behavior-Version"][0])
1096 min_level_dc = DS_DOMAIN_FUNCTION_2000
1097 # well, this is the least
1100 if level_forest < DS_DOMAIN_FUNCTION_2000 or level_domain < DS_DOMAIN_FUNCTION_2000:
1101 raise CommandError("Domain and/or forest function level(s) is/are invalid. Correct them or reprovision!")
1102 if min_level_dc < DS_DOMAIN_FUNCTION_2000:
1103 raise CommandError("Lowest function level of a DC is invalid. Correct this or reprovision!")
1104 if level_forest > level_domain:
1105 raise CommandError("Forest function level is higher than the domain level(s). Correct this or reprovision!")
1106 if level_domain > min_level_dc:
1107 raise CommandError("Domain function level is higher than the lowest function level of a DC. Correct this or reprovision!")
1109 if subcommand == "show":
1110 self.message("Domain and forest function level for domain '%s'" % domain_dn)
1111 if level_forest == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1112 self.message("\nATTENTION: You run SAMBA 4 on a forest function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1113 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1114 self.message("\nATTENTION: You run SAMBA 4 on a domain function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1115 if min_level_dc == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1116 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)!")
1120 if level_forest == DS_DOMAIN_FUNCTION_2000:
1122 elif level_forest == DS_DOMAIN_FUNCTION_2003_MIXED:
1123 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1124 elif level_forest == DS_DOMAIN_FUNCTION_2003:
1126 elif level_forest == DS_DOMAIN_FUNCTION_2008:
1128 elif level_forest == DS_DOMAIN_FUNCTION_2008_R2:
1130 elif level_forest == DS_DOMAIN_FUNCTION_2012:
1132 elif level_forest == DS_DOMAIN_FUNCTION_2012_R2:
1135 outstr = "higher than 2012 R2"
1136 self.message("Forest function level: (Windows) " + outstr)
1138 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1139 outstr = "2000 mixed (NT4 DC support)"
1140 elif level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed == 0:
1142 elif level_domain == DS_DOMAIN_FUNCTION_2003_MIXED:
1143 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1144 elif level_domain == DS_DOMAIN_FUNCTION_2003:
1146 elif level_domain == DS_DOMAIN_FUNCTION_2008:
1148 elif level_domain == DS_DOMAIN_FUNCTION_2008_R2:
1150 elif level_domain == DS_DOMAIN_FUNCTION_2012:
1152 elif level_domain == DS_DOMAIN_FUNCTION_2012_R2:
1155 outstr = "higher than 2012 R2"
1156 self.message("Domain function level: (Windows) " + outstr)
1158 if min_level_dc == DS_DOMAIN_FUNCTION_2000:
1160 elif min_level_dc == DS_DOMAIN_FUNCTION_2003:
1162 elif min_level_dc == DS_DOMAIN_FUNCTION_2008:
1164 elif min_level_dc == DS_DOMAIN_FUNCTION_2008_R2:
1166 elif min_level_dc == DS_DOMAIN_FUNCTION_2012:
1168 elif min_level_dc == DS_DOMAIN_FUNCTION_2012_R2:
1171 outstr = "higher than 2012 R2"
1172 self.message("Lowest function level of a DC: (Windows) " + outstr)
1174 elif subcommand == "raise":
1177 if domain_level is not None:
1178 if domain_level == "2003":
1179 new_level_domain = DS_DOMAIN_FUNCTION_2003
1180 elif domain_level == "2008":
1181 new_level_domain = DS_DOMAIN_FUNCTION_2008
1182 elif domain_level == "2008_R2":
1183 new_level_domain = DS_DOMAIN_FUNCTION_2008_R2
1184 elif domain_level == "2012":
1185 new_level_domain = DS_DOMAIN_FUNCTION_2012
1186 elif domain_level == "2012_R2":
1187 new_level_domain = DS_DOMAIN_FUNCTION_2012_R2
1189 if new_level_domain <= level_domain and level_domain_mixed == 0:
1190 raise CommandError("Domain function level can't be smaller than or equal to the actual one!")
1191 if new_level_domain > min_level_dc:
1192 raise CommandError("Domain function level can't be higher than the lowest function level of a DC!")
1194 # Deactivate mixed/interim domain support
1195 if level_domain_mixed != 0:
1196 # Directly on the base DN
1198 m.dn = ldb.Dn(samdb, domain_dn)
1199 m["nTMixedDomain"] = ldb.MessageElement("0",
1200 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1204 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup") + ",CN=Partitions,%s" % samdb.get_config_basedn())
1205 m["nTMixedDomain"] = ldb.MessageElement("0",
1206 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1209 except ldb.LdbError as e:
1210 (enum, emsg) = e.args
1211 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1214 # Directly on the base DN
1216 m.dn = ldb.Dn(samdb, domain_dn)
1217 m["msDS-Behavior-Version"]= ldb.MessageElement(
1218 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1219 "msDS-Behavior-Version")
1223 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup")
1224 + ",CN=Partitions,%s" % samdb.get_config_basedn())
1225 m["msDS-Behavior-Version"]= ldb.MessageElement(
1226 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1227 "msDS-Behavior-Version")
1230 except ldb.LdbError as e2:
1231 (enum, emsg) = e2.args
1232 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1235 level_domain = new_level_domain
1236 msgs.append("Domain function level changed!")
1238 if forest_level is not None:
1239 if forest_level == "2003":
1240 new_level_forest = DS_DOMAIN_FUNCTION_2003
1241 elif forest_level == "2008":
1242 new_level_forest = DS_DOMAIN_FUNCTION_2008
1243 elif forest_level == "2008_R2":
1244 new_level_forest = DS_DOMAIN_FUNCTION_2008_R2
1245 elif forest_level == "2012":
1246 new_level_forest = DS_DOMAIN_FUNCTION_2012
1247 elif forest_level == "2012_R2":
1248 new_level_forest = DS_DOMAIN_FUNCTION_2012_R2
1250 if new_level_forest <= level_forest:
1251 raise CommandError("Forest function level can't be smaller than or equal to the actual one!")
1252 if new_level_forest > level_domain:
1253 raise CommandError("Forest function level can't be higher than the domain function level(s). Please raise it/them first!")
1256 m.dn = ldb.Dn(samdb, "CN=Partitions,%s" % samdb.get_config_basedn())
1257 m["msDS-Behavior-Version"]= ldb.MessageElement(
1258 str(new_level_forest), ldb.FLAG_MOD_REPLACE,
1259 "msDS-Behavior-Version")
1261 msgs.append("Forest function level changed!")
1262 msgs.append("All changes applied successfully!")
1263 self.message("\n".join(msgs))
1265 raise CommandError("invalid argument: '%s' (choose from 'show', 'raise')" % subcommand)
1267 class cmd_domain_passwordsettings_show(Command):
1268 """Display current password settings for the domain."""
1270 synopsis = "%prog [options]"
1272 takes_optiongroups = {
1273 "sambaopts": options.SambaOptions,
1274 "versionopts": options.VersionOptions,
1275 "credopts": options.CredentialsOptions,
1279 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1280 metavar="URL", dest="H"),
1283 def run(self, H=None, credopts=None, sambaopts=None, versionopts=None):
1284 lp = sambaopts.get_loadparm()
1285 creds = credopts.get_credentials(lp)
1287 samdb = SamDB(url=H, session_info=system_session(),
1288 credentials=creds, lp=lp)
1290 domain_dn = samdb.domain_dn()
1291 res = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1292 attrs=["pwdProperties", "pwdHistoryLength", "minPwdLength",
1293 "minPwdAge", "maxPwdAge", "lockoutDuration", "lockoutThreshold",
1294 "lockOutObservationWindow"])
1295 assert(len(res) == 1)
1297 pwd_props = int(res[0]["pwdProperties"][0])
1298 pwd_hist_len = int(res[0]["pwdHistoryLength"][0])
1299 cur_min_pwd_len = int(res[0]["minPwdLength"][0])
1301 cur_min_pwd_age = int(abs(int(res[0]["minPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1302 if int(res[0]["maxPwdAge"][0]) == -0x8000000000000000:
1305 cur_max_pwd_age = int(abs(int(res[0]["maxPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1306 cur_account_lockout_threshold = int(res[0]["lockoutThreshold"][0])
1308 if int(res[0]["lockoutDuration"][0]) == -0x8000000000000000:
1309 cur_account_lockout_duration = 0
1311 cur_account_lockout_duration = abs(int(res[0]["lockoutDuration"][0])) / (1e7 * 60)
1312 cur_reset_account_lockout_after = abs(int(res[0]["lockOutObservationWindow"][0])) / (1e7 * 60)
1313 except Exception as e:
1314 raise CommandError("Could not retrieve password properties!", e)
1316 self.message("Password informations for domain '%s'" % domain_dn)
1318 if pwd_props & DOMAIN_PASSWORD_COMPLEX != 0:
1319 self.message("Password complexity: on")
1321 self.message("Password complexity: off")
1322 if pwd_props & DOMAIN_PASSWORD_STORE_CLEARTEXT != 0:
1323 self.message("Store plaintext passwords: on")
1325 self.message("Store plaintext passwords: off")
1326 self.message("Password history length: %d" % pwd_hist_len)
1327 self.message("Minimum password length: %d" % cur_min_pwd_len)
1328 self.message("Minimum password age (days): %d" % cur_min_pwd_age)
1329 self.message("Maximum password age (days): %d" % cur_max_pwd_age)
1330 self.message("Account lockout duration (mins): %d" % cur_account_lockout_duration)
1331 self.message("Account lockout threshold (attempts): %d" % cur_account_lockout_threshold)
1332 self.message("Reset account lockout after (mins): %d" % cur_reset_account_lockout_after)
1334 class cmd_domain_passwordsettings_set(Command):
1335 """Set password settings.
1337 Password complexity, password lockout policy, history length,
1338 minimum password length, the minimum and maximum password age) on
1339 a Samba AD DC server.
1341 Use against a Windows DC is possible, but group policy will override it.
1344 synopsis = "%prog <options> [options]"
1346 takes_optiongroups = {
1347 "sambaopts": options.SambaOptions,
1348 "versionopts": options.VersionOptions,
1349 "credopts": options.CredentialsOptions,
1353 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1354 metavar="URL", dest="H"),
1355 Option("--quiet", help="Be quiet", action="store_true"),
1356 Option("--complexity", type="choice", choices=["on","off","default"],
1357 help="The password complexity (on | off | default). Default is 'on'"),
1358 Option("--store-plaintext", type="choice", choices=["on","off","default"],
1359 help="Store plaintext passwords where account have 'store passwords with reversible encryption' set (on | off | default). Default is 'off'"),
1360 Option("--history-length",
1361 help="The password history length (<integer> | default). Default is 24.", type=str),
1362 Option("--min-pwd-length",
1363 help="The minimum password length (<integer> | default). Default is 7.", type=str),
1364 Option("--min-pwd-age",
1365 help="The minimum password age (<integer in days> | default). Default is 1.", type=str),
1366 Option("--max-pwd-age",
1367 help="The maximum password age (<integer in days> | default). Default is 43.", type=str),
1368 Option("--account-lockout-duration",
1369 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),
1370 Option("--account-lockout-threshold",
1371 help="The number of bad password attempts allowed before locking out the account (<integer> | default). Default is 0 (never lock out).", type=str),
1372 Option("--reset-account-lockout-after",
1373 help="After this time is elapsed, the recorded number of attempts restarts from zero (<integer> | default). Default is 30.", type=str),
1376 def run(self, H=None, min_pwd_age=None, max_pwd_age=None,
1377 quiet=False, complexity=None, store_plaintext=None, history_length=None,
1378 min_pwd_length=None, account_lockout_duration=None, account_lockout_threshold=None,
1379 reset_account_lockout_after=None, credopts=None, sambaopts=None,
1381 lp = sambaopts.get_loadparm()
1382 creds = credopts.get_credentials(lp)
1384 samdb = SamDB(url=H, session_info=system_session(),
1385 credentials=creds, lp=lp)
1387 domain_dn = samdb.domain_dn()
1390 m.dn = ldb.Dn(samdb, domain_dn)
1391 pwd_props = int(samdb.get_pwdProperties())
1393 if complexity is not None:
1394 if complexity == "on" or complexity == "default":
1395 pwd_props = pwd_props | DOMAIN_PASSWORD_COMPLEX
1396 msgs.append("Password complexity activated!")
1397 elif complexity == "off":
1398 pwd_props = pwd_props & (~DOMAIN_PASSWORD_COMPLEX)
1399 msgs.append("Password complexity deactivated!")
1401 if store_plaintext is not None:
1402 if store_plaintext == "on" or store_plaintext == "default":
1403 pwd_props = pwd_props | DOMAIN_PASSWORD_STORE_CLEARTEXT
1404 msgs.append("Plaintext password storage for changed passwords activated!")
1405 elif store_plaintext == "off":
1406 pwd_props = pwd_props & (~DOMAIN_PASSWORD_STORE_CLEARTEXT)
1407 msgs.append("Plaintext password storage for changed passwords deactivated!")
1409 if complexity is not None or store_plaintext is not None:
1410 m["pwdProperties"] = ldb.MessageElement(str(pwd_props),
1411 ldb.FLAG_MOD_REPLACE, "pwdProperties")
1413 if history_length is not None:
1414 if history_length == "default":
1417 pwd_hist_len = int(history_length)
1419 if pwd_hist_len < 0 or pwd_hist_len > 24:
1420 raise CommandError("Password history length must be in the range of 0 to 24!")
1422 m["pwdHistoryLength"] = ldb.MessageElement(str(pwd_hist_len),
1423 ldb.FLAG_MOD_REPLACE, "pwdHistoryLength")
1424 msgs.append("Password history length changed!")
1426 if min_pwd_length is not None:
1427 if min_pwd_length == "default":
1430 min_pwd_len = int(min_pwd_length)
1432 if min_pwd_len < 0 or min_pwd_len > 14:
1433 raise CommandError("Minimum password length must be in the range of 0 to 14!")
1435 m["minPwdLength"] = ldb.MessageElement(str(min_pwd_len),
1436 ldb.FLAG_MOD_REPLACE, "minPwdLength")
1437 msgs.append("Minimum password length changed!")
1439 if min_pwd_age is not None:
1440 if min_pwd_age == "default":
1443 min_pwd_age = int(min_pwd_age)
1445 if min_pwd_age < 0 or min_pwd_age > 998:
1446 raise CommandError("Minimum password age must be in the range of 0 to 998!")
1449 min_pwd_age_ticks = -int(min_pwd_age * (24 * 60 * 60 * 1e7))
1451 m["minPwdAge"] = ldb.MessageElement(str(min_pwd_age_ticks),
1452 ldb.FLAG_MOD_REPLACE, "minPwdAge")
1453 msgs.append("Minimum password age changed!")
1455 if max_pwd_age is not None:
1456 if max_pwd_age == "default":
1459 max_pwd_age = int(max_pwd_age)
1461 if max_pwd_age < 0 or max_pwd_age > 999:
1462 raise CommandError("Maximum password age must be in the range of 0 to 999!")
1465 if max_pwd_age == 0:
1466 max_pwd_age_ticks = -0x8000000000000000
1468 max_pwd_age_ticks = -int(max_pwd_age * (24 * 60 * 60 * 1e7))
1470 m["maxPwdAge"] = ldb.MessageElement(str(max_pwd_age_ticks),
1471 ldb.FLAG_MOD_REPLACE, "maxPwdAge")
1472 msgs.append("Maximum password age changed!")
1474 if account_lockout_duration is not None:
1475 if account_lockout_duration == "default":
1476 account_lockout_duration = 30
1478 account_lockout_duration = int(account_lockout_duration)
1480 if account_lockout_duration < 0 or account_lockout_duration > 99999:
1481 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1484 if account_lockout_duration == 0:
1485 account_lockout_duration_ticks = -0x8000000000000000
1487 account_lockout_duration_ticks = -int(account_lockout_duration * (60 * 1e7))
1489 m["lockoutDuration"] = ldb.MessageElement(str(account_lockout_duration_ticks),
1490 ldb.FLAG_MOD_REPLACE, "lockoutDuration")
1491 msgs.append("Account lockout duration changed!")
1493 if account_lockout_threshold is not None:
1494 if account_lockout_threshold == "default":
1495 account_lockout_threshold = 0
1497 account_lockout_threshold = int(account_lockout_threshold)
1499 m["lockoutThreshold"] = ldb.MessageElement(str(account_lockout_threshold),
1500 ldb.FLAG_MOD_REPLACE, "lockoutThreshold")
1501 msgs.append("Account lockout threshold changed!")
1503 if reset_account_lockout_after is not None:
1504 if reset_account_lockout_after == "default":
1505 reset_account_lockout_after = 30
1507 reset_account_lockout_after = int(reset_account_lockout_after)
1509 if reset_account_lockout_after < 0 or reset_account_lockout_after > 99999:
1510 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1513 if reset_account_lockout_after == 0:
1514 reset_account_lockout_after_ticks = -0x8000000000000000
1516 reset_account_lockout_after_ticks = -int(reset_account_lockout_after * (60 * 1e7))
1518 m["lockOutObservationWindow"] = ldb.MessageElement(str(reset_account_lockout_after_ticks),
1519 ldb.FLAG_MOD_REPLACE, "lockOutObservationWindow")
1520 msgs.append("Duration to reset account lockout after changed!")
1522 if max_pwd_age > 0 and min_pwd_age >= max_pwd_age:
1523 raise CommandError("Maximum password age (%d) must be greater than minimum password age (%d)!" % (max_pwd_age, min_pwd_age))
1526 raise CommandError("You must specify at least one option to set. Try --help")
1528 msgs.append("All changes applied successfully!")
1529 self.message("\n".join(msgs))
1531 class cmd_domain_passwordsettings(SuperCommand):
1532 """Manage password policy settings."""
1535 subcommands["show"] = cmd_domain_passwordsettings_show()
1536 subcommands["set"] = cmd_domain_passwordsettings_set()
1538 class cmd_domain_classicupgrade(Command):
1539 """Upgrade from Samba classic (NT4-like) database to Samba AD DC database.
1541 Specify either a directory with all Samba classic DC databases and state files (with --dbdir) or
1542 the testparm utility from your classic installation (with --testparm).
1545 synopsis = "%prog [options] <classic_smb_conf>"
1547 takes_optiongroups = {
1548 "sambaopts": options.SambaOptions,
1549 "versionopts": options.VersionOptions
1553 Option("--dbdir", type="string", metavar="DIR",
1554 help="Path to samba classic DC database directory"),
1555 Option("--testparm", type="string", metavar="PATH",
1556 help="Path to samba classic DC testparm utility from the previous installation. This allows the default paths of the previous installation to be followed"),
1557 Option("--targetdir", type="string", metavar="DIR",
1558 help="Path prefix where the new Samba 4.0 AD domain should be initialised"),
1559 Option("--quiet", help="Be quiet", action="store_true"),
1560 Option("--verbose", help="Be verbose", action="store_true"),
1561 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
1562 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
1563 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
1564 "BIND9_FLATFILE uses bind9 text database to store zone information, "
1565 "BIND9_DLZ uses samba4 AD to store zone information, "
1566 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
1567 default="SAMBA_INTERNAL")
1571 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
1572 action="store_true"),
1573 Option("--use-xattrs", type="choice", choices=["yes","no","auto"],
1574 metavar="[yes|no|auto]",
1575 help="Define if we should use the native fs capabilities or a tdb file for "
1576 "storing attributes likes ntacl when --use-ntvfs is set. "
1577 "auto tries to make an inteligent guess based on the user rights and system capabilities",
1580 if samba.is_ntvfs_fileserver_built():
1581 takes_options.extend(ntvfs_options)
1583 takes_args = ["smbconf"]
1585 def run(self, smbconf=None, targetdir=None, dbdir=None, testparm=None,
1586 quiet=False, verbose=False, use_xattrs="auto", sambaopts=None, versionopts=None,
1587 dns_backend=None, use_ntvfs=False):
1589 if not os.path.exists(smbconf):
1590 raise CommandError("File %s does not exist" % smbconf)
1592 if testparm and not os.path.exists(testparm):
1593 raise CommandError("Testparm utility %s does not exist" % testparm)
1595 if dbdir and not os.path.exists(dbdir):
1596 raise CommandError("Directory %s does not exist" % dbdir)
1598 if not dbdir and not testparm:
1599 raise CommandError("Please specify either dbdir or testparm")
1601 logger = self.get_logger()
1603 logger.setLevel(logging.DEBUG)
1605 logger.setLevel(logging.WARNING)
1607 logger.setLevel(logging.INFO)
1609 if dbdir and testparm:
1610 logger.warning("both dbdir and testparm specified, ignoring dbdir.")
1613 lp = sambaopts.get_loadparm()
1615 s3conf = s3param.get_context()
1618 s3conf.set("realm", sambaopts.realm)
1620 if targetdir is not None:
1621 if not os.path.isdir(targetdir):
1625 if use_xattrs == "yes":
1627 elif use_xattrs == "auto" and use_ntvfs == False:
1629 elif use_ntvfs == False:
1630 raise CommandError("--use-xattrs=no requires --use-ntvfs (not supported for production use). "
1631 "Please re-run with --use-xattrs omitted.")
1632 elif use_xattrs == "auto" and not s3conf.get("posix:eadb"):
1634 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
1636 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
1639 samba.ntacls.setntacl(lp, tmpfile.name,
1640 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
1643 # FIXME: Don't catch all exceptions here
1644 logger.info("You are not root or your system does not support xattr, using tdb backend for attributes. "
1645 "If you intend to use this provision in production, rerun the script as root on a system supporting xattrs.")
1649 # Set correct default values from dbdir or testparm
1652 paths["state directory"] = dbdir
1653 paths["private dir"] = dbdir
1654 paths["lock directory"] = dbdir
1655 paths["smb passwd file"] = dbdir + "/smbpasswd"
1657 paths["state directory"] = get_testparm_var(testparm, smbconf, "state directory")
1658 paths["private dir"] = get_testparm_var(testparm, smbconf, "private dir")
1659 paths["smb passwd file"] = get_testparm_var(testparm, smbconf, "smb passwd file")
1660 paths["lock directory"] = get_testparm_var(testparm, smbconf, "lock directory")
1661 # "testparm" from Samba 3 < 3.4.x is not aware of the parameter
1662 # "state directory", instead make use of "lock directory"
1663 if len(paths["state directory"]) == 0:
1664 paths["state directory"] = paths["lock directory"]
1667 s3conf.set(p, paths[p])
1669 # load smb.conf parameters
1670 logger.info("Reading smb.conf")
1671 s3conf.load(smbconf)
1672 samba3 = Samba3(smbconf, s3conf)
1674 logger.info("Provisioning")
1675 upgrade_from_samba3(samba3, logger, targetdir, session_info=system_session(),
1676 useeadb=eadb, dns_backend=dns_backend, use_ntvfs=use_ntvfs)
1679 class cmd_domain_samba3upgrade(cmd_domain_classicupgrade):
1680 __doc__ = cmd_domain_classicupgrade.__doc__
1682 # This command is present for backwards compatibility only,
1683 # and should not be shown.
1687 class LocalDCCredentialsOptions(options.CredentialsOptions):
1688 def __init__(self, parser):
1689 options.CredentialsOptions.__init__(self, parser, special_name="local-dc")
1691 class DomainTrustCommand(Command):
1692 """List domain trusts."""
1695 Command.__init__(self)
1696 self.local_lp = None
1698 self.local_server = None
1699 self.local_binding_string = None
1700 self.local_creds = None
1702 self.remote_server = None
1703 self.remote_binding_string = None
1704 self.remote_creds = None
1706 def _uint32(self, v):
1707 return ctypes.c_uint32(v).value
1709 def check_runtime_error(self, runtime, val):
1713 err32 = self._uint32(runtime[0])
1719 class LocalRuntimeError(CommandError):
1720 def __init__(exception_self, self, runtime, message):
1721 err32 = self._uint32(runtime[0])
1723 msg = "LOCAL_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1724 self.local_server, message, err32, errstr)
1725 CommandError.__init__(exception_self, msg)
1727 class RemoteRuntimeError(CommandError):
1728 def __init__(exception_self, self, runtime, message):
1729 err32 = self._uint32(runtime[0])
1731 msg = "REMOTE_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1732 self.remote_server, message, err32, errstr)
1733 CommandError.__init__(exception_self, msg)
1735 class LocalLdbError(CommandError):
1736 def __init__(exception_self, self, ldb_error, message):
1737 errval = ldb_error[0]
1738 errstr = ldb_error[1]
1739 msg = "LOCAL_DC[%s]: %s - ERROR(%d) - %s" % (
1740 self.local_server, message, errval, errstr)
1741 CommandError.__init__(exception_self, msg)
1743 def setup_local_server(self, sambaopts, localdcopts):
1744 if self.local_server is not None:
1745 return self.local_server
1747 lp = sambaopts.get_loadparm()
1749 local_server = localdcopts.ipaddress
1750 if local_server is None:
1751 server_role = lp.server_role()
1752 if server_role != "ROLE_ACTIVE_DIRECTORY_DC":
1753 raise CommandError("Invalid server_role %s" % (server_role))
1754 local_server = lp.get('netbios name')
1755 local_transport = "ncalrpc"
1756 local_binding_options = ""
1757 local_binding_options += ",auth_type=ncalrpc_as_system"
1758 local_ldap_url = None
1761 local_transport = "ncacn_np"
1762 local_binding_options = ""
1763 local_ldap_url = "ldap://%s" % local_server
1764 local_creds = localdcopts.get_credentials(lp)
1768 self.local_server = local_server
1769 self.local_binding_string = "%s:%s[%s]" % (local_transport, local_server, local_binding_options)
1770 self.local_ldap_url = local_ldap_url
1771 self.local_creds = local_creds
1772 return self.local_server
1774 def new_local_lsa_connection(self):
1775 return lsa.lsarpc(self.local_binding_string, self.local_lp, self.local_creds)
1777 def new_local_netlogon_connection(self):
1778 return netlogon.netlogon(self.local_binding_string, self.local_lp, self.local_creds)
1780 def new_local_ldap_connection(self):
1781 return SamDB(url=self.local_ldap_url,
1782 session_info=system_session(),
1783 credentials=self.local_creds,
1786 def setup_remote_server(self, credopts, domain,
1788 require_writable=True):
1791 assert require_writable
1793 if self.remote_server is not None:
1794 return self.remote_server
1796 self.remote_server = "__unknown__remote_server__.%s" % domain
1797 assert self.local_server is not None
1799 remote_creds = credopts.get_credentials(self.local_lp)
1800 remote_server = credopts.ipaddress
1801 remote_binding_options = ""
1803 # TODO: we should also support NT4 domains
1804 # we could use local_netlogon.netr_DsRGetDCNameEx2() with the remote domain name
1805 # and delegate NBT or CLDAP to the local netlogon server
1807 remote_net = Net(remote_creds, self.local_lp, server=remote_server)
1808 remote_flags = nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS
1809 if require_writable:
1810 remote_flags |= nbt.NBT_SERVER_WRITABLE
1812 remote_flags |= nbt.NBT_SERVER_PDC
1813 remote_info = remote_net.finddc(flags=remote_flags, domain=domain, address=remote_server)
1814 except NTSTATUSError as error:
1815 raise CommandError("Failed to find a writeable DC for domain '%s': %s" %
1818 raise CommandError("Failed to find a writeable DC for domain '%s'" % domain)
1820 nbt.NBT_SERVER_PDC: "PDC",
1821 nbt.NBT_SERVER_GC: "GC",
1822 nbt.NBT_SERVER_LDAP: "LDAP",
1823 nbt.NBT_SERVER_DS: "DS",
1824 nbt.NBT_SERVER_KDC: "KDC",
1825 nbt.NBT_SERVER_TIMESERV: "TIMESERV",
1826 nbt.NBT_SERVER_CLOSEST: "CLOSEST",
1827 nbt.NBT_SERVER_WRITABLE: "WRITABLE",
1828 nbt.NBT_SERVER_GOOD_TIMESERV: "GOOD_TIMESERV",
1829 nbt.NBT_SERVER_NDNC: "NDNC",
1830 nbt.NBT_SERVER_SELECT_SECRET_DOMAIN_6: "SELECT_SECRET_DOMAIN_6",
1831 nbt.NBT_SERVER_FULL_SECRET_DOMAIN_6: "FULL_SECRET_DOMAIN_6",
1832 nbt.NBT_SERVER_ADS_WEB_SERVICE: "ADS_WEB_SERVICE",
1833 nbt.NBT_SERVER_DS_8: "DS_8",
1834 nbt.NBT_SERVER_HAS_DNS_NAME: "HAS_DNS_NAME",
1835 nbt.NBT_SERVER_IS_DEFAULT_NC: "IS_DEFAULT_NC",
1836 nbt.NBT_SERVER_FOREST_ROOT: "FOREST_ROOT",
1838 server_type_string = self.generic_bitmap_to_string(flag_map,
1839 remote_info.server_type, names_only=True)
1840 self.outf.write("RemoteDC Netbios[%s] DNS[%s] ServerType[%s]\n" % (
1841 remote_info.pdc_name,
1842 remote_info.pdc_dns_name,
1843 server_type_string))
1845 self.remote_server = remote_info.pdc_dns_name
1846 self.remote_binding_string="ncacn_np:%s[%s]" % (self.remote_server, remote_binding_options)
1847 self.remote_creds = remote_creds
1848 return self.remote_server
1850 def new_remote_lsa_connection(self):
1851 return lsa.lsarpc(self.remote_binding_string, self.local_lp, self.remote_creds)
1853 def new_remote_netlogon_connection(self):
1854 return netlogon.netlogon(self.remote_binding_string, self.local_lp, self.remote_creds)
1856 def get_lsa_info(self, conn, policy_access):
1857 objectAttr = lsa.ObjectAttribute()
1858 objectAttr.sec_qos = lsa.QosInfo()
1860 policy = conn.OpenPolicy2(''.decode('utf-8'),
1861 objectAttr, policy_access)
1863 info = conn.QueryInfoPolicy2(policy, lsa.LSA_POLICY_INFO_DNS)
1865 return (policy, info)
1867 def get_netlogon_dc_info(self, conn, server):
1868 info = conn.netr_DsRGetDCNameEx2(server,
1869 None, 0, None, None, None,
1870 netlogon.DS_RETURN_DNS_NAME)
1873 def netr_DomainTrust_to_name(self, t):
1874 if t.trust_type == lsa.LSA_TRUST_TYPE_DOWNLEVEL:
1875 return t.netbios_name
1879 def netr_DomainTrust_to_type(self, a, t):
1881 primary_parent = None
1883 if _t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
1885 if not _t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1886 primary_parent = a[_t.parent_index]
1889 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1890 if t is primary_parent:
1893 if t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1896 parent = a[t.parent_index]
1897 if parent is primary:
1902 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1907 def netr_DomainTrust_to_transitive(self, t):
1908 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1911 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE:
1914 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1919 def netr_DomainTrust_to_direction(self, t):
1920 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND and \
1921 t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1924 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND:
1927 if t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1932 def generic_enum_to_string(self, e_dict, v, names_only=False):
1936 v32 = self._uint32(v)
1937 w = "__unknown__%08X__" % v32
1939 r = "0x%x (%s)" % (v, w)
1942 def generic_bitmap_to_string(self, b_dict, v, names_only=False):
1947 for b in sorted(b_dict.keys()):
1954 c32 = self._uint32(c)
1955 s += ["__unknown_%08X__" % c32]
1960 r = "0x%x (%s)" % (v, w)
1963 def trustType_string(self, v):
1965 lsa.LSA_TRUST_TYPE_DOWNLEVEL : "DOWNLEVEL",
1966 lsa.LSA_TRUST_TYPE_UPLEVEL : "UPLEVEL",
1967 lsa.LSA_TRUST_TYPE_MIT : "MIT",
1968 lsa.LSA_TRUST_TYPE_DCE : "DCE",
1970 return self.generic_enum_to_string(types, v)
1972 def trustDirection_string(self, v):
1974 lsa.LSA_TRUST_DIRECTION_INBOUND |
1975 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "BOTH",
1976 lsa.LSA_TRUST_DIRECTION_INBOUND : "INBOUND",
1977 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "OUTBOUND",
1979 return self.generic_enum_to_string(directions, v)
1981 def trustAttributes_string(self, v):
1983 lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE : "NON_TRANSITIVE",
1984 lsa.LSA_TRUST_ATTRIBUTE_UPLEVEL_ONLY : "UPLEVEL_ONLY",
1985 lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN : "QUARANTINED_DOMAIN",
1986 lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE : "FOREST_TRANSITIVE",
1987 lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION : "CROSS_ORGANIZATION",
1988 lsa.LSA_TRUST_ATTRIBUTE_WITHIN_FOREST : "WITHIN_FOREST",
1989 lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL : "TREAT_AS_EXTERNAL",
1990 lsa.LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION : "USES_RC4_ENCRYPTION",
1992 return self.generic_bitmap_to_string(attributes, v)
1994 def kerb_EncTypes_string(self, v):
1996 security.KERB_ENCTYPE_DES_CBC_CRC : "DES_CBC_CRC",
1997 security.KERB_ENCTYPE_DES_CBC_MD5 : "DES_CBC_MD5",
1998 security.KERB_ENCTYPE_RC4_HMAC_MD5 : "RC4_HMAC_MD5",
1999 security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96 : "AES128_CTS_HMAC_SHA1_96",
2000 security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96 : "AES256_CTS_HMAC_SHA1_96",
2001 security.KERB_ENCTYPE_FAST_SUPPORTED : "FAST_SUPPORTED",
2002 security.KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED : "COMPOUND_IDENTITY_SUPPORTED",
2003 security.KERB_ENCTYPE_CLAIMS_SUPPORTED : "CLAIMS_SUPPORTED",
2004 security.KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED : "RESOURCE_SID_COMPRESSION_DISABLED",
2006 return self.generic_bitmap_to_string(enctypes, v)
2008 def entry_tln_status(self, e_flags, ):
2010 return "Status[Enabled]"
2013 lsa.LSA_TLN_DISABLED_NEW : "Disabled-New",
2014 lsa.LSA_TLN_DISABLED_ADMIN : "Disabled",
2015 lsa.LSA_TLN_DISABLED_CONFLICT : "Disabled-Conflicting",
2017 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
2019 def entry_dom_status(self, e_flags):
2021 return "Status[Enabled]"
2024 lsa.LSA_SID_DISABLED_ADMIN : "Disabled-SID",
2025 lsa.LSA_SID_DISABLED_CONFLICT : "Disabled-SID-Conflicting",
2026 lsa.LSA_NB_DISABLED_ADMIN : "Disabled-NB",
2027 lsa.LSA_NB_DISABLED_CONFLICT : "Disabled-NB-Conflicting",
2029 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
2031 def write_forest_trust_info(self, fti, tln=None, collisions=None):
2033 tln_string = " TDO[%s]" % tln
2037 self.outf.write("Namespaces[%d]%s:\n" % (
2038 len(fti.entries), tln_string))
2040 for i in xrange(0, len(fti.entries)):
2044 collision_string = ""
2046 if collisions is not None:
2047 for c in collisions.entries:
2051 collision_string = " Collision[%s]" % (c.name.string)
2053 d = e.forest_trust_data
2054 if e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
2055 self.outf.write("TLN: %-32s DNS[*.%s]%s\n" % (
2056 self.entry_tln_status(flags),
2057 d.string, collision_string))
2058 elif e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
2059 self.outf.write("TLN_EX: %-29s DNS[*.%s]\n" % (
2061 elif e.type == lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
2062 self.outf.write("DOM: %-32s DNS[%s] Netbios[%s] SID[%s]%s\n" % (
2063 self.entry_dom_status(flags),
2064 d.dns_domain_name.string,
2065 d.netbios_domain_name.string,
2066 d.domain_sid, collision_string))
2069 class cmd_domain_trust_list(DomainTrustCommand):
2070 """List domain trusts."""
2072 synopsis = "%prog [options]"
2074 takes_optiongroups = {
2075 "sambaopts": options.SambaOptions,
2076 "versionopts": options.VersionOptions,
2077 "localdcopts": LocalDCCredentialsOptions,
2083 def run(self, sambaopts=None, versionopts=None, localdcopts=None):
2085 local_server = self.setup_local_server(sambaopts, localdcopts)
2087 local_netlogon = self.new_local_netlogon_connection()
2088 except RuntimeError as error:
2089 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2092 local_netlogon_trusts = local_netlogon.netr_DsrEnumerateDomainTrusts(local_server,
2093 netlogon.NETR_TRUST_FLAG_IN_FOREST |
2094 netlogon.NETR_TRUST_FLAG_OUTBOUND |
2095 netlogon.NETR_TRUST_FLAG_INBOUND)
2096 except RuntimeError as error:
2097 if self.check_runtime_error(error, werror.WERR_RPC_S_PROCNUM_OUT_OF_RANGE):
2098 # TODO: we could implement a fallback to lsa.EnumTrustDom()
2099 raise CommandError("LOCAL_DC[%s]: netr_DsrEnumerateDomainTrusts not supported." % (
2101 raise self.LocalRuntimeError(self, error, "netr_DsrEnumerateDomainTrusts failed")
2103 a = local_netlogon_trusts.array
2105 if t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
2107 self.outf.write("%-14s %-15s %-19s %s\n" % (
2108 "Type[%s]" % self.netr_DomainTrust_to_type(a, t),
2109 "Transitive[%s]" % self.netr_DomainTrust_to_transitive(t),
2110 "Direction[%s]" % self.netr_DomainTrust_to_direction(t),
2111 "Name[%s]" % self.netr_DomainTrust_to_name(t)))
2114 class cmd_domain_trust_show(DomainTrustCommand):
2115 """Show trusted domain details."""
2117 synopsis = "%prog NAME [options]"
2119 takes_optiongroups = {
2120 "sambaopts": options.SambaOptions,
2121 "versionopts": options.VersionOptions,
2122 "localdcopts": LocalDCCredentialsOptions,
2128 takes_args = ["domain"]
2130 def run(self, domain, sambaopts=None, versionopts=None, localdcopts=None):
2132 local_server = self.setup_local_server(sambaopts, localdcopts)
2134 local_lsa = self.new_local_lsa_connection()
2135 except RuntimeError as error:
2136 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2139 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2140 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2141 except RuntimeError as error:
2142 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2144 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2145 local_lsa_info.name.string,
2146 local_lsa_info.dns_domain.string,
2147 local_lsa_info.sid))
2149 lsaString = lsa.String()
2150 lsaString.string = domain
2152 local_tdo_full = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2153 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2154 local_tdo_info = local_tdo_full.info_ex
2155 local_tdo_posix = local_tdo_full.posix_offset
2156 except NTSTATUSError as error:
2157 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2158 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2160 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(FULL_INFO) failed")
2163 local_tdo_enctypes = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2164 lsaString, lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES)
2165 except NTSTATUSError as error:
2166 if self.check_runtime_error(error, ntstatus.NT_STATUS_INVALID_PARAMETER):
2168 if self.check_runtime_error(error, ntstatus.NT_STATUS_INVALID_INFO_CLASS):
2171 if error is not None:
2172 raise self.LocalRuntimeError(self, error,
2173 "QueryTrustedDomainInfoByName(SUPPORTED_ENCRYPTION_TYPES) failed")
2175 local_tdo_enctypes = lsa.TrustDomainInfoSupportedEncTypes()
2176 local_tdo_enctypes.enc_types = 0
2179 local_tdo_forest = None
2180 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2181 local_tdo_forest = local_lsa.lsaRQueryForestTrustInformation(local_policy,
2182 lsaString, lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
2183 except RuntimeError as error:
2184 if self.check_runtime_error(error, ntstatus.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
2186 if self.check_runtime_error(error, ntstatus.NT_STATUS_NOT_FOUND):
2188 if error is not None:
2189 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation failed")
2191 local_tdo_forest = lsa.ForestTrustInformation()
2192 local_tdo_forest.count = 0
2193 local_tdo_forest.entries = []
2195 self.outf.write("TrustedDomain:\n\n");
2196 self.outf.write("NetbiosName: %s\n" % local_tdo_info.netbios_name.string)
2197 if local_tdo_info.netbios_name.string != local_tdo_info.domain_name.string:
2198 self.outf.write("DnsName: %s\n" % local_tdo_info.domain_name.string)
2199 self.outf.write("SID: %s\n" % local_tdo_info.sid)
2200 self.outf.write("Type: %s\n" % self.trustType_string(local_tdo_info.trust_type))
2201 self.outf.write("Direction: %s\n" % self.trustDirection_string(local_tdo_info.trust_direction))
2202 self.outf.write("Attributes: %s\n" % self.trustAttributes_string(local_tdo_info.trust_attributes))
2203 posix_offset_u32 = ctypes.c_uint32(local_tdo_posix.posix_offset).value
2204 posix_offset_i32 = ctypes.c_int32(local_tdo_posix.posix_offset).value
2205 self.outf.write("PosixOffset: 0x%08X (%d)\n" % (posix_offset_u32, posix_offset_i32))
2206 self.outf.write("kerb_EncTypes: %s\n" % self.kerb_EncTypes_string(local_tdo_enctypes.enc_types))
2208 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2209 self.write_forest_trust_info(local_tdo_forest,
2210 tln=local_tdo_info.domain_name.string)
2214 class cmd_domain_trust_create(DomainTrustCommand):
2215 """Create a domain or forest trust."""
2217 synopsis = "%prog DOMAIN [options]"
2219 takes_optiongroups = {
2220 "sambaopts": options.SambaOptions,
2221 "versionopts": options.VersionOptions,
2222 "credopts": options.CredentialsOptions,
2223 "localdcopts": LocalDCCredentialsOptions,
2227 Option("--type", type="choice", metavar="TYPE",
2228 choices=["external", "forest"],
2229 help="The type of the trust: 'external' or 'forest'.",
2231 default="external"),
2232 Option("--direction", type="choice", metavar="DIRECTION",
2233 choices=["incoming", "outgoing", "both"],
2234 help="The trust direction: 'incoming', 'outgoing' or 'both'.",
2235 dest='trust_direction',
2237 Option("--create-location", type="choice", metavar="LOCATION",
2238 choices=["local", "both"],
2239 help="Where to create the trusted domain object: 'local' or 'both'.",
2240 dest='create_location',
2242 Option("--cross-organisation", action="store_true",
2243 help="The related domains does not belong to the same organisation.",
2244 dest='cross_organisation',
2246 Option("--quarantined", type="choice", metavar="yes|no",
2247 choices=["yes", "no", None],
2248 help="Special SID filtering rules are applied to the trust. "
2249 "With --type=external the default is yes. "
2250 "With --type=forest the default is no.",
2251 dest='quarantined_arg',
2253 Option("--not-transitive", action="store_true",
2254 help="The forest trust is not transitive.",
2255 dest='not_transitive',
2257 Option("--treat-as-external", action="store_true",
2258 help="The treat the forest trust as external.",
2259 dest='treat_as_external',
2261 Option("--no-aes-keys", action="store_false",
2262 help="The trust uses aes kerberos keys.",
2263 dest='use_aes_keys',
2265 Option("--skip-validation", action="store_false",
2266 help="Skip validation of the trust.",
2271 takes_args = ["domain"]
2273 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2274 trust_type=None, trust_direction=None, create_location=None,
2275 cross_organisation=False, quarantined_arg=None,
2276 not_transitive=False, treat_as_external=False,
2277 use_aes_keys=False, validate=True):
2279 lsaString = lsa.String()
2282 if quarantined_arg is None:
2283 if trust_type == 'external':
2285 elif quarantined_arg == 'yes':
2288 if trust_type != 'forest':
2290 raise CommandError("--not-transitive requires --type=forest")
2291 if treat_as_external:
2292 raise CommandError("--treat-as-external requires --type=forest")
2296 enc_types = lsa.TrustDomainInfoSupportedEncTypes()
2297 enc_types.enc_types = security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96
2298 enc_types.enc_types |= security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96
2300 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2301 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2302 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2304 local_trust_info = lsa.TrustDomainInfoInfoEx()
2305 local_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2306 local_trust_info.trust_direction = 0
2307 if trust_direction == "both":
2308 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2309 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2310 elif trust_direction == "incoming":
2311 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2312 elif trust_direction == "outgoing":
2313 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2314 local_trust_info.trust_attributes = 0
2315 if cross_organisation:
2316 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2318 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2319 if trust_type == "forest":
2320 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2322 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2323 if treat_as_external:
2324 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2326 def get_password(name):
2329 if password is not None and password is not '':
2331 password = getpass("New %s Password: " % name)
2332 passwordverify = getpass("Retype %s Password: " % name)
2333 if not password == passwordverify:
2335 self.outf.write("Sorry, passwords do not match.\n")
2337 incoming_secret = None
2338 outgoing_secret = None
2339 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2340 if create_location == "local":
2341 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2342 incoming_password = get_password("Incoming Trust")
2343 incoming_secret = string_to_byte_array(incoming_password.encode('utf-16-le'))
2344 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2345 outgoing_password = get_password("Outgoing Trust")
2346 outgoing_secret = string_to_byte_array(outgoing_password.encode('utf-16-le'))
2348 remote_trust_info = None
2350 # We use 240 random bytes.
2351 # Windows uses 28 or 240 random bytes. I guess it's
2352 # based on the trust type external vs. forest.
2354 # The initial trust password can be up to 512 bytes
2355 # while the versioned passwords used for periodic updates
2356 # can only be up to 498 bytes, as netr_ServerPasswordSet2()
2357 # needs to pass the NL_PASSWORD_VERSION structure within the
2358 # 512 bytes and a 2 bytes confounder is required.
2360 def random_trust_secret(length):
2361 pw = samba.generate_random_machine_password(length//2, length//2)
2362 return string_to_byte_array(pw.encode('utf-16-le'))
2364 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2365 incoming_secret = random_trust_secret(240)
2366 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2367 outgoing_secret = random_trust_secret(240)
2369 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2370 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2372 remote_trust_info = lsa.TrustDomainInfoInfoEx()
2373 remote_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2374 remote_trust_info.trust_direction = 0
2375 if trust_direction == "both":
2376 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2377 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2378 elif trust_direction == "incoming":
2379 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2380 elif trust_direction == "outgoing":
2381 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2382 remote_trust_info.trust_attributes = 0
2383 if cross_organisation:
2384 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2386 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2387 if trust_type == "forest":
2388 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2390 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2391 if treat_as_external:
2392 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2394 local_server = self.setup_local_server(sambaopts, localdcopts)
2396 local_lsa = self.new_local_lsa_connection()
2397 except RuntimeError as error:
2398 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2401 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2402 except RuntimeError as error:
2403 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2405 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2406 local_lsa_info.name.string,
2407 local_lsa_info.dns_domain.string,
2408 local_lsa_info.sid))
2411 remote_server = self.setup_remote_server(credopts, domain)
2412 except RuntimeError as error:
2413 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2416 remote_lsa = self.new_remote_lsa_connection()
2417 except RuntimeError as error:
2418 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2421 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2422 except RuntimeError as error:
2423 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2425 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2426 remote_lsa_info.name.string,
2427 remote_lsa_info.dns_domain.string,
2428 remote_lsa_info.sid))
2430 local_trust_info.domain_name.string = remote_lsa_info.dns_domain.string
2431 local_trust_info.netbios_name.string = remote_lsa_info.name.string
2432 local_trust_info.sid = remote_lsa_info.sid
2434 if remote_trust_info:
2435 remote_trust_info.domain_name.string = local_lsa_info.dns_domain.string
2436 remote_trust_info.netbios_name.string = local_lsa_info.name.string
2437 remote_trust_info.sid = local_lsa_info.sid
2440 lsaString.string = local_trust_info.domain_name.string
2441 local_old_netbios = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2442 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2443 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2444 except NTSTATUSError as error:
2445 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2446 raise self.LocalRuntimeError(self, error,
2447 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2451 lsaString.string = local_trust_info.netbios_name.string
2452 local_old_dns = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2453 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2454 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2455 except NTSTATUSError as error:
2456 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2457 raise self.LocalRuntimeError(self, error,
2458 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2461 if remote_trust_info:
2463 lsaString.string = remote_trust_info.domain_name.string
2464 remote_old_netbios = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2465 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2466 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2467 except NTSTATUSError as error:
2468 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2469 raise self.RemoteRuntimeError(self, error,
2470 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2474 lsaString.string = remote_trust_info.netbios_name.string
2475 remote_old_dns = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2476 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2477 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2478 except NTSTATUSError as error:
2479 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2480 raise self.RemoteRuntimeError(self, error,
2481 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2485 local_netlogon = self.new_local_netlogon_connection()
2486 except RuntimeError as error:
2487 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2490 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
2491 except RuntimeError as error:
2492 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
2494 if remote_trust_info:
2496 remote_netlogon = self.new_remote_netlogon_connection()
2497 except RuntimeError as error:
2498 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2501 remote_netlogon_info = self.get_netlogon_dc_info(remote_netlogon, remote_server)
2502 except RuntimeError as error:
2503 raise self.RemoteRuntimeError(self, error, "failed to get netlogon dc info")
2505 def generate_AuthInOutBlob(secret, update_time):
2507 blob = drsblobs.trustAuthInOutBlob()
2512 clear = drsblobs.AuthInfoClear()
2513 clear.size = len(secret)
2514 clear.password = secret
2516 info = drsblobs.AuthenticationInformation()
2517 info.LastUpdateTime = samba.unix2nttime(update_time)
2518 info.AuthType = lsa.TRUST_AUTH_TYPE_CLEAR
2519 info.AuthInfo = clear
2521 array = drsblobs.AuthenticationInformationArray()
2523 array.array = [info]
2525 blob = drsblobs.trustAuthInOutBlob()
2527 blob.current = array
2531 def generate_AuthInfoInternal(session_key, incoming=None, outgoing=None):
2532 confounder = [0] * 512
2533 for i in range(len(confounder)):
2534 confounder[i] = random.randint(0, 255)
2536 trustpass = drsblobs.trustDomainPasswords()
2538 trustpass.confounder = confounder
2539 trustpass.outgoing = outgoing
2540 trustpass.incoming = incoming
2542 trustpass_blob = ndr_pack(trustpass)
2544 encrypted_trustpass = arcfour_encrypt(session_key, trustpass_blob)
2546 auth_blob = lsa.DATA_BUF2()
2547 auth_blob.size = len(encrypted_trustpass)
2548 auth_blob.data = string_to_byte_array(encrypted_trustpass)
2550 auth_info = lsa.TrustDomainInfoAuthInfoInternal()
2551 auth_info.auth_blob = auth_blob
2555 update_time = samba.current_unix_time()
2556 incoming_blob = generate_AuthInOutBlob(incoming_secret, update_time)
2557 outgoing_blob = generate_AuthInOutBlob(outgoing_secret, update_time)
2559 local_tdo_handle = None
2560 remote_tdo_handle = None
2562 local_auth_info = generate_AuthInfoInternal(local_lsa.session_key,
2563 incoming=incoming_blob,
2564 outgoing=outgoing_blob)
2565 if remote_trust_info:
2566 remote_auth_info = generate_AuthInfoInternal(remote_lsa.session_key,
2567 incoming=outgoing_blob,
2568 outgoing=incoming_blob)
2571 if remote_trust_info:
2572 self.outf.write("Creating remote TDO.\n")
2573 current_request = { "location": "remote", "name": "CreateTrustedDomainEx2"}
2574 remote_tdo_handle = remote_lsa.CreateTrustedDomainEx2(remote_policy,
2577 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2578 self.outf.write("Remote TDO created.\n")
2580 self.outf.write("Setting supported encryption types on remote TDO.\n")
2581 current_request = { "location": "remote", "name": "SetInformationTrustedDomain"}
2582 remote_lsa.SetInformationTrustedDomain(remote_tdo_handle,
2583 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2586 self.outf.write("Creating local TDO.\n")
2587 current_request = { "location": "local", "name": "CreateTrustedDomainEx2"}
2588 local_tdo_handle = local_lsa.CreateTrustedDomainEx2(local_policy,
2591 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2592 self.outf.write("Local TDO created\n")
2594 self.outf.write("Setting supported encryption types on local TDO.\n")
2595 current_request = { "location": "local", "name": "SetInformationTrustedDomain"}
2596 local_lsa.SetInformationTrustedDomain(local_tdo_handle,
2597 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2599 except RuntimeError as error:
2600 self.outf.write("Error: %s failed %sly - cleaning up\n" % (
2601 current_request['name'], current_request['location']))
2602 if remote_tdo_handle:
2603 self.outf.write("Deleting remote TDO.\n")
2604 remote_lsa.DeleteObject(remote_tdo_handle)
2605 remote_tdo_handle = None
2606 if local_tdo_handle:
2607 self.outf.write("Deleting local TDO.\n")
2608 local_lsa.DeleteObject(local_tdo_handle)
2609 local_tdo_handle = None
2610 if current_request['location'] is "remote":
2611 raise self.RemoteRuntimeError(self, error, "%s" % (
2612 current_request['name']))
2613 raise self.LocalRuntimeError(self, error, "%s" % (
2614 current_request['name']))
2617 if local_trust_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2618 self.outf.write("Setup local forest trust information...\n")
2620 # get all information about the remote trust
2621 # this triggers netr_GetForestTrustInformation to the remote domain
2622 # and lsaRSetForestTrustInformation() locally, but new top level
2623 # names are disabled by default.
2624 local_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
2625 remote_lsa_info.dns_domain.string,
2626 netlogon.DS_GFTI_UPDATE_TDO)
2627 except RuntimeError as error:
2628 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2631 # here we try to enable all top level names
2632 local_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
2633 remote_lsa_info.dns_domain,
2634 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2637 except RuntimeError as error:
2638 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2640 self.write_forest_trust_info(local_forest_info,
2641 tln=remote_lsa_info.dns_domain.string,
2642 collisions=local_forest_collision)
2644 if remote_trust_info:
2645 self.outf.write("Setup remote forest trust information...\n")
2647 # get all information about the local trust (from the perspective of the remote domain)
2648 # this triggers netr_GetForestTrustInformation to our domain.
2649 # and lsaRSetForestTrustInformation() remotely, but new top level
2650 # names are disabled by default.
2651 remote_forest_info = remote_netlogon.netr_DsRGetForestTrustInformation(remote_netlogon_info.dc_unc,
2652 local_lsa_info.dns_domain.string,
2653 netlogon.DS_GFTI_UPDATE_TDO)
2654 except RuntimeError as error:
2655 raise self.RemoteRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2658 # here we try to enable all top level names
2659 remote_forest_collision = remote_lsa.lsaRSetForestTrustInformation(remote_policy,
2660 local_lsa_info.dns_domain,
2661 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2664 except RuntimeError as error:
2665 raise self.RemoteRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2667 self.write_forest_trust_info(remote_forest_info,
2668 tln=local_lsa_info.dns_domain.string,
2669 collisions=remote_forest_collision)
2671 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2672 self.outf.write("Validating outgoing trust...\n")
2674 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_netlogon_info.dc_unc,
2675 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2677 remote_lsa_info.dns_domain.string)
2678 except RuntimeError as error:
2679 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2681 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2682 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2684 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2685 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2686 local_trust_verify.trusted_dc_name,
2687 local_trust_verify.tc_connection_status[1],
2688 local_trust_verify.pdc_connection_status[1])
2690 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2691 local_trust_verify.trusted_dc_name,
2692 local_trust_verify.tc_connection_status[1],
2693 local_trust_verify.pdc_connection_status[1])
2695 if local_trust_status != werror.WERR_SUCCESS or local_conn_status != werror.WERR_SUCCESS:
2696 raise CommandError(local_validation)
2698 self.outf.write("OK: %s\n" % local_validation)
2700 if remote_trust_info:
2701 if remote_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2702 self.outf.write("Validating incoming trust...\n")
2704 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_netlogon_info.dc_unc,
2705 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2707 local_lsa_info.dns_domain.string)
2708 except RuntimeError as error:
2709 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2711 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2712 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2714 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2715 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2716 remote_trust_verify.trusted_dc_name,
2717 remote_trust_verify.tc_connection_status[1],
2718 remote_trust_verify.pdc_connection_status[1])
2720 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2721 remote_trust_verify.trusted_dc_name,
2722 remote_trust_verify.tc_connection_status[1],
2723 remote_trust_verify.pdc_connection_status[1])
2725 if remote_trust_status != werror.WERR_SUCCESS or remote_conn_status != werror.WERR_SUCCESS:
2726 raise CommandError(remote_validation)
2728 self.outf.write("OK: %s\n" % remote_validation)
2730 if remote_tdo_handle is not None:
2732 remote_lsa.Close(remote_tdo_handle)
2733 except RuntimeError as error:
2735 remote_tdo_handle = None
2736 if local_tdo_handle is not None:
2738 local_lsa.Close(local_tdo_handle)
2739 except RuntimeError as error:
2741 local_tdo_handle = None
2743 self.outf.write("Success.\n")
2746 class cmd_domain_trust_delete(DomainTrustCommand):
2747 """Delete a domain trust."""
2749 synopsis = "%prog DOMAIN [options]"
2751 takes_optiongroups = {
2752 "sambaopts": options.SambaOptions,
2753 "versionopts": options.VersionOptions,
2754 "credopts": options.CredentialsOptions,
2755 "localdcopts": LocalDCCredentialsOptions,
2759 Option("--delete-location", type="choice", metavar="LOCATION",
2760 choices=["local", "both"],
2761 help="Where to delete the trusted domain object: 'local' or 'both'.",
2762 dest='delete_location',
2766 takes_args = ["domain"]
2768 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2769 delete_location=None):
2771 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2772 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2773 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2775 if delete_location == "local":
2776 remote_policy_access = None
2778 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2779 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2780 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2782 local_server = self.setup_local_server(sambaopts, localdcopts)
2784 local_lsa = self.new_local_lsa_connection()
2785 except RuntimeError as error:
2786 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2789 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2790 except RuntimeError as error:
2791 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2793 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2794 local_lsa_info.name.string,
2795 local_lsa_info.dns_domain.string,
2796 local_lsa_info.sid))
2798 local_tdo_info = None
2799 local_tdo_handle = None
2800 remote_tdo_info = None
2801 remote_tdo_handle = None
2803 lsaString = lsa.String()
2805 lsaString.string = domain
2806 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2807 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2808 except NTSTATUSError as error:
2809 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2810 raise CommandError("Failed to find trust for domain '%s'" % domain)
2811 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2814 if remote_policy_access is not None:
2816 remote_server = self.setup_remote_server(credopts, domain)
2817 except RuntimeError as error:
2818 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2821 remote_lsa = self.new_remote_lsa_connection()
2822 except RuntimeError as error:
2823 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2826 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2827 except RuntimeError as error:
2828 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2830 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2831 remote_lsa_info.name.string,
2832 remote_lsa_info.dns_domain.string,
2833 remote_lsa_info.sid))
2835 if remote_lsa_info.sid != local_tdo_info.sid or \
2836 remote_lsa_info.name.string != local_tdo_info.netbios_name.string or \
2837 remote_lsa_info.dns_domain.string != local_tdo_info.domain_name.string:
2838 raise CommandError("LocalTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2839 local_tdo_info.netbios_name.string,
2840 local_tdo_info.domain_name.string,
2841 local_tdo_info.sid))
2844 lsaString.string = local_lsa_info.dns_domain.string
2845 remote_tdo_info = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2846 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2847 except NTSTATUSError as error:
2848 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2849 raise self.RemoteRuntimeError(self, error, "QueryTrustedDomainInfoByName(%s)" % (
2853 if remote_tdo_info is not None:
2854 if local_lsa_info.sid != remote_tdo_info.sid or \
2855 local_lsa_info.name.string != remote_tdo_info.netbios_name.string or \
2856 local_lsa_info.dns_domain.string != remote_tdo_info.domain_name.string:
2857 raise CommandError("RemoteTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2858 remote_tdo_info.netbios_name.string,
2859 remote_tdo_info.domain_name.string,
2860 remote_tdo_info.sid))
2862 if local_tdo_info is not None:
2864 lsaString.string = local_tdo_info.domain_name.string
2865 local_tdo_handle = local_lsa.OpenTrustedDomainByName(local_policy,
2867 security.SEC_STD_DELETE)
2868 except RuntimeError as error:
2869 raise self.LocalRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2872 local_lsa.DeleteObject(local_tdo_handle)
2873 local_tdo_handle = None
2875 if remote_tdo_info is not None:
2877 lsaString.string = remote_tdo_info.domain_name.string
2878 remote_tdo_handle = remote_lsa.OpenTrustedDomainByName(remote_policy,
2880 security.SEC_STD_DELETE)
2881 except RuntimeError as error:
2882 raise self.RemoteRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2885 if remote_tdo_handle is not None:
2887 remote_lsa.DeleteObject(remote_tdo_handle)
2888 remote_tdo_handle = None
2889 self.outf.write("RemoteTDO deleted.\n")
2890 except RuntimeError as error:
2891 self.outf.write("%s\n" % self.RemoteRuntimeError(self, error, "DeleteObject() failed"))
2893 if local_tdo_handle is not None:
2895 local_lsa.DeleteObject(local_tdo_handle)
2896 local_tdo_handle = None
2897 self.outf.write("LocalTDO deleted.\n")
2898 except RuntimeError as error:
2899 self.outf.write("%s\n" % self.LocalRuntimeError(self, error, "DeleteObject() failed"))
2903 class cmd_domain_trust_validate(DomainTrustCommand):
2904 """Validate a domain trust."""
2906 synopsis = "%prog DOMAIN [options]"
2908 takes_optiongroups = {
2909 "sambaopts": options.SambaOptions,
2910 "versionopts": options.VersionOptions,
2911 "credopts": options.CredentialsOptions,
2912 "localdcopts": LocalDCCredentialsOptions,
2916 Option("--validate-location", type="choice", metavar="LOCATION",
2917 choices=["local", "both"],
2918 help="Where to validate the trusted domain object: 'local' or 'both'.",
2919 dest='validate_location',
2923 takes_args = ["domain"]
2925 def run(self, domain, sambaopts=None, versionopts=None, credopts=None, localdcopts=None,
2926 validate_location=None):
2928 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2930 local_server = self.setup_local_server(sambaopts, localdcopts)
2932 local_lsa = self.new_local_lsa_connection()
2933 except RuntimeError as error:
2934 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2937 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2938 except RuntimeError as error:
2939 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2941 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2942 local_lsa_info.name.string,
2943 local_lsa_info.dns_domain.string,
2944 local_lsa_info.sid))
2947 lsaString = lsa.String()
2948 lsaString.string = domain
2949 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2950 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2951 except NTSTATUSError as error:
2952 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2953 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2955 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
2957 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
2958 local_tdo_info.netbios_name.string,
2959 local_tdo_info.domain_name.string,
2960 local_tdo_info.sid))
2963 local_netlogon = self.new_local_netlogon_connection()
2964 except RuntimeError as error:
2965 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2968 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_server,
2969 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2971 local_tdo_info.domain_name.string)
2972 except RuntimeError as error:
2973 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2975 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2976 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2978 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2979 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2980 local_trust_verify.trusted_dc_name,
2981 local_trust_verify.tc_connection_status[1],
2982 local_trust_verify.pdc_connection_status[1])
2984 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2985 local_trust_verify.trusted_dc_name,
2986 local_trust_verify.tc_connection_status[1],
2987 local_trust_verify.pdc_connection_status[1])
2989 if local_trust_status != werror.WERR_SUCCESS or local_conn_status != werror.WERR_SUCCESS:
2990 raise CommandError(local_validation)
2992 self.outf.write("OK: %s\n" % local_validation)
2995 server = local_trust_verify.trusted_dc_name.replace('\\', '')
2996 domain_and_server = "%s\\%s" % (local_tdo_info.domain_name.string, server)
2997 local_trust_rediscover = local_netlogon.netr_LogonControl2Ex(local_server,
2998 netlogon.NETLOGON_CONTROL_REDISCOVER,
3001 except RuntimeError as error:
3002 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
3004 local_conn_status = self._uint32(local_trust_rediscover.tc_connection_status[0])
3005 local_rediscover = "LocalRediscover: DC[%s] CONNECTION[%s]" % (
3006 local_trust_rediscover.trusted_dc_name,
3007 local_trust_rediscover.tc_connection_status[1])
3009 if local_conn_status != werror.WERR_SUCCESS:
3010 raise CommandError(local_rediscover)
3012 self.outf.write("OK: %s\n" % local_rediscover)
3014 if validate_location != "local":
3016 remote_server = self.setup_remote_server(credopts, domain, require_pdc=False)
3017 except RuntimeError as error:
3018 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
3021 remote_netlogon = self.new_remote_netlogon_connection()
3022 except RuntimeError as error:
3023 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
3026 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_server,
3027 netlogon.NETLOGON_CONTROL_TC_VERIFY,
3029 local_lsa_info.dns_domain.string)
3030 except RuntimeError as error:
3031 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
3033 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
3034 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
3036 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
3037 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
3038 remote_trust_verify.trusted_dc_name,
3039 remote_trust_verify.tc_connection_status[1],
3040 remote_trust_verify.pdc_connection_status[1])
3042 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
3043 remote_trust_verify.trusted_dc_name,
3044 remote_trust_verify.tc_connection_status[1],
3045 remote_trust_verify.pdc_connection_status[1])
3047 if remote_trust_status != werror.WERR_SUCCESS or remote_conn_status != werror.WERR_SUCCESS:
3048 raise CommandError(remote_validation)
3050 self.outf.write("OK: %s\n" % remote_validation)
3053 server = remote_trust_verify.trusted_dc_name.replace('\\', '')
3054 domain_and_server = "%s\\%s" % (local_lsa_info.dns_domain.string, server)
3055 remote_trust_rediscover = remote_netlogon.netr_LogonControl2Ex(remote_server,
3056 netlogon.NETLOGON_CONTROL_REDISCOVER,
3059 except RuntimeError as error:
3060 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
3062 remote_conn_status = self._uint32(remote_trust_rediscover.tc_connection_status[0])
3064 remote_rediscover = "RemoteRediscover: DC[%s] CONNECTION[%s]" % (
3065 remote_trust_rediscover.trusted_dc_name,
3066 remote_trust_rediscover.tc_connection_status[1])
3068 if remote_conn_status != werror.WERR_SUCCESS:
3069 raise CommandError(remote_rediscover)
3071 self.outf.write("OK: %s\n" % remote_rediscover)
3075 class cmd_domain_trust_namespaces(DomainTrustCommand):
3076 """Manage forest trust namespaces."""
3078 synopsis = "%prog [DOMAIN] [options]"
3080 takes_optiongroups = {
3081 "sambaopts": options.SambaOptions,
3082 "versionopts": options.VersionOptions,
3083 "localdcopts": LocalDCCredentialsOptions,
3087 Option("--refresh", type="choice", metavar="check|store",
3088 choices=["check", "store", None],
3089 help="List and maybe store refreshed forest trust information: 'check' or 'store'.",
3092 Option("--enable-all", action="store_true",
3093 help="Try to update disabled entries, not allowed with --refresh=check.",
3096 Option("--enable-tln", action="append", metavar='DNSDOMAIN',
3097 help="Enable a top level name entry. Can be specified multiple times.",
3100 Option("--disable-tln", action="append", metavar='DNSDOMAIN',
3101 help="Disable a top level name entry. Can be specified multiple times.",
3104 Option("--add-tln-ex", action="append", metavar='DNSDOMAIN',
3105 help="Add a top level exclusion entry. Can be specified multiple times.",
3108 Option("--delete-tln-ex", action="append", metavar='DNSDOMAIN',
3109 help="Delete a top level exclusion entry. Can be specified multiple times.",
3110 dest='delete_tln_ex',
3112 Option("--enable-nb", action="append", metavar='NETBIOSDOMAIN',
3113 help="Enable a netbios name in a domain entry. Can be specified multiple times.",
3116 Option("--disable-nb", action="append", metavar='NETBIOSDOMAIN',
3117 help="Disable a netbios name in a domain entry. Can be specified multiple times.",
3120 Option("--enable-sid", action="append", metavar='DOMAINSID',
3121 help="Enable a SID in a domain entry. Can be specified multiple times.",
3122 dest='enable_sid_str',
3124 Option("--disable-sid", action="append", metavar='DOMAINSID',
3125 help="Disable a SID in a domain entry. Can be specified multiple times.",
3126 dest='disable_sid_str',
3128 Option("--add-upn-suffix", action="append", metavar='DNSDOMAIN',
3129 help="Add a new uPNSuffixes attribute for the local forest. Can be specified multiple times.",
3132 Option("--delete-upn-suffix", action="append", metavar='DNSDOMAIN',
3133 help="Delete an existing uPNSuffixes attribute of the local forest. Can be specified multiple times.",
3136 Option("--add-spn-suffix", action="append", metavar='DNSDOMAIN',
3137 help="Add a new msDS-SPNSuffixes attribute for the local forest. Can be specified multiple times.",
3140 Option("--delete-spn-suffix", action="append", metavar='DNSDOMAIN',
3141 help="Delete an existing msDS-SPNSuffixes attribute of the local forest. Can be specified multiple times.",
3146 takes_args = ["domain?"]
3148 def run(self, domain=None, sambaopts=None, localdcopts=None, versionopts=None,
3149 refresh=None, enable_all=False,
3150 enable_tln=[], disable_tln=[], add_tln_ex=[], delete_tln_ex=[],
3151 enable_sid_str=[], disable_sid_str=[], enable_nb=[], disable_nb=[],
3152 add_upn=[], delete_upn=[], add_spn=[], delete_spn=[]):
3154 require_update = False
3157 if refresh == "store":
3158 raise CommandError("--refresh=%s not allowed without DOMAIN" % refresh)
3161 raise CommandError("--enable-all not allowed without DOMAIN")
3163 if len(enable_tln) > 0:
3164 raise CommandError("--enable-tln not allowed without DOMAIN")
3165 if len(disable_tln) > 0:
3166 raise CommandError("--disable-tln not allowed without DOMAIN")
3168 if len(add_tln_ex) > 0:
3169 raise CommandError("--add-tln-ex not allowed without DOMAIN")
3170 if len(delete_tln_ex) > 0:
3171 raise CommandError("--delete-tln-ex not allowed without DOMAIN")
3173 if len(enable_nb) > 0:
3174 raise CommandError("--enable-nb not allowed without DOMAIN")
3175 if len(disable_nb) > 0:
3176 raise CommandError("--disable-nb not allowed without DOMAIN")
3178 if len(enable_sid_str) > 0:
3179 raise CommandError("--enable-sid not allowed without DOMAIN")
3180 if len(disable_sid_str) > 0:
3181 raise CommandError("--disable-sid not allowed without DOMAIN")
3183 if len(add_upn) > 0:
3185 if not n.startswith("*."):
3187 raise CommandError("value[%s] specified for --add-upn-suffix should not include with '*.'" % n)
3188 require_update = True
3189 if len(delete_upn) > 0:
3190 for n in delete_upn:
3191 if not n.startswith("*."):
3193 raise CommandError("value[%s] specified for --delete-upn-suffix should not include with '*.'" % n)
3194 require_update = True
3196 for d in delete_upn:
3197 if a.lower() != d.lower():
3199 raise CommandError("value[%s] specified for --add-upn-suffix and --delete-upn-suffix" % a)
3201 if len(add_spn) > 0:
3203 if not n.startswith("*."):
3205 raise CommandError("value[%s] specified for --add-spn-suffix should not include with '*.'" % n)
3206 require_update = True
3207 if len(delete_spn) > 0:
3208 for n in delete_spn:
3209 if not n.startswith("*."):
3211 raise CommandError("value[%s] specified for --delete-spn-suffix should not include with '*.'" % n)
3212 require_update = True
3214 for d in delete_spn:
3215 if a.lower() != d.lower():
3217 raise CommandError("value[%s] specified for --add-spn-suffix and --delete-spn-suffix" % a)
3219 if len(add_upn) > 0:
3220 raise CommandError("--add-upn-suffix not allowed together with DOMAIN")
3221 if len(delete_upn) > 0:
3222 raise CommandError("--delete-upn-suffix not allowed together with DOMAIN")
3223 if len(add_spn) > 0:
3224 raise CommandError("--add-spn-suffix not allowed together with DOMAIN")
3225 if len(delete_spn) > 0:
3226 raise CommandError("--delete-spn-suffix not allowed together with DOMAIN")
3228 if refresh is not None:
3229 if refresh == "store":
3230 require_update = True
3232 if enable_all and refresh != "store":
3233 raise CommandError("--enable-all not allowed together with --refresh=%s" % refresh)
3235 if len(enable_tln) > 0:
3236 raise CommandError("--enable-tln not allowed together with --refresh")
3237 if len(disable_tln) > 0:
3238 raise CommandError("--disable-tln not allowed together with --refresh")
3240 if len(add_tln_ex) > 0:
3241 raise CommandError("--add-tln-ex not allowed together with --refresh")
3242 if len(delete_tln_ex) > 0:
3243 raise CommandError("--delete-tln-ex not allowed together with --refresh")
3245 if len(enable_nb) > 0:
3246 raise CommandError("--enable-nb not allowed together with --refresh")
3247 if len(disable_nb) > 0:
3248 raise CommandError("--disable-nb not allowed together with --refresh")
3250 if len(enable_sid_str) > 0:
3251 raise CommandError("--enable-sid not allowed together with --refresh")
3252 if len(disable_sid_str) > 0:
3253 raise CommandError("--disable-sid not allowed together with --refresh")
3256 require_update = True
3258 if len(enable_tln) > 0:
3259 raise CommandError("--enable-tln not allowed together with --enable-all")
3261 if len(enable_nb) > 0:
3262 raise CommandError("--enable-nb not allowed together with --enable-all")
3264 if len(enable_sid_str) > 0:
3265 raise CommandError("--enable-sid not allowed together with --enable-all")
3267 if len(enable_tln) > 0:
3268 require_update = True
3269 if len(disable_tln) > 0:
3270 require_update = True
3271 for e in enable_tln:
3272 for d in disable_tln:
3273 if e.lower() != d.lower():
3275 raise CommandError("value[%s] specified for --enable-tln and --disable-tln" % e)
3277 if len(add_tln_ex) > 0:
3278 for n in add_tln_ex:
3279 if not n.startswith("*."):
3281 raise CommandError("value[%s] specified for --add-tln-ex should not include with '*.'" % n)
3282 require_update = True
3283 if len(delete_tln_ex) > 0:
3284 for n in delete_tln_ex:
3285 if not n.startswith("*."):
3287 raise CommandError("value[%s] specified for --delete-tln-ex should not include with '*.'" % n)
3288 require_update = True
3289 for a in add_tln_ex:
3290 for d in delete_tln_ex:
3291 if a.lower() != d.lower():
3293 raise CommandError("value[%s] specified for --add-tln-ex and --delete-tln-ex" % a)
3295 if len(enable_nb) > 0:
3296 require_update = True
3297 if len(disable_nb) > 0:
3298 require_update = True
3300 for d in disable_nb:
3301 if e.upper() != d.upper():
3303 raise CommandError("value[%s] specified for --enable-nb and --disable-nb" % e)
3306 for s in enable_sid_str:
3308 sid = security.dom_sid(s)
3309 except TypeError as error:
3310 raise CommandError("value[%s] specified for --enable-sid is not a valid SID" % s)
3311 enable_sid.append(sid)
3313 for s in disable_sid_str:
3315 sid = security.dom_sid(s)
3316 except TypeError as error:
3317 raise CommandError("value[%s] specified for --disable-sid is not a valid SID" % s)
3318 disable_sid.append(sid)
3319 if len(enable_sid) > 0:
3320 require_update = True
3321 if len(disable_sid) > 0:
3322 require_update = True
3323 for e in enable_sid:
3324 for d in disable_sid:
3327 raise CommandError("value[%s] specified for --enable-sid and --disable-sid" % e)
3329 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
3331 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
3333 local_server = self.setup_local_server(sambaopts, localdcopts)
3335 local_lsa = self.new_local_lsa_connection()
3336 except RuntimeError as error:
3337 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
3340 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
3341 except RuntimeError as error:
3342 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
3344 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
3345 local_lsa_info.name.string,
3346 local_lsa_info.dns_domain.string,
3347 local_lsa_info.sid))
3351 local_netlogon = self.new_local_netlogon_connection()
3352 except RuntimeError as error:
3353 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3356 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3357 except RuntimeError as error:
3358 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3360 if local_netlogon_info.domain_name != local_netlogon_info.forest_name:
3361 raise CommandError("The local domain [%s] is not the forest root [%s]" % (
3362 local_netlogon_info.domain_name,
3363 local_netlogon_info.forest_name))
3366 # get all information about our own forest
3367 own_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3369 except RuntimeError as error:
3370 if self.check_runtime_error(error, werror.WERR_RPC_S_PROCNUM_OUT_OF_RANGE):
3371 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3374 if self.check_runtime_error(error, werror.WERR_INVALID_FUNCTION):
3375 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3378 if self.check_runtime_error(error, werror.WERR_NERR_ACFNOTLOADED):
3379 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3382 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3384 self.outf.write("Own forest trust information...\n")
3385 self.write_forest_trust_info(own_forest_info,
3386 tln=local_lsa_info.dns_domain.string)
3389 local_samdb = self.new_local_ldap_connection()
3390 except RuntimeError as error:
3391 raise self.LocalRuntimeError(self, error, "failed to connect to SamDB")
3393 local_partitions_dn = "CN=Partitions,%s" % str(local_samdb.get_config_basedn())
3394 attrs = ['uPNSuffixes', 'msDS-SPNSuffixes']
3396 msgs = local_samdb.search(base=local_partitions_dn,
3397 scope=ldb.SCOPE_BASE,
3398 expression="(objectClass=crossRefContainer)",
3400 stored_msg = msgs[0]
3401 except ldb.LdbError as error:
3402 raise self.LocalLdbError(self, error, "failed to search partition dn")
3404 stored_upn_vals = []
3405 if 'uPNSuffixes' in stored_msg:
3406 stored_upn_vals.extend(stored_msg['uPNSuffixes'])
3408 stored_spn_vals = []
3409 if 'msDS-SPNSuffixes' in stored_msg:
3410 stored_spn_vals.extend(stored_msg['msDS-SPNSuffixes'])
3412 self.outf.write("Stored uPNSuffixes attributes[%d]:\n" % len(stored_upn_vals))
3413 for v in stored_upn_vals:
3414 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3415 self.outf.write("Stored msDS-SPNSuffixes attributes[%d]:\n" % len(stored_spn_vals))
3416 for v in stored_spn_vals:
3417 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3419 if not require_update:
3423 update_upn_vals = []
3424 update_upn_vals.extend(stored_upn_vals)
3427 update_spn_vals = []
3428 update_spn_vals.extend(stored_spn_vals)
3432 for i in xrange(0, len(update_upn_vals)):
3433 v = update_upn_vals[i]
3434 if v.lower() != upn.lower():
3439 raise CommandError("Entry already present for value[%s] specified for --add-upn-suffix" % upn)
3440 update_upn_vals.append(upn)
3443 for upn in delete_upn:
3445 for i in xrange(0, len(update_upn_vals)):
3446 v = update_upn_vals[i]
3447 if v.lower() != upn.lower():
3452 raise CommandError("Entry not found for value[%s] specified for --delete-upn-suffix" % upn)
3454 update_upn_vals.pop(idx)
3459 for i in xrange(0, len(update_spn_vals)):
3460 v = update_spn_vals[i]
3461 if v.lower() != spn.lower():
3466 raise CommandError("Entry already present for value[%s] specified for --add-spn-suffix" % spn)
3467 update_spn_vals.append(spn)
3470 for spn in delete_spn:
3472 for i in xrange(0, len(update_spn_vals)):
3473 v = update_spn_vals[i]
3474 if v.lower() != spn.lower():
3479 raise CommandError("Entry not found for value[%s] specified for --delete-spn-suffix" % spn)
3481 update_spn_vals.pop(idx)
3484 self.outf.write("Update uPNSuffixes attributes[%d]:\n" % len(update_upn_vals))
3485 for v in update_upn_vals:
3486 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3487 self.outf.write("Update msDS-SPNSuffixes attributes[%d]:\n" % len(update_spn_vals))
3488 for v in update_spn_vals:
3489 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3491 update_msg = ldb.Message()
3492 update_msg.dn = stored_msg.dn
3495 update_msg['uPNSuffixes'] = ldb.MessageElement(update_upn_vals,
3496 ldb.FLAG_MOD_REPLACE,
3499 update_msg['msDS-SPNSuffixes'] = ldb.MessageElement(update_spn_vals,
3500 ldb.FLAG_MOD_REPLACE,
3503 local_samdb.modify(update_msg)
3504 except ldb.LdbError as error:
3505 raise self.LocalLdbError(self, error, "failed to update partition dn")
3508 stored_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3510 except RuntimeError as error:
3511 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3513 self.outf.write("Stored forest trust information...\n")
3514 self.write_forest_trust_info(stored_forest_info,
3515 tln=local_lsa_info.dns_domain.string)
3519 lsaString = lsa.String()
3520 lsaString.string = domain
3521 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
3522 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
3523 except NTSTATUSError as error:
3524 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
3525 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
3527 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
3529 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
3530 local_tdo_info.netbios_name.string,
3531 local_tdo_info.domain_name.string,
3532 local_tdo_info.sid))
3534 if not local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
3535 raise CommandError("trusted domain object for domain [%s] is not marked as FOREST_TRANSITIVE." % domain)
3537 if refresh is not None:
3539 local_netlogon = self.new_local_netlogon_connection()
3540 except RuntimeError as error:
3541 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3544 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3545 except RuntimeError as error:
3546 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3548 lsa_update_check = 1
3549 if refresh == "store":
3550 netlogon_update_tdo = netlogon.DS_GFTI_UPDATE_TDO
3552 lsa_update_check = 0
3554 netlogon_update_tdo = 0
3557 # get all information about the remote trust
3558 # this triggers netr_GetForestTrustInformation to the remote domain
3559 # and lsaRSetForestTrustInformation() locally, but new top level
3560 # names are disabled by default.
3561 fresh_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3562 local_tdo_info.domain_name.string,
3563 netlogon_update_tdo)
3564 except RuntimeError as error:
3565 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3568 fresh_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3569 local_tdo_info.domain_name,
3570 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3573 except RuntimeError as error:
3574 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3576 self.outf.write("Fresh forest trust information...\n")
3577 self.write_forest_trust_info(fresh_forest_info,
3578 tln=local_tdo_info.domain_name.string,
3579 collisions=fresh_forest_collision)
3581 if refresh == "store":
3583 lsaString = lsa.String()
3584 lsaString.string = local_tdo_info.domain_name.string
3585 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3587 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3588 except RuntimeError as error:
3589 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3591 self.outf.write("Stored forest trust information...\n")
3592 self.write_forest_trust_info(stored_forest_info,
3593 tln=local_tdo_info.domain_name.string)
3598 # The none --refresh path
3602 lsaString = lsa.String()
3603 lsaString.string = local_tdo_info.domain_name.string
3604 local_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3606 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3607 except RuntimeError as error:
3608 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3610 self.outf.write("Local forest trust information...\n")
3611 self.write_forest_trust_info(local_forest_info,
3612 tln=local_tdo_info.domain_name.string)
3614 if not require_update:
3618 entries.extend(local_forest_info.entries)
3619 update_forest_info = lsa.ForestTrustInformation()
3620 update_forest_info.count = len(entries)
3621 update_forest_info.entries = entries
3624 for i in xrange(0, len(update_forest_info.entries)):
3625 r = update_forest_info.entries[i]
3626 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3628 if update_forest_info.entries[i].flags == 0:
3630 update_forest_info.entries[i].time = 0
3631 update_forest_info.entries[i].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3632 for i in xrange(0, len(update_forest_info.entries)):
3633 r = update_forest_info.entries[i]
3634 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3636 if update_forest_info.entries[i].flags == 0:
3638 update_forest_info.entries[i].time = 0
3639 update_forest_info.entries[i].flags &= ~lsa.LSA_NB_DISABLED_MASK
3640 update_forest_info.entries[i].flags &= ~lsa.LSA_SID_DISABLED_MASK
3642 for tln in enable_tln:
3644 for i in xrange(0, len(update_forest_info.entries)):
3645 r = update_forest_info.entries[i]
3646 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3648 if r.forest_trust_data.string.lower() != tln.lower():
3653 raise CommandError("Entry not found for value[%s] specified for --enable-tln" % tln)
3654 if not update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_MASK:
3655 raise CommandError("Entry found for value[%s] specified for --enable-tln is already enabled" % tln)
3656 update_forest_info.entries[idx].time = 0
3657 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3659 for tln in disable_tln:
3661 for i in xrange(0, len(update_forest_info.entries)):
3662 r = update_forest_info.entries[i]
3663 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3665 if r.forest_trust_data.string.lower() != tln.lower():
3670 raise CommandError("Entry not found for value[%s] specified for --disable-tln" % tln)
3671 if update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_ADMIN:
3672 raise CommandError("Entry found for value[%s] specified for --disable-tln is already disabled" % tln)
3673 update_forest_info.entries[idx].time = 0
3674 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3675 update_forest_info.entries[idx].flags |= lsa.LSA_TLN_DISABLED_ADMIN
3677 for tln_ex in add_tln_ex:
3679 for i in xrange(0, len(update_forest_info.entries)):
3680 r = update_forest_info.entries[i]
3681 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3683 if r.forest_trust_data.string.lower() != tln_ex.lower():
3688 raise CommandError("Entry already present for value[%s] specified for --add-tln-ex" % tln_ex)
3690 tln_dot = ".%s" % tln_ex.lower()
3692 for i in xrange(0, len(update_forest_info.entries)):
3693 r = update_forest_info.entries[i]
3694 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3696 r_dot = ".%s" % r.forest_trust_data.string.lower()
3697 if tln_dot == r_dot:
3698 raise CommandError("TLN entry present for value[%s] specified for --add-tln-ex" % tln_ex)
3699 if not tln_dot.endswith(r_dot):
3705 raise CommandError("No TLN parent present for value[%s] specified for --add-tln-ex" % tln_ex)
3707 r = lsa.ForestTrustRecord()
3708 r.type = lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX
3711 r.forest_trust_data.string = tln_ex
3714 entries.extend(update_forest_info.entries)
3715 entries.insert(idx + 1, r)
3716 update_forest_info.count = len(entries)
3717 update_forest_info.entries = entries
3719 for tln_ex in delete_tln_ex:
3721 for i in xrange(0, len(update_forest_info.entries)):
3722 r = update_forest_info.entries[i]
3723 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3725 if r.forest_trust_data.string.lower() != tln_ex.lower():
3730 raise CommandError("Entry not found for value[%s] specified for --delete-tln-ex" % tln_ex)
3733 entries.extend(update_forest_info.entries)
3735 update_forest_info.count = len(entries)
3736 update_forest_info.entries = entries
3738 for nb in enable_nb:
3740 for i in xrange(0, len(update_forest_info.entries)):
3741 r = update_forest_info.entries[i]
3742 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3744 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3749 raise CommandError("Entry not found for value[%s] specified for --enable-nb" % nb)
3750 if not update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_MASK:
3751 raise CommandError("Entry found for value[%s] specified for --enable-nb is already enabled" % nb)
3752 update_forest_info.entries[idx].time = 0
3753 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3755 for nb in disable_nb:
3757 for i in xrange(0, len(update_forest_info.entries)):
3758 r = update_forest_info.entries[i]
3759 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3761 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3766 raise CommandError("Entry not found for value[%s] specified for --delete-nb" % nb)
3767 if update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_ADMIN:
3768 raise CommandError("Entry found for value[%s] specified for --disable-nb is already disabled" % nb)
3769 update_forest_info.entries[idx].time = 0
3770 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3771 update_forest_info.entries[idx].flags |= lsa.LSA_NB_DISABLED_ADMIN
3773 for sid in enable_sid:
3775 for i in xrange(0, len(update_forest_info.entries)):
3776 r = update_forest_info.entries[i]
3777 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3779 if r.forest_trust_data.domain_sid != sid:
3784 raise CommandError("Entry not found for value[%s] specified for --enable-sid" % sid)
3785 if not update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_MASK:
3786 raise CommandError("Entry found for value[%s] specified for --enable-sid is already enabled" % nb)
3787 update_forest_info.entries[idx].time = 0
3788 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3790 for sid in disable_sid:
3792 for i in xrange(0, len(update_forest_info.entries)):
3793 r = update_forest_info.entries[i]
3794 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3796 if r.forest_trust_data.domain_sid != sid:
3801 raise CommandError("Entry not found for value[%s] specified for --delete-sid" % sid)
3802 if update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_ADMIN:
3803 raise CommandError("Entry found for value[%s] specified for --disable-sid is already disabled" % nb)
3804 update_forest_info.entries[idx].time = 0
3805 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3806 update_forest_info.entries[idx].flags |= lsa.LSA_SID_DISABLED_ADMIN
3809 update_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3810 local_tdo_info.domain_name,
3811 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3812 update_forest_info, 0)
3813 except RuntimeError as error:
3814 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3816 self.outf.write("Updated forest trust information...\n")
3817 self.write_forest_trust_info(update_forest_info,
3818 tln=local_tdo_info.domain_name.string,
3819 collisions=update_forest_collision)
3822 lsaString = lsa.String()
3823 lsaString.string = local_tdo_info.domain_name.string
3824 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3826 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3827 except RuntimeError as error:
3828 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3830 self.outf.write("Stored forest trust information...\n")
3831 self.write_forest_trust_info(stored_forest_info,
3832 tln=local_tdo_info.domain_name.string)
3835 class cmd_domain_tombstones_expunge(Command):
3836 """Expunge tombstones from the database.
3838 This command expunges tombstones from the database."""
3839 synopsis = "%prog NC [NC [...]] [options]"
3842 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
3843 metavar="URL", dest="H"),
3844 Option("--current-time",
3845 help="The current time to evaluate the tombstone lifetime from, expressed as YYYY-MM-DD",
3847 Option("--tombstone-lifetime", help="Number of days a tombstone should be preserved for", type=int),
3850 takes_args = ["nc*"]
3852 takes_optiongroups = {
3853 "sambaopts": options.SambaOptions,
3854 "credopts": options.CredentialsOptions,
3855 "versionopts": options.VersionOptions,
3858 def run(self, *ncs, **kwargs):
3859 sambaopts = kwargs.get("sambaopts")
3860 credopts = kwargs.get("credopts")
3861 versionpts = kwargs.get("versionopts")
3863 current_time_string = kwargs.get("current_time")
3864 tombstone_lifetime = kwargs.get("tombstone_lifetime")
3865 lp = sambaopts.get_loadparm()
3866 creds = credopts.get_credentials(lp)
3867 samdb = SamDB(url=H, session_info=system_session(),
3868 credentials=creds, lp=lp)
3870 if current_time_string is not None:
3871 current_time_obj = time.strptime(current_time_string, "%Y-%m-%d")
3872 current_time = long(time.mktime(current_time_obj))
3875 current_time = long(time.time())
3878 res = samdb.search(expression="", base="", scope=ldb.SCOPE_BASE,
3879 attrs=["namingContexts"])
3882 for nc in res[0]["namingContexts"]:
3887 started_transaction = False
3889 samdb.transaction_start()
3890 started_transaction = True
3892 removed_links) = samdb.garbage_collect_tombstones(ncs,
3893 current_time=current_time,
3894 tombstone_lifetime=tombstone_lifetime)
3896 except Exception as err:
3897 if started_transaction:
3898 samdb.transaction_cancel()
3899 raise CommandError("Failed to expunge / garbage collect tombstones", err)
3901 samdb.transaction_commit()
3903 self.outf.write("Removed %d objects and %d links successfully\n"
3904 % (removed_objects, removed_links))
3908 class cmd_domain_trust(SuperCommand):
3909 """Domain and forest trust management."""
3912 subcommands["list"] = cmd_domain_trust_list()
3913 subcommands["show"] = cmd_domain_trust_show()
3914 subcommands["create"] = cmd_domain_trust_create()
3915 subcommands["delete"] = cmd_domain_trust_delete()
3916 subcommands["validate"] = cmd_domain_trust_validate()
3917 subcommands["namespaces"] = cmd_domain_trust_namespaces()
3919 class cmd_domain_tombstones(SuperCommand):
3920 """Domain tombstone and recycled object management."""
3923 subcommands["expunge"] = cmd_domain_tombstones_expunge()
3925 class ldif_schema_update:
3926 """Helper class for applying LDIF schema updates"""
3929 self.is_defunct = False
3930 self.unknown_oid = None
3934 def _ldap_schemaUpdateNow(self, samdb):
3938 add: schemaUpdateNow
3941 samdb.modify_ldif(ldif)
3943 def can_ignore_failure(self, error):
3944 """Checks if we can safely ignore failure to apply an LDIF update"""
3945 (num, errstr) = error.args
3947 # Microsoft has marked objects as defunct that Samba doesn't know about
3948 if num == ldb.ERR_NO_SUCH_OBJECT and self.is_defunct:
3949 print("Defunct object %s doesn't exist, skipping" % self.dn)
3951 elif self.unknown_oid is not None:
3952 print("Skipping unknown OID %s for object %s" %(self.unknown_oid, self.dn))
3957 def apply(self, samdb):
3958 """Applies a single LDIF update to the schema"""
3962 samdb.modify_ldif(self.ldif, controls=['relax:0'])
3963 except ldb.LdbError as e:
3964 if e.args[0] == ldb.ERR_INVALID_ATTRIBUTE_SYNTAX:
3966 # REFRESH after a failed change
3968 # Otherwise the OID-to-attribute mapping in
3969 # _apply_updates_in_file() won't work, because it
3970 # can't lookup the new OID in the schema
3971 self._ldap_schemaUpdateNow(samdb)
3973 samdb.modify_ldif(self.ldif, controls=['relax:0'])
3976 except ldb.LdbError as e:
3977 if self.can_ignore_failure(e):
3980 print("Exception: %s" % e)
3981 print("Encountered while trying to apply the following LDIF")
3982 print("----------------------------------------------------")
3983 print("%s" % self.ldif)
3989 class cmd_domain_schema_upgrade(Command):
3990 """Domain schema upgrading"""
3992 synopsis = "%prog [options]"
3994 takes_optiongroups = {
3995 "sambaopts": options.SambaOptions,
3996 "versionopts": options.VersionOptions,
3997 "credopts": options.CredentialsOptions,
4001 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
4002 metavar="URL", dest="H"),
4003 Option("--quiet", help="Be quiet", action="store_true"),
4004 Option("--verbose", help="Be verbose", action="store_true"),
4005 Option("--schema", type="choice", metavar="SCHEMA",
4006 choices=["2012", "2012_R2"],
4007 help="The schema file to upgrade to. Default is (Windows) 2012_R2.",
4009 Option("--ldf-file", type=str, default=None,
4010 help="Just apply the schema updates in the adprep/.LDF file(s) specified"),
4011 Option("--base-dir", type=str, default=None,
4012 help="Location of ldf files Default is ${SETUPDIR}/adprep.")
4015 def _apply_updates_in_file(self, samdb, ldif_file):
4017 Applies a series of updates specified in an .LDIF file. The .LDIF file
4018 is based on the adprep Schema updates provided by Microsoft.
4021 ldif_op = ldif_schema_update()
4023 # parse the file line by line and work out each update operation to apply
4024 for line in ldif_file:
4026 line = line.rstrip()
4028 # the operations in the .LDIF file are separated by blank lines. If
4029 # we hit a blank line, try to apply the update we've parsed so far
4032 # keep going if we haven't parsed anything yet
4033 if ldif_op.ldif == '':
4036 # Apply the individual change
4037 count += ldif_op.apply(samdb)
4039 # start storing the next operation from scratch again
4040 ldif_op = ldif_schema_update()
4043 # replace the placeholder domain name in the .ldif file with the real domain
4044 if line.upper().endswith('DC=X'):
4045 line = line[:-len('DC=X')] + str(samdb.get_default_basedn())
4046 elif line.upper().endswith('CN=X'):
4047 line = line[:-len('CN=X')] + str(samdb.get_default_basedn())
4049 values = line.split(':')
4051 if values[0].lower() == 'dn':
4052 ldif_op.dn = values[1].strip()
4054 # replace the Windows-specific operation with the Samba one
4055 if values[0].lower() == 'changetype':
4056 line = line.lower().replace(': ntdsschemaadd',
4058 line = line.lower().replace(': ntdsschemamodify',
4061 if values[0].lower() in ['rdnattid', 'subclassof',
4062 'systemposssuperiors',
4064 'systemauxiliaryclass']:
4067 # The Microsoft updates contain some OIDs we don't recognize.
4068 # Query the DB to see if we can work out the OID this update is
4069 # referring to. If we find a match, then replace the OID with
4070 # the ldapDisplayname
4072 res = samdb.search(base=samdb.get_schema_basedn(),
4073 expression="(|(attributeId=%s)(governsId=%s))" %
4075 attrs=['ldapDisplayName'])
4078 ldif_op.unknown_oid = value
4080 display_name = res[0]['ldapDisplayName'][0]
4081 line = line.replace(value, ' ' + display_name)
4083 # Microsoft has marked objects as defunct that Samba doesn't know about
4084 if values[0].lower() == 'isdefunct' and values[1].strip().lower() == 'true':
4085 ldif_op.is_defunct = True
4087 # Samba has added the showInAdvancedViewOnly attribute to all objects,
4088 # so rather than doing an add, we need to do a replace
4089 if values[0].lower() == 'add' and values[1].strip().lower() == 'showinadvancedviewonly':
4090 line = 'replace: showInAdvancedViewOnly'
4092 # Add the line to the current LDIF operation (including the newline
4093 # we stripped off at the start of the loop)
4094 ldif_op.ldif += line + '\n'
4099 def _apply_update(self, samdb, update_file, base_dir):
4100 """Wrapper function for parsing an LDIF file and applying the updates"""
4102 print("Applying %s updates..." % update_file)
4106 ldif_file = open(os.path.join(base_dir, update_file))
4108 count = self._apply_updates_in_file(samdb, ldif_file)
4114 print("%u changes applied" % count)
4118 def run(self, **kwargs):
4119 from samba.ms_schema_markdown import read_ms_markdown
4120 from samba.schema import Schema
4122 updates_allowed_overriden = False
4123 sambaopts = kwargs.get("sambaopts")
4124 credopts = kwargs.get("credopts")
4125 versionpts = kwargs.get("versionopts")
4126 lp = sambaopts.get_loadparm()
4127 creds = credopts.get_credentials(lp)
4129 target_schema = kwargs.get("schema")
4130 ldf_files = kwargs.get("ldf_file")
4131 base_dir = kwargs.get("base_dir")
4135 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
4137 # we're not going to get far if the config doesn't allow schema updates
4138 if lp.get("dsdb:schema update allowed") is None:
4139 lp.set("dsdb:schema update allowed", "yes")
4140 print("Temporarily overriding 'dsdb:schema update allowed' setting")
4141 updates_allowed_overriden = True
4143 own_dn = ldb.Dn(samdb, samdb.get_dsServiceName())
4144 master = get_fsmo_roleowner(samdb, str(samdb.get_schema_basedn()),
4146 if own_dn != master:
4147 raise CommandError("This server is not the schema master.")
4149 # if specific LDIF files were specified, just apply them
4151 schema_updates = ldf_files.split(",")
4155 # work out the version of the target schema we're upgrading to
4156 end = Schema.get_version(target_schema)
4158 # work out the version of the schema we're currently using
4159 res = samdb.search(base=samdb.get_schema_basedn(),
4160 scope=ldb.SCOPE_BASE, attrs=['objectVersion'])
4163 raise CommandError('Could not determine current schema version')
4164 start = int(res[0]['objectVersion'][0]) + 1
4166 diff_dir = setup_path("adprep/WindowsServerDocs")
4167 if base_dir is None:
4168 # Read from the Schema-Updates.md file
4169 temp_folder = tempfile.mkdtemp()
4171 update_file = setup_path("adprep/WindowsServerDocs/Schema-Updates.md")
4174 read_ms_markdown(update_file, temp_folder)
4175 except Exception as e:
4176 print("Exception in markdown parsing: %s" % e)
4177 shutil.rmtree(temp_folder)
4178 raise CommandError('Failed to upgrade schema')
4180 base_dir = temp_folder
4182 for version in range(start, end + 1):
4183 update = 'Sch%d.ldf' % version
4184 schema_updates.append(update)
4186 # Apply patches if we parsed the Schema-Updates.md file
4187 diff = os.path.abspath(os.path.join(diff_dir, update + '.diff'))
4188 if temp_folder and os.path.exists(diff):
4190 p = subprocess.Popen(['patch', update, '-i', diff],
4191 stdout=subprocess.PIPE,
4192 stderr=subprocess.PIPE, cwd=temp_folder)
4193 except (OSError, IOError):
4194 shutil.rmtree(temp_folder)
4195 raise CommandError("Failed to upgrade schema. Check if 'patch' is installed.")
4197 stdout, stderr = p.communicate()
4200 print("Exception in patch: %s\n%s" % (stdout, stderr))
4201 shutil.rmtree(temp_folder)
4202 raise CommandError('Failed to upgrade schema')
4204 print("Patched %s using %s" % (update, diff))
4206 if base_dir is None:
4207 base_dir = setup_path("adprep")
4209 samdb.transaction_start()
4211 error_encountered = False
4214 # Apply the schema updates needed to move to the new schema version
4215 for ldif_file in schema_updates:
4216 count += self._apply_update(samdb, ldif_file, base_dir)
4219 samdb.transaction_commit()
4220 print("Schema successfully updated")
4222 print("No changes applied to schema")
4223 samdb.transaction_cancel()
4224 except Exception as e:
4225 print("Exception: %s" % e)
4226 print("Error encountered, aborting schema upgrade")
4227 samdb.transaction_cancel()
4228 error_encountered = True
4230 if updates_allowed_overriden:
4231 lp.set("dsdb:schema update allowed", "no")
4234 shutil.rmtree(temp_folder)
4236 if error_encountered:
4237 raise CommandError('Failed to upgrade schema')
4239 class cmd_domain_functional_prep(Command):
4240 """Domain functional level preparation"""
4242 synopsis = "%prog [options]"
4244 takes_optiongroups = {
4245 "sambaopts": options.SambaOptions,
4246 "versionopts": options.VersionOptions,
4247 "credopts": options.CredentialsOptions,
4251 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
4252 metavar="URL", dest="H"),
4253 Option("--quiet", help="Be quiet", action="store_true"),
4254 Option("--verbose", help="Be verbose", action="store_true"),
4255 Option("--function-level", type="choice", metavar="FUNCTION_LEVEL",
4256 choices=["2008_R2", "2012", "2012_R2"],
4257 help="The schema file to upgrade to. Default is (Windows) 2012_R2.",
4259 Option("--forest-prep", action="store_true",
4260 help="Run the forest prep (by default, both the domain and forest prep are run)."),
4261 Option("--domain-prep", action="store_true",
4262 help="Run the domain prep (by default, both the domain and forest prep are run).")
4265 def run(self, **kwargs):
4266 updates_allowed_overriden = False
4267 sambaopts = kwargs.get("sambaopts")
4268 credopts = kwargs.get("credopts")
4269 versionpts = kwargs.get("versionopts")
4270 lp = sambaopts.get_loadparm()
4271 creds = credopts.get_credentials(lp)
4273 target_level = string_version_to_constant[kwargs.get("function_level")]
4274 forest_prep = kwargs.get("forest_prep")
4275 domain_prep = kwargs.get("domain_prep")
4277 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
4279 # we're not going to get far if the config doesn't allow schema updates
4280 if lp.get("dsdb:schema update allowed") is None:
4281 lp.set("dsdb:schema update allowed", "yes")
4282 print("Temporarily overriding 'dsdb:schema update allowed' setting")
4283 updates_allowed_overriden = True
4285 if forest_prep is None and domain_prep is None:
4289 own_dn = ldb.Dn(samdb, samdb.get_dsServiceName())
4291 master = get_fsmo_roleowner(samdb, str(samdb.get_schema_basedn()),
4293 if own_dn != master:
4294 raise CommandError("This server is not the schema master.")
4297 domain_dn = samdb.domain_dn()
4298 infrastructure_dn = "CN=Infrastructure," + domain_dn
4299 master = get_fsmo_roleowner(samdb, infrastructure_dn,
4301 if own_dn != master:
4302 raise CommandError("This server is not the infrastructure master.")
4305 samdb.transaction_start()
4306 error_encountered = False
4308 from samba.forest_update import ForestUpdate
4309 forest = ForestUpdate(samdb, fix=True)
4311 forest.check_updates_iterator([53, 79, 80, 81, 82, 83])
4312 forest.check_updates_functional_level(target_level,
4313 DS_DOMAIN_FUNCTION_2008_R2,
4314 update_revision=True)
4316 samdb.transaction_commit()
4317 except Exception as e:
4318 print("Exception: %s" % e)
4319 samdb.transaction_cancel()
4320 error_encountered = True
4323 samdb.transaction_start()
4324 error_encountered = False
4326 from samba.domain_update import DomainUpdate
4328 domain = DomainUpdate(samdb, fix=True)
4329 domain.check_updates_functional_level(target_level,
4330 DS_DOMAIN_FUNCTION_2008,
4331 update_revision=True)
4333 samdb.transaction_commit()
4334 except Exception as e:
4335 print("Exception: %s" % e)
4336 samdb.transaction_cancel()
4337 error_encountered = True
4339 if updates_allowed_overriden:
4340 lp.set("dsdb:schema update allowed", "no")
4342 if error_encountered:
4343 raise CommandError('Failed to perform functional prep')
4345 class cmd_domain(SuperCommand):
4346 """Domain management."""
4349 subcommands["demote"] = cmd_domain_demote()
4350 if cmd_domain_export_keytab is not None:
4351 subcommands["exportkeytab"] = cmd_domain_export_keytab()
4352 subcommands["info"] = cmd_domain_info()
4353 subcommands["provision"] = cmd_domain_provision()
4354 subcommands["join"] = cmd_domain_join()
4355 subcommands["dcpromo"] = cmd_domain_dcpromo()
4356 subcommands["level"] = cmd_domain_level()
4357 subcommands["passwordsettings"] = cmd_domain_passwordsettings()
4358 subcommands["classicupgrade"] = cmd_domain_classicupgrade()
4359 subcommands["samba3upgrade"] = cmd_domain_samba3upgrade()
4360 subcommands["trust"] = cmd_domain_trust()
4361 subcommands["tombstones"] = cmd_domain_tombstones()
4362 subcommands["schemaupgrade"] = cmd_domain_schema_upgrade()
4363 subcommands["functionalprep"] = cmd_domain_functional_prep()