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)",
146 def get_testparm_var(testparm, smbconf, varname):
147 errfile = open(os.devnull, 'w')
148 p = subprocess.Popen([testparm, '-s', '-l',
149 '--parameter-name=%s' % varname, smbconf],
150 stdout=subprocess.PIPE, stderr=errfile)
151 (out,err) = p.communicate()
153 lines = out.split('\n')
155 return lines[0].strip()
159 import samba.dckeytab
161 cmd_domain_export_keytab = None
163 class cmd_domain_export_keytab(Command):
164 """Dump Kerberos keys of the domain into a keytab."""
166 synopsis = "%prog <keytab> [options]"
168 takes_optiongroups = {
169 "sambaopts": options.SambaOptions,
170 "credopts": options.CredentialsOptions,
171 "versionopts": options.VersionOptions,
175 Option("--principal", help="extract only this principal", type=str),
178 takes_args = ["keytab"]
180 def run(self, keytab, credopts=None, sambaopts=None, versionopts=None, principal=None):
181 lp = sambaopts.get_loadparm()
183 net.export_keytab(keytab=keytab, principal=principal)
186 class cmd_domain_info(Command):
187 """Print basic info about a domain and the DC passed as parameter."""
189 synopsis = "%prog <ip_address> [options]"
194 takes_optiongroups = {
195 "sambaopts": options.SambaOptions,
196 "credopts": options.CredentialsOptions,
197 "versionopts": options.VersionOptions,
200 takes_args = ["address"]
202 def run(self, address, credopts=None, sambaopts=None, versionopts=None):
203 lp = sambaopts.get_loadparm()
205 res = netcmd_get_domain_infos_via_cldap(lp, None, address)
207 raise CommandError("Invalid IP address '" + address + "'!")
208 self.outf.write("Forest : %s\n" % res.forest)
209 self.outf.write("Domain : %s\n" % res.dns_domain)
210 self.outf.write("Netbios domain : %s\n" % res.domain_name)
211 self.outf.write("DC name : %s\n" % res.pdc_dns_name)
212 self.outf.write("DC netbios name : %s\n" % res.pdc_name)
213 self.outf.write("Server site : %s\n" % res.server_site)
214 self.outf.write("Client site : %s\n" % res.client_site)
217 class cmd_domain_provision(Command):
218 """Provision a domain."""
220 synopsis = "%prog [options]"
222 takes_optiongroups = {
223 "sambaopts": options.SambaOptions,
224 "versionopts": options.VersionOptions,
228 Option("--interactive", help="Ask for names", action="store_true"),
229 Option("--domain", type="string", metavar="DOMAIN",
230 help="NetBIOS domain name to use"),
231 Option("--domain-guid", type="string", metavar="GUID",
232 help="set domainguid (otherwise random)"),
233 Option("--domain-sid", type="string", metavar="SID",
234 help="set domainsid (otherwise random)"),
235 Option("--ntds-guid", type="string", metavar="GUID",
236 help="set NTDS object GUID (otherwise random)"),
237 Option("--invocationid", type="string", metavar="GUID",
238 help="set invocationid (otherwise random)"),
239 Option("--host-name", type="string", metavar="HOSTNAME",
240 help="set hostname"),
241 Option("--host-ip", type="string", metavar="IPADDRESS",
242 help="set IPv4 ipaddress"),
243 Option("--host-ip6", type="string", metavar="IP6ADDRESS",
244 help="set IPv6 ipaddress"),
245 Option("--site", type="string", metavar="SITENAME",
246 help="set site name"),
247 Option("--adminpass", type="string", metavar="PASSWORD",
248 help="choose admin password (otherwise random)"),
249 Option("--krbtgtpass", type="string", metavar="PASSWORD",
250 help="choose krbtgt password (otherwise random)"),
251 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
252 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
253 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
254 "BIND9_FLATFILE uses bind9 text database to store zone information, "
255 "BIND9_DLZ uses samba4 AD to store zone information, "
256 "NONE skips the DNS setup entirely (not recommended)",
257 default="SAMBA_INTERNAL"),
258 Option("--dnspass", type="string", metavar="PASSWORD",
259 help="choose dns password (otherwise random)"),
260 Option("--root", type="string", metavar="USERNAME",
261 help="choose 'root' unix username"),
262 Option("--nobody", type="string", metavar="USERNAME",
263 help="choose 'nobody' user"),
264 Option("--users", type="string", metavar="GROUPNAME",
265 help="choose 'users' group"),
266 Option("--blank", action="store_true",
267 help="do not add users or groups, just the structure"),
268 Option("--server-role", type="choice", metavar="ROLE",
269 choices=["domain controller", "dc", "member server", "member", "standalone"],
270 help="The server role (domain controller | dc | member server | member | standalone). Default is dc.",
271 default="domain controller"),
272 Option("--function-level", type="choice", metavar="FOR-FUN-LEVEL",
273 choices=["2000", "2003", "2008", "2008_R2"],
274 help="The domain and forest function level (2000 | 2003 | 2008 | 2008_R2 - always native). Default is (Windows) 2008_R2 Native.",
276 Option("--base-schema", type="choice", metavar="BASE-SCHEMA",
277 choices=["2008_R2", "2008_R2_old", "2012", "2012_R2"],
278 help="The base schema files to use. Default is (Windows) 2008_R2.",
280 Option("--next-rid", type="int", metavar="NEXTRID", default=1000,
281 help="The initial nextRid value (only needed for upgrades). Default is 1000."),
282 Option("--partitions-only",
283 help="Configure Samba's partitions, but do not modify them (ie, join a BDC)", action="store_true"),
284 Option("--use-rfc2307", action="store_true", help="Use AD to store posix attributes (default = no)"),
288 Option("--ldapadminpass", type="string", metavar="PASSWORD",
289 help="choose password to set between Samba and its LDAP backend (otherwise random)"),
290 Option("--ldap-backend-type", type="choice", metavar="LDAP-BACKEND-TYPE",
291 help="Test initialisation support for unsupported LDAP backend type (fedora-ds or openldap) DO NOT USE",
292 choices=["fedora-ds", "openldap"]),
293 Option("--ol-mmr-urls", type="string", metavar="LDAPSERVER",
294 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\""),
295 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",
296 action="store_true"),
297 Option("--slapd-path", type="string", metavar="SLAPD-PATH",
298 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."),
299 Option("--ldap-backend-extra-port", type="int", metavar="LDAP-BACKEND-EXTRA-PORT", help="Additional TCP port for LDAP backend server (to use for replication)"),
300 Option("--ldap-backend-forced-uri", type="string", metavar="LDAP-BACKEND-FORCED-URI",
301 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"),
302 Option("--ldap-backend-nosync", help="Configure LDAP backend not to call fsync() (for performance in test environments)", action="store_true"),
306 Option("--use-xattrs", type="choice", choices=["yes","no","auto"],
307 metavar="[yes|no|auto]",
308 help="Define if we should use the native fs capabilities or a tdb file for "
309 "storing attributes likes ntacl when --use-ntvfs is set. "
310 "auto tries to make an inteligent guess based on the user rights and system capabilities",
314 takes_options.extend(common_provision_join_options)
316 if os.getenv('TEST_LDAP', "no") == "yes":
317 takes_options.extend(openldap_options)
319 if samba.is_ntvfs_fileserver_built():
320 takes_options.extend(common_ntvfs_options)
321 takes_options.extend(ntvfs_options)
325 def run(self, sambaopts=None, versionopts=None,
348 ldap_backend_type=None,
352 partitions_only=None,
359 ldap_backend_nosync=None,
360 ldap_backend_extra_port=None,
361 ldap_backend_forced_uri=None,
362 ldap_dryrun_mode=None,
364 plaintext_secrets=False,
367 self.logger = self.get_logger("provision")
369 self.logger.setLevel(logging.WARNING)
371 self.logger.setLevel(logging.INFO)
373 lp = sambaopts.get_loadparm()
374 smbconf = lp.configfile
376 if dns_forwarder is not None:
377 suggested_forwarder = dns_forwarder
379 suggested_forwarder = self._get_nameserver_ip()
380 if suggested_forwarder is None:
381 suggested_forwarder = "none"
383 if len(self.raw_argv) == 1:
387 from getpass import getpass
390 def ask(prompt, default=None):
391 if default is not None:
392 print("%s [%s]: " % (prompt, default), end=' ')
394 print("%s: " % (prompt,), end=' ')
395 return sys.stdin.readline().rstrip("\n") or default
398 default = socket.getfqdn().split(".", 1)[1].upper()
401 realm = ask("Realm", default)
402 if realm in (None, ""):
403 raise CommandError("No realm set!")
406 default = realm.split(".")[0]
409 domain = ask("Domain", default)
411 raise CommandError("No domain set!")
413 server_role = ask("Server Role (dc, member, standalone)", "dc")
415 dns_backend = ask("DNS backend (SAMBA_INTERNAL, BIND9_FLATFILE, BIND9_DLZ, NONE)", "SAMBA_INTERNAL")
416 if dns_backend in (None, ''):
417 raise CommandError("No DNS backend set!")
419 if dns_backend == "SAMBA_INTERNAL":
420 dns_forwarder = ask("DNS forwarder IP address (write 'none' to disable forwarding)", suggested_forwarder)
421 if dns_forwarder.lower() in (None, 'none'):
422 suggested_forwarder = None
426 adminpassplain = getpass("Administrator password: ")
427 issue = self._adminpass_issue(adminpassplain)
429 self.errf.write("%s.\n" % issue)
431 adminpassverify = getpass("Retype password: ")
432 if not adminpassplain == adminpassverify:
433 self.errf.write("Sorry, passwords do not match.\n")
435 adminpass = adminpassplain
439 realm = sambaopts._lp.get('realm')
441 raise CommandError("No realm set!")
443 raise CommandError("No domain set!")
446 issue = self._adminpass_issue(adminpass)
448 raise CommandError(issue)
450 self.logger.info("Administrator password will be set randomly!")
452 if function_level == "2000":
453 dom_for_fun_level = DS_DOMAIN_FUNCTION_2000
454 elif function_level == "2003":
455 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
456 elif function_level == "2008":
457 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008
458 elif function_level == "2008_R2":
459 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
461 if dns_backend == "SAMBA_INTERNAL" and dns_forwarder is None:
462 dns_forwarder = suggested_forwarder
464 samdb_fill = FILL_FULL
466 samdb_fill = FILL_NT4SYNC
467 elif partitions_only:
468 samdb_fill = FILL_DRS
470 if targetdir is not None:
471 if not os.path.isdir(targetdir):
476 if use_xattrs == "yes":
478 elif use_xattrs == "auto" and use_ntvfs == False:
480 elif use_ntvfs == False:
481 raise CommandError("--use-xattrs=no requires --use-ntvfs (not supported for production use). "
482 "Please re-run with --use-xattrs omitted.")
483 elif use_xattrs == "auto" and not lp.get("posix:eadb"):
485 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
487 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
490 samba.ntacls.setntacl(lp, file.name,
491 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
494 self.logger.info("You are not root or your system does not support xattr, using tdb backend for attributes. ")
499 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.")
500 if ldap_backend_type == "existing":
501 if ldap_backend_forced_uri is not None:
502 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)
504 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")
506 if ldap_backend_forced_uri is not None:
507 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")
509 if domain_sid is not None:
510 domain_sid = security.dom_sid(domain_sid)
512 session = system_session()
513 if backend_store is None:
514 backend_store = get_default_backend_store()
516 result = provision(self.logger,
517 session, smbconf=smbconf, targetdir=targetdir,
518 samdb_fill=samdb_fill, realm=realm, domain=domain,
519 domainguid=domain_guid, domainsid=domain_sid,
521 hostip=host_ip, hostip6=host_ip6,
522 sitename=site, ntdsguid=ntds_guid,
523 invocationid=invocationid, adminpass=adminpass,
524 krbtgtpass=krbtgtpass, machinepass=machinepass,
525 dns_backend=dns_backend, dns_forwarder=dns_forwarder,
526 dnspass=dnspass, root=root, nobody=nobody,
528 serverrole=server_role, dom_for_fun_level=dom_for_fun_level,
529 backend_type=ldap_backend_type,
530 ldapadminpass=ldapadminpass, ol_mmr_urls=ol_mmr_urls, slapd_path=slapd_path,
531 useeadb=eadb, next_rid=next_rid, lp=lp, use_ntvfs=use_ntvfs,
532 use_rfc2307=use_rfc2307, skip_sysvolacl=False,
533 ldap_backend_extra_port=ldap_backend_extra_port,
534 ldap_backend_forced_uri=ldap_backend_forced_uri,
535 nosync=ldap_backend_nosync, ldap_dryrun_mode=ldap_dryrun_mode,
536 base_schema=base_schema,
537 plaintext_secrets=plaintext_secrets,
538 backend_store=backend_store)
540 except ProvisioningError as e:
541 raise CommandError("Provision failed", e)
543 result.report_logger(self.logger)
545 def _get_nameserver_ip(self):
546 """Grab the nameserver IP address from /etc/resolv.conf."""
548 RESOLV_CONF="/etc/resolv.conf"
550 if not path.isfile(RESOLV_CONF):
551 self.logger.warning("Failed to locate %s" % RESOLV_CONF)
556 handle = open(RESOLV_CONF, 'r')
558 if not line.startswith('nameserver'):
560 # we want the last non-space continuous string of the line
561 return line.strip().split()[-1]
563 if handle is not None:
566 self.logger.warning("No nameserver found in %s" % RESOLV_CONF)
568 def _adminpass_issue(self, adminpass):
569 """Returns error string for a bad administrator password,
570 or None if acceptable"""
572 if len(adminpass.decode('utf-8')) < DEFAULT_MIN_PWD_LENGTH:
573 return "Administrator password does not meet the default minimum" \
574 " password length requirement (%d characters)" \
575 % DEFAULT_MIN_PWD_LENGTH
576 elif not samba.check_password_quality(adminpass):
577 return "Administrator password does not meet the default" \
583 class cmd_domain_dcpromo(Command):
584 """Promote an existing domain member or NT4 PDC to an AD DC."""
586 synopsis = "%prog <dnsdomain> [DC|RODC] [options]"
588 takes_optiongroups = {
589 "sambaopts": options.SambaOptions,
590 "versionopts": options.VersionOptions,
591 "credopts": options.CredentialsOptions,
595 takes_options.extend(common_join_options)
597 takes_options.extend(common_provision_join_options)
599 if samba.is_ntvfs_fileserver_built():
600 takes_options.extend(common_ntvfs_options)
603 takes_args = ["domain", "role?"]
605 def run(self, domain, role=None, sambaopts=None, credopts=None,
606 versionopts=None, server=None, site=None, targetdir=None,
607 domain_critical_only=False, parent_domain=None, machinepass=None,
608 use_ntvfs=False, dns_backend=None,
609 quiet=False, verbose=False, plaintext_secrets=False,
611 lp = sambaopts.get_loadparm()
612 creds = credopts.get_credentials(lp)
613 net = Net(creds, lp, server=credopts.ipaddress)
615 logger = self.get_logger()
617 logger.setLevel(logging.DEBUG)
619 logger.setLevel(logging.WARNING)
621 logger.setLevel(logging.INFO)
623 netbios_name = lp.get("netbios name")
629 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
630 site=site, netbios_name=netbios_name, targetdir=targetdir,
631 domain_critical_only=domain_critical_only,
632 machinepass=machinepass, use_ntvfs=use_ntvfs,
633 dns_backend=dns_backend,
634 promote_existing=True, plaintext_secrets=plaintext_secrets,
635 backend_store=backend_store)
637 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
638 site=site, netbios_name=netbios_name, targetdir=targetdir,
639 domain_critical_only=domain_critical_only,
640 machinepass=machinepass, use_ntvfs=use_ntvfs, dns_backend=dns_backend,
641 promote_existing=True, plaintext_secrets=plaintext_secrets,
642 backend_store=backend_store)
644 raise CommandError("Invalid role '%s' (possible values: DC, RODC)" % role)
647 class cmd_domain_join(Command):
648 """Join domain as either member or backup domain controller."""
650 synopsis = "%prog <dnsdomain> [DC|RODC|MEMBER|SUBDOMAIN] [options]"
652 takes_optiongroups = {
653 "sambaopts": options.SambaOptions,
654 "versionopts": options.VersionOptions,
655 "credopts": options.CredentialsOptions,
659 Option("--parent-domain", help="parent domain to create subdomain under", type=str),
660 Option("--adminpass", type="string", metavar="PASSWORD",
661 help="choose adminstrator password when joining as a subdomain (otherwise random)"),
665 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
668 takes_options.extend(common_join_options)
669 takes_options.extend(common_provision_join_options)
671 if samba.is_ntvfs_fileserver_built():
672 takes_options.extend(ntvfs_options)
674 takes_args = ["domain", "role?"]
676 def run(self, domain, role=None, sambaopts=None, credopts=None,
677 versionopts=None, server=None, site=None, targetdir=None,
678 domain_critical_only=False, parent_domain=None, machinepass=None,
679 use_ntvfs=False, dns_backend=None, adminpass=None,
680 quiet=False, verbose=False,
681 plaintext_secrets=False,
683 lp = sambaopts.get_loadparm()
684 creds = credopts.get_credentials(lp)
685 net = Net(creds, lp, server=credopts.ipaddress)
688 site = "Default-First-Site-Name"
690 logger = self.get_logger()
692 logger.setLevel(logging.DEBUG)
694 logger.setLevel(logging.WARNING)
696 logger.setLevel(logging.INFO)
698 netbios_name = lp.get("netbios name")
703 if role is None or role == "MEMBER":
704 (join_password, sid, domain_name) = net.join_member(
705 domain, netbios_name, LIBNET_JOIN_AUTOMATIC,
706 machinepass=machinepass)
708 self.errf.write("Joined domain %s (%s)\n" % (domain_name, sid))
710 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
711 site=site, netbios_name=netbios_name, targetdir=targetdir,
712 domain_critical_only=domain_critical_only,
713 machinepass=machinepass, use_ntvfs=use_ntvfs,
714 dns_backend=dns_backend,
715 plaintext_secrets=plaintext_secrets,
716 backend_store=backend_store)
718 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
719 site=site, netbios_name=netbios_name, targetdir=targetdir,
720 domain_critical_only=domain_critical_only,
721 machinepass=machinepass, use_ntvfs=use_ntvfs,
722 dns_backend=dns_backend,
723 plaintext_secrets=plaintext_secrets,
724 backend_store=backend_store)
725 elif role == "SUBDOMAIN":
727 logger.info("Administrator password will be set randomly!")
729 netbios_domain = lp.get("workgroup")
730 if parent_domain is None:
731 parent_domain = ".".join(domain.split(".")[1:])
732 join_subdomain(logger=logger, server=server, creds=creds, lp=lp, dnsdomain=domain,
733 parent_domain=parent_domain, site=site,
734 netbios_name=netbios_name, netbios_domain=netbios_domain,
735 targetdir=targetdir, machinepass=machinepass,
736 use_ntvfs=use_ntvfs, dns_backend=dns_backend,
738 plaintext_secrets=plaintext_secrets,
739 backend_store=backend_store)
741 raise CommandError("Invalid role '%s' (possible values: MEMBER, DC, RODC, SUBDOMAIN)" % role)
744 class cmd_domain_demote(Command):
745 """Demote ourselves from the role of Domain Controller."""
747 synopsis = "%prog [options]"
750 Option("--server", help="writable DC to write demotion changes on", type=str),
751 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
752 metavar="URL", dest="H"),
753 Option("--remove-other-dead-server", help="Dead DC (name or NTDS GUID) "
754 "to remove ALL references to (rather than this DC)", type=str),
755 Option("-q", "--quiet", help="Be quiet", action="store_true"),
756 Option("-v", "--verbose", help="Be verbose", action="store_true"),
759 takes_optiongroups = {
760 "sambaopts": options.SambaOptions,
761 "credopts": options.CredentialsOptions,
762 "versionopts": options.VersionOptions,
765 def run(self, sambaopts=None, credopts=None,
766 versionopts=None, server=None,
767 remove_other_dead_server=None, H=None,
768 verbose=False, quiet=False):
769 lp = sambaopts.get_loadparm()
770 creds = credopts.get_credentials(lp)
771 net = Net(creds, lp, server=credopts.ipaddress)
773 logger = self.get_logger()
775 logger.setLevel(logging.DEBUG)
777 logger.setLevel(logging.WARNING)
779 logger.setLevel(logging.INFO)
781 if remove_other_dead_server is not None:
782 if server is not None:
783 samdb = SamDB(url="ldap://%s" % server,
784 session_info=system_session(),
785 credentials=creds, lp=lp)
787 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
789 remove_dc.remove_dc(samdb, logger, remove_other_dead_server)
790 except remove_dc.DemoteException as err:
791 raise CommandError("Demote failed: %s" % err)
794 netbios_name = lp.get("netbios name")
795 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
797 res = samdb.search(expression='(&(objectClass=computer)(serverReferenceBL=*))', attrs=["dnsHostName", "name"])
799 raise CommandError("Unable to search for servers")
802 raise CommandError("You are the last server in the domain")
806 if str(e["name"]).lower() != netbios_name.lower():
807 server = e["dnsHostName"]
810 ntds_guid = samdb.get_ntds_GUID()
811 msg = samdb.search(base=str(samdb.get_config_basedn()),
812 scope=ldb.SCOPE_SUBTREE, expression="(objectGUID=%s)" % ntds_guid,
814 if len(msg) == 0 or "options" not in msg[0]:
815 raise CommandError("Failed to find options on %s" % ntds_guid)
818 dsa_options = int(str(msg[0]['options']))
820 res = samdb.search(expression="(fSMORoleOwner=%s)" % str(ntds_dn),
821 controls=["search_options:1:2"])
824 raise CommandError("Current DC is still the owner of %d role(s), "
825 "use the role command to transfer roles to "
829 self.errf.write("Using %s as partner server for the demotion\n" %
831 (drsuapiBind, drsuapi_handle, supportedExtensions) = drsuapi_connect(server, lp, creds)
833 self.errf.write("Deactivating inbound replication\n")
838 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
839 dsa_options |= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
840 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
844 self.errf.write("Asking partner server %s to synchronize from us\n"
846 for part in (samdb.get_schema_basedn(),
847 samdb.get_config_basedn(),
848 samdb.get_root_basedn()):
849 nc = drsuapi.DsReplicaObjectIdentifier()
852 req1 = drsuapi.DsReplicaSyncRequest1()
853 req1.naming_context = nc;
854 req1.options = drsuapi.DRSUAPI_DRS_WRIT_REP
855 req1.source_dsa_guid = misc.GUID(ntds_guid)
858 drsuapiBind.DsReplicaSync(drsuapi_handle, 1, req1)
859 except RuntimeError as e1:
860 (werr, string) = e1.args
861 if werr == werror.WERR_DS_DRA_NO_REPLICA:
865 "Error while replicating out last local changes from '%s' for demotion, "
866 "re-enabling inbound replication\n" % part)
867 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
868 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
870 raise CommandError("Error while sending a DsReplicaSync for partition '%s'" % str(part), string)
872 remote_samdb = SamDB(url="ldap://%s" % server,
873 session_info=system_session(),
874 credentials=creds, lp=lp)
876 self.errf.write("Changing userControl and container\n")
877 res = remote_samdb.search(base=str(remote_samdb.domain_dn()),
878 expression="(&(objectClass=user)(sAMAccountName=%s$))" %
879 netbios_name.upper(),
880 attrs=["userAccountControl"])
882 uac = int(str(res[0]["userAccountControl"]))
884 except Exception as e:
885 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
887 "Error while demoting, re-enabling inbound replication\n")
888 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
889 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
891 raise CommandError("Error while changing account control", e)
894 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
896 "Error while demoting, re-enabling inbound replication")
897 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
898 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
900 raise CommandError("Unable to find object with samaccountName = %s$"
901 " in the remote dc" % netbios_name.upper())
905 uac &= ~(UF_SERVER_TRUST_ACCOUNT|UF_TRUSTED_FOR_DELEGATION|UF_PARTIAL_SECRETS_ACCOUNT)
906 uac |= UF_WORKSTATION_TRUST_ACCOUNT
911 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
912 ldb.FLAG_MOD_REPLACE,
913 "userAccountControl")
915 remote_samdb.modify(msg)
916 except Exception as e:
917 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
919 "Error while demoting, re-enabling inbound replication")
920 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
921 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
924 raise CommandError("Error while changing account control", e)
926 parent = msg.dn.parent()
927 dc_name = res[0].dn.get_rdn_value()
928 rdn = "CN=%s" % dc_name
930 # Let's move to the Computer container
934 computer_dn = ldb.Dn(remote_samdb, "CN=Computers,%s" % str(remote_samdb.domain_dn()))
935 res = remote_samdb.search(base=computer_dn, expression=rdn, scope=ldb.SCOPE_ONELEVEL)
938 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
939 scope=ldb.SCOPE_ONELEVEL)
940 while(len(res) != 0 and i < 100):
942 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
943 scope=ldb.SCOPE_ONELEVEL)
946 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
948 "Error while demoting, re-enabling inbound replication\n")
949 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
950 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
956 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
957 ldb.FLAG_MOD_REPLACE,
958 "userAccountControl")
960 remote_samdb.modify(msg)
962 raise CommandError("Unable to find a slot for renaming %s,"
963 " all names from %s-1 to %s-%d seemed used" %
964 (str(dc_dn), rdn, rdn, i - 9))
966 newrdn = "%s-%d" % (rdn, i)
969 newdn = ldb.Dn(remote_samdb, "%s,%s" % (newrdn, str(computer_dn)))
970 remote_samdb.rename(dc_dn, newdn)
971 except Exception as e:
972 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
974 "Error while demoting, re-enabling inbound replication\n")
975 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
976 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
982 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
983 ldb.FLAG_MOD_REPLACE,
984 "userAccountControl")
986 remote_samdb.modify(msg)
987 raise CommandError("Error while renaming %s to %s" % (str(dc_dn), str(newdn)), e)
990 server_dsa_dn = samdb.get_serverName()
991 domain = remote_samdb.get_root_basedn()
994 req1 = drsuapi.DsRemoveDSServerRequest1()
995 req1.server_dn = str(server_dsa_dn)
996 req1.domain_dn = str(domain)
999 drsuapiBind.DsRemoveDSServer(drsuapi_handle, 1, req1)
1000 except RuntimeError as e3:
1001 (werr, string) = e3.args
1002 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
1004 "Error while demoting, re-enabling inbound replication\n")
1005 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
1006 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
1012 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
1013 ldb.FLAG_MOD_REPLACE,
1014 "userAccountControl")
1015 remote_samdb.modify(msg)
1016 remote_samdb.rename(newdn, dc_dn)
1017 if werr == werror.WERR_DS_DRA_NO_REPLICA:
1018 raise CommandError("The DC %s is not present on (already "
1019 "removed from) the remote server: %s" %
1020 (server_dsa_dn, e3))
1022 raise CommandError("Error while sending a removeDsServer "
1024 (server_dsa_dn, e3))
1026 remove_dc.remove_sysvol_references(remote_samdb, logger, dc_name)
1028 # These are objects under the computer account that should be deleted
1029 for s in ("CN=Enterprise,CN=NTFRS Subscriptions",
1030 "CN=%s, CN=NTFRS Subscriptions" % lp.get("realm"),
1031 "CN=Domain system Volumes (SYSVOL Share), CN=NTFRS Subscriptions",
1032 "CN=NTFRS Subscriptions"):
1034 remote_samdb.delete(ldb.Dn(remote_samdb,
1035 "%s,%s" % (s, str(newdn))))
1036 except ldb.LdbError as l:
1039 # get dns host name for target server to demote, remove dns references
1040 remove_dc.remove_dns_references(remote_samdb, logger, samdb.host_dns_name(),
1041 ignore_no_name=True)
1043 self.errf.write("Demote successful\n")
1046 class cmd_domain_level(Command):
1047 """Raise domain and forest function levels."""
1049 synopsis = "%prog (show|raise <options>) [options]"
1051 takes_optiongroups = {
1052 "sambaopts": options.SambaOptions,
1053 "credopts": options.CredentialsOptions,
1054 "versionopts": options.VersionOptions,
1058 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1059 metavar="URL", dest="H"),
1060 Option("-q", "--quiet", help="Be quiet", action="store_true"), # unused
1061 Option("--forest-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
1062 help="The forest function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)"),
1063 Option("--domain-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
1064 help="The domain function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)")
1067 takes_args = ["subcommand"]
1069 def run(self, subcommand, H=None, forest_level=None, domain_level=None,
1070 quiet=False, credopts=None, sambaopts=None, versionopts=None):
1071 lp = sambaopts.get_loadparm()
1072 creds = credopts.get_credentials(lp, fallback_machine=True)
1074 samdb = SamDB(url=H, session_info=system_session(),
1075 credentials=creds, lp=lp)
1077 domain_dn = samdb.domain_dn()
1079 res_forest = samdb.search("CN=Partitions,%s" % samdb.get_config_basedn(),
1080 scope=ldb.SCOPE_BASE, attrs=["msDS-Behavior-Version"])
1081 assert len(res_forest) == 1
1083 res_domain = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1084 attrs=["msDS-Behavior-Version", "nTMixedDomain"])
1085 assert len(res_domain) == 1
1087 res_dc_s = samdb.search("CN=Sites,%s" % samdb.get_config_basedn(),
1088 scope=ldb.SCOPE_SUBTREE, expression="(objectClass=nTDSDSA)",
1089 attrs=["msDS-Behavior-Version"])
1090 assert len(res_dc_s) >= 1
1092 # default values, since "msDS-Behavior-Version" does not exist on Windows 2000 AD
1093 level_forest = DS_DOMAIN_FUNCTION_2000
1094 level_domain = DS_DOMAIN_FUNCTION_2000
1096 if "msDS-Behavior-Version" in res_forest[0]:
1097 level_forest = int(res_forest[0]["msDS-Behavior-Version"][0])
1098 if "msDS-Behavior-Version" in res_domain[0]:
1099 level_domain = int(res_domain[0]["msDS-Behavior-Version"][0])
1100 level_domain_mixed = int(res_domain[0]["nTMixedDomain"][0])
1103 for msg in res_dc_s:
1104 if "msDS-Behavior-Version" in msg:
1105 if min_level_dc is None or int(msg["msDS-Behavior-Version"][0]) < min_level_dc:
1106 min_level_dc = int(msg["msDS-Behavior-Version"][0])
1108 min_level_dc = DS_DOMAIN_FUNCTION_2000
1109 # well, this is the least
1112 if level_forest < DS_DOMAIN_FUNCTION_2000 or level_domain < DS_DOMAIN_FUNCTION_2000:
1113 raise CommandError("Domain and/or forest function level(s) is/are invalid. Correct them or reprovision!")
1114 if min_level_dc < DS_DOMAIN_FUNCTION_2000:
1115 raise CommandError("Lowest function level of a DC is invalid. Correct this or reprovision!")
1116 if level_forest > level_domain:
1117 raise CommandError("Forest function level is higher than the domain level(s). Correct this or reprovision!")
1118 if level_domain > min_level_dc:
1119 raise CommandError("Domain function level is higher than the lowest function level of a DC. Correct this or reprovision!")
1121 if subcommand == "show":
1122 self.message("Domain and forest function level for domain '%s'" % domain_dn)
1123 if level_forest == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1124 self.message("\nATTENTION: You run SAMBA 4 on a forest function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1125 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1126 self.message("\nATTENTION: You run SAMBA 4 on a domain function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1127 if min_level_dc == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1128 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)!")
1132 if level_forest == DS_DOMAIN_FUNCTION_2000:
1134 elif level_forest == DS_DOMAIN_FUNCTION_2003_MIXED:
1135 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1136 elif level_forest == DS_DOMAIN_FUNCTION_2003:
1138 elif level_forest == DS_DOMAIN_FUNCTION_2008:
1140 elif level_forest == DS_DOMAIN_FUNCTION_2008_R2:
1142 elif level_forest == DS_DOMAIN_FUNCTION_2012:
1144 elif level_forest == DS_DOMAIN_FUNCTION_2012_R2:
1147 outstr = "higher than 2012 R2"
1148 self.message("Forest function level: (Windows) " + outstr)
1150 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1151 outstr = "2000 mixed (NT4 DC support)"
1152 elif level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed == 0:
1154 elif level_domain == DS_DOMAIN_FUNCTION_2003_MIXED:
1155 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1156 elif level_domain == DS_DOMAIN_FUNCTION_2003:
1158 elif level_domain == DS_DOMAIN_FUNCTION_2008:
1160 elif level_domain == DS_DOMAIN_FUNCTION_2008_R2:
1162 elif level_domain == DS_DOMAIN_FUNCTION_2012:
1164 elif level_domain == DS_DOMAIN_FUNCTION_2012_R2:
1167 outstr = "higher than 2012 R2"
1168 self.message("Domain function level: (Windows) " + outstr)
1170 if min_level_dc == DS_DOMAIN_FUNCTION_2000:
1172 elif min_level_dc == DS_DOMAIN_FUNCTION_2003:
1174 elif min_level_dc == DS_DOMAIN_FUNCTION_2008:
1176 elif min_level_dc == DS_DOMAIN_FUNCTION_2008_R2:
1178 elif min_level_dc == DS_DOMAIN_FUNCTION_2012:
1180 elif min_level_dc == DS_DOMAIN_FUNCTION_2012_R2:
1183 outstr = "higher than 2012 R2"
1184 self.message("Lowest function level of a DC: (Windows) " + outstr)
1186 elif subcommand == "raise":
1189 if domain_level is not None:
1190 if domain_level == "2003":
1191 new_level_domain = DS_DOMAIN_FUNCTION_2003
1192 elif domain_level == "2008":
1193 new_level_domain = DS_DOMAIN_FUNCTION_2008
1194 elif domain_level == "2008_R2":
1195 new_level_domain = DS_DOMAIN_FUNCTION_2008_R2
1196 elif domain_level == "2012":
1197 new_level_domain = DS_DOMAIN_FUNCTION_2012
1198 elif domain_level == "2012_R2":
1199 new_level_domain = DS_DOMAIN_FUNCTION_2012_R2
1201 if new_level_domain <= level_domain and level_domain_mixed == 0:
1202 raise CommandError("Domain function level can't be smaller than or equal to the actual one!")
1203 if new_level_domain > min_level_dc:
1204 raise CommandError("Domain function level can't be higher than the lowest function level of a DC!")
1206 # Deactivate mixed/interim domain support
1207 if level_domain_mixed != 0:
1208 # Directly on the base DN
1210 m.dn = ldb.Dn(samdb, domain_dn)
1211 m["nTMixedDomain"] = ldb.MessageElement("0",
1212 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1216 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup") + ",CN=Partitions,%s" % samdb.get_config_basedn())
1217 m["nTMixedDomain"] = ldb.MessageElement("0",
1218 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1221 except ldb.LdbError as e:
1222 (enum, emsg) = e.args
1223 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1226 # Directly on the base DN
1228 m.dn = ldb.Dn(samdb, domain_dn)
1229 m["msDS-Behavior-Version"]= ldb.MessageElement(
1230 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1231 "msDS-Behavior-Version")
1235 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup")
1236 + ",CN=Partitions,%s" % samdb.get_config_basedn())
1237 m["msDS-Behavior-Version"]= ldb.MessageElement(
1238 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1239 "msDS-Behavior-Version")
1242 except ldb.LdbError as e2:
1243 (enum, emsg) = e2.args
1244 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1247 level_domain = new_level_domain
1248 msgs.append("Domain function level changed!")
1250 if forest_level is not None:
1251 if forest_level == "2003":
1252 new_level_forest = DS_DOMAIN_FUNCTION_2003
1253 elif forest_level == "2008":
1254 new_level_forest = DS_DOMAIN_FUNCTION_2008
1255 elif forest_level == "2008_R2":
1256 new_level_forest = DS_DOMAIN_FUNCTION_2008_R2
1257 elif forest_level == "2012":
1258 new_level_forest = DS_DOMAIN_FUNCTION_2012
1259 elif forest_level == "2012_R2":
1260 new_level_forest = DS_DOMAIN_FUNCTION_2012_R2
1262 if new_level_forest <= level_forest:
1263 raise CommandError("Forest function level can't be smaller than or equal to the actual one!")
1264 if new_level_forest > level_domain:
1265 raise CommandError("Forest function level can't be higher than the domain function level(s). Please raise it/them first!")
1268 m.dn = ldb.Dn(samdb, "CN=Partitions,%s" % samdb.get_config_basedn())
1269 m["msDS-Behavior-Version"]= ldb.MessageElement(
1270 str(new_level_forest), ldb.FLAG_MOD_REPLACE,
1271 "msDS-Behavior-Version")
1273 msgs.append("Forest function level changed!")
1274 msgs.append("All changes applied successfully!")
1275 self.message("\n".join(msgs))
1277 raise CommandError("invalid argument: '%s' (choose from 'show', 'raise')" % subcommand)
1279 class cmd_domain_passwordsettings_show(Command):
1280 """Display current password settings for the domain."""
1282 synopsis = "%prog [options]"
1284 takes_optiongroups = {
1285 "sambaopts": options.SambaOptions,
1286 "versionopts": options.VersionOptions,
1287 "credopts": options.CredentialsOptions,
1291 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1292 metavar="URL", dest="H"),
1295 def run(self, H=None, credopts=None, sambaopts=None, versionopts=None):
1296 lp = sambaopts.get_loadparm()
1297 creds = credopts.get_credentials(lp)
1299 samdb = SamDB(url=H, session_info=system_session(),
1300 credentials=creds, lp=lp)
1302 domain_dn = samdb.domain_dn()
1303 res = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1304 attrs=["pwdProperties", "pwdHistoryLength", "minPwdLength",
1305 "minPwdAge", "maxPwdAge", "lockoutDuration", "lockoutThreshold",
1306 "lockOutObservationWindow"])
1307 assert(len(res) == 1)
1309 pwd_props = int(res[0]["pwdProperties"][0])
1310 pwd_hist_len = int(res[0]["pwdHistoryLength"][0])
1311 cur_min_pwd_len = int(res[0]["minPwdLength"][0])
1313 cur_min_pwd_age = int(abs(int(res[0]["minPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1314 if int(res[0]["maxPwdAge"][0]) == -0x8000000000000000:
1317 cur_max_pwd_age = int(abs(int(res[0]["maxPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1318 cur_account_lockout_threshold = int(res[0]["lockoutThreshold"][0])
1320 if int(res[0]["lockoutDuration"][0]) == -0x8000000000000000:
1321 cur_account_lockout_duration = 0
1323 cur_account_lockout_duration = abs(int(res[0]["lockoutDuration"][0])) / (1e7 * 60)
1324 cur_reset_account_lockout_after = abs(int(res[0]["lockOutObservationWindow"][0])) / (1e7 * 60)
1325 except Exception as e:
1326 raise CommandError("Could not retrieve password properties!", e)
1328 self.message("Password informations for domain '%s'" % domain_dn)
1330 if pwd_props & DOMAIN_PASSWORD_COMPLEX != 0:
1331 self.message("Password complexity: on")
1333 self.message("Password complexity: off")
1334 if pwd_props & DOMAIN_PASSWORD_STORE_CLEARTEXT != 0:
1335 self.message("Store plaintext passwords: on")
1337 self.message("Store plaintext passwords: off")
1338 self.message("Password history length: %d" % pwd_hist_len)
1339 self.message("Minimum password length: %d" % cur_min_pwd_len)
1340 self.message("Minimum password age (days): %d" % cur_min_pwd_age)
1341 self.message("Maximum password age (days): %d" % cur_max_pwd_age)
1342 self.message("Account lockout duration (mins): %d" % cur_account_lockout_duration)
1343 self.message("Account lockout threshold (attempts): %d" % cur_account_lockout_threshold)
1344 self.message("Reset account lockout after (mins): %d" % cur_reset_account_lockout_after)
1346 class cmd_domain_passwordsettings_set(Command):
1347 """Set password settings.
1349 Password complexity, password lockout policy, history length,
1350 minimum password length, the minimum and maximum password age) on
1351 a Samba AD DC server.
1353 Use against a Windows DC is possible, but group policy will override it.
1356 synopsis = "%prog <options> [options]"
1358 takes_optiongroups = {
1359 "sambaopts": options.SambaOptions,
1360 "versionopts": options.VersionOptions,
1361 "credopts": options.CredentialsOptions,
1365 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1366 metavar="URL", dest="H"),
1367 Option("-q", "--quiet", help="Be quiet", action="store_true"), # unused
1368 Option("--complexity", type="choice", choices=["on","off","default"],
1369 help="The password complexity (on | off | default). Default is 'on'"),
1370 Option("--store-plaintext", type="choice", choices=["on","off","default"],
1371 help="Store plaintext passwords where account have 'store passwords with reversible encryption' set (on | off | default). Default is 'off'"),
1372 Option("--history-length",
1373 help="The password history length (<integer> | default). Default is 24.", type=str),
1374 Option("--min-pwd-length",
1375 help="The minimum password length (<integer> | default). Default is 7.", type=str),
1376 Option("--min-pwd-age",
1377 help="The minimum password age (<integer in days> | default). Default is 1.", type=str),
1378 Option("--max-pwd-age",
1379 help="The maximum password age (<integer in days> | default). Default is 43.", type=str),
1380 Option("--account-lockout-duration",
1381 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),
1382 Option("--account-lockout-threshold",
1383 help="The number of bad password attempts allowed before locking out the account (<integer> | default). Default is 0 (never lock out).", type=str),
1384 Option("--reset-account-lockout-after",
1385 help="After this time is elapsed, the recorded number of attempts restarts from zero (<integer> | default). Default is 30.", type=str),
1388 def run(self, H=None, min_pwd_age=None, max_pwd_age=None,
1389 quiet=False, complexity=None, store_plaintext=None, history_length=None,
1390 min_pwd_length=None, account_lockout_duration=None, account_lockout_threshold=None,
1391 reset_account_lockout_after=None, credopts=None, sambaopts=None,
1393 lp = sambaopts.get_loadparm()
1394 creds = credopts.get_credentials(lp)
1396 samdb = SamDB(url=H, session_info=system_session(),
1397 credentials=creds, lp=lp)
1399 domain_dn = samdb.domain_dn()
1402 m.dn = ldb.Dn(samdb, domain_dn)
1403 pwd_props = int(samdb.get_pwdProperties())
1405 if complexity is not None:
1406 if complexity == "on" or complexity == "default":
1407 pwd_props = pwd_props | DOMAIN_PASSWORD_COMPLEX
1408 msgs.append("Password complexity activated!")
1409 elif complexity == "off":
1410 pwd_props = pwd_props & (~DOMAIN_PASSWORD_COMPLEX)
1411 msgs.append("Password complexity deactivated!")
1413 if store_plaintext is not None:
1414 if store_plaintext == "on" or store_plaintext == "default":
1415 pwd_props = pwd_props | DOMAIN_PASSWORD_STORE_CLEARTEXT
1416 msgs.append("Plaintext password storage for changed passwords activated!")
1417 elif store_plaintext == "off":
1418 pwd_props = pwd_props & (~DOMAIN_PASSWORD_STORE_CLEARTEXT)
1419 msgs.append("Plaintext password storage for changed passwords deactivated!")
1421 if complexity is not None or store_plaintext is not None:
1422 m["pwdProperties"] = ldb.MessageElement(str(pwd_props),
1423 ldb.FLAG_MOD_REPLACE, "pwdProperties")
1425 if history_length is not None:
1426 if history_length == "default":
1429 pwd_hist_len = int(history_length)
1431 if pwd_hist_len < 0 or pwd_hist_len > 24:
1432 raise CommandError("Password history length must be in the range of 0 to 24!")
1434 m["pwdHistoryLength"] = ldb.MessageElement(str(pwd_hist_len),
1435 ldb.FLAG_MOD_REPLACE, "pwdHistoryLength")
1436 msgs.append("Password history length changed!")
1438 if min_pwd_length is not None:
1439 if min_pwd_length == "default":
1442 min_pwd_len = int(min_pwd_length)
1444 if min_pwd_len < 0 or min_pwd_len > 14:
1445 raise CommandError("Minimum password length must be in the range of 0 to 14!")
1447 m["minPwdLength"] = ldb.MessageElement(str(min_pwd_len),
1448 ldb.FLAG_MOD_REPLACE, "minPwdLength")
1449 msgs.append("Minimum password length changed!")
1451 if min_pwd_age is not None:
1452 if min_pwd_age == "default":
1455 min_pwd_age = int(min_pwd_age)
1457 if min_pwd_age < 0 or min_pwd_age > 998:
1458 raise CommandError("Minimum password age must be in the range of 0 to 998!")
1461 min_pwd_age_ticks = -int(min_pwd_age * (24 * 60 * 60 * 1e7))
1463 m["minPwdAge"] = ldb.MessageElement(str(min_pwd_age_ticks),
1464 ldb.FLAG_MOD_REPLACE, "minPwdAge")
1465 msgs.append("Minimum password age changed!")
1467 if max_pwd_age is not None:
1468 if max_pwd_age == "default":
1471 max_pwd_age = int(max_pwd_age)
1473 if max_pwd_age < 0 or max_pwd_age > 999:
1474 raise CommandError("Maximum password age must be in the range of 0 to 999!")
1477 if max_pwd_age == 0:
1478 max_pwd_age_ticks = -0x8000000000000000
1480 max_pwd_age_ticks = -int(max_pwd_age * (24 * 60 * 60 * 1e7))
1482 m["maxPwdAge"] = ldb.MessageElement(str(max_pwd_age_ticks),
1483 ldb.FLAG_MOD_REPLACE, "maxPwdAge")
1484 msgs.append("Maximum password age changed!")
1486 if account_lockout_duration is not None:
1487 if account_lockout_duration == "default":
1488 account_lockout_duration = 30
1490 account_lockout_duration = int(account_lockout_duration)
1492 if account_lockout_duration < 0 or account_lockout_duration > 99999:
1493 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1496 if account_lockout_duration == 0:
1497 account_lockout_duration_ticks = -0x8000000000000000
1499 account_lockout_duration_ticks = -int(account_lockout_duration * (60 * 1e7))
1501 m["lockoutDuration"] = ldb.MessageElement(str(account_lockout_duration_ticks),
1502 ldb.FLAG_MOD_REPLACE, "lockoutDuration")
1503 msgs.append("Account lockout duration changed!")
1505 if account_lockout_threshold is not None:
1506 if account_lockout_threshold == "default":
1507 account_lockout_threshold = 0
1509 account_lockout_threshold = int(account_lockout_threshold)
1511 m["lockoutThreshold"] = ldb.MessageElement(str(account_lockout_threshold),
1512 ldb.FLAG_MOD_REPLACE, "lockoutThreshold")
1513 msgs.append("Account lockout threshold changed!")
1515 if reset_account_lockout_after is not None:
1516 if reset_account_lockout_after == "default":
1517 reset_account_lockout_after = 30
1519 reset_account_lockout_after = int(reset_account_lockout_after)
1521 if reset_account_lockout_after < 0 or reset_account_lockout_after > 99999:
1522 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1525 if reset_account_lockout_after == 0:
1526 reset_account_lockout_after_ticks = -0x8000000000000000
1528 reset_account_lockout_after_ticks = -int(reset_account_lockout_after * (60 * 1e7))
1530 m["lockOutObservationWindow"] = ldb.MessageElement(str(reset_account_lockout_after_ticks),
1531 ldb.FLAG_MOD_REPLACE, "lockOutObservationWindow")
1532 msgs.append("Duration to reset account lockout after changed!")
1534 if max_pwd_age and max_pwd_age > 0 and min_pwd_age >= max_pwd_age:
1535 raise CommandError("Maximum password age (%d) must be greater than minimum password age (%d)!" % (max_pwd_age, min_pwd_age))
1538 raise CommandError("You must specify at least one option to set. Try --help")
1540 msgs.append("All changes applied successfully!")
1541 self.message("\n".join(msgs))
1543 class cmd_domain_passwordsettings(SuperCommand):
1544 """Manage password policy settings."""
1547 subcommands["pso"] = cmd_domain_passwordsettings_pso()
1548 subcommands["show"] = cmd_domain_passwordsettings_show()
1549 subcommands["set"] = cmd_domain_passwordsettings_set()
1551 class cmd_domain_classicupgrade(Command):
1552 """Upgrade from Samba classic (NT4-like) database to Samba AD DC database.
1554 Specify either a directory with all Samba classic DC databases and state files (with --dbdir) or
1555 the testparm utility from your classic installation (with --testparm).
1558 synopsis = "%prog [options] <classic_smb_conf>"
1560 takes_optiongroups = {
1561 "sambaopts": options.SambaOptions,
1562 "versionopts": options.VersionOptions
1566 Option("--dbdir", type="string", metavar="DIR",
1567 help="Path to samba classic DC database directory"),
1568 Option("--testparm", type="string", metavar="PATH",
1569 help="Path to samba classic DC testparm utility from the previous installation. This allows the default paths of the previous installation to be followed"),
1570 Option("--targetdir", type="string", metavar="DIR",
1571 help="Path prefix where the new Samba 4.0 AD domain should be initialised"),
1572 Option("-q", "--quiet", help="Be quiet", action="store_true"),
1573 Option("-v", "--verbose", help="Be verbose", action="store_true"),
1574 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
1575 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
1576 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
1577 "BIND9_FLATFILE uses bind9 text database to store zone information, "
1578 "BIND9_DLZ uses samba4 AD to store zone information, "
1579 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
1580 default="SAMBA_INTERNAL")
1584 Option("--use-xattrs", type="choice", choices=["yes","no","auto"],
1585 metavar="[yes|no|auto]",
1586 help="Define if we should use the native fs capabilities or a tdb file for "
1587 "storing attributes likes ntacl when --use-ntvfs is set. "
1588 "auto tries to make an inteligent guess based on the user rights and system capabilities",
1591 if samba.is_ntvfs_fileserver_built():
1592 takes_options.extend(common_ntvfs_options)
1593 takes_options.extend(ntvfs_options)
1595 takes_args = ["smbconf"]
1597 def run(self, smbconf=None, targetdir=None, dbdir=None, testparm=None,
1598 quiet=False, verbose=False, use_xattrs="auto", sambaopts=None, versionopts=None,
1599 dns_backend=None, use_ntvfs=False):
1601 if not os.path.exists(smbconf):
1602 raise CommandError("File %s does not exist" % smbconf)
1604 if testparm and not os.path.exists(testparm):
1605 raise CommandError("Testparm utility %s does not exist" % testparm)
1607 if dbdir and not os.path.exists(dbdir):
1608 raise CommandError("Directory %s does not exist" % dbdir)
1610 if not dbdir and not testparm:
1611 raise CommandError("Please specify either dbdir or testparm")
1613 logger = self.get_logger()
1615 logger.setLevel(logging.DEBUG)
1617 logger.setLevel(logging.WARNING)
1619 logger.setLevel(logging.INFO)
1621 if dbdir and testparm:
1622 logger.warning("both dbdir and testparm specified, ignoring dbdir.")
1625 lp = sambaopts.get_loadparm()
1627 s3conf = s3param.get_context()
1630 s3conf.set("realm", sambaopts.realm)
1632 if targetdir is not None:
1633 if not os.path.isdir(targetdir):
1637 if use_xattrs == "yes":
1639 elif use_xattrs == "auto" and use_ntvfs == False:
1641 elif use_ntvfs == False:
1642 raise CommandError("--use-xattrs=no requires --use-ntvfs (not supported for production use). "
1643 "Please re-run with --use-xattrs omitted.")
1644 elif use_xattrs == "auto" and not s3conf.get("posix:eadb"):
1646 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
1648 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
1651 samba.ntacls.setntacl(lp, tmpfile.name,
1652 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
1655 # FIXME: Don't catch all exceptions here
1656 logger.info("You are not root or your system does not support xattr, using tdb backend for attributes. "
1657 "If you intend to use this provision in production, rerun the script as root on a system supporting xattrs.")
1661 # Set correct default values from dbdir or testparm
1664 paths["state directory"] = dbdir
1665 paths["private dir"] = dbdir
1666 paths["lock directory"] = dbdir
1667 paths["smb passwd file"] = dbdir + "/smbpasswd"
1669 paths["state directory"] = get_testparm_var(testparm, smbconf, "state directory")
1670 paths["private dir"] = get_testparm_var(testparm, smbconf, "private dir")
1671 paths["smb passwd file"] = get_testparm_var(testparm, smbconf, "smb passwd file")
1672 paths["lock directory"] = get_testparm_var(testparm, smbconf, "lock directory")
1673 # "testparm" from Samba 3 < 3.4.x is not aware of the parameter
1674 # "state directory", instead make use of "lock directory"
1675 if len(paths["state directory"]) == 0:
1676 paths["state directory"] = paths["lock directory"]
1679 s3conf.set(p, paths[p])
1681 # load smb.conf parameters
1682 logger.info("Reading smb.conf")
1683 s3conf.load(smbconf)
1684 samba3 = Samba3(smbconf, s3conf)
1686 logger.info("Provisioning")
1687 upgrade_from_samba3(samba3, logger, targetdir, session_info=system_session(),
1688 useeadb=eadb, dns_backend=dns_backend, use_ntvfs=use_ntvfs)
1691 class cmd_domain_samba3upgrade(cmd_domain_classicupgrade):
1692 __doc__ = cmd_domain_classicupgrade.__doc__
1694 # This command is present for backwards compatibility only,
1695 # and should not be shown.
1699 class LocalDCCredentialsOptions(options.CredentialsOptions):
1700 def __init__(self, parser):
1701 options.CredentialsOptions.__init__(self, parser, special_name="local-dc")
1703 class DomainTrustCommand(Command):
1704 """List domain trusts."""
1707 Command.__init__(self)
1708 self.local_lp = None
1710 self.local_server = None
1711 self.local_binding_string = None
1712 self.local_creds = None
1714 self.remote_server = None
1715 self.remote_binding_string = None
1716 self.remote_creds = None
1718 def _uint32(self, v):
1719 return ctypes.c_uint32(v).value
1721 def check_runtime_error(self, runtime, val):
1725 err32 = self._uint32(runtime.args[0])
1731 class LocalRuntimeError(CommandError):
1732 def __init__(exception_self, self, runtime, message):
1733 err32 = self._uint32(runtime.args[0])
1734 errstr = runtime.args[1]
1735 msg = "LOCAL_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1736 self.local_server, message, err32, errstr)
1737 CommandError.__init__(exception_self, msg)
1739 class RemoteRuntimeError(CommandError):
1740 def __init__(exception_self, self, runtime, message):
1741 err32 = self._uint32(runtime.args[0])
1742 errstr = runtime.args[1]
1743 msg = "REMOTE_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1744 self.remote_server, message, err32, errstr)
1745 CommandError.__init__(exception_self, msg)
1747 class LocalLdbError(CommandError):
1748 def __init__(exception_self, self, ldb_error, message):
1749 errval = ldb_error.args[0]
1750 errstr = ldb_error.args[1]
1751 msg = "LOCAL_DC[%s]: %s - ERROR(%d) - %s" % (
1752 self.local_server, message, errval, errstr)
1753 CommandError.__init__(exception_self, msg)
1755 def setup_local_server(self, sambaopts, localdcopts):
1756 if self.local_server is not None:
1757 return self.local_server
1759 lp = sambaopts.get_loadparm()
1761 local_server = localdcopts.ipaddress
1762 if local_server is None:
1763 server_role = lp.server_role()
1764 if server_role != "ROLE_ACTIVE_DIRECTORY_DC":
1765 raise CommandError("Invalid server_role %s" % (server_role))
1766 local_server = lp.get('netbios name')
1767 local_transport = "ncalrpc"
1768 local_binding_options = ""
1769 local_binding_options += ",auth_type=ncalrpc_as_system"
1770 local_ldap_url = None
1773 local_transport = "ncacn_np"
1774 local_binding_options = ""
1775 local_ldap_url = "ldap://%s" % local_server
1776 local_creds = localdcopts.get_credentials(lp)
1780 self.local_server = local_server
1781 self.local_binding_string = "%s:%s[%s]" % (local_transport, local_server, local_binding_options)
1782 self.local_ldap_url = local_ldap_url
1783 self.local_creds = local_creds
1784 return self.local_server
1786 def new_local_lsa_connection(self):
1787 return lsa.lsarpc(self.local_binding_string, self.local_lp, self.local_creds)
1789 def new_local_netlogon_connection(self):
1790 return netlogon.netlogon(self.local_binding_string, self.local_lp, self.local_creds)
1792 def new_local_ldap_connection(self):
1793 return SamDB(url=self.local_ldap_url,
1794 session_info=system_session(),
1795 credentials=self.local_creds,
1798 def setup_remote_server(self, credopts, domain,
1800 require_writable=True):
1803 assert require_writable
1805 if self.remote_server is not None:
1806 return self.remote_server
1808 self.remote_server = "__unknown__remote_server__.%s" % domain
1809 assert self.local_server is not None
1811 remote_creds = credopts.get_credentials(self.local_lp)
1812 remote_server = credopts.ipaddress
1813 remote_binding_options = ""
1815 # TODO: we should also support NT4 domains
1816 # we could use local_netlogon.netr_DsRGetDCNameEx2() with the remote domain name
1817 # and delegate NBT or CLDAP to the local netlogon server
1819 remote_net = Net(remote_creds, self.local_lp, server=remote_server)
1820 remote_flags = nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS
1821 if require_writable:
1822 remote_flags |= nbt.NBT_SERVER_WRITABLE
1824 remote_flags |= nbt.NBT_SERVER_PDC
1825 remote_info = remote_net.finddc(flags=remote_flags, domain=domain, address=remote_server)
1826 except NTSTATUSError as error:
1827 raise CommandError("Failed to find a writeable DC for domain '%s': %s" %
1830 raise CommandError("Failed to find a writeable DC for domain '%s'" % domain)
1832 nbt.NBT_SERVER_PDC: "PDC",
1833 nbt.NBT_SERVER_GC: "GC",
1834 nbt.NBT_SERVER_LDAP: "LDAP",
1835 nbt.NBT_SERVER_DS: "DS",
1836 nbt.NBT_SERVER_KDC: "KDC",
1837 nbt.NBT_SERVER_TIMESERV: "TIMESERV",
1838 nbt.NBT_SERVER_CLOSEST: "CLOSEST",
1839 nbt.NBT_SERVER_WRITABLE: "WRITABLE",
1840 nbt.NBT_SERVER_GOOD_TIMESERV: "GOOD_TIMESERV",
1841 nbt.NBT_SERVER_NDNC: "NDNC",
1842 nbt.NBT_SERVER_SELECT_SECRET_DOMAIN_6: "SELECT_SECRET_DOMAIN_6",
1843 nbt.NBT_SERVER_FULL_SECRET_DOMAIN_6: "FULL_SECRET_DOMAIN_6",
1844 nbt.NBT_SERVER_ADS_WEB_SERVICE: "ADS_WEB_SERVICE",
1845 nbt.NBT_SERVER_DS_8: "DS_8",
1846 nbt.NBT_SERVER_HAS_DNS_NAME: "HAS_DNS_NAME",
1847 nbt.NBT_SERVER_IS_DEFAULT_NC: "IS_DEFAULT_NC",
1848 nbt.NBT_SERVER_FOREST_ROOT: "FOREST_ROOT",
1850 server_type_string = self.generic_bitmap_to_string(flag_map,
1851 remote_info.server_type, names_only=True)
1852 self.outf.write("RemoteDC Netbios[%s] DNS[%s] ServerType[%s]\n" % (
1853 remote_info.pdc_name,
1854 remote_info.pdc_dns_name,
1855 server_type_string))
1857 self.remote_server = remote_info.pdc_dns_name
1858 self.remote_binding_string="ncacn_np:%s[%s]" % (self.remote_server, remote_binding_options)
1859 self.remote_creds = remote_creds
1860 return self.remote_server
1862 def new_remote_lsa_connection(self):
1863 return lsa.lsarpc(self.remote_binding_string, self.local_lp, self.remote_creds)
1865 def new_remote_netlogon_connection(self):
1866 return netlogon.netlogon(self.remote_binding_string, self.local_lp, self.remote_creds)
1868 def get_lsa_info(self, conn, policy_access):
1869 objectAttr = lsa.ObjectAttribute()
1870 objectAttr.sec_qos = lsa.QosInfo()
1872 policy = conn.OpenPolicy2(''.decode('utf-8'),
1873 objectAttr, policy_access)
1875 info = conn.QueryInfoPolicy2(policy, lsa.LSA_POLICY_INFO_DNS)
1877 return (policy, info)
1879 def get_netlogon_dc_info(self, conn, server):
1880 info = conn.netr_DsRGetDCNameEx2(server,
1881 None, 0, None, None, None,
1882 netlogon.DS_RETURN_DNS_NAME)
1885 def netr_DomainTrust_to_name(self, t):
1886 if t.trust_type == lsa.LSA_TRUST_TYPE_DOWNLEVEL:
1887 return t.netbios_name
1891 def netr_DomainTrust_to_type(self, a, t):
1893 primary_parent = None
1895 if _t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
1897 if not _t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1898 primary_parent = a[_t.parent_index]
1901 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1902 if t is primary_parent:
1905 if t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1908 parent = a[t.parent_index]
1909 if parent is primary:
1914 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1919 def netr_DomainTrust_to_transitive(self, t):
1920 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1923 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE:
1926 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1931 def netr_DomainTrust_to_direction(self, t):
1932 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND and \
1933 t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1936 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND:
1939 if t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1944 def generic_enum_to_string(self, e_dict, v, names_only=False):
1948 v32 = self._uint32(v)
1949 w = "__unknown__%08X__" % v32
1951 r = "0x%x (%s)" % (v, w)
1954 def generic_bitmap_to_string(self, b_dict, v, names_only=False):
1959 for b in sorted(b_dict.keys()):
1966 c32 = self._uint32(c)
1967 s += ["__unknown_%08X__" % c32]
1972 r = "0x%x (%s)" % (v, w)
1975 def trustType_string(self, v):
1977 lsa.LSA_TRUST_TYPE_DOWNLEVEL : "DOWNLEVEL",
1978 lsa.LSA_TRUST_TYPE_UPLEVEL : "UPLEVEL",
1979 lsa.LSA_TRUST_TYPE_MIT : "MIT",
1980 lsa.LSA_TRUST_TYPE_DCE : "DCE",
1982 return self.generic_enum_to_string(types, v)
1984 def trustDirection_string(self, v):
1986 lsa.LSA_TRUST_DIRECTION_INBOUND |
1987 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "BOTH",
1988 lsa.LSA_TRUST_DIRECTION_INBOUND : "INBOUND",
1989 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "OUTBOUND",
1991 return self.generic_enum_to_string(directions, v)
1993 def trustAttributes_string(self, v):
1995 lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE : "NON_TRANSITIVE",
1996 lsa.LSA_TRUST_ATTRIBUTE_UPLEVEL_ONLY : "UPLEVEL_ONLY",
1997 lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN : "QUARANTINED_DOMAIN",
1998 lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE : "FOREST_TRANSITIVE",
1999 lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION : "CROSS_ORGANIZATION",
2000 lsa.LSA_TRUST_ATTRIBUTE_WITHIN_FOREST : "WITHIN_FOREST",
2001 lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL : "TREAT_AS_EXTERNAL",
2002 lsa.LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION : "USES_RC4_ENCRYPTION",
2004 return self.generic_bitmap_to_string(attributes, v)
2006 def kerb_EncTypes_string(self, v):
2008 security.KERB_ENCTYPE_DES_CBC_CRC : "DES_CBC_CRC",
2009 security.KERB_ENCTYPE_DES_CBC_MD5 : "DES_CBC_MD5",
2010 security.KERB_ENCTYPE_RC4_HMAC_MD5 : "RC4_HMAC_MD5",
2011 security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96 : "AES128_CTS_HMAC_SHA1_96",
2012 security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96 : "AES256_CTS_HMAC_SHA1_96",
2013 security.KERB_ENCTYPE_FAST_SUPPORTED : "FAST_SUPPORTED",
2014 security.KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED : "COMPOUND_IDENTITY_SUPPORTED",
2015 security.KERB_ENCTYPE_CLAIMS_SUPPORTED : "CLAIMS_SUPPORTED",
2016 security.KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED : "RESOURCE_SID_COMPRESSION_DISABLED",
2018 return self.generic_bitmap_to_string(enctypes, v)
2020 def entry_tln_status(self, e_flags, ):
2022 return "Status[Enabled]"
2025 lsa.LSA_TLN_DISABLED_NEW : "Disabled-New",
2026 lsa.LSA_TLN_DISABLED_ADMIN : "Disabled",
2027 lsa.LSA_TLN_DISABLED_CONFLICT : "Disabled-Conflicting",
2029 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
2031 def entry_dom_status(self, e_flags):
2033 return "Status[Enabled]"
2036 lsa.LSA_SID_DISABLED_ADMIN : "Disabled-SID",
2037 lsa.LSA_SID_DISABLED_CONFLICT : "Disabled-SID-Conflicting",
2038 lsa.LSA_NB_DISABLED_ADMIN : "Disabled-NB",
2039 lsa.LSA_NB_DISABLED_CONFLICT : "Disabled-NB-Conflicting",
2041 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
2043 def write_forest_trust_info(self, fti, tln=None, collisions=None):
2045 tln_string = " TDO[%s]" % tln
2049 self.outf.write("Namespaces[%d]%s:\n" % (
2050 len(fti.entries), tln_string))
2052 for i, e in enumerate(fti.entries):
2055 collision_string = ""
2057 if collisions is not None:
2058 for c in collisions.entries:
2062 collision_string = " Collision[%s]" % (c.name.string)
2064 d = e.forest_trust_data
2065 if e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
2066 self.outf.write("TLN: %-32s DNS[*.%s]%s\n" % (
2067 self.entry_tln_status(flags),
2068 d.string, collision_string))
2069 elif e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
2070 self.outf.write("TLN_EX: %-29s DNS[*.%s]\n" % (
2072 elif e.type == lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
2073 self.outf.write("DOM: %-32s DNS[%s] Netbios[%s] SID[%s]%s\n" % (
2074 self.entry_dom_status(flags),
2075 d.dns_domain_name.string,
2076 d.netbios_domain_name.string,
2077 d.domain_sid, collision_string))
2080 class cmd_domain_trust_list(DomainTrustCommand):
2081 """List domain trusts."""
2083 synopsis = "%prog [options]"
2085 takes_optiongroups = {
2086 "sambaopts": options.SambaOptions,
2087 "versionopts": options.VersionOptions,
2088 "localdcopts": LocalDCCredentialsOptions,
2094 def run(self, sambaopts=None, versionopts=None, localdcopts=None):
2096 local_server = self.setup_local_server(sambaopts, localdcopts)
2098 local_netlogon = self.new_local_netlogon_connection()
2099 except RuntimeError as error:
2100 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2103 local_netlogon_trusts = local_netlogon.netr_DsrEnumerateDomainTrusts(local_server,
2104 netlogon.NETR_TRUST_FLAG_IN_FOREST |
2105 netlogon.NETR_TRUST_FLAG_OUTBOUND |
2106 netlogon.NETR_TRUST_FLAG_INBOUND)
2107 except RuntimeError as error:
2108 if self.check_runtime_error(error, werror.WERR_RPC_S_PROCNUM_OUT_OF_RANGE):
2109 # TODO: we could implement a fallback to lsa.EnumTrustDom()
2110 raise CommandError("LOCAL_DC[%s]: netr_DsrEnumerateDomainTrusts not supported." % (
2112 raise self.LocalRuntimeError(self, error, "netr_DsrEnumerateDomainTrusts failed")
2114 a = local_netlogon_trusts.array
2116 if t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
2118 self.outf.write("%-14s %-15s %-19s %s\n" % (
2119 "Type[%s]" % self.netr_DomainTrust_to_type(a, t),
2120 "Transitive[%s]" % self.netr_DomainTrust_to_transitive(t),
2121 "Direction[%s]" % self.netr_DomainTrust_to_direction(t),
2122 "Name[%s]" % self.netr_DomainTrust_to_name(t)))
2125 class cmd_domain_trust_show(DomainTrustCommand):
2126 """Show trusted domain details."""
2128 synopsis = "%prog NAME [options]"
2130 takes_optiongroups = {
2131 "sambaopts": options.SambaOptions,
2132 "versionopts": options.VersionOptions,
2133 "localdcopts": LocalDCCredentialsOptions,
2139 takes_args = ["domain"]
2141 def run(self, domain, sambaopts=None, versionopts=None, localdcopts=None):
2143 local_server = self.setup_local_server(sambaopts, localdcopts)
2145 local_lsa = self.new_local_lsa_connection()
2146 except RuntimeError as error:
2147 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2150 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2151 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2152 except RuntimeError as error:
2153 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2155 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2156 local_lsa_info.name.string,
2157 local_lsa_info.dns_domain.string,
2158 local_lsa_info.sid))
2160 lsaString = lsa.String()
2161 lsaString.string = domain
2163 local_tdo_full = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2164 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2165 local_tdo_info = local_tdo_full.info_ex
2166 local_tdo_posix = local_tdo_full.posix_offset
2167 except NTSTATUSError as error:
2168 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2169 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2171 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(FULL_INFO) failed")
2174 local_tdo_enctypes = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2175 lsaString, lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES)
2176 except NTSTATUSError as error:
2177 if self.check_runtime_error(error, ntstatus.NT_STATUS_INVALID_PARAMETER):
2179 if self.check_runtime_error(error, ntstatus.NT_STATUS_INVALID_INFO_CLASS):
2182 if error is not None:
2183 raise self.LocalRuntimeError(self, error,
2184 "QueryTrustedDomainInfoByName(SUPPORTED_ENCRYPTION_TYPES) failed")
2186 local_tdo_enctypes = lsa.TrustDomainInfoSupportedEncTypes()
2187 local_tdo_enctypes.enc_types = 0
2190 local_tdo_forest = None
2191 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2192 local_tdo_forest = local_lsa.lsaRQueryForestTrustInformation(local_policy,
2193 lsaString, lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
2194 except RuntimeError as error:
2195 if self.check_runtime_error(error, ntstatus.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
2197 if self.check_runtime_error(error, ntstatus.NT_STATUS_NOT_FOUND):
2199 if error is not None:
2200 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation failed")
2202 local_tdo_forest = lsa.ForestTrustInformation()
2203 local_tdo_forest.count = 0
2204 local_tdo_forest.entries = []
2206 self.outf.write("TrustedDomain:\n\n");
2207 self.outf.write("NetbiosName: %s\n" % local_tdo_info.netbios_name.string)
2208 if local_tdo_info.netbios_name.string != local_tdo_info.domain_name.string:
2209 self.outf.write("DnsName: %s\n" % local_tdo_info.domain_name.string)
2210 self.outf.write("SID: %s\n" % local_tdo_info.sid)
2211 self.outf.write("Type: %s\n" % self.trustType_string(local_tdo_info.trust_type))
2212 self.outf.write("Direction: %s\n" % self.trustDirection_string(local_tdo_info.trust_direction))
2213 self.outf.write("Attributes: %s\n" % self.trustAttributes_string(local_tdo_info.trust_attributes))
2214 posix_offset_u32 = ctypes.c_uint32(local_tdo_posix.posix_offset).value
2215 posix_offset_i32 = ctypes.c_int32(local_tdo_posix.posix_offset).value
2216 self.outf.write("PosixOffset: 0x%08X (%d)\n" % (posix_offset_u32, posix_offset_i32))
2217 self.outf.write("kerb_EncTypes: %s\n" % self.kerb_EncTypes_string(local_tdo_enctypes.enc_types))
2219 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2220 self.write_forest_trust_info(local_tdo_forest,
2221 tln=local_tdo_info.domain_name.string)
2225 class cmd_domain_trust_create(DomainTrustCommand):
2226 """Create a domain or forest trust."""
2228 synopsis = "%prog DOMAIN [options]"
2230 takes_optiongroups = {
2231 "sambaopts": options.SambaOptions,
2232 "versionopts": options.VersionOptions,
2233 "credopts": options.CredentialsOptions,
2234 "localdcopts": LocalDCCredentialsOptions,
2238 Option("--type", type="choice", metavar="TYPE",
2239 choices=["external", "forest"],
2240 help="The type of the trust: 'external' or 'forest'.",
2242 default="external"),
2243 Option("--direction", type="choice", metavar="DIRECTION",
2244 choices=["incoming", "outgoing", "both"],
2245 help="The trust direction: 'incoming', 'outgoing' or 'both'.",
2246 dest='trust_direction',
2248 Option("--create-location", type="choice", metavar="LOCATION",
2249 choices=["local", "both"],
2250 help="Where to create the trusted domain object: 'local' or 'both'.",
2251 dest='create_location',
2253 Option("--cross-organisation", action="store_true",
2254 help="The related domains does not belong to the same organisation.",
2255 dest='cross_organisation',
2257 Option("--quarantined", type="choice", metavar="yes|no",
2258 choices=["yes", "no", None],
2259 help="Special SID filtering rules are applied to the trust. "
2260 "With --type=external the default is yes. "
2261 "With --type=forest the default is no.",
2262 dest='quarantined_arg',
2264 Option("--not-transitive", action="store_true",
2265 help="The forest trust is not transitive.",
2266 dest='not_transitive',
2268 Option("--treat-as-external", action="store_true",
2269 help="The treat the forest trust as external.",
2270 dest='treat_as_external',
2272 Option("--no-aes-keys", action="store_false",
2273 help="The trust uses aes kerberos keys.",
2274 dest='use_aes_keys',
2276 Option("--skip-validation", action="store_false",
2277 help="Skip validation of the trust.",
2282 takes_args = ["domain"]
2284 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2285 trust_type=None, trust_direction=None, create_location=None,
2286 cross_organisation=False, quarantined_arg=None,
2287 not_transitive=False, treat_as_external=False,
2288 use_aes_keys=False, validate=True):
2290 lsaString = lsa.String()
2293 if quarantined_arg is None:
2294 if trust_type == 'external':
2296 elif quarantined_arg == 'yes':
2299 if trust_type != 'forest':
2301 raise CommandError("--not-transitive requires --type=forest")
2302 if treat_as_external:
2303 raise CommandError("--treat-as-external requires --type=forest")
2307 enc_types = lsa.TrustDomainInfoSupportedEncTypes()
2308 enc_types.enc_types = security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96
2309 enc_types.enc_types |= security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96
2311 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2312 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2313 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2315 local_trust_info = lsa.TrustDomainInfoInfoEx()
2316 local_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2317 local_trust_info.trust_direction = 0
2318 if trust_direction == "both":
2319 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2320 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2321 elif trust_direction == "incoming":
2322 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2323 elif trust_direction == "outgoing":
2324 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2325 local_trust_info.trust_attributes = 0
2326 if cross_organisation:
2327 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2329 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2330 if trust_type == "forest":
2331 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2333 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2334 if treat_as_external:
2335 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2337 def get_password(name):
2340 if password is not None and password is not '':
2342 password = getpass("New %s Password: " % name)
2343 passwordverify = getpass("Retype %s Password: " % name)
2344 if not password == passwordverify:
2346 self.outf.write("Sorry, passwords do not match.\n")
2348 incoming_secret = None
2349 outgoing_secret = None
2350 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2351 if create_location == "local":
2352 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2353 incoming_password = get_password("Incoming Trust")
2354 incoming_secret = string_to_byte_array(incoming_password.encode('utf-16-le'))
2355 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2356 outgoing_password = get_password("Outgoing Trust")
2357 outgoing_secret = string_to_byte_array(outgoing_password.encode('utf-16-le'))
2359 remote_trust_info = None
2361 # We use 240 random bytes.
2362 # Windows uses 28 or 240 random bytes. I guess it's
2363 # based on the trust type external vs. forest.
2365 # The initial trust password can be up to 512 bytes
2366 # while the versioned passwords used for periodic updates
2367 # can only be up to 498 bytes, as netr_ServerPasswordSet2()
2368 # needs to pass the NL_PASSWORD_VERSION structure within the
2369 # 512 bytes and a 2 bytes confounder is required.
2371 def random_trust_secret(length):
2372 pw = samba.generate_random_machine_password(length//2, length//2)
2373 return string_to_byte_array(pw.encode('utf-16-le'))
2375 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2376 incoming_secret = random_trust_secret(240)
2377 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2378 outgoing_secret = random_trust_secret(240)
2380 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2381 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2383 remote_trust_info = lsa.TrustDomainInfoInfoEx()
2384 remote_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2385 remote_trust_info.trust_direction = 0
2386 if trust_direction == "both":
2387 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2388 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2389 elif trust_direction == "incoming":
2390 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2391 elif trust_direction == "outgoing":
2392 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2393 remote_trust_info.trust_attributes = 0
2394 if cross_organisation:
2395 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2397 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2398 if trust_type == "forest":
2399 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2401 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2402 if treat_as_external:
2403 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2405 local_server = self.setup_local_server(sambaopts, localdcopts)
2407 local_lsa = self.new_local_lsa_connection()
2408 except RuntimeError as error:
2409 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2412 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2413 except RuntimeError as error:
2414 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2416 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2417 local_lsa_info.name.string,
2418 local_lsa_info.dns_domain.string,
2419 local_lsa_info.sid))
2422 remote_server = self.setup_remote_server(credopts, domain)
2423 except RuntimeError as error:
2424 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2427 remote_lsa = self.new_remote_lsa_connection()
2428 except RuntimeError as error:
2429 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2432 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2433 except RuntimeError as error:
2434 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2436 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2437 remote_lsa_info.name.string,
2438 remote_lsa_info.dns_domain.string,
2439 remote_lsa_info.sid))
2441 local_trust_info.domain_name.string = remote_lsa_info.dns_domain.string
2442 local_trust_info.netbios_name.string = remote_lsa_info.name.string
2443 local_trust_info.sid = remote_lsa_info.sid
2445 if remote_trust_info:
2446 remote_trust_info.domain_name.string = local_lsa_info.dns_domain.string
2447 remote_trust_info.netbios_name.string = local_lsa_info.name.string
2448 remote_trust_info.sid = local_lsa_info.sid
2451 lsaString.string = local_trust_info.domain_name.string
2452 local_old_netbios = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2453 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2454 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2455 except NTSTATUSError as error:
2456 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2457 raise self.LocalRuntimeError(self, error,
2458 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2462 lsaString.string = local_trust_info.netbios_name.string
2463 local_old_dns = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2464 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2465 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2466 except NTSTATUSError as error:
2467 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2468 raise self.LocalRuntimeError(self, error,
2469 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2472 if remote_trust_info:
2474 lsaString.string = remote_trust_info.domain_name.string
2475 remote_old_netbios = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2476 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2477 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2478 except NTSTATUSError as error:
2479 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2480 raise self.RemoteRuntimeError(self, error,
2481 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2485 lsaString.string = remote_trust_info.netbios_name.string
2486 remote_old_dns = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2487 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2488 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2489 except NTSTATUSError as error:
2490 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2491 raise self.RemoteRuntimeError(self, error,
2492 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2496 local_netlogon = self.new_local_netlogon_connection()
2497 except RuntimeError as error:
2498 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2501 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
2502 except RuntimeError as error:
2503 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
2505 if remote_trust_info:
2507 remote_netlogon = self.new_remote_netlogon_connection()
2508 except RuntimeError as error:
2509 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2512 remote_netlogon_info = self.get_netlogon_dc_info(remote_netlogon, remote_server)
2513 except RuntimeError as error:
2514 raise self.RemoteRuntimeError(self, error, "failed to get netlogon dc info")
2516 def generate_AuthInOutBlob(secret, update_time):
2518 blob = drsblobs.trustAuthInOutBlob()
2523 clear = drsblobs.AuthInfoClear()
2524 clear.size = len(secret)
2525 clear.password = secret
2527 info = drsblobs.AuthenticationInformation()
2528 info.LastUpdateTime = samba.unix2nttime(update_time)
2529 info.AuthType = lsa.TRUST_AUTH_TYPE_CLEAR
2530 info.AuthInfo = clear
2532 array = drsblobs.AuthenticationInformationArray()
2534 array.array = [info]
2536 blob = drsblobs.trustAuthInOutBlob()
2538 blob.current = array
2542 def generate_AuthInfoInternal(session_key, incoming=None, outgoing=None):
2543 confounder = [0] * 512
2544 for i in range(len(confounder)):
2545 confounder[i] = random.randint(0, 255)
2547 trustpass = drsblobs.trustDomainPasswords()
2549 trustpass.confounder = confounder
2550 trustpass.outgoing = outgoing
2551 trustpass.incoming = incoming
2553 trustpass_blob = ndr_pack(trustpass)
2555 encrypted_trustpass = arcfour_encrypt(session_key, trustpass_blob)
2557 auth_blob = lsa.DATA_BUF2()
2558 auth_blob.size = len(encrypted_trustpass)
2559 auth_blob.data = string_to_byte_array(encrypted_trustpass)
2561 auth_info = lsa.TrustDomainInfoAuthInfoInternal()
2562 auth_info.auth_blob = auth_blob
2566 update_time = samba.current_unix_time()
2567 incoming_blob = generate_AuthInOutBlob(incoming_secret, update_time)
2568 outgoing_blob = generate_AuthInOutBlob(outgoing_secret, update_time)
2570 local_tdo_handle = None
2571 remote_tdo_handle = None
2573 local_auth_info = generate_AuthInfoInternal(local_lsa.session_key,
2574 incoming=incoming_blob,
2575 outgoing=outgoing_blob)
2576 if remote_trust_info:
2577 remote_auth_info = generate_AuthInfoInternal(remote_lsa.session_key,
2578 incoming=outgoing_blob,
2579 outgoing=incoming_blob)
2582 if remote_trust_info:
2583 self.outf.write("Creating remote TDO.\n")
2584 current_request = { "location": "remote", "name": "CreateTrustedDomainEx2"}
2585 remote_tdo_handle = remote_lsa.CreateTrustedDomainEx2(remote_policy,
2588 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2589 self.outf.write("Remote TDO created.\n")
2591 self.outf.write("Setting supported encryption types on remote TDO.\n")
2592 current_request = { "location": "remote", "name": "SetInformationTrustedDomain"}
2593 remote_lsa.SetInformationTrustedDomain(remote_tdo_handle,
2594 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2597 self.outf.write("Creating local TDO.\n")
2598 current_request = { "location": "local", "name": "CreateTrustedDomainEx2"}
2599 local_tdo_handle = local_lsa.CreateTrustedDomainEx2(local_policy,
2602 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2603 self.outf.write("Local TDO created\n")
2605 self.outf.write("Setting supported encryption types on local TDO.\n")
2606 current_request = { "location": "local", "name": "SetInformationTrustedDomain"}
2607 local_lsa.SetInformationTrustedDomain(local_tdo_handle,
2608 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2610 except RuntimeError as error:
2611 self.outf.write("Error: %s failed %sly - cleaning up\n" % (
2612 current_request['name'], current_request['location']))
2613 if remote_tdo_handle:
2614 self.outf.write("Deleting remote TDO.\n")
2615 remote_lsa.DeleteObject(remote_tdo_handle)
2616 remote_tdo_handle = None
2617 if local_tdo_handle:
2618 self.outf.write("Deleting local TDO.\n")
2619 local_lsa.DeleteObject(local_tdo_handle)
2620 local_tdo_handle = None
2621 if current_request['location'] is "remote":
2622 raise self.RemoteRuntimeError(self, error, "%s" % (
2623 current_request['name']))
2624 raise self.LocalRuntimeError(self, error, "%s" % (
2625 current_request['name']))
2628 if local_trust_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2629 self.outf.write("Setup local forest trust information...\n")
2631 # get all information about the remote trust
2632 # this triggers netr_GetForestTrustInformation to the remote domain
2633 # and lsaRSetForestTrustInformation() locally, but new top level
2634 # names are disabled by default.
2635 local_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
2636 remote_lsa_info.dns_domain.string,
2637 netlogon.DS_GFTI_UPDATE_TDO)
2638 except RuntimeError as error:
2639 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2642 # here we try to enable all top level names
2643 local_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
2644 remote_lsa_info.dns_domain,
2645 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2648 except RuntimeError as error:
2649 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2651 self.write_forest_trust_info(local_forest_info,
2652 tln=remote_lsa_info.dns_domain.string,
2653 collisions=local_forest_collision)
2655 if remote_trust_info:
2656 self.outf.write("Setup remote forest trust information...\n")
2658 # get all information about the local trust (from the perspective of the remote domain)
2659 # this triggers netr_GetForestTrustInformation to our domain.
2660 # and lsaRSetForestTrustInformation() remotely, but new top level
2661 # names are disabled by default.
2662 remote_forest_info = remote_netlogon.netr_DsRGetForestTrustInformation(remote_netlogon_info.dc_unc,
2663 local_lsa_info.dns_domain.string,
2664 netlogon.DS_GFTI_UPDATE_TDO)
2665 except RuntimeError as error:
2666 raise self.RemoteRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2669 # here we try to enable all top level names
2670 remote_forest_collision = remote_lsa.lsaRSetForestTrustInformation(remote_policy,
2671 local_lsa_info.dns_domain,
2672 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2675 except RuntimeError as error:
2676 raise self.RemoteRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2678 self.write_forest_trust_info(remote_forest_info,
2679 tln=local_lsa_info.dns_domain.string,
2680 collisions=remote_forest_collision)
2682 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2683 self.outf.write("Validating outgoing trust...\n")
2685 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_netlogon_info.dc_unc,
2686 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2688 remote_lsa_info.dns_domain.string)
2689 except RuntimeError as error:
2690 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2692 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2693 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2695 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2696 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2697 local_trust_verify.trusted_dc_name,
2698 local_trust_verify.tc_connection_status[1],
2699 local_trust_verify.pdc_connection_status[1])
2701 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2702 local_trust_verify.trusted_dc_name,
2703 local_trust_verify.tc_connection_status[1],
2704 local_trust_verify.pdc_connection_status[1])
2706 if local_trust_status != werror.WERR_SUCCESS or local_conn_status != werror.WERR_SUCCESS:
2707 raise CommandError(local_validation)
2709 self.outf.write("OK: %s\n" % local_validation)
2711 if remote_trust_info:
2712 if remote_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2713 self.outf.write("Validating incoming trust...\n")
2715 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_netlogon_info.dc_unc,
2716 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2718 local_lsa_info.dns_domain.string)
2719 except RuntimeError as error:
2720 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2722 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2723 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2725 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2726 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2727 remote_trust_verify.trusted_dc_name,
2728 remote_trust_verify.tc_connection_status[1],
2729 remote_trust_verify.pdc_connection_status[1])
2731 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2732 remote_trust_verify.trusted_dc_name,
2733 remote_trust_verify.tc_connection_status[1],
2734 remote_trust_verify.pdc_connection_status[1])
2736 if remote_trust_status != werror.WERR_SUCCESS or remote_conn_status != werror.WERR_SUCCESS:
2737 raise CommandError(remote_validation)
2739 self.outf.write("OK: %s\n" % remote_validation)
2741 if remote_tdo_handle is not None:
2743 remote_lsa.Close(remote_tdo_handle)
2744 except RuntimeError as error:
2746 remote_tdo_handle = None
2747 if local_tdo_handle is not None:
2749 local_lsa.Close(local_tdo_handle)
2750 except RuntimeError as error:
2752 local_tdo_handle = None
2754 self.outf.write("Success.\n")
2757 class cmd_domain_trust_delete(DomainTrustCommand):
2758 """Delete a domain trust."""
2760 synopsis = "%prog DOMAIN [options]"
2762 takes_optiongroups = {
2763 "sambaopts": options.SambaOptions,
2764 "versionopts": options.VersionOptions,
2765 "credopts": options.CredentialsOptions,
2766 "localdcopts": LocalDCCredentialsOptions,
2770 Option("--delete-location", type="choice", metavar="LOCATION",
2771 choices=["local", "both"],
2772 help="Where to delete the trusted domain object: 'local' or 'both'.",
2773 dest='delete_location',
2777 takes_args = ["domain"]
2779 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2780 delete_location=None):
2782 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2783 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2784 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2786 if delete_location == "local":
2787 remote_policy_access = None
2789 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2790 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2791 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2793 local_server = self.setup_local_server(sambaopts, localdcopts)
2795 local_lsa = self.new_local_lsa_connection()
2796 except RuntimeError as error:
2797 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2800 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2801 except RuntimeError as error:
2802 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2804 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2805 local_lsa_info.name.string,
2806 local_lsa_info.dns_domain.string,
2807 local_lsa_info.sid))
2809 local_tdo_info = None
2810 local_tdo_handle = None
2811 remote_tdo_info = None
2812 remote_tdo_handle = None
2814 lsaString = lsa.String()
2816 lsaString.string = domain
2817 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2818 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2819 except NTSTATUSError as error:
2820 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2821 raise CommandError("Failed to find trust for domain '%s'" % domain)
2822 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2825 if remote_policy_access is not None:
2827 remote_server = self.setup_remote_server(credopts, domain)
2828 except RuntimeError as error:
2829 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2832 remote_lsa = self.new_remote_lsa_connection()
2833 except RuntimeError as error:
2834 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2837 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2838 except RuntimeError as error:
2839 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2841 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2842 remote_lsa_info.name.string,
2843 remote_lsa_info.dns_domain.string,
2844 remote_lsa_info.sid))
2846 if remote_lsa_info.sid != local_tdo_info.sid or \
2847 remote_lsa_info.name.string != local_tdo_info.netbios_name.string or \
2848 remote_lsa_info.dns_domain.string != local_tdo_info.domain_name.string:
2849 raise CommandError("LocalTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2850 local_tdo_info.netbios_name.string,
2851 local_tdo_info.domain_name.string,
2852 local_tdo_info.sid))
2855 lsaString.string = local_lsa_info.dns_domain.string
2856 remote_tdo_info = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2857 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2858 except NTSTATUSError as error:
2859 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2860 raise self.RemoteRuntimeError(self, error, "QueryTrustedDomainInfoByName(%s)" % (
2864 if remote_tdo_info is not None:
2865 if local_lsa_info.sid != remote_tdo_info.sid or \
2866 local_lsa_info.name.string != remote_tdo_info.netbios_name.string or \
2867 local_lsa_info.dns_domain.string != remote_tdo_info.domain_name.string:
2868 raise CommandError("RemoteTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2869 remote_tdo_info.netbios_name.string,
2870 remote_tdo_info.domain_name.string,
2871 remote_tdo_info.sid))
2873 if local_tdo_info is not None:
2875 lsaString.string = local_tdo_info.domain_name.string
2876 local_tdo_handle = local_lsa.OpenTrustedDomainByName(local_policy,
2878 security.SEC_STD_DELETE)
2879 except RuntimeError as error:
2880 raise self.LocalRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2883 local_lsa.DeleteObject(local_tdo_handle)
2884 local_tdo_handle = None
2886 if remote_tdo_info is not None:
2888 lsaString.string = remote_tdo_info.domain_name.string
2889 remote_tdo_handle = remote_lsa.OpenTrustedDomainByName(remote_policy,
2891 security.SEC_STD_DELETE)
2892 except RuntimeError as error:
2893 raise self.RemoteRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2896 if remote_tdo_handle is not None:
2898 remote_lsa.DeleteObject(remote_tdo_handle)
2899 remote_tdo_handle = None
2900 self.outf.write("RemoteTDO deleted.\n")
2901 except RuntimeError as error:
2902 self.outf.write("%s\n" % self.RemoteRuntimeError(self, error, "DeleteObject() failed"))
2904 if local_tdo_handle is not None:
2906 local_lsa.DeleteObject(local_tdo_handle)
2907 local_tdo_handle = None
2908 self.outf.write("LocalTDO deleted.\n")
2909 except RuntimeError as error:
2910 self.outf.write("%s\n" % self.LocalRuntimeError(self, error, "DeleteObject() failed"))
2914 class cmd_domain_trust_validate(DomainTrustCommand):
2915 """Validate a domain trust."""
2917 synopsis = "%prog DOMAIN [options]"
2919 takes_optiongroups = {
2920 "sambaopts": options.SambaOptions,
2921 "versionopts": options.VersionOptions,
2922 "credopts": options.CredentialsOptions,
2923 "localdcopts": LocalDCCredentialsOptions,
2927 Option("--validate-location", type="choice", metavar="LOCATION",
2928 choices=["local", "both"],
2929 help="Where to validate the trusted domain object: 'local' or 'both'.",
2930 dest='validate_location',
2934 takes_args = ["domain"]
2936 def run(self, domain, sambaopts=None, versionopts=None, credopts=None, localdcopts=None,
2937 validate_location=None):
2939 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2941 local_server = self.setup_local_server(sambaopts, localdcopts)
2943 local_lsa = self.new_local_lsa_connection()
2944 except RuntimeError as error:
2945 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2948 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2949 except RuntimeError as error:
2950 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2952 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2953 local_lsa_info.name.string,
2954 local_lsa_info.dns_domain.string,
2955 local_lsa_info.sid))
2958 lsaString = lsa.String()
2959 lsaString.string = domain
2960 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2961 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2962 except NTSTATUSError as error:
2963 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2964 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2966 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
2968 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
2969 local_tdo_info.netbios_name.string,
2970 local_tdo_info.domain_name.string,
2971 local_tdo_info.sid))
2974 local_netlogon = self.new_local_netlogon_connection()
2975 except RuntimeError as error:
2976 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2979 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_server,
2980 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2982 local_tdo_info.domain_name.string)
2983 except RuntimeError as error:
2984 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2986 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2987 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2989 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2990 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2991 local_trust_verify.trusted_dc_name,
2992 local_trust_verify.tc_connection_status[1],
2993 local_trust_verify.pdc_connection_status[1])
2995 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2996 local_trust_verify.trusted_dc_name,
2997 local_trust_verify.tc_connection_status[1],
2998 local_trust_verify.pdc_connection_status[1])
3000 if local_trust_status != werror.WERR_SUCCESS or local_conn_status != werror.WERR_SUCCESS:
3001 raise CommandError(local_validation)
3003 self.outf.write("OK: %s\n" % local_validation)
3006 server = local_trust_verify.trusted_dc_name.replace('\\', '')
3007 domain_and_server = "%s\\%s" % (local_tdo_info.domain_name.string, server)
3008 local_trust_rediscover = local_netlogon.netr_LogonControl2Ex(local_server,
3009 netlogon.NETLOGON_CONTROL_REDISCOVER,
3012 except RuntimeError as error:
3013 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
3015 local_conn_status = self._uint32(local_trust_rediscover.tc_connection_status[0])
3016 local_rediscover = "LocalRediscover: DC[%s] CONNECTION[%s]" % (
3017 local_trust_rediscover.trusted_dc_name,
3018 local_trust_rediscover.tc_connection_status[1])
3020 if local_conn_status != werror.WERR_SUCCESS:
3021 raise CommandError(local_rediscover)
3023 self.outf.write("OK: %s\n" % local_rediscover)
3025 if validate_location != "local":
3027 remote_server = self.setup_remote_server(credopts, domain, require_pdc=False)
3028 except RuntimeError as error:
3029 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
3032 remote_netlogon = self.new_remote_netlogon_connection()
3033 except RuntimeError as error:
3034 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
3037 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_server,
3038 netlogon.NETLOGON_CONTROL_TC_VERIFY,
3040 local_lsa_info.dns_domain.string)
3041 except RuntimeError as error:
3042 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
3044 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
3045 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
3047 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
3048 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
3049 remote_trust_verify.trusted_dc_name,
3050 remote_trust_verify.tc_connection_status[1],
3051 remote_trust_verify.pdc_connection_status[1])
3053 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
3054 remote_trust_verify.trusted_dc_name,
3055 remote_trust_verify.tc_connection_status[1],
3056 remote_trust_verify.pdc_connection_status[1])
3058 if remote_trust_status != werror.WERR_SUCCESS or remote_conn_status != werror.WERR_SUCCESS:
3059 raise CommandError(remote_validation)
3061 self.outf.write("OK: %s\n" % remote_validation)
3064 server = remote_trust_verify.trusted_dc_name.replace('\\', '')
3065 domain_and_server = "%s\\%s" % (local_lsa_info.dns_domain.string, server)
3066 remote_trust_rediscover = remote_netlogon.netr_LogonControl2Ex(remote_server,
3067 netlogon.NETLOGON_CONTROL_REDISCOVER,
3070 except RuntimeError as error:
3071 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
3073 remote_conn_status = self._uint32(remote_trust_rediscover.tc_connection_status[0])
3075 remote_rediscover = "RemoteRediscover: DC[%s] CONNECTION[%s]" % (
3076 remote_trust_rediscover.trusted_dc_name,
3077 remote_trust_rediscover.tc_connection_status[1])
3079 if remote_conn_status != werror.WERR_SUCCESS:
3080 raise CommandError(remote_rediscover)
3082 self.outf.write("OK: %s\n" % remote_rediscover)
3086 class cmd_domain_trust_namespaces(DomainTrustCommand):
3087 """Manage forest trust namespaces."""
3089 synopsis = "%prog [DOMAIN] [options]"
3091 takes_optiongroups = {
3092 "sambaopts": options.SambaOptions,
3093 "versionopts": options.VersionOptions,
3094 "localdcopts": LocalDCCredentialsOptions,
3098 Option("--refresh", type="choice", metavar="check|store",
3099 choices=["check", "store", None],
3100 help="List and maybe store refreshed forest trust information: 'check' or 'store'.",
3103 Option("--enable-all", action="store_true",
3104 help="Try to update disabled entries, not allowed with --refresh=check.",
3107 Option("--enable-tln", action="append", metavar='DNSDOMAIN',
3108 help="Enable a top level name entry. Can be specified multiple times.",
3111 Option("--disable-tln", action="append", metavar='DNSDOMAIN',
3112 help="Disable a top level name entry. Can be specified multiple times.",
3115 Option("--add-tln-ex", action="append", metavar='DNSDOMAIN',
3116 help="Add a top level exclusion entry. Can be specified multiple times.",
3119 Option("--delete-tln-ex", action="append", metavar='DNSDOMAIN',
3120 help="Delete a top level exclusion entry. Can be specified multiple times.",
3121 dest='delete_tln_ex',
3123 Option("--enable-nb", action="append", metavar='NETBIOSDOMAIN',
3124 help="Enable a netbios name in a domain entry. Can be specified multiple times.",
3127 Option("--disable-nb", action="append", metavar='NETBIOSDOMAIN',
3128 help="Disable a netbios name in a domain entry. Can be specified multiple times.",
3131 Option("--enable-sid", action="append", metavar='DOMAINSID',
3132 help="Enable a SID in a domain entry. Can be specified multiple times.",
3133 dest='enable_sid_str',
3135 Option("--disable-sid", action="append", metavar='DOMAINSID',
3136 help="Disable a SID in a domain entry. Can be specified multiple times.",
3137 dest='disable_sid_str',
3139 Option("--add-upn-suffix", action="append", metavar='DNSDOMAIN',
3140 help="Add a new uPNSuffixes attribute for the local forest. Can be specified multiple times.",
3143 Option("--delete-upn-suffix", action="append", metavar='DNSDOMAIN',
3144 help="Delete an existing uPNSuffixes attribute of the local forest. Can be specified multiple times.",
3147 Option("--add-spn-suffix", action="append", metavar='DNSDOMAIN',
3148 help="Add a new msDS-SPNSuffixes attribute for the local forest. Can be specified multiple times.",
3151 Option("--delete-spn-suffix", action="append", metavar='DNSDOMAIN',
3152 help="Delete an existing msDS-SPNSuffixes attribute of the local forest. Can be specified multiple times.",
3157 takes_args = ["domain?"]
3159 def run(self, domain=None, sambaopts=None, localdcopts=None, versionopts=None,
3160 refresh=None, enable_all=False,
3161 enable_tln=[], disable_tln=[], add_tln_ex=[], delete_tln_ex=[],
3162 enable_sid_str=[], disable_sid_str=[], enable_nb=[], disable_nb=[],
3163 add_upn=[], delete_upn=[], add_spn=[], delete_spn=[]):
3165 require_update = False
3168 if refresh == "store":
3169 raise CommandError("--refresh=%s not allowed without DOMAIN" % refresh)
3172 raise CommandError("--enable-all not allowed without DOMAIN")
3174 if len(enable_tln) > 0:
3175 raise CommandError("--enable-tln not allowed without DOMAIN")
3176 if len(disable_tln) > 0:
3177 raise CommandError("--disable-tln not allowed without DOMAIN")
3179 if len(add_tln_ex) > 0:
3180 raise CommandError("--add-tln-ex not allowed without DOMAIN")
3181 if len(delete_tln_ex) > 0:
3182 raise CommandError("--delete-tln-ex not allowed without DOMAIN")
3184 if len(enable_nb) > 0:
3185 raise CommandError("--enable-nb not allowed without DOMAIN")
3186 if len(disable_nb) > 0:
3187 raise CommandError("--disable-nb not allowed without DOMAIN")
3189 if len(enable_sid_str) > 0:
3190 raise CommandError("--enable-sid not allowed without DOMAIN")
3191 if len(disable_sid_str) > 0:
3192 raise CommandError("--disable-sid not allowed without DOMAIN")
3194 if len(add_upn) > 0:
3196 if not n.startswith("*."):
3198 raise CommandError("value[%s] specified for --add-upn-suffix should not include with '*.'" % n)
3199 require_update = True
3200 if len(delete_upn) > 0:
3201 for n in delete_upn:
3202 if not n.startswith("*."):
3204 raise CommandError("value[%s] specified for --delete-upn-suffix should not include with '*.'" % n)
3205 require_update = True
3207 for d in delete_upn:
3208 if a.lower() != d.lower():
3210 raise CommandError("value[%s] specified for --add-upn-suffix and --delete-upn-suffix" % a)
3212 if len(add_spn) > 0:
3214 if not n.startswith("*."):
3216 raise CommandError("value[%s] specified for --add-spn-suffix should not include with '*.'" % n)
3217 require_update = True
3218 if len(delete_spn) > 0:
3219 for n in delete_spn:
3220 if not n.startswith("*."):
3222 raise CommandError("value[%s] specified for --delete-spn-suffix should not include with '*.'" % n)
3223 require_update = True
3225 for d in delete_spn:
3226 if a.lower() != d.lower():
3228 raise CommandError("value[%s] specified for --add-spn-suffix and --delete-spn-suffix" % a)
3230 if len(add_upn) > 0:
3231 raise CommandError("--add-upn-suffix not allowed together with DOMAIN")
3232 if len(delete_upn) > 0:
3233 raise CommandError("--delete-upn-suffix not allowed together with DOMAIN")
3234 if len(add_spn) > 0:
3235 raise CommandError("--add-spn-suffix not allowed together with DOMAIN")
3236 if len(delete_spn) > 0:
3237 raise CommandError("--delete-spn-suffix not allowed together with DOMAIN")
3239 if refresh is not None:
3240 if refresh == "store":
3241 require_update = True
3243 if enable_all and refresh != "store":
3244 raise CommandError("--enable-all not allowed together with --refresh=%s" % refresh)
3246 if len(enable_tln) > 0:
3247 raise CommandError("--enable-tln not allowed together with --refresh")
3248 if len(disable_tln) > 0:
3249 raise CommandError("--disable-tln not allowed together with --refresh")
3251 if len(add_tln_ex) > 0:
3252 raise CommandError("--add-tln-ex not allowed together with --refresh")
3253 if len(delete_tln_ex) > 0:
3254 raise CommandError("--delete-tln-ex not allowed together with --refresh")
3256 if len(enable_nb) > 0:
3257 raise CommandError("--enable-nb not allowed together with --refresh")
3258 if len(disable_nb) > 0:
3259 raise CommandError("--disable-nb not allowed together with --refresh")
3261 if len(enable_sid_str) > 0:
3262 raise CommandError("--enable-sid not allowed together with --refresh")
3263 if len(disable_sid_str) > 0:
3264 raise CommandError("--disable-sid not allowed together with --refresh")
3267 require_update = True
3269 if len(enable_tln) > 0:
3270 raise CommandError("--enable-tln not allowed together with --enable-all")
3272 if len(enable_nb) > 0:
3273 raise CommandError("--enable-nb not allowed together with --enable-all")
3275 if len(enable_sid_str) > 0:
3276 raise CommandError("--enable-sid not allowed together with --enable-all")
3278 if len(enable_tln) > 0:
3279 require_update = True
3280 if len(disable_tln) > 0:
3281 require_update = True
3282 for e in enable_tln:
3283 for d in disable_tln:
3284 if e.lower() != d.lower():
3286 raise CommandError("value[%s] specified for --enable-tln and --disable-tln" % e)
3288 if len(add_tln_ex) > 0:
3289 for n in add_tln_ex:
3290 if not n.startswith("*."):
3292 raise CommandError("value[%s] specified for --add-tln-ex should not include with '*.'" % n)
3293 require_update = True
3294 if len(delete_tln_ex) > 0:
3295 for n in delete_tln_ex:
3296 if not n.startswith("*."):
3298 raise CommandError("value[%s] specified for --delete-tln-ex should not include with '*.'" % n)
3299 require_update = True
3300 for a in add_tln_ex:
3301 for d in delete_tln_ex:
3302 if a.lower() != d.lower():
3304 raise CommandError("value[%s] specified for --add-tln-ex and --delete-tln-ex" % a)
3306 if len(enable_nb) > 0:
3307 require_update = True
3308 if len(disable_nb) > 0:
3309 require_update = True
3311 for d in disable_nb:
3312 if e.upper() != d.upper():
3314 raise CommandError("value[%s] specified for --enable-nb and --disable-nb" % e)
3317 for s in enable_sid_str:
3319 sid = security.dom_sid(s)
3320 except TypeError as error:
3321 raise CommandError("value[%s] specified for --enable-sid is not a valid SID" % s)
3322 enable_sid.append(sid)
3324 for s in disable_sid_str:
3326 sid = security.dom_sid(s)
3327 except TypeError as error:
3328 raise CommandError("value[%s] specified for --disable-sid is not a valid SID" % s)
3329 disable_sid.append(sid)
3330 if len(enable_sid) > 0:
3331 require_update = True
3332 if len(disable_sid) > 0:
3333 require_update = True
3334 for e in enable_sid:
3335 for d in disable_sid:
3338 raise CommandError("value[%s] specified for --enable-sid and --disable-sid" % e)
3340 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
3342 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
3344 local_server = self.setup_local_server(sambaopts, localdcopts)
3346 local_lsa = self.new_local_lsa_connection()
3347 except RuntimeError as error:
3348 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
3351 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
3352 except RuntimeError as error:
3353 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
3355 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
3356 local_lsa_info.name.string,
3357 local_lsa_info.dns_domain.string,
3358 local_lsa_info.sid))
3362 local_netlogon = self.new_local_netlogon_connection()
3363 except RuntimeError as error:
3364 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3367 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3368 except RuntimeError as error:
3369 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3371 if local_netlogon_info.domain_name != local_netlogon_info.forest_name:
3372 raise CommandError("The local domain [%s] is not the forest root [%s]" % (
3373 local_netlogon_info.domain_name,
3374 local_netlogon_info.forest_name))
3377 # get all information about our own forest
3378 own_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3380 except RuntimeError as error:
3381 if self.check_runtime_error(error, werror.WERR_RPC_S_PROCNUM_OUT_OF_RANGE):
3382 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3385 if self.check_runtime_error(error, werror.WERR_INVALID_FUNCTION):
3386 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3389 if self.check_runtime_error(error, werror.WERR_NERR_ACFNOTLOADED):
3390 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3393 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3395 self.outf.write("Own forest trust information...\n")
3396 self.write_forest_trust_info(own_forest_info,
3397 tln=local_lsa_info.dns_domain.string)
3400 local_samdb = self.new_local_ldap_connection()
3401 except RuntimeError as error:
3402 raise self.LocalRuntimeError(self, error, "failed to connect to SamDB")
3404 local_partitions_dn = "CN=Partitions,%s" % str(local_samdb.get_config_basedn())
3405 attrs = ['uPNSuffixes', 'msDS-SPNSuffixes']
3407 msgs = local_samdb.search(base=local_partitions_dn,
3408 scope=ldb.SCOPE_BASE,
3409 expression="(objectClass=crossRefContainer)",
3411 stored_msg = msgs[0]
3412 except ldb.LdbError as error:
3413 raise self.LocalLdbError(self, error, "failed to search partition dn")
3415 stored_upn_vals = []
3416 if 'uPNSuffixes' in stored_msg:
3417 stored_upn_vals.extend(stored_msg['uPNSuffixes'])
3419 stored_spn_vals = []
3420 if 'msDS-SPNSuffixes' in stored_msg:
3421 stored_spn_vals.extend(stored_msg['msDS-SPNSuffixes'])
3423 self.outf.write("Stored uPNSuffixes attributes[%d]:\n" % len(stored_upn_vals))
3424 for v in stored_upn_vals:
3425 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3426 self.outf.write("Stored msDS-SPNSuffixes attributes[%d]:\n" % len(stored_spn_vals))
3427 for v in stored_spn_vals:
3428 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3430 if not require_update:
3434 update_upn_vals = []
3435 update_upn_vals.extend(stored_upn_vals)
3438 update_spn_vals = []
3439 update_spn_vals.extend(stored_spn_vals)
3442 for i, v in enumerate(update_upn_vals):
3443 if v.lower() == upn.lower():
3444 raise CommandError("Entry already present for "
3445 "value[%s] specified for "
3446 "--add-upn-suffix" % upn)
3447 update_upn_vals.append(upn)
3450 for upn in delete_upn:
3452 for i, v in enumerate(update_upn_vals):
3453 if v.lower() != upn.lower():
3458 raise CommandError("Entry not found for value[%s] specified for --delete-upn-suffix" % upn)
3460 update_upn_vals.pop(idx)
3464 for i, v in enumerate(update_spn_vals):
3465 if v.lower() == spn.lower():
3466 raise CommandError("Entry already present for "
3467 "value[%s] specified for "
3468 "--add-spn-suffix" % spn)
3469 update_spn_vals.append(spn)
3472 for spn in delete_spn:
3474 for i, v in enumerate(update_spn_vals):
3475 if v.lower() != spn.lower():
3480 raise CommandError("Entry not found for value[%s] specified for --delete-spn-suffix" % spn)
3482 update_spn_vals.pop(idx)
3485 self.outf.write("Update uPNSuffixes attributes[%d]:\n" % len(update_upn_vals))
3486 for v in update_upn_vals:
3487 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3488 self.outf.write("Update msDS-SPNSuffixes attributes[%d]:\n" % len(update_spn_vals))
3489 for v in update_spn_vals:
3490 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3492 update_msg = ldb.Message()
3493 update_msg.dn = stored_msg.dn
3496 update_msg['uPNSuffixes'] = ldb.MessageElement(update_upn_vals,
3497 ldb.FLAG_MOD_REPLACE,
3500 update_msg['msDS-SPNSuffixes'] = ldb.MessageElement(update_spn_vals,
3501 ldb.FLAG_MOD_REPLACE,
3504 local_samdb.modify(update_msg)
3505 except ldb.LdbError as error:
3506 raise self.LocalLdbError(self, error, "failed to update partition dn")
3509 stored_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3511 except RuntimeError as error:
3512 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3514 self.outf.write("Stored forest trust information...\n")
3515 self.write_forest_trust_info(stored_forest_info,
3516 tln=local_lsa_info.dns_domain.string)
3520 lsaString = lsa.String()
3521 lsaString.string = domain
3522 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
3523 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
3524 except NTSTATUSError as error:
3525 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
3526 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
3528 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
3530 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
3531 local_tdo_info.netbios_name.string,
3532 local_tdo_info.domain_name.string,
3533 local_tdo_info.sid))
3535 if not local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
3536 raise CommandError("trusted domain object for domain [%s] is not marked as FOREST_TRANSITIVE." % domain)
3538 if refresh is not None:
3540 local_netlogon = self.new_local_netlogon_connection()
3541 except RuntimeError as error:
3542 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3545 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3546 except RuntimeError as error:
3547 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3549 lsa_update_check = 1
3550 if refresh == "store":
3551 netlogon_update_tdo = netlogon.DS_GFTI_UPDATE_TDO
3553 lsa_update_check = 0
3555 netlogon_update_tdo = 0
3558 # get all information about the remote trust
3559 # this triggers netr_GetForestTrustInformation to the remote domain
3560 # and lsaRSetForestTrustInformation() locally, but new top level
3561 # names are disabled by default.
3562 fresh_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3563 local_tdo_info.domain_name.string,
3564 netlogon_update_tdo)
3565 except RuntimeError as error:
3566 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3569 fresh_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3570 local_tdo_info.domain_name,
3571 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3574 except RuntimeError as error:
3575 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3577 self.outf.write("Fresh forest trust information...\n")
3578 self.write_forest_trust_info(fresh_forest_info,
3579 tln=local_tdo_info.domain_name.string,
3580 collisions=fresh_forest_collision)
3582 if refresh == "store":
3584 lsaString = lsa.String()
3585 lsaString.string = local_tdo_info.domain_name.string
3586 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3588 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3589 except RuntimeError as error:
3590 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3592 self.outf.write("Stored forest trust information...\n")
3593 self.write_forest_trust_info(stored_forest_info,
3594 tln=local_tdo_info.domain_name.string)
3599 # The none --refresh path
3603 lsaString = lsa.String()
3604 lsaString.string = local_tdo_info.domain_name.string
3605 local_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3607 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3608 except RuntimeError as error:
3609 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3611 self.outf.write("Local forest trust information...\n")
3612 self.write_forest_trust_info(local_forest_info,
3613 tln=local_tdo_info.domain_name.string)
3615 if not require_update:
3619 entries.extend(local_forest_info.entries)
3620 update_forest_info = lsa.ForestTrustInformation()
3621 update_forest_info.count = len(entries)
3622 update_forest_info.entries = entries
3625 for i, r in enumerate(update_forest_info.entries):
3626 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3628 if update_forest_info.entries[i].flags == 0:
3630 update_forest_info.entries[i].time = 0
3631 update_forest_info.entries[i].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3632 for i, r in enumerate(update_forest_info.entries):
3633 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3635 if update_forest_info.entries[i].flags == 0:
3637 update_forest_info.entries[i].time = 0
3638 update_forest_info.entries[i].flags &= ~lsa.LSA_NB_DISABLED_MASK
3639 update_forest_info.entries[i].flags &= ~lsa.LSA_SID_DISABLED_MASK
3641 for tln in enable_tln:
3643 for i, r in enumerate(update_forest_info.entries):
3644 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3646 if r.forest_trust_data.string.lower() != tln.lower():
3651 raise CommandError("Entry not found for value[%s] specified for --enable-tln" % tln)
3652 if not update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_MASK:
3653 raise CommandError("Entry found for value[%s] specified for --enable-tln is already enabled" % tln)
3654 update_forest_info.entries[idx].time = 0
3655 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3657 for tln in disable_tln:
3659 for i, r in enumerate(update_forest_info.entries):
3660 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3662 if r.forest_trust_data.string.lower() != tln.lower():
3667 raise CommandError("Entry not found for value[%s] specified for --disable-tln" % tln)
3668 if update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_ADMIN:
3669 raise CommandError("Entry found for value[%s] specified for --disable-tln is already disabled" % tln)
3670 update_forest_info.entries[idx].time = 0
3671 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3672 update_forest_info.entries[idx].flags |= lsa.LSA_TLN_DISABLED_ADMIN
3674 for tln_ex in add_tln_ex:
3676 for i, r in enumerate(update_forest_info.entries):
3677 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3679 if r.forest_trust_data.string.lower() != tln_ex.lower():
3684 raise CommandError("Entry already present for value[%s] specified for --add-tln-ex" % tln_ex)
3686 tln_dot = ".%s" % tln_ex.lower()
3688 for i, r in enumerate(update_forest_info.entries):
3689 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3691 r_dot = ".%s" % r.forest_trust_data.string.lower()
3692 if tln_dot == r_dot:
3693 raise CommandError("TLN entry present for value[%s] specified for --add-tln-ex" % tln_ex)
3694 if not tln_dot.endswith(r_dot):
3700 raise CommandError("No TLN parent present for value[%s] specified for --add-tln-ex" % tln_ex)
3702 r = lsa.ForestTrustRecord()
3703 r.type = lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX
3706 r.forest_trust_data.string = tln_ex
3709 entries.extend(update_forest_info.entries)
3710 entries.insert(idx + 1, r)
3711 update_forest_info.count = len(entries)
3712 update_forest_info.entries = entries
3714 for tln_ex in delete_tln_ex:
3716 for i, r in enumerate(update_forest_info.entries):
3717 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3719 if r.forest_trust_data.string.lower() != tln_ex.lower():
3724 raise CommandError("Entry not found for value[%s] specified for --delete-tln-ex" % tln_ex)
3727 entries.extend(update_forest_info.entries)
3729 update_forest_info.count = len(entries)
3730 update_forest_info.entries = entries
3732 for nb in enable_nb:
3734 for i, r in enumerate(update_forest_info.entries):
3735 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3737 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3742 raise CommandError("Entry not found for value[%s] specified for --enable-nb" % nb)
3743 if not update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_MASK:
3744 raise CommandError("Entry found for value[%s] specified for --enable-nb is already enabled" % nb)
3745 update_forest_info.entries[idx].time = 0
3746 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3748 for nb in disable_nb:
3750 for i, r in enumerate(update_forest_info.entries):
3751 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3753 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3758 raise CommandError("Entry not found for value[%s] specified for --delete-nb" % nb)
3759 if update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_ADMIN:
3760 raise CommandError("Entry found for value[%s] specified for --disable-nb is already disabled" % nb)
3761 update_forest_info.entries[idx].time = 0
3762 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3763 update_forest_info.entries[idx].flags |= lsa.LSA_NB_DISABLED_ADMIN
3765 for sid in enable_sid:
3767 for i, r in enumerate(update_forest_info.entries):
3768 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3770 if r.forest_trust_data.domain_sid != sid:
3775 raise CommandError("Entry not found for value[%s] specified for --enable-sid" % sid)
3776 if not update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_MASK:
3777 raise CommandError("Entry found for value[%s] specified for --enable-sid is already enabled" % nb)
3778 update_forest_info.entries[idx].time = 0
3779 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3781 for sid in disable_sid:
3783 for i, r in enumerate(update_forest_info.entries):
3784 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3786 if r.forest_trust_data.domain_sid != sid:
3791 raise CommandError("Entry not found for value[%s] specified for --delete-sid" % sid)
3792 if update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_ADMIN:
3793 raise CommandError("Entry found for value[%s] specified for --disable-sid is already disabled" % nb)
3794 update_forest_info.entries[idx].time = 0
3795 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3796 update_forest_info.entries[idx].flags |= lsa.LSA_SID_DISABLED_ADMIN
3799 update_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3800 local_tdo_info.domain_name,
3801 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3802 update_forest_info, 0)
3803 except RuntimeError as error:
3804 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3806 self.outf.write("Updated forest trust information...\n")
3807 self.write_forest_trust_info(update_forest_info,
3808 tln=local_tdo_info.domain_name.string,
3809 collisions=update_forest_collision)
3812 lsaString = lsa.String()
3813 lsaString.string = local_tdo_info.domain_name.string
3814 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3816 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3817 except RuntimeError as error:
3818 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3820 self.outf.write("Stored forest trust information...\n")
3821 self.write_forest_trust_info(stored_forest_info,
3822 tln=local_tdo_info.domain_name.string)
3825 class cmd_domain_tombstones_expunge(Command):
3826 """Expunge tombstones from the database.
3828 This command expunges tombstones from the database."""
3829 synopsis = "%prog NC [NC [...]] [options]"
3832 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
3833 metavar="URL", dest="H"),
3834 Option("--current-time",
3835 help="The current time to evaluate the tombstone lifetime from, expressed as YYYY-MM-DD",
3837 Option("--tombstone-lifetime", help="Number of days a tombstone should be preserved for", type=int),
3840 takes_args = ["nc*"]
3842 takes_optiongroups = {
3843 "sambaopts": options.SambaOptions,
3844 "credopts": options.CredentialsOptions,
3845 "versionopts": options.VersionOptions,
3848 def run(self, *ncs, **kwargs):
3849 sambaopts = kwargs.get("sambaopts")
3850 credopts = kwargs.get("credopts")
3851 versionpts = kwargs.get("versionopts")
3853 current_time_string = kwargs.get("current_time")
3854 tombstone_lifetime = kwargs.get("tombstone_lifetime")
3855 lp = sambaopts.get_loadparm()
3856 creds = credopts.get_credentials(lp)
3857 samdb = SamDB(url=H, session_info=system_session(),
3858 credentials=creds, lp=lp)
3860 if current_time_string is not None:
3861 current_time_obj = time.strptime(current_time_string, "%Y-%m-%d")
3862 current_time = long(time.mktime(current_time_obj))
3865 current_time = long(time.time())
3868 res = samdb.search(expression="", base="", scope=ldb.SCOPE_BASE,
3869 attrs=["namingContexts"])
3872 for nc in res[0]["namingContexts"]:
3877 started_transaction = False
3879 samdb.transaction_start()
3880 started_transaction = True
3882 removed_links) = samdb.garbage_collect_tombstones(ncs,
3883 current_time=current_time,
3884 tombstone_lifetime=tombstone_lifetime)
3886 except Exception as err:
3887 if started_transaction:
3888 samdb.transaction_cancel()
3889 raise CommandError("Failed to expunge / garbage collect tombstones", err)
3891 samdb.transaction_commit()
3893 self.outf.write("Removed %d objects and %d links successfully\n"
3894 % (removed_objects, removed_links))
3898 class cmd_domain_trust(SuperCommand):
3899 """Domain and forest trust management."""
3902 subcommands["list"] = cmd_domain_trust_list()
3903 subcommands["show"] = cmd_domain_trust_show()
3904 subcommands["create"] = cmd_domain_trust_create()
3905 subcommands["delete"] = cmd_domain_trust_delete()
3906 subcommands["validate"] = cmd_domain_trust_validate()
3907 subcommands["namespaces"] = cmd_domain_trust_namespaces()
3909 class cmd_domain_tombstones(SuperCommand):
3910 """Domain tombstone and recycled object management."""
3913 subcommands["expunge"] = cmd_domain_tombstones_expunge()
3915 class ldif_schema_update:
3916 """Helper class for applying LDIF schema updates"""
3919 self.is_defunct = False
3920 self.unknown_oid = None
3924 def can_ignore_failure(self, error):
3925 """Checks if we can safely ignore failure to apply an LDIF update"""
3926 (num, errstr) = error.args
3928 # Microsoft has marked objects as defunct that Samba doesn't know about
3929 if num == ldb.ERR_NO_SUCH_OBJECT and self.is_defunct:
3930 print("Defunct object %s doesn't exist, skipping" % self.dn)
3932 elif self.unknown_oid is not None:
3933 print("Skipping unknown OID %s for object %s" %(self.unknown_oid, self.dn))
3938 def apply(self, samdb):
3939 """Applies a single LDIF update to the schema"""
3943 samdb.modify_ldif(self.ldif, controls=['relax:0'])
3944 except ldb.LdbError as e:
3945 if e.args[0] == ldb.ERR_INVALID_ATTRIBUTE_SYNTAX:
3947 # REFRESH after a failed change
3949 # Otherwise the OID-to-attribute mapping in
3950 # _apply_updates_in_file() won't work, because it
3951 # can't lookup the new OID in the schema
3952 samdb.set_schema_update_now()
3954 samdb.modify_ldif(self.ldif, controls=['relax:0'])
3957 except ldb.LdbError as e:
3958 if self.can_ignore_failure(e):
3961 print("Exception: %s" % e)
3962 print("Encountered while trying to apply the following LDIF")
3963 print("----------------------------------------------------")
3964 print("%s" % self.ldif)
3970 class cmd_domain_schema_upgrade(Command):
3971 """Domain schema upgrading"""
3973 synopsis = "%prog [options]"
3975 takes_optiongroups = {
3976 "sambaopts": options.SambaOptions,
3977 "versionopts": options.VersionOptions,
3978 "credopts": options.CredentialsOptions,
3982 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
3983 metavar="URL", dest="H"),
3984 Option("-q", "--quiet", help="Be quiet", action="store_true"), #unused
3985 Option("-v", "--verbose", help="Be verbose", action="store_true"),
3986 Option("--schema", type="choice", metavar="SCHEMA",
3987 choices=["2012", "2012_R2"],
3988 help="The schema file to upgrade to. Default is (Windows) 2012_R2.",
3990 Option("--ldf-file", type=str, default=None,
3991 help="Just apply the schema updates in the adprep/.LDF file(s) specified"),
3992 Option("--base-dir", type=str, default=None,
3993 help="Location of ldf files Default is ${SETUPDIR}/adprep.")
3996 def _apply_updates_in_file(self, samdb, ldif_file):
3998 Applies a series of updates specified in an .LDIF file. The .LDIF file
3999 is based on the adprep Schema updates provided by Microsoft.
4002 ldif_op = ldif_schema_update()
4004 # parse the file line by line and work out each update operation to apply
4005 for line in ldif_file:
4007 line = line.rstrip()
4009 # the operations in the .LDIF file are separated by blank lines. If
4010 # we hit a blank line, try to apply the update we've parsed so far
4013 # keep going if we haven't parsed anything yet
4014 if ldif_op.ldif == '':
4017 # Apply the individual change
4018 count += ldif_op.apply(samdb)
4020 # start storing the next operation from scratch again
4021 ldif_op = ldif_schema_update()
4024 # replace the placeholder domain name in the .ldif file with the real domain
4025 if line.upper().endswith('DC=X'):
4026 line = line[:-len('DC=X')] + str(samdb.get_default_basedn())
4027 elif line.upper().endswith('CN=X'):
4028 line = line[:-len('CN=X')] + str(samdb.get_default_basedn())
4030 values = line.split(':')
4032 if values[0].lower() == 'dn':
4033 ldif_op.dn = values[1].strip()
4035 # replace the Windows-specific operation with the Samba one
4036 if values[0].lower() == 'changetype':
4037 line = line.lower().replace(': ntdsschemaadd',
4039 line = line.lower().replace(': ntdsschemamodify',
4042 if values[0].lower() in ['rdnattid', 'subclassof',
4043 'systemposssuperiors',
4045 'systemauxiliaryclass']:
4048 # The Microsoft updates contain some OIDs we don't recognize.
4049 # Query the DB to see if we can work out the OID this update is
4050 # referring to. If we find a match, then replace the OID with
4051 # the ldapDisplayname
4053 res = samdb.search(base=samdb.get_schema_basedn(),
4054 expression="(|(attributeId=%s)(governsId=%s))" %
4056 attrs=['ldapDisplayName'])
4059 ldif_op.unknown_oid = value
4061 display_name = res[0]['ldapDisplayName'][0]
4062 line = line.replace(value, ' ' + display_name)
4064 # Microsoft has marked objects as defunct that Samba doesn't know about
4065 if values[0].lower() == 'isdefunct' and values[1].strip().lower() == 'true':
4066 ldif_op.is_defunct = True
4068 # Samba has added the showInAdvancedViewOnly attribute to all objects,
4069 # so rather than doing an add, we need to do a replace
4070 if values[0].lower() == 'add' and values[1].strip().lower() == 'showinadvancedviewonly':
4071 line = 'replace: showInAdvancedViewOnly'
4073 # Add the line to the current LDIF operation (including the newline
4074 # we stripped off at the start of the loop)
4075 ldif_op.ldif += line + '\n'
4080 def _apply_update(self, samdb, update_file, base_dir):
4081 """Wrapper function for parsing an LDIF file and applying the updates"""
4083 print("Applying %s updates..." % update_file)
4087 ldif_file = open(os.path.join(base_dir, update_file))
4089 count = self._apply_updates_in_file(samdb, ldif_file)
4095 print("%u changes applied" % count)
4099 def run(self, **kwargs):
4100 from samba.ms_schema_markdown import read_ms_markdown
4101 from samba.schema import Schema
4103 updates_allowed_overriden = False
4104 sambaopts = kwargs.get("sambaopts")
4105 credopts = kwargs.get("credopts")
4106 versionpts = kwargs.get("versionopts")
4107 lp = sambaopts.get_loadparm()
4108 creds = credopts.get_credentials(lp)
4110 target_schema = kwargs.get("schema")
4111 ldf_files = kwargs.get("ldf_file")
4112 base_dir = kwargs.get("base_dir")
4116 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
4118 # we're not going to get far if the config doesn't allow schema updates
4119 if lp.get("dsdb:schema update allowed") is None:
4120 lp.set("dsdb:schema update allowed", "yes")
4121 print("Temporarily overriding 'dsdb:schema update allowed' setting")
4122 updates_allowed_overriden = True
4124 own_dn = ldb.Dn(samdb, samdb.get_dsServiceName())
4125 master = get_fsmo_roleowner(samdb, str(samdb.get_schema_basedn()),
4127 if own_dn != master:
4128 raise CommandError("This server is not the schema master.")
4130 # if specific LDIF files were specified, just apply them
4132 schema_updates = ldf_files.split(",")
4136 # work out the version of the target schema we're upgrading to
4137 end = Schema.get_version(target_schema)
4139 # work out the version of the schema we're currently using
4140 res = samdb.search(base=samdb.get_schema_basedn(),
4141 scope=ldb.SCOPE_BASE, attrs=['objectVersion'])
4144 raise CommandError('Could not determine current schema version')
4145 start = int(res[0]['objectVersion'][0]) + 1
4147 diff_dir = setup_path("adprep/WindowsServerDocs")
4148 if base_dir is None:
4149 # Read from the Schema-Updates.md file
4150 temp_folder = tempfile.mkdtemp()
4152 update_file = setup_path("adprep/WindowsServerDocs/Schema-Updates.md")
4155 read_ms_markdown(update_file, temp_folder)
4156 except Exception as e:
4157 print("Exception in markdown parsing: %s" % e)
4158 shutil.rmtree(temp_folder)
4159 raise CommandError('Failed to upgrade schema')
4161 base_dir = temp_folder
4163 for version in range(start, end + 1):
4164 update = 'Sch%d.ldf' % version
4165 schema_updates.append(update)
4167 # Apply patches if we parsed the Schema-Updates.md file
4168 diff = os.path.abspath(os.path.join(diff_dir, update + '.diff'))
4169 if temp_folder and os.path.exists(diff):
4171 p = subprocess.Popen(['patch', update, '-i', diff],
4172 stdout=subprocess.PIPE,
4173 stderr=subprocess.PIPE, cwd=temp_folder)
4174 except (OSError, IOError):
4175 shutil.rmtree(temp_folder)
4176 raise CommandError("Failed to upgrade schema. Check if 'patch' is installed.")
4178 stdout, stderr = p.communicate()
4181 print("Exception in patch: %s\n%s" % (stdout, stderr))
4182 shutil.rmtree(temp_folder)
4183 raise CommandError('Failed to upgrade schema')
4185 print("Patched %s using %s" % (update, diff))
4187 if base_dir is None:
4188 base_dir = setup_path("adprep")
4190 samdb.transaction_start()
4192 error_encountered = False
4195 # Apply the schema updates needed to move to the new schema version
4196 for ldif_file in schema_updates:
4197 count += self._apply_update(samdb, ldif_file, base_dir)
4200 samdb.transaction_commit()
4201 print("Schema successfully updated")
4203 print("No changes applied to schema")
4204 samdb.transaction_cancel()
4205 except Exception as e:
4206 print("Exception: %s" % e)
4207 print("Error encountered, aborting schema upgrade")
4208 samdb.transaction_cancel()
4209 error_encountered = True
4211 if updates_allowed_overriden:
4212 lp.set("dsdb:schema update allowed", "no")
4215 shutil.rmtree(temp_folder)
4217 if error_encountered:
4218 raise CommandError('Failed to upgrade schema')
4220 class cmd_domain_functional_prep(Command):
4221 """Domain functional level preparation"""
4223 synopsis = "%prog [options]"
4225 takes_optiongroups = {
4226 "sambaopts": options.SambaOptions,
4227 "versionopts": options.VersionOptions,
4228 "credopts": options.CredentialsOptions,
4232 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
4233 metavar="URL", dest="H"),
4234 Option("-q", "--quiet", help="Be quiet", action="store_true"),
4235 Option("-v", "--verbose", help="Be verbose", action="store_true"),
4236 Option("--function-level", type="choice", metavar="FUNCTION_LEVEL",
4237 choices=["2008_R2", "2012", "2012_R2"],
4238 help="The schema file to upgrade to. Default is (Windows) 2012_R2.",
4240 Option("--forest-prep", action="store_true",
4241 help="Run the forest prep (by default, both the domain and forest prep are run)."),
4242 Option("--domain-prep", action="store_true",
4243 help="Run the domain prep (by default, both the domain and forest prep are run).")
4246 def run(self, **kwargs):
4247 updates_allowed_overriden = False
4248 sambaopts = kwargs.get("sambaopts")
4249 credopts = kwargs.get("credopts")
4250 versionpts = kwargs.get("versionopts")
4251 lp = sambaopts.get_loadparm()
4252 creds = credopts.get_credentials(lp)
4254 target_level = string_version_to_constant[kwargs.get("function_level")]
4255 forest_prep = kwargs.get("forest_prep")
4256 domain_prep = kwargs.get("domain_prep")
4258 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
4260 # we're not going to get far if the config doesn't allow schema updates
4261 if lp.get("dsdb:schema update allowed") is None:
4262 lp.set("dsdb:schema update allowed", "yes")
4263 print("Temporarily overriding 'dsdb:schema update allowed' setting")
4264 updates_allowed_overriden = True
4266 if forest_prep is None and domain_prep is None:
4270 own_dn = ldb.Dn(samdb, samdb.get_dsServiceName())
4272 master = get_fsmo_roleowner(samdb, str(samdb.get_schema_basedn()),
4274 if own_dn != master:
4275 raise CommandError("This server is not the schema master.")
4278 domain_dn = samdb.domain_dn()
4279 infrastructure_dn = "CN=Infrastructure," + domain_dn
4280 master = get_fsmo_roleowner(samdb, infrastructure_dn,
4282 if own_dn != master:
4283 raise CommandError("This server is not the infrastructure master.")
4286 samdb.transaction_start()
4287 error_encountered = False
4289 from samba.forest_update import ForestUpdate
4290 forest = ForestUpdate(samdb, fix=True)
4292 forest.check_updates_iterator([53, 79, 80, 81, 82, 83])
4293 forest.check_updates_functional_level(target_level,
4294 DS_DOMAIN_FUNCTION_2008_R2,
4295 update_revision=True)
4297 samdb.transaction_commit()
4298 except Exception as e:
4299 print("Exception: %s" % e)
4300 samdb.transaction_cancel()
4301 error_encountered = True
4304 samdb.transaction_start()
4305 error_encountered = False
4307 from samba.domain_update import DomainUpdate
4309 domain = DomainUpdate(samdb, fix=True)
4310 domain.check_updates_functional_level(target_level,
4311 DS_DOMAIN_FUNCTION_2008,
4312 update_revision=True)
4314 samdb.transaction_commit()
4315 except Exception as e:
4316 print("Exception: %s" % e)
4317 samdb.transaction_cancel()
4318 error_encountered = True
4320 if updates_allowed_overriden:
4321 lp.set("dsdb:schema update allowed", "no")
4323 if error_encountered:
4324 raise CommandError('Failed to perform functional prep')
4326 class cmd_domain(SuperCommand):
4327 """Domain management."""
4330 subcommands["demote"] = cmd_domain_demote()
4331 if cmd_domain_export_keytab is not None:
4332 subcommands["exportkeytab"] = cmd_domain_export_keytab()
4333 subcommands["info"] = cmd_domain_info()
4334 subcommands["provision"] = cmd_domain_provision()
4335 subcommands["join"] = cmd_domain_join()
4336 subcommands["dcpromo"] = cmd_domain_dcpromo()
4337 subcommands["level"] = cmd_domain_level()
4338 subcommands["passwordsettings"] = cmd_domain_passwordsettings()
4339 subcommands["classicupgrade"] = cmd_domain_classicupgrade()
4340 subcommands["samba3upgrade"] = cmd_domain_samba3upgrade()
4341 subcommands["trust"] = cmd_domain_trust()
4342 subcommands["tombstones"] = cmd_domain_tombstones()
4343 subcommands["schemaupgrade"] = cmd_domain_schema_upgrade()
4344 subcommands["functionalprep"] = cmd_domain_functional_prep()
4345 subcommands["backup"] = cmd_domain_backup()