3 # Copyright Matthias Dieter Wallnoefer 2009
4 # Copyright Andrew Kroeger 2009
5 # Copyright Jelmer Vernooij 2007-2012
6 # Copyright Giampaolo Lauria 2011
7 # Copyright Matthieu Patou <mat@matws.net> 2011
8 # Copyright Andrew Bartlett 2008-2015
9 # Copyright Stefan Metzmacher 2012
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 3 of the License, or
14 # (at your option) any later version.
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 from __future__ import print_function
26 from __future__ import division
27 import samba.getopt as options
39 from samba import ntstatus
40 from samba import NTSTATUSError
41 from samba import werror
42 from getpass import getpass
43 from samba.net import Net, LIBNET_JOIN_AUTOMATIC
45 from samba.join import join_RODC, join_DC, join_subdomain
46 from samba.auth import system_session
47 from samba.samdb import SamDB, get_default_backend_store
48 from samba.ndr import ndr_unpack, ndr_pack, ndr_print
49 from samba.dcerpc import drsuapi
50 from samba.dcerpc import drsblobs
51 from samba.dcerpc import lsa
52 from samba.dcerpc import netlogon
53 from samba.dcerpc import security
54 from samba.dcerpc import nbt
55 from samba.dcerpc import misc
56 from samba.dcerpc.samr import DOMAIN_PASSWORD_COMPLEX, DOMAIN_PASSWORD_STORE_CLEARTEXT
57 from samba.netcmd import (
63 from samba.netcmd.fsmo import get_fsmo_roleowner
64 from samba.netcmd.common import netcmd_get_domain_infos_via_cldap
65 from samba.samba3 import Samba3
66 from samba.samba3 import param as s3param
67 from samba.upgrade import upgrade_from_samba3
68 from samba.drs_utils import (
69 sendDsReplicaSync, drsuapi_connect, drsException,
71 from samba import remove_dc, arcfour_encrypt, string_to_byte_array
73 from samba.dsdb import (
74 DS_DOMAIN_FUNCTION_2000,
75 DS_DOMAIN_FUNCTION_2003,
76 DS_DOMAIN_FUNCTION_2003_MIXED,
77 DS_DOMAIN_FUNCTION_2008,
78 DS_DOMAIN_FUNCTION_2008_R2,
79 DS_DOMAIN_FUNCTION_2012,
80 DS_DOMAIN_FUNCTION_2012_R2,
81 DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL,
82 DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL,
83 UF_WORKSTATION_TRUST_ACCOUNT,
84 UF_SERVER_TRUST_ACCOUNT,
85 UF_TRUSTED_FOR_DELEGATION,
86 UF_PARTIAL_SECRETS_ACCOUNT
89 from samba.provision import (
92 DEFAULT_MIN_PWD_LENGTH,
96 from samba.provision.common import (
102 from samba.netcmd.pso import cmd_domain_passwordsettings_pso
103 from samba.netcmd.domain_backup import cmd_domain_backup
105 string_version_to_constant = {
106 "2008_R2": DS_DOMAIN_FUNCTION_2008_R2,
107 "2012": DS_DOMAIN_FUNCTION_2012,
108 "2012_R2": DS_DOMAIN_FUNCTION_2012_R2,
111 common_provision_join_options = [
112 Option("--machinepass", type="string", metavar="PASSWORD",
113 help="choose machine password (otherwise random)"),
114 Option("--plaintext-secrets", action="store_true",
115 help="Store secret/sensitive values as plain text on disk" +
116 "(default is to encrypt secret/ensitive values)"),
117 Option("--backend-store", type="choice", metavar="BACKENDSTORE",
118 choices=["tdb", "mdb"],
119 help="Specify the database backend to be used "
120 "(default is %s)" % get_default_backend_store()),
121 Option("--targetdir", metavar="DIR",
122 help="Set target directory (where to store provision)", type=str),
123 Option("-q", "--quiet", help="Be quiet", action="store_true"),
126 common_join_options = [
127 Option("--server", help="DC to join", type=str),
128 Option("--site", help="site to join", type=str),
129 Option("--domain-critical-only",
130 help="only replicate critical domain objects",
131 action="store_true"),
132 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
133 choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
134 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
135 "BIND9_DLZ uses samba4 AD to store zone information, "
136 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
137 default="SAMBA_INTERNAL"),
138 Option("-v", "--verbose", help="Be verbose", action="store_true")
141 common_ntvfs_options = [
142 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
147 def get_testparm_var(testparm, smbconf, varname):
148 errfile = open(os.devnull, 'w')
149 p = subprocess.Popen([testparm, '-s', '-l',
150 '--parameter-name=%s' % varname, smbconf],
151 stdout=subprocess.PIPE, stderr=errfile)
152 (out, err) = p.communicate()
154 lines = out.split('\n')
156 return lines[0].strip()
160 import samba.dckeytab
162 cmd_domain_export_keytab = None
164 class cmd_domain_export_keytab(Command):
165 """Dump Kerberos keys of the domain into a keytab."""
167 synopsis = "%prog <keytab> [options]"
169 takes_optiongroups = {
170 "sambaopts": options.SambaOptions,
171 "credopts": options.CredentialsOptions,
172 "versionopts": options.VersionOptions,
176 Option("--principal", help="extract only this principal", type=str),
179 takes_args = ["keytab"]
181 def run(self, keytab, credopts=None, sambaopts=None, versionopts=None, principal=None):
182 lp = sambaopts.get_loadparm()
184 net.export_keytab(keytab=keytab, principal=principal)
187 class cmd_domain_info(Command):
188 """Print basic info about a domain and the DC passed as parameter."""
190 synopsis = "%prog <ip_address> [options]"
195 takes_optiongroups = {
196 "sambaopts": options.SambaOptions,
197 "credopts": options.CredentialsOptions,
198 "versionopts": options.VersionOptions,
201 takes_args = ["address"]
203 def run(self, address, credopts=None, sambaopts=None, versionopts=None):
204 lp = sambaopts.get_loadparm()
206 res = netcmd_get_domain_infos_via_cldap(lp, None, address)
208 raise CommandError("Invalid IP address '" + address + "'!")
209 self.outf.write("Forest : %s\n" % res.forest)
210 self.outf.write("Domain : %s\n" % res.dns_domain)
211 self.outf.write("Netbios domain : %s\n" % res.domain_name)
212 self.outf.write("DC name : %s\n" % res.pdc_dns_name)
213 self.outf.write("DC netbios name : %s\n" % res.pdc_name)
214 self.outf.write("Server site : %s\n" % res.server_site)
215 self.outf.write("Client site : %s\n" % res.client_site)
218 class cmd_domain_provision(Command):
219 """Provision a domain."""
221 synopsis = "%prog [options]"
223 takes_optiongroups = {
224 "sambaopts": options.SambaOptions,
225 "versionopts": options.VersionOptions,
229 Option("--interactive", help="Ask for names", action="store_true"),
230 Option("--domain", type="string", metavar="DOMAIN",
231 help="NetBIOS domain name to use"),
232 Option("--domain-guid", type="string", metavar="GUID",
233 help="set domainguid (otherwise random)"),
234 Option("--domain-sid", type="string", metavar="SID",
235 help="set domainsid (otherwise random)"),
236 Option("--ntds-guid", type="string", metavar="GUID",
237 help="set NTDS object GUID (otherwise random)"),
238 Option("--invocationid", type="string", metavar="GUID",
239 help="set invocationid (otherwise random)"),
240 Option("--host-name", type="string", metavar="HOSTNAME",
241 help="set hostname"),
242 Option("--host-ip", type="string", metavar="IPADDRESS",
243 help="set IPv4 ipaddress"),
244 Option("--host-ip6", type="string", metavar="IP6ADDRESS",
245 help="set IPv6 ipaddress"),
246 Option("--site", type="string", metavar="SITENAME",
247 help="set site name"),
248 Option("--adminpass", type="string", metavar="PASSWORD",
249 help="choose admin password (otherwise random)"),
250 Option("--krbtgtpass", type="string", metavar="PASSWORD",
251 help="choose krbtgt password (otherwise random)"),
252 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
253 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
254 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
255 "BIND9_FLATFILE uses bind9 text database to store zone information, "
256 "BIND9_DLZ uses samba4 AD to store zone information, "
257 "NONE skips the DNS setup entirely (not recommended)",
258 default="SAMBA_INTERNAL"),
259 Option("--dnspass", type="string", metavar="PASSWORD",
260 help="choose dns password (otherwise random)"),
261 Option("--root", type="string", metavar="USERNAME",
262 help="choose 'root' unix username"),
263 Option("--nobody", type="string", metavar="USERNAME",
264 help="choose 'nobody' user"),
265 Option("--users", type="string", metavar="GROUPNAME",
266 help="choose 'users' group"),
267 Option("--blank", action="store_true",
268 help="do not add users or groups, just the structure"),
269 Option("--server-role", type="choice", metavar="ROLE",
270 choices=["domain controller", "dc", "member server", "member", "standalone"],
271 help="The server role (domain controller | dc | member server | member | standalone). Default is dc.",
272 default="domain controller"),
273 Option("--function-level", type="choice", metavar="FOR-FUN-LEVEL",
274 choices=["2000", "2003", "2008", "2008_R2"],
275 help="The domain and forest function level (2000 | 2003 | 2008 | 2008_R2 - always native). Default is (Windows) 2008_R2 Native.",
277 Option("--base-schema", type="choice", metavar="BASE-SCHEMA",
278 choices=["2008_R2", "2008_R2_old", "2012", "2012_R2"],
279 help="The base schema files to use. Default is (Windows) 2008_R2.",
281 Option("--next-rid", type="int", metavar="NEXTRID", default=1000,
282 help="The initial nextRid value (only needed for upgrades). Default is 1000."),
283 Option("--partitions-only",
284 help="Configure Samba's partitions, but do not modify them (ie, join a BDC)", action="store_true"),
285 Option("--use-rfc2307", action="store_true", help="Use AD to store posix attributes (default = no)"),
289 Option("--ldapadminpass", type="string", metavar="PASSWORD",
290 help="choose password to set between Samba and its LDAP backend (otherwise random)"),
291 Option("--ldap-backend-type", type="choice", metavar="LDAP-BACKEND-TYPE",
292 help="Test initialisation support for unsupported LDAP backend type (fedora-ds or openldap) DO NOT USE",
293 choices=["fedora-ds", "openldap"]),
294 Option("--ol-mmr-urls", type="string", metavar="LDAPSERVER",
295 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\""),
296 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",
297 action="store_true"),
298 Option("--slapd-path", type="string", metavar="SLAPD-PATH",
299 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."),
300 Option("--ldap-backend-extra-port", type="int", metavar="LDAP-BACKEND-EXTRA-PORT", help="Additional TCP port for LDAP backend server (to use for replication)"),
301 Option("--ldap-backend-forced-uri", type="string", metavar="LDAP-BACKEND-FORCED-URI",
302 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"),
303 Option("--ldap-backend-nosync", help="Configure LDAP backend not to call fsync() (for performance in test environments)", action="store_true"),
307 Option("--use-xattrs", type="choice", choices=["yes", "no", "auto"],
308 metavar="[yes|no|auto]",
309 help="Define if we should use the native fs capabilities or a tdb file for "
310 "storing attributes likes ntacl when --use-ntvfs is set. "
311 "auto tries to make an inteligent guess based on the user rights and system capabilities",
315 takes_options.extend(common_provision_join_options)
317 if os.getenv('TEST_LDAP', "no") == "yes":
318 takes_options.extend(openldap_options)
320 if samba.is_ntvfs_fileserver_built():
321 takes_options.extend(common_ntvfs_options)
322 takes_options.extend(ntvfs_options)
326 def run(self, sambaopts=None, versionopts=None,
349 ldap_backend_type=None,
353 partitions_only=None,
360 ldap_backend_nosync=None,
361 ldap_backend_extra_port=None,
362 ldap_backend_forced_uri=None,
363 ldap_dryrun_mode=None,
365 plaintext_secrets=False,
368 self.logger = self.get_logger("provision")
370 self.logger.setLevel(logging.WARNING)
372 self.logger.setLevel(logging.INFO)
374 lp = sambaopts.get_loadparm()
375 smbconf = lp.configfile
377 if dns_forwarder is not None:
378 suggested_forwarder = dns_forwarder
380 suggested_forwarder = self._get_nameserver_ip()
381 if suggested_forwarder is None:
382 suggested_forwarder = "none"
384 if len(self.raw_argv) == 1:
388 from getpass import getpass
391 def ask(prompt, default=None):
392 if default is not None:
393 print("%s [%s]: " % (prompt, default), end=' ')
395 print("%s: " % (prompt,), end=' ')
396 return sys.stdin.readline().rstrip("\n") or default
399 default = socket.getfqdn().split(".", 1)[1].upper()
402 realm = ask("Realm", default)
403 if realm in (None, ""):
404 raise CommandError("No realm set!")
407 default = realm.split(".")[0]
410 domain = ask("Domain", default)
412 raise CommandError("No domain set!")
414 server_role = ask("Server Role (dc, member, standalone)", "dc")
416 dns_backend = ask("DNS backend (SAMBA_INTERNAL, BIND9_FLATFILE, BIND9_DLZ, NONE)", "SAMBA_INTERNAL")
417 if dns_backend in (None, ''):
418 raise CommandError("No DNS backend set!")
420 if dns_backend == "SAMBA_INTERNAL":
421 dns_forwarder = ask("DNS forwarder IP address (write 'none' to disable forwarding)", suggested_forwarder)
422 if dns_forwarder.lower() in (None, 'none'):
423 suggested_forwarder = None
427 adminpassplain = getpass("Administrator password: ")
428 issue = self._adminpass_issue(adminpassplain)
430 self.errf.write("%s.\n" % issue)
432 adminpassverify = getpass("Retype password: ")
433 if not adminpassplain == adminpassverify:
434 self.errf.write("Sorry, passwords do not match.\n")
436 adminpass = adminpassplain
440 realm = sambaopts._lp.get('realm')
442 raise CommandError("No realm set!")
444 raise CommandError("No domain set!")
447 issue = self._adminpass_issue(adminpass)
449 raise CommandError(issue)
451 self.logger.info("Administrator password will be set randomly!")
453 if function_level == "2000":
454 dom_for_fun_level = DS_DOMAIN_FUNCTION_2000
455 elif function_level == "2003":
456 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
457 elif function_level == "2008":
458 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008
459 elif function_level == "2008_R2":
460 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
462 if dns_backend == "SAMBA_INTERNAL" and dns_forwarder is None:
463 dns_forwarder = suggested_forwarder
465 samdb_fill = FILL_FULL
467 samdb_fill = FILL_NT4SYNC
468 elif partitions_only:
469 samdb_fill = FILL_DRS
471 if targetdir is not None:
472 if not os.path.isdir(targetdir):
477 if use_xattrs == "yes":
479 elif use_xattrs == "auto" and use_ntvfs == False:
481 elif use_ntvfs == False:
482 raise CommandError("--use-xattrs=no requires --use-ntvfs (not supported for production use). "
483 "Please re-run with --use-xattrs omitted.")
484 elif use_xattrs == "auto" and not lp.get("posix:eadb"):
486 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
488 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
491 samba.ntacls.setntacl(lp, file.name,
492 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
495 self.logger.info("You are not root or your system does not support xattr, using tdb backend for attributes. ")
500 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.")
501 if ldap_backend_type == "existing":
502 if ldap_backend_forced_uri is not None:
503 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)
505 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")
507 if ldap_backend_forced_uri is not None:
508 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")
510 if domain_sid is not None:
511 domain_sid = security.dom_sid(domain_sid)
513 session = system_session()
514 if backend_store is None:
515 backend_store = get_default_backend_store()
517 result = provision(self.logger,
518 session, smbconf=smbconf, targetdir=targetdir,
519 samdb_fill=samdb_fill, realm=realm, domain=domain,
520 domainguid=domain_guid, domainsid=domain_sid,
522 hostip=host_ip, hostip6=host_ip6,
523 sitename=site, ntdsguid=ntds_guid,
524 invocationid=invocationid, adminpass=adminpass,
525 krbtgtpass=krbtgtpass, machinepass=machinepass,
526 dns_backend=dns_backend, dns_forwarder=dns_forwarder,
527 dnspass=dnspass, root=root, nobody=nobody,
529 serverrole=server_role, dom_for_fun_level=dom_for_fun_level,
530 backend_type=ldap_backend_type,
531 ldapadminpass=ldapadminpass, ol_mmr_urls=ol_mmr_urls, slapd_path=slapd_path,
532 useeadb=eadb, next_rid=next_rid, lp=lp, use_ntvfs=use_ntvfs,
533 use_rfc2307=use_rfc2307, skip_sysvolacl=False,
534 ldap_backend_extra_port=ldap_backend_extra_port,
535 ldap_backend_forced_uri=ldap_backend_forced_uri,
536 nosync=ldap_backend_nosync, ldap_dryrun_mode=ldap_dryrun_mode,
537 base_schema=base_schema,
538 plaintext_secrets=plaintext_secrets,
539 backend_store=backend_store)
541 except ProvisioningError as e:
542 raise CommandError("Provision failed", e)
544 result.report_logger(self.logger)
546 def _get_nameserver_ip(self):
547 """Grab the nameserver IP address from /etc/resolv.conf."""
549 RESOLV_CONF = "/etc/resolv.conf"
551 if not path.isfile(RESOLV_CONF):
552 self.logger.warning("Failed to locate %s" % RESOLV_CONF)
557 handle = open(RESOLV_CONF, 'r')
559 if not line.startswith('nameserver'):
561 # we want the last non-space continuous string of the line
562 return line.strip().split()[-1]
564 if handle is not None:
567 self.logger.warning("No nameserver found in %s" % RESOLV_CONF)
569 def _adminpass_issue(self, adminpass):
570 """Returns error string for a bad administrator password,
571 or None if acceptable"""
573 if len(adminpass.decode('utf-8')) < DEFAULT_MIN_PWD_LENGTH:
574 return "Administrator password does not meet the default minimum" \
575 " password length requirement (%d characters)" \
576 % DEFAULT_MIN_PWD_LENGTH
577 elif not samba.check_password_quality(adminpass):
578 return "Administrator password does not meet the default" \
584 class cmd_domain_dcpromo(Command):
585 """Promote an existing domain member or NT4 PDC to an AD DC."""
587 synopsis = "%prog <dnsdomain> [DC|RODC] [options]"
589 takes_optiongroups = {
590 "sambaopts": options.SambaOptions,
591 "versionopts": options.VersionOptions,
592 "credopts": options.CredentialsOptions,
596 takes_options.extend(common_join_options)
598 takes_options.extend(common_provision_join_options)
600 if samba.is_ntvfs_fileserver_built():
601 takes_options.extend(common_ntvfs_options)
604 takes_args = ["domain", "role?"]
606 def run(self, domain, role=None, sambaopts=None, credopts=None,
607 versionopts=None, server=None, site=None, targetdir=None,
608 domain_critical_only=False, parent_domain=None, machinepass=None,
609 use_ntvfs=False, dns_backend=None,
610 quiet=False, verbose=False, plaintext_secrets=False,
612 lp = sambaopts.get_loadparm()
613 creds = credopts.get_credentials(lp)
614 net = Net(creds, lp, server=credopts.ipaddress)
616 logger = self.get_logger()
618 logger.setLevel(logging.DEBUG)
620 logger.setLevel(logging.WARNING)
622 logger.setLevel(logging.INFO)
624 netbios_name = lp.get("netbios name")
630 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
631 site=site, netbios_name=netbios_name, targetdir=targetdir,
632 domain_critical_only=domain_critical_only,
633 machinepass=machinepass, use_ntvfs=use_ntvfs,
634 dns_backend=dns_backend,
635 promote_existing=True, plaintext_secrets=plaintext_secrets,
636 backend_store=backend_store)
638 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
639 site=site, netbios_name=netbios_name, targetdir=targetdir,
640 domain_critical_only=domain_critical_only,
641 machinepass=machinepass, use_ntvfs=use_ntvfs, dns_backend=dns_backend,
642 promote_existing=True, plaintext_secrets=plaintext_secrets,
643 backend_store=backend_store)
645 raise CommandError("Invalid role '%s' (possible values: DC, RODC)" % role)
648 class cmd_domain_join(Command):
649 """Join domain as either member or backup domain controller."""
651 synopsis = "%prog <dnsdomain> [DC|RODC|MEMBER|SUBDOMAIN] [options]"
653 takes_optiongroups = {
654 "sambaopts": options.SambaOptions,
655 "versionopts": options.VersionOptions,
656 "credopts": options.CredentialsOptions,
660 Option("--parent-domain", help="parent domain to create subdomain under", type=str),
661 Option("--adminpass", type="string", metavar="PASSWORD",
662 help="choose adminstrator password when joining as a subdomain (otherwise random)"),
666 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
669 takes_options.extend(common_join_options)
670 takes_options.extend(common_provision_join_options)
672 if samba.is_ntvfs_fileserver_built():
673 takes_options.extend(ntvfs_options)
675 takes_args = ["domain", "role?"]
677 def run(self, domain, role=None, sambaopts=None, credopts=None,
678 versionopts=None, server=None, site=None, targetdir=None,
679 domain_critical_only=False, parent_domain=None, machinepass=None,
680 use_ntvfs=False, dns_backend=None, adminpass=None,
681 quiet=False, verbose=False,
682 plaintext_secrets=False,
684 lp = sambaopts.get_loadparm()
685 creds = credopts.get_credentials(lp)
686 net = Net(creds, lp, server=credopts.ipaddress)
689 site = "Default-First-Site-Name"
691 logger = self.get_logger()
693 logger.setLevel(logging.DEBUG)
695 logger.setLevel(logging.WARNING)
697 logger.setLevel(logging.INFO)
699 netbios_name = lp.get("netbios name")
704 if role is None or role == "MEMBER":
705 (join_password, sid, domain_name) = net.join_member(
706 domain, netbios_name, LIBNET_JOIN_AUTOMATIC,
707 machinepass=machinepass)
709 self.errf.write("Joined domain %s (%s)\n" % (domain_name, sid))
711 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
712 site=site, netbios_name=netbios_name, targetdir=targetdir,
713 domain_critical_only=domain_critical_only,
714 machinepass=machinepass, use_ntvfs=use_ntvfs,
715 dns_backend=dns_backend,
716 plaintext_secrets=plaintext_secrets,
717 backend_store=backend_store)
719 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
720 site=site, netbios_name=netbios_name, targetdir=targetdir,
721 domain_critical_only=domain_critical_only,
722 machinepass=machinepass, use_ntvfs=use_ntvfs,
723 dns_backend=dns_backend,
724 plaintext_secrets=plaintext_secrets,
725 backend_store=backend_store)
726 elif role == "SUBDOMAIN":
728 logger.info("Administrator password will be set randomly!")
730 netbios_domain = lp.get("workgroup")
731 if parent_domain is None:
732 parent_domain = ".".join(domain.split(".")[1:])
733 join_subdomain(logger=logger, server=server, creds=creds, lp=lp, dnsdomain=domain,
734 parent_domain=parent_domain, site=site,
735 netbios_name=netbios_name, netbios_domain=netbios_domain,
736 targetdir=targetdir, machinepass=machinepass,
737 use_ntvfs=use_ntvfs, dns_backend=dns_backend,
739 plaintext_secrets=plaintext_secrets,
740 backend_store=backend_store)
742 raise CommandError("Invalid role '%s' (possible values: MEMBER, DC, RODC, SUBDOMAIN)" % role)
745 class cmd_domain_demote(Command):
746 """Demote ourselves from the role of Domain Controller."""
748 synopsis = "%prog [options]"
751 Option("--server", help="writable DC to write demotion changes on", type=str),
752 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
753 metavar="URL", dest="H"),
754 Option("--remove-other-dead-server", help="Dead DC (name or NTDS GUID) "
755 "to remove ALL references to (rather than this DC)", type=str),
756 Option("-q", "--quiet", help="Be quiet", action="store_true"),
757 Option("-v", "--verbose", help="Be verbose", action="store_true"),
760 takes_optiongroups = {
761 "sambaopts": options.SambaOptions,
762 "credopts": options.CredentialsOptions,
763 "versionopts": options.VersionOptions,
766 def run(self, sambaopts=None, credopts=None,
767 versionopts=None, server=None,
768 remove_other_dead_server=None, H=None,
769 verbose=False, quiet=False):
770 lp = sambaopts.get_loadparm()
771 creds = credopts.get_credentials(lp)
772 net = Net(creds, lp, server=credopts.ipaddress)
774 logger = self.get_logger()
776 logger.setLevel(logging.DEBUG)
778 logger.setLevel(logging.WARNING)
780 logger.setLevel(logging.INFO)
782 if remove_other_dead_server is not None:
783 if server is not None:
784 samdb = SamDB(url="ldap://%s" % server,
785 session_info=system_session(),
786 credentials=creds, lp=lp)
788 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
790 remove_dc.remove_dc(samdb, logger, remove_other_dead_server)
791 except remove_dc.DemoteException as err:
792 raise CommandError("Demote failed: %s" % err)
795 netbios_name = lp.get("netbios name")
796 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
798 res = samdb.search(expression='(&(objectClass=computer)(serverReferenceBL=*))', attrs=["dnsHostName", "name"])
800 raise CommandError("Unable to search for servers")
803 raise CommandError("You are the last server in the domain")
807 if str(e["name"]).lower() != netbios_name.lower():
808 server = e["dnsHostName"]
811 ntds_guid = samdb.get_ntds_GUID()
812 msg = samdb.search(base=str(samdb.get_config_basedn()),
813 scope=ldb.SCOPE_SUBTREE, expression="(objectGUID=%s)" % ntds_guid,
815 if len(msg) == 0 or "options" not in msg[0]:
816 raise CommandError("Failed to find options on %s" % ntds_guid)
819 dsa_options = int(str(msg[0]['options']))
821 res = samdb.search(expression="(fSMORoleOwner=%s)" % str(ntds_dn),
822 controls=["search_options:1:2"])
825 raise CommandError("Current DC is still the owner of %d role(s), "
826 "use the role command to transfer roles to "
830 self.errf.write("Using %s as partner server for the demotion\n" %
832 (drsuapiBind, drsuapi_handle, supportedExtensions) = drsuapi_connect(server, lp, creds)
834 self.errf.write("Deactivating inbound replication\n")
839 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
840 dsa_options |= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
841 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
845 self.errf.write("Asking partner server %s to synchronize from us\n"
847 for part in (samdb.get_schema_basedn(),
848 samdb.get_config_basedn(),
849 samdb.get_root_basedn()):
850 nc = drsuapi.DsReplicaObjectIdentifier()
853 req1 = drsuapi.DsReplicaSyncRequest1()
854 req1.naming_context = nc;
855 req1.options = drsuapi.DRSUAPI_DRS_WRIT_REP
856 req1.source_dsa_guid = misc.GUID(ntds_guid)
859 drsuapiBind.DsReplicaSync(drsuapi_handle, 1, req1)
860 except RuntimeError as e1:
861 (werr, string) = e1.args
862 if werr == werror.WERR_DS_DRA_NO_REPLICA:
866 "Error while replicating out last local changes from '%s' for demotion, "
867 "re-enabling inbound replication\n" % part)
868 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
869 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
871 raise CommandError("Error while sending a DsReplicaSync for partition '%s'" % str(part), string)
873 remote_samdb = SamDB(url="ldap://%s" % server,
874 session_info=system_session(),
875 credentials=creds, lp=lp)
877 self.errf.write("Changing userControl and container\n")
878 res = remote_samdb.search(base=str(remote_samdb.domain_dn()),
879 expression="(&(objectClass=user)(sAMAccountName=%s$))" %
880 netbios_name.upper(),
881 attrs=["userAccountControl"])
883 uac = int(str(res[0]["userAccountControl"]))
885 except Exception as e:
886 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
888 "Error while demoting, re-enabling inbound replication\n")
889 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
890 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
892 raise CommandError("Error while changing account control", e)
895 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
897 "Error while demoting, re-enabling inbound replication")
898 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
899 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
901 raise CommandError("Unable to find object with samaccountName = %s$"
902 " in the remote dc" % netbios_name.upper())
906 uac &= ~(UF_SERVER_TRUST_ACCOUNT |UF_TRUSTED_FOR_DELEGATION |UF_PARTIAL_SECRETS_ACCOUNT)
907 uac |= UF_WORKSTATION_TRUST_ACCOUNT
912 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
913 ldb.FLAG_MOD_REPLACE,
914 "userAccountControl")
916 remote_samdb.modify(msg)
917 except Exception as e:
918 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
920 "Error while demoting, re-enabling inbound replication")
921 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
922 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
925 raise CommandError("Error while changing account control", e)
927 parent = msg.dn.parent()
928 dc_name = res[0].dn.get_rdn_value()
929 rdn = "CN=%s" % dc_name
931 # Let's move to the Computer container
935 computer_dn = ldb.Dn(remote_samdb, "CN=Computers,%s" % str(remote_samdb.domain_dn()))
936 res = remote_samdb.search(base=computer_dn, expression=rdn, scope=ldb.SCOPE_ONELEVEL)
939 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
940 scope=ldb.SCOPE_ONELEVEL)
941 while(len(res) != 0 and i < 100):
943 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
944 scope=ldb.SCOPE_ONELEVEL)
947 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
949 "Error while demoting, re-enabling inbound replication\n")
950 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
951 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
957 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
958 ldb.FLAG_MOD_REPLACE,
959 "userAccountControl")
961 remote_samdb.modify(msg)
963 raise CommandError("Unable to find a slot for renaming %s,"
964 " all names from %s-1 to %s-%d seemed used" %
965 (str(dc_dn), rdn, rdn, i - 9))
967 newrdn = "%s-%d" % (rdn, i)
970 newdn = ldb.Dn(remote_samdb, "%s,%s" % (newrdn, str(computer_dn)))
971 remote_samdb.rename(dc_dn, newdn)
972 except Exception as e:
973 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
975 "Error while demoting, re-enabling inbound replication\n")
976 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
977 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
983 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
984 ldb.FLAG_MOD_REPLACE,
985 "userAccountControl")
987 remote_samdb.modify(msg)
988 raise CommandError("Error while renaming %s to %s" % (str(dc_dn), str(newdn)), e)
991 server_dsa_dn = samdb.get_serverName()
992 domain = remote_samdb.get_root_basedn()
995 req1 = drsuapi.DsRemoveDSServerRequest1()
996 req1.server_dn = str(server_dsa_dn)
997 req1.domain_dn = str(domain)
1000 drsuapiBind.DsRemoveDSServer(drsuapi_handle, 1, req1)
1001 except RuntimeError as e3:
1002 (werr, string) = e3.args
1003 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
1005 "Error while demoting, re-enabling inbound replication\n")
1006 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
1007 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
1013 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
1014 ldb.FLAG_MOD_REPLACE,
1015 "userAccountControl")
1016 remote_samdb.modify(msg)
1017 remote_samdb.rename(newdn, dc_dn)
1018 if werr == werror.WERR_DS_DRA_NO_REPLICA:
1019 raise CommandError("The DC %s is not present on (already "
1020 "removed from) the remote server: %s" %
1021 (server_dsa_dn, e3))
1023 raise CommandError("Error while sending a removeDsServer "
1025 (server_dsa_dn, e3))
1027 remove_dc.remove_sysvol_references(remote_samdb, logger, dc_name)
1029 # These are objects under the computer account that should be deleted
1030 for s in ("CN=Enterprise,CN=NTFRS Subscriptions",
1031 "CN=%s, CN=NTFRS Subscriptions" % lp.get("realm"),
1032 "CN=Domain system Volumes (SYSVOL Share), CN=NTFRS Subscriptions",
1033 "CN=NTFRS Subscriptions"):
1035 remote_samdb.delete(ldb.Dn(remote_samdb,
1036 "%s,%s" % (s, str(newdn))))
1037 except ldb.LdbError as l:
1040 # get dns host name for target server to demote, remove dns references
1041 remove_dc.remove_dns_references(remote_samdb, logger, samdb.host_dns_name(),
1042 ignore_no_name=True)
1044 self.errf.write("Demote successful\n")
1047 class cmd_domain_level(Command):
1048 """Raise domain and forest function levels."""
1050 synopsis = "%prog (show|raise <options>) [options]"
1052 takes_optiongroups = {
1053 "sambaopts": options.SambaOptions,
1054 "credopts": options.CredentialsOptions,
1055 "versionopts": options.VersionOptions,
1059 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1060 metavar="URL", dest="H"),
1061 Option("-q", "--quiet", help="Be quiet", action="store_true"), # unused
1062 Option("--forest-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
1063 help="The forest function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)"),
1064 Option("--domain-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
1065 help="The domain function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)")
1068 takes_args = ["subcommand"]
1070 def run(self, subcommand, H=None, forest_level=None, domain_level=None,
1071 quiet=False, credopts=None, sambaopts=None, versionopts=None):
1072 lp = sambaopts.get_loadparm()
1073 creds = credopts.get_credentials(lp, fallback_machine=True)
1075 samdb = SamDB(url=H, session_info=system_session(),
1076 credentials=creds, lp=lp)
1078 domain_dn = samdb.domain_dn()
1080 res_forest = samdb.search("CN=Partitions,%s" % samdb.get_config_basedn(),
1081 scope=ldb.SCOPE_BASE, attrs=["msDS-Behavior-Version"])
1082 assert len(res_forest) == 1
1084 res_domain = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1085 attrs=["msDS-Behavior-Version", "nTMixedDomain"])
1086 assert len(res_domain) == 1
1088 res_dc_s = samdb.search("CN=Sites,%s" % samdb.get_config_basedn(),
1089 scope=ldb.SCOPE_SUBTREE, expression="(objectClass=nTDSDSA)",
1090 attrs=["msDS-Behavior-Version"])
1091 assert len(res_dc_s) >= 1
1093 # default values, since "msDS-Behavior-Version" does not exist on Windows 2000 AD
1094 level_forest = DS_DOMAIN_FUNCTION_2000
1095 level_domain = DS_DOMAIN_FUNCTION_2000
1097 if "msDS-Behavior-Version" in res_forest[0]:
1098 level_forest = int(res_forest[0]["msDS-Behavior-Version"][0])
1099 if "msDS-Behavior-Version" in res_domain[0]:
1100 level_domain = int(res_domain[0]["msDS-Behavior-Version"][0])
1101 level_domain_mixed = int(res_domain[0]["nTMixedDomain"][0])
1104 for msg in res_dc_s:
1105 if "msDS-Behavior-Version" in msg:
1106 if min_level_dc is None or int(msg["msDS-Behavior-Version"][0]) < min_level_dc:
1107 min_level_dc = int(msg["msDS-Behavior-Version"][0])
1109 min_level_dc = DS_DOMAIN_FUNCTION_2000
1110 # well, this is the least
1113 if level_forest < DS_DOMAIN_FUNCTION_2000 or level_domain < DS_DOMAIN_FUNCTION_2000:
1114 raise CommandError("Domain and/or forest function level(s) is/are invalid. Correct them or reprovision!")
1115 if min_level_dc < DS_DOMAIN_FUNCTION_2000:
1116 raise CommandError("Lowest function level of a DC is invalid. Correct this or reprovision!")
1117 if level_forest > level_domain:
1118 raise CommandError("Forest function level is higher than the domain level(s). Correct this or reprovision!")
1119 if level_domain > min_level_dc:
1120 raise CommandError("Domain function level is higher than the lowest function level of a DC. Correct this or reprovision!")
1122 if subcommand == "show":
1123 self.message("Domain and forest function level for domain '%s'" % domain_dn)
1124 if level_forest == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1125 self.message("\nATTENTION: You run SAMBA 4 on a forest function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1126 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1127 self.message("\nATTENTION: You run SAMBA 4 on a domain function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1128 if min_level_dc == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1129 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)!")
1133 if level_forest == DS_DOMAIN_FUNCTION_2000:
1135 elif level_forest == DS_DOMAIN_FUNCTION_2003_MIXED:
1136 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1137 elif level_forest == DS_DOMAIN_FUNCTION_2003:
1139 elif level_forest == DS_DOMAIN_FUNCTION_2008:
1141 elif level_forest == DS_DOMAIN_FUNCTION_2008_R2:
1143 elif level_forest == DS_DOMAIN_FUNCTION_2012:
1145 elif level_forest == DS_DOMAIN_FUNCTION_2012_R2:
1148 outstr = "higher than 2012 R2"
1149 self.message("Forest function level: (Windows) " + outstr)
1151 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1152 outstr = "2000 mixed (NT4 DC support)"
1153 elif level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed == 0:
1155 elif level_domain == DS_DOMAIN_FUNCTION_2003_MIXED:
1156 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1157 elif level_domain == DS_DOMAIN_FUNCTION_2003:
1159 elif level_domain == DS_DOMAIN_FUNCTION_2008:
1161 elif level_domain == DS_DOMAIN_FUNCTION_2008_R2:
1163 elif level_domain == DS_DOMAIN_FUNCTION_2012:
1165 elif level_domain == DS_DOMAIN_FUNCTION_2012_R2:
1168 outstr = "higher than 2012 R2"
1169 self.message("Domain function level: (Windows) " + outstr)
1171 if min_level_dc == DS_DOMAIN_FUNCTION_2000:
1173 elif min_level_dc == DS_DOMAIN_FUNCTION_2003:
1175 elif min_level_dc == DS_DOMAIN_FUNCTION_2008:
1177 elif min_level_dc == DS_DOMAIN_FUNCTION_2008_R2:
1179 elif min_level_dc == DS_DOMAIN_FUNCTION_2012:
1181 elif min_level_dc == DS_DOMAIN_FUNCTION_2012_R2:
1184 outstr = "higher than 2012 R2"
1185 self.message("Lowest function level of a DC: (Windows) " + outstr)
1187 elif subcommand == "raise":
1190 if domain_level is not None:
1191 if domain_level == "2003":
1192 new_level_domain = DS_DOMAIN_FUNCTION_2003
1193 elif domain_level == "2008":
1194 new_level_domain = DS_DOMAIN_FUNCTION_2008
1195 elif domain_level == "2008_R2":
1196 new_level_domain = DS_DOMAIN_FUNCTION_2008_R2
1197 elif domain_level == "2012":
1198 new_level_domain = DS_DOMAIN_FUNCTION_2012
1199 elif domain_level == "2012_R2":
1200 new_level_domain = DS_DOMAIN_FUNCTION_2012_R2
1202 if new_level_domain <= level_domain and level_domain_mixed == 0:
1203 raise CommandError("Domain function level can't be smaller than or equal to the actual one!")
1204 if new_level_domain > min_level_dc:
1205 raise CommandError("Domain function level can't be higher than the lowest function level of a DC!")
1207 # Deactivate mixed/interim domain support
1208 if level_domain_mixed != 0:
1209 # Directly on the base DN
1211 m.dn = ldb.Dn(samdb, domain_dn)
1212 m["nTMixedDomain"] = ldb.MessageElement("0",
1213 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1217 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup") + ",CN=Partitions,%s" % samdb.get_config_basedn())
1218 m["nTMixedDomain"] = ldb.MessageElement("0",
1219 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1222 except ldb.LdbError as e:
1223 (enum, emsg) = e.args
1224 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1227 # Directly on the base DN
1229 m.dn = ldb.Dn(samdb, domain_dn)
1230 m["msDS-Behavior-Version"] = ldb.MessageElement(
1231 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1232 "msDS-Behavior-Version")
1236 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup")
1237 + ",CN=Partitions,%s" % samdb.get_config_basedn())
1238 m["msDS-Behavior-Version"] = ldb.MessageElement(
1239 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1240 "msDS-Behavior-Version")
1243 except ldb.LdbError as e2:
1244 (enum, emsg) = e2.args
1245 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1248 level_domain = new_level_domain
1249 msgs.append("Domain function level changed!")
1251 if forest_level is not None:
1252 if forest_level == "2003":
1253 new_level_forest = DS_DOMAIN_FUNCTION_2003
1254 elif forest_level == "2008":
1255 new_level_forest = DS_DOMAIN_FUNCTION_2008
1256 elif forest_level == "2008_R2":
1257 new_level_forest = DS_DOMAIN_FUNCTION_2008_R2
1258 elif forest_level == "2012":
1259 new_level_forest = DS_DOMAIN_FUNCTION_2012
1260 elif forest_level == "2012_R2":
1261 new_level_forest = DS_DOMAIN_FUNCTION_2012_R2
1263 if new_level_forest <= level_forest:
1264 raise CommandError("Forest function level can't be smaller than or equal to the actual one!")
1265 if new_level_forest > level_domain:
1266 raise CommandError("Forest function level can't be higher than the domain function level(s). Please raise it/them first!")
1269 m.dn = ldb.Dn(samdb, "CN=Partitions,%s" % samdb.get_config_basedn())
1270 m["msDS-Behavior-Version"] = ldb.MessageElement(
1271 str(new_level_forest), ldb.FLAG_MOD_REPLACE,
1272 "msDS-Behavior-Version")
1274 msgs.append("Forest function level changed!")
1275 msgs.append("All changes applied successfully!")
1276 self.message("\n".join(msgs))
1278 raise CommandError("invalid argument: '%s' (choose from 'show', 'raise')" % subcommand)
1281 class cmd_domain_passwordsettings_show(Command):
1282 """Display current password settings for the domain."""
1284 synopsis = "%prog [options]"
1286 takes_optiongroups = {
1287 "sambaopts": options.SambaOptions,
1288 "versionopts": options.VersionOptions,
1289 "credopts": options.CredentialsOptions,
1293 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1294 metavar="URL", dest="H"),
1297 def run(self, H=None, credopts=None, sambaopts=None, versionopts=None):
1298 lp = sambaopts.get_loadparm()
1299 creds = credopts.get_credentials(lp)
1301 samdb = SamDB(url=H, session_info=system_session(),
1302 credentials=creds, lp=lp)
1304 domain_dn = samdb.domain_dn()
1305 res = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1306 attrs=["pwdProperties", "pwdHistoryLength", "minPwdLength",
1307 "minPwdAge", "maxPwdAge", "lockoutDuration", "lockoutThreshold",
1308 "lockOutObservationWindow"])
1309 assert(len(res) == 1)
1311 pwd_props = int(res[0]["pwdProperties"][0])
1312 pwd_hist_len = int(res[0]["pwdHistoryLength"][0])
1313 cur_min_pwd_len = int(res[0]["minPwdLength"][0])
1315 cur_min_pwd_age = int(abs(int(res[0]["minPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1316 if int(res[0]["maxPwdAge"][0]) == -0x8000000000000000:
1319 cur_max_pwd_age = int(abs(int(res[0]["maxPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1320 cur_account_lockout_threshold = int(res[0]["lockoutThreshold"][0])
1322 if int(res[0]["lockoutDuration"][0]) == -0x8000000000000000:
1323 cur_account_lockout_duration = 0
1325 cur_account_lockout_duration = abs(int(res[0]["lockoutDuration"][0])) / (1e7 * 60)
1326 cur_reset_account_lockout_after = abs(int(res[0]["lockOutObservationWindow"][0])) / (1e7 * 60)
1327 except Exception as e:
1328 raise CommandError("Could not retrieve password properties!", e)
1330 self.message("Password informations for domain '%s'" % domain_dn)
1332 if pwd_props & DOMAIN_PASSWORD_COMPLEX != 0:
1333 self.message("Password complexity: on")
1335 self.message("Password complexity: off")
1336 if pwd_props & DOMAIN_PASSWORD_STORE_CLEARTEXT != 0:
1337 self.message("Store plaintext passwords: on")
1339 self.message("Store plaintext passwords: off")
1340 self.message("Password history length: %d" % pwd_hist_len)
1341 self.message("Minimum password length: %d" % cur_min_pwd_len)
1342 self.message("Minimum password age (days): %d" % cur_min_pwd_age)
1343 self.message("Maximum password age (days): %d" % cur_max_pwd_age)
1344 self.message("Account lockout duration (mins): %d" % cur_account_lockout_duration)
1345 self.message("Account lockout threshold (attempts): %d" % cur_account_lockout_threshold)
1346 self.message("Reset account lockout after (mins): %d" % cur_reset_account_lockout_after)
1349 class cmd_domain_passwordsettings_set(Command):
1350 """Set password settings.
1352 Password complexity, password lockout policy, history length,
1353 minimum password length, the minimum and maximum password age) on
1354 a Samba AD DC server.
1356 Use against a Windows DC is possible, but group policy will override it.
1359 synopsis = "%prog <options> [options]"
1361 takes_optiongroups = {
1362 "sambaopts": options.SambaOptions,
1363 "versionopts": options.VersionOptions,
1364 "credopts": options.CredentialsOptions,
1368 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1369 metavar="URL", dest="H"),
1370 Option("-q", "--quiet", help="Be quiet", action="store_true"), # unused
1371 Option("--complexity", type="choice", choices=["on", "off", "default"],
1372 help="The password complexity (on | off | default). Default is 'on'"),
1373 Option("--store-plaintext", type="choice", choices=["on", "off", "default"],
1374 help="Store plaintext passwords where account have 'store passwords with reversible encryption' set (on | off | default). Default is 'off'"),
1375 Option("--history-length",
1376 help="The password history length (<integer> | default). Default is 24.", type=str),
1377 Option("--min-pwd-length",
1378 help="The minimum password length (<integer> | default). Default is 7.", type=str),
1379 Option("--min-pwd-age",
1380 help="The minimum password age (<integer in days> | default). Default is 1.", type=str),
1381 Option("--max-pwd-age",
1382 help="The maximum password age (<integer in days> | default). Default is 43.", type=str),
1383 Option("--account-lockout-duration",
1384 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),
1385 Option("--account-lockout-threshold",
1386 help="The number of bad password attempts allowed before locking out the account (<integer> | default). Default is 0 (never lock out).", type=str),
1387 Option("--reset-account-lockout-after",
1388 help="After this time is elapsed, the recorded number of attempts restarts from zero (<integer> | default). Default is 30.", type=str),
1391 def run(self, H=None, min_pwd_age=None, max_pwd_age=None,
1392 quiet=False, complexity=None, store_plaintext=None, history_length=None,
1393 min_pwd_length=None, account_lockout_duration=None, account_lockout_threshold=None,
1394 reset_account_lockout_after=None, credopts=None, sambaopts=None,
1396 lp = sambaopts.get_loadparm()
1397 creds = credopts.get_credentials(lp)
1399 samdb = SamDB(url=H, session_info=system_session(),
1400 credentials=creds, lp=lp)
1402 domain_dn = samdb.domain_dn()
1405 m.dn = ldb.Dn(samdb, domain_dn)
1406 pwd_props = int(samdb.get_pwdProperties())
1408 if complexity is not None:
1409 if complexity == "on" or complexity == "default":
1410 pwd_props = pwd_props | DOMAIN_PASSWORD_COMPLEX
1411 msgs.append("Password complexity activated!")
1412 elif complexity == "off":
1413 pwd_props = pwd_props & (~DOMAIN_PASSWORD_COMPLEX)
1414 msgs.append("Password complexity deactivated!")
1416 if store_plaintext is not None:
1417 if store_plaintext == "on" or store_plaintext == "default":
1418 pwd_props = pwd_props | DOMAIN_PASSWORD_STORE_CLEARTEXT
1419 msgs.append("Plaintext password storage for changed passwords activated!")
1420 elif store_plaintext == "off":
1421 pwd_props = pwd_props & (~DOMAIN_PASSWORD_STORE_CLEARTEXT)
1422 msgs.append("Plaintext password storage for changed passwords deactivated!")
1424 if complexity is not None or store_plaintext is not None:
1425 m["pwdProperties"] = ldb.MessageElement(str(pwd_props),
1426 ldb.FLAG_MOD_REPLACE, "pwdProperties")
1428 if history_length is not None:
1429 if history_length == "default":
1432 pwd_hist_len = int(history_length)
1434 if pwd_hist_len < 0 or pwd_hist_len > 24:
1435 raise CommandError("Password history length must be in the range of 0 to 24!")
1437 m["pwdHistoryLength"] = ldb.MessageElement(str(pwd_hist_len),
1438 ldb.FLAG_MOD_REPLACE, "pwdHistoryLength")
1439 msgs.append("Password history length changed!")
1441 if min_pwd_length is not None:
1442 if min_pwd_length == "default":
1445 min_pwd_len = int(min_pwd_length)
1447 if min_pwd_len < 0 or min_pwd_len > 14:
1448 raise CommandError("Minimum password length must be in the range of 0 to 14!")
1450 m["minPwdLength"] = ldb.MessageElement(str(min_pwd_len),
1451 ldb.FLAG_MOD_REPLACE, "minPwdLength")
1452 msgs.append("Minimum password length changed!")
1454 if min_pwd_age is not None:
1455 if min_pwd_age == "default":
1458 min_pwd_age = int(min_pwd_age)
1460 if min_pwd_age < 0 or min_pwd_age > 998:
1461 raise CommandError("Minimum password age must be in the range of 0 to 998!")
1464 min_pwd_age_ticks = -int(min_pwd_age * (24 * 60 * 60 * 1e7))
1466 m["minPwdAge"] = ldb.MessageElement(str(min_pwd_age_ticks),
1467 ldb.FLAG_MOD_REPLACE, "minPwdAge")
1468 msgs.append("Minimum password age changed!")
1470 if max_pwd_age is not None:
1471 if max_pwd_age == "default":
1474 max_pwd_age = int(max_pwd_age)
1476 if max_pwd_age < 0 or max_pwd_age > 999:
1477 raise CommandError("Maximum password age must be in the range of 0 to 999!")
1480 if max_pwd_age == 0:
1481 max_pwd_age_ticks = -0x8000000000000000
1483 max_pwd_age_ticks = -int(max_pwd_age * (24 * 60 * 60 * 1e7))
1485 m["maxPwdAge"] = ldb.MessageElement(str(max_pwd_age_ticks),
1486 ldb.FLAG_MOD_REPLACE, "maxPwdAge")
1487 msgs.append("Maximum password age changed!")
1489 if account_lockout_duration is not None:
1490 if account_lockout_duration == "default":
1491 account_lockout_duration = 30
1493 account_lockout_duration = int(account_lockout_duration)
1495 if account_lockout_duration < 0 or account_lockout_duration > 99999:
1496 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1499 if account_lockout_duration == 0:
1500 account_lockout_duration_ticks = -0x8000000000000000
1502 account_lockout_duration_ticks = -int(account_lockout_duration * (60 * 1e7))
1504 m["lockoutDuration"] = ldb.MessageElement(str(account_lockout_duration_ticks),
1505 ldb.FLAG_MOD_REPLACE, "lockoutDuration")
1506 msgs.append("Account lockout duration changed!")
1508 if account_lockout_threshold is not None:
1509 if account_lockout_threshold == "default":
1510 account_lockout_threshold = 0
1512 account_lockout_threshold = int(account_lockout_threshold)
1514 m["lockoutThreshold"] = ldb.MessageElement(str(account_lockout_threshold),
1515 ldb.FLAG_MOD_REPLACE, "lockoutThreshold")
1516 msgs.append("Account lockout threshold changed!")
1518 if reset_account_lockout_after is not None:
1519 if reset_account_lockout_after == "default":
1520 reset_account_lockout_after = 30
1522 reset_account_lockout_after = int(reset_account_lockout_after)
1524 if reset_account_lockout_after < 0 or reset_account_lockout_after > 99999:
1525 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1528 if reset_account_lockout_after == 0:
1529 reset_account_lockout_after_ticks = -0x8000000000000000
1531 reset_account_lockout_after_ticks = -int(reset_account_lockout_after * (60 * 1e7))
1533 m["lockOutObservationWindow"] = ldb.MessageElement(str(reset_account_lockout_after_ticks),
1534 ldb.FLAG_MOD_REPLACE, "lockOutObservationWindow")
1535 msgs.append("Duration to reset account lockout after changed!")
1537 if max_pwd_age and max_pwd_age > 0 and min_pwd_age >= max_pwd_age:
1538 raise CommandError("Maximum password age (%d) must be greater than minimum password age (%d)!" % (max_pwd_age, min_pwd_age))
1541 raise CommandError("You must specify at least one option to set. Try --help")
1543 msgs.append("All changes applied successfully!")
1544 self.message("\n".join(msgs))
1547 class cmd_domain_passwordsettings(SuperCommand):
1548 """Manage password policy settings."""
1551 subcommands["pso"] = cmd_domain_passwordsettings_pso()
1552 subcommands["show"] = cmd_domain_passwordsettings_show()
1553 subcommands["set"] = cmd_domain_passwordsettings_set()
1556 class cmd_domain_classicupgrade(Command):
1557 """Upgrade from Samba classic (NT4-like) database to Samba AD DC database.
1559 Specify either a directory with all Samba classic DC databases and state files (with --dbdir) or
1560 the testparm utility from your classic installation (with --testparm).
1563 synopsis = "%prog [options] <classic_smb_conf>"
1565 takes_optiongroups = {
1566 "sambaopts": options.SambaOptions,
1567 "versionopts": options.VersionOptions
1571 Option("--dbdir", type="string", metavar="DIR",
1572 help="Path to samba classic DC database directory"),
1573 Option("--testparm", type="string", metavar="PATH",
1574 help="Path to samba classic DC testparm utility from the previous installation. This allows the default paths of the previous installation to be followed"),
1575 Option("--targetdir", type="string", metavar="DIR",
1576 help="Path prefix where the new Samba 4.0 AD domain should be initialised"),
1577 Option("-q", "--quiet", help="Be quiet", action="store_true"),
1578 Option("-v", "--verbose", help="Be verbose", action="store_true"),
1579 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
1580 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
1581 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
1582 "BIND9_FLATFILE uses bind9 text database to store zone information, "
1583 "BIND9_DLZ uses samba4 AD to store zone information, "
1584 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
1585 default="SAMBA_INTERNAL")
1589 Option("--use-xattrs", type="choice", choices=["yes", "no", "auto"],
1590 metavar="[yes|no|auto]",
1591 help="Define if we should use the native fs capabilities or a tdb file for "
1592 "storing attributes likes ntacl when --use-ntvfs is set. "
1593 "auto tries to make an inteligent guess based on the user rights and system capabilities",
1596 if samba.is_ntvfs_fileserver_built():
1597 takes_options.extend(common_ntvfs_options)
1598 takes_options.extend(ntvfs_options)
1600 takes_args = ["smbconf"]
1602 def run(self, smbconf=None, targetdir=None, dbdir=None, testparm=None,
1603 quiet=False, verbose=False, use_xattrs="auto", sambaopts=None, versionopts=None,
1604 dns_backend=None, use_ntvfs=False):
1606 if not os.path.exists(smbconf):
1607 raise CommandError("File %s does not exist" % smbconf)
1609 if testparm and not os.path.exists(testparm):
1610 raise CommandError("Testparm utility %s does not exist" % testparm)
1612 if dbdir and not os.path.exists(dbdir):
1613 raise CommandError("Directory %s does not exist" % dbdir)
1615 if not dbdir and not testparm:
1616 raise CommandError("Please specify either dbdir or testparm")
1618 logger = self.get_logger()
1620 logger.setLevel(logging.DEBUG)
1622 logger.setLevel(logging.WARNING)
1624 logger.setLevel(logging.INFO)
1626 if dbdir and testparm:
1627 logger.warning("both dbdir and testparm specified, ignoring dbdir.")
1630 lp = sambaopts.get_loadparm()
1632 s3conf = s3param.get_context()
1635 s3conf.set("realm", sambaopts.realm)
1637 if targetdir is not None:
1638 if not os.path.isdir(targetdir):
1642 if use_xattrs == "yes":
1644 elif use_xattrs == "auto" and use_ntvfs == False:
1646 elif use_ntvfs == False:
1647 raise CommandError("--use-xattrs=no requires --use-ntvfs (not supported for production use). "
1648 "Please re-run with --use-xattrs omitted.")
1649 elif use_xattrs == "auto" and not s3conf.get("posix:eadb"):
1651 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
1653 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
1656 samba.ntacls.setntacl(lp, tmpfile.name,
1657 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
1660 # FIXME: Don't catch all exceptions here
1661 logger.info("You are not root or your system does not support xattr, using tdb backend for attributes. "
1662 "If you intend to use this provision in production, rerun the script as root on a system supporting xattrs.")
1666 # Set correct default values from dbdir or testparm
1669 paths["state directory"] = dbdir
1670 paths["private dir"] = dbdir
1671 paths["lock directory"] = dbdir
1672 paths["smb passwd file"] = dbdir + "/smbpasswd"
1674 paths["state directory"] = get_testparm_var(testparm, smbconf, "state directory")
1675 paths["private dir"] = get_testparm_var(testparm, smbconf, "private dir")
1676 paths["smb passwd file"] = get_testparm_var(testparm, smbconf, "smb passwd file")
1677 paths["lock directory"] = get_testparm_var(testparm, smbconf, "lock directory")
1678 # "testparm" from Samba 3 < 3.4.x is not aware of the parameter
1679 # "state directory", instead make use of "lock directory"
1680 if len(paths["state directory"]) == 0:
1681 paths["state directory"] = paths["lock directory"]
1684 s3conf.set(p, paths[p])
1686 # load smb.conf parameters
1687 logger.info("Reading smb.conf")
1688 s3conf.load(smbconf)
1689 samba3 = Samba3(smbconf, s3conf)
1691 logger.info("Provisioning")
1692 upgrade_from_samba3(samba3, logger, targetdir, session_info=system_session(),
1693 useeadb=eadb, dns_backend=dns_backend, use_ntvfs=use_ntvfs)
1696 class cmd_domain_samba3upgrade(cmd_domain_classicupgrade):
1697 __doc__ = cmd_domain_classicupgrade.__doc__
1699 # This command is present for backwards compatibility only,
1700 # and should not be shown.
1705 class LocalDCCredentialsOptions(options.CredentialsOptions):
1706 def __init__(self, parser):
1707 options.CredentialsOptions.__init__(self, parser, special_name="local-dc")
1710 class DomainTrustCommand(Command):
1711 """List domain trusts."""
1714 Command.__init__(self)
1715 self.local_lp = None
1717 self.local_server = None
1718 self.local_binding_string = None
1719 self.local_creds = None
1721 self.remote_server = None
1722 self.remote_binding_string = None
1723 self.remote_creds = None
1725 def _uint32(self, v):
1726 return ctypes.c_uint32(v).value
1728 def check_runtime_error(self, runtime, val):
1732 err32 = self._uint32(runtime.args[0])
1738 class LocalRuntimeError(CommandError):
1739 def __init__(exception_self, self, runtime, message):
1740 err32 = self._uint32(runtime.args[0])
1741 errstr = runtime.args[1]
1742 msg = "LOCAL_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1743 self.local_server, message, err32, errstr)
1744 CommandError.__init__(exception_self, msg)
1746 class RemoteRuntimeError(CommandError):
1747 def __init__(exception_self, self, runtime, message):
1748 err32 = self._uint32(runtime.args[0])
1749 errstr = runtime.args[1]
1750 msg = "REMOTE_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1751 self.remote_server, message, err32, errstr)
1752 CommandError.__init__(exception_self, msg)
1754 class LocalLdbError(CommandError):
1755 def __init__(exception_self, self, ldb_error, message):
1756 errval = ldb_error.args[0]
1757 errstr = ldb_error.args[1]
1758 msg = "LOCAL_DC[%s]: %s - ERROR(%d) - %s" % (
1759 self.local_server, message, errval, errstr)
1760 CommandError.__init__(exception_self, msg)
1762 def setup_local_server(self, sambaopts, localdcopts):
1763 if self.local_server is not None:
1764 return self.local_server
1766 lp = sambaopts.get_loadparm()
1768 local_server = localdcopts.ipaddress
1769 if local_server is None:
1770 server_role = lp.server_role()
1771 if server_role != "ROLE_ACTIVE_DIRECTORY_DC":
1772 raise CommandError("Invalid server_role %s" % (server_role))
1773 local_server = lp.get('netbios name')
1774 local_transport = "ncalrpc"
1775 local_binding_options = ""
1776 local_binding_options += ",auth_type=ncalrpc_as_system"
1777 local_ldap_url = None
1780 local_transport = "ncacn_np"
1781 local_binding_options = ""
1782 local_ldap_url = "ldap://%s" % local_server
1783 local_creds = localdcopts.get_credentials(lp)
1787 self.local_server = local_server
1788 self.local_binding_string = "%s:%s[%s]" % (local_transport, local_server, local_binding_options)
1789 self.local_ldap_url = local_ldap_url
1790 self.local_creds = local_creds
1791 return self.local_server
1793 def new_local_lsa_connection(self):
1794 return lsa.lsarpc(self.local_binding_string, self.local_lp, self.local_creds)
1796 def new_local_netlogon_connection(self):
1797 return netlogon.netlogon(self.local_binding_string, self.local_lp, self.local_creds)
1799 def new_local_ldap_connection(self):
1800 return SamDB(url=self.local_ldap_url,
1801 session_info=system_session(),
1802 credentials=self.local_creds,
1805 def setup_remote_server(self, credopts, domain,
1807 require_writable=True):
1810 assert require_writable
1812 if self.remote_server is not None:
1813 return self.remote_server
1815 self.remote_server = "__unknown__remote_server__.%s" % domain
1816 assert self.local_server is not None
1818 remote_creds = credopts.get_credentials(self.local_lp)
1819 remote_server = credopts.ipaddress
1820 remote_binding_options = ""
1822 # TODO: we should also support NT4 domains
1823 # we could use local_netlogon.netr_DsRGetDCNameEx2() with the remote domain name
1824 # and delegate NBT or CLDAP to the local netlogon server
1826 remote_net = Net(remote_creds, self.local_lp, server=remote_server)
1827 remote_flags = nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS
1828 if require_writable:
1829 remote_flags |= nbt.NBT_SERVER_WRITABLE
1831 remote_flags |= nbt.NBT_SERVER_PDC
1832 remote_info = remote_net.finddc(flags=remote_flags, domain=domain, address=remote_server)
1833 except NTSTATUSError as error:
1834 raise CommandError("Failed to find a writeable DC for domain '%s': %s" %
1837 raise CommandError("Failed to find a writeable DC for domain '%s'" % domain)
1839 nbt.NBT_SERVER_PDC: "PDC",
1840 nbt.NBT_SERVER_GC: "GC",
1841 nbt.NBT_SERVER_LDAP: "LDAP",
1842 nbt.NBT_SERVER_DS: "DS",
1843 nbt.NBT_SERVER_KDC: "KDC",
1844 nbt.NBT_SERVER_TIMESERV: "TIMESERV",
1845 nbt.NBT_SERVER_CLOSEST: "CLOSEST",
1846 nbt.NBT_SERVER_WRITABLE: "WRITABLE",
1847 nbt.NBT_SERVER_GOOD_TIMESERV: "GOOD_TIMESERV",
1848 nbt.NBT_SERVER_NDNC: "NDNC",
1849 nbt.NBT_SERVER_SELECT_SECRET_DOMAIN_6: "SELECT_SECRET_DOMAIN_6",
1850 nbt.NBT_SERVER_FULL_SECRET_DOMAIN_6: "FULL_SECRET_DOMAIN_6",
1851 nbt.NBT_SERVER_ADS_WEB_SERVICE: "ADS_WEB_SERVICE",
1852 nbt.NBT_SERVER_DS_8: "DS_8",
1853 nbt.NBT_SERVER_HAS_DNS_NAME: "HAS_DNS_NAME",
1854 nbt.NBT_SERVER_IS_DEFAULT_NC: "IS_DEFAULT_NC",
1855 nbt.NBT_SERVER_FOREST_ROOT: "FOREST_ROOT",
1857 server_type_string = self.generic_bitmap_to_string(flag_map,
1858 remote_info.server_type, names_only=True)
1859 self.outf.write("RemoteDC Netbios[%s] DNS[%s] ServerType[%s]\n" % (
1860 remote_info.pdc_name,
1861 remote_info.pdc_dns_name,
1862 server_type_string))
1864 self.remote_server = remote_info.pdc_dns_name
1865 self.remote_binding_string = "ncacn_np:%s[%s]" % (self.remote_server, remote_binding_options)
1866 self.remote_creds = remote_creds
1867 return self.remote_server
1869 def new_remote_lsa_connection(self):
1870 return lsa.lsarpc(self.remote_binding_string, self.local_lp, self.remote_creds)
1872 def new_remote_netlogon_connection(self):
1873 return netlogon.netlogon(self.remote_binding_string, self.local_lp, self.remote_creds)
1875 def get_lsa_info(self, conn, policy_access):
1876 objectAttr = lsa.ObjectAttribute()
1877 objectAttr.sec_qos = lsa.QosInfo()
1879 policy = conn.OpenPolicy2(''.decode('utf-8'),
1880 objectAttr, policy_access)
1882 info = conn.QueryInfoPolicy2(policy, lsa.LSA_POLICY_INFO_DNS)
1884 return (policy, info)
1886 def get_netlogon_dc_unc(self, conn, server, domain):
1888 info = conn.netr_DsRGetDCNameEx2(server,
1889 None, 0, None, None, None,
1890 netlogon.DS_RETURN_DNS_NAME)
1892 except RuntimeError:
1893 return conn.netr_GetDcName(server, domain)
1895 def get_netlogon_dc_info(self, conn, server):
1896 info = conn.netr_DsRGetDCNameEx2(server,
1897 None, 0, None, None, None,
1898 netlogon.DS_RETURN_DNS_NAME)
1901 def netr_DomainTrust_to_name(self, t):
1902 if t.trust_type == lsa.LSA_TRUST_TYPE_DOWNLEVEL:
1903 return t.netbios_name
1907 def netr_DomainTrust_to_type(self, a, t):
1909 primary_parent = None
1911 if _t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
1913 if not _t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1914 primary_parent = a[_t.parent_index]
1917 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1918 if t is primary_parent:
1921 if t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1924 parent = a[t.parent_index]
1925 if parent is primary:
1930 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1935 def netr_DomainTrust_to_transitive(self, t):
1936 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1939 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE:
1942 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1947 def netr_DomainTrust_to_direction(self, t):
1948 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND and \
1949 t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1952 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND:
1955 if t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1960 def generic_enum_to_string(self, e_dict, v, names_only=False):
1964 v32 = self._uint32(v)
1965 w = "__unknown__%08X__" % v32
1967 r = "0x%x (%s)" % (v, w)
1970 def generic_bitmap_to_string(self, b_dict, v, names_only=False):
1975 for b in sorted(b_dict.keys()):
1982 c32 = self._uint32(c)
1983 s += ["__unknown_%08X__" % c32]
1988 r = "0x%x (%s)" % (v, w)
1991 def trustType_string(self, v):
1993 lsa.LSA_TRUST_TYPE_DOWNLEVEL: "DOWNLEVEL",
1994 lsa.LSA_TRUST_TYPE_UPLEVEL: "UPLEVEL",
1995 lsa.LSA_TRUST_TYPE_MIT: "MIT",
1996 lsa.LSA_TRUST_TYPE_DCE: "DCE",
1998 return self.generic_enum_to_string(types, v)
2000 def trustDirection_string(self, v):
2002 lsa.LSA_TRUST_DIRECTION_INBOUND |
2003 lsa.LSA_TRUST_DIRECTION_OUTBOUND: "BOTH",
2004 lsa.LSA_TRUST_DIRECTION_INBOUND: "INBOUND",
2005 lsa.LSA_TRUST_DIRECTION_OUTBOUND: "OUTBOUND",
2007 return self.generic_enum_to_string(directions, v)
2009 def trustAttributes_string(self, v):
2011 lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE: "NON_TRANSITIVE",
2012 lsa.LSA_TRUST_ATTRIBUTE_UPLEVEL_ONLY: "UPLEVEL_ONLY",
2013 lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN: "QUARANTINED_DOMAIN",
2014 lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE: "FOREST_TRANSITIVE",
2015 lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION: "CROSS_ORGANIZATION",
2016 lsa.LSA_TRUST_ATTRIBUTE_WITHIN_FOREST: "WITHIN_FOREST",
2017 lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL: "TREAT_AS_EXTERNAL",
2018 lsa.LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION: "USES_RC4_ENCRYPTION",
2020 return self.generic_bitmap_to_string(attributes, v)
2022 def kerb_EncTypes_string(self, v):
2024 security.KERB_ENCTYPE_DES_CBC_CRC: "DES_CBC_CRC",
2025 security.KERB_ENCTYPE_DES_CBC_MD5: "DES_CBC_MD5",
2026 security.KERB_ENCTYPE_RC4_HMAC_MD5: "RC4_HMAC_MD5",
2027 security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96: "AES128_CTS_HMAC_SHA1_96",
2028 security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96: "AES256_CTS_HMAC_SHA1_96",
2029 security.KERB_ENCTYPE_FAST_SUPPORTED: "FAST_SUPPORTED",
2030 security.KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED: "COMPOUND_IDENTITY_SUPPORTED",
2031 security.KERB_ENCTYPE_CLAIMS_SUPPORTED: "CLAIMS_SUPPORTED",
2032 security.KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED: "RESOURCE_SID_COMPRESSION_DISABLED",
2034 return self.generic_bitmap_to_string(enctypes, v)
2036 def entry_tln_status(self, e_flags, ):
2038 return "Status[Enabled]"
2041 lsa.LSA_TLN_DISABLED_NEW: "Disabled-New",
2042 lsa.LSA_TLN_DISABLED_ADMIN: "Disabled",
2043 lsa.LSA_TLN_DISABLED_CONFLICT: "Disabled-Conflicting",
2045 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
2047 def entry_dom_status(self, e_flags):
2049 return "Status[Enabled]"
2052 lsa.LSA_SID_DISABLED_ADMIN: "Disabled-SID",
2053 lsa.LSA_SID_DISABLED_CONFLICT: "Disabled-SID-Conflicting",
2054 lsa.LSA_NB_DISABLED_ADMIN: "Disabled-NB",
2055 lsa.LSA_NB_DISABLED_CONFLICT: "Disabled-NB-Conflicting",
2057 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
2059 def write_forest_trust_info(self, fti, tln=None, collisions=None):
2061 tln_string = " TDO[%s]" % tln
2065 self.outf.write("Namespaces[%d]%s:\n" % (
2066 len(fti.entries), tln_string))
2068 for i, e in enumerate(fti.entries):
2071 collision_string = ""
2073 if collisions is not None:
2074 for c in collisions.entries:
2078 collision_string = " Collision[%s]" % (c.name.string)
2080 d = e.forest_trust_data
2081 if e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
2082 self.outf.write("TLN: %-32s DNS[*.%s]%s\n" % (
2083 self.entry_tln_status(flags),
2084 d.string, collision_string))
2085 elif e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
2086 self.outf.write("TLN_EX: %-29s DNS[*.%s]\n" % (
2088 elif e.type == lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
2089 self.outf.write("DOM: %-32s DNS[%s] Netbios[%s] SID[%s]%s\n" % (
2090 self.entry_dom_status(flags),
2091 d.dns_domain_name.string,
2092 d.netbios_domain_name.string,
2093 d.domain_sid, collision_string))
2097 class cmd_domain_trust_list(DomainTrustCommand):
2098 """List domain trusts."""
2100 synopsis = "%prog [options]"
2102 takes_optiongroups = {
2103 "sambaopts": options.SambaOptions,
2104 "versionopts": options.VersionOptions,
2105 "localdcopts": LocalDCCredentialsOptions,
2111 def run(self, sambaopts=None, versionopts=None, localdcopts=None):
2113 local_server = self.setup_local_server(sambaopts, localdcopts)
2115 local_netlogon = self.new_local_netlogon_connection()
2116 except RuntimeError as error:
2117 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2120 local_netlogon_trusts = \
2121 local_netlogon.netr_DsrEnumerateDomainTrusts(local_server,
2122 netlogon.NETR_TRUST_FLAG_IN_FOREST |
2123 netlogon.NETR_TRUST_FLAG_OUTBOUND |
2124 netlogon.NETR_TRUST_FLAG_INBOUND)
2125 except RuntimeError as error:
2126 if self.check_runtime_error(error, werror.WERR_RPC_S_PROCNUM_OUT_OF_RANGE):
2127 # TODO: we could implement a fallback to lsa.EnumTrustDom()
2128 raise CommandError("LOCAL_DC[%s]: netr_DsrEnumerateDomainTrusts not supported." % (
2130 raise self.LocalRuntimeError(self, error, "netr_DsrEnumerateDomainTrusts failed")
2132 a = local_netlogon_trusts.array
2134 if t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
2136 self.outf.write("%-14s %-15s %-19s %s\n" % (
2137 "Type[%s]" % self.netr_DomainTrust_to_type(a, t),
2138 "Transitive[%s]" % self.netr_DomainTrust_to_transitive(t),
2139 "Direction[%s]" % self.netr_DomainTrust_to_direction(t),
2140 "Name[%s]" % self.netr_DomainTrust_to_name(t)))
2144 class cmd_domain_trust_show(DomainTrustCommand):
2145 """Show trusted domain details."""
2147 synopsis = "%prog NAME [options]"
2149 takes_optiongroups = {
2150 "sambaopts": options.SambaOptions,
2151 "versionopts": options.VersionOptions,
2152 "localdcopts": LocalDCCredentialsOptions,
2158 takes_args = ["domain"]
2160 def run(self, domain, sambaopts=None, versionopts=None, localdcopts=None):
2162 local_server = self.setup_local_server(sambaopts, localdcopts)
2164 local_lsa = self.new_local_lsa_connection()
2165 except RuntimeError as error:
2166 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2169 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2170 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2171 except RuntimeError as error:
2172 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2174 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2175 local_lsa_info.name.string,
2176 local_lsa_info.dns_domain.string,
2177 local_lsa_info.sid))
2179 lsaString = lsa.String()
2180 lsaString.string = domain
2183 local_lsa.QueryTrustedDomainInfoByName(local_policy,
2185 lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2186 local_tdo_info = local_tdo_full.info_ex
2187 local_tdo_posix = local_tdo_full.posix_offset
2188 except NTSTATUSError as error:
2189 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2190 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2192 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(FULL_INFO) failed")
2195 local_tdo_enctypes = \
2196 local_lsa.QueryTrustedDomainInfoByName(local_policy,
2198 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES)
2199 except NTSTATUSError as error:
2200 if self.check_runtime_error(error, ntstatus.NT_STATUS_INVALID_PARAMETER):
2202 if self.check_runtime_error(error, ntstatus.NT_STATUS_INVALID_INFO_CLASS):
2205 if error is not None:
2206 raise self.LocalRuntimeError(self, error,
2207 "QueryTrustedDomainInfoByName(SUPPORTED_ENCRYPTION_TYPES) failed")
2209 local_tdo_enctypes = lsa.TrustDomainInfoSupportedEncTypes()
2210 local_tdo_enctypes.enc_types = 0
2213 local_tdo_forest = None
2214 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2215 local_tdo_forest = \
2216 local_lsa.lsaRQueryForestTrustInformation(local_policy,
2218 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
2219 except RuntimeError as error:
2220 if self.check_runtime_error(error, ntstatus.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
2222 if self.check_runtime_error(error, ntstatus.NT_STATUS_NOT_FOUND):
2224 if error is not None:
2225 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation failed")
2227 local_tdo_forest = lsa.ForestTrustInformation()
2228 local_tdo_forest.count = 0
2229 local_tdo_forest.entries = []
2231 self.outf.write("TrustedDomain:\n\n");
2232 self.outf.write("NetbiosName: %s\n" % local_tdo_info.netbios_name.string)
2233 if local_tdo_info.netbios_name.string != local_tdo_info.domain_name.string:
2234 self.outf.write("DnsName: %s\n" % local_tdo_info.domain_name.string)
2235 self.outf.write("SID: %s\n" % local_tdo_info.sid)
2236 self.outf.write("Type: %s\n" % self.trustType_string(local_tdo_info.trust_type))
2237 self.outf.write("Direction: %s\n" % self.trustDirection_string(local_tdo_info.trust_direction))
2238 self.outf.write("Attributes: %s\n" % self.trustAttributes_string(local_tdo_info.trust_attributes))
2239 posix_offset_u32 = ctypes.c_uint32(local_tdo_posix.posix_offset).value
2240 posix_offset_i32 = ctypes.c_int32(local_tdo_posix.posix_offset).value
2241 self.outf.write("PosixOffset: 0x%08X (%d)\n" % (posix_offset_u32, posix_offset_i32))
2242 self.outf.write("kerb_EncTypes: %s\n" % self.kerb_EncTypes_string(local_tdo_enctypes.enc_types))
2244 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2245 self.write_forest_trust_info(local_tdo_forest,
2246 tln=local_tdo_info.domain_name.string)
2251 class cmd_domain_trust_create(DomainTrustCommand):
2252 """Create a domain or forest trust."""
2254 synopsis = "%prog DOMAIN [options]"
2256 takes_optiongroups = {
2257 "sambaopts": options.SambaOptions,
2258 "versionopts": options.VersionOptions,
2259 "credopts": options.CredentialsOptions,
2260 "localdcopts": LocalDCCredentialsOptions,
2264 Option("--type", type="choice", metavar="TYPE",
2265 choices=["external", "forest"],
2266 help="The type of the trust: 'external' or 'forest'.",
2268 default="external"),
2269 Option("--direction", type="choice", metavar="DIRECTION",
2270 choices=["incoming", "outgoing", "both"],
2271 help="The trust direction: 'incoming', 'outgoing' or 'both'.",
2272 dest='trust_direction',
2274 Option("--create-location", type="choice", metavar="LOCATION",
2275 choices=["local", "both"],
2276 help="Where to create the trusted domain object: 'local' or 'both'.",
2277 dest='create_location',
2279 Option("--cross-organisation", action="store_true",
2280 help="The related domains does not belong to the same organisation.",
2281 dest='cross_organisation',
2283 Option("--quarantined", type="choice", metavar="yes|no",
2284 choices=["yes", "no", None],
2285 help="Special SID filtering rules are applied to the trust. "
2286 "With --type=external the default is yes. "
2287 "With --type=forest the default is no.",
2288 dest='quarantined_arg',
2290 Option("--not-transitive", action="store_true",
2291 help="The forest trust is not transitive.",
2292 dest='not_transitive',
2294 Option("--treat-as-external", action="store_true",
2295 help="The treat the forest trust as external.",
2296 dest='treat_as_external',
2298 Option("--no-aes-keys", action="store_false",
2299 help="The trust uses aes kerberos keys.",
2300 dest='use_aes_keys',
2302 Option("--skip-validation", action="store_false",
2303 help="Skip validation of the trust.",
2308 takes_args = ["domain"]
2310 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2311 trust_type=None, trust_direction=None, create_location=None,
2312 cross_organisation=False, quarantined_arg=None,
2313 not_transitive=False, treat_as_external=False,
2314 use_aes_keys=False, validate=True):
2316 lsaString = lsa.String()
2319 if quarantined_arg is None:
2320 if trust_type == 'external':
2322 elif quarantined_arg == 'yes':
2325 if trust_type != 'forest':
2327 raise CommandError("--not-transitive requires --type=forest")
2328 if treat_as_external:
2329 raise CommandError("--treat-as-external requires --type=forest")
2333 enc_types = lsa.TrustDomainInfoSupportedEncTypes()
2334 enc_types.enc_types = security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96
2335 enc_types.enc_types |= security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96
2337 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2338 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2339 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2341 local_trust_info = lsa.TrustDomainInfoInfoEx()
2342 local_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2343 local_trust_info.trust_direction = 0
2344 if trust_direction == "both":
2345 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2346 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2347 elif trust_direction == "incoming":
2348 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2349 elif trust_direction == "outgoing":
2350 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2351 local_trust_info.trust_attributes = 0
2352 if cross_organisation:
2353 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2355 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2356 if trust_type == "forest":
2357 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2359 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2360 if treat_as_external:
2361 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2363 def get_password(name):
2366 if password is not None and password is not '':
2368 password = getpass("New %s Password: " % name)
2369 passwordverify = getpass("Retype %s Password: " % name)
2370 if not password == passwordverify:
2372 self.outf.write("Sorry, passwords do not match.\n")
2374 incoming_secret = None
2375 outgoing_secret = None
2376 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2377 if create_location == "local":
2378 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2379 incoming_password = get_password("Incoming Trust")
2380 incoming_secret = string_to_byte_array(incoming_password.encode('utf-16-le'))
2381 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2382 outgoing_password = get_password("Outgoing Trust")
2383 outgoing_secret = string_to_byte_array(outgoing_password.encode('utf-16-le'))
2385 remote_trust_info = None
2387 # We use 240 random bytes.
2388 # Windows uses 28 or 240 random bytes. I guess it's
2389 # based on the trust type external vs. forest.
2391 # The initial trust password can be up to 512 bytes
2392 # while the versioned passwords used for periodic updates
2393 # can only be up to 498 bytes, as netr_ServerPasswordSet2()
2394 # needs to pass the NL_PASSWORD_VERSION structure within the
2395 # 512 bytes and a 2 bytes confounder is required.
2397 def random_trust_secret(length):
2398 pw = samba.generate_random_machine_password(length // 2, length // 2)
2399 return string_to_byte_array(pw.encode('utf-16-le'))
2401 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2402 incoming_secret = random_trust_secret(240)
2403 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2404 outgoing_secret = random_trust_secret(240)
2406 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2407 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2409 remote_trust_info = lsa.TrustDomainInfoInfoEx()
2410 remote_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2411 remote_trust_info.trust_direction = 0
2412 if trust_direction == "both":
2413 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2414 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2415 elif trust_direction == "incoming":
2416 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2417 elif trust_direction == "outgoing":
2418 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2419 remote_trust_info.trust_attributes = 0
2420 if cross_organisation:
2421 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2423 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2424 if trust_type == "forest":
2425 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2427 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2428 if treat_as_external:
2429 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2431 local_server = self.setup_local_server(sambaopts, localdcopts)
2433 local_lsa = self.new_local_lsa_connection()
2434 except RuntimeError as error:
2435 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2438 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2439 except RuntimeError as error:
2440 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2442 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2443 local_lsa_info.name.string,
2444 local_lsa_info.dns_domain.string,
2445 local_lsa_info.sid))
2448 remote_server = self.setup_remote_server(credopts, domain)
2449 except RuntimeError as error:
2450 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2453 remote_lsa = self.new_remote_lsa_connection()
2454 except RuntimeError as error:
2455 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2458 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2459 except RuntimeError as error:
2460 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2462 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2463 remote_lsa_info.name.string,
2464 remote_lsa_info.dns_domain.string,
2465 remote_lsa_info.sid))
2467 local_trust_info.domain_name.string = remote_lsa_info.dns_domain.string
2468 local_trust_info.netbios_name.string = remote_lsa_info.name.string
2469 local_trust_info.sid = remote_lsa_info.sid
2471 if remote_trust_info:
2472 remote_trust_info.domain_name.string = local_lsa_info.dns_domain.string
2473 remote_trust_info.netbios_name.string = local_lsa_info.name.string
2474 remote_trust_info.sid = local_lsa_info.sid
2477 lsaString.string = local_trust_info.domain_name.string
2478 local_old_netbios = \
2479 local_lsa.QueryTrustedDomainInfoByName(local_policy,
2481 lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2482 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2483 except NTSTATUSError as error:
2484 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2485 raise self.LocalRuntimeError(self, error,
2486 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2490 lsaString.string = local_trust_info.netbios_name.string
2492 local_lsa.QueryTrustedDomainInfoByName(local_policy,
2494 lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2495 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2496 except NTSTATUSError as error:
2497 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2498 raise self.LocalRuntimeError(self, error,
2499 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2502 if remote_trust_info:
2504 lsaString.string = remote_trust_info.domain_name.string
2505 remote_old_netbios = \
2506 remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2508 lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2509 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2510 except NTSTATUSError as error:
2511 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2512 raise self.RemoteRuntimeError(self, error,
2513 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2517 lsaString.string = remote_trust_info.netbios_name.string
2519 remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2521 lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2522 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2523 except NTSTATUSError as error:
2524 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2525 raise self.RemoteRuntimeError(self, error,
2526 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2530 local_netlogon = self.new_local_netlogon_connection()
2531 except RuntimeError as error:
2532 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2535 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
2536 except RuntimeError as error:
2537 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
2539 if remote_trust_info:
2541 remote_netlogon = self.new_remote_netlogon_connection()
2542 except RuntimeError as error:
2543 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2546 remote_netlogon_dc_unc = self.get_netlogon_dc_unc(remote_netlogon,
2547 remote_server, domain)
2548 except RuntimeError as error:
2549 raise self.RemoteRuntimeError(self, error, "failed to get netlogon dc info")
2551 def generate_AuthInOutBlob(secret, update_time):
2553 blob = drsblobs.trustAuthInOutBlob()
2558 clear = drsblobs.AuthInfoClear()
2559 clear.size = len(secret)
2560 clear.password = secret
2562 info = drsblobs.AuthenticationInformation()
2563 info.LastUpdateTime = samba.unix2nttime(update_time)
2564 info.AuthType = lsa.TRUST_AUTH_TYPE_CLEAR
2565 info.AuthInfo = clear
2567 array = drsblobs.AuthenticationInformationArray()
2569 array.array = [info]
2571 blob = drsblobs.trustAuthInOutBlob()
2573 blob.current = array
2577 def generate_AuthInfoInternal(session_key, incoming=None, outgoing=None):
2578 confounder = [0] * 512
2579 for i in range(len(confounder)):
2580 confounder[i] = random.randint(0, 255)
2582 trustpass = drsblobs.trustDomainPasswords()
2584 trustpass.confounder = confounder
2585 trustpass.outgoing = outgoing
2586 trustpass.incoming = incoming
2588 trustpass_blob = ndr_pack(trustpass)
2590 encrypted_trustpass = arcfour_encrypt(session_key, trustpass_blob)
2592 auth_blob = lsa.DATA_BUF2()
2593 auth_blob.size = len(encrypted_trustpass)
2594 auth_blob.data = string_to_byte_array(encrypted_trustpass)
2596 auth_info = lsa.TrustDomainInfoAuthInfoInternal()
2597 auth_info.auth_blob = auth_blob
2601 update_time = samba.current_unix_time()
2602 incoming_blob = generate_AuthInOutBlob(incoming_secret, update_time)
2603 outgoing_blob = generate_AuthInOutBlob(outgoing_secret, update_time)
2605 local_tdo_handle = None
2606 remote_tdo_handle = None
2608 local_auth_info = generate_AuthInfoInternal(local_lsa.session_key,
2609 incoming=incoming_blob,
2610 outgoing=outgoing_blob)
2611 if remote_trust_info:
2612 remote_auth_info = generate_AuthInfoInternal(remote_lsa.session_key,
2613 incoming=outgoing_blob,
2614 outgoing=incoming_blob)
2617 if remote_trust_info:
2618 self.outf.write("Creating remote TDO.\n")
2619 current_request = {"location": "remote", "name": "CreateTrustedDomainEx2"}
2620 remote_tdo_handle = \
2621 remote_lsa.CreateTrustedDomainEx2(remote_policy,
2624 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2625 self.outf.write("Remote TDO created.\n")
2627 self.outf.write("Setting supported encryption types on remote TDO.\n")
2628 current_request = {"location": "remote", "name": "SetInformationTrustedDomain"}
2629 remote_lsa.SetInformationTrustedDomain(remote_tdo_handle,
2630 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2633 self.outf.write("Creating local TDO.\n")
2634 current_request = {"location": "local", "name": "CreateTrustedDomainEx2"}
2635 local_tdo_handle = local_lsa.CreateTrustedDomainEx2(local_policy,
2638 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2639 self.outf.write("Local TDO created\n")
2641 self.outf.write("Setting supported encryption types on local TDO.\n")
2642 current_request = {"location": "local", "name": "SetInformationTrustedDomain"}
2643 local_lsa.SetInformationTrustedDomain(local_tdo_handle,
2644 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2646 except RuntimeError as error:
2647 self.outf.write("Error: %s failed %sly - cleaning up\n" % (
2648 current_request['name'], current_request['location']))
2649 if remote_tdo_handle:
2650 self.outf.write("Deleting remote TDO.\n")
2651 remote_lsa.DeleteObject(remote_tdo_handle)
2652 remote_tdo_handle = None
2653 if local_tdo_handle:
2654 self.outf.write("Deleting local TDO.\n")
2655 local_lsa.DeleteObject(local_tdo_handle)
2656 local_tdo_handle = None
2657 if current_request['location'] is "remote":
2658 raise self.RemoteRuntimeError(self, error, "%s" % (
2659 current_request['name']))
2660 raise self.LocalRuntimeError(self, error, "%s" % (
2661 current_request['name']))
2664 if local_trust_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2665 self.outf.write("Setup local forest trust information...\n")
2667 # get all information about the remote trust
2668 # this triggers netr_GetForestTrustInformation to the remote domain
2669 # and lsaRSetForestTrustInformation() locally, but new top level
2670 # names are disabled by default.
2671 local_forest_info = \
2672 local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
2673 remote_lsa_info.dns_domain.string,
2674 netlogon.DS_GFTI_UPDATE_TDO)
2675 except RuntimeError as error:
2676 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2679 # here we try to enable all top level names
2680 local_forest_collision = \
2681 local_lsa.lsaRSetForestTrustInformation(local_policy,
2682 remote_lsa_info.dns_domain,
2683 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2686 except RuntimeError as error:
2687 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2689 self.write_forest_trust_info(local_forest_info,
2690 tln=remote_lsa_info.dns_domain.string,
2691 collisions=local_forest_collision)
2693 if remote_trust_info:
2694 self.outf.write("Setup remote forest trust information...\n")
2696 # get all information about the local trust (from the perspective of the remote domain)
2697 # this triggers netr_GetForestTrustInformation to our domain.
2698 # and lsaRSetForestTrustInformation() remotely, but new top level
2699 # names are disabled by default.
2700 remote_forest_info = \
2701 remote_netlogon.netr_DsRGetForestTrustInformation(remote_netlogon_dc_unc,
2702 local_lsa_info.dns_domain.string,
2703 netlogon.DS_GFTI_UPDATE_TDO)
2704 except RuntimeError as error:
2705 raise self.RemoteRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2708 # here we try to enable all top level names
2709 remote_forest_collision = \
2710 remote_lsa.lsaRSetForestTrustInformation(remote_policy,
2711 local_lsa_info.dns_domain,
2712 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2715 except RuntimeError as error:
2716 raise self.RemoteRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2718 self.write_forest_trust_info(remote_forest_info,
2719 tln=local_lsa_info.dns_domain.string,
2720 collisions=remote_forest_collision)
2722 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2723 self.outf.write("Validating outgoing trust...\n")
2725 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_netlogon_info.dc_unc,
2726 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2728 remote_lsa_info.dns_domain.string)
2729 except RuntimeError as error:
2730 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2732 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2733 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2735 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2736 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2737 local_trust_verify.trusted_dc_name,
2738 local_trust_verify.tc_connection_status[1],
2739 local_trust_verify.pdc_connection_status[1])
2741 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2742 local_trust_verify.trusted_dc_name,
2743 local_trust_verify.tc_connection_status[1],
2744 local_trust_verify.pdc_connection_status[1])
2746 if local_trust_status != werror.WERR_SUCCESS or local_conn_status != werror.WERR_SUCCESS:
2747 raise CommandError(local_validation)
2749 self.outf.write("OK: %s\n" % local_validation)
2751 if remote_trust_info:
2752 if remote_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2753 self.outf.write("Validating incoming trust...\n")
2755 remote_trust_verify = \
2756 remote_netlogon.netr_LogonControl2Ex(remote_netlogon_dc_unc,
2757 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2759 local_lsa_info.dns_domain.string)
2760 except RuntimeError as error:
2761 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2763 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2764 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2766 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2767 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2768 remote_trust_verify.trusted_dc_name,
2769 remote_trust_verify.tc_connection_status[1],
2770 remote_trust_verify.pdc_connection_status[1])
2772 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2773 remote_trust_verify.trusted_dc_name,
2774 remote_trust_verify.tc_connection_status[1],
2775 remote_trust_verify.pdc_connection_status[1])
2777 if remote_trust_status != werror.WERR_SUCCESS or remote_conn_status != werror.WERR_SUCCESS:
2778 raise CommandError(remote_validation)
2780 self.outf.write("OK: %s\n" % remote_validation)
2782 if remote_tdo_handle is not None:
2784 remote_lsa.Close(remote_tdo_handle)
2785 except RuntimeError as error:
2787 remote_tdo_handle = None
2788 if local_tdo_handle is not None:
2790 local_lsa.Close(local_tdo_handle)
2791 except RuntimeError as error:
2793 local_tdo_handle = None
2795 self.outf.write("Success.\n")
2799 class cmd_domain_trust_delete(DomainTrustCommand):
2800 """Delete a domain trust."""
2802 synopsis = "%prog DOMAIN [options]"
2804 takes_optiongroups = {
2805 "sambaopts": options.SambaOptions,
2806 "versionopts": options.VersionOptions,
2807 "credopts": options.CredentialsOptions,
2808 "localdcopts": LocalDCCredentialsOptions,
2812 Option("--delete-location", type="choice", metavar="LOCATION",
2813 choices=["local", "both"],
2814 help="Where to delete the trusted domain object: 'local' or 'both'.",
2815 dest='delete_location',
2819 takes_args = ["domain"]
2821 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2822 delete_location=None):
2824 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2825 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2826 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2828 if delete_location == "local":
2829 remote_policy_access = None
2831 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2832 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2833 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2835 local_server = self.setup_local_server(sambaopts, localdcopts)
2837 local_lsa = self.new_local_lsa_connection()
2838 except RuntimeError as error:
2839 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2842 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2843 except RuntimeError as error:
2844 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2846 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2847 local_lsa_info.name.string,
2848 local_lsa_info.dns_domain.string,
2849 local_lsa_info.sid))
2851 local_tdo_info = None
2852 local_tdo_handle = None
2853 remote_tdo_info = None
2854 remote_tdo_handle = None
2856 lsaString = lsa.String()
2858 lsaString.string = domain
2859 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2860 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2861 except NTSTATUSError as error:
2862 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2863 raise CommandError("Failed to find trust for domain '%s'" % domain)
2864 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2867 if remote_policy_access is not None:
2869 remote_server = self.setup_remote_server(credopts, domain)
2870 except RuntimeError as error:
2871 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2874 remote_lsa = self.new_remote_lsa_connection()
2875 except RuntimeError as error:
2876 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2879 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2880 except RuntimeError as error:
2881 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2883 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2884 remote_lsa_info.name.string,
2885 remote_lsa_info.dns_domain.string,
2886 remote_lsa_info.sid))
2888 if remote_lsa_info.sid != local_tdo_info.sid or \
2889 remote_lsa_info.name.string != local_tdo_info.netbios_name.string or \
2890 remote_lsa_info.dns_domain.string != local_tdo_info.domain_name.string:
2891 raise CommandError("LocalTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2892 local_tdo_info.netbios_name.string,
2893 local_tdo_info.domain_name.string,
2894 local_tdo_info.sid))
2897 lsaString.string = local_lsa_info.dns_domain.string
2899 remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2901 lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2902 except NTSTATUSError as error:
2903 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2904 raise self.RemoteRuntimeError(self, error, "QueryTrustedDomainInfoByName(%s)" % (
2908 if remote_tdo_info is not None:
2909 if local_lsa_info.sid != remote_tdo_info.sid or \
2910 local_lsa_info.name.string != remote_tdo_info.netbios_name.string or \
2911 local_lsa_info.dns_domain.string != remote_tdo_info.domain_name.string:
2912 raise CommandError("RemoteTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2913 remote_tdo_info.netbios_name.string,
2914 remote_tdo_info.domain_name.string,
2915 remote_tdo_info.sid))
2917 if local_tdo_info is not None:
2919 lsaString.string = local_tdo_info.domain_name.string
2920 local_tdo_handle = \
2921 local_lsa.OpenTrustedDomainByName(local_policy,
2923 security.SEC_STD_DELETE)
2924 except RuntimeError as error:
2925 raise self.LocalRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2928 local_lsa.DeleteObject(local_tdo_handle)
2929 local_tdo_handle = None
2931 if remote_tdo_info is not None:
2933 lsaString.string = remote_tdo_info.domain_name.string
2934 remote_tdo_handle = \
2935 remote_lsa.OpenTrustedDomainByName(remote_policy,
2937 security.SEC_STD_DELETE)
2938 except RuntimeError as error:
2939 raise self.RemoteRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2942 if remote_tdo_handle is not None:
2944 remote_lsa.DeleteObject(remote_tdo_handle)
2945 remote_tdo_handle = None
2946 self.outf.write("RemoteTDO deleted.\n")
2947 except RuntimeError as error:
2948 self.outf.write("%s\n" % self.RemoteRuntimeError(self, error, "DeleteObject() failed"))
2950 if local_tdo_handle is not None:
2952 local_lsa.DeleteObject(local_tdo_handle)
2953 local_tdo_handle = None
2954 self.outf.write("LocalTDO deleted.\n")
2955 except RuntimeError as error:
2956 self.outf.write("%s\n" % self.LocalRuntimeError(self, error, "DeleteObject() failed"))
2961 class cmd_domain_trust_validate(DomainTrustCommand):
2962 """Validate a domain trust."""
2964 synopsis = "%prog DOMAIN [options]"
2966 takes_optiongroups = {
2967 "sambaopts": options.SambaOptions,
2968 "versionopts": options.VersionOptions,
2969 "credopts": options.CredentialsOptions,
2970 "localdcopts": LocalDCCredentialsOptions,
2974 Option("--validate-location", type="choice", metavar="LOCATION",
2975 choices=["local", "both"],
2976 help="Where to validate the trusted domain object: 'local' or 'both'.",
2977 dest='validate_location',
2981 takes_args = ["domain"]
2983 def run(self, domain, sambaopts=None, versionopts=None, credopts=None, localdcopts=None,
2984 validate_location=None):
2986 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2988 local_server = self.setup_local_server(sambaopts, localdcopts)
2990 local_lsa = self.new_local_lsa_connection()
2991 except RuntimeError as error:
2992 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2995 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2996 except RuntimeError as error:
2997 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2999 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
3000 local_lsa_info.name.string,
3001 local_lsa_info.dns_domain.string,
3002 local_lsa_info.sid))
3005 lsaString = lsa.String()
3006 lsaString.string = domain
3008 local_lsa.QueryTrustedDomainInfoByName(local_policy,
3010 lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
3011 except NTSTATUSError as error:
3012 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
3013 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
3015 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
3017 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
3018 local_tdo_info.netbios_name.string,
3019 local_tdo_info.domain_name.string,
3020 local_tdo_info.sid))
3023 local_netlogon = self.new_local_netlogon_connection()
3024 except RuntimeError as error:
3025 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3028 local_trust_verify = \
3029 local_netlogon.netr_LogonControl2Ex(local_server,
3030 netlogon.NETLOGON_CONTROL_TC_VERIFY,
3032 local_tdo_info.domain_name.string)
3033 except RuntimeError as error:
3034 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
3036 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
3037 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
3039 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
3040 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
3041 local_trust_verify.trusted_dc_name,
3042 local_trust_verify.tc_connection_status[1],
3043 local_trust_verify.pdc_connection_status[1])
3045 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
3046 local_trust_verify.trusted_dc_name,
3047 local_trust_verify.tc_connection_status[1],
3048 local_trust_verify.pdc_connection_status[1])
3050 if local_trust_status != werror.WERR_SUCCESS or local_conn_status != werror.WERR_SUCCESS:
3051 raise CommandError(local_validation)
3053 self.outf.write("OK: %s\n" % local_validation)
3056 server = local_trust_verify.trusted_dc_name.replace('\\', '')
3057 domain_and_server = "%s\\%s" % (local_tdo_info.domain_name.string, server)
3058 local_trust_rediscover = \
3059 local_netlogon.netr_LogonControl2Ex(local_server,
3060 netlogon.NETLOGON_CONTROL_REDISCOVER,
3063 except RuntimeError as error:
3064 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
3066 local_conn_status = self._uint32(local_trust_rediscover.tc_connection_status[0])
3067 local_rediscover = "LocalRediscover: DC[%s] CONNECTION[%s]" % (
3068 local_trust_rediscover.trusted_dc_name,
3069 local_trust_rediscover.tc_connection_status[1])
3071 if local_conn_status != werror.WERR_SUCCESS:
3072 raise CommandError(local_rediscover)
3074 self.outf.write("OK: %s\n" % local_rediscover)
3076 if validate_location != "local":
3078 remote_server = self.setup_remote_server(credopts, domain, require_pdc=False)
3079 except RuntimeError as error:
3080 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
3083 remote_netlogon = self.new_remote_netlogon_connection()
3084 except RuntimeError as error:
3085 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
3088 remote_trust_verify = \
3089 remote_netlogon.netr_LogonControl2Ex(remote_server,
3090 netlogon.NETLOGON_CONTROL_TC_VERIFY,
3092 local_lsa_info.dns_domain.string)
3093 except RuntimeError as error:
3094 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
3096 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
3097 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
3099 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
3100 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
3101 remote_trust_verify.trusted_dc_name,
3102 remote_trust_verify.tc_connection_status[1],
3103 remote_trust_verify.pdc_connection_status[1])
3105 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
3106 remote_trust_verify.trusted_dc_name,
3107 remote_trust_verify.tc_connection_status[1],
3108 remote_trust_verify.pdc_connection_status[1])
3110 if remote_trust_status != werror.WERR_SUCCESS or remote_conn_status != werror.WERR_SUCCESS:
3111 raise CommandError(remote_validation)
3113 self.outf.write("OK: %s\n" % remote_validation)
3116 server = remote_trust_verify.trusted_dc_name.replace('\\', '')
3117 domain_and_server = "%s\\%s" % (local_lsa_info.dns_domain.string, server)
3118 remote_trust_rediscover = \
3119 remote_netlogon.netr_LogonControl2Ex(remote_server,
3120 netlogon.NETLOGON_CONTROL_REDISCOVER,
3123 except RuntimeError as error:
3124 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
3126 remote_conn_status = self._uint32(remote_trust_rediscover.tc_connection_status[0])
3128 remote_rediscover = "RemoteRediscover: DC[%s] CONNECTION[%s]" % (
3129 remote_trust_rediscover.trusted_dc_name,
3130 remote_trust_rediscover.tc_connection_status[1])
3132 if remote_conn_status != werror.WERR_SUCCESS:
3133 raise CommandError(remote_rediscover)
3135 self.outf.write("OK: %s\n" % remote_rediscover)
3140 class cmd_domain_trust_namespaces(DomainTrustCommand):
3141 """Manage forest trust namespaces."""
3143 synopsis = "%prog [DOMAIN] [options]"
3145 takes_optiongroups = {
3146 "sambaopts": options.SambaOptions,
3147 "versionopts": options.VersionOptions,
3148 "localdcopts": LocalDCCredentialsOptions,
3152 Option("--refresh", type="choice", metavar="check|store",
3153 choices=["check", "store", None],
3154 help="List and maybe store refreshed forest trust information: 'check' or 'store'.",
3157 Option("--enable-all", action="store_true",
3158 help="Try to update disabled entries, not allowed with --refresh=check.",
3161 Option("--enable-tln", action="append", metavar='DNSDOMAIN',
3162 help="Enable a top level name entry. Can be specified multiple times.",
3165 Option("--disable-tln", action="append", metavar='DNSDOMAIN',
3166 help="Disable a top level name entry. Can be specified multiple times.",
3169 Option("--add-tln-ex", action="append", metavar='DNSDOMAIN',
3170 help="Add a top level exclusion entry. Can be specified multiple times.",
3173 Option("--delete-tln-ex", action="append", metavar='DNSDOMAIN',
3174 help="Delete a top level exclusion entry. Can be specified multiple times.",
3175 dest='delete_tln_ex',
3177 Option("--enable-nb", action="append", metavar='NETBIOSDOMAIN',
3178 help="Enable a netbios name in a domain entry. Can be specified multiple times.",
3181 Option("--disable-nb", action="append", metavar='NETBIOSDOMAIN',
3182 help="Disable a netbios name in a domain entry. Can be specified multiple times.",
3185 Option("--enable-sid", action="append", metavar='DOMAINSID',
3186 help="Enable a SID in a domain entry. Can be specified multiple times.",
3187 dest='enable_sid_str',
3189 Option("--disable-sid", action="append", metavar='DOMAINSID',
3190 help="Disable a SID in a domain entry. Can be specified multiple times.",
3191 dest='disable_sid_str',
3193 Option("--add-upn-suffix", action="append", metavar='DNSDOMAIN',
3194 help="Add a new uPNSuffixes attribute for the local forest. Can be specified multiple times.",
3197 Option("--delete-upn-suffix", action="append", metavar='DNSDOMAIN',
3198 help="Delete an existing uPNSuffixes attribute of the local forest. Can be specified multiple times.",
3201 Option("--add-spn-suffix", action="append", metavar='DNSDOMAIN',
3202 help="Add a new msDS-SPNSuffixes attribute for the local forest. Can be specified multiple times.",
3205 Option("--delete-spn-suffix", action="append", metavar='DNSDOMAIN',
3206 help="Delete an existing msDS-SPNSuffixes attribute of the local forest. Can be specified multiple times.",
3211 takes_args = ["domain?"]
3213 def run(self, domain=None, sambaopts=None, localdcopts=None, versionopts=None,
3214 refresh=None, enable_all=False,
3215 enable_tln=[], disable_tln=[], add_tln_ex=[], delete_tln_ex=[],
3216 enable_sid_str=[], disable_sid_str=[], enable_nb=[], disable_nb=[],
3217 add_upn=[], delete_upn=[], add_spn=[], delete_spn=[]):
3219 require_update = False
3222 if refresh == "store":
3223 raise CommandError("--refresh=%s not allowed without DOMAIN" % refresh)
3226 raise CommandError("--enable-all not allowed without DOMAIN")
3228 if len(enable_tln) > 0:
3229 raise CommandError("--enable-tln not allowed without DOMAIN")
3230 if len(disable_tln) > 0:
3231 raise CommandError("--disable-tln not allowed without DOMAIN")
3233 if len(add_tln_ex) > 0:
3234 raise CommandError("--add-tln-ex not allowed without DOMAIN")
3235 if len(delete_tln_ex) > 0:
3236 raise CommandError("--delete-tln-ex not allowed without DOMAIN")
3238 if len(enable_nb) > 0:
3239 raise CommandError("--enable-nb not allowed without DOMAIN")
3240 if len(disable_nb) > 0:
3241 raise CommandError("--disable-nb not allowed without DOMAIN")
3243 if len(enable_sid_str) > 0:
3244 raise CommandError("--enable-sid not allowed without DOMAIN")
3245 if len(disable_sid_str) > 0:
3246 raise CommandError("--disable-sid not allowed without DOMAIN")
3248 if len(add_upn) > 0:
3250 if not n.startswith("*."):
3252 raise CommandError("value[%s] specified for --add-upn-suffix should not include with '*.'" % n)
3253 require_update = True
3254 if len(delete_upn) > 0:
3255 for n in delete_upn:
3256 if not n.startswith("*."):
3258 raise CommandError("value[%s] specified for --delete-upn-suffix should not include with '*.'" % n)
3259 require_update = True
3261 for d in delete_upn:
3262 if a.lower() != d.lower():
3264 raise CommandError("value[%s] specified for --add-upn-suffix and --delete-upn-suffix" % a)
3266 if len(add_spn) > 0:
3268 if not n.startswith("*."):
3270 raise CommandError("value[%s] specified for --add-spn-suffix should not include with '*.'" % n)
3271 require_update = True
3272 if len(delete_spn) > 0:
3273 for n in delete_spn:
3274 if not n.startswith("*."):
3276 raise CommandError("value[%s] specified for --delete-spn-suffix should not include with '*.'" % n)
3277 require_update = True
3279 for d in delete_spn:
3280 if a.lower() != d.lower():
3282 raise CommandError("value[%s] specified for --add-spn-suffix and --delete-spn-suffix" % a)
3284 if len(add_upn) > 0:
3285 raise CommandError("--add-upn-suffix not allowed together with DOMAIN")
3286 if len(delete_upn) > 0:
3287 raise CommandError("--delete-upn-suffix not allowed together with DOMAIN")
3288 if len(add_spn) > 0:
3289 raise CommandError("--add-spn-suffix not allowed together with DOMAIN")
3290 if len(delete_spn) > 0:
3291 raise CommandError("--delete-spn-suffix not allowed together with DOMAIN")
3293 if refresh is not None:
3294 if refresh == "store":
3295 require_update = True
3297 if enable_all and refresh != "store":
3298 raise CommandError("--enable-all not allowed together with --refresh=%s" % refresh)
3300 if len(enable_tln) > 0:
3301 raise CommandError("--enable-tln not allowed together with --refresh")
3302 if len(disable_tln) > 0:
3303 raise CommandError("--disable-tln not allowed together with --refresh")
3305 if len(add_tln_ex) > 0:
3306 raise CommandError("--add-tln-ex not allowed together with --refresh")
3307 if len(delete_tln_ex) > 0:
3308 raise CommandError("--delete-tln-ex not allowed together with --refresh")
3310 if len(enable_nb) > 0:
3311 raise CommandError("--enable-nb not allowed together with --refresh")
3312 if len(disable_nb) > 0:
3313 raise CommandError("--disable-nb not allowed together with --refresh")
3315 if len(enable_sid_str) > 0:
3316 raise CommandError("--enable-sid not allowed together with --refresh")
3317 if len(disable_sid_str) > 0:
3318 raise CommandError("--disable-sid not allowed together with --refresh")
3321 require_update = True
3323 if len(enable_tln) > 0:
3324 raise CommandError("--enable-tln not allowed together with --enable-all")
3326 if len(enable_nb) > 0:
3327 raise CommandError("--enable-nb not allowed together with --enable-all")
3329 if len(enable_sid_str) > 0:
3330 raise CommandError("--enable-sid not allowed together with --enable-all")
3332 if len(enable_tln) > 0:
3333 require_update = True
3334 if len(disable_tln) > 0:
3335 require_update = True
3336 for e in enable_tln:
3337 for d in disable_tln:
3338 if e.lower() != d.lower():
3340 raise CommandError("value[%s] specified for --enable-tln and --disable-tln" % e)
3342 if len(add_tln_ex) > 0:
3343 for n in add_tln_ex:
3344 if not n.startswith("*."):
3346 raise CommandError("value[%s] specified for --add-tln-ex should not include with '*.'" % n)
3347 require_update = True
3348 if len(delete_tln_ex) > 0:
3349 for n in delete_tln_ex:
3350 if not n.startswith("*."):
3352 raise CommandError("value[%s] specified for --delete-tln-ex should not include with '*.'" % n)
3353 require_update = True
3354 for a in add_tln_ex:
3355 for d in delete_tln_ex:
3356 if a.lower() != d.lower():
3358 raise CommandError("value[%s] specified for --add-tln-ex and --delete-tln-ex" % a)
3360 if len(enable_nb) > 0:
3361 require_update = True
3362 if len(disable_nb) > 0:
3363 require_update = True
3365 for d in disable_nb:
3366 if e.upper() != d.upper():
3368 raise CommandError("value[%s] specified for --enable-nb and --disable-nb" % e)
3371 for s in enable_sid_str:
3373 sid = security.dom_sid(s)
3374 except TypeError as error:
3375 raise CommandError("value[%s] specified for --enable-sid is not a valid SID" % s)
3376 enable_sid.append(sid)
3378 for s in disable_sid_str:
3380 sid = security.dom_sid(s)
3381 except TypeError as error:
3382 raise CommandError("value[%s] specified for --disable-sid is not a valid SID" % s)
3383 disable_sid.append(sid)
3384 if len(enable_sid) > 0:
3385 require_update = True
3386 if len(disable_sid) > 0:
3387 require_update = True
3388 for e in enable_sid:
3389 for d in disable_sid:
3392 raise CommandError("value[%s] specified for --enable-sid and --disable-sid" % e)
3394 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
3396 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
3398 local_server = self.setup_local_server(sambaopts, localdcopts)
3400 local_lsa = self.new_local_lsa_connection()
3401 except RuntimeError as error:
3402 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
3405 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
3406 except RuntimeError as error:
3407 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
3409 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
3410 local_lsa_info.name.string,
3411 local_lsa_info.dns_domain.string,
3412 local_lsa_info.sid))
3416 local_netlogon = self.new_local_netlogon_connection()
3417 except RuntimeError as error:
3418 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3421 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3422 except RuntimeError as error:
3423 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3425 if local_netlogon_info.domain_name != local_netlogon_info.forest_name:
3426 raise CommandError("The local domain [%s] is not the forest root [%s]" % (
3427 local_netlogon_info.domain_name,
3428 local_netlogon_info.forest_name))
3431 # get all information about our own forest
3432 own_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3434 except RuntimeError as error:
3435 if self.check_runtime_error(error, werror.WERR_RPC_S_PROCNUM_OUT_OF_RANGE):
3436 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3439 if self.check_runtime_error(error, werror.WERR_INVALID_FUNCTION):
3440 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3443 if self.check_runtime_error(error, werror.WERR_NERR_ACFNOTLOADED):
3444 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3447 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3449 self.outf.write("Own forest trust information...\n")
3450 self.write_forest_trust_info(own_forest_info,
3451 tln=local_lsa_info.dns_domain.string)
3454 local_samdb = self.new_local_ldap_connection()
3455 except RuntimeError as error:
3456 raise self.LocalRuntimeError(self, error, "failed to connect to SamDB")
3458 local_partitions_dn = "CN=Partitions,%s" % str(local_samdb.get_config_basedn())
3459 attrs = ['uPNSuffixes', 'msDS-SPNSuffixes']
3461 msgs = local_samdb.search(base=local_partitions_dn,
3462 scope=ldb.SCOPE_BASE,
3463 expression="(objectClass=crossRefContainer)",
3465 stored_msg = msgs[0]
3466 except ldb.LdbError as error:
3467 raise self.LocalLdbError(self, error, "failed to search partition dn")
3469 stored_upn_vals = []
3470 if 'uPNSuffixes' in stored_msg:
3471 stored_upn_vals.extend(stored_msg['uPNSuffixes'])
3473 stored_spn_vals = []
3474 if 'msDS-SPNSuffixes' in stored_msg:
3475 stored_spn_vals.extend(stored_msg['msDS-SPNSuffixes'])
3477 self.outf.write("Stored uPNSuffixes attributes[%d]:\n" % len(stored_upn_vals))
3478 for v in stored_upn_vals:
3479 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3480 self.outf.write("Stored msDS-SPNSuffixes attributes[%d]:\n" % len(stored_spn_vals))
3481 for v in stored_spn_vals:
3482 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3484 if not require_update:
3488 update_upn_vals = []
3489 update_upn_vals.extend(stored_upn_vals)
3492 update_spn_vals = []
3493 update_spn_vals.extend(stored_spn_vals)
3496 for i, v in enumerate(update_upn_vals):
3497 if v.lower() == upn.lower():
3498 raise CommandError("Entry already present for "
3499 "value[%s] specified for "
3500 "--add-upn-suffix" % upn)
3501 update_upn_vals.append(upn)
3504 for upn in delete_upn:
3506 for i, v in enumerate(update_upn_vals):
3507 if v.lower() != upn.lower():
3512 raise CommandError("Entry not found for value[%s] specified for --delete-upn-suffix" % upn)
3514 update_upn_vals.pop(idx)
3518 for i, v in enumerate(update_spn_vals):
3519 if v.lower() == spn.lower():
3520 raise CommandError("Entry already present for "
3521 "value[%s] specified for "
3522 "--add-spn-suffix" % spn)
3523 update_spn_vals.append(spn)
3526 for spn in delete_spn:
3528 for i, v in enumerate(update_spn_vals):
3529 if v.lower() != spn.lower():
3534 raise CommandError("Entry not found for value[%s] specified for --delete-spn-suffix" % spn)
3536 update_spn_vals.pop(idx)
3539 self.outf.write("Update uPNSuffixes attributes[%d]:\n" % len(update_upn_vals))
3540 for v in update_upn_vals:
3541 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3542 self.outf.write("Update msDS-SPNSuffixes attributes[%d]:\n" % len(update_spn_vals))
3543 for v in update_spn_vals:
3544 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3546 update_msg = ldb.Message()
3547 update_msg.dn = stored_msg.dn
3550 update_msg['uPNSuffixes'] = ldb.MessageElement(update_upn_vals,
3551 ldb.FLAG_MOD_REPLACE,
3554 update_msg['msDS-SPNSuffixes'] = ldb.MessageElement(update_spn_vals,
3555 ldb.FLAG_MOD_REPLACE,
3558 local_samdb.modify(update_msg)
3559 except ldb.LdbError as error:
3560 raise self.LocalLdbError(self, error, "failed to update partition dn")
3563 stored_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3565 except RuntimeError as error:
3566 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3568 self.outf.write("Stored forest trust information...\n")
3569 self.write_forest_trust_info(stored_forest_info,
3570 tln=local_lsa_info.dns_domain.string)
3574 lsaString = lsa.String()
3575 lsaString.string = domain
3577 local_lsa.QueryTrustedDomainInfoByName(local_policy,
3579 lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
3580 except NTSTATUSError as error:
3581 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
3582 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
3584 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
3586 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
3587 local_tdo_info.netbios_name.string,
3588 local_tdo_info.domain_name.string,
3589 local_tdo_info.sid))
3591 if not local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
3592 raise CommandError("trusted domain object for domain [%s] is not marked as FOREST_TRANSITIVE." % domain)
3594 if refresh is not None:
3596 local_netlogon = self.new_local_netlogon_connection()
3597 except RuntimeError as error:
3598 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3601 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3602 except RuntimeError as error:
3603 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3605 lsa_update_check = 1
3606 if refresh == "store":
3607 netlogon_update_tdo = netlogon.DS_GFTI_UPDATE_TDO
3609 lsa_update_check = 0
3611 netlogon_update_tdo = 0
3614 # get all information about the remote trust
3615 # this triggers netr_GetForestTrustInformation to the remote domain
3616 # and lsaRSetForestTrustInformation() locally, but new top level
3617 # names are disabled by default.
3618 fresh_forest_info = \
3619 local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3620 local_tdo_info.domain_name.string,
3621 netlogon_update_tdo)
3622 except RuntimeError as error:
3623 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3626 fresh_forest_collision = \
3627 local_lsa.lsaRSetForestTrustInformation(local_policy,
3628 local_tdo_info.domain_name,
3629 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3632 except RuntimeError as error:
3633 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3635 self.outf.write("Fresh forest trust information...\n")
3636 self.write_forest_trust_info(fresh_forest_info,
3637 tln=local_tdo_info.domain_name.string,
3638 collisions=fresh_forest_collision)
3640 if refresh == "store":
3642 lsaString = lsa.String()
3643 lsaString.string = local_tdo_info.domain_name.string
3644 stored_forest_info = \
3645 local_lsa.lsaRQueryForestTrustInformation(local_policy,
3647 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3648 except RuntimeError as error:
3649 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3651 self.outf.write("Stored forest trust information...\n")
3652 self.write_forest_trust_info(stored_forest_info,
3653 tln=local_tdo_info.domain_name.string)
3658 # The none --refresh path
3662 lsaString = lsa.String()
3663 lsaString.string = local_tdo_info.domain_name.string
3664 local_forest_info = \
3665 local_lsa.lsaRQueryForestTrustInformation(local_policy,
3667 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3668 except RuntimeError as error:
3669 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3671 self.outf.write("Local forest trust information...\n")
3672 self.write_forest_trust_info(local_forest_info,
3673 tln=local_tdo_info.domain_name.string)
3675 if not require_update:
3679 entries.extend(local_forest_info.entries)
3680 update_forest_info = lsa.ForestTrustInformation()
3681 update_forest_info.count = len(entries)
3682 update_forest_info.entries = entries
3685 for i, r in enumerate(update_forest_info.entries):
3686 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3688 if update_forest_info.entries[i].flags == 0:
3690 update_forest_info.entries[i].time = 0
3691 update_forest_info.entries[i].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3692 for i, r in enumerate(update_forest_info.entries):
3693 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3695 if update_forest_info.entries[i].flags == 0:
3697 update_forest_info.entries[i].time = 0
3698 update_forest_info.entries[i].flags &= ~lsa.LSA_NB_DISABLED_MASK
3699 update_forest_info.entries[i].flags &= ~lsa.LSA_SID_DISABLED_MASK
3701 for tln in enable_tln:
3703 for i, r in enumerate(update_forest_info.entries):
3704 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3706 if r.forest_trust_data.string.lower() != tln.lower():
3711 raise CommandError("Entry not found for value[%s] specified for --enable-tln" % tln)
3712 if not update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_MASK:
3713 raise CommandError("Entry found for value[%s] specified for --enable-tln is already enabled" % tln)
3714 update_forest_info.entries[idx].time = 0
3715 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3717 for tln in disable_tln:
3719 for i, r in enumerate(update_forest_info.entries):
3720 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3722 if r.forest_trust_data.string.lower() != tln.lower():
3727 raise CommandError("Entry not found for value[%s] specified for --disable-tln" % tln)
3728 if update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_ADMIN:
3729 raise CommandError("Entry found for value[%s] specified for --disable-tln is already disabled" % tln)
3730 update_forest_info.entries[idx].time = 0
3731 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3732 update_forest_info.entries[idx].flags |= lsa.LSA_TLN_DISABLED_ADMIN
3734 for tln_ex in add_tln_ex:
3736 for i, r in enumerate(update_forest_info.entries):
3737 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3739 if r.forest_trust_data.string.lower() != tln_ex.lower():
3744 raise CommandError("Entry already present for value[%s] specified for --add-tln-ex" % tln_ex)
3746 tln_dot = ".%s" % tln_ex.lower()
3748 for i, r in enumerate(update_forest_info.entries):
3749 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3751 r_dot = ".%s" % r.forest_trust_data.string.lower()
3752 if tln_dot == r_dot:
3753 raise CommandError("TLN entry present for value[%s] specified for --add-tln-ex" % tln_ex)
3754 if not tln_dot.endswith(r_dot):
3760 raise CommandError("No TLN parent present for value[%s] specified for --add-tln-ex" % tln_ex)
3762 r = lsa.ForestTrustRecord()
3763 r.type = lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX
3766 r.forest_trust_data.string = tln_ex
3769 entries.extend(update_forest_info.entries)
3770 entries.insert(idx + 1, r)
3771 update_forest_info.count = len(entries)
3772 update_forest_info.entries = entries
3774 for tln_ex in delete_tln_ex:
3776 for i, r in enumerate(update_forest_info.entries):
3777 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3779 if r.forest_trust_data.string.lower() != tln_ex.lower():
3784 raise CommandError("Entry not found for value[%s] specified for --delete-tln-ex" % tln_ex)
3787 entries.extend(update_forest_info.entries)
3789 update_forest_info.count = len(entries)
3790 update_forest_info.entries = entries
3792 for nb in enable_nb:
3794 for i, r in enumerate(update_forest_info.entries):
3795 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3797 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3802 raise CommandError("Entry not found for value[%s] specified for --enable-nb" % nb)
3803 if not update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_MASK:
3804 raise CommandError("Entry found for value[%s] specified for --enable-nb is already enabled" % nb)
3805 update_forest_info.entries[idx].time = 0
3806 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3808 for nb in disable_nb:
3810 for i, r in enumerate(update_forest_info.entries):
3811 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3813 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3818 raise CommandError("Entry not found for value[%s] specified for --delete-nb" % nb)
3819 if update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_ADMIN:
3820 raise CommandError("Entry found for value[%s] specified for --disable-nb is already disabled" % nb)
3821 update_forest_info.entries[idx].time = 0
3822 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3823 update_forest_info.entries[idx].flags |= lsa.LSA_NB_DISABLED_ADMIN
3825 for sid in enable_sid:
3827 for i, r in enumerate(update_forest_info.entries):
3828 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3830 if r.forest_trust_data.domain_sid != sid:
3835 raise CommandError("Entry not found for value[%s] specified for --enable-sid" % sid)
3836 if not update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_MASK:
3837 raise CommandError("Entry found for value[%s] specified for --enable-sid is already enabled" % nb)
3838 update_forest_info.entries[idx].time = 0
3839 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3841 for sid in disable_sid:
3843 for i, r in enumerate(update_forest_info.entries):
3844 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3846 if r.forest_trust_data.domain_sid != sid:
3851 raise CommandError("Entry not found for value[%s] specified for --delete-sid" % sid)
3852 if update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_ADMIN:
3853 raise CommandError("Entry found for value[%s] specified for --disable-sid is already disabled" % nb)
3854 update_forest_info.entries[idx].time = 0
3855 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3856 update_forest_info.entries[idx].flags |= lsa.LSA_SID_DISABLED_ADMIN
3859 update_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3860 local_tdo_info.domain_name,
3861 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3862 update_forest_info, 0)
3863 except RuntimeError as error:
3864 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3866 self.outf.write("Updated forest trust information...\n")
3867 self.write_forest_trust_info(update_forest_info,
3868 tln=local_tdo_info.domain_name.string,
3869 collisions=update_forest_collision)
3872 lsaString = lsa.String()
3873 lsaString.string = local_tdo_info.domain_name.string
3874 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3876 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3877 except RuntimeError as error:
3878 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3880 self.outf.write("Stored forest trust information...\n")
3881 self.write_forest_trust_info(stored_forest_info,
3882 tln=local_tdo_info.domain_name.string)
3886 class cmd_domain_tombstones_expunge(Command):
3887 """Expunge tombstones from the database.
3889 This command expunges tombstones from the database."""
3890 synopsis = "%prog NC [NC [...]] [options]"
3893 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
3894 metavar="URL", dest="H"),
3895 Option("--current-time",
3896 help="The current time to evaluate the tombstone lifetime from, expressed as YYYY-MM-DD",
3898 Option("--tombstone-lifetime", help="Number of days a tombstone should be preserved for", type=int),
3901 takes_args = ["nc*"]
3903 takes_optiongroups = {
3904 "sambaopts": options.SambaOptions,
3905 "credopts": options.CredentialsOptions,
3906 "versionopts": options.VersionOptions,
3909 def run(self, *ncs, **kwargs):
3910 sambaopts = kwargs.get("sambaopts")
3911 credopts = kwargs.get("credopts")
3912 versionpts = kwargs.get("versionopts")
3914 current_time_string = kwargs.get("current_time")
3915 tombstone_lifetime = kwargs.get("tombstone_lifetime")
3916 lp = sambaopts.get_loadparm()
3917 creds = credopts.get_credentials(lp)
3918 samdb = SamDB(url=H, session_info=system_session(),
3919 credentials=creds, lp=lp)
3921 if current_time_string is not None:
3922 current_time_obj = time.strptime(current_time_string, "%Y-%m-%d")
3923 current_time = long(time.mktime(current_time_obj))
3926 current_time = long(time.time())
3929 res = samdb.search(expression="", base="", scope=ldb.SCOPE_BASE,
3930 attrs=["namingContexts"])
3933 for nc in res[0]["namingContexts"]:
3938 started_transaction = False
3940 samdb.transaction_start()
3941 started_transaction = True
3943 removed_links) = samdb.garbage_collect_tombstones(ncs,
3944 current_time=current_time,
3945 tombstone_lifetime=tombstone_lifetime)
3947 except Exception as err:
3948 if started_transaction:
3949 samdb.transaction_cancel()
3950 raise CommandError("Failed to expunge / garbage collect tombstones", err)
3952 samdb.transaction_commit()
3954 self.outf.write("Removed %d objects and %d links successfully\n"
3955 % (removed_objects, removed_links))
3959 class cmd_domain_trust(SuperCommand):
3960 """Domain and forest trust management."""
3963 subcommands["list"] = cmd_domain_trust_list()
3964 subcommands["show"] = cmd_domain_trust_show()
3965 subcommands["create"] = cmd_domain_trust_create()
3966 subcommands["delete"] = cmd_domain_trust_delete()
3967 subcommands["validate"] = cmd_domain_trust_validate()
3968 subcommands["namespaces"] = cmd_domain_trust_namespaces()
3971 class cmd_domain_tombstones(SuperCommand):
3972 """Domain tombstone and recycled object management."""
3975 subcommands["expunge"] = cmd_domain_tombstones_expunge()
3978 class ldif_schema_update:
3979 """Helper class for applying LDIF schema updates"""
3982 self.is_defunct = False
3983 self.unknown_oid = None
3987 def can_ignore_failure(self, error):
3988 """Checks if we can safely ignore failure to apply an LDIF update"""
3989 (num, errstr) = error.args
3991 # Microsoft has marked objects as defunct that Samba doesn't know about
3992 if num == ldb.ERR_NO_SUCH_OBJECT and self.is_defunct:
3993 print("Defunct object %s doesn't exist, skipping" % self.dn)
3995 elif self.unknown_oid is not None:
3996 print("Skipping unknown OID %s for object %s" % (self.unknown_oid, self.dn))
4001 def apply(self, samdb):
4002 """Applies a single LDIF update to the schema"""
4006 samdb.modify_ldif(self.ldif, controls=['relax:0'])
4007 except ldb.LdbError as e:
4008 if e.args[0] == ldb.ERR_INVALID_ATTRIBUTE_SYNTAX:
4010 # REFRESH after a failed change
4012 # Otherwise the OID-to-attribute mapping in
4013 # _apply_updates_in_file() won't work, because it
4014 # can't lookup the new OID in the schema
4015 samdb.set_schema_update_now()
4017 samdb.modify_ldif(self.ldif, controls=['relax:0'])
4020 except ldb.LdbError as e:
4021 if self.can_ignore_failure(e):
4024 print("Exception: %s" % e)
4025 print("Encountered while trying to apply the following LDIF")
4026 print("----------------------------------------------------")
4027 print("%s" % self.ldif)
4034 class cmd_domain_schema_upgrade(Command):
4035 """Domain schema upgrading"""
4037 synopsis = "%prog [options]"
4039 takes_optiongroups = {
4040 "sambaopts": options.SambaOptions,
4041 "versionopts": options.VersionOptions,
4042 "credopts": options.CredentialsOptions,
4046 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
4047 metavar="URL", dest="H"),
4048 Option("-q", "--quiet", help="Be quiet", action="store_true"), # unused
4049 Option("-v", "--verbose", help="Be verbose", action="store_true"),
4050 Option("--schema", type="choice", metavar="SCHEMA",
4051 choices=["2012", "2012_R2"],
4052 help="The schema file to upgrade to. Default is (Windows) 2012_R2.",
4054 Option("--ldf-file", type=str, default=None,
4055 help="Just apply the schema updates in the adprep/.LDF file(s) specified"),
4056 Option("--base-dir", type=str, default=None,
4057 help="Location of ldf files Default is ${SETUPDIR}/adprep.")
4060 def _apply_updates_in_file(self, samdb, ldif_file):
4062 Applies a series of updates specified in an .LDIF file. The .LDIF file
4063 is based on the adprep Schema updates provided by Microsoft.
4066 ldif_op = ldif_schema_update()
4068 # parse the file line by line and work out each update operation to apply
4069 for line in ldif_file:
4071 line = line.rstrip()
4073 # the operations in the .LDIF file are separated by blank lines. If
4074 # we hit a blank line, try to apply the update we've parsed so far
4077 # keep going if we haven't parsed anything yet
4078 if ldif_op.ldif == '':
4081 # Apply the individual change
4082 count += ldif_op.apply(samdb)
4084 # start storing the next operation from scratch again
4085 ldif_op = ldif_schema_update()
4088 # replace the placeholder domain name in the .ldif file with the real domain
4089 if line.upper().endswith('DC=X'):
4090 line = line[:-len('DC=X')] + str(samdb.get_default_basedn())
4091 elif line.upper().endswith('CN=X'):
4092 line = line[:-len('CN=X')] + str(samdb.get_default_basedn())
4094 values = line.split(':')
4096 if values[0].lower() == 'dn':
4097 ldif_op.dn = values[1].strip()
4099 # replace the Windows-specific operation with the Samba one
4100 if values[0].lower() == 'changetype':
4101 line = line.lower().replace(': ntdsschemaadd',
4103 line = line.lower().replace(': ntdsschemamodify',
4106 if values[0].lower() in ['rdnattid', 'subclassof',
4107 'systemposssuperiors',
4109 'systemauxiliaryclass']:
4112 # The Microsoft updates contain some OIDs we don't recognize.
4113 # Query the DB to see if we can work out the OID this update is
4114 # referring to. If we find a match, then replace the OID with
4115 # the ldapDisplayname
4117 res = samdb.search(base=samdb.get_schema_basedn(),
4118 expression="(|(attributeId=%s)(governsId=%s))" %
4120 attrs=['ldapDisplayName'])
4123 ldif_op.unknown_oid = value
4125 display_name = res[0]['ldapDisplayName'][0]
4126 line = line.replace(value, ' ' + display_name)
4128 # Microsoft has marked objects as defunct that Samba doesn't know about
4129 if values[0].lower() == 'isdefunct' and values[1].strip().lower() == 'true':
4130 ldif_op.is_defunct = True
4132 # Samba has added the showInAdvancedViewOnly attribute to all objects,
4133 # so rather than doing an add, we need to do a replace
4134 if values[0].lower() == 'add' and values[1].strip().lower() == 'showinadvancedviewonly':
4135 line = 'replace: showInAdvancedViewOnly'
4137 # Add the line to the current LDIF operation (including the newline
4138 # we stripped off at the start of the loop)
4139 ldif_op.ldif += line + '\n'
4144 def _apply_update(self, samdb, update_file, base_dir):
4145 """Wrapper function for parsing an LDIF file and applying the updates"""
4147 print("Applying %s updates..." % update_file)
4151 ldif_file = open(os.path.join(base_dir, update_file))
4153 count = self._apply_updates_in_file(samdb, ldif_file)
4159 print("%u changes applied" % count)
4163 def run(self, **kwargs):
4164 from samba.ms_schema_markdown import read_ms_markdown
4165 from samba.schema import Schema
4167 updates_allowed_overriden = False
4168 sambaopts = kwargs.get("sambaopts")
4169 credopts = kwargs.get("credopts")
4170 versionpts = kwargs.get("versionopts")
4171 lp = sambaopts.get_loadparm()
4172 creds = credopts.get_credentials(lp)
4174 target_schema = kwargs.get("schema")
4175 ldf_files = kwargs.get("ldf_file")
4176 base_dir = kwargs.get("base_dir")
4180 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
4182 # we're not going to get far if the config doesn't allow schema updates
4183 if lp.get("dsdb:schema update allowed") is None:
4184 lp.set("dsdb:schema update allowed", "yes")
4185 print("Temporarily overriding 'dsdb:schema update allowed' setting")
4186 updates_allowed_overriden = True
4188 own_dn = ldb.Dn(samdb, samdb.get_dsServiceName())
4189 master = get_fsmo_roleowner(samdb, str(samdb.get_schema_basedn()),
4191 if own_dn != master:
4192 raise CommandError("This server is not the schema master.")
4194 # if specific LDIF files were specified, just apply them
4196 schema_updates = ldf_files.split(",")
4200 # work out the version of the target schema we're upgrading to
4201 end = Schema.get_version(target_schema)
4203 # work out the version of the schema we're currently using
4204 res = samdb.search(base=samdb.get_schema_basedn(),
4205 scope=ldb.SCOPE_BASE, attrs=['objectVersion'])
4208 raise CommandError('Could not determine current schema version')
4209 start = int(res[0]['objectVersion'][0]) + 1
4211 diff_dir = setup_path("adprep/WindowsServerDocs")
4212 if base_dir is None:
4213 # Read from the Schema-Updates.md file
4214 temp_folder = tempfile.mkdtemp()
4216 update_file = setup_path("adprep/WindowsServerDocs/Schema-Updates.md")
4219 read_ms_markdown(update_file, temp_folder)
4220 except Exception as e:
4221 print("Exception in markdown parsing: %s" % e)
4222 shutil.rmtree(temp_folder)
4223 raise CommandError('Failed to upgrade schema')
4225 base_dir = temp_folder
4227 for version in range(start, end + 1):
4228 update = 'Sch%d.ldf' % version
4229 schema_updates.append(update)
4231 # Apply patches if we parsed the Schema-Updates.md file
4232 diff = os.path.abspath(os.path.join(diff_dir, update + '.diff'))
4233 if temp_folder and os.path.exists(diff):
4235 p = subprocess.Popen(['patch', update, '-i', diff],
4236 stdout=subprocess.PIPE,
4237 stderr=subprocess.PIPE, cwd=temp_folder)
4238 except (OSError, IOError):
4239 shutil.rmtree(temp_folder)
4240 raise CommandError("Failed to upgrade schema. Check if 'patch' is installed.")
4242 stdout, stderr = p.communicate()
4245 print("Exception in patch: %s\n%s" % (stdout, stderr))
4246 shutil.rmtree(temp_folder)
4247 raise CommandError('Failed to upgrade schema')
4249 print("Patched %s using %s" % (update, diff))
4251 if base_dir is None:
4252 base_dir = setup_path("adprep")
4254 samdb.transaction_start()
4256 error_encountered = False
4259 # Apply the schema updates needed to move to the new schema version
4260 for ldif_file in schema_updates:
4261 count += self._apply_update(samdb, ldif_file, base_dir)
4264 samdb.transaction_commit()
4265 print("Schema successfully updated")
4267 print("No changes applied to schema")
4268 samdb.transaction_cancel()
4269 except Exception as e:
4270 print("Exception: %s" % e)
4271 print("Error encountered, aborting schema upgrade")
4272 samdb.transaction_cancel()
4273 error_encountered = True
4275 if updates_allowed_overriden:
4276 lp.set("dsdb:schema update allowed", "no")
4279 shutil.rmtree(temp_folder)
4281 if error_encountered:
4282 raise CommandError('Failed to upgrade schema')
4285 class cmd_domain_functional_prep(Command):
4286 """Domain functional level preparation"""
4288 synopsis = "%prog [options]"
4290 takes_optiongroups = {
4291 "sambaopts": options.SambaOptions,
4292 "versionopts": options.VersionOptions,
4293 "credopts": options.CredentialsOptions,
4297 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
4298 metavar="URL", dest="H"),
4299 Option("-q", "--quiet", help="Be quiet", action="store_true"),
4300 Option("-v", "--verbose", help="Be verbose", action="store_true"),
4301 Option("--function-level", type="choice", metavar="FUNCTION_LEVEL",
4302 choices=["2008_R2", "2012", "2012_R2"],
4303 help="The schema file to upgrade to. Default is (Windows) 2012_R2.",
4305 Option("--forest-prep", action="store_true",
4306 help="Run the forest prep (by default, both the domain and forest prep are run)."),
4307 Option("--domain-prep", action="store_true",
4308 help="Run the domain prep (by default, both the domain and forest prep are run).")
4311 def run(self, **kwargs):
4312 updates_allowed_overriden = False
4313 sambaopts = kwargs.get("sambaopts")
4314 credopts = kwargs.get("credopts")
4315 versionpts = kwargs.get("versionopts")
4316 lp = sambaopts.get_loadparm()
4317 creds = credopts.get_credentials(lp)
4319 target_level = string_version_to_constant[kwargs.get("function_level")]
4320 forest_prep = kwargs.get("forest_prep")
4321 domain_prep = kwargs.get("domain_prep")
4323 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
4325 # we're not going to get far if the config doesn't allow schema updates
4326 if lp.get("dsdb:schema update allowed") is None:
4327 lp.set("dsdb:schema update allowed", "yes")
4328 print("Temporarily overriding 'dsdb:schema update allowed' setting")
4329 updates_allowed_overriden = True
4331 if forest_prep is None and domain_prep is None:
4335 own_dn = ldb.Dn(samdb, samdb.get_dsServiceName())
4337 master = get_fsmo_roleowner(samdb, str(samdb.get_schema_basedn()),
4339 if own_dn != master:
4340 raise CommandError("This server is not the schema master.")
4343 domain_dn = samdb.domain_dn()
4344 infrastructure_dn = "CN=Infrastructure," + domain_dn
4345 master = get_fsmo_roleowner(samdb, infrastructure_dn,
4347 if own_dn != master:
4348 raise CommandError("This server is not the infrastructure master.")
4351 samdb.transaction_start()
4352 error_encountered = False
4354 from samba.forest_update import ForestUpdate
4355 forest = ForestUpdate(samdb, fix=True)
4357 forest.check_updates_iterator([53, 79, 80, 81, 82, 83])
4358 forest.check_updates_functional_level(target_level,
4359 DS_DOMAIN_FUNCTION_2008_R2,
4360 update_revision=True)
4362 samdb.transaction_commit()
4363 except Exception as e:
4364 print("Exception: %s" % e)
4365 samdb.transaction_cancel()
4366 error_encountered = True
4369 samdb.transaction_start()
4370 error_encountered = False
4372 from samba.domain_update import DomainUpdate
4374 domain = DomainUpdate(samdb, fix=True)
4375 domain.check_updates_functional_level(target_level,
4376 DS_DOMAIN_FUNCTION_2008,
4377 update_revision=True)
4379 samdb.transaction_commit()
4380 except Exception as e:
4381 print("Exception: %s" % e)
4382 samdb.transaction_cancel()
4383 error_encountered = True
4385 if updates_allowed_overriden:
4386 lp.set("dsdb:schema update allowed", "no")
4388 if error_encountered:
4389 raise CommandError('Failed to perform functional prep')
4392 class cmd_domain(SuperCommand):
4393 """Domain management."""
4396 subcommands["demote"] = cmd_domain_demote()
4397 if cmd_domain_export_keytab is not None:
4398 subcommands["exportkeytab"] = cmd_domain_export_keytab()
4399 subcommands["info"] = cmd_domain_info()
4400 subcommands["provision"] = cmd_domain_provision()
4401 subcommands["join"] = cmd_domain_join()
4402 subcommands["dcpromo"] = cmd_domain_dcpromo()
4403 subcommands["level"] = cmd_domain_level()
4404 subcommands["passwordsettings"] = cmd_domain_passwordsettings()
4405 subcommands["classicupgrade"] = cmd_domain_classicupgrade()
4406 subcommands["samba3upgrade"] = cmd_domain_samba3upgrade()
4407 subcommands["trust"] = cmd_domain_trust()
4408 subcommands["tombstones"] = cmd_domain_tombstones()
4409 subcommands["schemaupgrade"] = cmd_domain_schema_upgrade()
4410 subcommands["functionalprep"] = cmd_domain_functional_prep()
4411 subcommands["backup"] = cmd_domain_backup()