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"
847 for part in (samdb.get_schema_basedn(),
848 samdb.get_config_basedn(),
849 samdb.get_root_basedn()):
850 nc = drsuapi.DsReplicaObjectIdentifier()
853 req1 = drsuapi.DsReplicaSyncRequest1()
854 req1.naming_context = nc;
855 req1.options = drsuapi.DRSUAPI_DRS_WRIT_REP
856 req1.source_dsa_guid = misc.GUID(ntds_guid)
859 drsuapiBind.DsReplicaSync(drsuapi_handle, 1, req1)
860 except RuntimeError as e1:
861 (werr, string) = e1.args
862 if werr == werror.WERR_DS_DRA_NO_REPLICA:
866 "Error while replicating out last local changes from '%s' for demotion, "
867 "re-enabling inbound replication\n" % part)
868 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
869 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
871 raise CommandError("Error while sending a DsReplicaSync for partition '%s'" % str(part), string)
873 remote_samdb = SamDB(url="ldap://%s" % server,
874 session_info=system_session(),
875 credentials=creds, lp=lp)
877 self.errf.write("Changing userControl and container\n")
878 res = remote_samdb.search(base=str(remote_samdb.domain_dn()),
879 expression="(&(objectClass=user)(sAMAccountName=%s$))" %
880 netbios_name.upper(),
881 attrs=["userAccountControl"])
883 uac = int(str(res[0]["userAccountControl"]))
885 except Exception as e:
886 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
888 "Error while demoting, re-enabling inbound replication\n")
889 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
890 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
892 raise CommandError("Error while changing account control", e)
895 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
897 "Error while demoting, re-enabling inbound replication")
898 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
899 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
901 raise CommandError("Unable to find object with samaccountName = %s$"
902 " in the remote dc" % netbios_name.upper())
906 uac &= ~(UF_SERVER_TRUST_ACCOUNT|UF_TRUSTED_FOR_DELEGATION|UF_PARTIAL_SECRETS_ACCOUNT)
907 uac |= UF_WORKSTATION_TRUST_ACCOUNT
912 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
913 ldb.FLAG_MOD_REPLACE,
914 "userAccountControl")
916 remote_samdb.modify(msg)
917 except Exception as e:
918 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
920 "Error while demoting, re-enabling inbound replication")
921 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
922 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
925 raise CommandError("Error while changing account control", e)
927 parent = msg.dn.parent()
928 dc_name = res[0].dn.get_rdn_value()
929 rdn = "CN=%s" % dc_name
931 # Let's move to the Computer container
935 computer_dn = ldb.Dn(remote_samdb, "CN=Computers,%s" % str(remote_samdb.domain_dn()))
936 res = remote_samdb.search(base=computer_dn, expression=rdn, scope=ldb.SCOPE_ONELEVEL)
939 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
940 scope=ldb.SCOPE_ONELEVEL)
941 while(len(res) != 0 and i < 100):
943 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
944 scope=ldb.SCOPE_ONELEVEL)
947 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
949 "Error while demoting, re-enabling inbound replication\n")
950 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
951 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
957 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
958 ldb.FLAG_MOD_REPLACE,
959 "userAccountControl")
961 remote_samdb.modify(msg)
963 raise CommandError("Unable to find a slot for renaming %s,"
964 " all names from %s-1 to %s-%d seemed used" %
965 (str(dc_dn), rdn, rdn, i - 9))
967 newrdn = "%s-%d" % (rdn, i)
970 newdn = ldb.Dn(remote_samdb, "%s,%s" % (newrdn, str(computer_dn)))
971 remote_samdb.rename(dc_dn, newdn)
972 except Exception as e:
973 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
975 "Error while demoting, re-enabling inbound replication\n")
976 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
977 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
983 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
984 ldb.FLAG_MOD_REPLACE,
985 "userAccountControl")
987 remote_samdb.modify(msg)
988 raise CommandError("Error while renaming %s to %s" % (str(dc_dn), str(newdn)), e)
991 server_dsa_dn = samdb.get_serverName()
992 domain = remote_samdb.get_root_basedn()
995 req1 = drsuapi.DsRemoveDSServerRequest1()
996 req1.server_dn = str(server_dsa_dn)
997 req1.domain_dn = str(domain)
1000 drsuapiBind.DsRemoveDSServer(drsuapi_handle, 1, req1)
1001 except RuntimeError as e3:
1002 (werr, string) = e3.args
1003 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
1005 "Error while demoting, re-enabling inbound replication\n")
1006 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
1007 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
1013 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
1014 ldb.FLAG_MOD_REPLACE,
1015 "userAccountControl")
1016 remote_samdb.modify(msg)
1017 remote_samdb.rename(newdn, dc_dn)
1018 if werr == werror.WERR_DS_DRA_NO_REPLICA:
1019 raise CommandError("The DC %s is not present on (already "
1020 "removed from) the remote server: %s" %
1021 (server_dsa_dn, e3))
1023 raise CommandError("Error while sending a removeDsServer "
1025 (server_dsa_dn, e3))
1027 remove_dc.remove_sysvol_references(remote_samdb, logger, dc_name)
1029 # These are objects under the computer account that should be deleted
1030 for s in ("CN=Enterprise,CN=NTFRS Subscriptions",
1031 "CN=%s, CN=NTFRS Subscriptions" % lp.get("realm"),
1032 "CN=Domain system Volumes (SYSVOL Share), CN=NTFRS Subscriptions",
1033 "CN=NTFRS Subscriptions"):
1035 remote_samdb.delete(ldb.Dn(remote_samdb,
1036 "%s,%s" % (s, str(newdn))))
1037 except ldb.LdbError as l:
1040 # get dns host name for target server to demote, remove dns references
1041 remove_dc.remove_dns_references(remote_samdb, logger, samdb.host_dns_name(),
1042 ignore_no_name=True)
1044 self.errf.write("Demote successful\n")
1047 class cmd_domain_level(Command):
1048 """Raise domain and forest function levels."""
1050 synopsis = "%prog (show|raise <options>) [options]"
1052 takes_optiongroups = {
1053 "sambaopts": options.SambaOptions,
1054 "credopts": options.CredentialsOptions,
1055 "versionopts": options.VersionOptions,
1059 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1060 metavar="URL", dest="H"),
1061 Option("-q", "--quiet", help="Be quiet", action="store_true"), # unused
1062 Option("--forest-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
1063 help="The forest function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)"),
1064 Option("--domain-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
1065 help="The domain function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)")
1068 takes_args = ["subcommand"]
1070 def run(self, subcommand, H=None, forest_level=None, domain_level=None,
1071 quiet=False, credopts=None, sambaopts=None, versionopts=None):
1072 lp = sambaopts.get_loadparm()
1073 creds = credopts.get_credentials(lp, fallback_machine=True)
1075 samdb = SamDB(url=H, session_info=system_session(),
1076 credentials=creds, lp=lp)
1078 domain_dn = samdb.domain_dn()
1080 res_forest = samdb.search("CN=Partitions,%s" % samdb.get_config_basedn(),
1081 scope=ldb.SCOPE_BASE, attrs=["msDS-Behavior-Version"])
1082 assert len(res_forest) == 1
1084 res_domain = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1085 attrs=["msDS-Behavior-Version", "nTMixedDomain"])
1086 assert len(res_domain) == 1
1088 res_dc_s = samdb.search("CN=Sites,%s" % samdb.get_config_basedn(),
1089 scope=ldb.SCOPE_SUBTREE, expression="(objectClass=nTDSDSA)",
1090 attrs=["msDS-Behavior-Version"])
1091 assert len(res_dc_s) >= 1
1093 # default values, since "msDS-Behavior-Version" does not exist on Windows 2000 AD
1094 level_forest = DS_DOMAIN_FUNCTION_2000
1095 level_domain = DS_DOMAIN_FUNCTION_2000
1097 if "msDS-Behavior-Version" in res_forest[0]:
1098 level_forest = int(res_forest[0]["msDS-Behavior-Version"][0])
1099 if "msDS-Behavior-Version" in res_domain[0]:
1100 level_domain = int(res_domain[0]["msDS-Behavior-Version"][0])
1101 level_domain_mixed = int(res_domain[0]["nTMixedDomain"][0])
1104 for msg in res_dc_s:
1105 if "msDS-Behavior-Version" in msg:
1106 if min_level_dc is None or int(msg["msDS-Behavior-Version"][0]) < min_level_dc:
1107 min_level_dc = int(msg["msDS-Behavior-Version"][0])
1109 min_level_dc = DS_DOMAIN_FUNCTION_2000
1110 # well, this is the least
1113 if level_forest < DS_DOMAIN_FUNCTION_2000 or level_domain < DS_DOMAIN_FUNCTION_2000:
1114 raise CommandError("Domain and/or forest function level(s) is/are invalid. Correct them or reprovision!")
1115 if min_level_dc < DS_DOMAIN_FUNCTION_2000:
1116 raise CommandError("Lowest function level of a DC is invalid. Correct this or reprovision!")
1117 if level_forest > level_domain:
1118 raise CommandError("Forest function level is higher than the domain level(s). Correct this or reprovision!")
1119 if level_domain > min_level_dc:
1120 raise CommandError("Domain function level is higher than the lowest function level of a DC. Correct this or reprovision!")
1122 if subcommand == "show":
1123 self.message("Domain and forest function level for domain '%s'" % domain_dn)
1124 if level_forest == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1125 self.message("\nATTENTION: You run SAMBA 4 on a forest function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1126 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1127 self.message("\nATTENTION: You run SAMBA 4 on a domain function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1128 if min_level_dc == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1129 self.message("\nATTENTION: You run SAMBA 4 on a lowest function level of a DC lower than Windows 2003. This isn't supported! Please step-up or upgrade the concerning DC(s)!")
1133 if level_forest == DS_DOMAIN_FUNCTION_2000:
1135 elif level_forest == DS_DOMAIN_FUNCTION_2003_MIXED:
1136 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1137 elif level_forest == DS_DOMAIN_FUNCTION_2003:
1139 elif level_forest == DS_DOMAIN_FUNCTION_2008:
1141 elif level_forest == DS_DOMAIN_FUNCTION_2008_R2:
1143 elif level_forest == DS_DOMAIN_FUNCTION_2012:
1145 elif level_forest == DS_DOMAIN_FUNCTION_2012_R2:
1148 outstr = "higher than 2012 R2"
1149 self.message("Forest function level: (Windows) " + outstr)
1151 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1152 outstr = "2000 mixed (NT4 DC support)"
1153 elif level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed == 0:
1155 elif level_domain == DS_DOMAIN_FUNCTION_2003_MIXED:
1156 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1157 elif level_domain == DS_DOMAIN_FUNCTION_2003:
1159 elif level_domain == DS_DOMAIN_FUNCTION_2008:
1161 elif level_domain == DS_DOMAIN_FUNCTION_2008_R2:
1163 elif level_domain == DS_DOMAIN_FUNCTION_2012:
1165 elif level_domain == DS_DOMAIN_FUNCTION_2012_R2:
1168 outstr = "higher than 2012 R2"
1169 self.message("Domain function level: (Windows) " + outstr)
1171 if min_level_dc == DS_DOMAIN_FUNCTION_2000:
1173 elif min_level_dc == DS_DOMAIN_FUNCTION_2003:
1175 elif min_level_dc == DS_DOMAIN_FUNCTION_2008:
1177 elif min_level_dc == DS_DOMAIN_FUNCTION_2008_R2:
1179 elif min_level_dc == DS_DOMAIN_FUNCTION_2012:
1181 elif min_level_dc == DS_DOMAIN_FUNCTION_2012_R2:
1184 outstr = "higher than 2012 R2"
1185 self.message("Lowest function level of a DC: (Windows) " + outstr)
1187 elif subcommand == "raise":
1190 if domain_level is not None:
1191 if domain_level == "2003":
1192 new_level_domain = DS_DOMAIN_FUNCTION_2003
1193 elif domain_level == "2008":
1194 new_level_domain = DS_DOMAIN_FUNCTION_2008
1195 elif domain_level == "2008_R2":
1196 new_level_domain = DS_DOMAIN_FUNCTION_2008_R2
1197 elif domain_level == "2012":
1198 new_level_domain = DS_DOMAIN_FUNCTION_2012
1199 elif domain_level == "2012_R2":
1200 new_level_domain = DS_DOMAIN_FUNCTION_2012_R2
1202 if new_level_domain <= level_domain and level_domain_mixed == 0:
1203 raise CommandError("Domain function level can't be smaller than or equal to the actual one!")
1204 if new_level_domain > min_level_dc:
1205 raise CommandError("Domain function level can't be higher than the lowest function level of a DC!")
1207 # Deactivate mixed/interim domain support
1208 if level_domain_mixed != 0:
1209 # Directly on the base DN
1211 m.dn = ldb.Dn(samdb, domain_dn)
1212 m["nTMixedDomain"] = ldb.MessageElement("0",
1213 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1217 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup") + ",CN=Partitions,%s" % samdb.get_config_basedn())
1218 m["nTMixedDomain"] = ldb.MessageElement("0",
1219 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1222 except ldb.LdbError as e:
1223 (enum, emsg) = e.args
1224 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1227 # Directly on the base DN
1229 m.dn = ldb.Dn(samdb, domain_dn)
1230 m["msDS-Behavior-Version"]= ldb.MessageElement(
1231 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1232 "msDS-Behavior-Version")
1236 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup")
1237 + ",CN=Partitions,%s" % samdb.get_config_basedn())
1238 m["msDS-Behavior-Version"]= ldb.MessageElement(
1239 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1240 "msDS-Behavior-Version")
1243 except ldb.LdbError as e2:
1244 (enum, emsg) = e2.args
1245 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1248 level_domain = new_level_domain
1249 msgs.append("Domain function level changed!")
1251 if forest_level is not None:
1252 if forest_level == "2003":
1253 new_level_forest = DS_DOMAIN_FUNCTION_2003
1254 elif forest_level == "2008":
1255 new_level_forest = DS_DOMAIN_FUNCTION_2008
1256 elif forest_level == "2008_R2":
1257 new_level_forest = DS_DOMAIN_FUNCTION_2008_R2
1258 elif forest_level == "2012":
1259 new_level_forest = DS_DOMAIN_FUNCTION_2012
1260 elif forest_level == "2012_R2":
1261 new_level_forest = DS_DOMAIN_FUNCTION_2012_R2
1263 if new_level_forest <= level_forest:
1264 raise CommandError("Forest function level can't be smaller than or equal to the actual one!")
1265 if new_level_forest > level_domain:
1266 raise CommandError("Forest function level can't be higher than the domain function level(s). Please raise it/them first!")
1269 m.dn = ldb.Dn(samdb, "CN=Partitions,%s" % samdb.get_config_basedn())
1270 m["msDS-Behavior-Version"]= ldb.MessageElement(
1271 str(new_level_forest), ldb.FLAG_MOD_REPLACE,
1272 "msDS-Behavior-Version")
1274 msgs.append("Forest function level changed!")
1275 msgs.append("All changes applied successfully!")
1276 self.message("\n".join(msgs))
1278 raise CommandError("invalid argument: '%s' (choose from 'show', 'raise')" % subcommand)
1280 class cmd_domain_passwordsettings_show(Command):
1281 """Display current password settings for the domain."""
1283 synopsis = "%prog [options]"
1285 takes_optiongroups = {
1286 "sambaopts": options.SambaOptions,
1287 "versionopts": options.VersionOptions,
1288 "credopts": options.CredentialsOptions,
1292 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1293 metavar="URL", dest="H"),
1296 def run(self, H=None, credopts=None, sambaopts=None, versionopts=None):
1297 lp = sambaopts.get_loadparm()
1298 creds = credopts.get_credentials(lp)
1300 samdb = SamDB(url=H, session_info=system_session(),
1301 credentials=creds, lp=lp)
1303 domain_dn = samdb.domain_dn()
1304 res = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1305 attrs=["pwdProperties", "pwdHistoryLength", "minPwdLength",
1306 "minPwdAge", "maxPwdAge", "lockoutDuration", "lockoutThreshold",
1307 "lockOutObservationWindow"])
1308 assert(len(res) == 1)
1310 pwd_props = int(res[0]["pwdProperties"][0])
1311 pwd_hist_len = int(res[0]["pwdHistoryLength"][0])
1312 cur_min_pwd_len = int(res[0]["minPwdLength"][0])
1314 cur_min_pwd_age = int(abs(int(res[0]["minPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1315 if int(res[0]["maxPwdAge"][0]) == -0x8000000000000000:
1318 cur_max_pwd_age = int(abs(int(res[0]["maxPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1319 cur_account_lockout_threshold = int(res[0]["lockoutThreshold"][0])
1321 if int(res[0]["lockoutDuration"][0]) == -0x8000000000000000:
1322 cur_account_lockout_duration = 0
1324 cur_account_lockout_duration = abs(int(res[0]["lockoutDuration"][0])) / (1e7 * 60)
1325 cur_reset_account_lockout_after = abs(int(res[0]["lockOutObservationWindow"][0])) / (1e7 * 60)
1326 except Exception as e:
1327 raise CommandError("Could not retrieve password properties!", e)
1329 self.message("Password informations for domain '%s'" % domain_dn)
1331 if pwd_props & DOMAIN_PASSWORD_COMPLEX != 0:
1332 self.message("Password complexity: on")
1334 self.message("Password complexity: off")
1335 if pwd_props & DOMAIN_PASSWORD_STORE_CLEARTEXT != 0:
1336 self.message("Store plaintext passwords: on")
1338 self.message("Store plaintext passwords: off")
1339 self.message("Password history length: %d" % pwd_hist_len)
1340 self.message("Minimum password length: %d" % cur_min_pwd_len)
1341 self.message("Minimum password age (days): %d" % cur_min_pwd_age)
1342 self.message("Maximum password age (days): %d" % cur_max_pwd_age)
1343 self.message("Account lockout duration (mins): %d" % cur_account_lockout_duration)
1344 self.message("Account lockout threshold (attempts): %d" % cur_account_lockout_threshold)
1345 self.message("Reset account lockout after (mins): %d" % cur_reset_account_lockout_after)
1347 class cmd_domain_passwordsettings_set(Command):
1348 """Set password settings.
1350 Password complexity, password lockout policy, history length,
1351 minimum password length, the minimum and maximum password age) on
1352 a Samba AD DC server.
1354 Use against a Windows DC is possible, but group policy will override it.
1357 synopsis = "%prog <options> [options]"
1359 takes_optiongroups = {
1360 "sambaopts": options.SambaOptions,
1361 "versionopts": options.VersionOptions,
1362 "credopts": options.CredentialsOptions,
1366 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1367 metavar="URL", dest="H"),
1368 Option("-q", "--quiet", help="Be quiet", action="store_true"), # unused
1369 Option("--complexity", type="choice", choices=["on","off","default"],
1370 help="The password complexity (on | off | default). Default is 'on'"),
1371 Option("--store-plaintext", type="choice", choices=["on","off","default"],
1372 help="Store plaintext passwords where account have 'store passwords with reversible encryption' set (on | off | default). Default is 'off'"),
1373 Option("--history-length",
1374 help="The password history length (<integer> | default). Default is 24.", type=str),
1375 Option("--min-pwd-length",
1376 help="The minimum password length (<integer> | default). Default is 7.", type=str),
1377 Option("--min-pwd-age",
1378 help="The minimum password age (<integer in days> | default). Default is 1.", type=str),
1379 Option("--max-pwd-age",
1380 help="The maximum password age (<integer in days> | default). Default is 43.", type=str),
1381 Option("--account-lockout-duration",
1382 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),
1383 Option("--account-lockout-threshold",
1384 help="The number of bad password attempts allowed before locking out the account (<integer> | default). Default is 0 (never lock out).", type=str),
1385 Option("--reset-account-lockout-after",
1386 help="After this time is elapsed, the recorded number of attempts restarts from zero (<integer> | default). Default is 30.", type=str),
1389 def run(self, H=None, min_pwd_age=None, max_pwd_age=None,
1390 quiet=False, complexity=None, store_plaintext=None, history_length=None,
1391 min_pwd_length=None, account_lockout_duration=None, account_lockout_threshold=None,
1392 reset_account_lockout_after=None, credopts=None, sambaopts=None,
1394 lp = sambaopts.get_loadparm()
1395 creds = credopts.get_credentials(lp)
1397 samdb = SamDB(url=H, session_info=system_session(),
1398 credentials=creds, lp=lp)
1400 domain_dn = samdb.domain_dn()
1403 m.dn = ldb.Dn(samdb, domain_dn)
1404 pwd_props = int(samdb.get_pwdProperties())
1406 if complexity is not None:
1407 if complexity == "on" or complexity == "default":
1408 pwd_props = pwd_props | DOMAIN_PASSWORD_COMPLEX
1409 msgs.append("Password complexity activated!")
1410 elif complexity == "off":
1411 pwd_props = pwd_props & (~DOMAIN_PASSWORD_COMPLEX)
1412 msgs.append("Password complexity deactivated!")
1414 if store_plaintext is not None:
1415 if store_plaintext == "on" or store_plaintext == "default":
1416 pwd_props = pwd_props | DOMAIN_PASSWORD_STORE_CLEARTEXT
1417 msgs.append("Plaintext password storage for changed passwords activated!")
1418 elif store_plaintext == "off":
1419 pwd_props = pwd_props & (~DOMAIN_PASSWORD_STORE_CLEARTEXT)
1420 msgs.append("Plaintext password storage for changed passwords deactivated!")
1422 if complexity is not None or store_plaintext is not None:
1423 m["pwdProperties"] = ldb.MessageElement(str(pwd_props),
1424 ldb.FLAG_MOD_REPLACE, "pwdProperties")
1426 if history_length is not None:
1427 if history_length == "default":
1430 pwd_hist_len = int(history_length)
1432 if pwd_hist_len < 0 or pwd_hist_len > 24:
1433 raise CommandError("Password history length must be in the range of 0 to 24!")
1435 m["pwdHistoryLength"] = ldb.MessageElement(str(pwd_hist_len),
1436 ldb.FLAG_MOD_REPLACE, "pwdHistoryLength")
1437 msgs.append("Password history length changed!")
1439 if min_pwd_length is not None:
1440 if min_pwd_length == "default":
1443 min_pwd_len = int(min_pwd_length)
1445 if min_pwd_len < 0 or min_pwd_len > 14:
1446 raise CommandError("Minimum password length must be in the range of 0 to 14!")
1448 m["minPwdLength"] = ldb.MessageElement(str(min_pwd_len),
1449 ldb.FLAG_MOD_REPLACE, "minPwdLength")
1450 msgs.append("Minimum password length changed!")
1452 if min_pwd_age is not None:
1453 if min_pwd_age == "default":
1456 min_pwd_age = int(min_pwd_age)
1458 if min_pwd_age < 0 or min_pwd_age > 998:
1459 raise CommandError("Minimum password age must be in the range of 0 to 998!")
1462 min_pwd_age_ticks = -int(min_pwd_age * (24 * 60 * 60 * 1e7))
1464 m["minPwdAge"] = ldb.MessageElement(str(min_pwd_age_ticks),
1465 ldb.FLAG_MOD_REPLACE, "minPwdAge")
1466 msgs.append("Minimum password age changed!")
1468 if max_pwd_age is not None:
1469 if max_pwd_age == "default":
1472 max_pwd_age = int(max_pwd_age)
1474 if max_pwd_age < 0 or max_pwd_age > 999:
1475 raise CommandError("Maximum password age must be in the range of 0 to 999!")
1478 if max_pwd_age == 0:
1479 max_pwd_age_ticks = -0x8000000000000000
1481 max_pwd_age_ticks = -int(max_pwd_age * (24 * 60 * 60 * 1e7))
1483 m["maxPwdAge"] = ldb.MessageElement(str(max_pwd_age_ticks),
1484 ldb.FLAG_MOD_REPLACE, "maxPwdAge")
1485 msgs.append("Maximum password age changed!")
1487 if account_lockout_duration is not None:
1488 if account_lockout_duration == "default":
1489 account_lockout_duration = 30
1491 account_lockout_duration = int(account_lockout_duration)
1493 if account_lockout_duration < 0 or account_lockout_duration > 99999:
1494 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1497 if account_lockout_duration == 0:
1498 account_lockout_duration_ticks = -0x8000000000000000
1500 account_lockout_duration_ticks = -int(account_lockout_duration * (60 * 1e7))
1502 m["lockoutDuration"] = ldb.MessageElement(str(account_lockout_duration_ticks),
1503 ldb.FLAG_MOD_REPLACE, "lockoutDuration")
1504 msgs.append("Account lockout duration changed!")
1506 if account_lockout_threshold is not None:
1507 if account_lockout_threshold == "default":
1508 account_lockout_threshold = 0
1510 account_lockout_threshold = int(account_lockout_threshold)
1512 m["lockoutThreshold"] = ldb.MessageElement(str(account_lockout_threshold),
1513 ldb.FLAG_MOD_REPLACE, "lockoutThreshold")
1514 msgs.append("Account lockout threshold changed!")
1516 if reset_account_lockout_after is not None:
1517 if reset_account_lockout_after == "default":
1518 reset_account_lockout_after = 30
1520 reset_account_lockout_after = int(reset_account_lockout_after)
1522 if reset_account_lockout_after < 0 or reset_account_lockout_after > 99999:
1523 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1526 if reset_account_lockout_after == 0:
1527 reset_account_lockout_after_ticks = -0x8000000000000000
1529 reset_account_lockout_after_ticks = -int(reset_account_lockout_after * (60 * 1e7))
1531 m["lockOutObservationWindow"] = ldb.MessageElement(str(reset_account_lockout_after_ticks),
1532 ldb.FLAG_MOD_REPLACE, "lockOutObservationWindow")
1533 msgs.append("Duration to reset account lockout after changed!")
1535 if max_pwd_age and max_pwd_age > 0 and min_pwd_age >= max_pwd_age:
1536 raise CommandError("Maximum password age (%d) must be greater than minimum password age (%d)!" % (max_pwd_age, min_pwd_age))
1539 raise CommandError("You must specify at least one option to set. Try --help")
1541 msgs.append("All changes applied successfully!")
1542 self.message("\n".join(msgs))
1544 class cmd_domain_passwordsettings(SuperCommand):
1545 """Manage password policy settings."""
1548 subcommands["pso"] = cmd_domain_passwordsettings_pso()
1549 subcommands["show"] = cmd_domain_passwordsettings_show()
1550 subcommands["set"] = cmd_domain_passwordsettings_set()
1552 class cmd_domain_classicupgrade(Command):
1553 """Upgrade from Samba classic (NT4-like) database to Samba AD DC database.
1555 Specify either a directory with all Samba classic DC databases and state files (with --dbdir) or
1556 the testparm utility from your classic installation (with --testparm).
1559 synopsis = "%prog [options] <classic_smb_conf>"
1561 takes_optiongroups = {
1562 "sambaopts": options.SambaOptions,
1563 "versionopts": options.VersionOptions
1567 Option("--dbdir", type="string", metavar="DIR",
1568 help="Path to samba classic DC database directory"),
1569 Option("--testparm", type="string", metavar="PATH",
1570 help="Path to samba classic DC testparm utility from the previous installation. This allows the default paths of the previous installation to be followed"),
1571 Option("--targetdir", type="string", metavar="DIR",
1572 help="Path prefix where the new Samba 4.0 AD domain should be initialised"),
1573 Option("-q", "--quiet", help="Be quiet", action="store_true"),
1574 Option("-v", "--verbose", help="Be verbose", action="store_true"),
1575 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
1576 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
1577 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
1578 "BIND9_FLATFILE uses bind9 text database to store zone information, "
1579 "BIND9_DLZ uses samba4 AD to store zone information, "
1580 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
1581 default="SAMBA_INTERNAL")
1585 Option("--use-xattrs", type="choice", choices=["yes","no","auto"],
1586 metavar="[yes|no|auto]",
1587 help="Define if we should use the native fs capabilities or a tdb file for "
1588 "storing attributes likes ntacl when --use-ntvfs is set. "
1589 "auto tries to make an inteligent guess based on the user rights and system capabilities",
1592 if samba.is_ntvfs_fileserver_built():
1593 takes_options.extend(common_ntvfs_options)
1594 takes_options.extend(ntvfs_options)
1596 takes_args = ["smbconf"]
1598 def run(self, smbconf=None, targetdir=None, dbdir=None, testparm=None,
1599 quiet=False, verbose=False, use_xattrs="auto", sambaopts=None, versionopts=None,
1600 dns_backend=None, use_ntvfs=False):
1602 if not os.path.exists(smbconf):
1603 raise CommandError("File %s does not exist" % smbconf)
1605 if testparm and not os.path.exists(testparm):
1606 raise CommandError("Testparm utility %s does not exist" % testparm)
1608 if dbdir and not os.path.exists(dbdir):
1609 raise CommandError("Directory %s does not exist" % dbdir)
1611 if not dbdir and not testparm:
1612 raise CommandError("Please specify either dbdir or testparm")
1614 logger = self.get_logger()
1616 logger.setLevel(logging.DEBUG)
1618 logger.setLevel(logging.WARNING)
1620 logger.setLevel(logging.INFO)
1622 if dbdir and testparm:
1623 logger.warning("both dbdir and testparm specified, ignoring dbdir.")
1626 lp = sambaopts.get_loadparm()
1628 s3conf = s3param.get_context()
1631 s3conf.set("realm", sambaopts.realm)
1633 if targetdir is not None:
1634 if not os.path.isdir(targetdir):
1638 if use_xattrs == "yes":
1640 elif use_xattrs == "auto" and use_ntvfs == False:
1642 elif use_ntvfs == False:
1643 raise CommandError("--use-xattrs=no requires --use-ntvfs (not supported for production use). "
1644 "Please re-run with --use-xattrs omitted.")
1645 elif use_xattrs == "auto" and not s3conf.get("posix:eadb"):
1647 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
1649 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
1652 samba.ntacls.setntacl(lp, tmpfile.name,
1653 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
1656 # FIXME: Don't catch all exceptions here
1657 logger.info("You are not root or your system does not support xattr, using tdb backend for attributes. "
1658 "If you intend to use this provision in production, rerun the script as root on a system supporting xattrs.")
1662 # Set correct default values from dbdir or testparm
1665 paths["state directory"] = dbdir
1666 paths["private dir"] = dbdir
1667 paths["lock directory"] = dbdir
1668 paths["smb passwd file"] = dbdir + "/smbpasswd"
1670 paths["state directory"] = get_testparm_var(testparm, smbconf, "state directory")
1671 paths["private dir"] = get_testparm_var(testparm, smbconf, "private dir")
1672 paths["smb passwd file"] = get_testparm_var(testparm, smbconf, "smb passwd file")
1673 paths["lock directory"] = get_testparm_var(testparm, smbconf, "lock directory")
1674 # "testparm" from Samba 3 < 3.4.x is not aware of the parameter
1675 # "state directory", instead make use of "lock directory"
1676 if len(paths["state directory"]) == 0:
1677 paths["state directory"] = paths["lock directory"]
1680 s3conf.set(p, paths[p])
1682 # load smb.conf parameters
1683 logger.info("Reading smb.conf")
1684 s3conf.load(smbconf)
1685 samba3 = Samba3(smbconf, s3conf)
1687 logger.info("Provisioning")
1688 upgrade_from_samba3(samba3, logger, targetdir, session_info=system_session(),
1689 useeadb=eadb, dns_backend=dns_backend, use_ntvfs=use_ntvfs)
1692 class cmd_domain_samba3upgrade(cmd_domain_classicupgrade):
1693 __doc__ = cmd_domain_classicupgrade.__doc__
1695 # This command is present for backwards compatibility only,
1696 # and should not be shown.
1700 class LocalDCCredentialsOptions(options.CredentialsOptions):
1701 def __init__(self, parser):
1702 options.CredentialsOptions.__init__(self, parser, special_name="local-dc")
1704 class DomainTrustCommand(Command):
1705 """List domain trusts."""
1708 Command.__init__(self)
1709 self.local_lp = None
1711 self.local_server = None
1712 self.local_binding_string = None
1713 self.local_creds = None
1715 self.remote_server = None
1716 self.remote_binding_string = None
1717 self.remote_creds = None
1719 def _uint32(self, v):
1720 return ctypes.c_uint32(v).value
1722 def check_runtime_error(self, runtime, val):
1726 err32 = self._uint32(runtime.args[0])
1732 class LocalRuntimeError(CommandError):
1733 def __init__(exception_self, self, runtime, message):
1734 err32 = self._uint32(runtime.args[0])
1735 errstr = runtime.args[1]
1736 msg = "LOCAL_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1737 self.local_server, message, err32, errstr)
1738 CommandError.__init__(exception_self, msg)
1740 class RemoteRuntimeError(CommandError):
1741 def __init__(exception_self, self, runtime, message):
1742 err32 = self._uint32(runtime.args[0])
1743 errstr = runtime.args[1]
1744 msg = "REMOTE_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1745 self.remote_server, message, err32, errstr)
1746 CommandError.__init__(exception_self, msg)
1748 class LocalLdbError(CommandError):
1749 def __init__(exception_self, self, ldb_error, message):
1750 errval = ldb_error.args[0]
1751 errstr = ldb_error.args[1]
1752 msg = "LOCAL_DC[%s]: %s - ERROR(%d) - %s" % (
1753 self.local_server, message, errval, errstr)
1754 CommandError.__init__(exception_self, msg)
1756 def setup_local_server(self, sambaopts, localdcopts):
1757 if self.local_server is not None:
1758 return self.local_server
1760 lp = sambaopts.get_loadparm()
1762 local_server = localdcopts.ipaddress
1763 if local_server is None:
1764 server_role = lp.server_role()
1765 if server_role != "ROLE_ACTIVE_DIRECTORY_DC":
1766 raise CommandError("Invalid server_role %s" % (server_role))
1767 local_server = lp.get('netbios name')
1768 local_transport = "ncalrpc"
1769 local_binding_options = ""
1770 local_binding_options += ",auth_type=ncalrpc_as_system"
1771 local_ldap_url = None
1774 local_transport = "ncacn_np"
1775 local_binding_options = ""
1776 local_ldap_url = "ldap://%s" % local_server
1777 local_creds = localdcopts.get_credentials(lp)
1781 self.local_server = local_server
1782 self.local_binding_string = "%s:%s[%s]" % (local_transport, local_server, local_binding_options)
1783 self.local_ldap_url = local_ldap_url
1784 self.local_creds = local_creds
1785 return self.local_server
1787 def new_local_lsa_connection(self):
1788 return lsa.lsarpc(self.local_binding_string, self.local_lp, self.local_creds)
1790 def new_local_netlogon_connection(self):
1791 return netlogon.netlogon(self.local_binding_string, self.local_lp, self.local_creds)
1793 def new_local_ldap_connection(self):
1794 return SamDB(url=self.local_ldap_url,
1795 session_info=system_session(),
1796 credentials=self.local_creds,
1799 def setup_remote_server(self, credopts, domain,
1801 require_writable=True):
1804 assert require_writable
1806 if self.remote_server is not None:
1807 return self.remote_server
1809 self.remote_server = "__unknown__remote_server__.%s" % domain
1810 assert self.local_server is not None
1812 remote_creds = credopts.get_credentials(self.local_lp)
1813 remote_server = credopts.ipaddress
1814 remote_binding_options = ""
1816 # TODO: we should also support NT4 domains
1817 # we could use local_netlogon.netr_DsRGetDCNameEx2() with the remote domain name
1818 # and delegate NBT or CLDAP to the local netlogon server
1820 remote_net = Net(remote_creds, self.local_lp, server=remote_server)
1821 remote_flags = nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS
1822 if require_writable:
1823 remote_flags |= nbt.NBT_SERVER_WRITABLE
1825 remote_flags |= nbt.NBT_SERVER_PDC
1826 remote_info = remote_net.finddc(flags=remote_flags, domain=domain, address=remote_server)
1827 except NTSTATUSError as error:
1828 raise CommandError("Failed to find a writeable DC for domain '%s': %s" %
1831 raise CommandError("Failed to find a writeable DC for domain '%s'" % domain)
1833 nbt.NBT_SERVER_PDC: "PDC",
1834 nbt.NBT_SERVER_GC: "GC",
1835 nbt.NBT_SERVER_LDAP: "LDAP",
1836 nbt.NBT_SERVER_DS: "DS",
1837 nbt.NBT_SERVER_KDC: "KDC",
1838 nbt.NBT_SERVER_TIMESERV: "TIMESERV",
1839 nbt.NBT_SERVER_CLOSEST: "CLOSEST",
1840 nbt.NBT_SERVER_WRITABLE: "WRITABLE",
1841 nbt.NBT_SERVER_GOOD_TIMESERV: "GOOD_TIMESERV",
1842 nbt.NBT_SERVER_NDNC: "NDNC",
1843 nbt.NBT_SERVER_SELECT_SECRET_DOMAIN_6: "SELECT_SECRET_DOMAIN_6",
1844 nbt.NBT_SERVER_FULL_SECRET_DOMAIN_6: "FULL_SECRET_DOMAIN_6",
1845 nbt.NBT_SERVER_ADS_WEB_SERVICE: "ADS_WEB_SERVICE",
1846 nbt.NBT_SERVER_DS_8: "DS_8",
1847 nbt.NBT_SERVER_HAS_DNS_NAME: "HAS_DNS_NAME",
1848 nbt.NBT_SERVER_IS_DEFAULT_NC: "IS_DEFAULT_NC",
1849 nbt.NBT_SERVER_FOREST_ROOT: "FOREST_ROOT",
1851 server_type_string = self.generic_bitmap_to_string(flag_map,
1852 remote_info.server_type, names_only=True)
1853 self.outf.write("RemoteDC Netbios[%s] DNS[%s] ServerType[%s]\n" % (
1854 remote_info.pdc_name,
1855 remote_info.pdc_dns_name,
1856 server_type_string))
1858 self.remote_server = remote_info.pdc_dns_name
1859 self.remote_binding_string="ncacn_np:%s[%s]" % (self.remote_server, remote_binding_options)
1860 self.remote_creds = remote_creds
1861 return self.remote_server
1863 def new_remote_lsa_connection(self):
1864 return lsa.lsarpc(self.remote_binding_string, self.local_lp, self.remote_creds)
1866 def new_remote_netlogon_connection(self):
1867 return netlogon.netlogon(self.remote_binding_string, self.local_lp, self.remote_creds)
1869 def get_lsa_info(self, conn, policy_access):
1870 objectAttr = lsa.ObjectAttribute()
1871 objectAttr.sec_qos = lsa.QosInfo()
1873 policy = conn.OpenPolicy2(''.decode('utf-8'),
1874 objectAttr, policy_access)
1876 info = conn.QueryInfoPolicy2(policy, lsa.LSA_POLICY_INFO_DNS)
1878 return (policy, info)
1880 def get_netlogon_dc_unc(self, conn, server, domain):
1882 info = conn.netr_DsRGetDCNameEx2(server,
1883 None, 0, None, None, None,
1884 netlogon.DS_RETURN_DNS_NAME)
1886 except RuntimeError:
1887 return conn.netr_GetDcName(server, domain)
1889 def get_netlogon_dc_info(self, conn, server):
1890 info = conn.netr_DsRGetDCNameEx2(server,
1891 None, 0, None, None, None,
1892 netlogon.DS_RETURN_DNS_NAME)
1895 def netr_DomainTrust_to_name(self, t):
1896 if t.trust_type == lsa.LSA_TRUST_TYPE_DOWNLEVEL:
1897 return t.netbios_name
1901 def netr_DomainTrust_to_type(self, a, t):
1903 primary_parent = None
1905 if _t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
1907 if not _t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1908 primary_parent = a[_t.parent_index]
1911 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1912 if t is primary_parent:
1915 if t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1918 parent = a[t.parent_index]
1919 if parent is primary:
1924 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1929 def netr_DomainTrust_to_transitive(self, t):
1930 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1933 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE:
1936 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1941 def netr_DomainTrust_to_direction(self, t):
1942 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND and \
1943 t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1946 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND:
1949 if t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1954 def generic_enum_to_string(self, e_dict, v, names_only=False):
1958 v32 = self._uint32(v)
1959 w = "__unknown__%08X__" % v32
1961 r = "0x%x (%s)" % (v, w)
1964 def generic_bitmap_to_string(self, b_dict, v, names_only=False):
1969 for b in sorted(b_dict.keys()):
1976 c32 = self._uint32(c)
1977 s += ["__unknown_%08X__" % c32]
1982 r = "0x%x (%s)" % (v, w)
1985 def trustType_string(self, v):
1987 lsa.LSA_TRUST_TYPE_DOWNLEVEL : "DOWNLEVEL",
1988 lsa.LSA_TRUST_TYPE_UPLEVEL : "UPLEVEL",
1989 lsa.LSA_TRUST_TYPE_MIT : "MIT",
1990 lsa.LSA_TRUST_TYPE_DCE : "DCE",
1992 return self.generic_enum_to_string(types, v)
1994 def trustDirection_string(self, v):
1996 lsa.LSA_TRUST_DIRECTION_INBOUND |
1997 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "BOTH",
1998 lsa.LSA_TRUST_DIRECTION_INBOUND : "INBOUND",
1999 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "OUTBOUND",
2001 return self.generic_enum_to_string(directions, v)
2003 def trustAttributes_string(self, v):
2005 lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE : "NON_TRANSITIVE",
2006 lsa.LSA_TRUST_ATTRIBUTE_UPLEVEL_ONLY : "UPLEVEL_ONLY",
2007 lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN : "QUARANTINED_DOMAIN",
2008 lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE : "FOREST_TRANSITIVE",
2009 lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION : "CROSS_ORGANIZATION",
2010 lsa.LSA_TRUST_ATTRIBUTE_WITHIN_FOREST : "WITHIN_FOREST",
2011 lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL : "TREAT_AS_EXTERNAL",
2012 lsa.LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION : "USES_RC4_ENCRYPTION",
2014 return self.generic_bitmap_to_string(attributes, v)
2016 def kerb_EncTypes_string(self, v):
2018 security.KERB_ENCTYPE_DES_CBC_CRC : "DES_CBC_CRC",
2019 security.KERB_ENCTYPE_DES_CBC_MD5 : "DES_CBC_MD5",
2020 security.KERB_ENCTYPE_RC4_HMAC_MD5 : "RC4_HMAC_MD5",
2021 security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96 : "AES128_CTS_HMAC_SHA1_96",
2022 security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96 : "AES256_CTS_HMAC_SHA1_96",
2023 security.KERB_ENCTYPE_FAST_SUPPORTED : "FAST_SUPPORTED",
2024 security.KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED : "COMPOUND_IDENTITY_SUPPORTED",
2025 security.KERB_ENCTYPE_CLAIMS_SUPPORTED : "CLAIMS_SUPPORTED",
2026 security.KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED : "RESOURCE_SID_COMPRESSION_DISABLED",
2028 return self.generic_bitmap_to_string(enctypes, v)
2030 def entry_tln_status(self, e_flags, ):
2032 return "Status[Enabled]"
2035 lsa.LSA_TLN_DISABLED_NEW : "Disabled-New",
2036 lsa.LSA_TLN_DISABLED_ADMIN : "Disabled",
2037 lsa.LSA_TLN_DISABLED_CONFLICT : "Disabled-Conflicting",
2039 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
2041 def entry_dom_status(self, e_flags):
2043 return "Status[Enabled]"
2046 lsa.LSA_SID_DISABLED_ADMIN : "Disabled-SID",
2047 lsa.LSA_SID_DISABLED_CONFLICT : "Disabled-SID-Conflicting",
2048 lsa.LSA_NB_DISABLED_ADMIN : "Disabled-NB",
2049 lsa.LSA_NB_DISABLED_CONFLICT : "Disabled-NB-Conflicting",
2051 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
2053 def write_forest_trust_info(self, fti, tln=None, collisions=None):
2055 tln_string = " TDO[%s]" % tln
2059 self.outf.write("Namespaces[%d]%s:\n" % (
2060 len(fti.entries), tln_string))
2062 for i, e in enumerate(fti.entries):
2065 collision_string = ""
2067 if collisions is not None:
2068 for c in collisions.entries:
2072 collision_string = " Collision[%s]" % (c.name.string)
2074 d = e.forest_trust_data
2075 if e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
2076 self.outf.write("TLN: %-32s DNS[*.%s]%s\n" % (
2077 self.entry_tln_status(flags),
2078 d.string, collision_string))
2079 elif e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
2080 self.outf.write("TLN_EX: %-29s DNS[*.%s]\n" % (
2082 elif e.type == lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
2083 self.outf.write("DOM: %-32s DNS[%s] Netbios[%s] SID[%s]%s\n" % (
2084 self.entry_dom_status(flags),
2085 d.dns_domain_name.string,
2086 d.netbios_domain_name.string,
2087 d.domain_sid, collision_string))
2090 class cmd_domain_trust_list(DomainTrustCommand):
2091 """List domain trusts."""
2093 synopsis = "%prog [options]"
2095 takes_optiongroups = {
2096 "sambaopts": options.SambaOptions,
2097 "versionopts": options.VersionOptions,
2098 "localdcopts": LocalDCCredentialsOptions,
2104 def run(self, sambaopts=None, versionopts=None, localdcopts=None):
2106 local_server = self.setup_local_server(sambaopts, localdcopts)
2108 local_netlogon = self.new_local_netlogon_connection()
2109 except RuntimeError as error:
2110 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2113 local_netlogon_trusts = local_netlogon.netr_DsrEnumerateDomainTrusts(local_server,
2114 netlogon.NETR_TRUST_FLAG_IN_FOREST |
2115 netlogon.NETR_TRUST_FLAG_OUTBOUND |
2116 netlogon.NETR_TRUST_FLAG_INBOUND)
2117 except RuntimeError as error:
2118 if self.check_runtime_error(error, werror.WERR_RPC_S_PROCNUM_OUT_OF_RANGE):
2119 # TODO: we could implement a fallback to lsa.EnumTrustDom()
2120 raise CommandError("LOCAL_DC[%s]: netr_DsrEnumerateDomainTrusts not supported." % (
2122 raise self.LocalRuntimeError(self, error, "netr_DsrEnumerateDomainTrusts failed")
2124 a = local_netlogon_trusts.array
2126 if t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
2128 self.outf.write("%-14s %-15s %-19s %s\n" % (
2129 "Type[%s]" % self.netr_DomainTrust_to_type(a, t),
2130 "Transitive[%s]" % self.netr_DomainTrust_to_transitive(t),
2131 "Direction[%s]" % self.netr_DomainTrust_to_direction(t),
2132 "Name[%s]" % self.netr_DomainTrust_to_name(t)))
2135 class cmd_domain_trust_show(DomainTrustCommand):
2136 """Show trusted domain details."""
2138 synopsis = "%prog NAME [options]"
2140 takes_optiongroups = {
2141 "sambaopts": options.SambaOptions,
2142 "versionopts": options.VersionOptions,
2143 "localdcopts": LocalDCCredentialsOptions,
2149 takes_args = ["domain"]
2151 def run(self, domain, sambaopts=None, versionopts=None, localdcopts=None):
2153 local_server = self.setup_local_server(sambaopts, localdcopts)
2155 local_lsa = self.new_local_lsa_connection()
2156 except RuntimeError as error:
2157 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2160 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2161 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2162 except RuntimeError as error:
2163 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2165 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2166 local_lsa_info.name.string,
2167 local_lsa_info.dns_domain.string,
2168 local_lsa_info.sid))
2170 lsaString = lsa.String()
2171 lsaString.string = domain
2173 local_tdo_full = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2174 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2175 local_tdo_info = local_tdo_full.info_ex
2176 local_tdo_posix = local_tdo_full.posix_offset
2177 except NTSTATUSError as error:
2178 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2179 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2181 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(FULL_INFO) failed")
2184 local_tdo_enctypes = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2185 lsaString, lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES)
2186 except NTSTATUSError as error:
2187 if self.check_runtime_error(error, ntstatus.NT_STATUS_INVALID_PARAMETER):
2189 if self.check_runtime_error(error, ntstatus.NT_STATUS_INVALID_INFO_CLASS):
2192 if error is not None:
2193 raise self.LocalRuntimeError(self, error,
2194 "QueryTrustedDomainInfoByName(SUPPORTED_ENCRYPTION_TYPES) failed")
2196 local_tdo_enctypes = lsa.TrustDomainInfoSupportedEncTypes()
2197 local_tdo_enctypes.enc_types = 0
2200 local_tdo_forest = None
2201 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2202 local_tdo_forest = local_lsa.lsaRQueryForestTrustInformation(local_policy,
2203 lsaString, lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
2204 except RuntimeError as error:
2205 if self.check_runtime_error(error, ntstatus.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
2207 if self.check_runtime_error(error, ntstatus.NT_STATUS_NOT_FOUND):
2209 if error is not None:
2210 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation failed")
2212 local_tdo_forest = lsa.ForestTrustInformation()
2213 local_tdo_forest.count = 0
2214 local_tdo_forest.entries = []
2216 self.outf.write("TrustedDomain:\n\n");
2217 self.outf.write("NetbiosName: %s\n" % local_tdo_info.netbios_name.string)
2218 if local_tdo_info.netbios_name.string != local_tdo_info.domain_name.string:
2219 self.outf.write("DnsName: %s\n" % local_tdo_info.domain_name.string)
2220 self.outf.write("SID: %s\n" % local_tdo_info.sid)
2221 self.outf.write("Type: %s\n" % self.trustType_string(local_tdo_info.trust_type))
2222 self.outf.write("Direction: %s\n" % self.trustDirection_string(local_tdo_info.trust_direction))
2223 self.outf.write("Attributes: %s\n" % self.trustAttributes_string(local_tdo_info.trust_attributes))
2224 posix_offset_u32 = ctypes.c_uint32(local_tdo_posix.posix_offset).value
2225 posix_offset_i32 = ctypes.c_int32(local_tdo_posix.posix_offset).value
2226 self.outf.write("PosixOffset: 0x%08X (%d)\n" % (posix_offset_u32, posix_offset_i32))
2227 self.outf.write("kerb_EncTypes: %s\n" % self.kerb_EncTypes_string(local_tdo_enctypes.enc_types))
2229 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2230 self.write_forest_trust_info(local_tdo_forest,
2231 tln=local_tdo_info.domain_name.string)
2235 class cmd_domain_trust_create(DomainTrustCommand):
2236 """Create a domain or forest trust."""
2238 synopsis = "%prog DOMAIN [options]"
2240 takes_optiongroups = {
2241 "sambaopts": options.SambaOptions,
2242 "versionopts": options.VersionOptions,
2243 "credopts": options.CredentialsOptions,
2244 "localdcopts": LocalDCCredentialsOptions,
2248 Option("--type", type="choice", metavar="TYPE",
2249 choices=["external", "forest"],
2250 help="The type of the trust: 'external' or 'forest'.",
2252 default="external"),
2253 Option("--direction", type="choice", metavar="DIRECTION",
2254 choices=["incoming", "outgoing", "both"],
2255 help="The trust direction: 'incoming', 'outgoing' or 'both'.",
2256 dest='trust_direction',
2258 Option("--create-location", type="choice", metavar="LOCATION",
2259 choices=["local", "both"],
2260 help="Where to create the trusted domain object: 'local' or 'both'.",
2261 dest='create_location',
2263 Option("--cross-organisation", action="store_true",
2264 help="The related domains does not belong to the same organisation.",
2265 dest='cross_organisation',
2267 Option("--quarantined", type="choice", metavar="yes|no",
2268 choices=["yes", "no", None],
2269 help="Special SID filtering rules are applied to the trust. "
2270 "With --type=external the default is yes. "
2271 "With --type=forest the default is no.",
2272 dest='quarantined_arg',
2274 Option("--not-transitive", action="store_true",
2275 help="The forest trust is not transitive.",
2276 dest='not_transitive',
2278 Option("--treat-as-external", action="store_true",
2279 help="The treat the forest trust as external.",
2280 dest='treat_as_external',
2282 Option("--no-aes-keys", action="store_false",
2283 help="The trust uses aes kerberos keys.",
2284 dest='use_aes_keys',
2286 Option("--skip-validation", action="store_false",
2287 help="Skip validation of the trust.",
2292 takes_args = ["domain"]
2294 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2295 trust_type=None, trust_direction=None, create_location=None,
2296 cross_organisation=False, quarantined_arg=None,
2297 not_transitive=False, treat_as_external=False,
2298 use_aes_keys=False, validate=True):
2300 lsaString = lsa.String()
2303 if quarantined_arg is None:
2304 if trust_type == 'external':
2306 elif quarantined_arg == 'yes':
2309 if trust_type != 'forest':
2311 raise CommandError("--not-transitive requires --type=forest")
2312 if treat_as_external:
2313 raise CommandError("--treat-as-external requires --type=forest")
2317 enc_types = lsa.TrustDomainInfoSupportedEncTypes()
2318 enc_types.enc_types = security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96
2319 enc_types.enc_types |= security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96
2321 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2322 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2323 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2325 local_trust_info = lsa.TrustDomainInfoInfoEx()
2326 local_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2327 local_trust_info.trust_direction = 0
2328 if trust_direction == "both":
2329 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2330 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2331 elif trust_direction == "incoming":
2332 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2333 elif trust_direction == "outgoing":
2334 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2335 local_trust_info.trust_attributes = 0
2336 if cross_organisation:
2337 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2339 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2340 if trust_type == "forest":
2341 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2343 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2344 if treat_as_external:
2345 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2347 def get_password(name):
2350 if password is not None and password is not '':
2352 password = getpass("New %s Password: " % name)
2353 passwordverify = getpass("Retype %s Password: " % name)
2354 if not password == passwordverify:
2356 self.outf.write("Sorry, passwords do not match.\n")
2358 incoming_secret = None
2359 outgoing_secret = None
2360 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2361 if create_location == "local":
2362 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2363 incoming_password = get_password("Incoming Trust")
2364 incoming_secret = string_to_byte_array(incoming_password.encode('utf-16-le'))
2365 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2366 outgoing_password = get_password("Outgoing Trust")
2367 outgoing_secret = string_to_byte_array(outgoing_password.encode('utf-16-le'))
2369 remote_trust_info = None
2371 # We use 240 random bytes.
2372 # Windows uses 28 or 240 random bytes. I guess it's
2373 # based on the trust type external vs. forest.
2375 # The initial trust password can be up to 512 bytes
2376 # while the versioned passwords used for periodic updates
2377 # can only be up to 498 bytes, as netr_ServerPasswordSet2()
2378 # needs to pass the NL_PASSWORD_VERSION structure within the
2379 # 512 bytes and a 2 bytes confounder is required.
2381 def random_trust_secret(length):
2382 pw = samba.generate_random_machine_password(length//2, length//2)
2383 return string_to_byte_array(pw.encode('utf-16-le'))
2385 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2386 incoming_secret = random_trust_secret(240)
2387 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2388 outgoing_secret = random_trust_secret(240)
2390 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2391 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2393 remote_trust_info = lsa.TrustDomainInfoInfoEx()
2394 remote_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2395 remote_trust_info.trust_direction = 0
2396 if trust_direction == "both":
2397 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2398 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2399 elif trust_direction == "incoming":
2400 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2401 elif trust_direction == "outgoing":
2402 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2403 remote_trust_info.trust_attributes = 0
2404 if cross_organisation:
2405 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2407 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2408 if trust_type == "forest":
2409 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2411 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2412 if treat_as_external:
2413 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2415 local_server = self.setup_local_server(sambaopts, localdcopts)
2417 local_lsa = self.new_local_lsa_connection()
2418 except RuntimeError as error:
2419 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2422 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2423 except RuntimeError as error:
2424 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2426 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2427 local_lsa_info.name.string,
2428 local_lsa_info.dns_domain.string,
2429 local_lsa_info.sid))
2432 remote_server = self.setup_remote_server(credopts, domain)
2433 except RuntimeError as error:
2434 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2437 remote_lsa = self.new_remote_lsa_connection()
2438 except RuntimeError as error:
2439 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2442 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2443 except RuntimeError as error:
2444 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2446 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2447 remote_lsa_info.name.string,
2448 remote_lsa_info.dns_domain.string,
2449 remote_lsa_info.sid))
2451 local_trust_info.domain_name.string = remote_lsa_info.dns_domain.string
2452 local_trust_info.netbios_name.string = remote_lsa_info.name.string
2453 local_trust_info.sid = remote_lsa_info.sid
2455 if remote_trust_info:
2456 remote_trust_info.domain_name.string = local_lsa_info.dns_domain.string
2457 remote_trust_info.netbios_name.string = local_lsa_info.name.string
2458 remote_trust_info.sid = local_lsa_info.sid
2461 lsaString.string = local_trust_info.domain_name.string
2462 local_old_netbios = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2463 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2464 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2465 except NTSTATUSError as error:
2466 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2467 raise self.LocalRuntimeError(self, error,
2468 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2472 lsaString.string = local_trust_info.netbios_name.string
2473 local_old_dns = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2474 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2475 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2476 except NTSTATUSError as error:
2477 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2478 raise self.LocalRuntimeError(self, error,
2479 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2482 if remote_trust_info:
2484 lsaString.string = remote_trust_info.domain_name.string
2485 remote_old_netbios = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2486 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2487 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2488 except NTSTATUSError as error:
2489 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2490 raise self.RemoteRuntimeError(self, error,
2491 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2495 lsaString.string = remote_trust_info.netbios_name.string
2496 remote_old_dns = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2497 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2498 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2499 except NTSTATUSError as error:
2500 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2501 raise self.RemoteRuntimeError(self, error,
2502 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2506 local_netlogon = self.new_local_netlogon_connection()
2507 except RuntimeError as error:
2508 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2511 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
2512 except RuntimeError as error:
2513 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
2515 if remote_trust_info:
2517 remote_netlogon = self.new_remote_netlogon_connection()
2518 except RuntimeError as error:
2519 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2522 remote_netlogon_dc_unc = self.get_netlogon_dc_unc(remote_netlogon,
2523 remote_server, domain)
2524 except RuntimeError as error:
2525 raise self.RemoteRuntimeError(self, error, "failed to get netlogon dc info")
2527 def generate_AuthInOutBlob(secret, update_time):
2529 blob = drsblobs.trustAuthInOutBlob()
2534 clear = drsblobs.AuthInfoClear()
2535 clear.size = len(secret)
2536 clear.password = secret
2538 info = drsblobs.AuthenticationInformation()
2539 info.LastUpdateTime = samba.unix2nttime(update_time)
2540 info.AuthType = lsa.TRUST_AUTH_TYPE_CLEAR
2541 info.AuthInfo = clear
2543 array = drsblobs.AuthenticationInformationArray()
2545 array.array = [info]
2547 blob = drsblobs.trustAuthInOutBlob()
2549 blob.current = array
2553 def generate_AuthInfoInternal(session_key, incoming=None, outgoing=None):
2554 confounder = [0] * 512
2555 for i in range(len(confounder)):
2556 confounder[i] = random.randint(0, 255)
2558 trustpass = drsblobs.trustDomainPasswords()
2560 trustpass.confounder = confounder
2561 trustpass.outgoing = outgoing
2562 trustpass.incoming = incoming
2564 trustpass_blob = ndr_pack(trustpass)
2566 encrypted_trustpass = arcfour_encrypt(session_key, trustpass_blob)
2568 auth_blob = lsa.DATA_BUF2()
2569 auth_blob.size = len(encrypted_trustpass)
2570 auth_blob.data = string_to_byte_array(encrypted_trustpass)
2572 auth_info = lsa.TrustDomainInfoAuthInfoInternal()
2573 auth_info.auth_blob = auth_blob
2577 update_time = samba.current_unix_time()
2578 incoming_blob = generate_AuthInOutBlob(incoming_secret, update_time)
2579 outgoing_blob = generate_AuthInOutBlob(outgoing_secret, update_time)
2581 local_tdo_handle = None
2582 remote_tdo_handle = None
2584 local_auth_info = generate_AuthInfoInternal(local_lsa.session_key,
2585 incoming=incoming_blob,
2586 outgoing=outgoing_blob)
2587 if remote_trust_info:
2588 remote_auth_info = generate_AuthInfoInternal(remote_lsa.session_key,
2589 incoming=outgoing_blob,
2590 outgoing=incoming_blob)
2593 if remote_trust_info:
2594 self.outf.write("Creating remote TDO.\n")
2595 current_request = { "location": "remote", "name": "CreateTrustedDomainEx2"}
2596 remote_tdo_handle = remote_lsa.CreateTrustedDomainEx2(remote_policy,
2599 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2600 self.outf.write("Remote TDO created.\n")
2602 self.outf.write("Setting supported encryption types on remote TDO.\n")
2603 current_request = { "location": "remote", "name": "SetInformationTrustedDomain"}
2604 remote_lsa.SetInformationTrustedDomain(remote_tdo_handle,
2605 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2608 self.outf.write("Creating local TDO.\n")
2609 current_request = { "location": "local", "name": "CreateTrustedDomainEx2"}
2610 local_tdo_handle = local_lsa.CreateTrustedDomainEx2(local_policy,
2613 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2614 self.outf.write("Local TDO created\n")
2616 self.outf.write("Setting supported encryption types on local TDO.\n")
2617 current_request = { "location": "local", "name": "SetInformationTrustedDomain"}
2618 local_lsa.SetInformationTrustedDomain(local_tdo_handle,
2619 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2621 except RuntimeError as error:
2622 self.outf.write("Error: %s failed %sly - cleaning up\n" % (
2623 current_request['name'], current_request['location']))
2624 if remote_tdo_handle:
2625 self.outf.write("Deleting remote TDO.\n")
2626 remote_lsa.DeleteObject(remote_tdo_handle)
2627 remote_tdo_handle = None
2628 if local_tdo_handle:
2629 self.outf.write("Deleting local TDO.\n")
2630 local_lsa.DeleteObject(local_tdo_handle)
2631 local_tdo_handle = None
2632 if current_request['location'] is "remote":
2633 raise self.RemoteRuntimeError(self, error, "%s" % (
2634 current_request['name']))
2635 raise self.LocalRuntimeError(self, error, "%s" % (
2636 current_request['name']))
2639 if local_trust_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2640 self.outf.write("Setup local forest trust information...\n")
2642 # get all information about the remote trust
2643 # this triggers netr_GetForestTrustInformation to the remote domain
2644 # and lsaRSetForestTrustInformation() locally, but new top level
2645 # names are disabled by default.
2646 local_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
2647 remote_lsa_info.dns_domain.string,
2648 netlogon.DS_GFTI_UPDATE_TDO)
2649 except RuntimeError as error:
2650 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2653 # here we try to enable all top level names
2654 local_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
2655 remote_lsa_info.dns_domain,
2656 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2659 except RuntimeError as error:
2660 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2662 self.write_forest_trust_info(local_forest_info,
2663 tln=remote_lsa_info.dns_domain.string,
2664 collisions=local_forest_collision)
2666 if remote_trust_info:
2667 self.outf.write("Setup remote forest trust information...\n")
2669 # get all information about the local trust (from the perspective of the remote domain)
2670 # this triggers netr_GetForestTrustInformation to our domain.
2671 # and lsaRSetForestTrustInformation() remotely, but new top level
2672 # names are disabled by default.
2673 remote_forest_info = remote_netlogon.netr_DsRGetForestTrustInformation(remote_netlogon_dc_unc,
2674 local_lsa_info.dns_domain.string,
2675 netlogon.DS_GFTI_UPDATE_TDO)
2676 except RuntimeError as error:
2677 raise self.RemoteRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2680 # here we try to enable all top level names
2681 remote_forest_collision = remote_lsa.lsaRSetForestTrustInformation(remote_policy,
2682 local_lsa_info.dns_domain,
2683 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2686 except RuntimeError as error:
2687 raise self.RemoteRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2689 self.write_forest_trust_info(remote_forest_info,
2690 tln=local_lsa_info.dns_domain.string,
2691 collisions=remote_forest_collision)
2693 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2694 self.outf.write("Validating outgoing trust...\n")
2696 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_netlogon_info.dc_unc,
2697 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2699 remote_lsa_info.dns_domain.string)
2700 except RuntimeError as error:
2701 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2703 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2704 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2706 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2707 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2708 local_trust_verify.trusted_dc_name,
2709 local_trust_verify.tc_connection_status[1],
2710 local_trust_verify.pdc_connection_status[1])
2712 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2713 local_trust_verify.trusted_dc_name,
2714 local_trust_verify.tc_connection_status[1],
2715 local_trust_verify.pdc_connection_status[1])
2717 if local_trust_status != werror.WERR_SUCCESS or local_conn_status != werror.WERR_SUCCESS:
2718 raise CommandError(local_validation)
2720 self.outf.write("OK: %s\n" % local_validation)
2722 if remote_trust_info:
2723 if remote_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2724 self.outf.write("Validating incoming trust...\n")
2726 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_netlogon_dc_unc,
2727 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2729 local_lsa_info.dns_domain.string)
2730 except RuntimeError as error:
2731 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2733 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2734 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2736 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2737 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2738 remote_trust_verify.trusted_dc_name,
2739 remote_trust_verify.tc_connection_status[1],
2740 remote_trust_verify.pdc_connection_status[1])
2742 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2743 remote_trust_verify.trusted_dc_name,
2744 remote_trust_verify.tc_connection_status[1],
2745 remote_trust_verify.pdc_connection_status[1])
2747 if remote_trust_status != werror.WERR_SUCCESS or remote_conn_status != werror.WERR_SUCCESS:
2748 raise CommandError(remote_validation)
2750 self.outf.write("OK: %s\n" % remote_validation)
2752 if remote_tdo_handle is not None:
2754 remote_lsa.Close(remote_tdo_handle)
2755 except RuntimeError as error:
2757 remote_tdo_handle = None
2758 if local_tdo_handle is not None:
2760 local_lsa.Close(local_tdo_handle)
2761 except RuntimeError as error:
2763 local_tdo_handle = None
2765 self.outf.write("Success.\n")
2768 class cmd_domain_trust_delete(DomainTrustCommand):
2769 """Delete a domain trust."""
2771 synopsis = "%prog DOMAIN [options]"
2773 takes_optiongroups = {
2774 "sambaopts": options.SambaOptions,
2775 "versionopts": options.VersionOptions,
2776 "credopts": options.CredentialsOptions,
2777 "localdcopts": LocalDCCredentialsOptions,
2781 Option("--delete-location", type="choice", metavar="LOCATION",
2782 choices=["local", "both"],
2783 help="Where to delete the trusted domain object: 'local' or 'both'.",
2784 dest='delete_location',
2788 takes_args = ["domain"]
2790 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2791 delete_location=None):
2793 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2794 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2795 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2797 if delete_location == "local":
2798 remote_policy_access = None
2800 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2801 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2802 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2804 local_server = self.setup_local_server(sambaopts, localdcopts)
2806 local_lsa = self.new_local_lsa_connection()
2807 except RuntimeError as error:
2808 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2811 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2812 except RuntimeError as error:
2813 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2815 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2816 local_lsa_info.name.string,
2817 local_lsa_info.dns_domain.string,
2818 local_lsa_info.sid))
2820 local_tdo_info = None
2821 local_tdo_handle = None
2822 remote_tdo_info = None
2823 remote_tdo_handle = None
2825 lsaString = lsa.String()
2827 lsaString.string = domain
2828 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2829 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2830 except NTSTATUSError as error:
2831 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2832 raise CommandError("Failed to find trust for domain '%s'" % domain)
2833 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2836 if remote_policy_access is not None:
2838 remote_server = self.setup_remote_server(credopts, domain)
2839 except RuntimeError as error:
2840 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2843 remote_lsa = self.new_remote_lsa_connection()
2844 except RuntimeError as error:
2845 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2848 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2849 except RuntimeError as error:
2850 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2852 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2853 remote_lsa_info.name.string,
2854 remote_lsa_info.dns_domain.string,
2855 remote_lsa_info.sid))
2857 if remote_lsa_info.sid != local_tdo_info.sid or \
2858 remote_lsa_info.name.string != local_tdo_info.netbios_name.string or \
2859 remote_lsa_info.dns_domain.string != local_tdo_info.domain_name.string:
2860 raise CommandError("LocalTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2861 local_tdo_info.netbios_name.string,
2862 local_tdo_info.domain_name.string,
2863 local_tdo_info.sid))
2866 lsaString.string = local_lsa_info.dns_domain.string
2867 remote_tdo_info = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2868 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2869 except NTSTATUSError as error:
2870 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2871 raise self.RemoteRuntimeError(self, error, "QueryTrustedDomainInfoByName(%s)" % (
2875 if remote_tdo_info is not None:
2876 if local_lsa_info.sid != remote_tdo_info.sid or \
2877 local_lsa_info.name.string != remote_tdo_info.netbios_name.string or \
2878 local_lsa_info.dns_domain.string != remote_tdo_info.domain_name.string:
2879 raise CommandError("RemoteTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2880 remote_tdo_info.netbios_name.string,
2881 remote_tdo_info.domain_name.string,
2882 remote_tdo_info.sid))
2884 if local_tdo_info is not None:
2886 lsaString.string = local_tdo_info.domain_name.string
2887 local_tdo_handle = local_lsa.OpenTrustedDomainByName(local_policy,
2889 security.SEC_STD_DELETE)
2890 except RuntimeError as error:
2891 raise self.LocalRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2894 local_lsa.DeleteObject(local_tdo_handle)
2895 local_tdo_handle = None
2897 if remote_tdo_info is not None:
2899 lsaString.string = remote_tdo_info.domain_name.string
2900 remote_tdo_handle = remote_lsa.OpenTrustedDomainByName(remote_policy,
2902 security.SEC_STD_DELETE)
2903 except RuntimeError as error:
2904 raise self.RemoteRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2907 if remote_tdo_handle is not None:
2909 remote_lsa.DeleteObject(remote_tdo_handle)
2910 remote_tdo_handle = None
2911 self.outf.write("RemoteTDO deleted.\n")
2912 except RuntimeError as error:
2913 self.outf.write("%s\n" % self.RemoteRuntimeError(self, error, "DeleteObject() failed"))
2915 if local_tdo_handle is not None:
2917 local_lsa.DeleteObject(local_tdo_handle)
2918 local_tdo_handle = None
2919 self.outf.write("LocalTDO deleted.\n")
2920 except RuntimeError as error:
2921 self.outf.write("%s\n" % self.LocalRuntimeError(self, error, "DeleteObject() failed"))
2925 class cmd_domain_trust_validate(DomainTrustCommand):
2926 """Validate a domain trust."""
2928 synopsis = "%prog DOMAIN [options]"
2930 takes_optiongroups = {
2931 "sambaopts": options.SambaOptions,
2932 "versionopts": options.VersionOptions,
2933 "credopts": options.CredentialsOptions,
2934 "localdcopts": LocalDCCredentialsOptions,
2938 Option("--validate-location", type="choice", metavar="LOCATION",
2939 choices=["local", "both"],
2940 help="Where to validate the trusted domain object: 'local' or 'both'.",
2941 dest='validate_location',
2945 takes_args = ["domain"]
2947 def run(self, domain, sambaopts=None, versionopts=None, credopts=None, localdcopts=None,
2948 validate_location=None):
2950 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2952 local_server = self.setup_local_server(sambaopts, localdcopts)
2954 local_lsa = self.new_local_lsa_connection()
2955 except RuntimeError as error:
2956 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2959 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2960 except RuntimeError as error:
2961 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2963 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2964 local_lsa_info.name.string,
2965 local_lsa_info.dns_domain.string,
2966 local_lsa_info.sid))
2969 lsaString = lsa.String()
2970 lsaString.string = domain
2971 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2972 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2973 except NTSTATUSError as error:
2974 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2975 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2977 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
2979 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
2980 local_tdo_info.netbios_name.string,
2981 local_tdo_info.domain_name.string,
2982 local_tdo_info.sid))
2985 local_netlogon = self.new_local_netlogon_connection()
2986 except RuntimeError as error:
2987 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2990 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_server,
2991 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2993 local_tdo_info.domain_name.string)
2994 except RuntimeError as error:
2995 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2997 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2998 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
3000 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
3001 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
3002 local_trust_verify.trusted_dc_name,
3003 local_trust_verify.tc_connection_status[1],
3004 local_trust_verify.pdc_connection_status[1])
3006 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
3007 local_trust_verify.trusted_dc_name,
3008 local_trust_verify.tc_connection_status[1],
3009 local_trust_verify.pdc_connection_status[1])
3011 if local_trust_status != werror.WERR_SUCCESS or local_conn_status != werror.WERR_SUCCESS:
3012 raise CommandError(local_validation)
3014 self.outf.write("OK: %s\n" % local_validation)
3017 server = local_trust_verify.trusted_dc_name.replace('\\', '')
3018 domain_and_server = "%s\\%s" % (local_tdo_info.domain_name.string, server)
3019 local_trust_rediscover = local_netlogon.netr_LogonControl2Ex(local_server,
3020 netlogon.NETLOGON_CONTROL_REDISCOVER,
3023 except RuntimeError as error:
3024 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
3026 local_conn_status = self._uint32(local_trust_rediscover.tc_connection_status[0])
3027 local_rediscover = "LocalRediscover: DC[%s] CONNECTION[%s]" % (
3028 local_trust_rediscover.trusted_dc_name,
3029 local_trust_rediscover.tc_connection_status[1])
3031 if local_conn_status != werror.WERR_SUCCESS:
3032 raise CommandError(local_rediscover)
3034 self.outf.write("OK: %s\n" % local_rediscover)
3036 if validate_location != "local":
3038 remote_server = self.setup_remote_server(credopts, domain, require_pdc=False)
3039 except RuntimeError as error:
3040 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
3043 remote_netlogon = self.new_remote_netlogon_connection()
3044 except RuntimeError as error:
3045 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
3048 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_server,
3049 netlogon.NETLOGON_CONTROL_TC_VERIFY,
3051 local_lsa_info.dns_domain.string)
3052 except RuntimeError as error:
3053 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
3055 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
3056 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
3058 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
3059 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
3060 remote_trust_verify.trusted_dc_name,
3061 remote_trust_verify.tc_connection_status[1],
3062 remote_trust_verify.pdc_connection_status[1])
3064 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
3065 remote_trust_verify.trusted_dc_name,
3066 remote_trust_verify.tc_connection_status[1],
3067 remote_trust_verify.pdc_connection_status[1])
3069 if remote_trust_status != werror.WERR_SUCCESS or remote_conn_status != werror.WERR_SUCCESS:
3070 raise CommandError(remote_validation)
3072 self.outf.write("OK: %s\n" % remote_validation)
3075 server = remote_trust_verify.trusted_dc_name.replace('\\', '')
3076 domain_and_server = "%s\\%s" % (local_lsa_info.dns_domain.string, server)
3077 remote_trust_rediscover = remote_netlogon.netr_LogonControl2Ex(remote_server,
3078 netlogon.NETLOGON_CONTROL_REDISCOVER,
3081 except RuntimeError as error:
3082 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
3084 remote_conn_status = self._uint32(remote_trust_rediscover.tc_connection_status[0])
3086 remote_rediscover = "RemoteRediscover: DC[%s] CONNECTION[%s]" % (
3087 remote_trust_rediscover.trusted_dc_name,
3088 remote_trust_rediscover.tc_connection_status[1])
3090 if remote_conn_status != werror.WERR_SUCCESS:
3091 raise CommandError(remote_rediscover)
3093 self.outf.write("OK: %s\n" % remote_rediscover)
3097 class cmd_domain_trust_namespaces(DomainTrustCommand):
3098 """Manage forest trust namespaces."""
3100 synopsis = "%prog [DOMAIN] [options]"
3102 takes_optiongroups = {
3103 "sambaopts": options.SambaOptions,
3104 "versionopts": options.VersionOptions,
3105 "localdcopts": LocalDCCredentialsOptions,
3109 Option("--refresh", type="choice", metavar="check|store",
3110 choices=["check", "store", None],
3111 help="List and maybe store refreshed forest trust information: 'check' or 'store'.",
3114 Option("--enable-all", action="store_true",
3115 help="Try to update disabled entries, not allowed with --refresh=check.",
3118 Option("--enable-tln", action="append", metavar='DNSDOMAIN',
3119 help="Enable a top level name entry. Can be specified multiple times.",
3122 Option("--disable-tln", action="append", metavar='DNSDOMAIN',
3123 help="Disable a top level name entry. Can be specified multiple times.",
3126 Option("--add-tln-ex", action="append", metavar='DNSDOMAIN',
3127 help="Add a top level exclusion entry. Can be specified multiple times.",
3130 Option("--delete-tln-ex", action="append", metavar='DNSDOMAIN',
3131 help="Delete a top level exclusion entry. Can be specified multiple times.",
3132 dest='delete_tln_ex',
3134 Option("--enable-nb", action="append", metavar='NETBIOSDOMAIN',
3135 help="Enable a netbios name in a domain entry. Can be specified multiple times.",
3138 Option("--disable-nb", action="append", metavar='NETBIOSDOMAIN',
3139 help="Disable a netbios name in a domain entry. Can be specified multiple times.",
3142 Option("--enable-sid", action="append", metavar='DOMAINSID',
3143 help="Enable a SID in a domain entry. Can be specified multiple times.",
3144 dest='enable_sid_str',
3146 Option("--disable-sid", action="append", metavar='DOMAINSID',
3147 help="Disable a SID in a domain entry. Can be specified multiple times.",
3148 dest='disable_sid_str',
3150 Option("--add-upn-suffix", action="append", metavar='DNSDOMAIN',
3151 help="Add a new uPNSuffixes attribute for the local forest. Can be specified multiple times.",
3154 Option("--delete-upn-suffix", action="append", metavar='DNSDOMAIN',
3155 help="Delete an existing uPNSuffixes attribute of the local forest. Can be specified multiple times.",
3158 Option("--add-spn-suffix", action="append", metavar='DNSDOMAIN',
3159 help="Add a new msDS-SPNSuffixes attribute for the local forest. Can be specified multiple times.",
3162 Option("--delete-spn-suffix", action="append", metavar='DNSDOMAIN',
3163 help="Delete an existing msDS-SPNSuffixes attribute of the local forest. Can be specified multiple times.",
3168 takes_args = ["domain?"]
3170 def run(self, domain=None, sambaopts=None, localdcopts=None, versionopts=None,
3171 refresh=None, enable_all=False,
3172 enable_tln=[], disable_tln=[], add_tln_ex=[], delete_tln_ex=[],
3173 enable_sid_str=[], disable_sid_str=[], enable_nb=[], disable_nb=[],
3174 add_upn=[], delete_upn=[], add_spn=[], delete_spn=[]):
3176 require_update = False
3179 if refresh == "store":
3180 raise CommandError("--refresh=%s not allowed without DOMAIN" % refresh)
3183 raise CommandError("--enable-all not allowed without DOMAIN")
3185 if len(enable_tln) > 0:
3186 raise CommandError("--enable-tln not allowed without DOMAIN")
3187 if len(disable_tln) > 0:
3188 raise CommandError("--disable-tln not allowed without DOMAIN")
3190 if len(add_tln_ex) > 0:
3191 raise CommandError("--add-tln-ex not allowed without DOMAIN")
3192 if len(delete_tln_ex) > 0:
3193 raise CommandError("--delete-tln-ex not allowed without DOMAIN")
3195 if len(enable_nb) > 0:
3196 raise CommandError("--enable-nb not allowed without DOMAIN")
3197 if len(disable_nb) > 0:
3198 raise CommandError("--disable-nb not allowed without DOMAIN")
3200 if len(enable_sid_str) > 0:
3201 raise CommandError("--enable-sid not allowed without DOMAIN")
3202 if len(disable_sid_str) > 0:
3203 raise CommandError("--disable-sid not allowed without DOMAIN")
3205 if len(add_upn) > 0:
3207 if not n.startswith("*."):
3209 raise CommandError("value[%s] specified for --add-upn-suffix should not include with '*.'" % n)
3210 require_update = True
3211 if len(delete_upn) > 0:
3212 for n in delete_upn:
3213 if not n.startswith("*."):
3215 raise CommandError("value[%s] specified for --delete-upn-suffix should not include with '*.'" % n)
3216 require_update = True
3218 for d in delete_upn:
3219 if a.lower() != d.lower():
3221 raise CommandError("value[%s] specified for --add-upn-suffix and --delete-upn-suffix" % a)
3223 if len(add_spn) > 0:
3225 if not n.startswith("*."):
3227 raise CommandError("value[%s] specified for --add-spn-suffix should not include with '*.'" % n)
3228 require_update = True
3229 if len(delete_spn) > 0:
3230 for n in delete_spn:
3231 if not n.startswith("*."):
3233 raise CommandError("value[%s] specified for --delete-spn-suffix should not include with '*.'" % n)
3234 require_update = True
3236 for d in delete_spn:
3237 if a.lower() != d.lower():
3239 raise CommandError("value[%s] specified for --add-spn-suffix and --delete-spn-suffix" % a)
3241 if len(add_upn) > 0:
3242 raise CommandError("--add-upn-suffix not allowed together with DOMAIN")
3243 if len(delete_upn) > 0:
3244 raise CommandError("--delete-upn-suffix not allowed together with DOMAIN")
3245 if len(add_spn) > 0:
3246 raise CommandError("--add-spn-suffix not allowed together with DOMAIN")
3247 if len(delete_spn) > 0:
3248 raise CommandError("--delete-spn-suffix not allowed together with DOMAIN")
3250 if refresh is not None:
3251 if refresh == "store":
3252 require_update = True
3254 if enable_all and refresh != "store":
3255 raise CommandError("--enable-all not allowed together with --refresh=%s" % refresh)
3257 if len(enable_tln) > 0:
3258 raise CommandError("--enable-tln not allowed together with --refresh")
3259 if len(disable_tln) > 0:
3260 raise CommandError("--disable-tln not allowed together with --refresh")
3262 if len(add_tln_ex) > 0:
3263 raise CommandError("--add-tln-ex not allowed together with --refresh")
3264 if len(delete_tln_ex) > 0:
3265 raise CommandError("--delete-tln-ex not allowed together with --refresh")
3267 if len(enable_nb) > 0:
3268 raise CommandError("--enable-nb not allowed together with --refresh")
3269 if len(disable_nb) > 0:
3270 raise CommandError("--disable-nb not allowed together with --refresh")
3272 if len(enable_sid_str) > 0:
3273 raise CommandError("--enable-sid not allowed together with --refresh")
3274 if len(disable_sid_str) > 0:
3275 raise CommandError("--disable-sid not allowed together with --refresh")
3278 require_update = True
3280 if len(enable_tln) > 0:
3281 raise CommandError("--enable-tln not allowed together with --enable-all")
3283 if len(enable_nb) > 0:
3284 raise CommandError("--enable-nb not allowed together with --enable-all")
3286 if len(enable_sid_str) > 0:
3287 raise CommandError("--enable-sid not allowed together with --enable-all")
3289 if len(enable_tln) > 0:
3290 require_update = True
3291 if len(disable_tln) > 0:
3292 require_update = True
3293 for e in enable_tln:
3294 for d in disable_tln:
3295 if e.lower() != d.lower():
3297 raise CommandError("value[%s] specified for --enable-tln and --disable-tln" % e)
3299 if len(add_tln_ex) > 0:
3300 for n in add_tln_ex:
3301 if not n.startswith("*."):
3303 raise CommandError("value[%s] specified for --add-tln-ex should not include with '*.'" % n)
3304 require_update = True
3305 if len(delete_tln_ex) > 0:
3306 for n in delete_tln_ex:
3307 if not n.startswith("*."):
3309 raise CommandError("value[%s] specified for --delete-tln-ex should not include with '*.'" % n)
3310 require_update = True
3311 for a in add_tln_ex:
3312 for d in delete_tln_ex:
3313 if a.lower() != d.lower():
3315 raise CommandError("value[%s] specified for --add-tln-ex and --delete-tln-ex" % a)
3317 if len(enable_nb) > 0:
3318 require_update = True
3319 if len(disable_nb) > 0:
3320 require_update = True
3322 for d in disable_nb:
3323 if e.upper() != d.upper():
3325 raise CommandError("value[%s] specified for --enable-nb and --disable-nb" % e)
3328 for s in enable_sid_str:
3330 sid = security.dom_sid(s)
3331 except TypeError as error:
3332 raise CommandError("value[%s] specified for --enable-sid is not a valid SID" % s)
3333 enable_sid.append(sid)
3335 for s in disable_sid_str:
3337 sid = security.dom_sid(s)
3338 except TypeError as error:
3339 raise CommandError("value[%s] specified for --disable-sid is not a valid SID" % s)
3340 disable_sid.append(sid)
3341 if len(enable_sid) > 0:
3342 require_update = True
3343 if len(disable_sid) > 0:
3344 require_update = True
3345 for e in enable_sid:
3346 for d in disable_sid:
3349 raise CommandError("value[%s] specified for --enable-sid and --disable-sid" % e)
3351 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
3353 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
3355 local_server = self.setup_local_server(sambaopts, localdcopts)
3357 local_lsa = self.new_local_lsa_connection()
3358 except RuntimeError as error:
3359 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
3362 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
3363 except RuntimeError as error:
3364 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
3366 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
3367 local_lsa_info.name.string,
3368 local_lsa_info.dns_domain.string,
3369 local_lsa_info.sid))
3373 local_netlogon = self.new_local_netlogon_connection()
3374 except RuntimeError as error:
3375 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3378 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3379 except RuntimeError as error:
3380 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3382 if local_netlogon_info.domain_name != local_netlogon_info.forest_name:
3383 raise CommandError("The local domain [%s] is not the forest root [%s]" % (
3384 local_netlogon_info.domain_name,
3385 local_netlogon_info.forest_name))
3388 # get all information about our own forest
3389 own_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3391 except RuntimeError as error:
3392 if self.check_runtime_error(error, werror.WERR_RPC_S_PROCNUM_OUT_OF_RANGE):
3393 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3396 if self.check_runtime_error(error, werror.WERR_INVALID_FUNCTION):
3397 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3400 if self.check_runtime_error(error, werror.WERR_NERR_ACFNOTLOADED):
3401 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3404 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3406 self.outf.write("Own forest trust information...\n")
3407 self.write_forest_trust_info(own_forest_info,
3408 tln=local_lsa_info.dns_domain.string)
3411 local_samdb = self.new_local_ldap_connection()
3412 except RuntimeError as error:
3413 raise self.LocalRuntimeError(self, error, "failed to connect to SamDB")
3415 local_partitions_dn = "CN=Partitions,%s" % str(local_samdb.get_config_basedn())
3416 attrs = ['uPNSuffixes', 'msDS-SPNSuffixes']
3418 msgs = local_samdb.search(base=local_partitions_dn,
3419 scope=ldb.SCOPE_BASE,
3420 expression="(objectClass=crossRefContainer)",
3422 stored_msg = msgs[0]
3423 except ldb.LdbError as error:
3424 raise self.LocalLdbError(self, error, "failed to search partition dn")
3426 stored_upn_vals = []
3427 if 'uPNSuffixes' in stored_msg:
3428 stored_upn_vals.extend(stored_msg['uPNSuffixes'])
3430 stored_spn_vals = []
3431 if 'msDS-SPNSuffixes' in stored_msg:
3432 stored_spn_vals.extend(stored_msg['msDS-SPNSuffixes'])
3434 self.outf.write("Stored uPNSuffixes attributes[%d]:\n" % len(stored_upn_vals))
3435 for v in stored_upn_vals:
3436 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3437 self.outf.write("Stored msDS-SPNSuffixes attributes[%d]:\n" % len(stored_spn_vals))
3438 for v in stored_spn_vals:
3439 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3441 if not require_update:
3445 update_upn_vals = []
3446 update_upn_vals.extend(stored_upn_vals)
3449 update_spn_vals = []
3450 update_spn_vals.extend(stored_spn_vals)
3453 for i, v in enumerate(update_upn_vals):
3454 if v.lower() == upn.lower():
3455 raise CommandError("Entry already present for "
3456 "value[%s] specified for "
3457 "--add-upn-suffix" % upn)
3458 update_upn_vals.append(upn)
3461 for upn in delete_upn:
3463 for i, v in enumerate(update_upn_vals):
3464 if v.lower() != upn.lower():
3469 raise CommandError("Entry not found for value[%s] specified for --delete-upn-suffix" % upn)
3471 update_upn_vals.pop(idx)
3475 for i, v in enumerate(update_spn_vals):
3476 if v.lower() == spn.lower():
3477 raise CommandError("Entry already present for "
3478 "value[%s] specified for "
3479 "--add-spn-suffix" % spn)
3480 update_spn_vals.append(spn)
3483 for spn in delete_spn:
3485 for i, v in enumerate(update_spn_vals):
3486 if v.lower() != spn.lower():
3491 raise CommandError("Entry not found for value[%s] specified for --delete-spn-suffix" % spn)
3493 update_spn_vals.pop(idx)
3496 self.outf.write("Update uPNSuffixes attributes[%d]:\n" % len(update_upn_vals))
3497 for v in update_upn_vals:
3498 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3499 self.outf.write("Update msDS-SPNSuffixes attributes[%d]:\n" % len(update_spn_vals))
3500 for v in update_spn_vals:
3501 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3503 update_msg = ldb.Message()
3504 update_msg.dn = stored_msg.dn
3507 update_msg['uPNSuffixes'] = ldb.MessageElement(update_upn_vals,
3508 ldb.FLAG_MOD_REPLACE,
3511 update_msg['msDS-SPNSuffixes'] = ldb.MessageElement(update_spn_vals,
3512 ldb.FLAG_MOD_REPLACE,
3515 local_samdb.modify(update_msg)
3516 except ldb.LdbError as error:
3517 raise self.LocalLdbError(self, error, "failed to update partition dn")
3520 stored_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3522 except RuntimeError as error:
3523 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3525 self.outf.write("Stored forest trust information...\n")
3526 self.write_forest_trust_info(stored_forest_info,
3527 tln=local_lsa_info.dns_domain.string)
3531 lsaString = lsa.String()
3532 lsaString.string = domain
3533 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
3534 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
3535 except NTSTATUSError as error:
3536 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
3537 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
3539 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
3541 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
3542 local_tdo_info.netbios_name.string,
3543 local_tdo_info.domain_name.string,
3544 local_tdo_info.sid))
3546 if not local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
3547 raise CommandError("trusted domain object for domain [%s] is not marked as FOREST_TRANSITIVE." % domain)
3549 if refresh is not None:
3551 local_netlogon = self.new_local_netlogon_connection()
3552 except RuntimeError as error:
3553 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3556 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3557 except RuntimeError as error:
3558 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3560 lsa_update_check = 1
3561 if refresh == "store":
3562 netlogon_update_tdo = netlogon.DS_GFTI_UPDATE_TDO
3564 lsa_update_check = 0
3566 netlogon_update_tdo = 0
3569 # get all information about the remote trust
3570 # this triggers netr_GetForestTrustInformation to the remote domain
3571 # and lsaRSetForestTrustInformation() locally, but new top level
3572 # names are disabled by default.
3573 fresh_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3574 local_tdo_info.domain_name.string,
3575 netlogon_update_tdo)
3576 except RuntimeError as error:
3577 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3580 fresh_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3581 local_tdo_info.domain_name,
3582 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3585 except RuntimeError as error:
3586 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3588 self.outf.write("Fresh forest trust information...\n")
3589 self.write_forest_trust_info(fresh_forest_info,
3590 tln=local_tdo_info.domain_name.string,
3591 collisions=fresh_forest_collision)
3593 if refresh == "store":
3595 lsaString = lsa.String()
3596 lsaString.string = local_tdo_info.domain_name.string
3597 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3599 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3600 except RuntimeError as error:
3601 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3603 self.outf.write("Stored forest trust information...\n")
3604 self.write_forest_trust_info(stored_forest_info,
3605 tln=local_tdo_info.domain_name.string)
3610 # The none --refresh path
3614 lsaString = lsa.String()
3615 lsaString.string = local_tdo_info.domain_name.string
3616 local_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3618 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3619 except RuntimeError as error:
3620 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3622 self.outf.write("Local forest trust information...\n")
3623 self.write_forest_trust_info(local_forest_info,
3624 tln=local_tdo_info.domain_name.string)
3626 if not require_update:
3630 entries.extend(local_forest_info.entries)
3631 update_forest_info = lsa.ForestTrustInformation()
3632 update_forest_info.count = len(entries)
3633 update_forest_info.entries = entries
3636 for i, r in enumerate(update_forest_info.entries):
3637 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3639 if update_forest_info.entries[i].flags == 0:
3641 update_forest_info.entries[i].time = 0
3642 update_forest_info.entries[i].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3643 for i, r in enumerate(update_forest_info.entries):
3644 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3646 if update_forest_info.entries[i].flags == 0:
3648 update_forest_info.entries[i].time = 0
3649 update_forest_info.entries[i].flags &= ~lsa.LSA_NB_DISABLED_MASK
3650 update_forest_info.entries[i].flags &= ~lsa.LSA_SID_DISABLED_MASK
3652 for tln in enable_tln:
3654 for i, r in enumerate(update_forest_info.entries):
3655 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3657 if r.forest_trust_data.string.lower() != tln.lower():
3662 raise CommandError("Entry not found for value[%s] specified for --enable-tln" % tln)
3663 if not update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_MASK:
3664 raise CommandError("Entry found for value[%s] specified for --enable-tln is already enabled" % tln)
3665 update_forest_info.entries[idx].time = 0
3666 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3668 for tln in disable_tln:
3670 for i, r in enumerate(update_forest_info.entries):
3671 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3673 if r.forest_trust_data.string.lower() != tln.lower():
3678 raise CommandError("Entry not found for value[%s] specified for --disable-tln" % tln)
3679 if update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_ADMIN:
3680 raise CommandError("Entry found for value[%s] specified for --disable-tln is already disabled" % tln)
3681 update_forest_info.entries[idx].time = 0
3682 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3683 update_forest_info.entries[idx].flags |= lsa.LSA_TLN_DISABLED_ADMIN
3685 for tln_ex in add_tln_ex:
3687 for i, r in enumerate(update_forest_info.entries):
3688 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3690 if r.forest_trust_data.string.lower() != tln_ex.lower():
3695 raise CommandError("Entry already present for value[%s] specified for --add-tln-ex" % tln_ex)
3697 tln_dot = ".%s" % tln_ex.lower()
3699 for i, r in enumerate(update_forest_info.entries):
3700 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3702 r_dot = ".%s" % r.forest_trust_data.string.lower()
3703 if tln_dot == r_dot:
3704 raise CommandError("TLN entry present for value[%s] specified for --add-tln-ex" % tln_ex)
3705 if not tln_dot.endswith(r_dot):
3711 raise CommandError("No TLN parent present for value[%s] specified for --add-tln-ex" % tln_ex)
3713 r = lsa.ForestTrustRecord()
3714 r.type = lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX
3717 r.forest_trust_data.string = tln_ex
3720 entries.extend(update_forest_info.entries)
3721 entries.insert(idx + 1, r)
3722 update_forest_info.count = len(entries)
3723 update_forest_info.entries = entries
3725 for tln_ex in delete_tln_ex:
3727 for i, r in enumerate(update_forest_info.entries):
3728 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3730 if r.forest_trust_data.string.lower() != tln_ex.lower():
3735 raise CommandError("Entry not found for value[%s] specified for --delete-tln-ex" % tln_ex)
3738 entries.extend(update_forest_info.entries)
3740 update_forest_info.count = len(entries)
3741 update_forest_info.entries = entries
3743 for nb in enable_nb:
3745 for i, r in enumerate(update_forest_info.entries):
3746 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3748 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3753 raise CommandError("Entry not found for value[%s] specified for --enable-nb" % nb)
3754 if not update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_MASK:
3755 raise CommandError("Entry found for value[%s] specified for --enable-nb is already enabled" % nb)
3756 update_forest_info.entries[idx].time = 0
3757 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3759 for nb in disable_nb:
3761 for i, r in enumerate(update_forest_info.entries):
3762 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3764 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3769 raise CommandError("Entry not found for value[%s] specified for --delete-nb" % nb)
3770 if update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_ADMIN:
3771 raise CommandError("Entry found for value[%s] specified for --disable-nb is already disabled" % nb)
3772 update_forest_info.entries[idx].time = 0
3773 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3774 update_forest_info.entries[idx].flags |= lsa.LSA_NB_DISABLED_ADMIN
3776 for sid in enable_sid:
3778 for i, r in enumerate(update_forest_info.entries):
3779 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3781 if r.forest_trust_data.domain_sid != sid:
3786 raise CommandError("Entry not found for value[%s] specified for --enable-sid" % sid)
3787 if not update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_MASK:
3788 raise CommandError("Entry found for value[%s] specified for --enable-sid is already enabled" % nb)
3789 update_forest_info.entries[idx].time = 0
3790 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3792 for sid in disable_sid:
3794 for i, r in enumerate(update_forest_info.entries):
3795 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3797 if r.forest_trust_data.domain_sid != sid:
3802 raise CommandError("Entry not found for value[%s] specified for --delete-sid" % sid)
3803 if update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_ADMIN:
3804 raise CommandError("Entry found for value[%s] specified for --disable-sid is already disabled" % nb)
3805 update_forest_info.entries[idx].time = 0
3806 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3807 update_forest_info.entries[idx].flags |= lsa.LSA_SID_DISABLED_ADMIN
3810 update_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3811 local_tdo_info.domain_name,
3812 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3813 update_forest_info, 0)
3814 except RuntimeError as error:
3815 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3817 self.outf.write("Updated forest trust information...\n")
3818 self.write_forest_trust_info(update_forest_info,
3819 tln=local_tdo_info.domain_name.string,
3820 collisions=update_forest_collision)
3823 lsaString = lsa.String()
3824 lsaString.string = local_tdo_info.domain_name.string
3825 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3827 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3828 except RuntimeError as error:
3829 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3831 self.outf.write("Stored forest trust information...\n")
3832 self.write_forest_trust_info(stored_forest_info,
3833 tln=local_tdo_info.domain_name.string)
3836 class cmd_domain_tombstones_expunge(Command):
3837 """Expunge tombstones from the database.
3839 This command expunges tombstones from the database."""
3840 synopsis = "%prog NC [NC [...]] [options]"
3843 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
3844 metavar="URL", dest="H"),
3845 Option("--current-time",
3846 help="The current time to evaluate the tombstone lifetime from, expressed as YYYY-MM-DD",
3848 Option("--tombstone-lifetime", help="Number of days a tombstone should be preserved for", type=int),
3851 takes_args = ["nc*"]
3853 takes_optiongroups = {
3854 "sambaopts": options.SambaOptions,
3855 "credopts": options.CredentialsOptions,
3856 "versionopts": options.VersionOptions,
3859 def run(self, *ncs, **kwargs):
3860 sambaopts = kwargs.get("sambaopts")
3861 credopts = kwargs.get("credopts")
3862 versionpts = kwargs.get("versionopts")
3864 current_time_string = kwargs.get("current_time")
3865 tombstone_lifetime = kwargs.get("tombstone_lifetime")
3866 lp = sambaopts.get_loadparm()
3867 creds = credopts.get_credentials(lp)
3868 samdb = SamDB(url=H, session_info=system_session(),
3869 credentials=creds, lp=lp)
3871 if current_time_string is not None:
3872 current_time_obj = time.strptime(current_time_string, "%Y-%m-%d")
3873 current_time = long(time.mktime(current_time_obj))
3876 current_time = long(time.time())
3879 res = samdb.search(expression="", base="", scope=ldb.SCOPE_BASE,
3880 attrs=["namingContexts"])
3883 for nc in res[0]["namingContexts"]:
3888 started_transaction = False
3890 samdb.transaction_start()
3891 started_transaction = True
3893 removed_links) = samdb.garbage_collect_tombstones(ncs,
3894 current_time=current_time,
3895 tombstone_lifetime=tombstone_lifetime)
3897 except Exception as err:
3898 if started_transaction:
3899 samdb.transaction_cancel()
3900 raise CommandError("Failed to expunge / garbage collect tombstones", err)
3902 samdb.transaction_commit()
3904 self.outf.write("Removed %d objects and %d links successfully\n"
3905 % (removed_objects, removed_links))
3909 class cmd_domain_trust(SuperCommand):
3910 """Domain and forest trust management."""
3913 subcommands["list"] = cmd_domain_trust_list()
3914 subcommands["show"] = cmd_domain_trust_show()
3915 subcommands["create"] = cmd_domain_trust_create()
3916 subcommands["delete"] = cmd_domain_trust_delete()
3917 subcommands["validate"] = cmd_domain_trust_validate()
3918 subcommands["namespaces"] = cmd_domain_trust_namespaces()
3920 class cmd_domain_tombstones(SuperCommand):
3921 """Domain tombstone and recycled object management."""
3924 subcommands["expunge"] = cmd_domain_tombstones_expunge()
3926 class ldif_schema_update:
3927 """Helper class for applying LDIF schema updates"""
3930 self.is_defunct = False
3931 self.unknown_oid = None
3935 def can_ignore_failure(self, error):
3936 """Checks if we can safely ignore failure to apply an LDIF update"""
3937 (num, errstr) = error.args
3939 # Microsoft has marked objects as defunct that Samba doesn't know about
3940 if num == ldb.ERR_NO_SUCH_OBJECT and self.is_defunct:
3941 print("Defunct object %s doesn't exist, skipping" % self.dn)
3943 elif self.unknown_oid is not None:
3944 print("Skipping unknown OID %s for object %s" %(self.unknown_oid, self.dn))
3949 def apply(self, samdb):
3950 """Applies a single LDIF update to the schema"""
3954 samdb.modify_ldif(self.ldif, controls=['relax:0'])
3955 except ldb.LdbError as e:
3956 if e.args[0] == ldb.ERR_INVALID_ATTRIBUTE_SYNTAX:
3958 # REFRESH after a failed change
3960 # Otherwise the OID-to-attribute mapping in
3961 # _apply_updates_in_file() won't work, because it
3962 # can't lookup the new OID in the schema
3963 samdb.set_schema_update_now()
3965 samdb.modify_ldif(self.ldif, controls=['relax:0'])
3968 except ldb.LdbError as e:
3969 if self.can_ignore_failure(e):
3972 print("Exception: %s" % e)
3973 print("Encountered while trying to apply the following LDIF")
3974 print("----------------------------------------------------")
3975 print("%s" % self.ldif)
3981 class cmd_domain_schema_upgrade(Command):
3982 """Domain schema upgrading"""
3984 synopsis = "%prog [options]"
3986 takes_optiongroups = {
3987 "sambaopts": options.SambaOptions,
3988 "versionopts": options.VersionOptions,
3989 "credopts": options.CredentialsOptions,
3993 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
3994 metavar="URL", dest="H"),
3995 Option("-q", "--quiet", help="Be quiet", action="store_true"), #unused
3996 Option("-v", "--verbose", help="Be verbose", action="store_true"),
3997 Option("--schema", type="choice", metavar="SCHEMA",
3998 choices=["2012", "2012_R2"],
3999 help="The schema file to upgrade to. Default is (Windows) 2012_R2.",
4001 Option("--ldf-file", type=str, default=None,
4002 help="Just apply the schema updates in the adprep/.LDF file(s) specified"),
4003 Option("--base-dir", type=str, default=None,
4004 help="Location of ldf files Default is ${SETUPDIR}/adprep.")
4007 def _apply_updates_in_file(self, samdb, ldif_file):
4009 Applies a series of updates specified in an .LDIF file. The .LDIF file
4010 is based on the adprep Schema updates provided by Microsoft.
4013 ldif_op = ldif_schema_update()
4015 # parse the file line by line and work out each update operation to apply
4016 for line in ldif_file:
4018 line = line.rstrip()
4020 # the operations in the .LDIF file are separated by blank lines. If
4021 # we hit a blank line, try to apply the update we've parsed so far
4024 # keep going if we haven't parsed anything yet
4025 if ldif_op.ldif == '':
4028 # Apply the individual change
4029 count += ldif_op.apply(samdb)
4031 # start storing the next operation from scratch again
4032 ldif_op = ldif_schema_update()
4035 # replace the placeholder domain name in the .ldif file with the real domain
4036 if line.upper().endswith('DC=X'):
4037 line = line[:-len('DC=X')] + str(samdb.get_default_basedn())
4038 elif line.upper().endswith('CN=X'):
4039 line = line[:-len('CN=X')] + str(samdb.get_default_basedn())
4041 values = line.split(':')
4043 if values[0].lower() == 'dn':
4044 ldif_op.dn = values[1].strip()
4046 # replace the Windows-specific operation with the Samba one
4047 if values[0].lower() == 'changetype':
4048 line = line.lower().replace(': ntdsschemaadd',
4050 line = line.lower().replace(': ntdsschemamodify',
4053 if values[0].lower() in ['rdnattid', 'subclassof',
4054 'systemposssuperiors',
4056 'systemauxiliaryclass']:
4059 # The Microsoft updates contain some OIDs we don't recognize.
4060 # Query the DB to see if we can work out the OID this update is
4061 # referring to. If we find a match, then replace the OID with
4062 # the ldapDisplayname
4064 res = samdb.search(base=samdb.get_schema_basedn(),
4065 expression="(|(attributeId=%s)(governsId=%s))" %
4067 attrs=['ldapDisplayName'])
4070 ldif_op.unknown_oid = value
4072 display_name = res[0]['ldapDisplayName'][0]
4073 line = line.replace(value, ' ' + display_name)
4075 # Microsoft has marked objects as defunct that Samba doesn't know about
4076 if values[0].lower() == 'isdefunct' and values[1].strip().lower() == 'true':
4077 ldif_op.is_defunct = True
4079 # Samba has added the showInAdvancedViewOnly attribute to all objects,
4080 # so rather than doing an add, we need to do a replace
4081 if values[0].lower() == 'add' and values[1].strip().lower() == 'showinadvancedviewonly':
4082 line = 'replace: showInAdvancedViewOnly'
4084 # Add the line to the current LDIF operation (including the newline
4085 # we stripped off at the start of the loop)
4086 ldif_op.ldif += line + '\n'
4091 def _apply_update(self, samdb, update_file, base_dir):
4092 """Wrapper function for parsing an LDIF file and applying the updates"""
4094 print("Applying %s updates..." % update_file)
4098 ldif_file = open(os.path.join(base_dir, update_file))
4100 count = self._apply_updates_in_file(samdb, ldif_file)
4106 print("%u changes applied" % count)
4110 def run(self, **kwargs):
4111 from samba.ms_schema_markdown import read_ms_markdown
4112 from samba.schema import Schema
4114 updates_allowed_overriden = False
4115 sambaopts = kwargs.get("sambaopts")
4116 credopts = kwargs.get("credopts")
4117 versionpts = kwargs.get("versionopts")
4118 lp = sambaopts.get_loadparm()
4119 creds = credopts.get_credentials(lp)
4121 target_schema = kwargs.get("schema")
4122 ldf_files = kwargs.get("ldf_file")
4123 base_dir = kwargs.get("base_dir")
4127 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
4129 # we're not going to get far if the config doesn't allow schema updates
4130 if lp.get("dsdb:schema update allowed") is None:
4131 lp.set("dsdb:schema update allowed", "yes")
4132 print("Temporarily overriding 'dsdb:schema update allowed' setting")
4133 updates_allowed_overriden = True
4135 own_dn = ldb.Dn(samdb, samdb.get_dsServiceName())
4136 master = get_fsmo_roleowner(samdb, str(samdb.get_schema_basedn()),
4138 if own_dn != master:
4139 raise CommandError("This server is not the schema master.")
4141 # if specific LDIF files were specified, just apply them
4143 schema_updates = ldf_files.split(",")
4147 # work out the version of the target schema we're upgrading to
4148 end = Schema.get_version(target_schema)
4150 # work out the version of the schema we're currently using
4151 res = samdb.search(base=samdb.get_schema_basedn(),
4152 scope=ldb.SCOPE_BASE, attrs=['objectVersion'])
4155 raise CommandError('Could not determine current schema version')
4156 start = int(res[0]['objectVersion'][0]) + 1
4158 diff_dir = setup_path("adprep/WindowsServerDocs")
4159 if base_dir is None:
4160 # Read from the Schema-Updates.md file
4161 temp_folder = tempfile.mkdtemp()
4163 update_file = setup_path("adprep/WindowsServerDocs/Schema-Updates.md")
4166 read_ms_markdown(update_file, temp_folder)
4167 except Exception as e:
4168 print("Exception in markdown parsing: %s" % e)
4169 shutil.rmtree(temp_folder)
4170 raise CommandError('Failed to upgrade schema')
4172 base_dir = temp_folder
4174 for version in range(start, end + 1):
4175 update = 'Sch%d.ldf' % version
4176 schema_updates.append(update)
4178 # Apply patches if we parsed the Schema-Updates.md file
4179 diff = os.path.abspath(os.path.join(diff_dir, update + '.diff'))
4180 if temp_folder and os.path.exists(diff):
4182 p = subprocess.Popen(['patch', update, '-i', diff],
4183 stdout=subprocess.PIPE,
4184 stderr=subprocess.PIPE, cwd=temp_folder)
4185 except (OSError, IOError):
4186 shutil.rmtree(temp_folder)
4187 raise CommandError("Failed to upgrade schema. Check if 'patch' is installed.")
4189 stdout, stderr = p.communicate()
4192 print("Exception in patch: %s\n%s" % (stdout, stderr))
4193 shutil.rmtree(temp_folder)
4194 raise CommandError('Failed to upgrade schema')
4196 print("Patched %s using %s" % (update, diff))
4198 if base_dir is None:
4199 base_dir = setup_path("adprep")
4201 samdb.transaction_start()
4203 error_encountered = False
4206 # Apply the schema updates needed to move to the new schema version
4207 for ldif_file in schema_updates:
4208 count += self._apply_update(samdb, ldif_file, base_dir)
4211 samdb.transaction_commit()
4212 print("Schema successfully updated")
4214 print("No changes applied to schema")
4215 samdb.transaction_cancel()
4216 except Exception as e:
4217 print("Exception: %s" % e)
4218 print("Error encountered, aborting schema upgrade")
4219 samdb.transaction_cancel()
4220 error_encountered = True
4222 if updates_allowed_overriden:
4223 lp.set("dsdb:schema update allowed", "no")
4226 shutil.rmtree(temp_folder)
4228 if error_encountered:
4229 raise CommandError('Failed to upgrade schema')
4231 class cmd_domain_functional_prep(Command):
4232 """Domain functional level preparation"""
4234 synopsis = "%prog [options]"
4236 takes_optiongroups = {
4237 "sambaopts": options.SambaOptions,
4238 "versionopts": options.VersionOptions,
4239 "credopts": options.CredentialsOptions,
4243 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
4244 metavar="URL", dest="H"),
4245 Option("-q", "--quiet", help="Be quiet", action="store_true"),
4246 Option("-v", "--verbose", help="Be verbose", action="store_true"),
4247 Option("--function-level", type="choice", metavar="FUNCTION_LEVEL",
4248 choices=["2008_R2", "2012", "2012_R2"],
4249 help="The schema file to upgrade to. Default is (Windows) 2012_R2.",
4251 Option("--forest-prep", action="store_true",
4252 help="Run the forest prep (by default, both the domain and forest prep are run)."),
4253 Option("--domain-prep", action="store_true",
4254 help="Run the domain prep (by default, both the domain and forest prep are run).")
4257 def run(self, **kwargs):
4258 updates_allowed_overriden = False
4259 sambaopts = kwargs.get("sambaopts")
4260 credopts = kwargs.get("credopts")
4261 versionpts = kwargs.get("versionopts")
4262 lp = sambaopts.get_loadparm()
4263 creds = credopts.get_credentials(lp)
4265 target_level = string_version_to_constant[kwargs.get("function_level")]
4266 forest_prep = kwargs.get("forest_prep")
4267 domain_prep = kwargs.get("domain_prep")
4269 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
4271 # we're not going to get far if the config doesn't allow schema updates
4272 if lp.get("dsdb:schema update allowed") is None:
4273 lp.set("dsdb:schema update allowed", "yes")
4274 print("Temporarily overriding 'dsdb:schema update allowed' setting")
4275 updates_allowed_overriden = True
4277 if forest_prep is None and domain_prep is None:
4281 own_dn = ldb.Dn(samdb, samdb.get_dsServiceName())
4283 master = get_fsmo_roleowner(samdb, str(samdb.get_schema_basedn()),
4285 if own_dn != master:
4286 raise CommandError("This server is not the schema master.")
4289 domain_dn = samdb.domain_dn()
4290 infrastructure_dn = "CN=Infrastructure," + domain_dn
4291 master = get_fsmo_roleowner(samdb, infrastructure_dn,
4293 if own_dn != master:
4294 raise CommandError("This server is not the infrastructure master.")
4297 samdb.transaction_start()
4298 error_encountered = False
4300 from samba.forest_update import ForestUpdate
4301 forest = ForestUpdate(samdb, fix=True)
4303 forest.check_updates_iterator([53, 79, 80, 81, 82, 83])
4304 forest.check_updates_functional_level(target_level,
4305 DS_DOMAIN_FUNCTION_2008_R2,
4306 update_revision=True)
4308 samdb.transaction_commit()
4309 except Exception as e:
4310 print("Exception: %s" % e)
4311 samdb.transaction_cancel()
4312 error_encountered = True
4315 samdb.transaction_start()
4316 error_encountered = False
4318 from samba.domain_update import DomainUpdate
4320 domain = DomainUpdate(samdb, fix=True)
4321 domain.check_updates_functional_level(target_level,
4322 DS_DOMAIN_FUNCTION_2008,
4323 update_revision=True)
4325 samdb.transaction_commit()
4326 except Exception as e:
4327 print("Exception: %s" % e)
4328 samdb.transaction_cancel()
4329 error_encountered = True
4331 if updates_allowed_overriden:
4332 lp.set("dsdb:schema update allowed", "no")
4334 if error_encountered:
4335 raise CommandError('Failed to perform functional prep')
4337 class cmd_domain(SuperCommand):
4338 """Domain management."""
4341 subcommands["demote"] = cmd_domain_demote()
4342 if cmd_domain_export_keytab is not None:
4343 subcommands["exportkeytab"] = cmd_domain_export_keytab()
4344 subcommands["info"] = cmd_domain_info()
4345 subcommands["provision"] = cmd_domain_provision()
4346 subcommands["join"] = cmd_domain_join()
4347 subcommands["dcpromo"] = cmd_domain_dcpromo()
4348 subcommands["level"] = cmd_domain_level()
4349 subcommands["passwordsettings"] = cmd_domain_passwordsettings()
4350 subcommands["classicupgrade"] = cmd_domain_classicupgrade()
4351 subcommands["samba3upgrade"] = cmd_domain_samba3upgrade()
4352 subcommands["trust"] = cmd_domain_trust()
4353 subcommands["tombstones"] = cmd_domain_tombstones()
4354 subcommands["schemaupgrade"] = cmd_domain_schema_upgrade()
4355 subcommands["functionalprep"] = cmd_domain_functional_prep()
4356 subcommands["backup"] = cmd_domain_backup()