3 # Copyright Matthias Dieter Wallnoefer 2009
4 # Copyright Andrew Kroeger 2009
5 # Copyright Jelmer Vernooij 2007-2012
6 # Copyright Giampaolo Lauria 2011
7 # Copyright Matthieu Patou <mat@matws.net> 2011
8 # Copyright Andrew Bartlett 2008-2015
9 # Copyright Stefan Metzmacher 2012
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 3 of the License, or
14 # (at your option) any later version.
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 from __future__ import print_function
26 from __future__ import division
27 import samba.getopt as options
39 from samba import ntstatus
40 from samba import NTSTATUSError
41 from samba import werror
42 from getpass import getpass
43 from samba.net import Net, LIBNET_JOIN_AUTOMATIC
45 from samba.join import join_RODC, join_DC, join_subdomain
46 from samba.auth import system_session
47 from samba.samdb import SamDB, get_default_backend_store
48 from samba.ndr import ndr_unpack, ndr_pack, ndr_print
49 from samba.dcerpc import drsuapi
50 from samba.dcerpc import drsblobs
51 from samba.dcerpc import lsa
52 from samba.dcerpc import netlogon
53 from samba.dcerpc import security
54 from samba.dcerpc import nbt
55 from samba.dcerpc import misc
56 from samba.dcerpc.samr import DOMAIN_PASSWORD_COMPLEX, DOMAIN_PASSWORD_STORE_CLEARTEXT
57 from samba.netcmd import (
63 from samba.netcmd.fsmo import get_fsmo_roleowner
64 from samba.netcmd.common import netcmd_get_domain_infos_via_cldap
65 from samba.samba3 import Samba3
66 from samba.samba3 import param as s3param
67 from samba.upgrade import upgrade_from_samba3
68 from samba.drs_utils import (
69 sendDsReplicaSync, drsuapi_connect, drsException,
71 from samba import remove_dc, arcfour_encrypt, string_to_byte_array
73 from samba.dsdb import (
74 DS_DOMAIN_FUNCTION_2000,
75 DS_DOMAIN_FUNCTION_2003,
76 DS_DOMAIN_FUNCTION_2003_MIXED,
77 DS_DOMAIN_FUNCTION_2008,
78 DS_DOMAIN_FUNCTION_2008_R2,
79 DS_DOMAIN_FUNCTION_2012,
80 DS_DOMAIN_FUNCTION_2012_R2,
81 DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL,
82 DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL,
83 UF_WORKSTATION_TRUST_ACCOUNT,
84 UF_SERVER_TRUST_ACCOUNT,
85 UF_TRUSTED_FOR_DELEGATION,
86 UF_PARTIAL_SECRETS_ACCOUNT
89 from samba.provision import (
92 DEFAULT_MIN_PWD_LENGTH,
96 from samba.provision.common import (
102 from samba.netcmd.pso import cmd_domain_passwordsettings_pso
103 from samba.netcmd.domain_backup import cmd_domain_backup
105 string_version_to_constant = {
106 "2008_R2": DS_DOMAIN_FUNCTION_2008_R2,
107 "2012": DS_DOMAIN_FUNCTION_2012,
108 "2012_R2": DS_DOMAIN_FUNCTION_2012_R2,
111 common_provision_join_options = [
112 Option("--machinepass", type="string", metavar="PASSWORD",
113 help="choose machine password (otherwise random)"),
114 Option("--plaintext-secrets", action="store_true",
115 help="Store secret/sensitive values as plain text on disk" +
116 "(default is to encrypt secret/ensitive values)"),
117 Option("--backend-store", type="choice", metavar="BACKENDSTORE",
118 choices=["tdb", "mdb"],
119 help="Specify the database backend to be used "
120 "(default is %s)" % get_default_backend_store()),
121 Option("--targetdir", metavar="DIR",
122 help="Set target directory (where to store provision)", type=str),
123 Option("-q", "--quiet", help="Be quiet", action="store_true"),
126 common_join_options = [
127 Option("--server", help="DC to join", type=str),
128 Option("--site", help="site to join", type=str),
129 Option("--domain-critical-only",
130 help="only replicate critical domain objects",
131 action="store_true"),
132 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
133 choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
134 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
135 "BIND9_DLZ uses samba4 AD to store zone information, "
136 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
137 default="SAMBA_INTERNAL"),
138 Option("-v", "--verbose", help="Be verbose", action="store_true")
141 common_ntvfs_options = [
142 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
146 def get_testparm_var(testparm, smbconf, varname):
147 errfile = open(os.devnull, 'w')
148 p = subprocess.Popen([testparm, '-s', '-l',
149 '--parameter-name=%s' % varname, smbconf],
150 stdout=subprocess.PIPE, stderr=errfile)
151 (out, err) = p.communicate()
153 lines = out.split('\n')
155 return lines[0].strip()
159 import samba.dckeytab
161 cmd_domain_export_keytab = None
163 class cmd_domain_export_keytab(Command):
164 """Dump Kerberos keys of the domain into a keytab."""
166 synopsis = "%prog <keytab> [options]"
168 takes_optiongroups = {
169 "sambaopts": options.SambaOptions,
170 "credopts": options.CredentialsOptions,
171 "versionopts": options.VersionOptions,
175 Option("--principal", help="extract only this principal", type=str),
178 takes_args = ["keytab"]
180 def run(self, keytab, credopts=None, sambaopts=None, versionopts=None, principal=None):
181 lp = sambaopts.get_loadparm()
183 net.export_keytab(keytab=keytab, principal=principal)
186 class cmd_domain_info(Command):
187 """Print basic info about a domain and the DC passed as parameter."""
189 synopsis = "%prog <ip_address> [options]"
194 takes_optiongroups = {
195 "sambaopts": options.SambaOptions,
196 "credopts": options.CredentialsOptions,
197 "versionopts": options.VersionOptions,
200 takes_args = ["address"]
202 def run(self, address, credopts=None, sambaopts=None, versionopts=None):
203 lp = sambaopts.get_loadparm()
205 res = netcmd_get_domain_infos_via_cldap(lp, None, address)
207 raise CommandError("Invalid IP address '" + address + "'!")
208 self.outf.write("Forest : %s\n" % res.forest)
209 self.outf.write("Domain : %s\n" % res.dns_domain)
210 self.outf.write("Netbios domain : %s\n" % res.domain_name)
211 self.outf.write("DC name : %s\n" % res.pdc_dns_name)
212 self.outf.write("DC netbios name : %s\n" % res.pdc_name)
213 self.outf.write("Server site : %s\n" % res.server_site)
214 self.outf.write("Client site : %s\n" % res.client_site)
217 class cmd_domain_provision(Command):
218 """Provision a domain."""
220 synopsis = "%prog [options]"
222 takes_optiongroups = {
223 "sambaopts": options.SambaOptions,
224 "versionopts": options.VersionOptions,
228 Option("--interactive", help="Ask for names", action="store_true"),
229 Option("--domain", type="string", metavar="DOMAIN",
230 help="NetBIOS domain name to use"),
231 Option("--domain-guid", type="string", metavar="GUID",
232 help="set domainguid (otherwise random)"),
233 Option("--domain-sid", type="string", metavar="SID",
234 help="set domainsid (otherwise random)"),
235 Option("--ntds-guid", type="string", metavar="GUID",
236 help="set NTDS object GUID (otherwise random)"),
237 Option("--invocationid", type="string", metavar="GUID",
238 help="set invocationid (otherwise random)"),
239 Option("--host-name", type="string", metavar="HOSTNAME",
240 help="set hostname"),
241 Option("--host-ip", type="string", metavar="IPADDRESS",
242 help="set IPv4 ipaddress"),
243 Option("--host-ip6", type="string", metavar="IP6ADDRESS",
244 help="set IPv6 ipaddress"),
245 Option("--site", type="string", metavar="SITENAME",
246 help="set site name"),
247 Option("--adminpass", type="string", metavar="PASSWORD",
248 help="choose admin password (otherwise random)"),
249 Option("--krbtgtpass", type="string", metavar="PASSWORD",
250 help="choose krbtgt password (otherwise random)"),
251 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
252 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
253 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
254 "BIND9_FLATFILE uses bind9 text database to store zone information, "
255 "BIND9_DLZ uses samba4 AD to store zone information, "
256 "NONE skips the DNS setup entirely (not recommended)",
257 default="SAMBA_INTERNAL"),
258 Option("--dnspass", type="string", metavar="PASSWORD",
259 help="choose dns password (otherwise random)"),
260 Option("--root", type="string", metavar="USERNAME",
261 help="choose 'root' unix username"),
262 Option("--nobody", type="string", metavar="USERNAME",
263 help="choose 'nobody' user"),
264 Option("--users", type="string", metavar="GROUPNAME",
265 help="choose 'users' group"),
266 Option("--blank", action="store_true",
267 help="do not add users or groups, just the structure"),
268 Option("--server-role", type="choice", metavar="ROLE",
269 choices=["domain controller", "dc", "member server", "member", "standalone"],
270 help="The server role (domain controller | dc | member server | member | standalone). Default is dc.",
271 default="domain controller"),
272 Option("--function-level", type="choice", metavar="FOR-FUN-LEVEL",
273 choices=["2000", "2003", "2008", "2008_R2"],
274 help="The domain and forest function level (2000 | 2003 | 2008 | 2008_R2 - always native). Default is (Windows) 2008_R2 Native.",
276 Option("--base-schema", type="choice", metavar="BASE-SCHEMA",
277 choices=["2008_R2", "2008_R2_old", "2012", "2012_R2"],
278 help="The base schema files to use. Default is (Windows) 2008_R2.",
280 Option("--next-rid", type="int", metavar="NEXTRID", default=1000,
281 help="The initial nextRid value (only needed for upgrades). Default is 1000."),
282 Option("--partitions-only",
283 help="Configure Samba's partitions, but do not modify them (ie, join a BDC)", action="store_true"),
284 Option("--use-rfc2307", action="store_true", help="Use AD to store posix attributes (default = no)"),
288 Option("--ldapadminpass", type="string", metavar="PASSWORD",
289 help="choose password to set between Samba and its LDAP backend (otherwise random)"),
290 Option("--ldap-backend-type", type="choice", metavar="LDAP-BACKEND-TYPE",
291 help="Test initialisation support for unsupported LDAP backend type (fedora-ds or openldap) DO NOT USE",
292 choices=["fedora-ds", "openldap"]),
293 Option("--ol-mmr-urls", type="string", metavar="LDAPSERVER",
294 help="List of LDAP-URLS [ ldap://<FQHN>:<PORT>/ (where <PORT> has to be different than 389!) ] separated with comma (\",\") for use with OpenLDAP-MMR (Multi-Master-Replication), e.g.: \"ldap://s4dc1:9000,ldap://s4dc2:9000\""),
295 Option("--ldap-dryrun-mode", help="Configure LDAP backend, but do not run any binaries and exit early. Used only for the test environment. DO NOT USE",
296 action="store_true"),
297 Option("--slapd-path", type="string", metavar="SLAPD-PATH",
298 help="Path to slapd for LDAP backend [e.g.:'/usr/local/libexec/slapd']. Required for Setup with LDAP-Backend. OpenLDAP Version >= 2.4.17 should be used."),
299 Option("--ldap-backend-extra-port", type="int", metavar="LDAP-BACKEND-EXTRA-PORT", help="Additional TCP port for LDAP backend server (to use for replication)"),
300 Option("--ldap-backend-forced-uri", type="string", metavar="LDAP-BACKEND-FORCED-URI",
301 help="Force the LDAP backend connection to be to a particular URI. Use this ONLY for 'existing' backends, or when debugging the interaction with the LDAP backend and you need to intercept the LDA"),
302 Option("--ldap-backend-nosync", help="Configure LDAP backend not to call fsync() (for performance in test environments)", action="store_true"),
306 Option("--use-xattrs", type="choice", choices=["yes", "no", "auto"],
307 metavar="[yes|no|auto]",
308 help="Define if we should use the native fs capabilities or a tdb file for "
309 "storing attributes likes ntacl when --use-ntvfs is set. "
310 "auto tries to make an inteligent guess based on the user rights and system capabilities",
314 takes_options.extend(common_provision_join_options)
316 if os.getenv('TEST_LDAP', "no") == "yes":
317 takes_options.extend(openldap_options)
319 if samba.is_ntvfs_fileserver_built():
320 takes_options.extend(common_ntvfs_options)
321 takes_options.extend(ntvfs_options)
325 def run(self, sambaopts=None, versionopts=None,
348 ldap_backend_type=None,
352 partitions_only=None,
359 ldap_backend_nosync=None,
360 ldap_backend_extra_port=None,
361 ldap_backend_forced_uri=None,
362 ldap_dryrun_mode=None,
364 plaintext_secrets=False,
367 self.logger = self.get_logger("provision")
369 self.logger.setLevel(logging.WARNING)
371 self.logger.setLevel(logging.INFO)
373 lp = sambaopts.get_loadparm()
374 smbconf = lp.configfile
376 if dns_forwarder is not None:
377 suggested_forwarder = dns_forwarder
379 suggested_forwarder = self._get_nameserver_ip()
380 if suggested_forwarder is None:
381 suggested_forwarder = "none"
383 if len(self.raw_argv) == 1:
387 from getpass import getpass
390 def ask(prompt, default=None):
391 if default is not None:
392 print("%s [%s]: " % (prompt, default), end=' ')
394 print("%s: " % (prompt,), end=' ')
395 return sys.stdin.readline().rstrip("\n") or default
398 default = socket.getfqdn().split(".", 1)[1].upper()
401 realm = ask("Realm", default)
402 if realm in (None, ""):
403 raise CommandError("No realm set!")
406 default = realm.split(".")[0]
409 domain = ask("Domain", default)
411 raise CommandError("No domain set!")
413 server_role = ask("Server Role (dc, member, standalone)", "dc")
415 dns_backend = ask("DNS backend (SAMBA_INTERNAL, BIND9_FLATFILE, BIND9_DLZ, NONE)", "SAMBA_INTERNAL")
416 if dns_backend in (None, ''):
417 raise CommandError("No DNS backend set!")
419 if dns_backend == "SAMBA_INTERNAL":
420 dns_forwarder = ask("DNS forwarder IP address (write 'none' to disable forwarding)", suggested_forwarder)
421 if dns_forwarder.lower() in (None, 'none'):
422 suggested_forwarder = None
426 adminpassplain = getpass("Administrator password: ")
427 issue = self._adminpass_issue(adminpassplain)
429 self.errf.write("%s.\n" % issue)
431 adminpassverify = getpass("Retype password: ")
432 if not adminpassplain == adminpassverify:
433 self.errf.write("Sorry, passwords do not match.\n")
435 adminpass = adminpassplain
439 realm = sambaopts._lp.get('realm')
441 raise CommandError("No realm set!")
443 raise CommandError("No domain set!")
446 issue = self._adminpass_issue(adminpass)
448 raise CommandError(issue)
450 self.logger.info("Administrator password will be set randomly!")
452 if function_level == "2000":
453 dom_for_fun_level = DS_DOMAIN_FUNCTION_2000
454 elif function_level == "2003":
455 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
456 elif function_level == "2008":
457 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008
458 elif function_level == "2008_R2":
459 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
461 if dns_backend == "SAMBA_INTERNAL" and dns_forwarder is None:
462 dns_forwarder = suggested_forwarder
464 samdb_fill = FILL_FULL
466 samdb_fill = FILL_NT4SYNC
467 elif partitions_only:
468 samdb_fill = FILL_DRS
470 if targetdir is not None:
471 if not os.path.isdir(targetdir):
476 if use_xattrs == "yes":
478 elif use_xattrs == "auto" and use_ntvfs == False:
480 elif use_ntvfs == False:
481 raise CommandError("--use-xattrs=no requires --use-ntvfs (not supported for production use). "
482 "Please re-run with --use-xattrs omitted.")
483 elif use_xattrs == "auto" and not lp.get("posix:eadb"):
485 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
487 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
490 samba.ntacls.setntacl(lp, file.name,
491 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
494 self.logger.info("You are not root or your system does not support xattr, using tdb backend for attributes. ")
499 self.logger.info("not using extended attributes to store ACLs and other metadata. If you intend to use this provision in production, rerun the script as root on a system supporting xattrs.")
500 if ldap_backend_type == "existing":
501 if ldap_backend_forced_uri is not None:
502 self.logger.warn("You have specified to use an existing LDAP server as the backend, please make sure an LDAP server is running at %s" % ldap_backend_forced_uri)
504 self.logger.info("You have specified to use an existing LDAP server as the backend, please make sure an LDAP server is running at the default location")
506 if ldap_backend_forced_uri is not None:
507 self.logger.warn("You have specified to use an fixed URI %s for connecting to your LDAP server backend. This is NOT RECOMMENDED, as our default communiation over ldapi:// is more secure and much less")
509 if domain_sid is not None:
510 domain_sid = security.dom_sid(domain_sid)
512 session = system_session()
513 if backend_store is None:
514 backend_store = get_default_backend_store()
516 result = provision(self.logger,
517 session, smbconf=smbconf, targetdir=targetdir,
518 samdb_fill=samdb_fill, realm=realm, domain=domain,
519 domainguid=domain_guid, domainsid=domain_sid,
521 hostip=host_ip, hostip6=host_ip6,
522 sitename=site, ntdsguid=ntds_guid,
523 invocationid=invocationid, adminpass=adminpass,
524 krbtgtpass=krbtgtpass, machinepass=machinepass,
525 dns_backend=dns_backend, dns_forwarder=dns_forwarder,
526 dnspass=dnspass, root=root, nobody=nobody,
528 serverrole=server_role, dom_for_fun_level=dom_for_fun_level,
529 backend_type=ldap_backend_type,
530 ldapadminpass=ldapadminpass, ol_mmr_urls=ol_mmr_urls, slapd_path=slapd_path,
531 useeadb=eadb, next_rid=next_rid, lp=lp, use_ntvfs=use_ntvfs,
532 use_rfc2307=use_rfc2307, skip_sysvolacl=False,
533 ldap_backend_extra_port=ldap_backend_extra_port,
534 ldap_backend_forced_uri=ldap_backend_forced_uri,
535 nosync=ldap_backend_nosync, ldap_dryrun_mode=ldap_dryrun_mode,
536 base_schema=base_schema,
537 plaintext_secrets=plaintext_secrets,
538 backend_store=backend_store)
540 except ProvisioningError as e:
541 raise CommandError("Provision failed", e)
543 result.report_logger(self.logger)
545 def _get_nameserver_ip(self):
546 """Grab the nameserver IP address from /etc/resolv.conf."""
548 RESOLV_CONF = "/etc/resolv.conf"
550 if not path.isfile(RESOLV_CONF):
551 self.logger.warning("Failed to locate %s" % RESOLV_CONF)
556 handle = open(RESOLV_CONF, 'r')
558 if not line.startswith('nameserver'):
560 # we want the last non-space continuous string of the line
561 return line.strip().split()[-1]
563 if handle is not None:
566 self.logger.warning("No nameserver found in %s" % RESOLV_CONF)
568 def _adminpass_issue(self, adminpass):
569 """Returns error string for a bad administrator password,
570 or None if acceptable"""
572 if len(adminpass.decode('utf-8')) < DEFAULT_MIN_PWD_LENGTH:
573 return "Administrator password does not meet the default minimum" \
574 " password length requirement (%d characters)" \
575 % DEFAULT_MIN_PWD_LENGTH
576 elif not samba.check_password_quality(adminpass):
577 return "Administrator password does not meet the default" \
583 class cmd_domain_dcpromo(Command):
584 """Promote an existing domain member or NT4 PDC to an AD DC."""
586 synopsis = "%prog <dnsdomain> [DC|RODC] [options]"
588 takes_optiongroups = {
589 "sambaopts": options.SambaOptions,
590 "versionopts": options.VersionOptions,
591 "credopts": options.CredentialsOptions,
595 takes_options.extend(common_join_options)
597 takes_options.extend(common_provision_join_options)
599 if samba.is_ntvfs_fileserver_built():
600 takes_options.extend(common_ntvfs_options)
603 takes_args = ["domain", "role?"]
605 def run(self, domain, role=None, sambaopts=None, credopts=None,
606 versionopts=None, server=None, site=None, targetdir=None,
607 domain_critical_only=False, parent_domain=None, machinepass=None,
608 use_ntvfs=False, dns_backend=None,
609 quiet=False, verbose=False, plaintext_secrets=False,
611 lp = sambaopts.get_loadparm()
612 creds = credopts.get_credentials(lp)
613 net = Net(creds, lp, server=credopts.ipaddress)
615 logger = self.get_logger()
617 logger.setLevel(logging.DEBUG)
619 logger.setLevel(logging.WARNING)
621 logger.setLevel(logging.INFO)
623 netbios_name = lp.get("netbios name")
629 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
630 site=site, netbios_name=netbios_name, targetdir=targetdir,
631 domain_critical_only=domain_critical_only,
632 machinepass=machinepass, use_ntvfs=use_ntvfs,
633 dns_backend=dns_backend,
634 promote_existing=True, plaintext_secrets=plaintext_secrets,
635 backend_store=backend_store)
637 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
638 site=site, netbios_name=netbios_name, targetdir=targetdir,
639 domain_critical_only=domain_critical_only,
640 machinepass=machinepass, use_ntvfs=use_ntvfs, dns_backend=dns_backend,
641 promote_existing=True, plaintext_secrets=plaintext_secrets,
642 backend_store=backend_store)
644 raise CommandError("Invalid role '%s' (possible values: DC, RODC)" % role)
647 class cmd_domain_join(Command):
648 """Join domain as either member or backup domain controller."""
650 synopsis = "%prog <dnsdomain> [DC|RODC|MEMBER|SUBDOMAIN] [options]"
652 takes_optiongroups = {
653 "sambaopts": options.SambaOptions,
654 "versionopts": options.VersionOptions,
655 "credopts": options.CredentialsOptions,
659 Option("--parent-domain", help="parent domain to create subdomain under", type=str),
660 Option("--adminpass", type="string", metavar="PASSWORD",
661 help="choose adminstrator password when joining as a subdomain (otherwise random)"),
665 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
668 takes_options.extend(common_join_options)
669 takes_options.extend(common_provision_join_options)
671 if samba.is_ntvfs_fileserver_built():
672 takes_options.extend(ntvfs_options)
674 takes_args = ["domain", "role?"]
676 def run(self, domain, role=None, sambaopts=None, credopts=None,
677 versionopts=None, server=None, site=None, targetdir=None,
678 domain_critical_only=False, parent_domain=None, machinepass=None,
679 use_ntvfs=False, dns_backend=None, adminpass=None,
680 quiet=False, verbose=False,
681 plaintext_secrets=False,
683 lp = sambaopts.get_loadparm()
684 creds = credopts.get_credentials(lp)
685 net = Net(creds, lp, server=credopts.ipaddress)
688 site = "Default-First-Site-Name"
690 logger = self.get_logger()
692 logger.setLevel(logging.DEBUG)
694 logger.setLevel(logging.WARNING)
696 logger.setLevel(logging.INFO)
698 netbios_name = lp.get("netbios name")
703 if role is None or role == "MEMBER":
704 (join_password, sid, domain_name) = net.join_member(
705 domain, netbios_name, LIBNET_JOIN_AUTOMATIC,
706 machinepass=machinepass)
708 self.errf.write("Joined domain %s (%s)\n" % (domain_name, sid))
710 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
711 site=site, netbios_name=netbios_name, targetdir=targetdir,
712 domain_critical_only=domain_critical_only,
713 machinepass=machinepass, use_ntvfs=use_ntvfs,
714 dns_backend=dns_backend,
715 plaintext_secrets=plaintext_secrets,
716 backend_store=backend_store)
718 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
719 site=site, netbios_name=netbios_name, targetdir=targetdir,
720 domain_critical_only=domain_critical_only,
721 machinepass=machinepass, use_ntvfs=use_ntvfs,
722 dns_backend=dns_backend,
723 plaintext_secrets=plaintext_secrets,
724 backend_store=backend_store)
725 elif role == "SUBDOMAIN":
727 logger.info("Administrator password will be set randomly!")
729 netbios_domain = lp.get("workgroup")
730 if parent_domain is None:
731 parent_domain = ".".join(domain.split(".")[1:])
732 join_subdomain(logger=logger, server=server, creds=creds, lp=lp, dnsdomain=domain,
733 parent_domain=parent_domain, site=site,
734 netbios_name=netbios_name, netbios_domain=netbios_domain,
735 targetdir=targetdir, machinepass=machinepass,
736 use_ntvfs=use_ntvfs, dns_backend=dns_backend,
738 plaintext_secrets=plaintext_secrets,
739 backend_store=backend_store)
741 raise CommandError("Invalid role '%s' (possible values: MEMBER, DC, RODC, SUBDOMAIN)" % role)
744 class cmd_domain_demote(Command):
745 """Demote ourselves from the role of Domain Controller."""
747 synopsis = "%prog [options]"
750 Option("--server", help="writable DC to write demotion changes on", type=str),
751 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
752 metavar="URL", dest="H"),
753 Option("--remove-other-dead-server", help="Dead DC (name or NTDS GUID) "
754 "to remove ALL references to (rather than this DC)", type=str),
755 Option("-q", "--quiet", help="Be quiet", action="store_true"),
756 Option("-v", "--verbose", help="Be verbose", action="store_true"),
759 takes_optiongroups = {
760 "sambaopts": options.SambaOptions,
761 "credopts": options.CredentialsOptions,
762 "versionopts": options.VersionOptions,
765 def run(self, sambaopts=None, credopts=None,
766 versionopts=None, server=None,
767 remove_other_dead_server=None, H=None,
768 verbose=False, quiet=False):
769 lp = sambaopts.get_loadparm()
770 creds = credopts.get_credentials(lp)
771 net = Net(creds, lp, server=credopts.ipaddress)
773 logger = self.get_logger()
775 logger.setLevel(logging.DEBUG)
777 logger.setLevel(logging.WARNING)
779 logger.setLevel(logging.INFO)
781 if remove_other_dead_server is not None:
782 if server is not None:
783 samdb = SamDB(url="ldap://%s" % server,
784 session_info=system_session(),
785 credentials=creds, lp=lp)
787 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
789 remove_dc.remove_dc(samdb, logger, remove_other_dead_server)
790 except remove_dc.DemoteException as err:
791 raise CommandError("Demote failed: %s" % err)
794 netbios_name = lp.get("netbios name")
795 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
797 res = samdb.search(expression='(&(objectClass=computer)(serverReferenceBL=*))', attrs=["dnsHostName", "name"])
799 raise CommandError("Unable to search for servers")
802 raise CommandError("You are the last server in the domain")
806 if str(e["name"]).lower() != netbios_name.lower():
807 server = e["dnsHostName"]
810 ntds_guid = samdb.get_ntds_GUID()
811 msg = samdb.search(base=str(samdb.get_config_basedn()),
812 scope=ldb.SCOPE_SUBTREE, expression="(objectGUID=%s)" % ntds_guid,
814 if len(msg) == 0 or "options" not in msg[0]:
815 raise CommandError("Failed to find options on %s" % ntds_guid)
818 dsa_options = int(str(msg[0]['options']))
820 res = samdb.search(expression="(fSMORoleOwner=%s)" % str(ntds_dn),
821 controls=["search_options:1:2"])
824 raise CommandError("Current DC is still the owner of %d role(s), "
825 "use the role command to transfer roles to "
829 self.errf.write("Using %s as partner server for the demotion\n" %
831 (drsuapiBind, drsuapi_handle, supportedExtensions) = drsuapi_connect(server, lp, creds)
833 self.errf.write("Deactivating inbound replication\n")
838 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
839 dsa_options |= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
840 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
844 self.errf.write("Asking partner server %s to synchronize from us\n"
846 for part in (samdb.get_schema_basedn(),
847 samdb.get_config_basedn(),
848 samdb.get_root_basedn()):
849 nc = drsuapi.DsReplicaObjectIdentifier()
852 req1 = drsuapi.DsReplicaSyncRequest1()
853 req1.naming_context = nc;
854 req1.options = drsuapi.DRSUAPI_DRS_WRIT_REP
855 req1.source_dsa_guid = misc.GUID(ntds_guid)
858 drsuapiBind.DsReplicaSync(drsuapi_handle, 1, req1)
859 except RuntimeError as e1:
860 (werr, string) = e1.args
861 if werr == werror.WERR_DS_DRA_NO_REPLICA:
865 "Error while replicating out last local changes from '%s' for demotion, "
866 "re-enabling inbound replication\n" % part)
867 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
868 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
870 raise CommandError("Error while sending a DsReplicaSync for partition '%s'" % str(part), string)
872 remote_samdb = SamDB(url="ldap://%s" % server,
873 session_info=system_session(),
874 credentials=creds, lp=lp)
876 self.errf.write("Changing userControl and container\n")
877 res = remote_samdb.search(base=str(remote_samdb.domain_dn()),
878 expression="(&(objectClass=user)(sAMAccountName=%s$))" %
879 netbios_name.upper(),
880 attrs=["userAccountControl"])
882 uac = int(str(res[0]["userAccountControl"]))
884 except Exception as e:
885 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
887 "Error while demoting, re-enabling inbound replication\n")
888 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
889 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
891 raise CommandError("Error while changing account control", e)
894 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
896 "Error while demoting, re-enabling inbound replication")
897 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
898 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
900 raise CommandError("Unable to find object with samaccountName = %s$"
901 " in the remote dc" % netbios_name.upper())
905 uac &= ~(UF_SERVER_TRUST_ACCOUNT |UF_TRUSTED_FOR_DELEGATION |UF_PARTIAL_SECRETS_ACCOUNT)
906 uac |= UF_WORKSTATION_TRUST_ACCOUNT
911 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
912 ldb.FLAG_MOD_REPLACE,
913 "userAccountControl")
915 remote_samdb.modify(msg)
916 except Exception as e:
917 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
919 "Error while demoting, re-enabling inbound replication")
920 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
921 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
924 raise CommandError("Error while changing account control", e)
926 parent = msg.dn.parent()
927 dc_name = res[0].dn.get_rdn_value()
928 rdn = "CN=%s" % dc_name
930 # Let's move to the Computer container
934 computer_dn = ldb.Dn(remote_samdb, "CN=Computers,%s" % str(remote_samdb.domain_dn()))
935 res = remote_samdb.search(base=computer_dn, expression=rdn, scope=ldb.SCOPE_ONELEVEL)
938 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
939 scope=ldb.SCOPE_ONELEVEL)
940 while(len(res) != 0 and i < 100):
942 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
943 scope=ldb.SCOPE_ONELEVEL)
946 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
948 "Error while demoting, re-enabling inbound replication\n")
949 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
950 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
956 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
957 ldb.FLAG_MOD_REPLACE,
958 "userAccountControl")
960 remote_samdb.modify(msg)
962 raise CommandError("Unable to find a slot for renaming %s,"
963 " all names from %s-1 to %s-%d seemed used" %
964 (str(dc_dn), rdn, rdn, i - 9))
966 newrdn = "%s-%d" % (rdn, i)
969 newdn = ldb.Dn(remote_samdb, "%s,%s" % (newrdn, str(computer_dn)))
970 remote_samdb.rename(dc_dn, newdn)
971 except Exception as e:
972 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
974 "Error while demoting, re-enabling inbound replication\n")
975 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
976 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
982 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
983 ldb.FLAG_MOD_REPLACE,
984 "userAccountControl")
986 remote_samdb.modify(msg)
987 raise CommandError("Error while renaming %s to %s" % (str(dc_dn), str(newdn)), e)
990 server_dsa_dn = samdb.get_serverName()
991 domain = remote_samdb.get_root_basedn()
994 req1 = drsuapi.DsRemoveDSServerRequest1()
995 req1.server_dn = str(server_dsa_dn)
996 req1.domain_dn = str(domain)
999 drsuapiBind.DsRemoveDSServer(drsuapi_handle, 1, req1)
1000 except RuntimeError as e3:
1001 (werr, string) = e3.args
1002 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
1004 "Error while demoting, re-enabling inbound replication\n")
1005 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
1006 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
1012 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
1013 ldb.FLAG_MOD_REPLACE,
1014 "userAccountControl")
1015 remote_samdb.modify(msg)
1016 remote_samdb.rename(newdn, dc_dn)
1017 if werr == werror.WERR_DS_DRA_NO_REPLICA:
1018 raise CommandError("The DC %s is not present on (already "
1019 "removed from) the remote server: %s" %
1020 (server_dsa_dn, e3))
1022 raise CommandError("Error while sending a removeDsServer "
1024 (server_dsa_dn, e3))
1026 remove_dc.remove_sysvol_references(remote_samdb, logger, dc_name)
1028 # These are objects under the computer account that should be deleted
1029 for s in ("CN=Enterprise,CN=NTFRS Subscriptions",
1030 "CN=%s, CN=NTFRS Subscriptions" % lp.get("realm"),
1031 "CN=Domain system Volumes (SYSVOL Share), CN=NTFRS Subscriptions",
1032 "CN=NTFRS Subscriptions"):
1034 remote_samdb.delete(ldb.Dn(remote_samdb,
1035 "%s,%s" % (s, str(newdn))))
1036 except ldb.LdbError as l:
1039 # get dns host name for target server to demote, remove dns references
1040 remove_dc.remove_dns_references(remote_samdb, logger, samdb.host_dns_name(),
1041 ignore_no_name=True)
1043 self.errf.write("Demote successful\n")
1046 class cmd_domain_level(Command):
1047 """Raise domain and forest function levels."""
1049 synopsis = "%prog (show|raise <options>) [options]"
1051 takes_optiongroups = {
1052 "sambaopts": options.SambaOptions,
1053 "credopts": options.CredentialsOptions,
1054 "versionopts": options.VersionOptions,
1058 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1059 metavar="URL", dest="H"),
1060 Option("-q", "--quiet", help="Be quiet", action="store_true"), # unused
1061 Option("--forest-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
1062 help="The forest function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)"),
1063 Option("--domain-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
1064 help="The domain function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)")
1067 takes_args = ["subcommand"]
1069 def run(self, subcommand, H=None, forest_level=None, domain_level=None,
1070 quiet=False, credopts=None, sambaopts=None, versionopts=None):
1071 lp = sambaopts.get_loadparm()
1072 creds = credopts.get_credentials(lp, fallback_machine=True)
1074 samdb = SamDB(url=H, session_info=system_session(),
1075 credentials=creds, lp=lp)
1077 domain_dn = samdb.domain_dn()
1079 res_forest = samdb.search("CN=Partitions,%s" % samdb.get_config_basedn(),
1080 scope=ldb.SCOPE_BASE, attrs=["msDS-Behavior-Version"])
1081 assert len(res_forest) == 1
1083 res_domain = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1084 attrs=["msDS-Behavior-Version", "nTMixedDomain"])
1085 assert len(res_domain) == 1
1087 res_dc_s = samdb.search("CN=Sites,%s" % samdb.get_config_basedn(),
1088 scope=ldb.SCOPE_SUBTREE, expression="(objectClass=nTDSDSA)",
1089 attrs=["msDS-Behavior-Version"])
1090 assert len(res_dc_s) >= 1
1092 # default values, since "msDS-Behavior-Version" does not exist on Windows 2000 AD
1093 level_forest = DS_DOMAIN_FUNCTION_2000
1094 level_domain = DS_DOMAIN_FUNCTION_2000
1096 if "msDS-Behavior-Version" in res_forest[0]:
1097 level_forest = int(res_forest[0]["msDS-Behavior-Version"][0])
1098 if "msDS-Behavior-Version" in res_domain[0]:
1099 level_domain = int(res_domain[0]["msDS-Behavior-Version"][0])
1100 level_domain_mixed = int(res_domain[0]["nTMixedDomain"][0])
1103 for msg in res_dc_s:
1104 if "msDS-Behavior-Version" in msg:
1105 if min_level_dc is None or int(msg["msDS-Behavior-Version"][0]) < min_level_dc:
1106 min_level_dc = int(msg["msDS-Behavior-Version"][0])
1108 min_level_dc = DS_DOMAIN_FUNCTION_2000
1109 # well, this is the least
1112 if level_forest < DS_DOMAIN_FUNCTION_2000 or level_domain < DS_DOMAIN_FUNCTION_2000:
1113 raise CommandError("Domain and/or forest function level(s) is/are invalid. Correct them or reprovision!")
1114 if min_level_dc < DS_DOMAIN_FUNCTION_2000:
1115 raise CommandError("Lowest function level of a DC is invalid. Correct this or reprovision!")
1116 if level_forest > level_domain:
1117 raise CommandError("Forest function level is higher than the domain level(s). Correct this or reprovision!")
1118 if level_domain > min_level_dc:
1119 raise CommandError("Domain function level is higher than the lowest function level of a DC. Correct this or reprovision!")
1121 if subcommand == "show":
1122 self.message("Domain and forest function level for domain '%s'" % domain_dn)
1123 if level_forest == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1124 self.message("\nATTENTION: You run SAMBA 4 on a forest function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1125 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1126 self.message("\nATTENTION: You run SAMBA 4 on a domain function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1127 if min_level_dc == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1128 self.message("\nATTENTION: You run SAMBA 4 on a lowest function level of a DC lower than Windows 2003. This isn't supported! Please step-up or upgrade the concerning DC(s)!")
1132 if level_forest == DS_DOMAIN_FUNCTION_2000:
1134 elif level_forest == DS_DOMAIN_FUNCTION_2003_MIXED:
1135 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1136 elif level_forest == DS_DOMAIN_FUNCTION_2003:
1138 elif level_forest == DS_DOMAIN_FUNCTION_2008:
1140 elif level_forest == DS_DOMAIN_FUNCTION_2008_R2:
1142 elif level_forest == DS_DOMAIN_FUNCTION_2012:
1144 elif level_forest == DS_DOMAIN_FUNCTION_2012_R2:
1147 outstr = "higher than 2012 R2"
1148 self.message("Forest function level: (Windows) " + outstr)
1150 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1151 outstr = "2000 mixed (NT4 DC support)"
1152 elif level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed == 0:
1154 elif level_domain == DS_DOMAIN_FUNCTION_2003_MIXED:
1155 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1156 elif level_domain == DS_DOMAIN_FUNCTION_2003:
1158 elif level_domain == DS_DOMAIN_FUNCTION_2008:
1160 elif level_domain == DS_DOMAIN_FUNCTION_2008_R2:
1162 elif level_domain == DS_DOMAIN_FUNCTION_2012:
1164 elif level_domain == DS_DOMAIN_FUNCTION_2012_R2:
1167 outstr = "higher than 2012 R2"
1168 self.message("Domain function level: (Windows) " + outstr)
1170 if min_level_dc == DS_DOMAIN_FUNCTION_2000:
1172 elif min_level_dc == DS_DOMAIN_FUNCTION_2003:
1174 elif min_level_dc == DS_DOMAIN_FUNCTION_2008:
1176 elif min_level_dc == DS_DOMAIN_FUNCTION_2008_R2:
1178 elif min_level_dc == DS_DOMAIN_FUNCTION_2012:
1180 elif min_level_dc == DS_DOMAIN_FUNCTION_2012_R2:
1183 outstr = "higher than 2012 R2"
1184 self.message("Lowest function level of a DC: (Windows) " + outstr)
1186 elif subcommand == "raise":
1189 if domain_level is not None:
1190 if domain_level == "2003":
1191 new_level_domain = DS_DOMAIN_FUNCTION_2003
1192 elif domain_level == "2008":
1193 new_level_domain = DS_DOMAIN_FUNCTION_2008
1194 elif domain_level == "2008_R2":
1195 new_level_domain = DS_DOMAIN_FUNCTION_2008_R2
1196 elif domain_level == "2012":
1197 new_level_domain = DS_DOMAIN_FUNCTION_2012
1198 elif domain_level == "2012_R2":
1199 new_level_domain = DS_DOMAIN_FUNCTION_2012_R2
1201 if new_level_domain <= level_domain and level_domain_mixed == 0:
1202 raise CommandError("Domain function level can't be smaller than or equal to the actual one!")
1203 if new_level_domain > min_level_dc:
1204 raise CommandError("Domain function level can't be higher than the lowest function level of a DC!")
1206 # Deactivate mixed/interim domain support
1207 if level_domain_mixed != 0:
1208 # Directly on the base DN
1210 m.dn = ldb.Dn(samdb, domain_dn)
1211 m["nTMixedDomain"] = ldb.MessageElement("0",
1212 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1216 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup") + ",CN=Partitions,%s" % samdb.get_config_basedn())
1217 m["nTMixedDomain"] = ldb.MessageElement("0",
1218 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1221 except ldb.LdbError as e:
1222 (enum, emsg) = e.args
1223 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1226 # Directly on the base DN
1228 m.dn = ldb.Dn(samdb, domain_dn)
1229 m["msDS-Behavior-Version"] = ldb.MessageElement(
1230 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1231 "msDS-Behavior-Version")
1235 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup")
1236 + ",CN=Partitions,%s" % samdb.get_config_basedn())
1237 m["msDS-Behavior-Version"] = ldb.MessageElement(
1238 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1239 "msDS-Behavior-Version")
1242 except ldb.LdbError as e2:
1243 (enum, emsg) = e2.args
1244 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1247 level_domain = new_level_domain
1248 msgs.append("Domain function level changed!")
1250 if forest_level is not None:
1251 if forest_level == "2003":
1252 new_level_forest = DS_DOMAIN_FUNCTION_2003
1253 elif forest_level == "2008":
1254 new_level_forest = DS_DOMAIN_FUNCTION_2008
1255 elif forest_level == "2008_R2":
1256 new_level_forest = DS_DOMAIN_FUNCTION_2008_R2
1257 elif forest_level == "2012":
1258 new_level_forest = DS_DOMAIN_FUNCTION_2012
1259 elif forest_level == "2012_R2":
1260 new_level_forest = DS_DOMAIN_FUNCTION_2012_R2
1262 if new_level_forest <= level_forest:
1263 raise CommandError("Forest function level can't be smaller than or equal to the actual one!")
1264 if new_level_forest > level_domain:
1265 raise CommandError("Forest function level can't be higher than the domain function level(s). Please raise it/them first!")
1268 m.dn = ldb.Dn(samdb, "CN=Partitions,%s" % samdb.get_config_basedn())
1269 m["msDS-Behavior-Version"] = ldb.MessageElement(
1270 str(new_level_forest), ldb.FLAG_MOD_REPLACE,
1271 "msDS-Behavior-Version")
1273 msgs.append("Forest function level changed!")
1274 msgs.append("All changes applied successfully!")
1275 self.message("\n".join(msgs))
1277 raise CommandError("invalid argument: '%s' (choose from 'show', 'raise')" % subcommand)
1279 class cmd_domain_passwordsettings_show(Command):
1280 """Display current password settings for the domain."""
1282 synopsis = "%prog [options]"
1284 takes_optiongroups = {
1285 "sambaopts": options.SambaOptions,
1286 "versionopts": options.VersionOptions,
1287 "credopts": options.CredentialsOptions,
1291 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1292 metavar="URL", dest="H"),
1295 def run(self, H=None, credopts=None, sambaopts=None, versionopts=None):
1296 lp = sambaopts.get_loadparm()
1297 creds = credopts.get_credentials(lp)
1299 samdb = SamDB(url=H, session_info=system_session(),
1300 credentials=creds, lp=lp)
1302 domain_dn = samdb.domain_dn()
1303 res = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1304 attrs=["pwdProperties", "pwdHistoryLength", "minPwdLength",
1305 "minPwdAge", "maxPwdAge", "lockoutDuration", "lockoutThreshold",
1306 "lockOutObservationWindow"])
1307 assert(len(res) == 1)
1309 pwd_props = int(res[0]["pwdProperties"][0])
1310 pwd_hist_len = int(res[0]["pwdHistoryLength"][0])
1311 cur_min_pwd_len = int(res[0]["minPwdLength"][0])
1313 cur_min_pwd_age = int(abs(int(res[0]["minPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1314 if int(res[0]["maxPwdAge"][0]) == -0x8000000000000000:
1317 cur_max_pwd_age = int(abs(int(res[0]["maxPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1318 cur_account_lockout_threshold = int(res[0]["lockoutThreshold"][0])
1320 if int(res[0]["lockoutDuration"][0]) == -0x8000000000000000:
1321 cur_account_lockout_duration = 0
1323 cur_account_lockout_duration = abs(int(res[0]["lockoutDuration"][0])) / (1e7 * 60)
1324 cur_reset_account_lockout_after = abs(int(res[0]["lockOutObservationWindow"][0])) / (1e7 * 60)
1325 except Exception as e:
1326 raise CommandError("Could not retrieve password properties!", e)
1328 self.message("Password informations for domain '%s'" % domain_dn)
1330 if pwd_props & DOMAIN_PASSWORD_COMPLEX != 0:
1331 self.message("Password complexity: on")
1333 self.message("Password complexity: off")
1334 if pwd_props & DOMAIN_PASSWORD_STORE_CLEARTEXT != 0:
1335 self.message("Store plaintext passwords: on")
1337 self.message("Store plaintext passwords: off")
1338 self.message("Password history length: %d" % pwd_hist_len)
1339 self.message("Minimum password length: %d" % cur_min_pwd_len)
1340 self.message("Minimum password age (days): %d" % cur_min_pwd_age)
1341 self.message("Maximum password age (days): %d" % cur_max_pwd_age)
1342 self.message("Account lockout duration (mins): %d" % cur_account_lockout_duration)
1343 self.message("Account lockout threshold (attempts): %d" % cur_account_lockout_threshold)
1344 self.message("Reset account lockout after (mins): %d" % cur_reset_account_lockout_after)
1346 class cmd_domain_passwordsettings_set(Command):
1347 """Set password settings.
1349 Password complexity, password lockout policy, history length,
1350 minimum password length, the minimum and maximum password age) on
1351 a Samba AD DC server.
1353 Use against a Windows DC is possible, but group policy will override it.
1356 synopsis = "%prog <options> [options]"
1358 takes_optiongroups = {
1359 "sambaopts": options.SambaOptions,
1360 "versionopts": options.VersionOptions,
1361 "credopts": options.CredentialsOptions,
1365 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1366 metavar="URL", dest="H"),
1367 Option("-q", "--quiet", help="Be quiet", action="store_true"), # unused
1368 Option("--complexity", type="choice", choices=["on", "off", "default"],
1369 help="The password complexity (on | off | default). Default is 'on'"),
1370 Option("--store-plaintext", type="choice", choices=["on", "off", "default"],
1371 help="Store plaintext passwords where account have 'store passwords with reversible encryption' set (on | off | default). Default is 'off'"),
1372 Option("--history-length",
1373 help="The password history length (<integer> | default). Default is 24.", type=str),
1374 Option("--min-pwd-length",
1375 help="The minimum password length (<integer> | default). Default is 7.", type=str),
1376 Option("--min-pwd-age",
1377 help="The minimum password age (<integer in days> | default). Default is 1.", type=str),
1378 Option("--max-pwd-age",
1379 help="The maximum password age (<integer in days> | default). Default is 43.", type=str),
1380 Option("--account-lockout-duration",
1381 help="The the length of time an account is locked out after exeeding the limit on bad password attempts (<integer in mins> | default). Default is 30 mins.", type=str),
1382 Option("--account-lockout-threshold",
1383 help="The number of bad password attempts allowed before locking out the account (<integer> | default). Default is 0 (never lock out).", type=str),
1384 Option("--reset-account-lockout-after",
1385 help="After this time is elapsed, the recorded number of attempts restarts from zero (<integer> | default). Default is 30.", type=str),
1388 def run(self, H=None, min_pwd_age=None, max_pwd_age=None,
1389 quiet=False, complexity=None, store_plaintext=None, history_length=None,
1390 min_pwd_length=None, account_lockout_duration=None, account_lockout_threshold=None,
1391 reset_account_lockout_after=None, credopts=None, sambaopts=None,
1393 lp = sambaopts.get_loadparm()
1394 creds = credopts.get_credentials(lp)
1396 samdb = SamDB(url=H, session_info=system_session(),
1397 credentials=creds, lp=lp)
1399 domain_dn = samdb.domain_dn()
1402 m.dn = ldb.Dn(samdb, domain_dn)
1403 pwd_props = int(samdb.get_pwdProperties())
1405 if complexity is not None:
1406 if complexity == "on" or complexity == "default":
1407 pwd_props = pwd_props | DOMAIN_PASSWORD_COMPLEX
1408 msgs.append("Password complexity activated!")
1409 elif complexity == "off":
1410 pwd_props = pwd_props & (~DOMAIN_PASSWORD_COMPLEX)
1411 msgs.append("Password complexity deactivated!")
1413 if store_plaintext is not None:
1414 if store_plaintext == "on" or store_plaintext == "default":
1415 pwd_props = pwd_props | DOMAIN_PASSWORD_STORE_CLEARTEXT
1416 msgs.append("Plaintext password storage for changed passwords activated!")
1417 elif store_plaintext == "off":
1418 pwd_props = pwd_props & (~DOMAIN_PASSWORD_STORE_CLEARTEXT)
1419 msgs.append("Plaintext password storage for changed passwords deactivated!")
1421 if complexity is not None or store_plaintext is not None:
1422 m["pwdProperties"] = ldb.MessageElement(str(pwd_props),
1423 ldb.FLAG_MOD_REPLACE, "pwdProperties")
1425 if history_length is not None:
1426 if history_length == "default":
1429 pwd_hist_len = int(history_length)
1431 if pwd_hist_len < 0 or pwd_hist_len > 24:
1432 raise CommandError("Password history length must be in the range of 0 to 24!")
1434 m["pwdHistoryLength"] = ldb.MessageElement(str(pwd_hist_len),
1435 ldb.FLAG_MOD_REPLACE, "pwdHistoryLength")
1436 msgs.append("Password history length changed!")
1438 if min_pwd_length is not None:
1439 if min_pwd_length == "default":
1442 min_pwd_len = int(min_pwd_length)
1444 if min_pwd_len < 0 or min_pwd_len > 14:
1445 raise CommandError("Minimum password length must be in the range of 0 to 14!")
1447 m["minPwdLength"] = ldb.MessageElement(str(min_pwd_len),
1448 ldb.FLAG_MOD_REPLACE, "minPwdLength")
1449 msgs.append("Minimum password length changed!")
1451 if min_pwd_age is not None:
1452 if min_pwd_age == "default":
1455 min_pwd_age = int(min_pwd_age)
1457 if min_pwd_age < 0 or min_pwd_age > 998:
1458 raise CommandError("Minimum password age must be in the range of 0 to 998!")
1461 min_pwd_age_ticks = -int(min_pwd_age * (24 * 60 * 60 * 1e7))
1463 m["minPwdAge"] = ldb.MessageElement(str(min_pwd_age_ticks),
1464 ldb.FLAG_MOD_REPLACE, "minPwdAge")
1465 msgs.append("Minimum password age changed!")
1467 if max_pwd_age is not None:
1468 if max_pwd_age == "default":
1471 max_pwd_age = int(max_pwd_age)
1473 if max_pwd_age < 0 or max_pwd_age > 999:
1474 raise CommandError("Maximum password age must be in the range of 0 to 999!")
1477 if max_pwd_age == 0:
1478 max_pwd_age_ticks = -0x8000000000000000
1480 max_pwd_age_ticks = -int(max_pwd_age * (24 * 60 * 60 * 1e7))
1482 m["maxPwdAge"] = ldb.MessageElement(str(max_pwd_age_ticks),
1483 ldb.FLAG_MOD_REPLACE, "maxPwdAge")
1484 msgs.append("Maximum password age changed!")
1486 if account_lockout_duration is not None:
1487 if account_lockout_duration == "default":
1488 account_lockout_duration = 30
1490 account_lockout_duration = int(account_lockout_duration)
1492 if account_lockout_duration < 0 or account_lockout_duration > 99999:
1493 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1496 if account_lockout_duration == 0:
1497 account_lockout_duration_ticks = -0x8000000000000000
1499 account_lockout_duration_ticks = -int(account_lockout_duration * (60 * 1e7))
1501 m["lockoutDuration"] = ldb.MessageElement(str(account_lockout_duration_ticks),
1502 ldb.FLAG_MOD_REPLACE, "lockoutDuration")
1503 msgs.append("Account lockout duration changed!")
1505 if account_lockout_threshold is not None:
1506 if account_lockout_threshold == "default":
1507 account_lockout_threshold = 0
1509 account_lockout_threshold = int(account_lockout_threshold)
1511 m["lockoutThreshold"] = ldb.MessageElement(str(account_lockout_threshold),
1512 ldb.FLAG_MOD_REPLACE, "lockoutThreshold")
1513 msgs.append("Account lockout threshold changed!")
1515 if reset_account_lockout_after is not None:
1516 if reset_account_lockout_after == "default":
1517 reset_account_lockout_after = 30
1519 reset_account_lockout_after = int(reset_account_lockout_after)
1521 if reset_account_lockout_after < 0 or reset_account_lockout_after > 99999:
1522 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1525 if reset_account_lockout_after == 0:
1526 reset_account_lockout_after_ticks = -0x8000000000000000
1528 reset_account_lockout_after_ticks = -int(reset_account_lockout_after * (60 * 1e7))
1530 m["lockOutObservationWindow"] = ldb.MessageElement(str(reset_account_lockout_after_ticks),
1531 ldb.FLAG_MOD_REPLACE, "lockOutObservationWindow")
1532 msgs.append("Duration to reset account lockout after changed!")
1534 if max_pwd_age and max_pwd_age > 0 and min_pwd_age >= max_pwd_age:
1535 raise CommandError("Maximum password age (%d) must be greater than minimum password age (%d)!" % (max_pwd_age, min_pwd_age))
1538 raise CommandError("You must specify at least one option to set. Try --help")
1540 msgs.append("All changes applied successfully!")
1541 self.message("\n".join(msgs))
1543 class cmd_domain_passwordsettings(SuperCommand):
1544 """Manage password policy settings."""
1547 subcommands["pso"] = cmd_domain_passwordsettings_pso()
1548 subcommands["show"] = cmd_domain_passwordsettings_show()
1549 subcommands["set"] = cmd_domain_passwordsettings_set()
1551 class cmd_domain_classicupgrade(Command):
1552 """Upgrade from Samba classic (NT4-like) database to Samba AD DC database.
1554 Specify either a directory with all Samba classic DC databases and state files (with --dbdir) or
1555 the testparm utility from your classic installation (with --testparm).
1558 synopsis = "%prog [options] <classic_smb_conf>"
1560 takes_optiongroups = {
1561 "sambaopts": options.SambaOptions,
1562 "versionopts": options.VersionOptions
1566 Option("--dbdir", type="string", metavar="DIR",
1567 help="Path to samba classic DC database directory"),
1568 Option("--testparm", type="string", metavar="PATH",
1569 help="Path to samba classic DC testparm utility from the previous installation. This allows the default paths of the previous installation to be followed"),
1570 Option("--targetdir", type="string", metavar="DIR",
1571 help="Path prefix where the new Samba 4.0 AD domain should be initialised"),
1572 Option("-q", "--quiet", help="Be quiet", action="store_true"),
1573 Option("-v", "--verbose", help="Be verbose", action="store_true"),
1574 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
1575 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
1576 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
1577 "BIND9_FLATFILE uses bind9 text database to store zone information, "
1578 "BIND9_DLZ uses samba4 AD to store zone information, "
1579 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
1580 default="SAMBA_INTERNAL")
1584 Option("--use-xattrs", type="choice", choices=["yes", "no", "auto"],
1585 metavar="[yes|no|auto]",
1586 help="Define if we should use the native fs capabilities or a tdb file for "
1587 "storing attributes likes ntacl when --use-ntvfs is set. "
1588 "auto tries to make an inteligent guess based on the user rights and system capabilities",
1591 if samba.is_ntvfs_fileserver_built():
1592 takes_options.extend(common_ntvfs_options)
1593 takes_options.extend(ntvfs_options)
1595 takes_args = ["smbconf"]
1597 def run(self, smbconf=None, targetdir=None, dbdir=None, testparm=None,
1598 quiet=False, verbose=False, use_xattrs="auto", sambaopts=None, versionopts=None,
1599 dns_backend=None, use_ntvfs=False):
1601 if not os.path.exists(smbconf):
1602 raise CommandError("File %s does not exist" % smbconf)
1604 if testparm and not os.path.exists(testparm):
1605 raise CommandError("Testparm utility %s does not exist" % testparm)
1607 if dbdir and not os.path.exists(dbdir):
1608 raise CommandError("Directory %s does not exist" % dbdir)
1610 if not dbdir and not testparm:
1611 raise CommandError("Please specify either dbdir or testparm")
1613 logger = self.get_logger()
1615 logger.setLevel(logging.DEBUG)
1617 logger.setLevel(logging.WARNING)
1619 logger.setLevel(logging.INFO)
1621 if dbdir and testparm:
1622 logger.warning("both dbdir and testparm specified, ignoring dbdir.")
1625 lp = sambaopts.get_loadparm()
1627 s3conf = s3param.get_context()
1630 s3conf.set("realm", sambaopts.realm)
1632 if targetdir is not None:
1633 if not os.path.isdir(targetdir):
1637 if use_xattrs == "yes":
1639 elif use_xattrs == "auto" and use_ntvfs == False:
1641 elif use_ntvfs == False:
1642 raise CommandError("--use-xattrs=no requires --use-ntvfs (not supported for production use). "
1643 "Please re-run with --use-xattrs omitted.")
1644 elif use_xattrs == "auto" and not s3conf.get("posix:eadb"):
1646 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
1648 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
1651 samba.ntacls.setntacl(lp, tmpfile.name,
1652 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
1655 # FIXME: Don't catch all exceptions here
1656 logger.info("You are not root or your system does not support xattr, using tdb backend for attributes. "
1657 "If you intend to use this provision in production, rerun the script as root on a system supporting xattrs.")
1661 # Set correct default values from dbdir or testparm
1664 paths["state directory"] = dbdir
1665 paths["private dir"] = dbdir
1666 paths["lock directory"] = dbdir
1667 paths["smb passwd file"] = dbdir + "/smbpasswd"
1669 paths["state directory"] = get_testparm_var(testparm, smbconf, "state directory")
1670 paths["private dir"] = get_testparm_var(testparm, smbconf, "private dir")
1671 paths["smb passwd file"] = get_testparm_var(testparm, smbconf, "smb passwd file")
1672 paths["lock directory"] = get_testparm_var(testparm, smbconf, "lock directory")
1673 # "testparm" from Samba 3 < 3.4.x is not aware of the parameter
1674 # "state directory", instead make use of "lock directory"
1675 if len(paths["state directory"]) == 0:
1676 paths["state directory"] = paths["lock directory"]
1679 s3conf.set(p, paths[p])
1681 # load smb.conf parameters
1682 logger.info("Reading smb.conf")
1683 s3conf.load(smbconf)
1684 samba3 = Samba3(smbconf, s3conf)
1686 logger.info("Provisioning")
1687 upgrade_from_samba3(samba3, logger, targetdir, session_info=system_session(),
1688 useeadb=eadb, dns_backend=dns_backend, use_ntvfs=use_ntvfs)
1691 class cmd_domain_samba3upgrade(cmd_domain_classicupgrade):
1692 __doc__ = cmd_domain_classicupgrade.__doc__
1694 # This command is present for backwards compatibility only,
1695 # and should not be shown.
1699 class LocalDCCredentialsOptions(options.CredentialsOptions):
1700 def __init__(self, parser):
1701 options.CredentialsOptions.__init__(self, parser, special_name="local-dc")
1703 class DomainTrustCommand(Command):
1704 """List domain trusts."""
1707 Command.__init__(self)
1708 self.local_lp = None
1710 self.local_server = None
1711 self.local_binding_string = None
1712 self.local_creds = None
1714 self.remote_server = None
1715 self.remote_binding_string = None
1716 self.remote_creds = None
1718 def _uint32(self, v):
1719 return ctypes.c_uint32(v).value
1721 def check_runtime_error(self, runtime, val):
1725 err32 = self._uint32(runtime.args[0])
1731 class LocalRuntimeError(CommandError):
1732 def __init__(exception_self, self, runtime, message):
1733 err32 = self._uint32(runtime.args[0])
1734 errstr = runtime.args[1]
1735 msg = "LOCAL_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1736 self.local_server, message, err32, errstr)
1737 CommandError.__init__(exception_self, msg)
1739 class RemoteRuntimeError(CommandError):
1740 def __init__(exception_self, self, runtime, message):
1741 err32 = self._uint32(runtime.args[0])
1742 errstr = runtime.args[1]
1743 msg = "REMOTE_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1744 self.remote_server, message, err32, errstr)
1745 CommandError.__init__(exception_self, msg)
1747 class LocalLdbError(CommandError):
1748 def __init__(exception_self, self, ldb_error, message):
1749 errval = ldb_error.args[0]
1750 errstr = ldb_error.args[1]
1751 msg = "LOCAL_DC[%s]: %s - ERROR(%d) - %s" % (
1752 self.local_server, message, errval, errstr)
1753 CommandError.__init__(exception_self, msg)
1755 def setup_local_server(self, sambaopts, localdcopts):
1756 if self.local_server is not None:
1757 return self.local_server
1759 lp = sambaopts.get_loadparm()
1761 local_server = localdcopts.ipaddress
1762 if local_server is None:
1763 server_role = lp.server_role()
1764 if server_role != "ROLE_ACTIVE_DIRECTORY_DC":
1765 raise CommandError("Invalid server_role %s" % (server_role))
1766 local_server = lp.get('netbios name')
1767 local_transport = "ncalrpc"
1768 local_binding_options = ""
1769 local_binding_options += ",auth_type=ncalrpc_as_system"
1770 local_ldap_url = None
1773 local_transport = "ncacn_np"
1774 local_binding_options = ""
1775 local_ldap_url = "ldap://%s" % local_server
1776 local_creds = localdcopts.get_credentials(lp)
1780 self.local_server = local_server
1781 self.local_binding_string = "%s:%s[%s]" % (local_transport, local_server, local_binding_options)
1782 self.local_ldap_url = local_ldap_url
1783 self.local_creds = local_creds
1784 return self.local_server
1786 def new_local_lsa_connection(self):
1787 return lsa.lsarpc(self.local_binding_string, self.local_lp, self.local_creds)
1789 def new_local_netlogon_connection(self):
1790 return netlogon.netlogon(self.local_binding_string, self.local_lp, self.local_creds)
1792 def new_local_ldap_connection(self):
1793 return SamDB(url=self.local_ldap_url,
1794 session_info=system_session(),
1795 credentials=self.local_creds,
1798 def setup_remote_server(self, credopts, domain,
1800 require_writable=True):
1803 assert require_writable
1805 if self.remote_server is not None:
1806 return self.remote_server
1808 self.remote_server = "__unknown__remote_server__.%s" % domain
1809 assert self.local_server is not None
1811 remote_creds = credopts.get_credentials(self.local_lp)
1812 remote_server = credopts.ipaddress
1813 remote_binding_options = ""
1815 # TODO: we should also support NT4 domains
1816 # we could use local_netlogon.netr_DsRGetDCNameEx2() with the remote domain name
1817 # and delegate NBT or CLDAP to the local netlogon server
1819 remote_net = Net(remote_creds, self.local_lp, server=remote_server)
1820 remote_flags = nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS
1821 if require_writable:
1822 remote_flags |= nbt.NBT_SERVER_WRITABLE
1824 remote_flags |= nbt.NBT_SERVER_PDC
1825 remote_info = remote_net.finddc(flags=remote_flags, domain=domain, address=remote_server)
1826 except NTSTATUSError as error:
1827 raise CommandError("Failed to find a writeable DC for domain '%s': %s" %
1830 raise CommandError("Failed to find a writeable DC for domain '%s'" % domain)
1832 nbt.NBT_SERVER_PDC: "PDC",
1833 nbt.NBT_SERVER_GC: "GC",
1834 nbt.NBT_SERVER_LDAP: "LDAP",
1835 nbt.NBT_SERVER_DS: "DS",
1836 nbt.NBT_SERVER_KDC: "KDC",
1837 nbt.NBT_SERVER_TIMESERV: "TIMESERV",
1838 nbt.NBT_SERVER_CLOSEST: "CLOSEST",
1839 nbt.NBT_SERVER_WRITABLE: "WRITABLE",
1840 nbt.NBT_SERVER_GOOD_TIMESERV: "GOOD_TIMESERV",
1841 nbt.NBT_SERVER_NDNC: "NDNC",
1842 nbt.NBT_SERVER_SELECT_SECRET_DOMAIN_6: "SELECT_SECRET_DOMAIN_6",
1843 nbt.NBT_SERVER_FULL_SECRET_DOMAIN_6: "FULL_SECRET_DOMAIN_6",
1844 nbt.NBT_SERVER_ADS_WEB_SERVICE: "ADS_WEB_SERVICE",
1845 nbt.NBT_SERVER_DS_8: "DS_8",
1846 nbt.NBT_SERVER_HAS_DNS_NAME: "HAS_DNS_NAME",
1847 nbt.NBT_SERVER_IS_DEFAULT_NC: "IS_DEFAULT_NC",
1848 nbt.NBT_SERVER_FOREST_ROOT: "FOREST_ROOT",
1850 server_type_string = self.generic_bitmap_to_string(flag_map,
1851 remote_info.server_type, names_only=True)
1852 self.outf.write("RemoteDC Netbios[%s] DNS[%s] ServerType[%s]\n" % (
1853 remote_info.pdc_name,
1854 remote_info.pdc_dns_name,
1855 server_type_string))
1857 self.remote_server = remote_info.pdc_dns_name
1858 self.remote_binding_string = "ncacn_np:%s[%s]" % (self.remote_server, remote_binding_options)
1859 self.remote_creds = remote_creds
1860 return self.remote_server
1862 def new_remote_lsa_connection(self):
1863 return lsa.lsarpc(self.remote_binding_string, self.local_lp, self.remote_creds)
1865 def new_remote_netlogon_connection(self):
1866 return netlogon.netlogon(self.remote_binding_string, self.local_lp, self.remote_creds)
1868 def get_lsa_info(self, conn, policy_access):
1869 objectAttr = lsa.ObjectAttribute()
1870 objectAttr.sec_qos = lsa.QosInfo()
1872 policy = conn.OpenPolicy2(''.decode('utf-8'),
1873 objectAttr, policy_access)
1875 info = conn.QueryInfoPolicy2(policy, lsa.LSA_POLICY_INFO_DNS)
1877 return (policy, info)
1879 def get_netlogon_dc_unc(self, conn, server, domain):
1881 info = conn.netr_DsRGetDCNameEx2(server,
1882 None, 0, None, None, None,
1883 netlogon.DS_RETURN_DNS_NAME)
1885 except RuntimeError:
1886 return conn.netr_GetDcName(server, domain)
1888 def get_netlogon_dc_info(self, conn, server):
1889 info = conn.netr_DsRGetDCNameEx2(server,
1890 None, 0, None, None, None,
1891 netlogon.DS_RETURN_DNS_NAME)
1894 def netr_DomainTrust_to_name(self, t):
1895 if t.trust_type == lsa.LSA_TRUST_TYPE_DOWNLEVEL:
1896 return t.netbios_name
1900 def netr_DomainTrust_to_type(self, a, t):
1902 primary_parent = None
1904 if _t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
1906 if not _t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1907 primary_parent = a[_t.parent_index]
1910 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1911 if t is primary_parent:
1914 if t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1917 parent = a[t.parent_index]
1918 if parent is primary:
1923 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1928 def netr_DomainTrust_to_transitive(self, t):
1929 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1932 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE:
1935 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1940 def netr_DomainTrust_to_direction(self, t):
1941 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND and \
1942 t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1945 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND:
1948 if t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1953 def generic_enum_to_string(self, e_dict, v, names_only=False):
1957 v32 = self._uint32(v)
1958 w = "__unknown__%08X__" % v32
1960 r = "0x%x (%s)" % (v, w)
1963 def generic_bitmap_to_string(self, b_dict, v, names_only=False):
1968 for b in sorted(b_dict.keys()):
1975 c32 = self._uint32(c)
1976 s += ["__unknown_%08X__" % c32]
1981 r = "0x%x (%s)" % (v, w)
1984 def trustType_string(self, v):
1986 lsa.LSA_TRUST_TYPE_DOWNLEVEL: "DOWNLEVEL",
1987 lsa.LSA_TRUST_TYPE_UPLEVEL: "UPLEVEL",
1988 lsa.LSA_TRUST_TYPE_MIT: "MIT",
1989 lsa.LSA_TRUST_TYPE_DCE: "DCE",
1991 return self.generic_enum_to_string(types, v)
1993 def trustDirection_string(self, v):
1995 lsa.LSA_TRUST_DIRECTION_INBOUND |
1996 lsa.LSA_TRUST_DIRECTION_OUTBOUND: "BOTH",
1997 lsa.LSA_TRUST_DIRECTION_INBOUND: "INBOUND",
1998 lsa.LSA_TRUST_DIRECTION_OUTBOUND: "OUTBOUND",
2000 return self.generic_enum_to_string(directions, v)
2002 def trustAttributes_string(self, v):
2004 lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE: "NON_TRANSITIVE",
2005 lsa.LSA_TRUST_ATTRIBUTE_UPLEVEL_ONLY: "UPLEVEL_ONLY",
2006 lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN: "QUARANTINED_DOMAIN",
2007 lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE: "FOREST_TRANSITIVE",
2008 lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION: "CROSS_ORGANIZATION",
2009 lsa.LSA_TRUST_ATTRIBUTE_WITHIN_FOREST: "WITHIN_FOREST",
2010 lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL: "TREAT_AS_EXTERNAL",
2011 lsa.LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION: "USES_RC4_ENCRYPTION",
2013 return self.generic_bitmap_to_string(attributes, v)
2015 def kerb_EncTypes_string(self, v):
2017 security.KERB_ENCTYPE_DES_CBC_CRC: "DES_CBC_CRC",
2018 security.KERB_ENCTYPE_DES_CBC_MD5: "DES_CBC_MD5",
2019 security.KERB_ENCTYPE_RC4_HMAC_MD5: "RC4_HMAC_MD5",
2020 security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96: "AES128_CTS_HMAC_SHA1_96",
2021 security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96: "AES256_CTS_HMAC_SHA1_96",
2022 security.KERB_ENCTYPE_FAST_SUPPORTED: "FAST_SUPPORTED",
2023 security.KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED: "COMPOUND_IDENTITY_SUPPORTED",
2024 security.KERB_ENCTYPE_CLAIMS_SUPPORTED: "CLAIMS_SUPPORTED",
2025 security.KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED: "RESOURCE_SID_COMPRESSION_DISABLED",
2027 return self.generic_bitmap_to_string(enctypes, v)
2029 def entry_tln_status(self, e_flags, ):
2031 return "Status[Enabled]"
2034 lsa.LSA_TLN_DISABLED_NEW: "Disabled-New",
2035 lsa.LSA_TLN_DISABLED_ADMIN: "Disabled",
2036 lsa.LSA_TLN_DISABLED_CONFLICT: "Disabled-Conflicting",
2038 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
2040 def entry_dom_status(self, e_flags):
2042 return "Status[Enabled]"
2045 lsa.LSA_SID_DISABLED_ADMIN: "Disabled-SID",
2046 lsa.LSA_SID_DISABLED_CONFLICT: "Disabled-SID-Conflicting",
2047 lsa.LSA_NB_DISABLED_ADMIN: "Disabled-NB",
2048 lsa.LSA_NB_DISABLED_CONFLICT: "Disabled-NB-Conflicting",
2050 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
2052 def write_forest_trust_info(self, fti, tln=None, collisions=None):
2054 tln_string = " TDO[%s]" % tln
2058 self.outf.write("Namespaces[%d]%s:\n" % (
2059 len(fti.entries), tln_string))
2061 for i, e in enumerate(fti.entries):
2064 collision_string = ""
2066 if collisions is not None:
2067 for c in collisions.entries:
2071 collision_string = " Collision[%s]" % (c.name.string)
2073 d = e.forest_trust_data
2074 if e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
2075 self.outf.write("TLN: %-32s DNS[*.%s]%s\n" % (
2076 self.entry_tln_status(flags),
2077 d.string, collision_string))
2078 elif e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
2079 self.outf.write("TLN_EX: %-29s DNS[*.%s]\n" % (
2081 elif e.type == lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
2082 self.outf.write("DOM: %-32s DNS[%s] Netbios[%s] SID[%s]%s\n" % (
2083 self.entry_dom_status(flags),
2084 d.dns_domain_name.string,
2085 d.netbios_domain_name.string,
2086 d.domain_sid, collision_string))
2089 class cmd_domain_trust_list(DomainTrustCommand):
2090 """List domain trusts."""
2092 synopsis = "%prog [options]"
2094 takes_optiongroups = {
2095 "sambaopts": options.SambaOptions,
2096 "versionopts": options.VersionOptions,
2097 "localdcopts": LocalDCCredentialsOptions,
2103 def run(self, sambaopts=None, versionopts=None, localdcopts=None):
2105 local_server = self.setup_local_server(sambaopts, localdcopts)
2107 local_netlogon = self.new_local_netlogon_connection()
2108 except RuntimeError as error:
2109 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2112 local_netlogon_trusts = \
2113 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
2174 local_lsa.QueryTrustedDomainInfoByName(local_policy,
2176 lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2177 local_tdo_info = local_tdo_full.info_ex
2178 local_tdo_posix = local_tdo_full.posix_offset
2179 except NTSTATUSError as error:
2180 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2181 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2183 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(FULL_INFO) failed")
2186 local_tdo_enctypes = \
2187 local_lsa.QueryTrustedDomainInfoByName(local_policy,
2189 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES)
2190 except NTSTATUSError as error:
2191 if self.check_runtime_error(error, ntstatus.NT_STATUS_INVALID_PARAMETER):
2193 if self.check_runtime_error(error, ntstatus.NT_STATUS_INVALID_INFO_CLASS):
2196 if error is not None:
2197 raise self.LocalRuntimeError(self, error,
2198 "QueryTrustedDomainInfoByName(SUPPORTED_ENCRYPTION_TYPES) failed")
2200 local_tdo_enctypes = lsa.TrustDomainInfoSupportedEncTypes()
2201 local_tdo_enctypes.enc_types = 0
2204 local_tdo_forest = None
2205 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2206 local_tdo_forest = \
2207 local_lsa.lsaRQueryForestTrustInformation(local_policy,
2209 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
2210 except RuntimeError as error:
2211 if self.check_runtime_error(error, ntstatus.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
2213 if self.check_runtime_error(error, ntstatus.NT_STATUS_NOT_FOUND):
2215 if error is not None:
2216 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation failed")
2218 local_tdo_forest = lsa.ForestTrustInformation()
2219 local_tdo_forest.count = 0
2220 local_tdo_forest.entries = []
2222 self.outf.write("TrustedDomain:\n\n");
2223 self.outf.write("NetbiosName: %s\n" % local_tdo_info.netbios_name.string)
2224 if local_tdo_info.netbios_name.string != local_tdo_info.domain_name.string:
2225 self.outf.write("DnsName: %s\n" % local_tdo_info.domain_name.string)
2226 self.outf.write("SID: %s\n" % local_tdo_info.sid)
2227 self.outf.write("Type: %s\n" % self.trustType_string(local_tdo_info.trust_type))
2228 self.outf.write("Direction: %s\n" % self.trustDirection_string(local_tdo_info.trust_direction))
2229 self.outf.write("Attributes: %s\n" % self.trustAttributes_string(local_tdo_info.trust_attributes))
2230 posix_offset_u32 = ctypes.c_uint32(local_tdo_posix.posix_offset).value
2231 posix_offset_i32 = ctypes.c_int32(local_tdo_posix.posix_offset).value
2232 self.outf.write("PosixOffset: 0x%08X (%d)\n" % (posix_offset_u32, posix_offset_i32))
2233 self.outf.write("kerb_EncTypes: %s\n" % self.kerb_EncTypes_string(local_tdo_enctypes.enc_types))
2235 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2236 self.write_forest_trust_info(local_tdo_forest,
2237 tln=local_tdo_info.domain_name.string)
2241 class cmd_domain_trust_create(DomainTrustCommand):
2242 """Create a domain or forest trust."""
2244 synopsis = "%prog DOMAIN [options]"
2246 takes_optiongroups = {
2247 "sambaopts": options.SambaOptions,
2248 "versionopts": options.VersionOptions,
2249 "credopts": options.CredentialsOptions,
2250 "localdcopts": LocalDCCredentialsOptions,
2254 Option("--type", type="choice", metavar="TYPE",
2255 choices=["external", "forest"],
2256 help="The type of the trust: 'external' or 'forest'.",
2258 default="external"),
2259 Option("--direction", type="choice", metavar="DIRECTION",
2260 choices=["incoming", "outgoing", "both"],
2261 help="The trust direction: 'incoming', 'outgoing' or 'both'.",
2262 dest='trust_direction',
2264 Option("--create-location", type="choice", metavar="LOCATION",
2265 choices=["local", "both"],
2266 help="Where to create the trusted domain object: 'local' or 'both'.",
2267 dest='create_location',
2269 Option("--cross-organisation", action="store_true",
2270 help="The related domains does not belong to the same organisation.",
2271 dest='cross_organisation',
2273 Option("--quarantined", type="choice", metavar="yes|no",
2274 choices=["yes", "no", None],
2275 help="Special SID filtering rules are applied to the trust. "
2276 "With --type=external the default is yes. "
2277 "With --type=forest the default is no.",
2278 dest='quarantined_arg',
2280 Option("--not-transitive", action="store_true",
2281 help="The forest trust is not transitive.",
2282 dest='not_transitive',
2284 Option("--treat-as-external", action="store_true",
2285 help="The treat the forest trust as external.",
2286 dest='treat_as_external',
2288 Option("--no-aes-keys", action="store_false",
2289 help="The trust uses aes kerberos keys.",
2290 dest='use_aes_keys',
2292 Option("--skip-validation", action="store_false",
2293 help="Skip validation of the trust.",
2298 takes_args = ["domain"]
2300 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2301 trust_type=None, trust_direction=None, create_location=None,
2302 cross_organisation=False, quarantined_arg=None,
2303 not_transitive=False, treat_as_external=False,
2304 use_aes_keys=False, validate=True):
2306 lsaString = lsa.String()
2309 if quarantined_arg is None:
2310 if trust_type == 'external':
2312 elif quarantined_arg == 'yes':
2315 if trust_type != 'forest':
2317 raise CommandError("--not-transitive requires --type=forest")
2318 if treat_as_external:
2319 raise CommandError("--treat-as-external requires --type=forest")
2323 enc_types = lsa.TrustDomainInfoSupportedEncTypes()
2324 enc_types.enc_types = security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96
2325 enc_types.enc_types |= security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96
2327 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2328 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2329 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2331 local_trust_info = lsa.TrustDomainInfoInfoEx()
2332 local_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2333 local_trust_info.trust_direction = 0
2334 if trust_direction == "both":
2335 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2336 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2337 elif trust_direction == "incoming":
2338 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2339 elif trust_direction == "outgoing":
2340 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2341 local_trust_info.trust_attributes = 0
2342 if cross_organisation:
2343 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2345 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2346 if trust_type == "forest":
2347 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2349 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2350 if treat_as_external:
2351 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2353 def get_password(name):
2356 if password is not None and password is not '':
2358 password = getpass("New %s Password: " % name)
2359 passwordverify = getpass("Retype %s Password: " % name)
2360 if not password == passwordverify:
2362 self.outf.write("Sorry, passwords do not match.\n")
2364 incoming_secret = None
2365 outgoing_secret = None
2366 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2367 if create_location == "local":
2368 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2369 incoming_password = get_password("Incoming Trust")
2370 incoming_secret = string_to_byte_array(incoming_password.encode('utf-16-le'))
2371 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2372 outgoing_password = get_password("Outgoing Trust")
2373 outgoing_secret = string_to_byte_array(outgoing_password.encode('utf-16-le'))
2375 remote_trust_info = None
2377 # We use 240 random bytes.
2378 # Windows uses 28 or 240 random bytes. I guess it's
2379 # based on the trust type external vs. forest.
2381 # The initial trust password can be up to 512 bytes
2382 # while the versioned passwords used for periodic updates
2383 # can only be up to 498 bytes, as netr_ServerPasswordSet2()
2384 # needs to pass the NL_PASSWORD_VERSION structure within the
2385 # 512 bytes and a 2 bytes confounder is required.
2387 def random_trust_secret(length):
2388 pw = samba.generate_random_machine_password(length // 2, length // 2)
2389 return string_to_byte_array(pw.encode('utf-16-le'))
2391 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2392 incoming_secret = random_trust_secret(240)
2393 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2394 outgoing_secret = random_trust_secret(240)
2396 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2397 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2399 remote_trust_info = lsa.TrustDomainInfoInfoEx()
2400 remote_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2401 remote_trust_info.trust_direction = 0
2402 if trust_direction == "both":
2403 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2404 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2405 elif trust_direction == "incoming":
2406 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2407 elif trust_direction == "outgoing":
2408 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2409 remote_trust_info.trust_attributes = 0
2410 if cross_organisation:
2411 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2413 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2414 if trust_type == "forest":
2415 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2417 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2418 if treat_as_external:
2419 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2421 local_server = self.setup_local_server(sambaopts, localdcopts)
2423 local_lsa = self.new_local_lsa_connection()
2424 except RuntimeError as error:
2425 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2428 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2429 except RuntimeError as error:
2430 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2432 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2433 local_lsa_info.name.string,
2434 local_lsa_info.dns_domain.string,
2435 local_lsa_info.sid))
2438 remote_server = self.setup_remote_server(credopts, domain)
2439 except RuntimeError as error:
2440 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2443 remote_lsa = self.new_remote_lsa_connection()
2444 except RuntimeError as error:
2445 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2448 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2449 except RuntimeError as error:
2450 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2452 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2453 remote_lsa_info.name.string,
2454 remote_lsa_info.dns_domain.string,
2455 remote_lsa_info.sid))
2457 local_trust_info.domain_name.string = remote_lsa_info.dns_domain.string
2458 local_trust_info.netbios_name.string = remote_lsa_info.name.string
2459 local_trust_info.sid = remote_lsa_info.sid
2461 if remote_trust_info:
2462 remote_trust_info.domain_name.string = local_lsa_info.dns_domain.string
2463 remote_trust_info.netbios_name.string = local_lsa_info.name.string
2464 remote_trust_info.sid = local_lsa_info.sid
2467 lsaString.string = local_trust_info.domain_name.string
2468 local_old_netbios = \
2469 local_lsa.QueryTrustedDomainInfoByName(local_policy,
2471 lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2472 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2473 except NTSTATUSError as error:
2474 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2475 raise self.LocalRuntimeError(self, error,
2476 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2480 lsaString.string = local_trust_info.netbios_name.string
2482 local_lsa.QueryTrustedDomainInfoByName(local_policy,
2484 lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2485 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2486 except NTSTATUSError as error:
2487 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2488 raise self.LocalRuntimeError(self, error,
2489 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2492 if remote_trust_info:
2494 lsaString.string = remote_trust_info.domain_name.string
2495 remote_old_netbios = \
2496 remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2498 lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2499 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2500 except NTSTATUSError as error:
2501 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2502 raise self.RemoteRuntimeError(self, error,
2503 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2507 lsaString.string = remote_trust_info.netbios_name.string
2509 remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2511 lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2512 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2513 except NTSTATUSError as error:
2514 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2515 raise self.RemoteRuntimeError(self, error,
2516 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2520 local_netlogon = self.new_local_netlogon_connection()
2521 except RuntimeError as error:
2522 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2525 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
2526 except RuntimeError as error:
2527 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
2529 if remote_trust_info:
2531 remote_netlogon = self.new_remote_netlogon_connection()
2532 except RuntimeError as error:
2533 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2536 remote_netlogon_dc_unc = self.get_netlogon_dc_unc(remote_netlogon,
2537 remote_server, domain)
2538 except RuntimeError as error:
2539 raise self.RemoteRuntimeError(self, error, "failed to get netlogon dc info")
2541 def generate_AuthInOutBlob(secret, update_time):
2543 blob = drsblobs.trustAuthInOutBlob()
2548 clear = drsblobs.AuthInfoClear()
2549 clear.size = len(secret)
2550 clear.password = secret
2552 info = drsblobs.AuthenticationInformation()
2553 info.LastUpdateTime = samba.unix2nttime(update_time)
2554 info.AuthType = lsa.TRUST_AUTH_TYPE_CLEAR
2555 info.AuthInfo = clear
2557 array = drsblobs.AuthenticationInformationArray()
2559 array.array = [info]
2561 blob = drsblobs.trustAuthInOutBlob()
2563 blob.current = array
2567 def generate_AuthInfoInternal(session_key, incoming=None, outgoing=None):
2568 confounder = [0] * 512
2569 for i in range(len(confounder)):
2570 confounder[i] = random.randint(0, 255)
2572 trustpass = drsblobs.trustDomainPasswords()
2574 trustpass.confounder = confounder
2575 trustpass.outgoing = outgoing
2576 trustpass.incoming = incoming
2578 trustpass_blob = ndr_pack(trustpass)
2580 encrypted_trustpass = arcfour_encrypt(session_key, trustpass_blob)
2582 auth_blob = lsa.DATA_BUF2()
2583 auth_blob.size = len(encrypted_trustpass)
2584 auth_blob.data = string_to_byte_array(encrypted_trustpass)
2586 auth_info = lsa.TrustDomainInfoAuthInfoInternal()
2587 auth_info.auth_blob = auth_blob
2591 update_time = samba.current_unix_time()
2592 incoming_blob = generate_AuthInOutBlob(incoming_secret, update_time)
2593 outgoing_blob = generate_AuthInOutBlob(outgoing_secret, update_time)
2595 local_tdo_handle = None
2596 remote_tdo_handle = None
2598 local_auth_info = generate_AuthInfoInternal(local_lsa.session_key,
2599 incoming=incoming_blob,
2600 outgoing=outgoing_blob)
2601 if remote_trust_info:
2602 remote_auth_info = generate_AuthInfoInternal(remote_lsa.session_key,
2603 incoming=outgoing_blob,
2604 outgoing=incoming_blob)
2607 if remote_trust_info:
2608 self.outf.write("Creating remote TDO.\n")
2609 current_request = {"location": "remote", "name": "CreateTrustedDomainEx2"}
2610 remote_tdo_handle = \
2611 remote_lsa.CreateTrustedDomainEx2(remote_policy,
2614 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2615 self.outf.write("Remote TDO created.\n")
2617 self.outf.write("Setting supported encryption types on remote TDO.\n")
2618 current_request = {"location": "remote", "name": "SetInformationTrustedDomain"}
2619 remote_lsa.SetInformationTrustedDomain(remote_tdo_handle,
2620 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2623 self.outf.write("Creating local TDO.\n")
2624 current_request = {"location": "local", "name": "CreateTrustedDomainEx2"}
2625 local_tdo_handle = local_lsa.CreateTrustedDomainEx2(local_policy,
2628 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2629 self.outf.write("Local TDO created\n")
2631 self.outf.write("Setting supported encryption types on local TDO.\n")
2632 current_request = {"location": "local", "name": "SetInformationTrustedDomain"}
2633 local_lsa.SetInformationTrustedDomain(local_tdo_handle,
2634 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2636 except RuntimeError as error:
2637 self.outf.write("Error: %s failed %sly - cleaning up\n" % (
2638 current_request['name'], current_request['location']))
2639 if remote_tdo_handle:
2640 self.outf.write("Deleting remote TDO.\n")
2641 remote_lsa.DeleteObject(remote_tdo_handle)
2642 remote_tdo_handle = None
2643 if local_tdo_handle:
2644 self.outf.write("Deleting local TDO.\n")
2645 local_lsa.DeleteObject(local_tdo_handle)
2646 local_tdo_handle = None
2647 if current_request['location'] is "remote":
2648 raise self.RemoteRuntimeError(self, error, "%s" % (
2649 current_request['name']))
2650 raise self.LocalRuntimeError(self, error, "%s" % (
2651 current_request['name']))
2654 if local_trust_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2655 self.outf.write("Setup local forest trust information...\n")
2657 # get all information about the remote trust
2658 # this triggers netr_GetForestTrustInformation to the remote domain
2659 # and lsaRSetForestTrustInformation() locally, but new top level
2660 # names are disabled by default.
2661 local_forest_info = \
2662 local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
2663 remote_lsa_info.dns_domain.string,
2664 netlogon.DS_GFTI_UPDATE_TDO)
2665 except RuntimeError as error:
2666 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2669 # here we try to enable all top level names
2670 local_forest_collision = \
2671 local_lsa.lsaRSetForestTrustInformation(local_policy,
2672 remote_lsa_info.dns_domain,
2673 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2676 except RuntimeError as error:
2677 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2679 self.write_forest_trust_info(local_forest_info,
2680 tln=remote_lsa_info.dns_domain.string,
2681 collisions=local_forest_collision)
2683 if remote_trust_info:
2684 self.outf.write("Setup remote forest trust information...\n")
2686 # get all information about the local trust (from the perspective of the remote domain)
2687 # this triggers netr_GetForestTrustInformation to our domain.
2688 # and lsaRSetForestTrustInformation() remotely, but new top level
2689 # names are disabled by default.
2690 remote_forest_info = \
2691 remote_netlogon.netr_DsRGetForestTrustInformation(remote_netlogon_dc_unc,
2692 local_lsa_info.dns_domain.string,
2693 netlogon.DS_GFTI_UPDATE_TDO)
2694 except RuntimeError as error:
2695 raise self.RemoteRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2698 # here we try to enable all top level names
2699 remote_forest_collision = \
2700 remote_lsa.lsaRSetForestTrustInformation(remote_policy,
2701 local_lsa_info.dns_domain,
2702 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2705 except RuntimeError as error:
2706 raise self.RemoteRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2708 self.write_forest_trust_info(remote_forest_info,
2709 tln=local_lsa_info.dns_domain.string,
2710 collisions=remote_forest_collision)
2712 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2713 self.outf.write("Validating outgoing trust...\n")
2715 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_netlogon_info.dc_unc,
2716 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2718 remote_lsa_info.dns_domain.string)
2719 except RuntimeError as error:
2720 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2722 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2723 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2725 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2726 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2727 local_trust_verify.trusted_dc_name,
2728 local_trust_verify.tc_connection_status[1],
2729 local_trust_verify.pdc_connection_status[1])
2731 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2732 local_trust_verify.trusted_dc_name,
2733 local_trust_verify.tc_connection_status[1],
2734 local_trust_verify.pdc_connection_status[1])
2736 if local_trust_status != werror.WERR_SUCCESS or local_conn_status != werror.WERR_SUCCESS:
2737 raise CommandError(local_validation)
2739 self.outf.write("OK: %s\n" % local_validation)
2741 if remote_trust_info:
2742 if remote_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2743 self.outf.write("Validating incoming trust...\n")
2745 remote_trust_verify = \
2746 remote_netlogon.netr_LogonControl2Ex(remote_netlogon_dc_unc,
2747 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2749 local_lsa_info.dns_domain.string)
2750 except RuntimeError as error:
2751 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2753 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2754 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2756 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2757 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2758 remote_trust_verify.trusted_dc_name,
2759 remote_trust_verify.tc_connection_status[1],
2760 remote_trust_verify.pdc_connection_status[1])
2762 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2763 remote_trust_verify.trusted_dc_name,
2764 remote_trust_verify.tc_connection_status[1],
2765 remote_trust_verify.pdc_connection_status[1])
2767 if remote_trust_status != werror.WERR_SUCCESS or remote_conn_status != werror.WERR_SUCCESS:
2768 raise CommandError(remote_validation)
2770 self.outf.write("OK: %s\n" % remote_validation)
2772 if remote_tdo_handle is not None:
2774 remote_lsa.Close(remote_tdo_handle)
2775 except RuntimeError as error:
2777 remote_tdo_handle = None
2778 if local_tdo_handle is not None:
2780 local_lsa.Close(local_tdo_handle)
2781 except RuntimeError as error:
2783 local_tdo_handle = None
2785 self.outf.write("Success.\n")
2788 class cmd_domain_trust_delete(DomainTrustCommand):
2789 """Delete a domain trust."""
2791 synopsis = "%prog DOMAIN [options]"
2793 takes_optiongroups = {
2794 "sambaopts": options.SambaOptions,
2795 "versionopts": options.VersionOptions,
2796 "credopts": options.CredentialsOptions,
2797 "localdcopts": LocalDCCredentialsOptions,
2801 Option("--delete-location", type="choice", metavar="LOCATION",
2802 choices=["local", "both"],
2803 help="Where to delete the trusted domain object: 'local' or 'both'.",
2804 dest='delete_location',
2808 takes_args = ["domain"]
2810 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2811 delete_location=None):
2813 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2814 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2815 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2817 if delete_location == "local":
2818 remote_policy_access = None
2820 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2821 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2822 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2824 local_server = self.setup_local_server(sambaopts, localdcopts)
2826 local_lsa = self.new_local_lsa_connection()
2827 except RuntimeError as error:
2828 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2831 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2832 except RuntimeError as error:
2833 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2835 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2836 local_lsa_info.name.string,
2837 local_lsa_info.dns_domain.string,
2838 local_lsa_info.sid))
2840 local_tdo_info = None
2841 local_tdo_handle = None
2842 remote_tdo_info = None
2843 remote_tdo_handle = None
2845 lsaString = lsa.String()
2847 lsaString.string = domain
2848 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2849 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2850 except NTSTATUSError as error:
2851 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2852 raise CommandError("Failed to find trust for domain '%s'" % domain)
2853 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2856 if remote_policy_access is not None:
2858 remote_server = self.setup_remote_server(credopts, domain)
2859 except RuntimeError as error:
2860 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2863 remote_lsa = self.new_remote_lsa_connection()
2864 except RuntimeError as error:
2865 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2868 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2869 except RuntimeError as error:
2870 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2872 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2873 remote_lsa_info.name.string,
2874 remote_lsa_info.dns_domain.string,
2875 remote_lsa_info.sid))
2877 if remote_lsa_info.sid != local_tdo_info.sid or \
2878 remote_lsa_info.name.string != local_tdo_info.netbios_name.string or \
2879 remote_lsa_info.dns_domain.string != local_tdo_info.domain_name.string:
2880 raise CommandError("LocalTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2881 local_tdo_info.netbios_name.string,
2882 local_tdo_info.domain_name.string,
2883 local_tdo_info.sid))
2886 lsaString.string = local_lsa_info.dns_domain.string
2888 remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2890 lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2891 except NTSTATUSError as error:
2892 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2893 raise self.RemoteRuntimeError(self, error, "QueryTrustedDomainInfoByName(%s)" % (
2897 if remote_tdo_info is not None:
2898 if local_lsa_info.sid != remote_tdo_info.sid or \
2899 local_lsa_info.name.string != remote_tdo_info.netbios_name.string or \
2900 local_lsa_info.dns_domain.string != remote_tdo_info.domain_name.string:
2901 raise CommandError("RemoteTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2902 remote_tdo_info.netbios_name.string,
2903 remote_tdo_info.domain_name.string,
2904 remote_tdo_info.sid))
2906 if local_tdo_info is not None:
2908 lsaString.string = local_tdo_info.domain_name.string
2909 local_tdo_handle = \
2910 local_lsa.OpenTrustedDomainByName(local_policy,
2912 security.SEC_STD_DELETE)
2913 except RuntimeError as error:
2914 raise self.LocalRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2917 local_lsa.DeleteObject(local_tdo_handle)
2918 local_tdo_handle = None
2920 if remote_tdo_info is not None:
2922 lsaString.string = remote_tdo_info.domain_name.string
2923 remote_tdo_handle = \
2924 remote_lsa.OpenTrustedDomainByName(remote_policy,
2926 security.SEC_STD_DELETE)
2927 except RuntimeError as error:
2928 raise self.RemoteRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2931 if remote_tdo_handle is not None:
2933 remote_lsa.DeleteObject(remote_tdo_handle)
2934 remote_tdo_handle = None
2935 self.outf.write("RemoteTDO deleted.\n")
2936 except RuntimeError as error:
2937 self.outf.write("%s\n" % self.RemoteRuntimeError(self, error, "DeleteObject() failed"))
2939 if local_tdo_handle is not None:
2941 local_lsa.DeleteObject(local_tdo_handle)
2942 local_tdo_handle = None
2943 self.outf.write("LocalTDO deleted.\n")
2944 except RuntimeError as error:
2945 self.outf.write("%s\n" % self.LocalRuntimeError(self, error, "DeleteObject() failed"))
2949 class cmd_domain_trust_validate(DomainTrustCommand):
2950 """Validate a domain trust."""
2952 synopsis = "%prog DOMAIN [options]"
2954 takes_optiongroups = {
2955 "sambaopts": options.SambaOptions,
2956 "versionopts": options.VersionOptions,
2957 "credopts": options.CredentialsOptions,
2958 "localdcopts": LocalDCCredentialsOptions,
2962 Option("--validate-location", type="choice", metavar="LOCATION",
2963 choices=["local", "both"],
2964 help="Where to validate the trusted domain object: 'local' or 'both'.",
2965 dest='validate_location',
2969 takes_args = ["domain"]
2971 def run(self, domain, sambaopts=None, versionopts=None, credopts=None, localdcopts=None,
2972 validate_location=None):
2974 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2976 local_server = self.setup_local_server(sambaopts, localdcopts)
2978 local_lsa = self.new_local_lsa_connection()
2979 except RuntimeError as error:
2980 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2983 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2984 except RuntimeError as error:
2985 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2987 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2988 local_lsa_info.name.string,
2989 local_lsa_info.dns_domain.string,
2990 local_lsa_info.sid))
2993 lsaString = lsa.String()
2994 lsaString.string = domain
2996 local_lsa.QueryTrustedDomainInfoByName(local_policy,
2998 lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2999 except NTSTATUSError as error:
3000 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
3001 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
3003 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
3005 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
3006 local_tdo_info.netbios_name.string,
3007 local_tdo_info.domain_name.string,
3008 local_tdo_info.sid))
3011 local_netlogon = self.new_local_netlogon_connection()
3012 except RuntimeError as error:
3013 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3016 local_trust_verify = \
3017 local_netlogon.netr_LogonControl2Ex(local_server,
3018 netlogon.NETLOGON_CONTROL_TC_VERIFY,
3020 local_tdo_info.domain_name.string)
3021 except RuntimeError as error:
3022 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
3024 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
3025 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
3027 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
3028 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
3029 local_trust_verify.trusted_dc_name,
3030 local_trust_verify.tc_connection_status[1],
3031 local_trust_verify.pdc_connection_status[1])
3033 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
3034 local_trust_verify.trusted_dc_name,
3035 local_trust_verify.tc_connection_status[1],
3036 local_trust_verify.pdc_connection_status[1])
3038 if local_trust_status != werror.WERR_SUCCESS or local_conn_status != werror.WERR_SUCCESS:
3039 raise CommandError(local_validation)
3041 self.outf.write("OK: %s\n" % local_validation)
3044 server = local_trust_verify.trusted_dc_name.replace('\\', '')
3045 domain_and_server = "%s\\%s" % (local_tdo_info.domain_name.string, server)
3046 local_trust_rediscover = \
3047 local_netlogon.netr_LogonControl2Ex(local_server,
3048 netlogon.NETLOGON_CONTROL_REDISCOVER,
3051 except RuntimeError as error:
3052 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
3054 local_conn_status = self._uint32(local_trust_rediscover.tc_connection_status[0])
3055 local_rediscover = "LocalRediscover: DC[%s] CONNECTION[%s]" % (
3056 local_trust_rediscover.trusted_dc_name,
3057 local_trust_rediscover.tc_connection_status[1])
3059 if local_conn_status != werror.WERR_SUCCESS:
3060 raise CommandError(local_rediscover)
3062 self.outf.write("OK: %s\n" % local_rediscover)
3064 if validate_location != "local":
3066 remote_server = self.setup_remote_server(credopts, domain, require_pdc=False)
3067 except RuntimeError as error:
3068 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
3071 remote_netlogon = self.new_remote_netlogon_connection()
3072 except RuntimeError as error:
3073 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
3076 remote_trust_verify = \
3077 remote_netlogon.netr_LogonControl2Ex(remote_server,
3078 netlogon.NETLOGON_CONTROL_TC_VERIFY,
3080 local_lsa_info.dns_domain.string)
3081 except RuntimeError as error:
3082 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
3084 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
3085 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
3087 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
3088 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
3089 remote_trust_verify.trusted_dc_name,
3090 remote_trust_verify.tc_connection_status[1],
3091 remote_trust_verify.pdc_connection_status[1])
3093 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
3094 remote_trust_verify.trusted_dc_name,
3095 remote_trust_verify.tc_connection_status[1],
3096 remote_trust_verify.pdc_connection_status[1])
3098 if remote_trust_status != werror.WERR_SUCCESS or remote_conn_status != werror.WERR_SUCCESS:
3099 raise CommandError(remote_validation)
3101 self.outf.write("OK: %s\n" % remote_validation)
3104 server = remote_trust_verify.trusted_dc_name.replace('\\', '')
3105 domain_and_server = "%s\\%s" % (local_lsa_info.dns_domain.string, server)
3106 remote_trust_rediscover = \
3107 remote_netlogon.netr_LogonControl2Ex(remote_server,
3108 netlogon.NETLOGON_CONTROL_REDISCOVER,
3111 except RuntimeError as error:
3112 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
3114 remote_conn_status = self._uint32(remote_trust_rediscover.tc_connection_status[0])
3116 remote_rediscover = "RemoteRediscover: DC[%s] CONNECTION[%s]" % (
3117 remote_trust_rediscover.trusted_dc_name,
3118 remote_trust_rediscover.tc_connection_status[1])
3120 if remote_conn_status != werror.WERR_SUCCESS:
3121 raise CommandError(remote_rediscover)
3123 self.outf.write("OK: %s\n" % remote_rediscover)
3127 class cmd_domain_trust_namespaces(DomainTrustCommand):
3128 """Manage forest trust namespaces."""
3130 synopsis = "%prog [DOMAIN] [options]"
3132 takes_optiongroups = {
3133 "sambaopts": options.SambaOptions,
3134 "versionopts": options.VersionOptions,
3135 "localdcopts": LocalDCCredentialsOptions,
3139 Option("--refresh", type="choice", metavar="check|store",
3140 choices=["check", "store", None],
3141 help="List and maybe store refreshed forest trust information: 'check' or 'store'.",
3144 Option("--enable-all", action="store_true",
3145 help="Try to update disabled entries, not allowed with --refresh=check.",
3148 Option("--enable-tln", action="append", metavar='DNSDOMAIN',
3149 help="Enable a top level name entry. Can be specified multiple times.",
3152 Option("--disable-tln", action="append", metavar='DNSDOMAIN',
3153 help="Disable a top level name entry. Can be specified multiple times.",
3156 Option("--add-tln-ex", action="append", metavar='DNSDOMAIN',
3157 help="Add a top level exclusion entry. Can be specified multiple times.",
3160 Option("--delete-tln-ex", action="append", metavar='DNSDOMAIN',
3161 help="Delete a top level exclusion entry. Can be specified multiple times.",
3162 dest='delete_tln_ex',
3164 Option("--enable-nb", action="append", metavar='NETBIOSDOMAIN',
3165 help="Enable a netbios name in a domain entry. Can be specified multiple times.",
3168 Option("--disable-nb", action="append", metavar='NETBIOSDOMAIN',
3169 help="Disable a netbios name in a domain entry. Can be specified multiple times.",
3172 Option("--enable-sid", action="append", metavar='DOMAINSID',
3173 help="Enable a SID in a domain entry. Can be specified multiple times.",
3174 dest='enable_sid_str',
3176 Option("--disable-sid", action="append", metavar='DOMAINSID',
3177 help="Disable a SID in a domain entry. Can be specified multiple times.",
3178 dest='disable_sid_str',
3180 Option("--add-upn-suffix", action="append", metavar='DNSDOMAIN',
3181 help="Add a new uPNSuffixes attribute for the local forest. Can be specified multiple times.",
3184 Option("--delete-upn-suffix", action="append", metavar='DNSDOMAIN',
3185 help="Delete an existing uPNSuffixes attribute of the local forest. Can be specified multiple times.",
3188 Option("--add-spn-suffix", action="append", metavar='DNSDOMAIN',
3189 help="Add a new msDS-SPNSuffixes attribute for the local forest. Can be specified multiple times.",
3192 Option("--delete-spn-suffix", action="append", metavar='DNSDOMAIN',
3193 help="Delete an existing msDS-SPNSuffixes attribute of the local forest. Can be specified multiple times.",
3198 takes_args = ["domain?"]
3200 def run(self, domain=None, sambaopts=None, localdcopts=None, versionopts=None,
3201 refresh=None, enable_all=False,
3202 enable_tln=[], disable_tln=[], add_tln_ex=[], delete_tln_ex=[],
3203 enable_sid_str=[], disable_sid_str=[], enable_nb=[], disable_nb=[],
3204 add_upn=[], delete_upn=[], add_spn=[], delete_spn=[]):
3206 require_update = False
3209 if refresh == "store":
3210 raise CommandError("--refresh=%s not allowed without DOMAIN" % refresh)
3213 raise CommandError("--enable-all not allowed without DOMAIN")
3215 if len(enable_tln) > 0:
3216 raise CommandError("--enable-tln not allowed without DOMAIN")
3217 if len(disable_tln) > 0:
3218 raise CommandError("--disable-tln not allowed without DOMAIN")
3220 if len(add_tln_ex) > 0:
3221 raise CommandError("--add-tln-ex not allowed without DOMAIN")
3222 if len(delete_tln_ex) > 0:
3223 raise CommandError("--delete-tln-ex not allowed without DOMAIN")
3225 if len(enable_nb) > 0:
3226 raise CommandError("--enable-nb not allowed without DOMAIN")
3227 if len(disable_nb) > 0:
3228 raise CommandError("--disable-nb not allowed without DOMAIN")
3230 if len(enable_sid_str) > 0:
3231 raise CommandError("--enable-sid not allowed without DOMAIN")
3232 if len(disable_sid_str) > 0:
3233 raise CommandError("--disable-sid not allowed without DOMAIN")
3235 if len(add_upn) > 0:
3237 if not n.startswith("*."):
3239 raise CommandError("value[%s] specified for --add-upn-suffix should not include with '*.'" % n)
3240 require_update = True
3241 if len(delete_upn) > 0:
3242 for n in delete_upn:
3243 if not n.startswith("*."):
3245 raise CommandError("value[%s] specified for --delete-upn-suffix should not include with '*.'" % n)
3246 require_update = True
3248 for d in delete_upn:
3249 if a.lower() != d.lower():
3251 raise CommandError("value[%s] specified for --add-upn-suffix and --delete-upn-suffix" % a)
3253 if len(add_spn) > 0:
3255 if not n.startswith("*."):
3257 raise CommandError("value[%s] specified for --add-spn-suffix should not include with '*.'" % n)
3258 require_update = True
3259 if len(delete_spn) > 0:
3260 for n in delete_spn:
3261 if not n.startswith("*."):
3263 raise CommandError("value[%s] specified for --delete-spn-suffix should not include with '*.'" % n)
3264 require_update = True
3266 for d in delete_spn:
3267 if a.lower() != d.lower():
3269 raise CommandError("value[%s] specified for --add-spn-suffix and --delete-spn-suffix" % a)
3271 if len(add_upn) > 0:
3272 raise CommandError("--add-upn-suffix not allowed together with DOMAIN")
3273 if len(delete_upn) > 0:
3274 raise CommandError("--delete-upn-suffix not allowed together with DOMAIN")
3275 if len(add_spn) > 0:
3276 raise CommandError("--add-spn-suffix not allowed together with DOMAIN")
3277 if len(delete_spn) > 0:
3278 raise CommandError("--delete-spn-suffix not allowed together with DOMAIN")
3280 if refresh is not None:
3281 if refresh == "store":
3282 require_update = True
3284 if enable_all and refresh != "store":
3285 raise CommandError("--enable-all not allowed together with --refresh=%s" % refresh)
3287 if len(enable_tln) > 0:
3288 raise CommandError("--enable-tln not allowed together with --refresh")
3289 if len(disable_tln) > 0:
3290 raise CommandError("--disable-tln not allowed together with --refresh")
3292 if len(add_tln_ex) > 0:
3293 raise CommandError("--add-tln-ex not allowed together with --refresh")
3294 if len(delete_tln_ex) > 0:
3295 raise CommandError("--delete-tln-ex not allowed together with --refresh")
3297 if len(enable_nb) > 0:
3298 raise CommandError("--enable-nb not allowed together with --refresh")
3299 if len(disable_nb) > 0:
3300 raise CommandError("--disable-nb not allowed together with --refresh")
3302 if len(enable_sid_str) > 0:
3303 raise CommandError("--enable-sid not allowed together with --refresh")
3304 if len(disable_sid_str) > 0:
3305 raise CommandError("--disable-sid not allowed together with --refresh")
3308 require_update = True
3310 if len(enable_tln) > 0:
3311 raise CommandError("--enable-tln not allowed together with --enable-all")
3313 if len(enable_nb) > 0:
3314 raise CommandError("--enable-nb not allowed together with --enable-all")
3316 if len(enable_sid_str) > 0:
3317 raise CommandError("--enable-sid not allowed together with --enable-all")
3319 if len(enable_tln) > 0:
3320 require_update = True
3321 if len(disable_tln) > 0:
3322 require_update = True
3323 for e in enable_tln:
3324 for d in disable_tln:
3325 if e.lower() != d.lower():
3327 raise CommandError("value[%s] specified for --enable-tln and --disable-tln" % e)
3329 if len(add_tln_ex) > 0:
3330 for n in add_tln_ex:
3331 if not n.startswith("*."):
3333 raise CommandError("value[%s] specified for --add-tln-ex should not include with '*.'" % n)
3334 require_update = True
3335 if len(delete_tln_ex) > 0:
3336 for n in delete_tln_ex:
3337 if not n.startswith("*."):
3339 raise CommandError("value[%s] specified for --delete-tln-ex should not include with '*.'" % n)
3340 require_update = True
3341 for a in add_tln_ex:
3342 for d in delete_tln_ex:
3343 if a.lower() != d.lower():
3345 raise CommandError("value[%s] specified for --add-tln-ex and --delete-tln-ex" % a)
3347 if len(enable_nb) > 0:
3348 require_update = True
3349 if len(disable_nb) > 0:
3350 require_update = True
3352 for d in disable_nb:
3353 if e.upper() != d.upper():
3355 raise CommandError("value[%s] specified for --enable-nb and --disable-nb" % e)
3358 for s in enable_sid_str:
3360 sid = security.dom_sid(s)
3361 except TypeError as error:
3362 raise CommandError("value[%s] specified for --enable-sid is not a valid SID" % s)
3363 enable_sid.append(sid)
3365 for s in disable_sid_str:
3367 sid = security.dom_sid(s)
3368 except TypeError as error:
3369 raise CommandError("value[%s] specified for --disable-sid is not a valid SID" % s)
3370 disable_sid.append(sid)
3371 if len(enable_sid) > 0:
3372 require_update = True
3373 if len(disable_sid) > 0:
3374 require_update = True
3375 for e in enable_sid:
3376 for d in disable_sid:
3379 raise CommandError("value[%s] specified for --enable-sid and --disable-sid" % e)
3381 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
3383 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
3385 local_server = self.setup_local_server(sambaopts, localdcopts)
3387 local_lsa = self.new_local_lsa_connection()
3388 except RuntimeError as error:
3389 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
3392 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
3393 except RuntimeError as error:
3394 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
3396 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
3397 local_lsa_info.name.string,
3398 local_lsa_info.dns_domain.string,
3399 local_lsa_info.sid))
3403 local_netlogon = self.new_local_netlogon_connection()
3404 except RuntimeError as error:
3405 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3408 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3409 except RuntimeError as error:
3410 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3412 if local_netlogon_info.domain_name != local_netlogon_info.forest_name:
3413 raise CommandError("The local domain [%s] is not the forest root [%s]" % (
3414 local_netlogon_info.domain_name,
3415 local_netlogon_info.forest_name))
3418 # get all information about our own forest
3419 own_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3421 except RuntimeError as error:
3422 if self.check_runtime_error(error, werror.WERR_RPC_S_PROCNUM_OUT_OF_RANGE):
3423 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3426 if self.check_runtime_error(error, werror.WERR_INVALID_FUNCTION):
3427 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3430 if self.check_runtime_error(error, werror.WERR_NERR_ACFNOTLOADED):
3431 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3434 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3436 self.outf.write("Own forest trust information...\n")
3437 self.write_forest_trust_info(own_forest_info,
3438 tln=local_lsa_info.dns_domain.string)
3441 local_samdb = self.new_local_ldap_connection()
3442 except RuntimeError as error:
3443 raise self.LocalRuntimeError(self, error, "failed to connect to SamDB")
3445 local_partitions_dn = "CN=Partitions,%s" % str(local_samdb.get_config_basedn())
3446 attrs = ['uPNSuffixes', 'msDS-SPNSuffixes']
3448 msgs = local_samdb.search(base=local_partitions_dn,
3449 scope=ldb.SCOPE_BASE,
3450 expression="(objectClass=crossRefContainer)",
3452 stored_msg = msgs[0]
3453 except ldb.LdbError as error:
3454 raise self.LocalLdbError(self, error, "failed to search partition dn")
3456 stored_upn_vals = []
3457 if 'uPNSuffixes' in stored_msg:
3458 stored_upn_vals.extend(stored_msg['uPNSuffixes'])
3460 stored_spn_vals = []
3461 if 'msDS-SPNSuffixes' in stored_msg:
3462 stored_spn_vals.extend(stored_msg['msDS-SPNSuffixes'])
3464 self.outf.write("Stored uPNSuffixes attributes[%d]:\n" % len(stored_upn_vals))
3465 for v in stored_upn_vals:
3466 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3467 self.outf.write("Stored msDS-SPNSuffixes attributes[%d]:\n" % len(stored_spn_vals))
3468 for v in stored_spn_vals:
3469 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3471 if not require_update:
3475 update_upn_vals = []
3476 update_upn_vals.extend(stored_upn_vals)
3479 update_spn_vals = []
3480 update_spn_vals.extend(stored_spn_vals)
3483 for i, v in enumerate(update_upn_vals):
3484 if v.lower() == upn.lower():
3485 raise CommandError("Entry already present for "
3486 "value[%s] specified for "
3487 "--add-upn-suffix" % upn)
3488 update_upn_vals.append(upn)
3491 for upn in delete_upn:
3493 for i, v in enumerate(update_upn_vals):
3494 if v.lower() != upn.lower():
3499 raise CommandError("Entry not found for value[%s] specified for --delete-upn-suffix" % upn)
3501 update_upn_vals.pop(idx)
3505 for i, v in enumerate(update_spn_vals):
3506 if v.lower() == spn.lower():
3507 raise CommandError("Entry already present for "
3508 "value[%s] specified for "
3509 "--add-spn-suffix" % spn)
3510 update_spn_vals.append(spn)
3513 for spn in delete_spn:
3515 for i, v in enumerate(update_spn_vals):
3516 if v.lower() != spn.lower():
3521 raise CommandError("Entry not found for value[%s] specified for --delete-spn-suffix" % spn)
3523 update_spn_vals.pop(idx)
3526 self.outf.write("Update uPNSuffixes attributes[%d]:\n" % len(update_upn_vals))
3527 for v in update_upn_vals:
3528 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3529 self.outf.write("Update msDS-SPNSuffixes attributes[%d]:\n" % len(update_spn_vals))
3530 for v in update_spn_vals:
3531 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3533 update_msg = ldb.Message()
3534 update_msg.dn = stored_msg.dn
3537 update_msg['uPNSuffixes'] = ldb.MessageElement(update_upn_vals,
3538 ldb.FLAG_MOD_REPLACE,
3541 update_msg['msDS-SPNSuffixes'] = ldb.MessageElement(update_spn_vals,
3542 ldb.FLAG_MOD_REPLACE,
3545 local_samdb.modify(update_msg)
3546 except ldb.LdbError as error:
3547 raise self.LocalLdbError(self, error, "failed to update partition dn")
3550 stored_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3552 except RuntimeError as error:
3553 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3555 self.outf.write("Stored forest trust information...\n")
3556 self.write_forest_trust_info(stored_forest_info,
3557 tln=local_lsa_info.dns_domain.string)
3561 lsaString = lsa.String()
3562 lsaString.string = domain
3564 local_lsa.QueryTrustedDomainInfoByName(local_policy,
3566 lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
3567 except NTSTATUSError as error:
3568 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
3569 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
3571 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
3573 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
3574 local_tdo_info.netbios_name.string,
3575 local_tdo_info.domain_name.string,
3576 local_tdo_info.sid))
3578 if not local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
3579 raise CommandError("trusted domain object for domain [%s] is not marked as FOREST_TRANSITIVE." % domain)
3581 if refresh is not None:
3583 local_netlogon = self.new_local_netlogon_connection()
3584 except RuntimeError as error:
3585 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3588 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3589 except RuntimeError as error:
3590 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3592 lsa_update_check = 1
3593 if refresh == "store":
3594 netlogon_update_tdo = netlogon.DS_GFTI_UPDATE_TDO
3596 lsa_update_check = 0
3598 netlogon_update_tdo = 0
3601 # get all information about the remote trust
3602 # this triggers netr_GetForestTrustInformation to the remote domain
3603 # and lsaRSetForestTrustInformation() locally, but new top level
3604 # names are disabled by default.
3605 fresh_forest_info = \
3606 local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3607 local_tdo_info.domain_name.string,
3608 netlogon_update_tdo)
3609 except RuntimeError as error:
3610 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3613 fresh_forest_collision = \
3614 local_lsa.lsaRSetForestTrustInformation(local_policy,
3615 local_tdo_info.domain_name,
3616 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3619 except RuntimeError as error:
3620 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3622 self.outf.write("Fresh forest trust information...\n")
3623 self.write_forest_trust_info(fresh_forest_info,
3624 tln=local_tdo_info.domain_name.string,
3625 collisions=fresh_forest_collision)
3627 if refresh == "store":
3629 lsaString = lsa.String()
3630 lsaString.string = local_tdo_info.domain_name.string
3631 stored_forest_info = \
3632 local_lsa.lsaRQueryForestTrustInformation(local_policy,
3634 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3635 except RuntimeError as error:
3636 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3638 self.outf.write("Stored forest trust information...\n")
3639 self.write_forest_trust_info(stored_forest_info,
3640 tln=local_tdo_info.domain_name.string)
3645 # The none --refresh path
3649 lsaString = lsa.String()
3650 lsaString.string = local_tdo_info.domain_name.string
3651 local_forest_info = \
3652 local_lsa.lsaRQueryForestTrustInformation(local_policy,
3654 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3655 except RuntimeError as error:
3656 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3658 self.outf.write("Local forest trust information...\n")
3659 self.write_forest_trust_info(local_forest_info,
3660 tln=local_tdo_info.domain_name.string)
3662 if not require_update:
3666 entries.extend(local_forest_info.entries)
3667 update_forest_info = lsa.ForestTrustInformation()
3668 update_forest_info.count = len(entries)
3669 update_forest_info.entries = entries
3672 for i, r in enumerate(update_forest_info.entries):
3673 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3675 if update_forest_info.entries[i].flags == 0:
3677 update_forest_info.entries[i].time = 0
3678 update_forest_info.entries[i].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3679 for i, r in enumerate(update_forest_info.entries):
3680 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3682 if update_forest_info.entries[i].flags == 0:
3684 update_forest_info.entries[i].time = 0
3685 update_forest_info.entries[i].flags &= ~lsa.LSA_NB_DISABLED_MASK
3686 update_forest_info.entries[i].flags &= ~lsa.LSA_SID_DISABLED_MASK
3688 for tln in enable_tln:
3690 for i, r in enumerate(update_forest_info.entries):
3691 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3693 if r.forest_trust_data.string.lower() != tln.lower():
3698 raise CommandError("Entry not found for value[%s] specified for --enable-tln" % tln)
3699 if not update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_MASK:
3700 raise CommandError("Entry found for value[%s] specified for --enable-tln is already enabled" % tln)
3701 update_forest_info.entries[idx].time = 0
3702 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3704 for tln in disable_tln:
3706 for i, r in enumerate(update_forest_info.entries):
3707 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3709 if r.forest_trust_data.string.lower() != tln.lower():
3714 raise CommandError("Entry not found for value[%s] specified for --disable-tln" % tln)
3715 if update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_ADMIN:
3716 raise CommandError("Entry found for value[%s] specified for --disable-tln is already disabled" % tln)
3717 update_forest_info.entries[idx].time = 0
3718 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3719 update_forest_info.entries[idx].flags |= lsa.LSA_TLN_DISABLED_ADMIN
3721 for tln_ex in add_tln_ex:
3723 for i, r in enumerate(update_forest_info.entries):
3724 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3726 if r.forest_trust_data.string.lower() != tln_ex.lower():
3731 raise CommandError("Entry already present for value[%s] specified for --add-tln-ex" % tln_ex)
3733 tln_dot = ".%s" % tln_ex.lower()
3735 for i, r in enumerate(update_forest_info.entries):
3736 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3738 r_dot = ".%s" % r.forest_trust_data.string.lower()
3739 if tln_dot == r_dot:
3740 raise CommandError("TLN entry present for value[%s] specified for --add-tln-ex" % tln_ex)
3741 if not tln_dot.endswith(r_dot):
3747 raise CommandError("No TLN parent present for value[%s] specified for --add-tln-ex" % tln_ex)
3749 r = lsa.ForestTrustRecord()
3750 r.type = lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX
3753 r.forest_trust_data.string = tln_ex
3756 entries.extend(update_forest_info.entries)
3757 entries.insert(idx + 1, r)
3758 update_forest_info.count = len(entries)
3759 update_forest_info.entries = entries
3761 for tln_ex in delete_tln_ex:
3763 for i, r in enumerate(update_forest_info.entries):
3764 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3766 if r.forest_trust_data.string.lower() != tln_ex.lower():
3771 raise CommandError("Entry not found for value[%s] specified for --delete-tln-ex" % tln_ex)
3774 entries.extend(update_forest_info.entries)
3776 update_forest_info.count = len(entries)
3777 update_forest_info.entries = entries
3779 for nb in enable_nb:
3781 for i, r in enumerate(update_forest_info.entries):
3782 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3784 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3789 raise CommandError("Entry not found for value[%s] specified for --enable-nb" % nb)
3790 if not update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_MASK:
3791 raise CommandError("Entry found for value[%s] specified for --enable-nb is already enabled" % nb)
3792 update_forest_info.entries[idx].time = 0
3793 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3795 for nb in disable_nb:
3797 for i, r in enumerate(update_forest_info.entries):
3798 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3800 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3805 raise CommandError("Entry not found for value[%s] specified for --delete-nb" % nb)
3806 if update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_ADMIN:
3807 raise CommandError("Entry found for value[%s] specified for --disable-nb is already disabled" % nb)
3808 update_forest_info.entries[idx].time = 0
3809 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3810 update_forest_info.entries[idx].flags |= lsa.LSA_NB_DISABLED_ADMIN
3812 for sid in enable_sid:
3814 for i, r in enumerate(update_forest_info.entries):
3815 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3817 if r.forest_trust_data.domain_sid != sid:
3822 raise CommandError("Entry not found for value[%s] specified for --enable-sid" % sid)
3823 if not update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_MASK:
3824 raise CommandError("Entry found for value[%s] specified for --enable-sid is already enabled" % nb)
3825 update_forest_info.entries[idx].time = 0
3826 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3828 for sid in disable_sid:
3830 for i, r in enumerate(update_forest_info.entries):
3831 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3833 if r.forest_trust_data.domain_sid != sid:
3838 raise CommandError("Entry not found for value[%s] specified for --delete-sid" % sid)
3839 if update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_ADMIN:
3840 raise CommandError("Entry found for value[%s] specified for --disable-sid is already disabled" % nb)
3841 update_forest_info.entries[idx].time = 0
3842 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3843 update_forest_info.entries[idx].flags |= lsa.LSA_SID_DISABLED_ADMIN
3846 update_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3847 local_tdo_info.domain_name,
3848 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3849 update_forest_info, 0)
3850 except RuntimeError as error:
3851 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3853 self.outf.write("Updated forest trust information...\n")
3854 self.write_forest_trust_info(update_forest_info,
3855 tln=local_tdo_info.domain_name.string,
3856 collisions=update_forest_collision)
3859 lsaString = lsa.String()
3860 lsaString.string = local_tdo_info.domain_name.string
3861 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3863 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3864 except RuntimeError as error:
3865 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3867 self.outf.write("Stored forest trust information...\n")
3868 self.write_forest_trust_info(stored_forest_info,
3869 tln=local_tdo_info.domain_name.string)
3872 class cmd_domain_tombstones_expunge(Command):
3873 """Expunge tombstones from the database.
3875 This command expunges tombstones from the database."""
3876 synopsis = "%prog NC [NC [...]] [options]"
3879 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
3880 metavar="URL", dest="H"),
3881 Option("--current-time",
3882 help="The current time to evaluate the tombstone lifetime from, expressed as YYYY-MM-DD",
3884 Option("--tombstone-lifetime", help="Number of days a tombstone should be preserved for", type=int),
3887 takes_args = ["nc*"]
3889 takes_optiongroups = {
3890 "sambaopts": options.SambaOptions,
3891 "credopts": options.CredentialsOptions,
3892 "versionopts": options.VersionOptions,
3895 def run(self, *ncs, **kwargs):
3896 sambaopts = kwargs.get("sambaopts")
3897 credopts = kwargs.get("credopts")
3898 versionpts = kwargs.get("versionopts")
3900 current_time_string = kwargs.get("current_time")
3901 tombstone_lifetime = kwargs.get("tombstone_lifetime")
3902 lp = sambaopts.get_loadparm()
3903 creds = credopts.get_credentials(lp)
3904 samdb = SamDB(url=H, session_info=system_session(),
3905 credentials=creds, lp=lp)
3907 if current_time_string is not None:
3908 current_time_obj = time.strptime(current_time_string, "%Y-%m-%d")
3909 current_time = long(time.mktime(current_time_obj))
3912 current_time = long(time.time())
3915 res = samdb.search(expression="", base="", scope=ldb.SCOPE_BASE,
3916 attrs=["namingContexts"])
3919 for nc in res[0]["namingContexts"]:
3924 started_transaction = False
3926 samdb.transaction_start()
3927 started_transaction = True
3929 removed_links) = samdb.garbage_collect_tombstones(ncs,
3930 current_time=current_time,
3931 tombstone_lifetime=tombstone_lifetime)
3933 except Exception as err:
3934 if started_transaction:
3935 samdb.transaction_cancel()
3936 raise CommandError("Failed to expunge / garbage collect tombstones", err)
3938 samdb.transaction_commit()
3940 self.outf.write("Removed %d objects and %d links successfully\n"
3941 % (removed_objects, removed_links))
3945 class cmd_domain_trust(SuperCommand):
3946 """Domain and forest trust management."""
3949 subcommands["list"] = cmd_domain_trust_list()
3950 subcommands["show"] = cmd_domain_trust_show()
3951 subcommands["create"] = cmd_domain_trust_create()
3952 subcommands["delete"] = cmd_domain_trust_delete()
3953 subcommands["validate"] = cmd_domain_trust_validate()
3954 subcommands["namespaces"] = cmd_domain_trust_namespaces()
3956 class cmd_domain_tombstones(SuperCommand):
3957 """Domain tombstone and recycled object management."""
3960 subcommands["expunge"] = cmd_domain_tombstones_expunge()
3962 class ldif_schema_update:
3963 """Helper class for applying LDIF schema updates"""
3966 self.is_defunct = False
3967 self.unknown_oid = None
3971 def can_ignore_failure(self, error):
3972 """Checks if we can safely ignore failure to apply an LDIF update"""
3973 (num, errstr) = error.args
3975 # Microsoft has marked objects as defunct that Samba doesn't know about
3976 if num == ldb.ERR_NO_SUCH_OBJECT and self.is_defunct:
3977 print("Defunct object %s doesn't exist, skipping" % self.dn)
3979 elif self.unknown_oid is not None:
3980 print("Skipping unknown OID %s for object %s" % (self.unknown_oid, self.dn))
3985 def apply(self, samdb):
3986 """Applies a single LDIF update to the schema"""
3990 samdb.modify_ldif(self.ldif, controls=['relax:0'])
3991 except ldb.LdbError as e:
3992 if e.args[0] == ldb.ERR_INVALID_ATTRIBUTE_SYNTAX:
3994 # REFRESH after a failed change
3996 # Otherwise the OID-to-attribute mapping in
3997 # _apply_updates_in_file() won't work, because it
3998 # can't lookup the new OID in the schema
3999 samdb.set_schema_update_now()
4001 samdb.modify_ldif(self.ldif, controls=['relax:0'])
4004 except ldb.LdbError as e:
4005 if self.can_ignore_failure(e):
4008 print("Exception: %s" % e)
4009 print("Encountered while trying to apply the following LDIF")
4010 print("----------------------------------------------------")
4011 print("%s" % self.ldif)
4017 class cmd_domain_schema_upgrade(Command):
4018 """Domain schema upgrading"""
4020 synopsis = "%prog [options]"
4022 takes_optiongroups = {
4023 "sambaopts": options.SambaOptions,
4024 "versionopts": options.VersionOptions,
4025 "credopts": options.CredentialsOptions,
4029 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
4030 metavar="URL", dest="H"),
4031 Option("-q", "--quiet", help="Be quiet", action="store_true"), #unused
4032 Option("-v", "--verbose", help="Be verbose", action="store_true"),
4033 Option("--schema", type="choice", metavar="SCHEMA",
4034 choices=["2012", "2012_R2"],
4035 help="The schema file to upgrade to. Default is (Windows) 2012_R2.",
4037 Option("--ldf-file", type=str, default=None,
4038 help="Just apply the schema updates in the adprep/.LDF file(s) specified"),
4039 Option("--base-dir", type=str, default=None,
4040 help="Location of ldf files Default is ${SETUPDIR}/adprep.")
4043 def _apply_updates_in_file(self, samdb, ldif_file):
4045 Applies a series of updates specified in an .LDIF file. The .LDIF file
4046 is based on the adprep Schema updates provided by Microsoft.
4049 ldif_op = ldif_schema_update()
4051 # parse the file line by line and work out each update operation to apply
4052 for line in ldif_file:
4054 line = line.rstrip()
4056 # the operations in the .LDIF file are separated by blank lines. If
4057 # we hit a blank line, try to apply the update we've parsed so far
4060 # keep going if we haven't parsed anything yet
4061 if ldif_op.ldif == '':
4064 # Apply the individual change
4065 count += ldif_op.apply(samdb)
4067 # start storing the next operation from scratch again
4068 ldif_op = ldif_schema_update()
4071 # replace the placeholder domain name in the .ldif file with the real domain
4072 if line.upper().endswith('DC=X'):
4073 line = line[:-len('DC=X')] + str(samdb.get_default_basedn())
4074 elif line.upper().endswith('CN=X'):
4075 line = line[:-len('CN=X')] + str(samdb.get_default_basedn())
4077 values = line.split(':')
4079 if values[0].lower() == 'dn':
4080 ldif_op.dn = values[1].strip()
4082 # replace the Windows-specific operation with the Samba one
4083 if values[0].lower() == 'changetype':
4084 line = line.lower().replace(': ntdsschemaadd',
4086 line = line.lower().replace(': ntdsschemamodify',
4089 if values[0].lower() in ['rdnattid', 'subclassof',
4090 'systemposssuperiors',
4092 'systemauxiliaryclass']:
4095 # The Microsoft updates contain some OIDs we don't recognize.
4096 # Query the DB to see if we can work out the OID this update is
4097 # referring to. If we find a match, then replace the OID with
4098 # the ldapDisplayname
4100 res = samdb.search(base=samdb.get_schema_basedn(),
4101 expression="(|(attributeId=%s)(governsId=%s))" %
4103 attrs=['ldapDisplayName'])
4106 ldif_op.unknown_oid = value
4108 display_name = res[0]['ldapDisplayName'][0]
4109 line = line.replace(value, ' ' + display_name)
4111 # Microsoft has marked objects as defunct that Samba doesn't know about
4112 if values[0].lower() == 'isdefunct' and values[1].strip().lower() == 'true':
4113 ldif_op.is_defunct = True
4115 # Samba has added the showInAdvancedViewOnly attribute to all objects,
4116 # so rather than doing an add, we need to do a replace
4117 if values[0].lower() == 'add' and values[1].strip().lower() == 'showinadvancedviewonly':
4118 line = 'replace: showInAdvancedViewOnly'
4120 # Add the line to the current LDIF operation (including the newline
4121 # we stripped off at the start of the loop)
4122 ldif_op.ldif += line + '\n'
4127 def _apply_update(self, samdb, update_file, base_dir):
4128 """Wrapper function for parsing an LDIF file and applying the updates"""
4130 print("Applying %s updates..." % update_file)
4134 ldif_file = open(os.path.join(base_dir, update_file))
4136 count = self._apply_updates_in_file(samdb, ldif_file)
4142 print("%u changes applied" % count)
4146 def run(self, **kwargs):
4147 from samba.ms_schema_markdown import read_ms_markdown
4148 from samba.schema import Schema
4150 updates_allowed_overriden = False
4151 sambaopts = kwargs.get("sambaopts")
4152 credopts = kwargs.get("credopts")
4153 versionpts = kwargs.get("versionopts")
4154 lp = sambaopts.get_loadparm()
4155 creds = credopts.get_credentials(lp)
4157 target_schema = kwargs.get("schema")
4158 ldf_files = kwargs.get("ldf_file")
4159 base_dir = kwargs.get("base_dir")
4163 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
4165 # we're not going to get far if the config doesn't allow schema updates
4166 if lp.get("dsdb:schema update allowed") is None:
4167 lp.set("dsdb:schema update allowed", "yes")
4168 print("Temporarily overriding 'dsdb:schema update allowed' setting")
4169 updates_allowed_overriden = True
4171 own_dn = ldb.Dn(samdb, samdb.get_dsServiceName())
4172 master = get_fsmo_roleowner(samdb, str(samdb.get_schema_basedn()),
4174 if own_dn != master:
4175 raise CommandError("This server is not the schema master.")
4177 # if specific LDIF files were specified, just apply them
4179 schema_updates = ldf_files.split(",")
4183 # work out the version of the target schema we're upgrading to
4184 end = Schema.get_version(target_schema)
4186 # work out the version of the schema we're currently using
4187 res = samdb.search(base=samdb.get_schema_basedn(),
4188 scope=ldb.SCOPE_BASE, attrs=['objectVersion'])
4191 raise CommandError('Could not determine current schema version')
4192 start = int(res[0]['objectVersion'][0]) + 1
4194 diff_dir = setup_path("adprep/WindowsServerDocs")
4195 if base_dir is None:
4196 # Read from the Schema-Updates.md file
4197 temp_folder = tempfile.mkdtemp()
4199 update_file = setup_path("adprep/WindowsServerDocs/Schema-Updates.md")
4202 read_ms_markdown(update_file, temp_folder)
4203 except Exception as e:
4204 print("Exception in markdown parsing: %s" % e)
4205 shutil.rmtree(temp_folder)
4206 raise CommandError('Failed to upgrade schema')
4208 base_dir = temp_folder
4210 for version in range(start, end + 1):
4211 update = 'Sch%d.ldf' % version
4212 schema_updates.append(update)
4214 # Apply patches if we parsed the Schema-Updates.md file
4215 diff = os.path.abspath(os.path.join(diff_dir, update + '.diff'))
4216 if temp_folder and os.path.exists(diff):
4218 p = subprocess.Popen(['patch', update, '-i', diff],
4219 stdout=subprocess.PIPE,
4220 stderr=subprocess.PIPE, cwd=temp_folder)
4221 except (OSError, IOError):
4222 shutil.rmtree(temp_folder)
4223 raise CommandError("Failed to upgrade schema. Check if 'patch' is installed.")
4225 stdout, stderr = p.communicate()
4228 print("Exception in patch: %s\n%s" % (stdout, stderr))
4229 shutil.rmtree(temp_folder)
4230 raise CommandError('Failed to upgrade schema')
4232 print("Patched %s using %s" % (update, diff))
4234 if base_dir is None:
4235 base_dir = setup_path("adprep")
4237 samdb.transaction_start()
4239 error_encountered = False
4242 # Apply the schema updates needed to move to the new schema version
4243 for ldif_file in schema_updates:
4244 count += self._apply_update(samdb, ldif_file, base_dir)
4247 samdb.transaction_commit()
4248 print("Schema successfully updated")
4250 print("No changes applied to schema")
4251 samdb.transaction_cancel()
4252 except Exception as e:
4253 print("Exception: %s" % e)
4254 print("Error encountered, aborting schema upgrade")
4255 samdb.transaction_cancel()
4256 error_encountered = True
4258 if updates_allowed_overriden:
4259 lp.set("dsdb:schema update allowed", "no")
4262 shutil.rmtree(temp_folder)
4264 if error_encountered:
4265 raise CommandError('Failed to upgrade schema')
4267 class cmd_domain_functional_prep(Command):
4268 """Domain functional level preparation"""
4270 synopsis = "%prog [options]"
4272 takes_optiongroups = {
4273 "sambaopts": options.SambaOptions,
4274 "versionopts": options.VersionOptions,
4275 "credopts": options.CredentialsOptions,
4279 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
4280 metavar="URL", dest="H"),
4281 Option("-q", "--quiet", help="Be quiet", action="store_true"),
4282 Option("-v", "--verbose", help="Be verbose", action="store_true"),
4283 Option("--function-level", type="choice", metavar="FUNCTION_LEVEL",
4284 choices=["2008_R2", "2012", "2012_R2"],
4285 help="The schema file to upgrade to. Default is (Windows) 2012_R2.",
4287 Option("--forest-prep", action="store_true",
4288 help="Run the forest prep (by default, both the domain and forest prep are run)."),
4289 Option("--domain-prep", action="store_true",
4290 help="Run the domain prep (by default, both the domain and forest prep are run).")
4293 def run(self, **kwargs):
4294 updates_allowed_overriden = False
4295 sambaopts = kwargs.get("sambaopts")
4296 credopts = kwargs.get("credopts")
4297 versionpts = kwargs.get("versionopts")
4298 lp = sambaopts.get_loadparm()
4299 creds = credopts.get_credentials(lp)
4301 target_level = string_version_to_constant[kwargs.get("function_level")]
4302 forest_prep = kwargs.get("forest_prep")
4303 domain_prep = kwargs.get("domain_prep")
4305 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
4307 # we're not going to get far if the config doesn't allow schema updates
4308 if lp.get("dsdb:schema update allowed") is None:
4309 lp.set("dsdb:schema update allowed", "yes")
4310 print("Temporarily overriding 'dsdb:schema update allowed' setting")
4311 updates_allowed_overriden = True
4313 if forest_prep is None and domain_prep is None:
4317 own_dn = ldb.Dn(samdb, samdb.get_dsServiceName())
4319 master = get_fsmo_roleowner(samdb, str(samdb.get_schema_basedn()),
4321 if own_dn != master:
4322 raise CommandError("This server is not the schema master.")
4325 domain_dn = samdb.domain_dn()
4326 infrastructure_dn = "CN=Infrastructure," + domain_dn
4327 master = get_fsmo_roleowner(samdb, infrastructure_dn,
4329 if own_dn != master:
4330 raise CommandError("This server is not the infrastructure master.")
4333 samdb.transaction_start()
4334 error_encountered = False
4336 from samba.forest_update import ForestUpdate
4337 forest = ForestUpdate(samdb, fix=True)
4339 forest.check_updates_iterator([53, 79, 80, 81, 82, 83])
4340 forest.check_updates_functional_level(target_level,
4341 DS_DOMAIN_FUNCTION_2008_R2,
4342 update_revision=True)
4344 samdb.transaction_commit()
4345 except Exception as e:
4346 print("Exception: %s" % e)
4347 samdb.transaction_cancel()
4348 error_encountered = True
4351 samdb.transaction_start()
4352 error_encountered = False
4354 from samba.domain_update import DomainUpdate
4356 domain = DomainUpdate(samdb, fix=True)
4357 domain.check_updates_functional_level(target_level,
4358 DS_DOMAIN_FUNCTION_2008,
4359 update_revision=True)
4361 samdb.transaction_commit()
4362 except Exception as e:
4363 print("Exception: %s" % e)
4364 samdb.transaction_cancel()
4365 error_encountered = True
4367 if updates_allowed_overriden:
4368 lp.set("dsdb:schema update allowed", "no")
4370 if error_encountered:
4371 raise CommandError('Failed to perform functional prep')
4373 class cmd_domain(SuperCommand):
4374 """Domain management."""
4377 subcommands["demote"] = cmd_domain_demote()
4378 if cmd_domain_export_keytab is not None:
4379 subcommands["exportkeytab"] = cmd_domain_export_keytab()
4380 subcommands["info"] = cmd_domain_info()
4381 subcommands["provision"] = cmd_domain_provision()
4382 subcommands["join"] = cmd_domain_join()
4383 subcommands["dcpromo"] = cmd_domain_dcpromo()
4384 subcommands["level"] = cmd_domain_level()
4385 subcommands["passwordsettings"] = cmd_domain_passwordsettings()
4386 subcommands["classicupgrade"] = cmd_domain_classicupgrade()
4387 subcommands["samba3upgrade"] = cmd_domain_samba3upgrade()
4388 subcommands["trust"] = cmd_domain_trust()
4389 subcommands["tombstones"] = cmd_domain_tombstones()
4390 subcommands["schemaupgrade"] = cmd_domain_schema_upgrade()
4391 subcommands["functionalprep"] = cmd_domain_functional_prep()
4392 subcommands["backup"] = cmd_domain_backup()