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("--targetdir", metavar="DIR",
110 help="Set target directory (where to store provision)", type=str)
113 def get_testparm_var(testparm, smbconf, varname):
114 errfile = open(os.devnull, 'w')
115 p = subprocess.Popen([testparm, '-s', '-l',
116 '--parameter-name=%s' % varname, smbconf],
117 stdout=subprocess.PIPE, stderr=errfile)
118 (out,err) = p.communicate()
120 lines = out.split('\n')
122 return lines[0].strip()
126 import samba.dckeytab
128 cmd_domain_export_keytab = None
130 class cmd_domain_export_keytab(Command):
131 """Dump Kerberos keys of the domain into a keytab."""
133 synopsis = "%prog <keytab> [options]"
135 takes_optiongroups = {
136 "sambaopts": options.SambaOptions,
137 "credopts": options.CredentialsOptions,
138 "versionopts": options.VersionOptions,
142 Option("--principal", help="extract only this principal", type=str),
145 takes_args = ["keytab"]
147 def run(self, keytab, credopts=None, sambaopts=None, versionopts=None, principal=None):
148 lp = sambaopts.get_loadparm()
150 net.export_keytab(keytab=keytab, principal=principal)
153 class cmd_domain_info(Command):
154 """Print basic info about a domain and the DC passed as parameter."""
156 synopsis = "%prog <ip_address> [options]"
161 takes_optiongroups = {
162 "sambaopts": options.SambaOptions,
163 "credopts": options.CredentialsOptions,
164 "versionopts": options.VersionOptions,
167 takes_args = ["address"]
169 def run(self, address, credopts=None, sambaopts=None, versionopts=None):
170 lp = sambaopts.get_loadparm()
172 res = netcmd_get_domain_infos_via_cldap(lp, None, address)
174 raise CommandError("Invalid IP address '" + address + "'!")
175 self.outf.write("Forest : %s\n" % res.forest)
176 self.outf.write("Domain : %s\n" % res.dns_domain)
177 self.outf.write("Netbios domain : %s\n" % res.domain_name)
178 self.outf.write("DC name : %s\n" % res.pdc_dns_name)
179 self.outf.write("DC netbios name : %s\n" % res.pdc_name)
180 self.outf.write("Server site : %s\n" % res.server_site)
181 self.outf.write("Client site : %s\n" % res.client_site)
184 class cmd_domain_provision(Command):
185 """Provision a domain."""
187 synopsis = "%prog [options]"
189 takes_optiongroups = {
190 "sambaopts": options.SambaOptions,
191 "versionopts": options.VersionOptions,
195 Option("--interactive", help="Ask for names", action="store_true"),
196 Option("--domain", type="string", metavar="DOMAIN",
197 help="NetBIOS domain name to use"),
198 Option("--domain-guid", type="string", metavar="GUID",
199 help="set domainguid (otherwise random)"),
200 Option("--domain-sid", type="string", metavar="SID",
201 help="set domainsid (otherwise random)"),
202 Option("--ntds-guid", type="string", metavar="GUID",
203 help="set NTDS object GUID (otherwise random)"),
204 Option("--invocationid", type="string", metavar="GUID",
205 help="set invocationid (otherwise random)"),
206 Option("--host-name", type="string", metavar="HOSTNAME",
207 help="set hostname"),
208 Option("--host-ip", type="string", metavar="IPADDRESS",
209 help="set IPv4 ipaddress"),
210 Option("--host-ip6", type="string", metavar="IP6ADDRESS",
211 help="set IPv6 ipaddress"),
212 Option("--site", type="string", metavar="SITENAME",
213 help="set site name"),
214 Option("--adminpass", type="string", metavar="PASSWORD",
215 help="choose admin password (otherwise random)"),
216 Option("--krbtgtpass", type="string", metavar="PASSWORD",
217 help="choose krbtgt password (otherwise random)"),
218 Option("--machinepass", type="string", metavar="PASSWORD",
219 help="choose machine password (otherwise random)"),
220 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
221 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
222 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
223 "BIND9_FLATFILE uses bind9 text database to store zone information, "
224 "BIND9_DLZ uses samba4 AD to store zone information, "
225 "NONE skips the DNS setup entirely (not recommended)",
226 default="SAMBA_INTERNAL"),
227 Option("--dnspass", type="string", metavar="PASSWORD",
228 help="choose dns password (otherwise random)"),
229 Option("--root", type="string", metavar="USERNAME",
230 help="choose 'root' unix username"),
231 Option("--nobody", type="string", metavar="USERNAME",
232 help="choose 'nobody' user"),
233 Option("--users", type="string", metavar="GROUPNAME",
234 help="choose 'users' group"),
235 Option("--quiet", help="Be quiet", action="store_true"),
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("--machinepass", type=str, metavar="PASSWORD",
578 help="choose machine password (otherwise random)"),
579 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
580 choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
581 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
582 "BIND9_DLZ uses samba4 AD to store zone information, "
583 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
584 default="SAMBA_INTERNAL"),
585 Option("--quiet", help="Be quiet", action="store_true"),
586 Option("--verbose", help="Be verbose", action="store_true")
589 takes_options.extend(common_provision_join_options)
592 Option("--use-ntvfs", action="store_true", help="Use NTVFS for the fileserver (default = no)"),
595 if samba.is_ntvfs_fileserver_built():
596 takes_options.extend(ntvfs_options)
599 takes_args = ["domain", "role?"]
601 def run(self, domain, role=None, sambaopts=None, credopts=None,
602 versionopts=None, server=None, site=None, targetdir=None,
603 domain_critical_only=False, parent_domain=None, machinepass=None,
604 use_ntvfs=False, dns_backend=None,
605 quiet=False, verbose=False):
606 lp = sambaopts.get_loadparm()
607 creds = credopts.get_credentials(lp)
608 net = Net(creds, lp, server=credopts.ipaddress)
610 logger = self.get_logger()
612 logger.setLevel(logging.DEBUG)
614 logger.setLevel(logging.WARNING)
616 logger.setLevel(logging.INFO)
618 netbios_name = lp.get("netbios name")
624 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
625 site=site, netbios_name=netbios_name, targetdir=targetdir,
626 domain_critical_only=domain_critical_only,
627 machinepass=machinepass, use_ntvfs=use_ntvfs,
628 dns_backend=dns_backend,
629 promote_existing=True)
631 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
632 site=site, netbios_name=netbios_name, targetdir=targetdir,
633 domain_critical_only=domain_critical_only,
634 machinepass=machinepass, use_ntvfs=use_ntvfs, dns_backend=dns_backend,
635 promote_existing=True)
637 raise CommandError("Invalid role '%s' (possible values: DC, RODC)" % role)
640 class cmd_domain_join(Command):
641 """Join domain as either member or backup domain controller."""
643 synopsis = "%prog <dnsdomain> [DC|RODC|MEMBER|SUBDOMAIN] [options]"
645 takes_optiongroups = {
646 "sambaopts": options.SambaOptions,
647 "versionopts": options.VersionOptions,
648 "credopts": options.CredentialsOptions,
652 Option("--server", help="DC to join", type=str),
653 Option("--site", help="site to join", type=str),
654 Option("--parent-domain", help="parent domain to create subdomain under", type=str),
655 Option("--domain-critical-only",
656 help="only replicate critical domain objects",
657 action="store_true"),
658 Option("--machinepass", type=str, metavar="PASSWORD",
659 help="choose machine password (otherwise random)"),
660 Option("--adminpass", type="string", metavar="PASSWORD",
661 help="choose adminstrator password when joining as a subdomain (otherwise random)"),
662 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
663 choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
664 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
665 "BIND9_DLZ uses samba4 AD to store zone information, "
666 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
667 default="SAMBA_INTERNAL"),
668 Option("--plaintext-secrets", action="store_true",
669 help="Store secret/sensitive values as plain text on disk" +
670 "(default is to encrypt secret/ensitive values)"),
671 Option("--quiet", help="Be quiet", action="store_true"),
672 Option("--verbose", help="Be verbose", action="store_true")
676 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
679 takes_options.extend(common_provision_join_options)
681 if samba.is_ntvfs_fileserver_built():
682 takes_options.extend(ntvfs_options)
684 takes_args = ["domain", "role?"]
686 def run(self, domain, role=None, sambaopts=None, credopts=None,
687 versionopts=None, server=None, site=None, targetdir=None,
688 domain_critical_only=False, parent_domain=None, machinepass=None,
689 use_ntvfs=False, dns_backend=None, adminpass=None,
690 quiet=False, verbose=False, plaintext_secrets=False):
691 lp = sambaopts.get_loadparm()
692 creds = credopts.get_credentials(lp)
693 net = Net(creds, lp, server=credopts.ipaddress)
696 site = "Default-First-Site-Name"
698 logger = self.get_logger()
700 logger.setLevel(logging.DEBUG)
702 logger.setLevel(logging.WARNING)
704 logger.setLevel(logging.INFO)
706 netbios_name = lp.get("netbios name")
711 if role is None or role == "MEMBER":
712 (join_password, sid, domain_name) = net.join_member(
713 domain, netbios_name, LIBNET_JOIN_AUTOMATIC,
714 machinepass=machinepass)
716 self.errf.write("Joined domain %s (%s)\n" % (domain_name, sid))
718 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
719 site=site, netbios_name=netbios_name, targetdir=targetdir,
720 domain_critical_only=domain_critical_only,
721 machinepass=machinepass, use_ntvfs=use_ntvfs,
722 dns_backend=dns_backend,
723 plaintext_secrets=plaintext_secrets)
725 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
726 site=site, netbios_name=netbios_name, targetdir=targetdir,
727 domain_critical_only=domain_critical_only,
728 machinepass=machinepass, use_ntvfs=use_ntvfs,
729 dns_backend=dns_backend,
730 plaintext_secrets=plaintext_secrets)
731 elif role == "SUBDOMAIN":
733 logger.info("Administrator password will be set randomly!")
735 netbios_domain = lp.get("workgroup")
736 if parent_domain is None:
737 parent_domain = ".".join(domain.split(".")[1:])
738 join_subdomain(logger=logger, server=server, creds=creds, lp=lp, dnsdomain=domain,
739 parent_domain=parent_domain, site=site,
740 netbios_name=netbios_name, netbios_domain=netbios_domain,
741 targetdir=targetdir, machinepass=machinepass,
742 use_ntvfs=use_ntvfs, dns_backend=dns_backend,
744 plaintext_secrets=plaintext_secrets)
746 raise CommandError("Invalid role '%s' (possible values: MEMBER, DC, RODC, SUBDOMAIN)" % role)
749 class cmd_domain_demote(Command):
750 """Demote ourselves from the role of Domain Controller."""
752 synopsis = "%prog [options]"
755 Option("--server", help="writable DC to write demotion changes on", type=str),
756 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
757 metavar="URL", dest="H"),
758 Option("--remove-other-dead-server", help="Dead DC (name or NTDS GUID) "
759 "to remove ALL references to (rather than this DC)", type=str),
760 Option("--quiet", help="Be quiet", action="store_true"),
761 Option("--verbose", help="Be verbose", action="store_true"),
764 takes_optiongroups = {
765 "sambaopts": options.SambaOptions,
766 "credopts": options.CredentialsOptions,
767 "versionopts": options.VersionOptions,
770 def run(self, sambaopts=None, credopts=None,
771 versionopts=None, server=None,
772 remove_other_dead_server=None, H=None,
773 verbose=False, quiet=False):
774 lp = sambaopts.get_loadparm()
775 creds = credopts.get_credentials(lp)
776 net = Net(creds, lp, server=credopts.ipaddress)
778 logger = self.get_logger()
780 logger.setLevel(logging.DEBUG)
782 logger.setLevel(logging.WARNING)
784 logger.setLevel(logging.INFO)
786 if remove_other_dead_server is not None:
787 if server is not None:
788 samdb = SamDB(url="ldap://%s" % server,
789 session_info=system_session(),
790 credentials=creds, lp=lp)
792 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
794 remove_dc.remove_dc(samdb, logger, remove_other_dead_server)
795 except remove_dc.DemoteException as err:
796 raise CommandError("Demote failed: %s" % err)
799 netbios_name = lp.get("netbios name")
800 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
802 res = samdb.search(expression='(&(objectClass=computer)(serverReferenceBL=*))', attrs=["dnsHostName", "name"])
804 raise CommandError("Unable to search for servers")
807 raise CommandError("You are the latest server in the domain")
811 if str(e["name"]).lower() != netbios_name.lower():
812 server = e["dnsHostName"]
815 ntds_guid = samdb.get_ntds_GUID()
816 msg = samdb.search(base=str(samdb.get_config_basedn()),
817 scope=ldb.SCOPE_SUBTREE, expression="(objectGUID=%s)" % ntds_guid,
819 if len(msg) == 0 or "options" not in msg[0]:
820 raise CommandError("Failed to find options on %s" % ntds_guid)
823 dsa_options = int(str(msg[0]['options']))
825 res = samdb.search(expression="(fSMORoleOwner=%s)" % str(ntds_dn),
826 controls=["search_options:1:2"])
829 raise CommandError("Current DC is still the owner of %d role(s), use the role command to transfer roles to another DC" % len(res))
831 self.errf.write("Using %s as partner server for the demotion\n" %
833 (drsuapiBind, drsuapi_handle, supportedExtensions) = drsuapi_connect(server, lp, creds)
835 self.errf.write("Deactivating inbound replication\n")
840 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
841 dsa_options |= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
842 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
846 self.errf.write("Asking partner server %s to synchronize from us\n"
848 for part in (samdb.get_schema_basedn(),
849 samdb.get_config_basedn(),
850 samdb.get_root_basedn()):
851 nc = drsuapi.DsReplicaObjectIdentifier()
854 req1 = drsuapi.DsReplicaSyncRequest1()
855 req1.naming_context = nc;
856 req1.options = drsuapi.DRSUAPI_DRS_WRIT_REP
857 req1.source_dsa_guid = misc.GUID(ntds_guid)
860 drsuapiBind.DsReplicaSync(drsuapi_handle, 1, req1)
861 except RuntimeError as e1:
862 (werr, string) = e1.args
863 if werr == werror.WERR_DS_DRA_NO_REPLICA:
867 "Error while replicating out last local changes from '%s' for demotion, "
868 "re-enabling inbound replication\n" % part)
869 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
870 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
872 raise CommandError("Error while sending a DsReplicaSync for partition '%s'" % str(part), string)
874 remote_samdb = SamDB(url="ldap://%s" % server,
875 session_info=system_session(),
876 credentials=creds, lp=lp)
878 self.errf.write("Changing userControl and container\n")
879 res = remote_samdb.search(base=str(remote_samdb.domain_dn()),
880 expression="(&(objectClass=user)(sAMAccountName=%s$))" %
881 netbios_name.upper(),
882 attrs=["userAccountControl"])
884 uac = int(str(res[0]["userAccountControl"]))
886 except Exception as e:
887 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
889 "Error while demoting, re-enabling inbound replication\n")
890 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
891 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
893 raise CommandError("Error while changing account control", e)
896 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
898 "Error while demoting, re-enabling inbound replication")
899 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
900 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
902 raise CommandError("Unable to find object with samaccountName = %s$"
903 " in the remote dc" % netbios_name.upper())
907 uac &= ~(UF_SERVER_TRUST_ACCOUNT|UF_TRUSTED_FOR_DELEGATION|UF_PARTIAL_SECRETS_ACCOUNT)
908 uac |= UF_WORKSTATION_TRUST_ACCOUNT
913 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
914 ldb.FLAG_MOD_REPLACE,
915 "userAccountControl")
917 remote_samdb.modify(msg)
918 except Exception as e:
919 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
921 "Error while demoting, re-enabling inbound replication")
922 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
923 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
926 raise CommandError("Error while changing account control", e)
928 parent = msg.dn.parent()
929 dc_name = res[0].dn.get_rdn_value()
930 rdn = "CN=%s" % dc_name
932 # Let's move to the Computer container
936 computer_dn = ldb.Dn(remote_samdb, "CN=Computers,%s" % str(remote_samdb.domain_dn()))
937 res = remote_samdb.search(base=computer_dn, expression=rdn, scope=ldb.SCOPE_ONELEVEL)
940 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
941 scope=ldb.SCOPE_ONELEVEL)
942 while(len(res) != 0 and i < 100):
944 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
945 scope=ldb.SCOPE_ONELEVEL)
948 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
950 "Error while demoting, re-enabling inbound replication\n")
951 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
952 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
958 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
959 ldb.FLAG_MOD_REPLACE,
960 "userAccountControl")
962 remote_samdb.modify(msg)
964 raise CommandError("Unable to find a slot for renaming %s,"
965 " all names from %s-1 to %s-%d seemed used" %
966 (str(dc_dn), rdn, rdn, i - 9))
968 newrdn = "%s-%d" % (rdn, i)
971 newdn = ldb.Dn(remote_samdb, "%s,%s" % (newrdn, str(computer_dn)))
972 remote_samdb.rename(dc_dn, newdn)
973 except Exception as e:
974 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
976 "Error while demoting, re-enabling inbound replication\n")
977 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
978 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
984 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
985 ldb.FLAG_MOD_REPLACE,
986 "userAccountControl")
988 remote_samdb.modify(msg)
989 raise CommandError("Error while renaming %s to %s" % (str(dc_dn), str(newdn)), e)
992 server_dsa_dn = samdb.get_serverName()
993 domain = remote_samdb.get_root_basedn()
996 req1 = drsuapi.DsRemoveDSServerRequest1()
997 req1.server_dn = str(server_dsa_dn)
998 req1.domain_dn = str(domain)
1001 drsuapiBind.DsRemoveDSServer(drsuapi_handle, 1, req1)
1002 except RuntimeError as e3:
1003 (werr, string) = e3.args
1004 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
1006 "Error while demoting, re-enabling inbound replication\n")
1007 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
1008 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
1014 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
1015 ldb.FLAG_MOD_REPLACE,
1016 "userAccountControl")
1017 remote_samdb.modify(msg)
1018 remote_samdb.rename(newdn, dc_dn)
1019 if werr == werror.WERR_DS_DRA_NO_REPLICA:
1020 raise CommandError("The DC %s is not present on (already removed from) the remote server: " % server_dsa_dn, e)
1022 raise CommandError("Error while sending a removeDsServer of %s: " % server_dsa_dn, e)
1024 remove_dc.remove_sysvol_references(remote_samdb, logger, dc_name)
1026 # These are objects under the computer account that should be deleted
1027 for s in ("CN=Enterprise,CN=NTFRS Subscriptions",
1028 "CN=%s, CN=NTFRS Subscriptions" % lp.get("realm"),
1029 "CN=Domain system Volumes (SYSVOL Share), CN=NTFRS Subscriptions",
1030 "CN=NTFRS Subscriptions"):
1032 remote_samdb.delete(ldb.Dn(remote_samdb,
1033 "%s,%s" % (s, str(newdn))))
1034 except ldb.LdbError as l:
1037 self.errf.write("Demote successful\n")
1040 class cmd_domain_level(Command):
1041 """Raise domain and forest function levels."""
1043 synopsis = "%prog (show|raise <options>) [options]"
1045 takes_optiongroups = {
1046 "sambaopts": options.SambaOptions,
1047 "credopts": options.CredentialsOptions,
1048 "versionopts": options.VersionOptions,
1052 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1053 metavar="URL", dest="H"),
1054 Option("--quiet", help="Be quiet", action="store_true"),
1055 Option("--forest-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
1056 help="The forest function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)"),
1057 Option("--domain-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
1058 help="The domain function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)")
1061 takes_args = ["subcommand"]
1063 def run(self, subcommand, H=None, forest_level=None, domain_level=None,
1064 quiet=False, credopts=None, sambaopts=None, versionopts=None):
1065 lp = sambaopts.get_loadparm()
1066 creds = credopts.get_credentials(lp, fallback_machine=True)
1068 samdb = SamDB(url=H, session_info=system_session(),
1069 credentials=creds, lp=lp)
1071 domain_dn = samdb.domain_dn()
1073 res_forest = samdb.search("CN=Partitions,%s" % samdb.get_config_basedn(),
1074 scope=ldb.SCOPE_BASE, attrs=["msDS-Behavior-Version"])
1075 assert len(res_forest) == 1
1077 res_domain = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1078 attrs=["msDS-Behavior-Version", "nTMixedDomain"])
1079 assert len(res_domain) == 1
1081 res_dc_s = samdb.search("CN=Sites,%s" % samdb.get_config_basedn(),
1082 scope=ldb.SCOPE_SUBTREE, expression="(objectClass=nTDSDSA)",
1083 attrs=["msDS-Behavior-Version"])
1084 assert len(res_dc_s) >= 1
1086 # default values, since "msDS-Behavior-Version" does not exist on Windows 2000 AD
1087 level_forest = DS_DOMAIN_FUNCTION_2000
1088 level_domain = DS_DOMAIN_FUNCTION_2000
1090 if "msDS-Behavior-Version" in res_forest[0]:
1091 level_forest = int(res_forest[0]["msDS-Behavior-Version"][0])
1092 if "msDS-Behavior-Version" in res_domain[0]:
1093 level_domain = int(res_domain[0]["msDS-Behavior-Version"][0])
1094 level_domain_mixed = int(res_domain[0]["nTMixedDomain"][0])
1097 for msg in res_dc_s:
1098 if "msDS-Behavior-Version" in msg:
1099 if min_level_dc is None or int(msg["msDS-Behavior-Version"][0]) < min_level_dc:
1100 min_level_dc = int(msg["msDS-Behavior-Version"][0])
1102 min_level_dc = DS_DOMAIN_FUNCTION_2000
1103 # well, this is the least
1106 if level_forest < DS_DOMAIN_FUNCTION_2000 or level_domain < DS_DOMAIN_FUNCTION_2000:
1107 raise CommandError("Domain and/or forest function level(s) is/are invalid. Correct them or reprovision!")
1108 if min_level_dc < DS_DOMAIN_FUNCTION_2000:
1109 raise CommandError("Lowest function level of a DC is invalid. Correct this or reprovision!")
1110 if level_forest > level_domain:
1111 raise CommandError("Forest function level is higher than the domain level(s). Correct this or reprovision!")
1112 if level_domain > min_level_dc:
1113 raise CommandError("Domain function level is higher than the lowest function level of a DC. Correct this or reprovision!")
1115 if subcommand == "show":
1116 self.message("Domain and forest function level for domain '%s'" % domain_dn)
1117 if level_forest == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1118 self.message("\nATTENTION: You run SAMBA 4 on a forest function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1119 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1120 self.message("\nATTENTION: You run SAMBA 4 on a domain function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1121 if min_level_dc == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1122 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)!")
1126 if level_forest == DS_DOMAIN_FUNCTION_2000:
1128 elif level_forest == DS_DOMAIN_FUNCTION_2003_MIXED:
1129 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1130 elif level_forest == DS_DOMAIN_FUNCTION_2003:
1132 elif level_forest == DS_DOMAIN_FUNCTION_2008:
1134 elif level_forest == DS_DOMAIN_FUNCTION_2008_R2:
1136 elif level_forest == DS_DOMAIN_FUNCTION_2012:
1138 elif level_forest == DS_DOMAIN_FUNCTION_2012_R2:
1141 outstr = "higher than 2012 R2"
1142 self.message("Forest function level: (Windows) " + outstr)
1144 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1145 outstr = "2000 mixed (NT4 DC support)"
1146 elif level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed == 0:
1148 elif level_domain == DS_DOMAIN_FUNCTION_2003_MIXED:
1149 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1150 elif level_domain == DS_DOMAIN_FUNCTION_2003:
1152 elif level_domain == DS_DOMAIN_FUNCTION_2008:
1154 elif level_domain == DS_DOMAIN_FUNCTION_2008_R2:
1156 elif level_domain == DS_DOMAIN_FUNCTION_2012:
1158 elif level_domain == DS_DOMAIN_FUNCTION_2012_R2:
1161 outstr = "higher than 2012 R2"
1162 self.message("Domain function level: (Windows) " + outstr)
1164 if min_level_dc == DS_DOMAIN_FUNCTION_2000:
1166 elif min_level_dc == DS_DOMAIN_FUNCTION_2003:
1168 elif min_level_dc == DS_DOMAIN_FUNCTION_2008:
1170 elif min_level_dc == DS_DOMAIN_FUNCTION_2008_R2:
1172 elif min_level_dc == DS_DOMAIN_FUNCTION_2012:
1174 elif min_level_dc == DS_DOMAIN_FUNCTION_2012_R2:
1177 outstr = "higher than 2012 R2"
1178 self.message("Lowest function level of a DC: (Windows) " + outstr)
1180 elif subcommand == "raise":
1183 if domain_level is not None:
1184 if domain_level == "2003":
1185 new_level_domain = DS_DOMAIN_FUNCTION_2003
1186 elif domain_level == "2008":
1187 new_level_domain = DS_DOMAIN_FUNCTION_2008
1188 elif domain_level == "2008_R2":
1189 new_level_domain = DS_DOMAIN_FUNCTION_2008_R2
1190 elif domain_level == "2012":
1191 new_level_domain = DS_DOMAIN_FUNCTION_2012
1192 elif domain_level == "2012_R2":
1193 new_level_domain = DS_DOMAIN_FUNCTION_2012_R2
1195 if new_level_domain <= level_domain and level_domain_mixed == 0:
1196 raise CommandError("Domain function level can't be smaller than or equal to the actual one!")
1197 if new_level_domain > min_level_dc:
1198 raise CommandError("Domain function level can't be higher than the lowest function level of a DC!")
1200 # Deactivate mixed/interim domain support
1201 if level_domain_mixed != 0:
1202 # Directly on the base DN
1204 m.dn = ldb.Dn(samdb, domain_dn)
1205 m["nTMixedDomain"] = ldb.MessageElement("0",
1206 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1210 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup") + ",CN=Partitions,%s" % samdb.get_config_basedn())
1211 m["nTMixedDomain"] = ldb.MessageElement("0",
1212 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1215 except ldb.LdbError as e:
1216 (enum, emsg) = e.args
1217 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1220 # Directly on the base DN
1222 m.dn = ldb.Dn(samdb, domain_dn)
1223 m["msDS-Behavior-Version"]= ldb.MessageElement(
1224 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1225 "msDS-Behavior-Version")
1229 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup")
1230 + ",CN=Partitions,%s" % samdb.get_config_basedn())
1231 m["msDS-Behavior-Version"]= ldb.MessageElement(
1232 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1233 "msDS-Behavior-Version")
1236 except ldb.LdbError as e2:
1237 (enum, emsg) = e2.args
1238 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1241 level_domain = new_level_domain
1242 msgs.append("Domain function level changed!")
1244 if forest_level is not None:
1245 if forest_level == "2003":
1246 new_level_forest = DS_DOMAIN_FUNCTION_2003
1247 elif forest_level == "2008":
1248 new_level_forest = DS_DOMAIN_FUNCTION_2008
1249 elif forest_level == "2008_R2":
1250 new_level_forest = DS_DOMAIN_FUNCTION_2008_R2
1251 elif forest_level == "2012":
1252 new_level_forest = DS_DOMAIN_FUNCTION_2012
1253 elif forest_level == "2012_R2":
1254 new_level_forest = DS_DOMAIN_FUNCTION_2012_R2
1256 if new_level_forest <= level_forest:
1257 raise CommandError("Forest function level can't be smaller than or equal to the actual one!")
1258 if new_level_forest > level_domain:
1259 raise CommandError("Forest function level can't be higher than the domain function level(s). Please raise it/them first!")
1262 m.dn = ldb.Dn(samdb, "CN=Partitions,%s" % samdb.get_config_basedn())
1263 m["msDS-Behavior-Version"]= ldb.MessageElement(
1264 str(new_level_forest), ldb.FLAG_MOD_REPLACE,
1265 "msDS-Behavior-Version")
1267 msgs.append("Forest function level changed!")
1268 msgs.append("All changes applied successfully!")
1269 self.message("\n".join(msgs))
1271 raise CommandError("invalid argument: '%s' (choose from 'show', 'raise')" % subcommand)
1273 class cmd_domain_passwordsettings_show(Command):
1274 """Display current password settings for the domain."""
1276 synopsis = "%prog [options]"
1278 takes_optiongroups = {
1279 "sambaopts": options.SambaOptions,
1280 "versionopts": options.VersionOptions,
1281 "credopts": options.CredentialsOptions,
1285 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1286 metavar="URL", dest="H"),
1289 def run(self, H=None, credopts=None, sambaopts=None, versionopts=None):
1290 lp = sambaopts.get_loadparm()
1291 creds = credopts.get_credentials(lp)
1293 samdb = SamDB(url=H, session_info=system_session(),
1294 credentials=creds, lp=lp)
1296 domain_dn = samdb.domain_dn()
1297 res = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1298 attrs=["pwdProperties", "pwdHistoryLength", "minPwdLength",
1299 "minPwdAge", "maxPwdAge", "lockoutDuration", "lockoutThreshold",
1300 "lockOutObservationWindow"])
1301 assert(len(res) == 1)
1303 pwd_props = int(res[0]["pwdProperties"][0])
1304 pwd_hist_len = int(res[0]["pwdHistoryLength"][0])
1305 cur_min_pwd_len = int(res[0]["minPwdLength"][0])
1307 cur_min_pwd_age = int(abs(int(res[0]["minPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1308 if int(res[0]["maxPwdAge"][0]) == -0x8000000000000000:
1311 cur_max_pwd_age = int(abs(int(res[0]["maxPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1312 cur_account_lockout_threshold = int(res[0]["lockoutThreshold"][0])
1314 if int(res[0]["lockoutDuration"][0]) == -0x8000000000000000:
1315 cur_account_lockout_duration = 0
1317 cur_account_lockout_duration = abs(int(res[0]["lockoutDuration"][0])) / (1e7 * 60)
1318 cur_reset_account_lockout_after = abs(int(res[0]["lockOutObservationWindow"][0])) / (1e7 * 60)
1319 except Exception as e:
1320 raise CommandError("Could not retrieve password properties!", e)
1322 self.message("Password informations for domain '%s'" % domain_dn)
1324 if pwd_props & DOMAIN_PASSWORD_COMPLEX != 0:
1325 self.message("Password complexity: on")
1327 self.message("Password complexity: off")
1328 if pwd_props & DOMAIN_PASSWORD_STORE_CLEARTEXT != 0:
1329 self.message("Store plaintext passwords: on")
1331 self.message("Store plaintext passwords: off")
1332 self.message("Password history length: %d" % pwd_hist_len)
1333 self.message("Minimum password length: %d" % cur_min_pwd_len)
1334 self.message("Minimum password age (days): %d" % cur_min_pwd_age)
1335 self.message("Maximum password age (days): %d" % cur_max_pwd_age)
1336 self.message("Account lockout duration (mins): %d" % cur_account_lockout_duration)
1337 self.message("Account lockout threshold (attempts): %d" % cur_account_lockout_threshold)
1338 self.message("Reset account lockout after (mins): %d" % cur_reset_account_lockout_after)
1340 class cmd_domain_passwordsettings_set(Command):
1341 """Set password settings.
1343 Password complexity, password lockout policy, history length,
1344 minimum password length, the minimum and maximum password age) on
1345 a Samba AD DC server.
1347 Use against a Windows DC is possible, but group policy will override it.
1350 synopsis = "%prog <options> [options]"
1352 takes_optiongroups = {
1353 "sambaopts": options.SambaOptions,
1354 "versionopts": options.VersionOptions,
1355 "credopts": options.CredentialsOptions,
1359 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1360 metavar="URL", dest="H"),
1361 Option("--quiet", help="Be quiet", action="store_true"),
1362 Option("--complexity", type="choice", choices=["on","off","default"],
1363 help="The password complexity (on | off | default). Default is 'on'"),
1364 Option("--store-plaintext", type="choice", choices=["on","off","default"],
1365 help="Store plaintext passwords where account have 'store passwords with reversible encryption' set (on | off | default). Default is 'off'"),
1366 Option("--history-length",
1367 help="The password history length (<integer> | default). Default is 24.", type=str),
1368 Option("--min-pwd-length",
1369 help="The minimum password length (<integer> | default). Default is 7.", type=str),
1370 Option("--min-pwd-age",
1371 help="The minimum password age (<integer in days> | default). Default is 1.", type=str),
1372 Option("--max-pwd-age",
1373 help="The maximum password age (<integer in days> | default). Default is 43.", type=str),
1374 Option("--account-lockout-duration",
1375 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),
1376 Option("--account-lockout-threshold",
1377 help="The number of bad password attempts allowed before locking out the account (<integer> | default). Default is 0 (never lock out).", type=str),
1378 Option("--reset-account-lockout-after",
1379 help="After this time is elapsed, the recorded number of attempts restarts from zero (<integer> | default). Default is 30.", type=str),
1382 def run(self, H=None, min_pwd_age=None, max_pwd_age=None,
1383 quiet=False, complexity=None, store_plaintext=None, history_length=None,
1384 min_pwd_length=None, account_lockout_duration=None, account_lockout_threshold=None,
1385 reset_account_lockout_after=None, credopts=None, sambaopts=None,
1387 lp = sambaopts.get_loadparm()
1388 creds = credopts.get_credentials(lp)
1390 samdb = SamDB(url=H, session_info=system_session(),
1391 credentials=creds, lp=lp)
1393 domain_dn = samdb.domain_dn()
1396 m.dn = ldb.Dn(samdb, domain_dn)
1397 pwd_props = int(samdb.get_pwdProperties())
1399 if complexity is not None:
1400 if complexity == "on" or complexity == "default":
1401 pwd_props = pwd_props | DOMAIN_PASSWORD_COMPLEX
1402 msgs.append("Password complexity activated!")
1403 elif complexity == "off":
1404 pwd_props = pwd_props & (~DOMAIN_PASSWORD_COMPLEX)
1405 msgs.append("Password complexity deactivated!")
1407 if store_plaintext is not None:
1408 if store_plaintext == "on" or store_plaintext == "default":
1409 pwd_props = pwd_props | DOMAIN_PASSWORD_STORE_CLEARTEXT
1410 msgs.append("Plaintext password storage for changed passwords activated!")
1411 elif store_plaintext == "off":
1412 pwd_props = pwd_props & (~DOMAIN_PASSWORD_STORE_CLEARTEXT)
1413 msgs.append("Plaintext password storage for changed passwords deactivated!")
1415 if complexity is not None or store_plaintext is not None:
1416 m["pwdProperties"] = ldb.MessageElement(str(pwd_props),
1417 ldb.FLAG_MOD_REPLACE, "pwdProperties")
1419 if history_length is not None:
1420 if history_length == "default":
1423 pwd_hist_len = int(history_length)
1425 if pwd_hist_len < 0 or pwd_hist_len > 24:
1426 raise CommandError("Password history length must be in the range of 0 to 24!")
1428 m["pwdHistoryLength"] = ldb.MessageElement(str(pwd_hist_len),
1429 ldb.FLAG_MOD_REPLACE, "pwdHistoryLength")
1430 msgs.append("Password history length changed!")
1432 if min_pwd_length is not None:
1433 if min_pwd_length == "default":
1436 min_pwd_len = int(min_pwd_length)
1438 if min_pwd_len < 0 or min_pwd_len > 14:
1439 raise CommandError("Minimum password length must be in the range of 0 to 14!")
1441 m["minPwdLength"] = ldb.MessageElement(str(min_pwd_len),
1442 ldb.FLAG_MOD_REPLACE, "minPwdLength")
1443 msgs.append("Minimum password length changed!")
1445 if min_pwd_age is not None:
1446 if min_pwd_age == "default":
1449 min_pwd_age = int(min_pwd_age)
1451 if min_pwd_age < 0 or min_pwd_age > 998:
1452 raise CommandError("Minimum password age must be in the range of 0 to 998!")
1455 min_pwd_age_ticks = -int(min_pwd_age * (24 * 60 * 60 * 1e7))
1457 m["minPwdAge"] = ldb.MessageElement(str(min_pwd_age_ticks),
1458 ldb.FLAG_MOD_REPLACE, "minPwdAge")
1459 msgs.append("Minimum password age changed!")
1461 if max_pwd_age is not None:
1462 if max_pwd_age == "default":
1465 max_pwd_age = int(max_pwd_age)
1467 if max_pwd_age < 0 or max_pwd_age > 999:
1468 raise CommandError("Maximum password age must be in the range of 0 to 999!")
1471 if max_pwd_age == 0:
1472 max_pwd_age_ticks = -0x8000000000000000
1474 max_pwd_age_ticks = -int(max_pwd_age * (24 * 60 * 60 * 1e7))
1476 m["maxPwdAge"] = ldb.MessageElement(str(max_pwd_age_ticks),
1477 ldb.FLAG_MOD_REPLACE, "maxPwdAge")
1478 msgs.append("Maximum password age changed!")
1480 if account_lockout_duration is not None:
1481 if account_lockout_duration == "default":
1482 account_lockout_duration = 30
1484 account_lockout_duration = int(account_lockout_duration)
1486 if account_lockout_duration < 0 or account_lockout_duration > 99999:
1487 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1490 if account_lockout_duration == 0:
1491 account_lockout_duration_ticks = -0x8000000000000000
1493 account_lockout_duration_ticks = -int(account_lockout_duration * (60 * 1e7))
1495 m["lockoutDuration"] = ldb.MessageElement(str(account_lockout_duration_ticks),
1496 ldb.FLAG_MOD_REPLACE, "lockoutDuration")
1497 msgs.append("Account lockout duration changed!")
1499 if account_lockout_threshold is not None:
1500 if account_lockout_threshold == "default":
1501 account_lockout_threshold = 0
1503 account_lockout_threshold = int(account_lockout_threshold)
1505 m["lockoutThreshold"] = ldb.MessageElement(str(account_lockout_threshold),
1506 ldb.FLAG_MOD_REPLACE, "lockoutThreshold")
1507 msgs.append("Account lockout threshold changed!")
1509 if reset_account_lockout_after is not None:
1510 if reset_account_lockout_after == "default":
1511 reset_account_lockout_after = 30
1513 reset_account_lockout_after = int(reset_account_lockout_after)
1515 if reset_account_lockout_after < 0 or reset_account_lockout_after > 99999:
1516 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1519 if reset_account_lockout_after == 0:
1520 reset_account_lockout_after_ticks = -0x8000000000000000
1522 reset_account_lockout_after_ticks = -int(reset_account_lockout_after * (60 * 1e7))
1524 m["lockOutObservationWindow"] = ldb.MessageElement(str(reset_account_lockout_after_ticks),
1525 ldb.FLAG_MOD_REPLACE, "lockOutObservationWindow")
1526 msgs.append("Duration to reset account lockout after changed!")
1528 if max_pwd_age > 0 and min_pwd_age >= max_pwd_age:
1529 raise CommandError("Maximum password age (%d) must be greater than minimum password age (%d)!" % (max_pwd_age, min_pwd_age))
1532 raise CommandError("You must specify at least one option to set. Try --help")
1534 msgs.append("All changes applied successfully!")
1535 self.message("\n".join(msgs))
1537 class cmd_domain_passwordsettings(SuperCommand):
1538 """Manage password policy settings."""
1541 subcommands["show"] = cmd_domain_passwordsettings_show()
1542 subcommands["set"] = cmd_domain_passwordsettings_set()
1544 class cmd_domain_classicupgrade(Command):
1545 """Upgrade from Samba classic (NT4-like) database to Samba AD DC database.
1547 Specify either a directory with all Samba classic DC databases and state files (with --dbdir) or
1548 the testparm utility from your classic installation (with --testparm).
1551 synopsis = "%prog [options] <classic_smb_conf>"
1553 takes_optiongroups = {
1554 "sambaopts": options.SambaOptions,
1555 "versionopts": options.VersionOptions
1559 Option("--dbdir", type="string", metavar="DIR",
1560 help="Path to samba classic DC database directory"),
1561 Option("--testparm", type="string", metavar="PATH",
1562 help="Path to samba classic DC testparm utility from the previous installation. This allows the default paths of the previous installation to be followed"),
1563 Option("--targetdir", type="string", metavar="DIR",
1564 help="Path prefix where the new Samba 4.0 AD domain should be initialised"),
1565 Option("--quiet", help="Be quiet", action="store_true"),
1566 Option("--verbose", help="Be verbose", action="store_true"),
1567 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
1568 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
1569 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
1570 "BIND9_FLATFILE uses bind9 text database to store zone information, "
1571 "BIND9_DLZ uses samba4 AD to store zone information, "
1572 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
1573 default="SAMBA_INTERNAL")
1577 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
1578 action="store_true"),
1579 Option("--use-xattrs", type="choice", choices=["yes","no","auto"],
1580 metavar="[yes|no|auto]",
1581 help="Define if we should use the native fs capabilities or a tdb file for "
1582 "storing attributes likes ntacl when --use-ntvfs is set. "
1583 "auto tries to make an inteligent guess based on the user rights and system capabilities",
1586 if samba.is_ntvfs_fileserver_built():
1587 takes_options.extend(ntvfs_options)
1589 takes_args = ["smbconf"]
1591 def run(self, smbconf=None, targetdir=None, dbdir=None, testparm=None,
1592 quiet=False, verbose=False, use_xattrs="auto", sambaopts=None, versionopts=None,
1593 dns_backend=None, use_ntvfs=False):
1595 if not os.path.exists(smbconf):
1596 raise CommandError("File %s does not exist" % smbconf)
1598 if testparm and not os.path.exists(testparm):
1599 raise CommandError("Testparm utility %s does not exist" % testparm)
1601 if dbdir and not os.path.exists(dbdir):
1602 raise CommandError("Directory %s does not exist" % dbdir)
1604 if not dbdir and not testparm:
1605 raise CommandError("Please specify either dbdir or testparm")
1607 logger = self.get_logger()
1609 logger.setLevel(logging.DEBUG)
1611 logger.setLevel(logging.WARNING)
1613 logger.setLevel(logging.INFO)
1615 if dbdir and testparm:
1616 logger.warning("both dbdir and testparm specified, ignoring dbdir.")
1619 lp = sambaopts.get_loadparm()
1621 s3conf = s3param.get_context()
1624 s3conf.set("realm", sambaopts.realm)
1626 if targetdir is not None:
1627 if not os.path.isdir(targetdir):
1631 if use_xattrs == "yes":
1633 elif use_xattrs == "auto" and use_ntvfs == False:
1635 elif use_ntvfs == False:
1636 raise CommandError("--use-xattrs=no requires --use-ntvfs (not supported for production use). "
1637 "Please re-run with --use-xattrs omitted.")
1638 elif use_xattrs == "auto" and not s3conf.get("posix:eadb"):
1640 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
1642 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
1645 samba.ntacls.setntacl(lp, tmpfile.name,
1646 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
1649 # FIXME: Don't catch all exceptions here
1650 logger.info("You are not root or your system does not support xattr, using tdb backend for attributes. "
1651 "If you intend to use this provision in production, rerun the script as root on a system supporting xattrs.")
1655 # Set correct default values from dbdir or testparm
1658 paths["state directory"] = dbdir
1659 paths["private dir"] = dbdir
1660 paths["lock directory"] = dbdir
1661 paths["smb passwd file"] = dbdir + "/smbpasswd"
1663 paths["state directory"] = get_testparm_var(testparm, smbconf, "state directory")
1664 paths["private dir"] = get_testparm_var(testparm, smbconf, "private dir")
1665 paths["smb passwd file"] = get_testparm_var(testparm, smbconf, "smb passwd file")
1666 paths["lock directory"] = get_testparm_var(testparm, smbconf, "lock directory")
1667 # "testparm" from Samba 3 < 3.4.x is not aware of the parameter
1668 # "state directory", instead make use of "lock directory"
1669 if len(paths["state directory"]) == 0:
1670 paths["state directory"] = paths["lock directory"]
1673 s3conf.set(p, paths[p])
1675 # load smb.conf parameters
1676 logger.info("Reading smb.conf")
1677 s3conf.load(smbconf)
1678 samba3 = Samba3(smbconf, s3conf)
1680 logger.info("Provisioning")
1681 upgrade_from_samba3(samba3, logger, targetdir, session_info=system_session(),
1682 useeadb=eadb, dns_backend=dns_backend, use_ntvfs=use_ntvfs)
1685 class cmd_domain_samba3upgrade(cmd_domain_classicupgrade):
1686 __doc__ = cmd_domain_classicupgrade.__doc__
1688 # This command is present for backwards compatibility only,
1689 # and should not be shown.
1693 class LocalDCCredentialsOptions(options.CredentialsOptions):
1694 def __init__(self, parser):
1695 options.CredentialsOptions.__init__(self, parser, special_name="local-dc")
1697 class DomainTrustCommand(Command):
1698 """List domain trusts."""
1701 Command.__init__(self)
1702 self.local_lp = None
1704 self.local_server = None
1705 self.local_binding_string = None
1706 self.local_creds = None
1708 self.remote_server = None
1709 self.remote_binding_string = None
1710 self.remote_creds = None
1712 def _uint32(self, v):
1713 return ctypes.c_uint32(v).value
1715 def check_runtime_error(self, runtime, val):
1719 err32 = self._uint32(runtime[0])
1725 class LocalRuntimeError(CommandError):
1726 def __init__(exception_self, self, runtime, message):
1727 err32 = self._uint32(runtime[0])
1729 msg = "LOCAL_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1730 self.local_server, message, err32, errstr)
1731 CommandError.__init__(exception_self, msg)
1733 class RemoteRuntimeError(CommandError):
1734 def __init__(exception_self, self, runtime, message):
1735 err32 = self._uint32(runtime[0])
1737 msg = "REMOTE_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1738 self.remote_server, message, err32, errstr)
1739 CommandError.__init__(exception_self, msg)
1741 class LocalLdbError(CommandError):
1742 def __init__(exception_self, self, ldb_error, message):
1743 errval = ldb_error[0]
1744 errstr = ldb_error[1]
1745 msg = "LOCAL_DC[%s]: %s - ERROR(%d) - %s" % (
1746 self.local_server, message, errval, errstr)
1747 CommandError.__init__(exception_self, msg)
1749 def setup_local_server(self, sambaopts, localdcopts):
1750 if self.local_server is not None:
1751 return self.local_server
1753 lp = sambaopts.get_loadparm()
1755 local_server = localdcopts.ipaddress
1756 if local_server is None:
1757 server_role = lp.server_role()
1758 if server_role != "ROLE_ACTIVE_DIRECTORY_DC":
1759 raise CommandError("Invalid server_role %s" % (server_role))
1760 local_server = lp.get('netbios name')
1761 local_transport = "ncalrpc"
1762 local_binding_options = ""
1763 local_binding_options += ",auth_type=ncalrpc_as_system"
1764 local_ldap_url = None
1767 local_transport = "ncacn_np"
1768 local_binding_options = ""
1769 local_ldap_url = "ldap://%s" % local_server
1770 local_creds = localdcopts.get_credentials(lp)
1774 self.local_server = local_server
1775 self.local_binding_string = "%s:%s[%s]" % (local_transport, local_server, local_binding_options)
1776 self.local_ldap_url = local_ldap_url
1777 self.local_creds = local_creds
1778 return self.local_server
1780 def new_local_lsa_connection(self):
1781 return lsa.lsarpc(self.local_binding_string, self.local_lp, self.local_creds)
1783 def new_local_netlogon_connection(self):
1784 return netlogon.netlogon(self.local_binding_string, self.local_lp, self.local_creds)
1786 def new_local_ldap_connection(self):
1787 return SamDB(url=self.local_ldap_url,
1788 session_info=system_session(),
1789 credentials=self.local_creds,
1792 def setup_remote_server(self, credopts, domain,
1794 require_writable=True):
1797 assert require_writable
1799 if self.remote_server is not None:
1800 return self.remote_server
1802 self.remote_server = "__unknown__remote_server__.%s" % domain
1803 assert self.local_server is not None
1805 remote_creds = credopts.get_credentials(self.local_lp)
1806 remote_server = credopts.ipaddress
1807 remote_binding_options = ""
1809 # TODO: we should also support NT4 domains
1810 # we could use local_netlogon.netr_DsRGetDCNameEx2() with the remote domain name
1811 # and delegate NBT or CLDAP to the local netlogon server
1813 remote_net = Net(remote_creds, self.local_lp, server=remote_server)
1814 remote_flags = nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS
1815 if require_writable:
1816 remote_flags |= nbt.NBT_SERVER_WRITABLE
1818 remote_flags |= nbt.NBT_SERVER_PDC
1819 remote_info = remote_net.finddc(flags=remote_flags, domain=domain, address=remote_server)
1820 except NTSTATUSError as error:
1821 raise CommandError("Failed to find a writeable DC for domain '%s': %s" %
1824 raise CommandError("Failed to find a writeable DC for domain '%s'" % domain)
1826 nbt.NBT_SERVER_PDC: "PDC",
1827 nbt.NBT_SERVER_GC: "GC",
1828 nbt.NBT_SERVER_LDAP: "LDAP",
1829 nbt.NBT_SERVER_DS: "DS",
1830 nbt.NBT_SERVER_KDC: "KDC",
1831 nbt.NBT_SERVER_TIMESERV: "TIMESERV",
1832 nbt.NBT_SERVER_CLOSEST: "CLOSEST",
1833 nbt.NBT_SERVER_WRITABLE: "WRITABLE",
1834 nbt.NBT_SERVER_GOOD_TIMESERV: "GOOD_TIMESERV",
1835 nbt.NBT_SERVER_NDNC: "NDNC",
1836 nbt.NBT_SERVER_SELECT_SECRET_DOMAIN_6: "SELECT_SECRET_DOMAIN_6",
1837 nbt.NBT_SERVER_FULL_SECRET_DOMAIN_6: "FULL_SECRET_DOMAIN_6",
1838 nbt.NBT_SERVER_ADS_WEB_SERVICE: "ADS_WEB_SERVICE",
1839 nbt.NBT_SERVER_DS_8: "DS_8",
1840 nbt.NBT_SERVER_HAS_DNS_NAME: "HAS_DNS_NAME",
1841 nbt.NBT_SERVER_IS_DEFAULT_NC: "IS_DEFAULT_NC",
1842 nbt.NBT_SERVER_FOREST_ROOT: "FOREST_ROOT",
1844 server_type_string = self.generic_bitmap_to_string(flag_map,
1845 remote_info.server_type, names_only=True)
1846 self.outf.write("RemoteDC Netbios[%s] DNS[%s] ServerType[%s]\n" % (
1847 remote_info.pdc_name,
1848 remote_info.pdc_dns_name,
1849 server_type_string))
1851 self.remote_server = remote_info.pdc_dns_name
1852 self.remote_binding_string="ncacn_np:%s[%s]" % (self.remote_server, remote_binding_options)
1853 self.remote_creds = remote_creds
1854 return self.remote_server
1856 def new_remote_lsa_connection(self):
1857 return lsa.lsarpc(self.remote_binding_string, self.local_lp, self.remote_creds)
1859 def new_remote_netlogon_connection(self):
1860 return netlogon.netlogon(self.remote_binding_string, self.local_lp, self.remote_creds)
1862 def get_lsa_info(self, conn, policy_access):
1863 objectAttr = lsa.ObjectAttribute()
1864 objectAttr.sec_qos = lsa.QosInfo()
1866 policy = conn.OpenPolicy2(''.decode('utf-8'),
1867 objectAttr, policy_access)
1869 info = conn.QueryInfoPolicy2(policy, lsa.LSA_POLICY_INFO_DNS)
1871 return (policy, info)
1873 def get_netlogon_dc_info(self, conn, server):
1874 info = conn.netr_DsRGetDCNameEx2(server,
1875 None, 0, None, None, None,
1876 netlogon.DS_RETURN_DNS_NAME)
1879 def netr_DomainTrust_to_name(self, t):
1880 if t.trust_type == lsa.LSA_TRUST_TYPE_DOWNLEVEL:
1881 return t.netbios_name
1885 def netr_DomainTrust_to_type(self, a, t):
1887 primary_parent = None
1889 if _t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
1891 if not _t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1892 primary_parent = a[_t.parent_index]
1895 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1896 if t is primary_parent:
1899 if t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1902 parent = a[t.parent_index]
1903 if parent is primary:
1908 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1913 def netr_DomainTrust_to_transitive(self, t):
1914 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1917 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE:
1920 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1925 def netr_DomainTrust_to_direction(self, t):
1926 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND and \
1927 t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1930 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND:
1933 if t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1938 def generic_enum_to_string(self, e_dict, v, names_only=False):
1942 v32 = self._uint32(v)
1943 w = "__unknown__%08X__" % v32
1945 r = "0x%x (%s)" % (v, w)
1948 def generic_bitmap_to_string(self, b_dict, v, names_only=False):
1953 for b in sorted(b_dict.keys()):
1960 c32 = self._uint32(c)
1961 s += ["__unknown_%08X__" % c32]
1966 r = "0x%x (%s)" % (v, w)
1969 def trustType_string(self, v):
1971 lsa.LSA_TRUST_TYPE_DOWNLEVEL : "DOWNLEVEL",
1972 lsa.LSA_TRUST_TYPE_UPLEVEL : "UPLEVEL",
1973 lsa.LSA_TRUST_TYPE_MIT : "MIT",
1974 lsa.LSA_TRUST_TYPE_DCE : "DCE",
1976 return self.generic_enum_to_string(types, v)
1978 def trustDirection_string(self, v):
1980 lsa.LSA_TRUST_DIRECTION_INBOUND |
1981 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "BOTH",
1982 lsa.LSA_TRUST_DIRECTION_INBOUND : "INBOUND",
1983 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "OUTBOUND",
1985 return self.generic_enum_to_string(directions, v)
1987 def trustAttributes_string(self, v):
1989 lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE : "NON_TRANSITIVE",
1990 lsa.LSA_TRUST_ATTRIBUTE_UPLEVEL_ONLY : "UPLEVEL_ONLY",
1991 lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN : "QUARANTINED_DOMAIN",
1992 lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE : "FOREST_TRANSITIVE",
1993 lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION : "CROSS_ORGANIZATION",
1994 lsa.LSA_TRUST_ATTRIBUTE_WITHIN_FOREST : "WITHIN_FOREST",
1995 lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL : "TREAT_AS_EXTERNAL",
1996 lsa.LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION : "USES_RC4_ENCRYPTION",
1998 return self.generic_bitmap_to_string(attributes, v)
2000 def kerb_EncTypes_string(self, v):
2002 security.KERB_ENCTYPE_DES_CBC_CRC : "DES_CBC_CRC",
2003 security.KERB_ENCTYPE_DES_CBC_MD5 : "DES_CBC_MD5",
2004 security.KERB_ENCTYPE_RC4_HMAC_MD5 : "RC4_HMAC_MD5",
2005 security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96 : "AES128_CTS_HMAC_SHA1_96",
2006 security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96 : "AES256_CTS_HMAC_SHA1_96",
2007 security.KERB_ENCTYPE_FAST_SUPPORTED : "FAST_SUPPORTED",
2008 security.KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED : "COMPOUND_IDENTITY_SUPPORTED",
2009 security.KERB_ENCTYPE_CLAIMS_SUPPORTED : "CLAIMS_SUPPORTED",
2010 security.KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED : "RESOURCE_SID_COMPRESSION_DISABLED",
2012 return self.generic_bitmap_to_string(enctypes, v)
2014 def entry_tln_status(self, e_flags, ):
2016 return "Status[Enabled]"
2019 lsa.LSA_TLN_DISABLED_NEW : "Disabled-New",
2020 lsa.LSA_TLN_DISABLED_ADMIN : "Disabled",
2021 lsa.LSA_TLN_DISABLED_CONFLICT : "Disabled-Conflicting",
2023 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
2025 def entry_dom_status(self, e_flags):
2027 return "Status[Enabled]"
2030 lsa.LSA_SID_DISABLED_ADMIN : "Disabled-SID",
2031 lsa.LSA_SID_DISABLED_CONFLICT : "Disabled-SID-Conflicting",
2032 lsa.LSA_NB_DISABLED_ADMIN : "Disabled-NB",
2033 lsa.LSA_NB_DISABLED_CONFLICT : "Disabled-NB-Conflicting",
2035 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
2037 def write_forest_trust_info(self, fti, tln=None, collisions=None):
2039 tln_string = " TDO[%s]" % tln
2043 self.outf.write("Namespaces[%d]%s:\n" % (
2044 len(fti.entries), tln_string))
2046 for i in xrange(0, len(fti.entries)):
2050 collision_string = ""
2052 if collisions is not None:
2053 for c in collisions.entries:
2057 collision_string = " Collision[%s]" % (c.name.string)
2059 d = e.forest_trust_data
2060 if e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
2061 self.outf.write("TLN: %-32s DNS[*.%s]%s\n" % (
2062 self.entry_tln_status(flags),
2063 d.string, collision_string))
2064 elif e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
2065 self.outf.write("TLN_EX: %-29s DNS[*.%s]\n" % (
2067 elif e.type == lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
2068 self.outf.write("DOM: %-32s DNS[%s] Netbios[%s] SID[%s]%s\n" % (
2069 self.entry_dom_status(flags),
2070 d.dns_domain_name.string,
2071 d.netbios_domain_name.string,
2072 d.domain_sid, collision_string))
2075 class cmd_domain_trust_list(DomainTrustCommand):
2076 """List domain trusts."""
2078 synopsis = "%prog [options]"
2080 takes_optiongroups = {
2081 "sambaopts": options.SambaOptions,
2082 "versionopts": options.VersionOptions,
2083 "localdcopts": LocalDCCredentialsOptions,
2089 def run(self, sambaopts=None, versionopts=None, localdcopts=None):
2091 local_server = self.setup_local_server(sambaopts, localdcopts)
2093 local_netlogon = self.new_local_netlogon_connection()
2094 except RuntimeError as error:
2095 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2098 local_netlogon_trusts = local_netlogon.netr_DsrEnumerateDomainTrusts(local_server,
2099 netlogon.NETR_TRUST_FLAG_IN_FOREST |
2100 netlogon.NETR_TRUST_FLAG_OUTBOUND |
2101 netlogon.NETR_TRUST_FLAG_INBOUND)
2102 except RuntimeError as error:
2103 if self.check_runtime_error(error, werror.WERR_RPC_S_PROCNUM_OUT_OF_RANGE):
2104 # TODO: we could implement a fallback to lsa.EnumTrustDom()
2105 raise CommandError("LOCAL_DC[%s]: netr_DsrEnumerateDomainTrusts not supported." % (
2107 raise self.LocalRuntimeError(self, error, "netr_DsrEnumerateDomainTrusts failed")
2109 a = local_netlogon_trusts.array
2111 if t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
2113 self.outf.write("%-14s %-15s %-19s %s\n" % (
2114 "Type[%s]" % self.netr_DomainTrust_to_type(a, t),
2115 "Transitive[%s]" % self.netr_DomainTrust_to_transitive(t),
2116 "Direction[%s]" % self.netr_DomainTrust_to_direction(t),
2117 "Name[%s]" % self.netr_DomainTrust_to_name(t)))
2120 class cmd_domain_trust_show(DomainTrustCommand):
2121 """Show trusted domain details."""
2123 synopsis = "%prog NAME [options]"
2125 takes_optiongroups = {
2126 "sambaopts": options.SambaOptions,
2127 "versionopts": options.VersionOptions,
2128 "localdcopts": LocalDCCredentialsOptions,
2134 takes_args = ["domain"]
2136 def run(self, domain, sambaopts=None, versionopts=None, localdcopts=None):
2138 local_server = self.setup_local_server(sambaopts, localdcopts)
2140 local_lsa = self.new_local_lsa_connection()
2141 except RuntimeError as error:
2142 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2145 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2146 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2147 except RuntimeError as error:
2148 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2150 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2151 local_lsa_info.name.string,
2152 local_lsa_info.dns_domain.string,
2153 local_lsa_info.sid))
2155 lsaString = lsa.String()
2156 lsaString.string = domain
2158 local_tdo_full = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2159 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2160 local_tdo_info = local_tdo_full.info_ex
2161 local_tdo_posix = local_tdo_full.posix_offset
2162 except NTSTATUSError as error:
2163 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2164 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2166 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(FULL_INFO) failed")
2169 local_tdo_enctypes = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2170 lsaString, lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES)
2171 except NTSTATUSError as error:
2172 if self.check_runtime_error(error, ntstatus.NT_STATUS_INVALID_PARAMETER):
2174 if self.check_runtime_error(error, ntstatus.NT_STATUS_INVALID_INFO_CLASS):
2177 if error is not None:
2178 raise self.LocalRuntimeError(self, error,
2179 "QueryTrustedDomainInfoByName(SUPPORTED_ENCRYPTION_TYPES) failed")
2181 local_tdo_enctypes = lsa.TrustDomainInfoSupportedEncTypes()
2182 local_tdo_enctypes.enc_types = 0
2185 local_tdo_forest = None
2186 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2187 local_tdo_forest = local_lsa.lsaRQueryForestTrustInformation(local_policy,
2188 lsaString, lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
2189 except RuntimeError as error:
2190 if self.check_runtime_error(error, ntstatus.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
2192 if self.check_runtime_error(error, ntstatus.NT_STATUS_NOT_FOUND):
2194 if error is not None:
2195 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation failed")
2197 local_tdo_forest = lsa.ForestTrustInformation()
2198 local_tdo_forest.count = 0
2199 local_tdo_forest.entries = []
2201 self.outf.write("TrustedDomain:\n\n");
2202 self.outf.write("NetbiosName: %s\n" % local_tdo_info.netbios_name.string)
2203 if local_tdo_info.netbios_name.string != local_tdo_info.domain_name.string:
2204 self.outf.write("DnsName: %s\n" % local_tdo_info.domain_name.string)
2205 self.outf.write("SID: %s\n" % local_tdo_info.sid)
2206 self.outf.write("Type: %s\n" % self.trustType_string(local_tdo_info.trust_type))
2207 self.outf.write("Direction: %s\n" % self.trustDirection_string(local_tdo_info.trust_direction))
2208 self.outf.write("Attributes: %s\n" % self.trustAttributes_string(local_tdo_info.trust_attributes))
2209 posix_offset_u32 = ctypes.c_uint32(local_tdo_posix.posix_offset).value
2210 posix_offset_i32 = ctypes.c_int32(local_tdo_posix.posix_offset).value
2211 self.outf.write("PosixOffset: 0x%08X (%d)\n" % (posix_offset_u32, posix_offset_i32))
2212 self.outf.write("kerb_EncTypes: %s\n" % self.kerb_EncTypes_string(local_tdo_enctypes.enc_types))
2214 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2215 self.write_forest_trust_info(local_tdo_forest,
2216 tln=local_tdo_info.domain_name.string)
2220 class cmd_domain_trust_create(DomainTrustCommand):
2221 """Create a domain or forest trust."""
2223 synopsis = "%prog DOMAIN [options]"
2225 takes_optiongroups = {
2226 "sambaopts": options.SambaOptions,
2227 "versionopts": options.VersionOptions,
2228 "credopts": options.CredentialsOptions,
2229 "localdcopts": LocalDCCredentialsOptions,
2233 Option("--type", type="choice", metavar="TYPE",
2234 choices=["external", "forest"],
2235 help="The type of the trust: 'external' or 'forest'.",
2237 default="external"),
2238 Option("--direction", type="choice", metavar="DIRECTION",
2239 choices=["incoming", "outgoing", "both"],
2240 help="The trust direction: 'incoming', 'outgoing' or 'both'.",
2241 dest='trust_direction',
2243 Option("--create-location", type="choice", metavar="LOCATION",
2244 choices=["local", "both"],
2245 help="Where to create the trusted domain object: 'local' or 'both'.",
2246 dest='create_location',
2248 Option("--cross-organisation", action="store_true",
2249 help="The related domains does not belong to the same organisation.",
2250 dest='cross_organisation',
2252 Option("--quarantined", type="choice", metavar="yes|no",
2253 choices=["yes", "no", None],
2254 help="Special SID filtering rules are applied to the trust. "
2255 "With --type=external the default is yes. "
2256 "With --type=forest the default is no.",
2257 dest='quarantined_arg',
2259 Option("--not-transitive", action="store_true",
2260 help="The forest trust is not transitive.",
2261 dest='not_transitive',
2263 Option("--treat-as-external", action="store_true",
2264 help="The treat the forest trust as external.",
2265 dest='treat_as_external',
2267 Option("--no-aes-keys", action="store_false",
2268 help="The trust uses aes kerberos keys.",
2269 dest='use_aes_keys',
2271 Option("--skip-validation", action="store_false",
2272 help="Skip validation of the trust.",
2277 takes_args = ["domain"]
2279 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2280 trust_type=None, trust_direction=None, create_location=None,
2281 cross_organisation=False, quarantined_arg=None,
2282 not_transitive=False, treat_as_external=False,
2283 use_aes_keys=False, validate=True):
2285 lsaString = lsa.String()
2288 if quarantined_arg is None:
2289 if trust_type == 'external':
2291 elif quarantined_arg == 'yes':
2294 if trust_type != 'forest':
2296 raise CommandError("--not-transitive requires --type=forest")
2297 if treat_as_external:
2298 raise CommandError("--treat-as-external requires --type=forest")
2302 enc_types = lsa.TrustDomainInfoSupportedEncTypes()
2303 enc_types.enc_types = security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96
2304 enc_types.enc_types |= security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96
2306 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2307 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2308 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2310 local_trust_info = lsa.TrustDomainInfoInfoEx()
2311 local_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2312 local_trust_info.trust_direction = 0
2313 if trust_direction == "both":
2314 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2315 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2316 elif trust_direction == "incoming":
2317 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2318 elif trust_direction == "outgoing":
2319 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2320 local_trust_info.trust_attributes = 0
2321 if cross_organisation:
2322 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2324 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2325 if trust_type == "forest":
2326 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2328 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2329 if treat_as_external:
2330 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2332 def get_password(name):
2335 if password is not None and password is not '':
2337 password = getpass("New %s Password: " % name)
2338 passwordverify = getpass("Retype %s Password: " % name)
2339 if not password == passwordverify:
2341 self.outf.write("Sorry, passwords do not match.\n")
2343 incoming_secret = None
2344 outgoing_secret = None
2345 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2346 if create_location == "local":
2347 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2348 incoming_password = get_password("Incoming Trust")
2349 incoming_secret = string_to_byte_array(incoming_password.encode('utf-16-le'))
2350 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2351 outgoing_password = get_password("Outgoing Trust")
2352 outgoing_secret = string_to_byte_array(outgoing_password.encode('utf-16-le'))
2354 remote_trust_info = None
2356 # We use 240 random bytes.
2357 # Windows uses 28 or 240 random bytes. I guess it's
2358 # based on the trust type external vs. forest.
2360 # The initial trust password can be up to 512 bytes
2361 # while the versioned passwords used for periodic updates
2362 # can only be up to 498 bytes, as netr_ServerPasswordSet2()
2363 # needs to pass the NL_PASSWORD_VERSION structure within the
2364 # 512 bytes and a 2 bytes confounder is required.
2366 def random_trust_secret(length):
2367 pw = samba.generate_random_machine_password(length//2, length//2)
2368 return string_to_byte_array(pw.encode('utf-16-le'))
2370 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2371 incoming_secret = random_trust_secret(240)
2372 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2373 outgoing_secret = random_trust_secret(240)
2375 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2376 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2378 remote_trust_info = lsa.TrustDomainInfoInfoEx()
2379 remote_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2380 remote_trust_info.trust_direction = 0
2381 if trust_direction == "both":
2382 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2383 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2384 elif trust_direction == "incoming":
2385 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2386 elif trust_direction == "outgoing":
2387 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2388 remote_trust_info.trust_attributes = 0
2389 if cross_organisation:
2390 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2392 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2393 if trust_type == "forest":
2394 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2396 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2397 if treat_as_external:
2398 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2400 local_server = self.setup_local_server(sambaopts, localdcopts)
2402 local_lsa = self.new_local_lsa_connection()
2403 except RuntimeError as error:
2404 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2407 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2408 except RuntimeError as error:
2409 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2411 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2412 local_lsa_info.name.string,
2413 local_lsa_info.dns_domain.string,
2414 local_lsa_info.sid))
2417 remote_server = self.setup_remote_server(credopts, domain)
2418 except RuntimeError as error:
2419 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2422 remote_lsa = self.new_remote_lsa_connection()
2423 except RuntimeError as error:
2424 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2427 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2428 except RuntimeError as error:
2429 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2431 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2432 remote_lsa_info.name.string,
2433 remote_lsa_info.dns_domain.string,
2434 remote_lsa_info.sid))
2436 local_trust_info.domain_name.string = remote_lsa_info.dns_domain.string
2437 local_trust_info.netbios_name.string = remote_lsa_info.name.string
2438 local_trust_info.sid = remote_lsa_info.sid
2440 if remote_trust_info:
2441 remote_trust_info.domain_name.string = local_lsa_info.dns_domain.string
2442 remote_trust_info.netbios_name.string = local_lsa_info.name.string
2443 remote_trust_info.sid = local_lsa_info.sid
2446 lsaString.string = local_trust_info.domain_name.string
2447 local_old_netbios = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2448 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2449 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2450 except NTSTATUSError as error:
2451 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2452 raise self.LocalRuntimeError(self, error,
2453 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2457 lsaString.string = local_trust_info.netbios_name.string
2458 local_old_dns = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2459 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2460 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2461 except NTSTATUSError as error:
2462 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2463 raise self.LocalRuntimeError(self, error,
2464 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2467 if remote_trust_info:
2469 lsaString.string = remote_trust_info.domain_name.string
2470 remote_old_netbios = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2471 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2472 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2473 except NTSTATUSError as error:
2474 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2475 raise self.RemoteRuntimeError(self, error,
2476 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2480 lsaString.string = remote_trust_info.netbios_name.string
2481 remote_old_dns = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2482 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2483 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2484 except NTSTATUSError as error:
2485 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2486 raise self.RemoteRuntimeError(self, error,
2487 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2491 local_netlogon = self.new_local_netlogon_connection()
2492 except RuntimeError as error:
2493 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2496 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
2497 except RuntimeError as error:
2498 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
2500 if remote_trust_info:
2502 remote_netlogon = self.new_remote_netlogon_connection()
2503 except RuntimeError as error:
2504 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2507 remote_netlogon_info = self.get_netlogon_dc_info(remote_netlogon, remote_server)
2508 except RuntimeError as error:
2509 raise self.RemoteRuntimeError(self, error, "failed to get netlogon dc info")
2511 def generate_AuthInOutBlob(secret, update_time):
2513 blob = drsblobs.trustAuthInOutBlob()
2518 clear = drsblobs.AuthInfoClear()
2519 clear.size = len(secret)
2520 clear.password = secret
2522 info = drsblobs.AuthenticationInformation()
2523 info.LastUpdateTime = samba.unix2nttime(update_time)
2524 info.AuthType = lsa.TRUST_AUTH_TYPE_CLEAR
2525 info.AuthInfo = clear
2527 array = drsblobs.AuthenticationInformationArray()
2529 array.array = [info]
2531 blob = drsblobs.trustAuthInOutBlob()
2533 blob.current = array
2537 def generate_AuthInfoInternal(session_key, incoming=None, outgoing=None):
2538 confounder = [0] * 512
2539 for i in range(len(confounder)):
2540 confounder[i] = random.randint(0, 255)
2542 trustpass = drsblobs.trustDomainPasswords()
2544 trustpass.confounder = confounder
2545 trustpass.outgoing = outgoing
2546 trustpass.incoming = incoming
2548 trustpass_blob = ndr_pack(trustpass)
2550 encrypted_trustpass = arcfour_encrypt(session_key, trustpass_blob)
2552 auth_blob = lsa.DATA_BUF2()
2553 auth_blob.size = len(encrypted_trustpass)
2554 auth_blob.data = string_to_byte_array(encrypted_trustpass)
2556 auth_info = lsa.TrustDomainInfoAuthInfoInternal()
2557 auth_info.auth_blob = auth_blob
2561 update_time = samba.current_unix_time()
2562 incoming_blob = generate_AuthInOutBlob(incoming_secret, update_time)
2563 outgoing_blob = generate_AuthInOutBlob(outgoing_secret, update_time)
2565 local_tdo_handle = None
2566 remote_tdo_handle = None
2568 local_auth_info = generate_AuthInfoInternal(local_lsa.session_key,
2569 incoming=incoming_blob,
2570 outgoing=outgoing_blob)
2571 if remote_trust_info:
2572 remote_auth_info = generate_AuthInfoInternal(remote_lsa.session_key,
2573 incoming=outgoing_blob,
2574 outgoing=incoming_blob)
2577 if remote_trust_info:
2578 self.outf.write("Creating remote TDO.\n")
2579 current_request = { "location": "remote", "name": "CreateTrustedDomainEx2"}
2580 remote_tdo_handle = remote_lsa.CreateTrustedDomainEx2(remote_policy,
2583 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2584 self.outf.write("Remote TDO created.\n")
2586 self.outf.write("Setting supported encryption types on remote TDO.\n")
2587 current_request = { "location": "remote", "name": "SetInformationTrustedDomain"}
2588 remote_lsa.SetInformationTrustedDomain(remote_tdo_handle,
2589 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2592 self.outf.write("Creating local TDO.\n")
2593 current_request = { "location": "local", "name": "CreateTrustedDomainEx2"}
2594 local_tdo_handle = local_lsa.CreateTrustedDomainEx2(local_policy,
2597 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2598 self.outf.write("Local TDO created\n")
2600 self.outf.write("Setting supported encryption types on local TDO.\n")
2601 current_request = { "location": "local", "name": "SetInformationTrustedDomain"}
2602 local_lsa.SetInformationTrustedDomain(local_tdo_handle,
2603 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2605 except RuntimeError as error:
2606 self.outf.write("Error: %s failed %sly - cleaning up\n" % (
2607 current_request['name'], current_request['location']))
2608 if remote_tdo_handle:
2609 self.outf.write("Deleting remote TDO.\n")
2610 remote_lsa.DeleteObject(remote_tdo_handle)
2611 remote_tdo_handle = None
2612 if local_tdo_handle:
2613 self.outf.write("Deleting local TDO.\n")
2614 local_lsa.DeleteObject(local_tdo_handle)
2615 local_tdo_handle = None
2616 if current_request['location'] is "remote":
2617 raise self.RemoteRuntimeError(self, error, "%s" % (
2618 current_request['name']))
2619 raise self.LocalRuntimeError(self, error, "%s" % (
2620 current_request['name']))
2623 if local_trust_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2624 self.outf.write("Setup local forest trust information...\n")
2626 # get all information about the remote trust
2627 # this triggers netr_GetForestTrustInformation to the remote domain
2628 # and lsaRSetForestTrustInformation() locally, but new top level
2629 # names are disabled by default.
2630 local_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
2631 remote_lsa_info.dns_domain.string,
2632 netlogon.DS_GFTI_UPDATE_TDO)
2633 except RuntimeError as error:
2634 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2637 # here we try to enable all top level names
2638 local_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
2639 remote_lsa_info.dns_domain,
2640 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2643 except RuntimeError as error:
2644 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2646 self.write_forest_trust_info(local_forest_info,
2647 tln=remote_lsa_info.dns_domain.string,
2648 collisions=local_forest_collision)
2650 if remote_trust_info:
2651 self.outf.write("Setup remote forest trust information...\n")
2653 # get all information about the local trust (from the perspective of the remote domain)
2654 # this triggers netr_GetForestTrustInformation to our domain.
2655 # and lsaRSetForestTrustInformation() remotely, but new top level
2656 # names are disabled by default.
2657 remote_forest_info = remote_netlogon.netr_DsRGetForestTrustInformation(remote_netlogon_info.dc_unc,
2658 local_lsa_info.dns_domain.string,
2659 netlogon.DS_GFTI_UPDATE_TDO)
2660 except RuntimeError as error:
2661 raise self.RemoteRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2664 # here we try to enable all top level names
2665 remote_forest_collision = remote_lsa.lsaRSetForestTrustInformation(remote_policy,
2666 local_lsa_info.dns_domain,
2667 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2670 except RuntimeError as error:
2671 raise self.RemoteRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2673 self.write_forest_trust_info(remote_forest_info,
2674 tln=local_lsa_info.dns_domain.string,
2675 collisions=remote_forest_collision)
2677 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2678 self.outf.write("Validating outgoing trust...\n")
2680 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_netlogon_info.dc_unc,
2681 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2683 remote_lsa_info.dns_domain.string)
2684 except RuntimeError as error:
2685 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2687 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2688 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2690 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2691 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2692 local_trust_verify.trusted_dc_name,
2693 local_trust_verify.tc_connection_status[1],
2694 local_trust_verify.pdc_connection_status[1])
2696 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2697 local_trust_verify.trusted_dc_name,
2698 local_trust_verify.tc_connection_status[1],
2699 local_trust_verify.pdc_connection_status[1])
2701 if local_trust_status != werror.WERR_SUCCESS or local_conn_status != werror.WERR_SUCCESS:
2702 raise CommandError(local_validation)
2704 self.outf.write("OK: %s\n" % local_validation)
2706 if remote_trust_info:
2707 if remote_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2708 self.outf.write("Validating incoming trust...\n")
2710 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_netlogon_info.dc_unc,
2711 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2713 local_lsa_info.dns_domain.string)
2714 except RuntimeError as error:
2715 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2717 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2718 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2720 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2721 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2722 remote_trust_verify.trusted_dc_name,
2723 remote_trust_verify.tc_connection_status[1],
2724 remote_trust_verify.pdc_connection_status[1])
2726 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2727 remote_trust_verify.trusted_dc_name,
2728 remote_trust_verify.tc_connection_status[1],
2729 remote_trust_verify.pdc_connection_status[1])
2731 if remote_trust_status != werror.WERR_SUCCESS or remote_conn_status != werror.WERR_SUCCESS:
2732 raise CommandError(remote_validation)
2734 self.outf.write("OK: %s\n" % remote_validation)
2736 if remote_tdo_handle is not None:
2738 remote_lsa.Close(remote_tdo_handle)
2739 except RuntimeError as error:
2741 remote_tdo_handle = None
2742 if local_tdo_handle is not None:
2744 local_lsa.Close(local_tdo_handle)
2745 except RuntimeError as error:
2747 local_tdo_handle = None
2749 self.outf.write("Success.\n")
2752 class cmd_domain_trust_delete(DomainTrustCommand):
2753 """Delete a domain trust."""
2755 synopsis = "%prog DOMAIN [options]"
2757 takes_optiongroups = {
2758 "sambaopts": options.SambaOptions,
2759 "versionopts": options.VersionOptions,
2760 "credopts": options.CredentialsOptions,
2761 "localdcopts": LocalDCCredentialsOptions,
2765 Option("--delete-location", type="choice", metavar="LOCATION",
2766 choices=["local", "both"],
2767 help="Where to delete the trusted domain object: 'local' or 'both'.",
2768 dest='delete_location',
2772 takes_args = ["domain"]
2774 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2775 delete_location=None):
2777 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2778 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2779 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2781 if delete_location == "local":
2782 remote_policy_access = None
2784 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2785 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2786 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2788 local_server = self.setup_local_server(sambaopts, localdcopts)
2790 local_lsa = self.new_local_lsa_connection()
2791 except RuntimeError as error:
2792 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2795 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2796 except RuntimeError as error:
2797 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2799 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2800 local_lsa_info.name.string,
2801 local_lsa_info.dns_domain.string,
2802 local_lsa_info.sid))
2804 local_tdo_info = None
2805 local_tdo_handle = None
2806 remote_tdo_info = None
2807 remote_tdo_handle = None
2809 lsaString = lsa.String()
2811 lsaString.string = domain
2812 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2813 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2814 except NTSTATUSError as error:
2815 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2816 raise CommandError("Failed to find trust for domain '%s'" % domain)
2817 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2820 if remote_policy_access is not None:
2822 remote_server = self.setup_remote_server(credopts, domain)
2823 except RuntimeError as error:
2824 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2827 remote_lsa = self.new_remote_lsa_connection()
2828 except RuntimeError as error:
2829 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2832 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2833 except RuntimeError as error:
2834 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2836 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2837 remote_lsa_info.name.string,
2838 remote_lsa_info.dns_domain.string,
2839 remote_lsa_info.sid))
2841 if remote_lsa_info.sid != local_tdo_info.sid or \
2842 remote_lsa_info.name.string != local_tdo_info.netbios_name.string or \
2843 remote_lsa_info.dns_domain.string != local_tdo_info.domain_name.string:
2844 raise CommandError("LocalTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2845 local_tdo_info.netbios_name.string,
2846 local_tdo_info.domain_name.string,
2847 local_tdo_info.sid))
2850 lsaString.string = local_lsa_info.dns_domain.string
2851 remote_tdo_info = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2852 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2853 except NTSTATUSError as error:
2854 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2855 raise self.RemoteRuntimeError(self, error, "QueryTrustedDomainInfoByName(%s)" % (
2859 if remote_tdo_info is not None:
2860 if local_lsa_info.sid != remote_tdo_info.sid or \
2861 local_lsa_info.name.string != remote_tdo_info.netbios_name.string or \
2862 local_lsa_info.dns_domain.string != remote_tdo_info.domain_name.string:
2863 raise CommandError("RemoteTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2864 remote_tdo_info.netbios_name.string,
2865 remote_tdo_info.domain_name.string,
2866 remote_tdo_info.sid))
2868 if local_tdo_info is not None:
2870 lsaString.string = local_tdo_info.domain_name.string
2871 local_tdo_handle = local_lsa.OpenTrustedDomainByName(local_policy,
2873 security.SEC_STD_DELETE)
2874 except RuntimeError as error:
2875 raise self.LocalRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2878 local_lsa.DeleteObject(local_tdo_handle)
2879 local_tdo_handle = None
2881 if remote_tdo_info is not None:
2883 lsaString.string = remote_tdo_info.domain_name.string
2884 remote_tdo_handle = remote_lsa.OpenTrustedDomainByName(remote_policy,
2886 security.SEC_STD_DELETE)
2887 except RuntimeError as error:
2888 raise self.RemoteRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2891 if remote_tdo_handle is not None:
2893 remote_lsa.DeleteObject(remote_tdo_handle)
2894 remote_tdo_handle = None
2895 self.outf.write("RemoteTDO deleted.\n")
2896 except RuntimeError as error:
2897 self.outf.write("%s\n" % self.RemoteRuntimeError(self, error, "DeleteObject() failed"))
2899 if local_tdo_handle is not None:
2901 local_lsa.DeleteObject(local_tdo_handle)
2902 local_tdo_handle = None
2903 self.outf.write("LocalTDO deleted.\n")
2904 except RuntimeError as error:
2905 self.outf.write("%s\n" % self.LocalRuntimeError(self, error, "DeleteObject() failed"))
2909 class cmd_domain_trust_validate(DomainTrustCommand):
2910 """Validate a domain trust."""
2912 synopsis = "%prog DOMAIN [options]"
2914 takes_optiongroups = {
2915 "sambaopts": options.SambaOptions,
2916 "versionopts": options.VersionOptions,
2917 "credopts": options.CredentialsOptions,
2918 "localdcopts": LocalDCCredentialsOptions,
2922 Option("--validate-location", type="choice", metavar="LOCATION",
2923 choices=["local", "both"],
2924 help="Where to validate the trusted domain object: 'local' or 'both'.",
2925 dest='validate_location',
2929 takes_args = ["domain"]
2931 def run(self, domain, sambaopts=None, versionopts=None, credopts=None, localdcopts=None,
2932 validate_location=None):
2934 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2936 local_server = self.setup_local_server(sambaopts, localdcopts)
2938 local_lsa = self.new_local_lsa_connection()
2939 except RuntimeError as error:
2940 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2943 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2944 except RuntimeError as error:
2945 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2947 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2948 local_lsa_info.name.string,
2949 local_lsa_info.dns_domain.string,
2950 local_lsa_info.sid))
2953 lsaString = lsa.String()
2954 lsaString.string = domain
2955 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2956 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2957 except NTSTATUSError as error:
2958 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2959 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2961 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
2963 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
2964 local_tdo_info.netbios_name.string,
2965 local_tdo_info.domain_name.string,
2966 local_tdo_info.sid))
2969 local_netlogon = self.new_local_netlogon_connection()
2970 except RuntimeError as error:
2971 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2974 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_server,
2975 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2977 local_tdo_info.domain_name.string)
2978 except RuntimeError as error:
2979 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2981 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2982 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2984 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2985 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2986 local_trust_verify.trusted_dc_name,
2987 local_trust_verify.tc_connection_status[1],
2988 local_trust_verify.pdc_connection_status[1])
2990 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2991 local_trust_verify.trusted_dc_name,
2992 local_trust_verify.tc_connection_status[1],
2993 local_trust_verify.pdc_connection_status[1])
2995 if local_trust_status != werror.WERR_SUCCESS or local_conn_status != werror.WERR_SUCCESS:
2996 raise CommandError(local_validation)
2998 self.outf.write("OK: %s\n" % local_validation)
3001 server = local_trust_verify.trusted_dc_name.replace('\\', '')
3002 domain_and_server = "%s\\%s" % (local_tdo_info.domain_name.string, server)
3003 local_trust_rediscover = local_netlogon.netr_LogonControl2Ex(local_server,
3004 netlogon.NETLOGON_CONTROL_REDISCOVER,
3007 except RuntimeError as error:
3008 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
3010 local_conn_status = self._uint32(local_trust_rediscover.tc_connection_status[0])
3011 local_rediscover = "LocalRediscover: DC[%s] CONNECTION[%s]" % (
3012 local_trust_rediscover.trusted_dc_name,
3013 local_trust_rediscover.tc_connection_status[1])
3015 if local_conn_status != werror.WERR_SUCCESS:
3016 raise CommandError(local_rediscover)
3018 self.outf.write("OK: %s\n" % local_rediscover)
3020 if validate_location != "local":
3022 remote_server = self.setup_remote_server(credopts, domain, require_pdc=False)
3023 except RuntimeError as error:
3024 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
3027 remote_netlogon = self.new_remote_netlogon_connection()
3028 except RuntimeError as error:
3029 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
3032 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_server,
3033 netlogon.NETLOGON_CONTROL_TC_VERIFY,
3035 local_lsa_info.dns_domain.string)
3036 except RuntimeError as error:
3037 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
3039 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
3040 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
3042 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
3043 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
3044 remote_trust_verify.trusted_dc_name,
3045 remote_trust_verify.tc_connection_status[1],
3046 remote_trust_verify.pdc_connection_status[1])
3048 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
3049 remote_trust_verify.trusted_dc_name,
3050 remote_trust_verify.tc_connection_status[1],
3051 remote_trust_verify.pdc_connection_status[1])
3053 if remote_trust_status != werror.WERR_SUCCESS or remote_conn_status != werror.WERR_SUCCESS:
3054 raise CommandError(remote_validation)
3056 self.outf.write("OK: %s\n" % remote_validation)
3059 server = remote_trust_verify.trusted_dc_name.replace('\\', '')
3060 domain_and_server = "%s\\%s" % (local_lsa_info.dns_domain.string, server)
3061 remote_trust_rediscover = remote_netlogon.netr_LogonControl2Ex(remote_server,
3062 netlogon.NETLOGON_CONTROL_REDISCOVER,
3065 except RuntimeError as error:
3066 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
3068 remote_conn_status = self._uint32(remote_trust_rediscover.tc_connection_status[0])
3070 remote_rediscover = "RemoteRediscover: DC[%s] CONNECTION[%s]" % (
3071 remote_trust_rediscover.trusted_dc_name,
3072 remote_trust_rediscover.tc_connection_status[1])
3074 if remote_conn_status != werror.WERR_SUCCESS:
3075 raise CommandError(remote_rediscover)
3077 self.outf.write("OK: %s\n" % remote_rediscover)
3081 class cmd_domain_trust_namespaces(DomainTrustCommand):
3082 """Manage forest trust namespaces."""
3084 synopsis = "%prog [DOMAIN] [options]"
3086 takes_optiongroups = {
3087 "sambaopts": options.SambaOptions,
3088 "versionopts": options.VersionOptions,
3089 "localdcopts": LocalDCCredentialsOptions,
3093 Option("--refresh", type="choice", metavar="check|store",
3094 choices=["check", "store", None],
3095 help="List and maybe store refreshed forest trust information: 'check' or 'store'.",
3098 Option("--enable-all", action="store_true",
3099 help="Try to update disabled entries, not allowed with --refresh=check.",
3102 Option("--enable-tln", action="append", metavar='DNSDOMAIN',
3103 help="Enable a top level name entry. Can be specified multiple times.",
3106 Option("--disable-tln", action="append", metavar='DNSDOMAIN',
3107 help="Disable a top level name entry. Can be specified multiple times.",
3110 Option("--add-tln-ex", action="append", metavar='DNSDOMAIN',
3111 help="Add a top level exclusion entry. Can be specified multiple times.",
3114 Option("--delete-tln-ex", action="append", metavar='DNSDOMAIN',
3115 help="Delete a top level exclusion entry. Can be specified multiple times.",
3116 dest='delete_tln_ex',
3118 Option("--enable-nb", action="append", metavar='NETBIOSDOMAIN',
3119 help="Enable a netbios name in a domain entry. Can be specified multiple times.",
3122 Option("--disable-nb", action="append", metavar='NETBIOSDOMAIN',
3123 help="Disable a netbios name in a domain entry. Can be specified multiple times.",
3126 Option("--enable-sid", action="append", metavar='DOMAINSID',
3127 help="Enable a SID in a domain entry. Can be specified multiple times.",
3128 dest='enable_sid_str',
3130 Option("--disable-sid", action="append", metavar='DOMAINSID',
3131 help="Disable a SID in a domain entry. Can be specified multiple times.",
3132 dest='disable_sid_str',
3134 Option("--add-upn-suffix", action="append", metavar='DNSDOMAIN',
3135 help="Add a new uPNSuffixes attribute for the local forest. Can be specified multiple times.",
3138 Option("--delete-upn-suffix", action="append", metavar='DNSDOMAIN',
3139 help="Delete an existing uPNSuffixes attribute of the local forest. Can be specified multiple times.",
3142 Option("--add-spn-suffix", action="append", metavar='DNSDOMAIN',
3143 help="Add a new msDS-SPNSuffixes attribute for the local forest. Can be specified multiple times.",
3146 Option("--delete-spn-suffix", action="append", metavar='DNSDOMAIN',
3147 help="Delete an existing msDS-SPNSuffixes attribute of the local forest. Can be specified multiple times.",
3152 takes_args = ["domain?"]
3154 def run(self, domain=None, sambaopts=None, localdcopts=None, versionopts=None,
3155 refresh=None, enable_all=False,
3156 enable_tln=[], disable_tln=[], add_tln_ex=[], delete_tln_ex=[],
3157 enable_sid_str=[], disable_sid_str=[], enable_nb=[], disable_nb=[],
3158 add_upn=[], delete_upn=[], add_spn=[], delete_spn=[]):
3160 require_update = False
3163 if refresh == "store":
3164 raise CommandError("--refresh=%s not allowed without DOMAIN" % refresh)
3167 raise CommandError("--enable-all not allowed without DOMAIN")
3169 if len(enable_tln) > 0:
3170 raise CommandError("--enable-tln not allowed without DOMAIN")
3171 if len(disable_tln) > 0:
3172 raise CommandError("--disable-tln not allowed without DOMAIN")
3174 if len(add_tln_ex) > 0:
3175 raise CommandError("--add-tln-ex not allowed without DOMAIN")
3176 if len(delete_tln_ex) > 0:
3177 raise CommandError("--delete-tln-ex not allowed without DOMAIN")
3179 if len(enable_nb) > 0:
3180 raise CommandError("--enable-nb not allowed without DOMAIN")
3181 if len(disable_nb) > 0:
3182 raise CommandError("--disable-nb not allowed without DOMAIN")
3184 if len(enable_sid_str) > 0:
3185 raise CommandError("--enable-sid not allowed without DOMAIN")
3186 if len(disable_sid_str) > 0:
3187 raise CommandError("--disable-sid not allowed without DOMAIN")
3189 if len(add_upn) > 0:
3191 if not n.startswith("*."):
3193 raise CommandError("value[%s] specified for --add-upn-suffix should not include with '*.'" % n)
3194 require_update = True
3195 if len(delete_upn) > 0:
3196 for n in delete_upn:
3197 if not n.startswith("*."):
3199 raise CommandError("value[%s] specified for --delete-upn-suffix should not include with '*.'" % n)
3200 require_update = True
3202 for d in delete_upn:
3203 if a.lower() != d.lower():
3205 raise CommandError("value[%s] specified for --add-upn-suffix and --delete-upn-suffix" % a)
3207 if len(add_spn) > 0:
3209 if not n.startswith("*."):
3211 raise CommandError("value[%s] specified for --add-spn-suffix should not include with '*.'" % n)
3212 require_update = True
3213 if len(delete_spn) > 0:
3214 for n in delete_spn:
3215 if not n.startswith("*."):
3217 raise CommandError("value[%s] specified for --delete-spn-suffix should not include with '*.'" % n)
3218 require_update = True
3220 for d in delete_spn:
3221 if a.lower() != d.lower():
3223 raise CommandError("value[%s] specified for --add-spn-suffix and --delete-spn-suffix" % a)
3225 if len(add_upn) > 0:
3226 raise CommandError("--add-upn-suffix not allowed together with DOMAIN")
3227 if len(delete_upn) > 0:
3228 raise CommandError("--delete-upn-suffix not allowed together with DOMAIN")
3229 if len(add_spn) > 0:
3230 raise CommandError("--add-spn-suffix not allowed together with DOMAIN")
3231 if len(delete_spn) > 0:
3232 raise CommandError("--delete-spn-suffix not allowed together with DOMAIN")
3234 if refresh is not None:
3235 if refresh == "store":
3236 require_update = True
3238 if enable_all and refresh != "store":
3239 raise CommandError("--enable-all not allowed together with --refresh=%s" % refresh)
3241 if len(enable_tln) > 0:
3242 raise CommandError("--enable-tln not allowed together with --refresh")
3243 if len(disable_tln) > 0:
3244 raise CommandError("--disable-tln not allowed together with --refresh")
3246 if len(add_tln_ex) > 0:
3247 raise CommandError("--add-tln-ex not allowed together with --refresh")
3248 if len(delete_tln_ex) > 0:
3249 raise CommandError("--delete-tln-ex not allowed together with --refresh")
3251 if len(enable_nb) > 0:
3252 raise CommandError("--enable-nb not allowed together with --refresh")
3253 if len(disable_nb) > 0:
3254 raise CommandError("--disable-nb not allowed together with --refresh")
3256 if len(enable_sid_str) > 0:
3257 raise CommandError("--enable-sid not allowed together with --refresh")
3258 if len(disable_sid_str) > 0:
3259 raise CommandError("--disable-sid not allowed together with --refresh")
3262 require_update = True
3264 if len(enable_tln) > 0:
3265 raise CommandError("--enable-tln not allowed together with --enable-all")
3267 if len(enable_nb) > 0:
3268 raise CommandError("--enable-nb not allowed together with --enable-all")
3270 if len(enable_sid_str) > 0:
3271 raise CommandError("--enable-sid not allowed together with --enable-all")
3273 if len(enable_tln) > 0:
3274 require_update = True
3275 if len(disable_tln) > 0:
3276 require_update = True
3277 for e in enable_tln:
3278 for d in disable_tln:
3279 if e.lower() != d.lower():
3281 raise CommandError("value[%s] specified for --enable-tln and --disable-tln" % e)
3283 if len(add_tln_ex) > 0:
3284 for n in add_tln_ex:
3285 if not n.startswith("*."):
3287 raise CommandError("value[%s] specified for --add-tln-ex should not include with '*.'" % n)
3288 require_update = True
3289 if len(delete_tln_ex) > 0:
3290 for n in delete_tln_ex:
3291 if not n.startswith("*."):
3293 raise CommandError("value[%s] specified for --delete-tln-ex should not include with '*.'" % n)
3294 require_update = True
3295 for a in add_tln_ex:
3296 for d in delete_tln_ex:
3297 if a.lower() != d.lower():
3299 raise CommandError("value[%s] specified for --add-tln-ex and --delete-tln-ex" % a)
3301 if len(enable_nb) > 0:
3302 require_update = True
3303 if len(disable_nb) > 0:
3304 require_update = True
3306 for d in disable_nb:
3307 if e.upper() != d.upper():
3309 raise CommandError("value[%s] specified for --enable-nb and --disable-nb" % e)
3312 for s in enable_sid_str:
3314 sid = security.dom_sid(s)
3315 except TypeError as error:
3316 raise CommandError("value[%s] specified for --enable-sid is not a valid SID" % s)
3317 enable_sid.append(sid)
3319 for s in disable_sid_str:
3321 sid = security.dom_sid(s)
3322 except TypeError as error:
3323 raise CommandError("value[%s] specified for --disable-sid is not a valid SID" % s)
3324 disable_sid.append(sid)
3325 if len(enable_sid) > 0:
3326 require_update = True
3327 if len(disable_sid) > 0:
3328 require_update = True
3329 for e in enable_sid:
3330 for d in disable_sid:
3333 raise CommandError("value[%s] specified for --enable-sid and --disable-sid" % e)
3335 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
3337 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
3339 local_server = self.setup_local_server(sambaopts, localdcopts)
3341 local_lsa = self.new_local_lsa_connection()
3342 except RuntimeError as error:
3343 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
3346 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
3347 except RuntimeError as error:
3348 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
3350 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
3351 local_lsa_info.name.string,
3352 local_lsa_info.dns_domain.string,
3353 local_lsa_info.sid))
3357 local_netlogon = self.new_local_netlogon_connection()
3358 except RuntimeError as error:
3359 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3362 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3363 except RuntimeError as error:
3364 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3366 if local_netlogon_info.domain_name != local_netlogon_info.forest_name:
3367 raise CommandError("The local domain [%s] is not the forest root [%s]" % (
3368 local_netlogon_info.domain_name,
3369 local_netlogon_info.forest_name))
3372 # get all information about our own forest
3373 own_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3375 except RuntimeError as error:
3376 if self.check_runtime_error(error, werror.WERR_RPC_S_PROCNUM_OUT_OF_RANGE):
3377 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3380 if self.check_runtime_error(error, werror.WERR_INVALID_FUNCTION):
3381 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3384 if self.check_runtime_error(error, werror.WERR_NERR_ACFNOTLOADED):
3385 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3388 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3390 self.outf.write("Own forest trust information...\n")
3391 self.write_forest_trust_info(own_forest_info,
3392 tln=local_lsa_info.dns_domain.string)
3395 local_samdb = self.new_local_ldap_connection()
3396 except RuntimeError as error:
3397 raise self.LocalRuntimeError(self, error, "failed to connect to SamDB")
3399 local_partitions_dn = "CN=Partitions,%s" % str(local_samdb.get_config_basedn())
3400 attrs = ['uPNSuffixes', 'msDS-SPNSuffixes']
3402 msgs = local_samdb.search(base=local_partitions_dn,
3403 scope=ldb.SCOPE_BASE,
3404 expression="(objectClass=crossRefContainer)",
3406 stored_msg = msgs[0]
3407 except ldb.LdbError as error:
3408 raise self.LocalLdbError(self, error, "failed to search partition dn")
3410 stored_upn_vals = []
3411 if 'uPNSuffixes' in stored_msg:
3412 stored_upn_vals.extend(stored_msg['uPNSuffixes'])
3414 stored_spn_vals = []
3415 if 'msDS-SPNSuffixes' in stored_msg:
3416 stored_spn_vals.extend(stored_msg['msDS-SPNSuffixes'])
3418 self.outf.write("Stored uPNSuffixes attributes[%d]:\n" % len(stored_upn_vals))
3419 for v in stored_upn_vals:
3420 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3421 self.outf.write("Stored msDS-SPNSuffixes attributes[%d]:\n" % len(stored_spn_vals))
3422 for v in stored_spn_vals:
3423 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3425 if not require_update:
3429 update_upn_vals = []
3430 update_upn_vals.extend(stored_upn_vals)
3433 update_spn_vals = []
3434 update_spn_vals.extend(stored_spn_vals)
3438 for i in xrange(0, len(update_upn_vals)):
3439 v = update_upn_vals[i]
3440 if v.lower() != upn.lower():
3445 raise CommandError("Entry already present for value[%s] specified for --add-upn-suffix" % upn)
3446 update_upn_vals.append(upn)
3449 for upn in delete_upn:
3451 for i in xrange(0, len(update_upn_vals)):
3452 v = update_upn_vals[i]
3453 if v.lower() != upn.lower():
3458 raise CommandError("Entry not found for value[%s] specified for --delete-upn-suffix" % upn)
3460 update_upn_vals.pop(idx)
3465 for i in xrange(0, len(update_spn_vals)):
3466 v = update_spn_vals[i]
3467 if v.lower() != spn.lower():
3472 raise CommandError("Entry already present for value[%s] specified for --add-spn-suffix" % spn)
3473 update_spn_vals.append(spn)
3476 for spn in delete_spn:
3478 for i in xrange(0, len(update_spn_vals)):
3479 v = update_spn_vals[i]
3480 if v.lower() != spn.lower():
3485 raise CommandError("Entry not found for value[%s] specified for --delete-spn-suffix" % spn)
3487 update_spn_vals.pop(idx)
3490 self.outf.write("Update uPNSuffixes attributes[%d]:\n" % len(update_upn_vals))
3491 for v in update_upn_vals:
3492 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3493 self.outf.write("Update msDS-SPNSuffixes attributes[%d]:\n" % len(update_spn_vals))
3494 for v in update_spn_vals:
3495 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3497 update_msg = ldb.Message()
3498 update_msg.dn = stored_msg.dn
3501 update_msg['uPNSuffixes'] = ldb.MessageElement(update_upn_vals,
3502 ldb.FLAG_MOD_REPLACE,
3505 update_msg['msDS-SPNSuffixes'] = ldb.MessageElement(update_spn_vals,
3506 ldb.FLAG_MOD_REPLACE,
3509 local_samdb.modify(update_msg)
3510 except ldb.LdbError as error:
3511 raise self.LocalLdbError(self, error, "failed to update partition dn")
3514 stored_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3516 except RuntimeError as error:
3517 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3519 self.outf.write("Stored forest trust information...\n")
3520 self.write_forest_trust_info(stored_forest_info,
3521 tln=local_lsa_info.dns_domain.string)
3525 lsaString = lsa.String()
3526 lsaString.string = domain
3527 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
3528 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
3529 except NTSTATUSError as error:
3530 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
3531 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
3533 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
3535 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
3536 local_tdo_info.netbios_name.string,
3537 local_tdo_info.domain_name.string,
3538 local_tdo_info.sid))
3540 if not local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
3541 raise CommandError("trusted domain object for domain [%s] is not marked as FOREST_TRANSITIVE." % domain)
3543 if refresh is not None:
3545 local_netlogon = self.new_local_netlogon_connection()
3546 except RuntimeError as error:
3547 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3550 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3551 except RuntimeError as error:
3552 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3554 lsa_update_check = 1
3555 if refresh == "store":
3556 netlogon_update_tdo = netlogon.DS_GFTI_UPDATE_TDO
3558 lsa_update_check = 0
3560 netlogon_update_tdo = 0
3563 # get all information about the remote trust
3564 # this triggers netr_GetForestTrustInformation to the remote domain
3565 # and lsaRSetForestTrustInformation() locally, but new top level
3566 # names are disabled by default.
3567 fresh_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3568 local_tdo_info.domain_name.string,
3569 netlogon_update_tdo)
3570 except RuntimeError as error:
3571 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3574 fresh_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3575 local_tdo_info.domain_name,
3576 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3579 except RuntimeError as error:
3580 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3582 self.outf.write("Fresh forest trust information...\n")
3583 self.write_forest_trust_info(fresh_forest_info,
3584 tln=local_tdo_info.domain_name.string,
3585 collisions=fresh_forest_collision)
3587 if refresh == "store":
3589 lsaString = lsa.String()
3590 lsaString.string = local_tdo_info.domain_name.string
3591 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3593 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3594 except RuntimeError as error:
3595 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3597 self.outf.write("Stored forest trust information...\n")
3598 self.write_forest_trust_info(stored_forest_info,
3599 tln=local_tdo_info.domain_name.string)
3604 # The none --refresh path
3608 lsaString = lsa.String()
3609 lsaString.string = local_tdo_info.domain_name.string
3610 local_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3612 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3613 except RuntimeError as error:
3614 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3616 self.outf.write("Local forest trust information...\n")
3617 self.write_forest_trust_info(local_forest_info,
3618 tln=local_tdo_info.domain_name.string)
3620 if not require_update:
3624 entries.extend(local_forest_info.entries)
3625 update_forest_info = lsa.ForestTrustInformation()
3626 update_forest_info.count = len(entries)
3627 update_forest_info.entries = entries
3630 for i in xrange(0, len(update_forest_info.entries)):
3631 r = update_forest_info.entries[i]
3632 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3634 if update_forest_info.entries[i].flags == 0:
3636 update_forest_info.entries[i].time = 0
3637 update_forest_info.entries[i].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3638 for i in xrange(0, len(update_forest_info.entries)):
3639 r = update_forest_info.entries[i]
3640 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3642 if update_forest_info.entries[i].flags == 0:
3644 update_forest_info.entries[i].time = 0
3645 update_forest_info.entries[i].flags &= ~lsa.LSA_NB_DISABLED_MASK
3646 update_forest_info.entries[i].flags &= ~lsa.LSA_SID_DISABLED_MASK
3648 for tln in enable_tln:
3650 for i in xrange(0, len(update_forest_info.entries)):
3651 r = update_forest_info.entries[i]
3652 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3654 if r.forest_trust_data.string.lower() != tln.lower():
3659 raise CommandError("Entry not found for value[%s] specified for --enable-tln" % tln)
3660 if not update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_MASK:
3661 raise CommandError("Entry found for value[%s] specified for --enable-tln is already enabled" % tln)
3662 update_forest_info.entries[idx].time = 0
3663 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3665 for tln in disable_tln:
3667 for i in xrange(0, len(update_forest_info.entries)):
3668 r = update_forest_info.entries[i]
3669 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3671 if r.forest_trust_data.string.lower() != tln.lower():
3676 raise CommandError("Entry not found for value[%s] specified for --disable-tln" % tln)
3677 if update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_ADMIN:
3678 raise CommandError("Entry found for value[%s] specified for --disable-tln is already disabled" % tln)
3679 update_forest_info.entries[idx].time = 0
3680 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3681 update_forest_info.entries[idx].flags |= lsa.LSA_TLN_DISABLED_ADMIN
3683 for tln_ex in add_tln_ex:
3685 for i in xrange(0, len(update_forest_info.entries)):
3686 r = update_forest_info.entries[i]
3687 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3689 if r.forest_trust_data.string.lower() != tln_ex.lower():
3694 raise CommandError("Entry already present for value[%s] specified for --add-tln-ex" % tln_ex)
3696 tln_dot = ".%s" % tln_ex.lower()
3698 for i in xrange(0, len(update_forest_info.entries)):
3699 r = update_forest_info.entries[i]
3700 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3702 r_dot = ".%s" % r.forest_trust_data.string.lower()
3703 if tln_dot == r_dot:
3704 raise CommandError("TLN entry present for value[%s] specified for --add-tln-ex" % tln_ex)
3705 if not tln_dot.endswith(r_dot):
3711 raise CommandError("No TLN parent present for value[%s] specified for --add-tln-ex" % tln_ex)
3713 r = lsa.ForestTrustRecord()
3714 r.type = lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX
3717 r.forest_trust_data.string = tln_ex
3720 entries.extend(update_forest_info.entries)
3721 entries.insert(idx + 1, r)
3722 update_forest_info.count = len(entries)
3723 update_forest_info.entries = entries
3725 for tln_ex in delete_tln_ex:
3727 for i in xrange(0, len(update_forest_info.entries)):
3728 r = update_forest_info.entries[i]
3729 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3731 if r.forest_trust_data.string.lower() != tln_ex.lower():
3736 raise CommandError("Entry not found for value[%s] specified for --delete-tln-ex" % tln_ex)
3739 entries.extend(update_forest_info.entries)
3741 update_forest_info.count = len(entries)
3742 update_forest_info.entries = entries
3744 for nb in enable_nb:
3746 for i in xrange(0, len(update_forest_info.entries)):
3747 r = update_forest_info.entries[i]
3748 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3750 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3755 raise CommandError("Entry not found for value[%s] specified for --enable-nb" % nb)
3756 if not update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_MASK:
3757 raise CommandError("Entry found for value[%s] specified for --enable-nb is already enabled" % nb)
3758 update_forest_info.entries[idx].time = 0
3759 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3761 for nb in disable_nb:
3763 for i in xrange(0, len(update_forest_info.entries)):
3764 r = update_forest_info.entries[i]
3765 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3767 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3772 raise CommandError("Entry not found for value[%s] specified for --delete-nb" % nb)
3773 if update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_ADMIN:
3774 raise CommandError("Entry found for value[%s] specified for --disable-nb is already disabled" % nb)
3775 update_forest_info.entries[idx].time = 0
3776 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3777 update_forest_info.entries[idx].flags |= lsa.LSA_NB_DISABLED_ADMIN
3779 for sid in enable_sid:
3781 for i in xrange(0, len(update_forest_info.entries)):
3782 r = update_forest_info.entries[i]
3783 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3785 if r.forest_trust_data.domain_sid != sid:
3790 raise CommandError("Entry not found for value[%s] specified for --enable-sid" % sid)
3791 if not update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_MASK:
3792 raise CommandError("Entry found for value[%s] specified for --enable-sid is already enabled" % nb)
3793 update_forest_info.entries[idx].time = 0
3794 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3796 for sid in disable_sid:
3798 for i in xrange(0, len(update_forest_info.entries)):
3799 r = update_forest_info.entries[i]
3800 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3802 if r.forest_trust_data.domain_sid != sid:
3807 raise CommandError("Entry not found for value[%s] specified for --delete-sid" % sid)
3808 if update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_ADMIN:
3809 raise CommandError("Entry found for value[%s] specified for --disable-sid is already disabled" % nb)
3810 update_forest_info.entries[idx].time = 0
3811 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3812 update_forest_info.entries[idx].flags |= lsa.LSA_SID_DISABLED_ADMIN
3815 update_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3816 local_tdo_info.domain_name,
3817 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3818 update_forest_info, 0)
3819 except RuntimeError as error:
3820 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3822 self.outf.write("Updated forest trust information...\n")
3823 self.write_forest_trust_info(update_forest_info,
3824 tln=local_tdo_info.domain_name.string,
3825 collisions=update_forest_collision)
3828 lsaString = lsa.String()
3829 lsaString.string = local_tdo_info.domain_name.string
3830 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3832 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3833 except RuntimeError as error:
3834 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3836 self.outf.write("Stored forest trust information...\n")
3837 self.write_forest_trust_info(stored_forest_info,
3838 tln=local_tdo_info.domain_name.string)
3841 class cmd_domain_tombstones_expunge(Command):
3842 """Expunge tombstones from the database.
3844 This command expunges tombstones from the database."""
3845 synopsis = "%prog NC [NC [...]] [options]"
3848 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
3849 metavar="URL", dest="H"),
3850 Option("--current-time",
3851 help="The current time to evaluate the tombstone lifetime from, expressed as YYYY-MM-DD",
3853 Option("--tombstone-lifetime", help="Number of days a tombstone should be preserved for", type=int),
3856 takes_args = ["nc*"]
3858 takes_optiongroups = {
3859 "sambaopts": options.SambaOptions,
3860 "credopts": options.CredentialsOptions,
3861 "versionopts": options.VersionOptions,
3864 def run(self, *ncs, **kwargs):
3865 sambaopts = kwargs.get("sambaopts")
3866 credopts = kwargs.get("credopts")
3867 versionpts = kwargs.get("versionopts")
3869 current_time_string = kwargs.get("current_time")
3870 tombstone_lifetime = kwargs.get("tombstone_lifetime")
3871 lp = sambaopts.get_loadparm()
3872 creds = credopts.get_credentials(lp)
3873 samdb = SamDB(url=H, session_info=system_session(),
3874 credentials=creds, lp=lp)
3876 if current_time_string is not None:
3877 current_time_obj = time.strptime(current_time_string, "%Y-%m-%d")
3878 current_time = long(time.mktime(current_time_obj))
3881 current_time = long(time.time())
3884 res = samdb.search(expression="", base="", scope=ldb.SCOPE_BASE,
3885 attrs=["namingContexts"])
3888 for nc in res[0]["namingContexts"]:
3893 started_transaction = False
3895 samdb.transaction_start()
3896 started_transaction = True
3898 removed_links) = samdb.garbage_collect_tombstones(ncs,
3899 current_time=current_time,
3900 tombstone_lifetime=tombstone_lifetime)
3902 except Exception as err:
3903 if started_transaction:
3904 samdb.transaction_cancel()
3905 raise CommandError("Failed to expunge / garbage collect tombstones", err)
3907 samdb.transaction_commit()
3909 self.outf.write("Removed %d objects and %d links successfully\n"
3910 % (removed_objects, removed_links))
3914 class cmd_domain_trust(SuperCommand):
3915 """Domain and forest trust management."""
3918 subcommands["list"] = cmd_domain_trust_list()
3919 subcommands["show"] = cmd_domain_trust_show()
3920 subcommands["create"] = cmd_domain_trust_create()
3921 subcommands["delete"] = cmd_domain_trust_delete()
3922 subcommands["validate"] = cmd_domain_trust_validate()
3923 subcommands["namespaces"] = cmd_domain_trust_namespaces()
3925 class cmd_domain_tombstones(SuperCommand):
3926 """Domain tombstone and recycled object management."""
3929 subcommands["expunge"] = cmd_domain_tombstones_expunge()
3931 class ldif_schema_update:
3932 """Helper class for applying LDIF schema updates"""
3935 self.is_defunct = False
3936 self.unknown_oid = None
3940 def _ldap_schemaUpdateNow(self, samdb):
3944 add: schemaUpdateNow
3947 samdb.modify_ldif(ldif)
3949 def can_ignore_failure(self, error):
3950 """Checks if we can safely ignore failure to apply an LDIF update"""
3951 (num, errstr) = error.args
3953 # Microsoft has marked objects as defunct that Samba doesn't know about
3954 if num == ldb.ERR_NO_SUCH_OBJECT and self.is_defunct:
3955 print("Defunct object %s doesn't exist, skipping" % self.dn)
3957 elif self.unknown_oid is not None:
3958 print("Skipping unknown OID %s for object %s" %(self.unknown_oid, self.dn))
3963 def apply(self, samdb):
3964 """Applies a single LDIF update to the schema"""
3968 samdb.modify_ldif(self.ldif, controls=['relax:0'])
3969 except ldb.LdbError as e:
3970 if e.args[0] == ldb.ERR_INVALID_ATTRIBUTE_SYNTAX:
3972 # REFRESH after a failed change
3974 # Otherwise the OID-to-attribute mapping in
3975 # _apply_updates_in_file() won't work, because it
3976 # can't lookup the new OID in the schema
3977 self._ldap_schemaUpdateNow(samdb)
3979 samdb.modify_ldif(self.ldif, controls=['relax:0'])
3982 except ldb.LdbError as e:
3983 if self.can_ignore_failure(e):
3986 print("Exception: %s" % e)
3987 print("Encountered while trying to apply the following LDIF")
3988 print("----------------------------------------------------")
3989 print("%s" % self.ldif)
3995 class cmd_domain_schema_upgrade(Command):
3996 """Domain schema upgrading"""
3998 synopsis = "%prog [options]"
4000 takes_optiongroups = {
4001 "sambaopts": options.SambaOptions,
4002 "versionopts": options.VersionOptions,
4003 "credopts": options.CredentialsOptions,
4007 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
4008 metavar="URL", dest="H"),
4009 Option("--quiet", help="Be quiet", action="store_true"),
4010 Option("--verbose", help="Be verbose", action="store_true"),
4011 Option("--schema", type="choice", metavar="SCHEMA",
4012 choices=["2012", "2012_R2"],
4013 help="The schema file to upgrade to. Default is (Windows) 2012_R2.",
4015 Option("--ldf-file", type=str, default=None,
4016 help="Just apply the schema updates in the adprep/.LDF file(s) specified"),
4017 Option("--base-dir", type=str, default=None,
4018 help="Location of ldf files Default is ${SETUPDIR}/adprep.")
4021 def _apply_updates_in_file(self, samdb, ldif_file):
4023 Applies a series of updates specified in an .LDIF file. The .LDIF file
4024 is based on the adprep Schema updates provided by Microsoft.
4027 ldif_op = ldif_schema_update()
4029 # parse the file line by line and work out each update operation to apply
4030 for line in ldif_file:
4032 line = line.rstrip()
4034 # the operations in the .LDIF file are separated by blank lines. If
4035 # we hit a blank line, try to apply the update we've parsed so far
4038 # keep going if we haven't parsed anything yet
4039 if ldif_op.ldif == '':
4042 # Apply the individual change
4043 count += ldif_op.apply(samdb)
4045 # start storing the next operation from scratch again
4046 ldif_op = ldif_schema_update()
4049 # replace the placeholder domain name in the .ldif file with the real domain
4050 if line.upper().endswith('DC=X'):
4051 line = line[:-len('DC=X')] + str(samdb.get_default_basedn())
4052 elif line.upper().endswith('CN=X'):
4053 line = line[:-len('CN=X')] + str(samdb.get_default_basedn())
4055 values = line.split(':')
4057 if values[0].lower() == 'dn':
4058 ldif_op.dn = values[1].strip()
4060 # replace the Windows-specific operation with the Samba one
4061 if values[0].lower() == 'changetype':
4062 line = line.lower().replace(': ntdsschemaadd',
4064 line = line.lower().replace(': ntdsschemamodify',
4067 if values[0].lower() in ['rdnattid', 'subclassof',
4068 'systemposssuperiors',
4070 'systemauxiliaryclass']:
4073 # The Microsoft updates contain some OIDs we don't recognize.
4074 # Query the DB to see if we can work out the OID this update is
4075 # referring to. If we find a match, then replace the OID with
4076 # the ldapDisplayname
4078 res = samdb.search(base=samdb.get_schema_basedn(),
4079 expression="(|(attributeId=%s)(governsId=%s))" %
4081 attrs=['ldapDisplayName'])
4084 ldif_op.unknown_oid = value
4086 display_name = res[0]['ldapDisplayName'][0]
4087 line = line.replace(value, ' ' + display_name)
4089 # Microsoft has marked objects as defunct that Samba doesn't know about
4090 if values[0].lower() == 'isdefunct' and values[1].strip().lower() == 'true':
4091 ldif_op.is_defunct = True
4093 # Samba has added the showInAdvancedViewOnly attribute to all objects,
4094 # so rather than doing an add, we need to do a replace
4095 if values[0].lower() == 'add' and values[1].strip().lower() == 'showinadvancedviewonly':
4096 line = 'replace: showInAdvancedViewOnly'
4098 # Add the line to the current LDIF operation (including the newline
4099 # we stripped off at the start of the loop)
4100 ldif_op.ldif += line + '\n'
4105 def _apply_update(self, samdb, update_file, base_dir):
4106 """Wrapper function for parsing an LDIF file and applying the updates"""
4108 print("Applying %s updates..." % update_file)
4112 ldif_file = open(os.path.join(base_dir, update_file))
4114 count = self._apply_updates_in_file(samdb, ldif_file)
4120 print("%u changes applied" % count)
4124 def run(self, **kwargs):
4125 from samba.ms_schema_markdown import read_ms_markdown
4126 from samba.schema import Schema
4128 updates_allowed_overriden = False
4129 sambaopts = kwargs.get("sambaopts")
4130 credopts = kwargs.get("credopts")
4131 versionpts = kwargs.get("versionopts")
4132 lp = sambaopts.get_loadparm()
4133 creds = credopts.get_credentials(lp)
4135 target_schema = kwargs.get("schema")
4136 ldf_files = kwargs.get("ldf_file")
4137 base_dir = kwargs.get("base_dir")
4141 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
4143 # we're not going to get far if the config doesn't allow schema updates
4144 if lp.get("dsdb:schema update allowed") is None:
4145 lp.set("dsdb:schema update allowed", "yes")
4146 print("Temporarily overriding 'dsdb:schema update allowed' setting")
4147 updates_allowed_overriden = True
4149 own_dn = ldb.Dn(samdb, samdb.get_dsServiceName())
4150 master = get_fsmo_roleowner(samdb, str(samdb.get_schema_basedn()),
4152 if own_dn != master:
4153 raise CommandError("This server is not the schema master.")
4155 # if specific LDIF files were specified, just apply them
4157 schema_updates = ldf_files.split(",")
4161 # work out the version of the target schema we're upgrading to
4162 end = Schema.get_version(target_schema)
4164 # work out the version of the schema we're currently using
4165 res = samdb.search(base=samdb.get_schema_basedn(),
4166 scope=ldb.SCOPE_BASE, attrs=['objectVersion'])
4169 raise CommandError('Could not determine current schema version')
4170 start = int(res[0]['objectVersion'][0]) + 1
4172 diff_dir = setup_path("adprep/WindowsServerDocs")
4173 if base_dir is None:
4174 # Read from the Schema-Updates.md file
4175 temp_folder = tempfile.mkdtemp()
4177 update_file = setup_path("adprep/WindowsServerDocs/Schema-Updates.md")
4180 read_ms_markdown(update_file, temp_folder)
4181 except Exception as e:
4182 print("Exception in markdown parsing: %s" % e)
4183 shutil.rmtree(temp_folder)
4184 raise CommandError('Failed to upgrade schema')
4186 base_dir = temp_folder
4188 for version in range(start, end + 1):
4189 update = 'Sch%d.ldf' % version
4190 schema_updates.append(update)
4192 # Apply patches if we parsed the Schema-Updates.md file
4193 diff = os.path.abspath(os.path.join(diff_dir, update + '.diff'))
4194 if temp_folder and os.path.exists(diff):
4196 p = subprocess.Popen(['patch', update, '-i', diff],
4197 stdout=subprocess.PIPE,
4198 stderr=subprocess.PIPE, cwd=temp_folder)
4199 except (OSError, IOError):
4200 shutil.rmtree(temp_folder)
4201 raise CommandError("Failed to upgrade schema. Check if 'patch' is installed.")
4203 stdout, stderr = p.communicate()
4206 print("Exception in patch: %s\n%s" % (stdout, stderr))
4207 shutil.rmtree(temp_folder)
4208 raise CommandError('Failed to upgrade schema')
4210 print("Patched %s using %s" % (update, diff))
4212 if base_dir is None:
4213 base_dir = setup_path("adprep")
4215 samdb.transaction_start()
4217 error_encountered = False
4220 # Apply the schema updates needed to move to the new schema version
4221 for ldif_file in schema_updates:
4222 count += self._apply_update(samdb, ldif_file, base_dir)
4225 samdb.transaction_commit()
4226 print("Schema successfully updated")
4228 print("No changes applied to schema")
4229 samdb.transaction_cancel()
4230 except Exception as e:
4231 print("Exception: %s" % e)
4232 print("Error encountered, aborting schema upgrade")
4233 samdb.transaction_cancel()
4234 error_encountered = True
4236 if updates_allowed_overriden:
4237 lp.set("dsdb:schema update allowed", "no")
4240 shutil.rmtree(temp_folder)
4242 if error_encountered:
4243 raise CommandError('Failed to upgrade schema')
4245 class cmd_domain_functional_prep(Command):
4246 """Domain functional level preparation"""
4248 synopsis = "%prog [options]"
4250 takes_optiongroups = {
4251 "sambaopts": options.SambaOptions,
4252 "versionopts": options.VersionOptions,
4253 "credopts": options.CredentialsOptions,
4257 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
4258 metavar="URL", dest="H"),
4259 Option("--quiet", help="Be quiet", action="store_true"),
4260 Option("--verbose", help="Be verbose", action="store_true"),
4261 Option("--function-level", type="choice", metavar="FUNCTION_LEVEL",
4262 choices=["2008_R2", "2012", "2012_R2"],
4263 help="The schema file to upgrade to. Default is (Windows) 2012_R2.",
4265 Option("--forest-prep", action="store_true",
4266 help="Run the forest prep (by default, both the domain and forest prep are run)."),
4267 Option("--domain-prep", action="store_true",
4268 help="Run the domain prep (by default, both the domain and forest prep are run).")
4271 def run(self, **kwargs):
4272 updates_allowed_overriden = False
4273 sambaopts = kwargs.get("sambaopts")
4274 credopts = kwargs.get("credopts")
4275 versionpts = kwargs.get("versionopts")
4276 lp = sambaopts.get_loadparm()
4277 creds = credopts.get_credentials(lp)
4279 target_level = string_version_to_constant[kwargs.get("function_level")]
4280 forest_prep = kwargs.get("forest_prep")
4281 domain_prep = kwargs.get("domain_prep")
4283 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
4285 # we're not going to get far if the config doesn't allow schema updates
4286 if lp.get("dsdb:schema update allowed") is None:
4287 lp.set("dsdb:schema update allowed", "yes")
4288 print("Temporarily overriding 'dsdb:schema update allowed' setting")
4289 updates_allowed_overriden = True
4291 if forest_prep is None and domain_prep is None:
4295 own_dn = ldb.Dn(samdb, samdb.get_dsServiceName())
4297 master = get_fsmo_roleowner(samdb, str(samdb.get_schema_basedn()),
4299 if own_dn != master:
4300 raise CommandError("This server is not the schema master.")
4303 domain_dn = samdb.domain_dn()
4304 infrastructure_dn = "CN=Infrastructure," + domain_dn
4305 master = get_fsmo_roleowner(samdb, infrastructure_dn,
4307 if own_dn != master:
4308 raise CommandError("This server is not the infrastructure master.")
4311 samdb.transaction_start()
4312 error_encountered = False
4314 from samba.forest_update import ForestUpdate
4315 forest = ForestUpdate(samdb, fix=True)
4317 forest.check_updates_iterator([53, 79, 80, 81, 82, 83])
4318 forest.check_updates_functional_level(target_level,
4319 DS_DOMAIN_FUNCTION_2008_R2,
4320 update_revision=True)
4322 samdb.transaction_commit()
4323 except Exception as e:
4324 print("Exception: %s" % e)
4325 samdb.transaction_cancel()
4326 error_encountered = True
4329 samdb.transaction_start()
4330 error_encountered = False
4332 from samba.domain_update import DomainUpdate
4334 domain = DomainUpdate(samdb, fix=True)
4335 domain.check_updates_functional_level(target_level,
4336 DS_DOMAIN_FUNCTION_2008,
4337 update_revision=True)
4339 samdb.transaction_commit()
4340 except Exception as e:
4341 print("Exception: %s" % e)
4342 samdb.transaction_cancel()
4343 error_encountered = True
4345 if updates_allowed_overriden:
4346 lp.set("dsdb:schema update allowed", "no")
4348 if error_encountered:
4349 raise CommandError('Failed to perform functional prep')
4351 class cmd_domain(SuperCommand):
4352 """Domain management."""
4355 subcommands["demote"] = cmd_domain_demote()
4356 if cmd_domain_export_keytab is not None:
4357 subcommands["exportkeytab"] = cmd_domain_export_keytab()
4358 subcommands["info"] = cmd_domain_info()
4359 subcommands["provision"] = cmd_domain_provision()
4360 subcommands["join"] = cmd_domain_join()
4361 subcommands["dcpromo"] = cmd_domain_dcpromo()
4362 subcommands["level"] = cmd_domain_level()
4363 subcommands["passwordsettings"] = cmd_domain_passwordsettings()
4364 subcommands["classicupgrade"] = cmd_domain_classicupgrade()
4365 subcommands["samba3upgrade"] = cmd_domain_samba3upgrade()
4366 subcommands["trust"] = cmd_domain_trust()
4367 subcommands["tombstones"] = cmd_domain_tombstones()
4368 subcommands["schemaupgrade"] = cmd_domain_schema_upgrade()
4369 subcommands["functionalprep"] = cmd_domain_functional_prep()