3 # Copyright Matthias Dieter Wallnoefer 2009
4 # Copyright Andrew Kroeger 2009
5 # Copyright Jelmer Vernooij 2007-2012
6 # Copyright Giampaolo Lauria 2011
7 # Copyright Matthieu Patou <mat@matws.net> 2011
8 # Copyright Andrew Bartlett 2008-2015
9 # Copyright Stefan Metzmacher 2012
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 3 of the License, or
14 # (at your option) any later version.
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 from __future__ import print_function
26 from __future__ import division
27 import samba.getopt as options
39 from samba import ntstatus
40 from samba import NTSTATUSError
41 from samba import werror
42 from getpass import getpass
43 from samba.net import Net, LIBNET_JOIN_AUTOMATIC
45 from samba.join import join_RODC, join_DC, join_subdomain
46 from samba.auth import system_session
47 from samba.samdb import SamDB, get_default_backend_store
48 from samba.ndr import ndr_unpack, ndr_pack, ndr_print
49 from samba.dcerpc import drsuapi
50 from samba.dcerpc import drsblobs
51 from samba.dcerpc import lsa
52 from samba.dcerpc import netlogon
53 from samba.dcerpc import security
54 from samba.dcerpc import nbt
55 from samba.dcerpc import misc
56 from samba.dcerpc.samr import DOMAIN_PASSWORD_COMPLEX, DOMAIN_PASSWORD_STORE_CLEARTEXT
57 from samba.netcmd import (
63 from samba.netcmd.fsmo import get_fsmo_roleowner
64 from samba.netcmd.common import netcmd_get_domain_infos_via_cldap
65 from samba.samba3 import Samba3
66 from samba.samba3 import param as s3param
67 from samba.upgrade import upgrade_from_samba3
68 from samba.drs_utils import (
69 sendDsReplicaSync, drsuapi_connect, drsException,
71 from samba import remove_dc, arcfour_encrypt, string_to_byte_array
73 from samba.dsdb import (
74 DS_DOMAIN_FUNCTION_2000,
75 DS_DOMAIN_FUNCTION_2003,
76 DS_DOMAIN_FUNCTION_2003_MIXED,
77 DS_DOMAIN_FUNCTION_2008,
78 DS_DOMAIN_FUNCTION_2008_R2,
79 DS_DOMAIN_FUNCTION_2012,
80 DS_DOMAIN_FUNCTION_2012_R2,
81 DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL,
82 DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL,
83 UF_WORKSTATION_TRUST_ACCOUNT,
84 UF_SERVER_TRUST_ACCOUNT,
85 UF_TRUSTED_FOR_DELEGATION,
86 UF_PARTIAL_SECRETS_ACCOUNT
89 from samba.provision import (
92 DEFAULT_MIN_PWD_LENGTH,
96 from samba.provision.common import (
102 string_version_to_constant = {
103 "2008_R2" : DS_DOMAIN_FUNCTION_2008_R2,
104 "2012": DS_DOMAIN_FUNCTION_2012,
105 "2012_R2": DS_DOMAIN_FUNCTION_2012_R2,
108 def get_testparm_var(testparm, smbconf, varname):
109 errfile = open(os.devnull, 'w')
110 p = subprocess.Popen([testparm, '-s', '-l',
111 '--parameter-name=%s' % varname, smbconf],
112 stdout=subprocess.PIPE, stderr=errfile)
113 (out,err) = p.communicate()
115 lines = out.split('\n')
117 return lines[0].strip()
121 import samba.dckeytab
123 cmd_domain_export_keytab = None
125 class cmd_domain_export_keytab(Command):
126 """Dump Kerberos keys of the domain into a keytab."""
128 synopsis = "%prog <keytab> [options]"
130 takes_optiongroups = {
131 "sambaopts": options.SambaOptions,
132 "credopts": options.CredentialsOptions,
133 "versionopts": options.VersionOptions,
137 Option("--principal", help="extract only this principal", type=str),
140 takes_args = ["keytab"]
142 def run(self, keytab, credopts=None, sambaopts=None, versionopts=None, principal=None):
143 lp = sambaopts.get_loadparm()
145 net.export_keytab(keytab=keytab, principal=principal)
148 class cmd_domain_info(Command):
149 """Print basic info about a domain and the DC passed as parameter."""
151 synopsis = "%prog <ip_address> [options]"
156 takes_optiongroups = {
157 "sambaopts": options.SambaOptions,
158 "credopts": options.CredentialsOptions,
159 "versionopts": options.VersionOptions,
162 takes_args = ["address"]
164 def run(self, address, credopts=None, sambaopts=None, versionopts=None):
165 lp = sambaopts.get_loadparm()
167 res = netcmd_get_domain_infos_via_cldap(lp, None, address)
169 raise CommandError("Invalid IP address '" + address + "'!")
170 self.outf.write("Forest : %s\n" % res.forest)
171 self.outf.write("Domain : %s\n" % res.dns_domain)
172 self.outf.write("Netbios domain : %s\n" % res.domain_name)
173 self.outf.write("DC name : %s\n" % res.pdc_dns_name)
174 self.outf.write("DC netbios name : %s\n" % res.pdc_name)
175 self.outf.write("Server site : %s\n" % res.server_site)
176 self.outf.write("Client site : %s\n" % res.client_site)
179 class cmd_domain_provision(Command):
180 """Provision a domain."""
182 synopsis = "%prog [options]"
184 takes_optiongroups = {
185 "sambaopts": options.SambaOptions,
186 "versionopts": options.VersionOptions,
190 Option("--interactive", help="Ask for names", action="store_true"),
191 Option("--domain", type="string", metavar="DOMAIN",
192 help="NetBIOS domain name to use"),
193 Option("--domain-guid", type="string", metavar="GUID",
194 help="set domainguid (otherwise random)"),
195 Option("--domain-sid", type="string", metavar="SID",
196 help="set domainsid (otherwise random)"),
197 Option("--ntds-guid", type="string", metavar="GUID",
198 help="set NTDS object GUID (otherwise random)"),
199 Option("--invocationid", type="string", metavar="GUID",
200 help="set invocationid (otherwise random)"),
201 Option("--host-name", type="string", metavar="HOSTNAME",
202 help="set hostname"),
203 Option("--host-ip", type="string", metavar="IPADDRESS",
204 help="set IPv4 ipaddress"),
205 Option("--host-ip6", type="string", metavar="IP6ADDRESS",
206 help="set IPv6 ipaddress"),
207 Option("--site", type="string", metavar="SITENAME",
208 help="set site name"),
209 Option("--adminpass", type="string", metavar="PASSWORD",
210 help="choose admin password (otherwise random)"),
211 Option("--krbtgtpass", type="string", metavar="PASSWORD",
212 help="choose krbtgt password (otherwise random)"),
213 Option("--machinepass", type="string", metavar="PASSWORD",
214 help="choose machine password (otherwise random)"),
215 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
216 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
217 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
218 "BIND9_FLATFILE uses bind9 text database to store zone information, "
219 "BIND9_DLZ uses samba4 AD to store zone information, "
220 "NONE skips the DNS setup entirely (not recommended)",
221 default="SAMBA_INTERNAL"),
222 Option("--dnspass", type="string", metavar="PASSWORD",
223 help="choose dns password (otherwise random)"),
224 Option("--root", type="string", metavar="USERNAME",
225 help="choose 'root' unix username"),
226 Option("--nobody", type="string", metavar="USERNAME",
227 help="choose 'nobody' user"),
228 Option("--users", type="string", metavar="GROUPNAME",
229 help="choose 'users' group"),
230 Option("--quiet", help="Be quiet", action="store_true"),
231 Option("--blank", action="store_true",
232 help="do not add users or groups, just the structure"),
233 Option("--server-role", type="choice", metavar="ROLE",
234 choices=["domain controller", "dc", "member server", "member", "standalone"],
235 help="The server role (domain controller | dc | member server | member | standalone). Default is dc.",
236 default="domain controller"),
237 Option("--function-level", type="choice", metavar="FOR-FUN-LEVEL",
238 choices=["2000", "2003", "2008", "2008_R2"],
239 help="The domain and forest function level (2000 | 2003 | 2008 | 2008_R2 - always native). Default is (Windows) 2008_R2 Native.",
241 Option("--base-schema", type="choice", metavar="BASE-SCHEMA",
242 choices=["2008_R2", "2008_R2_old", "2012", "2012_R2"],
243 help="The base schema files to use. Default is (Windows) 2008_R2.",
245 Option("--next-rid", type="int", metavar="NEXTRID", default=1000,
246 help="The initial nextRid value (only needed for upgrades). Default is 1000."),
247 Option("--partitions-only",
248 help="Configure Samba's partitions, but do not modify them (ie, join a BDC)", action="store_true"),
249 Option("--targetdir", type="string", metavar="DIR",
250 help="Set target directory"),
251 Option("--use-rfc2307", action="store_true", help="Use AD to store posix attributes (default = no)"),
252 Option("--plaintext-secrets", action="store_true",
253 help="Store secret/sensitive values as plain text on disk" +
254 "(default is to encrypt secret/ensitive values)"),
255 Option("--backend-store", type="choice", metavar="BACKENDSTORE",
256 choices=["tdb", "mdb"],
257 help="Specify the database backend to be used "
258 "(default is %s)" % get_default_backend_store()),
262 Option("--ldapadminpass", type="string", metavar="PASSWORD",
263 help="choose password to set between Samba and its LDAP backend (otherwise random)"),
264 Option("--ldap-backend-type", type="choice", metavar="LDAP-BACKEND-TYPE",
265 help="Test initialisation support for unsupported LDAP backend type (fedora-ds or openldap) DO NOT USE",
266 choices=["fedora-ds", "openldap"]),
267 Option("--ol-mmr-urls", type="string", metavar="LDAPSERVER",
268 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\""),
269 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",
270 action="store_true"),
271 Option("--slapd-path", type="string", metavar="SLAPD-PATH",
272 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."),
273 Option("--ldap-backend-extra-port", type="int", metavar="LDAP-BACKEND-EXTRA-PORT", help="Additional TCP port for LDAP backend server (to use for replication)"),
274 Option("--ldap-backend-forced-uri", type="string", metavar="LDAP-BACKEND-FORCED-URI",
275 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"),
276 Option("--ldap-backend-nosync", help="Configure LDAP backend not to call fsync() (for performance in test environments)", action="store_true"),
280 Option("--use-ntvfs", action="store_true", help="Use NTVFS for the fileserver (default = no)"),
281 Option("--use-xattrs", type="choice", choices=["yes","no","auto"],
282 metavar="[yes|no|auto]",
283 help="Define if we should use the native fs capabilities or a tdb file for "
284 "storing attributes likes ntacl when --use-ntvfs is set. "
285 "auto tries to make an inteligent guess based on the user rights and system capabilities",
289 if os.getenv('TEST_LDAP', "no") == "yes":
290 takes_options.extend(openldap_options)
292 if samba.is_ntvfs_fileserver_built():
293 takes_options.extend(ntvfs_options)
297 def run(self, sambaopts=None, versionopts=None,
320 ldap_backend_type=None,
324 partitions_only=None,
331 ldap_backend_nosync=None,
332 ldap_backend_extra_port=None,
333 ldap_backend_forced_uri=None,
334 ldap_dryrun_mode=None,
336 plaintext_secrets=False,
339 self.logger = self.get_logger("provision")
341 self.logger.setLevel(logging.WARNING)
343 self.logger.setLevel(logging.INFO)
345 lp = sambaopts.get_loadparm()
346 smbconf = lp.configfile
348 if dns_forwarder is not None:
349 suggested_forwarder = dns_forwarder
351 suggested_forwarder = self._get_nameserver_ip()
352 if suggested_forwarder is None:
353 suggested_forwarder = "none"
355 if len(self.raw_argv) == 1:
359 from getpass import getpass
362 def ask(prompt, default=None):
363 if default is not None:
364 print("%s [%s]: " % (prompt, default), end=' ')
366 print("%s: " % (prompt,), end=' ')
367 return sys.stdin.readline().rstrip("\n") or default
370 default = socket.getfqdn().split(".", 1)[1].upper()
373 realm = ask("Realm", default)
374 if realm in (None, ""):
375 raise CommandError("No realm set!")
378 default = realm.split(".")[0]
381 domain = ask("Domain", default)
383 raise CommandError("No domain set!")
385 server_role = ask("Server Role (dc, member, standalone)", "dc")
387 dns_backend = ask("DNS backend (SAMBA_INTERNAL, BIND9_FLATFILE, BIND9_DLZ, NONE)", "SAMBA_INTERNAL")
388 if dns_backend in (None, ''):
389 raise CommandError("No DNS backend set!")
391 if dns_backend == "SAMBA_INTERNAL":
392 dns_forwarder = ask("DNS forwarder IP address (write 'none' to disable forwarding)", suggested_forwarder)
393 if dns_forwarder.lower() in (None, 'none'):
394 suggested_forwarder = None
398 adminpassplain = getpass("Administrator password: ")
399 issue = self._adminpass_issue(adminpassplain)
401 self.errf.write("%s.\n" % issue)
403 adminpassverify = getpass("Retype password: ")
404 if not adminpassplain == adminpassverify:
405 self.errf.write("Sorry, passwords do not match.\n")
407 adminpass = adminpassplain
411 realm = sambaopts._lp.get('realm')
413 raise CommandError("No realm set!")
415 raise CommandError("No domain set!")
418 issue = self._adminpass_issue(adminpass)
420 raise CommandError(issue)
422 self.logger.info("Administrator password will be set randomly!")
424 if function_level == "2000":
425 dom_for_fun_level = DS_DOMAIN_FUNCTION_2000
426 elif function_level == "2003":
427 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
428 elif function_level == "2008":
429 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008
430 elif function_level == "2008_R2":
431 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
433 if dns_backend == "SAMBA_INTERNAL" and dns_forwarder is None:
434 dns_forwarder = suggested_forwarder
436 samdb_fill = FILL_FULL
438 samdb_fill = FILL_NT4SYNC
439 elif partitions_only:
440 samdb_fill = FILL_DRS
442 if targetdir is not None:
443 if not os.path.isdir(targetdir):
448 if use_xattrs == "yes":
450 elif use_xattrs == "auto" and use_ntvfs == False:
452 elif use_ntvfs == False:
453 raise CommandError("--use-xattrs=no requires --use-ntvfs (not supported for production use). "
454 "Please re-run with --use-xattrs omitted.")
455 elif use_xattrs == "auto" and not lp.get("posix:eadb"):
457 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
459 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
462 samba.ntacls.setntacl(lp, file.name,
463 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
466 self.logger.info("You are not root or your system does not support xattr, using tdb backend for attributes. ")
471 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.")
472 if ldap_backend_type == "existing":
473 if ldap_backend_forced_uri is not None:
474 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)
476 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")
478 if ldap_backend_forced_uri is not None:
479 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")
481 if domain_sid is not None:
482 domain_sid = security.dom_sid(domain_sid)
484 session = system_session()
485 if backend_store is None:
486 backend_store = get_default_backend_store()
488 result = provision(self.logger,
489 session, smbconf=smbconf, targetdir=targetdir,
490 samdb_fill=samdb_fill, realm=realm, domain=domain,
491 domainguid=domain_guid, domainsid=domain_sid,
493 hostip=host_ip, hostip6=host_ip6,
494 sitename=site, ntdsguid=ntds_guid,
495 invocationid=invocationid, adminpass=adminpass,
496 krbtgtpass=krbtgtpass, machinepass=machinepass,
497 dns_backend=dns_backend, dns_forwarder=dns_forwarder,
498 dnspass=dnspass, root=root, nobody=nobody,
500 serverrole=server_role, dom_for_fun_level=dom_for_fun_level,
501 backend_type=ldap_backend_type,
502 ldapadminpass=ldapadminpass, ol_mmr_urls=ol_mmr_urls, slapd_path=slapd_path,
503 useeadb=eadb, next_rid=next_rid, lp=lp, use_ntvfs=use_ntvfs,
504 use_rfc2307=use_rfc2307, skip_sysvolacl=False,
505 ldap_backend_extra_port=ldap_backend_extra_port,
506 ldap_backend_forced_uri=ldap_backend_forced_uri,
507 nosync=ldap_backend_nosync, ldap_dryrun_mode=ldap_dryrun_mode,
508 base_schema=base_schema,
509 plaintext_secrets=plaintext_secrets,
510 backend_store=backend_store)
512 except ProvisioningError as e:
513 raise CommandError("Provision failed", e)
515 result.report_logger(self.logger)
517 def _get_nameserver_ip(self):
518 """Grab the nameserver IP address from /etc/resolv.conf."""
520 RESOLV_CONF="/etc/resolv.conf"
522 if not path.isfile(RESOLV_CONF):
523 self.logger.warning("Failed to locate %s" % RESOLV_CONF)
528 handle = open(RESOLV_CONF, 'r')
530 if not line.startswith('nameserver'):
532 # we want the last non-space continuous string of the line
533 return line.strip().split()[-1]
535 if handle is not None:
538 self.logger.warning("No nameserver found in %s" % RESOLV_CONF)
540 def _adminpass_issue(self, adminpass):
541 """Returns error string for a bad administrator password,
542 or None if acceptable"""
544 if len(adminpass.decode('utf-8')) < DEFAULT_MIN_PWD_LENGTH:
545 return "Administrator password does not meet the default minimum" \
546 " password length requirement (%d characters)" \
547 % DEFAULT_MIN_PWD_LENGTH
548 elif not samba.check_password_quality(adminpass):
549 return "Administrator password does not meet the default" \
555 class cmd_domain_dcpromo(Command):
556 """Promote an existing domain member or NT4 PDC to an AD DC."""
558 synopsis = "%prog <dnsdomain> [DC|RODC] [options]"
560 takes_optiongroups = {
561 "sambaopts": options.SambaOptions,
562 "versionopts": options.VersionOptions,
563 "credopts": options.CredentialsOptions,
567 Option("--server", help="DC to join", type=str),
568 Option("--site", help="site to join", type=str),
569 Option("--targetdir", help="where to store provision", type=str),
570 Option("--domain-critical-only",
571 help="only replicate critical domain objects",
572 action="store_true"),
573 Option("--machinepass", type=str, metavar="PASSWORD",
574 help="choose machine password (otherwise random)"),
575 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
576 choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
577 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
578 "BIND9_DLZ uses samba4 AD to store zone information, "
579 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
580 default="SAMBA_INTERNAL"),
581 Option("--quiet", help="Be quiet", action="store_true"),
582 Option("--verbose", help="Be verbose", action="store_true")
586 Option("--use-ntvfs", action="store_true", help="Use NTVFS for the fileserver (default = no)"),
589 if samba.is_ntvfs_fileserver_built():
590 takes_options.extend(ntvfs_options)
593 takes_args = ["domain", "role?"]
595 def run(self, domain, role=None, sambaopts=None, credopts=None,
596 versionopts=None, server=None, site=None, targetdir=None,
597 domain_critical_only=False, parent_domain=None, machinepass=None,
598 use_ntvfs=False, dns_backend=None,
599 quiet=False, verbose=False):
600 lp = sambaopts.get_loadparm()
601 creds = credopts.get_credentials(lp)
602 net = Net(creds, lp, server=credopts.ipaddress)
604 logger = self.get_logger()
606 logger.setLevel(logging.DEBUG)
608 logger.setLevel(logging.WARNING)
610 logger.setLevel(logging.INFO)
612 netbios_name = lp.get("netbios name")
618 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
619 site=site, netbios_name=netbios_name, targetdir=targetdir,
620 domain_critical_only=domain_critical_only,
621 machinepass=machinepass, use_ntvfs=use_ntvfs,
622 dns_backend=dns_backend,
623 promote_existing=True)
625 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
626 site=site, netbios_name=netbios_name, targetdir=targetdir,
627 domain_critical_only=domain_critical_only,
628 machinepass=machinepass, use_ntvfs=use_ntvfs, dns_backend=dns_backend,
629 promote_existing=True)
631 raise CommandError("Invalid role '%s' (possible values: DC, RODC)" % role)
634 class cmd_domain_join(Command):
635 """Join domain as either member or backup domain controller."""
637 synopsis = "%prog <dnsdomain> [DC|RODC|MEMBER|SUBDOMAIN] [options]"
639 takes_optiongroups = {
640 "sambaopts": options.SambaOptions,
641 "versionopts": options.VersionOptions,
642 "credopts": options.CredentialsOptions,
646 Option("--server", help="DC to join", type=str),
647 Option("--site", help="site to join", type=str),
648 Option("--targetdir", help="where to store provision", type=str),
649 Option("--parent-domain", help="parent domain to create subdomain under", type=str),
650 Option("--domain-critical-only",
651 help="only replicate critical domain objects",
652 action="store_true"),
653 Option("--machinepass", type=str, metavar="PASSWORD",
654 help="choose machine password (otherwise random)"),
655 Option("--adminpass", type="string", metavar="PASSWORD",
656 help="choose adminstrator password when joining as a subdomain (otherwise random)"),
657 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
658 choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
659 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
660 "BIND9_DLZ uses samba4 AD to store zone information, "
661 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
662 default="SAMBA_INTERNAL"),
663 Option("--plaintext-secrets", action="store_true",
664 help="Store secret/sensitive values as plain text on disk" +
665 "(default is to encrypt secret/ensitive values)"),
666 Option("--quiet", help="Be quiet", action="store_true"),
667 Option("--verbose", help="Be verbose", action="store_true")
671 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
674 if samba.is_ntvfs_fileserver_built():
675 takes_options.extend(ntvfs_options)
677 takes_args = ["domain", "role?"]
679 def run(self, domain, role=None, sambaopts=None, credopts=None,
680 versionopts=None, server=None, site=None, targetdir=None,
681 domain_critical_only=False, parent_domain=None, machinepass=None,
682 use_ntvfs=False, dns_backend=None, adminpass=None,
683 quiet=False, verbose=False, plaintext_secrets=False):
684 lp = sambaopts.get_loadparm()
685 creds = credopts.get_credentials(lp)
686 net = Net(creds, lp, server=credopts.ipaddress)
689 site = "Default-First-Site-Name"
691 logger = self.get_logger()
693 logger.setLevel(logging.DEBUG)
695 logger.setLevel(logging.WARNING)
697 logger.setLevel(logging.INFO)
699 netbios_name = lp.get("netbios name")
704 if role is None or role == "MEMBER":
705 (join_password, sid, domain_name) = net.join_member(
706 domain, netbios_name, LIBNET_JOIN_AUTOMATIC,
707 machinepass=machinepass)
709 self.errf.write("Joined domain %s (%s)\n" % (domain_name, sid))
711 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
712 site=site, netbios_name=netbios_name, targetdir=targetdir,
713 domain_critical_only=domain_critical_only,
714 machinepass=machinepass, use_ntvfs=use_ntvfs,
715 dns_backend=dns_backend,
716 plaintext_secrets=plaintext_secrets)
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 elif role == "SUBDOMAIN":
726 logger.info("Administrator password will be set randomly!")
728 netbios_domain = lp.get("workgroup")
729 if parent_domain is None:
730 parent_domain = ".".join(domain.split(".")[1:])
731 join_subdomain(logger=logger, server=server, creds=creds, lp=lp, dnsdomain=domain,
732 parent_domain=parent_domain, site=site,
733 netbios_name=netbios_name, netbios_domain=netbios_domain,
734 targetdir=targetdir, machinepass=machinepass,
735 use_ntvfs=use_ntvfs, dns_backend=dns_backend,
737 plaintext_secrets=plaintext_secrets)
739 raise CommandError("Invalid role '%s' (possible values: MEMBER, DC, RODC, SUBDOMAIN)" % role)
742 class cmd_domain_demote(Command):
743 """Demote ourselves from the role of Domain Controller."""
745 synopsis = "%prog [options]"
748 Option("--server", help="writable DC to write demotion changes on", type=str),
749 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
750 metavar="URL", dest="H"),
751 Option("--remove-other-dead-server", help="Dead DC (name or NTDS GUID) "
752 "to remove ALL references to (rather than this DC)", type=str),
753 Option("--quiet", help="Be quiet", action="store_true"),
754 Option("--verbose", help="Be verbose", action="store_true"),
757 takes_optiongroups = {
758 "sambaopts": options.SambaOptions,
759 "credopts": options.CredentialsOptions,
760 "versionopts": options.VersionOptions,
763 def run(self, sambaopts=None, credopts=None,
764 versionopts=None, server=None,
765 remove_other_dead_server=None, H=None,
766 verbose=False, quiet=False):
767 lp = sambaopts.get_loadparm()
768 creds = credopts.get_credentials(lp)
769 net = Net(creds, lp, server=credopts.ipaddress)
771 logger = self.get_logger()
773 logger.setLevel(logging.DEBUG)
775 logger.setLevel(logging.WARNING)
777 logger.setLevel(logging.INFO)
779 if remove_other_dead_server is not None:
780 if server is not None:
781 samdb = SamDB(url="ldap://%s" % server,
782 session_info=system_session(),
783 credentials=creds, lp=lp)
785 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
787 remove_dc.remove_dc(samdb, logger, remove_other_dead_server)
788 except remove_dc.DemoteException as err:
789 raise CommandError("Demote failed: %s" % err)
792 netbios_name = lp.get("netbios name")
793 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
795 res = samdb.search(expression='(&(objectClass=computer)(serverReferenceBL=*))', attrs=["dnsHostName", "name"])
797 raise CommandError("Unable to search for servers")
800 raise CommandError("You are the latest server in the domain")
804 if str(e["name"]).lower() != netbios_name.lower():
805 server = e["dnsHostName"]
808 ntds_guid = samdb.get_ntds_GUID()
809 msg = samdb.search(base=str(samdb.get_config_basedn()),
810 scope=ldb.SCOPE_SUBTREE, expression="(objectGUID=%s)" % ntds_guid,
812 if len(msg) == 0 or "options" not in msg[0]:
813 raise CommandError("Failed to find options on %s" % ntds_guid)
816 dsa_options = int(str(msg[0]['options']))
818 res = samdb.search(expression="(fSMORoleOwner=%s)" % str(ntds_dn),
819 controls=["search_options:1:2"])
822 raise CommandError("Current DC is still the owner of %d role(s), use the role command to transfer roles to another DC" % len(res))
824 self.errf.write("Using %s as partner server for the demotion\n" %
826 (drsuapiBind, drsuapi_handle, supportedExtensions) = drsuapi_connect(server, lp, creds)
828 self.errf.write("Deactivating inbound replication\n")
833 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
834 dsa_options |= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
835 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
839 self.errf.write("Asking partner server %s to synchronize from us\n"
841 for part in (samdb.get_schema_basedn(),
842 samdb.get_config_basedn(),
843 samdb.get_root_basedn()):
844 nc = drsuapi.DsReplicaObjectIdentifier()
847 req1 = drsuapi.DsReplicaSyncRequest1()
848 req1.naming_context = nc;
849 req1.options = drsuapi.DRSUAPI_DRS_WRIT_REP
850 req1.source_dsa_guid = misc.GUID(ntds_guid)
853 drsuapiBind.DsReplicaSync(drsuapi_handle, 1, req1)
854 except RuntimeError as e1:
855 (werr, string) = e1.args
856 if werr == werror.WERR_DS_DRA_NO_REPLICA:
860 "Error while replicating out last local changes from '%s' for demotion, "
861 "re-enabling inbound replication\n" % part)
862 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
863 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
865 raise CommandError("Error while sending a DsReplicaSync for partition '%s'" % str(part), string)
867 remote_samdb = SamDB(url="ldap://%s" % server,
868 session_info=system_session(),
869 credentials=creds, lp=lp)
871 self.errf.write("Changing userControl and container\n")
872 res = remote_samdb.search(base=str(remote_samdb.domain_dn()),
873 expression="(&(objectClass=user)(sAMAccountName=%s$))" %
874 netbios_name.upper(),
875 attrs=["userAccountControl"])
877 uac = int(str(res[0]["userAccountControl"]))
879 except Exception as e:
880 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
882 "Error while demoting, re-enabling inbound replication\n")
883 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
884 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
886 raise CommandError("Error while changing account control", e)
889 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
891 "Error while demoting, re-enabling inbound replication")
892 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
893 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
895 raise CommandError("Unable to find object with samaccountName = %s$"
896 " in the remote dc" % netbios_name.upper())
900 uac &= ~(UF_SERVER_TRUST_ACCOUNT|UF_TRUSTED_FOR_DELEGATION|UF_PARTIAL_SECRETS_ACCOUNT)
901 uac |= UF_WORKSTATION_TRUST_ACCOUNT
906 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
907 ldb.FLAG_MOD_REPLACE,
908 "userAccountControl")
910 remote_samdb.modify(msg)
911 except Exception as e:
912 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
914 "Error while demoting, re-enabling inbound replication")
915 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
916 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
919 raise CommandError("Error while changing account control", e)
921 parent = msg.dn.parent()
922 dc_name = res[0].dn.get_rdn_value()
923 rdn = "CN=%s" % dc_name
925 # Let's move to the Computer container
929 computer_dn = ldb.Dn(remote_samdb, "CN=Computers,%s" % str(remote_samdb.domain_dn()))
930 res = remote_samdb.search(base=computer_dn, expression=rdn, scope=ldb.SCOPE_ONELEVEL)
933 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
934 scope=ldb.SCOPE_ONELEVEL)
935 while(len(res) != 0 and i < 100):
937 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
938 scope=ldb.SCOPE_ONELEVEL)
941 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
943 "Error while demoting, re-enabling inbound replication\n")
944 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
945 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
951 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
952 ldb.FLAG_MOD_REPLACE,
953 "userAccountControl")
955 remote_samdb.modify(msg)
957 raise CommandError("Unable to find a slot for renaming %s,"
958 " all names from %s-1 to %s-%d seemed used" %
959 (str(dc_dn), rdn, rdn, i - 9))
961 newrdn = "%s-%d" % (rdn, i)
964 newdn = ldb.Dn(remote_samdb, "%s,%s" % (newrdn, str(computer_dn)))
965 remote_samdb.rename(dc_dn, newdn)
966 except Exception as e:
967 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
969 "Error while demoting, re-enabling inbound replication\n")
970 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
971 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
977 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
978 ldb.FLAG_MOD_REPLACE,
979 "userAccountControl")
981 remote_samdb.modify(msg)
982 raise CommandError("Error while renaming %s to %s" % (str(dc_dn), str(newdn)), e)
985 server_dsa_dn = samdb.get_serverName()
986 domain = remote_samdb.get_root_basedn()
989 req1 = drsuapi.DsRemoveDSServerRequest1()
990 req1.server_dn = str(server_dsa_dn)
991 req1.domain_dn = str(domain)
994 drsuapiBind.DsRemoveDSServer(drsuapi_handle, 1, req1)
995 except RuntimeError as e3:
996 (werr, string) = e3.args
997 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
999 "Error while demoting, re-enabling inbound replication\n")
1000 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
1001 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
1007 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
1008 ldb.FLAG_MOD_REPLACE,
1009 "userAccountControl")
1010 remote_samdb.modify(msg)
1011 remote_samdb.rename(newdn, dc_dn)
1012 if werr == werror.WERR_DS_DRA_NO_REPLICA:
1013 raise CommandError("The DC %s is not present on (already removed from) the remote server: " % server_dsa_dn, e)
1015 raise CommandError("Error while sending a removeDsServer of %s: " % server_dsa_dn, e)
1017 remove_dc.remove_sysvol_references(remote_samdb, logger, dc_name)
1019 # These are objects under the computer account that should be deleted
1020 for s in ("CN=Enterprise,CN=NTFRS Subscriptions",
1021 "CN=%s, CN=NTFRS Subscriptions" % lp.get("realm"),
1022 "CN=Domain system Volumes (SYSVOL Share), CN=NTFRS Subscriptions",
1023 "CN=NTFRS Subscriptions"):
1025 remote_samdb.delete(ldb.Dn(remote_samdb,
1026 "%s,%s" % (s, str(newdn))))
1027 except ldb.LdbError as l:
1030 self.errf.write("Demote successful\n")
1033 class cmd_domain_level(Command):
1034 """Raise domain and forest function levels."""
1036 synopsis = "%prog (show|raise <options>) [options]"
1038 takes_optiongroups = {
1039 "sambaopts": options.SambaOptions,
1040 "credopts": options.CredentialsOptions,
1041 "versionopts": options.VersionOptions,
1045 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1046 metavar="URL", dest="H"),
1047 Option("--quiet", help="Be quiet", action="store_true"),
1048 Option("--forest-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
1049 help="The forest function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)"),
1050 Option("--domain-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
1051 help="The domain function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)")
1054 takes_args = ["subcommand"]
1056 def run(self, subcommand, H=None, forest_level=None, domain_level=None,
1057 quiet=False, credopts=None, sambaopts=None, versionopts=None):
1058 lp = sambaopts.get_loadparm()
1059 creds = credopts.get_credentials(lp, fallback_machine=True)
1061 samdb = SamDB(url=H, session_info=system_session(),
1062 credentials=creds, lp=lp)
1064 domain_dn = samdb.domain_dn()
1066 res_forest = samdb.search("CN=Partitions,%s" % samdb.get_config_basedn(),
1067 scope=ldb.SCOPE_BASE, attrs=["msDS-Behavior-Version"])
1068 assert len(res_forest) == 1
1070 res_domain = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1071 attrs=["msDS-Behavior-Version", "nTMixedDomain"])
1072 assert len(res_domain) == 1
1074 res_dc_s = samdb.search("CN=Sites,%s" % samdb.get_config_basedn(),
1075 scope=ldb.SCOPE_SUBTREE, expression="(objectClass=nTDSDSA)",
1076 attrs=["msDS-Behavior-Version"])
1077 assert len(res_dc_s) >= 1
1079 # default values, since "msDS-Behavior-Version" does not exist on Windows 2000 AD
1080 level_forest = DS_DOMAIN_FUNCTION_2000
1081 level_domain = DS_DOMAIN_FUNCTION_2000
1083 if "msDS-Behavior-Version" in res_forest[0]:
1084 level_forest = int(res_forest[0]["msDS-Behavior-Version"][0])
1085 if "msDS-Behavior-Version" in res_domain[0]:
1086 level_domain = int(res_domain[0]["msDS-Behavior-Version"][0])
1087 level_domain_mixed = int(res_domain[0]["nTMixedDomain"][0])
1090 for msg in res_dc_s:
1091 if "msDS-Behavior-Version" in msg:
1092 if min_level_dc is None or int(msg["msDS-Behavior-Version"][0]) < min_level_dc:
1093 min_level_dc = int(msg["msDS-Behavior-Version"][0])
1095 min_level_dc = DS_DOMAIN_FUNCTION_2000
1096 # well, this is the least
1099 if level_forest < DS_DOMAIN_FUNCTION_2000 or level_domain < DS_DOMAIN_FUNCTION_2000:
1100 raise CommandError("Domain and/or forest function level(s) is/are invalid. Correct them or reprovision!")
1101 if min_level_dc < DS_DOMAIN_FUNCTION_2000:
1102 raise CommandError("Lowest function level of a DC is invalid. Correct this or reprovision!")
1103 if level_forest > level_domain:
1104 raise CommandError("Forest function level is higher than the domain level(s). Correct this or reprovision!")
1105 if level_domain > min_level_dc:
1106 raise CommandError("Domain function level is higher than the lowest function level of a DC. Correct this or reprovision!")
1108 if subcommand == "show":
1109 self.message("Domain and forest function level for domain '%s'" % domain_dn)
1110 if level_forest == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1111 self.message("\nATTENTION: You run SAMBA 4 on a forest function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1112 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1113 self.message("\nATTENTION: You run SAMBA 4 on a domain function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1114 if min_level_dc == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1115 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)!")
1119 if level_forest == DS_DOMAIN_FUNCTION_2000:
1121 elif level_forest == DS_DOMAIN_FUNCTION_2003_MIXED:
1122 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1123 elif level_forest == DS_DOMAIN_FUNCTION_2003:
1125 elif level_forest == DS_DOMAIN_FUNCTION_2008:
1127 elif level_forest == DS_DOMAIN_FUNCTION_2008_R2:
1129 elif level_forest == DS_DOMAIN_FUNCTION_2012:
1131 elif level_forest == DS_DOMAIN_FUNCTION_2012_R2:
1134 outstr = "higher than 2012 R2"
1135 self.message("Forest function level: (Windows) " + outstr)
1137 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1138 outstr = "2000 mixed (NT4 DC support)"
1139 elif level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed == 0:
1141 elif level_domain == DS_DOMAIN_FUNCTION_2003_MIXED:
1142 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1143 elif level_domain == DS_DOMAIN_FUNCTION_2003:
1145 elif level_domain == DS_DOMAIN_FUNCTION_2008:
1147 elif level_domain == DS_DOMAIN_FUNCTION_2008_R2:
1149 elif level_domain == DS_DOMAIN_FUNCTION_2012:
1151 elif level_domain == DS_DOMAIN_FUNCTION_2012_R2:
1154 outstr = "higher than 2012 R2"
1155 self.message("Domain function level: (Windows) " + outstr)
1157 if min_level_dc == DS_DOMAIN_FUNCTION_2000:
1159 elif min_level_dc == DS_DOMAIN_FUNCTION_2003:
1161 elif min_level_dc == DS_DOMAIN_FUNCTION_2008:
1163 elif min_level_dc == DS_DOMAIN_FUNCTION_2008_R2:
1165 elif min_level_dc == DS_DOMAIN_FUNCTION_2012:
1167 elif min_level_dc == DS_DOMAIN_FUNCTION_2012_R2:
1170 outstr = "higher than 2012 R2"
1171 self.message("Lowest function level of a DC: (Windows) " + outstr)
1173 elif subcommand == "raise":
1176 if domain_level is not None:
1177 if domain_level == "2003":
1178 new_level_domain = DS_DOMAIN_FUNCTION_2003
1179 elif domain_level == "2008":
1180 new_level_domain = DS_DOMAIN_FUNCTION_2008
1181 elif domain_level == "2008_R2":
1182 new_level_domain = DS_DOMAIN_FUNCTION_2008_R2
1183 elif domain_level == "2012":
1184 new_level_domain = DS_DOMAIN_FUNCTION_2012
1185 elif domain_level == "2012_R2":
1186 new_level_domain = DS_DOMAIN_FUNCTION_2012_R2
1188 if new_level_domain <= level_domain and level_domain_mixed == 0:
1189 raise CommandError("Domain function level can't be smaller than or equal to the actual one!")
1190 if new_level_domain > min_level_dc:
1191 raise CommandError("Domain function level can't be higher than the lowest function level of a DC!")
1193 # Deactivate mixed/interim domain support
1194 if level_domain_mixed != 0:
1195 # Directly on the base DN
1197 m.dn = ldb.Dn(samdb, domain_dn)
1198 m["nTMixedDomain"] = ldb.MessageElement("0",
1199 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1203 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup") + ",CN=Partitions,%s" % samdb.get_config_basedn())
1204 m["nTMixedDomain"] = ldb.MessageElement("0",
1205 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1208 except ldb.LdbError as e:
1209 (enum, emsg) = e.args
1210 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1213 # Directly on the base DN
1215 m.dn = ldb.Dn(samdb, domain_dn)
1216 m["msDS-Behavior-Version"]= ldb.MessageElement(
1217 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1218 "msDS-Behavior-Version")
1222 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup")
1223 + ",CN=Partitions,%s" % samdb.get_config_basedn())
1224 m["msDS-Behavior-Version"]= ldb.MessageElement(
1225 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1226 "msDS-Behavior-Version")
1229 except ldb.LdbError as e2:
1230 (enum, emsg) = e2.args
1231 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1234 level_domain = new_level_domain
1235 msgs.append("Domain function level changed!")
1237 if forest_level is not None:
1238 if forest_level == "2003":
1239 new_level_forest = DS_DOMAIN_FUNCTION_2003
1240 elif forest_level == "2008":
1241 new_level_forest = DS_DOMAIN_FUNCTION_2008
1242 elif forest_level == "2008_R2":
1243 new_level_forest = DS_DOMAIN_FUNCTION_2008_R2
1244 elif forest_level == "2012":
1245 new_level_forest = DS_DOMAIN_FUNCTION_2012
1246 elif forest_level == "2012_R2":
1247 new_level_forest = DS_DOMAIN_FUNCTION_2012_R2
1249 if new_level_forest <= level_forest:
1250 raise CommandError("Forest function level can't be smaller than or equal to the actual one!")
1251 if new_level_forest > level_domain:
1252 raise CommandError("Forest function level can't be higher than the domain function level(s). Please raise it/them first!")
1255 m.dn = ldb.Dn(samdb, "CN=Partitions,%s" % samdb.get_config_basedn())
1256 m["msDS-Behavior-Version"]= ldb.MessageElement(
1257 str(new_level_forest), ldb.FLAG_MOD_REPLACE,
1258 "msDS-Behavior-Version")
1260 msgs.append("Forest function level changed!")
1261 msgs.append("All changes applied successfully!")
1262 self.message("\n".join(msgs))
1264 raise CommandError("invalid argument: '%s' (choose from 'show', 'raise')" % subcommand)
1266 class cmd_domain_passwordsettings_show(Command):
1267 """Display current password settings for the domain."""
1269 synopsis = "%prog [options]"
1271 takes_optiongroups = {
1272 "sambaopts": options.SambaOptions,
1273 "versionopts": options.VersionOptions,
1274 "credopts": options.CredentialsOptions,
1278 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1279 metavar="URL", dest="H"),
1282 def run(self, H=None, credopts=None, sambaopts=None, versionopts=None):
1283 lp = sambaopts.get_loadparm()
1284 creds = credopts.get_credentials(lp)
1286 samdb = SamDB(url=H, session_info=system_session(),
1287 credentials=creds, lp=lp)
1289 domain_dn = samdb.domain_dn()
1290 res = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1291 attrs=["pwdProperties", "pwdHistoryLength", "minPwdLength",
1292 "minPwdAge", "maxPwdAge", "lockoutDuration", "lockoutThreshold",
1293 "lockOutObservationWindow"])
1294 assert(len(res) == 1)
1296 pwd_props = int(res[0]["pwdProperties"][0])
1297 pwd_hist_len = int(res[0]["pwdHistoryLength"][0])
1298 cur_min_pwd_len = int(res[0]["minPwdLength"][0])
1300 cur_min_pwd_age = int(abs(int(res[0]["minPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1301 if int(res[0]["maxPwdAge"][0]) == -0x8000000000000000:
1304 cur_max_pwd_age = int(abs(int(res[0]["maxPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1305 cur_account_lockout_threshold = int(res[0]["lockoutThreshold"][0])
1307 if int(res[0]["lockoutDuration"][0]) == -0x8000000000000000:
1308 cur_account_lockout_duration = 0
1310 cur_account_lockout_duration = abs(int(res[0]["lockoutDuration"][0])) / (1e7 * 60)
1311 cur_reset_account_lockout_after = abs(int(res[0]["lockOutObservationWindow"][0])) / (1e7 * 60)
1312 except Exception as e:
1313 raise CommandError("Could not retrieve password properties!", e)
1315 self.message("Password informations for domain '%s'" % domain_dn)
1317 if pwd_props & DOMAIN_PASSWORD_COMPLEX != 0:
1318 self.message("Password complexity: on")
1320 self.message("Password complexity: off")
1321 if pwd_props & DOMAIN_PASSWORD_STORE_CLEARTEXT != 0:
1322 self.message("Store plaintext passwords: on")
1324 self.message("Store plaintext passwords: off")
1325 self.message("Password history length: %d" % pwd_hist_len)
1326 self.message("Minimum password length: %d" % cur_min_pwd_len)
1327 self.message("Minimum password age (days): %d" % cur_min_pwd_age)
1328 self.message("Maximum password age (days): %d" % cur_max_pwd_age)
1329 self.message("Account lockout duration (mins): %d" % cur_account_lockout_duration)
1330 self.message("Account lockout threshold (attempts): %d" % cur_account_lockout_threshold)
1331 self.message("Reset account lockout after (mins): %d" % cur_reset_account_lockout_after)
1333 class cmd_domain_passwordsettings_set(Command):
1334 """Set password settings.
1336 Password complexity, password lockout policy, history length,
1337 minimum password length, the minimum and maximum password age) on
1338 a Samba AD DC server.
1340 Use against a Windows DC is possible, but group policy will override it.
1343 synopsis = "%prog <options> [options]"
1345 takes_optiongroups = {
1346 "sambaopts": options.SambaOptions,
1347 "versionopts": options.VersionOptions,
1348 "credopts": options.CredentialsOptions,
1352 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1353 metavar="URL", dest="H"),
1354 Option("--quiet", help="Be quiet", action="store_true"),
1355 Option("--complexity", type="choice", choices=["on","off","default"],
1356 help="The password complexity (on | off | default). Default is 'on'"),
1357 Option("--store-plaintext", type="choice", choices=["on","off","default"],
1358 help="Store plaintext passwords where account have 'store passwords with reversible encryption' set (on | off | default). Default is 'off'"),
1359 Option("--history-length",
1360 help="The password history length (<integer> | default). Default is 24.", type=str),
1361 Option("--min-pwd-length",
1362 help="The minimum password length (<integer> | default). Default is 7.", type=str),
1363 Option("--min-pwd-age",
1364 help="The minimum password age (<integer in days> | default). Default is 1.", type=str),
1365 Option("--max-pwd-age",
1366 help="The maximum password age (<integer in days> | default). Default is 43.", type=str),
1367 Option("--account-lockout-duration",
1368 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),
1369 Option("--account-lockout-threshold",
1370 help="The number of bad password attempts allowed before locking out the account (<integer> | default). Default is 0 (never lock out).", type=str),
1371 Option("--reset-account-lockout-after",
1372 help="After this time is elapsed, the recorded number of attempts restarts from zero (<integer> | default). Default is 30.", type=str),
1375 def run(self, H=None, min_pwd_age=None, max_pwd_age=None,
1376 quiet=False, complexity=None, store_plaintext=None, history_length=None,
1377 min_pwd_length=None, account_lockout_duration=None, account_lockout_threshold=None,
1378 reset_account_lockout_after=None, credopts=None, sambaopts=None,
1380 lp = sambaopts.get_loadparm()
1381 creds = credopts.get_credentials(lp)
1383 samdb = SamDB(url=H, session_info=system_session(),
1384 credentials=creds, lp=lp)
1386 domain_dn = samdb.domain_dn()
1389 m.dn = ldb.Dn(samdb, domain_dn)
1390 pwd_props = int(samdb.get_pwdProperties())
1392 if complexity is not None:
1393 if complexity == "on" or complexity == "default":
1394 pwd_props = pwd_props | DOMAIN_PASSWORD_COMPLEX
1395 msgs.append("Password complexity activated!")
1396 elif complexity == "off":
1397 pwd_props = pwd_props & (~DOMAIN_PASSWORD_COMPLEX)
1398 msgs.append("Password complexity deactivated!")
1400 if store_plaintext is not None:
1401 if store_plaintext == "on" or store_plaintext == "default":
1402 pwd_props = pwd_props | DOMAIN_PASSWORD_STORE_CLEARTEXT
1403 msgs.append("Plaintext password storage for changed passwords activated!")
1404 elif store_plaintext == "off":
1405 pwd_props = pwd_props & (~DOMAIN_PASSWORD_STORE_CLEARTEXT)
1406 msgs.append("Plaintext password storage for changed passwords deactivated!")
1408 if complexity is not None or store_plaintext is not None:
1409 m["pwdProperties"] = ldb.MessageElement(str(pwd_props),
1410 ldb.FLAG_MOD_REPLACE, "pwdProperties")
1412 if history_length is not None:
1413 if history_length == "default":
1416 pwd_hist_len = int(history_length)
1418 if pwd_hist_len < 0 or pwd_hist_len > 24:
1419 raise CommandError("Password history length must be in the range of 0 to 24!")
1421 m["pwdHistoryLength"] = ldb.MessageElement(str(pwd_hist_len),
1422 ldb.FLAG_MOD_REPLACE, "pwdHistoryLength")
1423 msgs.append("Password history length changed!")
1425 if min_pwd_length is not None:
1426 if min_pwd_length == "default":
1429 min_pwd_len = int(min_pwd_length)
1431 if min_pwd_len < 0 or min_pwd_len > 14:
1432 raise CommandError("Minimum password length must be in the range of 0 to 14!")
1434 m["minPwdLength"] = ldb.MessageElement(str(min_pwd_len),
1435 ldb.FLAG_MOD_REPLACE, "minPwdLength")
1436 msgs.append("Minimum password length changed!")
1438 if min_pwd_age is not None:
1439 if min_pwd_age == "default":
1442 min_pwd_age = int(min_pwd_age)
1444 if min_pwd_age < 0 or min_pwd_age > 998:
1445 raise CommandError("Minimum password age must be in the range of 0 to 998!")
1448 min_pwd_age_ticks = -int(min_pwd_age * (24 * 60 * 60 * 1e7))
1450 m["minPwdAge"] = ldb.MessageElement(str(min_pwd_age_ticks),
1451 ldb.FLAG_MOD_REPLACE, "minPwdAge")
1452 msgs.append("Minimum password age changed!")
1454 if max_pwd_age is not None:
1455 if max_pwd_age == "default":
1458 max_pwd_age = int(max_pwd_age)
1460 if max_pwd_age < 0 or max_pwd_age > 999:
1461 raise CommandError("Maximum password age must be in the range of 0 to 999!")
1464 if max_pwd_age == 0:
1465 max_pwd_age_ticks = -0x8000000000000000
1467 max_pwd_age_ticks = -int(max_pwd_age * (24 * 60 * 60 * 1e7))
1469 m["maxPwdAge"] = ldb.MessageElement(str(max_pwd_age_ticks),
1470 ldb.FLAG_MOD_REPLACE, "maxPwdAge")
1471 msgs.append("Maximum password age changed!")
1473 if account_lockout_duration is not None:
1474 if account_lockout_duration == "default":
1475 account_lockout_duration = 30
1477 account_lockout_duration = int(account_lockout_duration)
1479 if account_lockout_duration < 0 or account_lockout_duration > 99999:
1480 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1483 if account_lockout_duration == 0:
1484 account_lockout_duration_ticks = -0x8000000000000000
1486 account_lockout_duration_ticks = -int(account_lockout_duration * (60 * 1e7))
1488 m["lockoutDuration"] = ldb.MessageElement(str(account_lockout_duration_ticks),
1489 ldb.FLAG_MOD_REPLACE, "lockoutDuration")
1490 msgs.append("Account lockout duration changed!")
1492 if account_lockout_threshold is not None:
1493 if account_lockout_threshold == "default":
1494 account_lockout_threshold = 0
1496 account_lockout_threshold = int(account_lockout_threshold)
1498 m["lockoutThreshold"] = ldb.MessageElement(str(account_lockout_threshold),
1499 ldb.FLAG_MOD_REPLACE, "lockoutThreshold")
1500 msgs.append("Account lockout threshold changed!")
1502 if reset_account_lockout_after is not None:
1503 if reset_account_lockout_after == "default":
1504 reset_account_lockout_after = 30
1506 reset_account_lockout_after = int(reset_account_lockout_after)
1508 if reset_account_lockout_after < 0 or reset_account_lockout_after > 99999:
1509 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1512 if reset_account_lockout_after == 0:
1513 reset_account_lockout_after_ticks = -0x8000000000000000
1515 reset_account_lockout_after_ticks = -int(reset_account_lockout_after * (60 * 1e7))
1517 m["lockOutObservationWindow"] = ldb.MessageElement(str(reset_account_lockout_after_ticks),
1518 ldb.FLAG_MOD_REPLACE, "lockOutObservationWindow")
1519 msgs.append("Duration to reset account lockout after changed!")
1521 if max_pwd_age > 0 and min_pwd_age >= max_pwd_age:
1522 raise CommandError("Maximum password age (%d) must be greater than minimum password age (%d)!" % (max_pwd_age, min_pwd_age))
1525 raise CommandError("You must specify at least one option to set. Try --help")
1527 msgs.append("All changes applied successfully!")
1528 self.message("\n".join(msgs))
1530 class cmd_domain_passwordsettings(SuperCommand):
1531 """Manage password policy settings."""
1534 subcommands["show"] = cmd_domain_passwordsettings_show()
1535 subcommands["set"] = cmd_domain_passwordsettings_set()
1537 class cmd_domain_classicupgrade(Command):
1538 """Upgrade from Samba classic (NT4-like) database to Samba AD DC database.
1540 Specify either a directory with all Samba classic DC databases and state files (with --dbdir) or
1541 the testparm utility from your classic installation (with --testparm).
1544 synopsis = "%prog [options] <classic_smb_conf>"
1546 takes_optiongroups = {
1547 "sambaopts": options.SambaOptions,
1548 "versionopts": options.VersionOptions
1552 Option("--dbdir", type="string", metavar="DIR",
1553 help="Path to samba classic DC database directory"),
1554 Option("--testparm", type="string", metavar="PATH",
1555 help="Path to samba classic DC testparm utility from the previous installation. This allows the default paths of the previous installation to be followed"),
1556 Option("--targetdir", type="string", metavar="DIR",
1557 help="Path prefix where the new Samba 4.0 AD domain should be initialised"),
1558 Option("--quiet", help="Be quiet", action="store_true"),
1559 Option("--verbose", help="Be verbose", action="store_true"),
1560 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
1561 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
1562 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
1563 "BIND9_FLATFILE uses bind9 text database to store zone information, "
1564 "BIND9_DLZ uses samba4 AD to store zone information, "
1565 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
1566 default="SAMBA_INTERNAL")
1570 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
1571 action="store_true"),
1572 Option("--use-xattrs", type="choice", choices=["yes","no","auto"],
1573 metavar="[yes|no|auto]",
1574 help="Define if we should use the native fs capabilities or a tdb file for "
1575 "storing attributes likes ntacl when --use-ntvfs is set. "
1576 "auto tries to make an inteligent guess based on the user rights and system capabilities",
1579 if samba.is_ntvfs_fileserver_built():
1580 takes_options.extend(ntvfs_options)
1582 takes_args = ["smbconf"]
1584 def run(self, smbconf=None, targetdir=None, dbdir=None, testparm=None,
1585 quiet=False, verbose=False, use_xattrs="auto", sambaopts=None, versionopts=None,
1586 dns_backend=None, use_ntvfs=False):
1588 if not os.path.exists(smbconf):
1589 raise CommandError("File %s does not exist" % smbconf)
1591 if testparm and not os.path.exists(testparm):
1592 raise CommandError("Testparm utility %s does not exist" % testparm)
1594 if dbdir and not os.path.exists(dbdir):
1595 raise CommandError("Directory %s does not exist" % dbdir)
1597 if not dbdir and not testparm:
1598 raise CommandError("Please specify either dbdir or testparm")
1600 logger = self.get_logger()
1602 logger.setLevel(logging.DEBUG)
1604 logger.setLevel(logging.WARNING)
1606 logger.setLevel(logging.INFO)
1608 if dbdir and testparm:
1609 logger.warning("both dbdir and testparm specified, ignoring dbdir.")
1612 lp = sambaopts.get_loadparm()
1614 s3conf = s3param.get_context()
1617 s3conf.set("realm", sambaopts.realm)
1619 if targetdir is not None:
1620 if not os.path.isdir(targetdir):
1624 if use_xattrs == "yes":
1626 elif use_xattrs == "auto" and use_ntvfs == False:
1628 elif use_ntvfs == False:
1629 raise CommandError("--use-xattrs=no requires --use-ntvfs (not supported for production use). "
1630 "Please re-run with --use-xattrs omitted.")
1631 elif use_xattrs == "auto" and not s3conf.get("posix:eadb"):
1633 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
1635 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
1638 samba.ntacls.setntacl(lp, tmpfile.name,
1639 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
1642 # FIXME: Don't catch all exceptions here
1643 logger.info("You are not root or your system does not support xattr, using tdb backend for attributes. "
1644 "If you intend to use this provision in production, rerun the script as root on a system supporting xattrs.")
1648 # Set correct default values from dbdir or testparm
1651 paths["state directory"] = dbdir
1652 paths["private dir"] = dbdir
1653 paths["lock directory"] = dbdir
1654 paths["smb passwd file"] = dbdir + "/smbpasswd"
1656 paths["state directory"] = get_testparm_var(testparm, smbconf, "state directory")
1657 paths["private dir"] = get_testparm_var(testparm, smbconf, "private dir")
1658 paths["smb passwd file"] = get_testparm_var(testparm, smbconf, "smb passwd file")
1659 paths["lock directory"] = get_testparm_var(testparm, smbconf, "lock directory")
1660 # "testparm" from Samba 3 < 3.4.x is not aware of the parameter
1661 # "state directory", instead make use of "lock directory"
1662 if len(paths["state directory"]) == 0:
1663 paths["state directory"] = paths["lock directory"]
1666 s3conf.set(p, paths[p])
1668 # load smb.conf parameters
1669 logger.info("Reading smb.conf")
1670 s3conf.load(smbconf)
1671 samba3 = Samba3(smbconf, s3conf)
1673 logger.info("Provisioning")
1674 upgrade_from_samba3(samba3, logger, targetdir, session_info=system_session(),
1675 useeadb=eadb, dns_backend=dns_backend, use_ntvfs=use_ntvfs)
1678 class cmd_domain_samba3upgrade(cmd_domain_classicupgrade):
1679 __doc__ = cmd_domain_classicupgrade.__doc__
1681 # This command is present for backwards compatibility only,
1682 # and should not be shown.
1686 class LocalDCCredentialsOptions(options.CredentialsOptions):
1687 def __init__(self, parser):
1688 options.CredentialsOptions.__init__(self, parser, special_name="local-dc")
1690 class DomainTrustCommand(Command):
1691 """List domain trusts."""
1694 Command.__init__(self)
1695 self.local_lp = None
1697 self.local_server = None
1698 self.local_binding_string = None
1699 self.local_creds = None
1701 self.remote_server = None
1702 self.remote_binding_string = None
1703 self.remote_creds = None
1705 def _uint32(self, v):
1706 return ctypes.c_uint32(v).value
1708 def check_runtime_error(self, runtime, val):
1712 err32 = self._uint32(runtime[0])
1718 class LocalRuntimeError(CommandError):
1719 def __init__(exception_self, self, runtime, message):
1720 err32 = self._uint32(runtime[0])
1722 msg = "LOCAL_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1723 self.local_server, message, err32, errstr)
1724 CommandError.__init__(exception_self, msg)
1726 class RemoteRuntimeError(CommandError):
1727 def __init__(exception_self, self, runtime, message):
1728 err32 = self._uint32(runtime[0])
1730 msg = "REMOTE_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1731 self.remote_server, message, err32, errstr)
1732 CommandError.__init__(exception_self, msg)
1734 class LocalLdbError(CommandError):
1735 def __init__(exception_self, self, ldb_error, message):
1736 errval = ldb_error[0]
1737 errstr = ldb_error[1]
1738 msg = "LOCAL_DC[%s]: %s - ERROR(%d) - %s" % (
1739 self.local_server, message, errval, errstr)
1740 CommandError.__init__(exception_self, msg)
1742 def setup_local_server(self, sambaopts, localdcopts):
1743 if self.local_server is not None:
1744 return self.local_server
1746 lp = sambaopts.get_loadparm()
1748 local_server = localdcopts.ipaddress
1749 if local_server is None:
1750 server_role = lp.server_role()
1751 if server_role != "ROLE_ACTIVE_DIRECTORY_DC":
1752 raise CommandError("Invalid server_role %s" % (server_role))
1753 local_server = lp.get('netbios name')
1754 local_transport = "ncalrpc"
1755 local_binding_options = ""
1756 local_binding_options += ",auth_type=ncalrpc_as_system"
1757 local_ldap_url = None
1760 local_transport = "ncacn_np"
1761 local_binding_options = ""
1762 local_ldap_url = "ldap://%s" % local_server
1763 local_creds = localdcopts.get_credentials(lp)
1767 self.local_server = local_server
1768 self.local_binding_string = "%s:%s[%s]" % (local_transport, local_server, local_binding_options)
1769 self.local_ldap_url = local_ldap_url
1770 self.local_creds = local_creds
1771 return self.local_server
1773 def new_local_lsa_connection(self):
1774 return lsa.lsarpc(self.local_binding_string, self.local_lp, self.local_creds)
1776 def new_local_netlogon_connection(self):
1777 return netlogon.netlogon(self.local_binding_string, self.local_lp, self.local_creds)
1779 def new_local_ldap_connection(self):
1780 return SamDB(url=self.local_ldap_url,
1781 session_info=system_session(),
1782 credentials=self.local_creds,
1785 def setup_remote_server(self, credopts, domain,
1787 require_writable=True):
1790 assert require_writable
1792 if self.remote_server is not None:
1793 return self.remote_server
1795 self.remote_server = "__unknown__remote_server__.%s" % domain
1796 assert self.local_server is not None
1798 remote_creds = credopts.get_credentials(self.local_lp)
1799 remote_server = credopts.ipaddress
1800 remote_binding_options = ""
1802 # TODO: we should also support NT4 domains
1803 # we could use local_netlogon.netr_DsRGetDCNameEx2() with the remote domain name
1804 # and delegate NBT or CLDAP to the local netlogon server
1806 remote_net = Net(remote_creds, self.local_lp, server=remote_server)
1807 remote_flags = nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS
1808 if require_writable:
1809 remote_flags |= nbt.NBT_SERVER_WRITABLE
1811 remote_flags |= nbt.NBT_SERVER_PDC
1812 remote_info = remote_net.finddc(flags=remote_flags, domain=domain, address=remote_server)
1813 except NTSTATUSError as error:
1814 raise CommandError("Failed to find a writeable DC for domain '%s': %s" %
1817 raise CommandError("Failed to find a writeable DC for domain '%s'" % domain)
1819 nbt.NBT_SERVER_PDC: "PDC",
1820 nbt.NBT_SERVER_GC: "GC",
1821 nbt.NBT_SERVER_LDAP: "LDAP",
1822 nbt.NBT_SERVER_DS: "DS",
1823 nbt.NBT_SERVER_KDC: "KDC",
1824 nbt.NBT_SERVER_TIMESERV: "TIMESERV",
1825 nbt.NBT_SERVER_CLOSEST: "CLOSEST",
1826 nbt.NBT_SERVER_WRITABLE: "WRITABLE",
1827 nbt.NBT_SERVER_GOOD_TIMESERV: "GOOD_TIMESERV",
1828 nbt.NBT_SERVER_NDNC: "NDNC",
1829 nbt.NBT_SERVER_SELECT_SECRET_DOMAIN_6: "SELECT_SECRET_DOMAIN_6",
1830 nbt.NBT_SERVER_FULL_SECRET_DOMAIN_6: "FULL_SECRET_DOMAIN_6",
1831 nbt.NBT_SERVER_ADS_WEB_SERVICE: "ADS_WEB_SERVICE",
1832 nbt.NBT_SERVER_DS_8: "DS_8",
1833 nbt.NBT_SERVER_HAS_DNS_NAME: "HAS_DNS_NAME",
1834 nbt.NBT_SERVER_IS_DEFAULT_NC: "IS_DEFAULT_NC",
1835 nbt.NBT_SERVER_FOREST_ROOT: "FOREST_ROOT",
1837 server_type_string = self.generic_bitmap_to_string(flag_map,
1838 remote_info.server_type, names_only=True)
1839 self.outf.write("RemoteDC Netbios[%s] DNS[%s] ServerType[%s]\n" % (
1840 remote_info.pdc_name,
1841 remote_info.pdc_dns_name,
1842 server_type_string))
1844 self.remote_server = remote_info.pdc_dns_name
1845 self.remote_binding_string="ncacn_np:%s[%s]" % (self.remote_server, remote_binding_options)
1846 self.remote_creds = remote_creds
1847 return self.remote_server
1849 def new_remote_lsa_connection(self):
1850 return lsa.lsarpc(self.remote_binding_string, self.local_lp, self.remote_creds)
1852 def new_remote_netlogon_connection(self):
1853 return netlogon.netlogon(self.remote_binding_string, self.local_lp, self.remote_creds)
1855 def get_lsa_info(self, conn, policy_access):
1856 objectAttr = lsa.ObjectAttribute()
1857 objectAttr.sec_qos = lsa.QosInfo()
1859 policy = conn.OpenPolicy2(''.decode('utf-8'),
1860 objectAttr, policy_access)
1862 info = conn.QueryInfoPolicy2(policy, lsa.LSA_POLICY_INFO_DNS)
1864 return (policy, info)
1866 def get_netlogon_dc_info(self, conn, server):
1867 info = conn.netr_DsRGetDCNameEx2(server,
1868 None, 0, None, None, None,
1869 netlogon.DS_RETURN_DNS_NAME)
1872 def netr_DomainTrust_to_name(self, t):
1873 if t.trust_type == lsa.LSA_TRUST_TYPE_DOWNLEVEL:
1874 return t.netbios_name
1878 def netr_DomainTrust_to_type(self, a, t):
1880 primary_parent = None
1882 if _t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
1884 if not _t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1885 primary_parent = a[_t.parent_index]
1888 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1889 if t is primary_parent:
1892 if t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1895 parent = a[t.parent_index]
1896 if parent is primary:
1901 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1906 def netr_DomainTrust_to_transitive(self, t):
1907 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1910 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE:
1913 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1918 def netr_DomainTrust_to_direction(self, t):
1919 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND and \
1920 t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1923 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND:
1926 if t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1931 def generic_enum_to_string(self, e_dict, v, names_only=False):
1935 v32 = self._uint32(v)
1936 w = "__unknown__%08X__" % v32
1938 r = "0x%x (%s)" % (v, w)
1941 def generic_bitmap_to_string(self, b_dict, v, names_only=False):
1946 for b in sorted(b_dict.keys()):
1953 c32 = self._uint32(c)
1954 s += ["__unknown_%08X__" % c32]
1959 r = "0x%x (%s)" % (v, w)
1962 def trustType_string(self, v):
1964 lsa.LSA_TRUST_TYPE_DOWNLEVEL : "DOWNLEVEL",
1965 lsa.LSA_TRUST_TYPE_UPLEVEL : "UPLEVEL",
1966 lsa.LSA_TRUST_TYPE_MIT : "MIT",
1967 lsa.LSA_TRUST_TYPE_DCE : "DCE",
1969 return self.generic_enum_to_string(types, v)
1971 def trustDirection_string(self, v):
1973 lsa.LSA_TRUST_DIRECTION_INBOUND |
1974 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "BOTH",
1975 lsa.LSA_TRUST_DIRECTION_INBOUND : "INBOUND",
1976 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "OUTBOUND",
1978 return self.generic_enum_to_string(directions, v)
1980 def trustAttributes_string(self, v):
1982 lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE : "NON_TRANSITIVE",
1983 lsa.LSA_TRUST_ATTRIBUTE_UPLEVEL_ONLY : "UPLEVEL_ONLY",
1984 lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN : "QUARANTINED_DOMAIN",
1985 lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE : "FOREST_TRANSITIVE",
1986 lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION : "CROSS_ORGANIZATION",
1987 lsa.LSA_TRUST_ATTRIBUTE_WITHIN_FOREST : "WITHIN_FOREST",
1988 lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL : "TREAT_AS_EXTERNAL",
1989 lsa.LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION : "USES_RC4_ENCRYPTION",
1991 return self.generic_bitmap_to_string(attributes, v)
1993 def kerb_EncTypes_string(self, v):
1995 security.KERB_ENCTYPE_DES_CBC_CRC : "DES_CBC_CRC",
1996 security.KERB_ENCTYPE_DES_CBC_MD5 : "DES_CBC_MD5",
1997 security.KERB_ENCTYPE_RC4_HMAC_MD5 : "RC4_HMAC_MD5",
1998 security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96 : "AES128_CTS_HMAC_SHA1_96",
1999 security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96 : "AES256_CTS_HMAC_SHA1_96",
2000 security.KERB_ENCTYPE_FAST_SUPPORTED : "FAST_SUPPORTED",
2001 security.KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED : "COMPOUND_IDENTITY_SUPPORTED",
2002 security.KERB_ENCTYPE_CLAIMS_SUPPORTED : "CLAIMS_SUPPORTED",
2003 security.KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED : "RESOURCE_SID_COMPRESSION_DISABLED",
2005 return self.generic_bitmap_to_string(enctypes, v)
2007 def entry_tln_status(self, e_flags, ):
2009 return "Status[Enabled]"
2012 lsa.LSA_TLN_DISABLED_NEW : "Disabled-New",
2013 lsa.LSA_TLN_DISABLED_ADMIN : "Disabled",
2014 lsa.LSA_TLN_DISABLED_CONFLICT : "Disabled-Conflicting",
2016 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
2018 def entry_dom_status(self, e_flags):
2020 return "Status[Enabled]"
2023 lsa.LSA_SID_DISABLED_ADMIN : "Disabled-SID",
2024 lsa.LSA_SID_DISABLED_CONFLICT : "Disabled-SID-Conflicting",
2025 lsa.LSA_NB_DISABLED_ADMIN : "Disabled-NB",
2026 lsa.LSA_NB_DISABLED_CONFLICT : "Disabled-NB-Conflicting",
2028 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
2030 def write_forest_trust_info(self, fti, tln=None, collisions=None):
2032 tln_string = " TDO[%s]" % tln
2036 self.outf.write("Namespaces[%d]%s:\n" % (
2037 len(fti.entries), tln_string))
2039 for i in xrange(0, len(fti.entries)):
2043 collision_string = ""
2045 if collisions is not None:
2046 for c in collisions.entries:
2050 collision_string = " Collision[%s]" % (c.name.string)
2052 d = e.forest_trust_data
2053 if e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
2054 self.outf.write("TLN: %-32s DNS[*.%s]%s\n" % (
2055 self.entry_tln_status(flags),
2056 d.string, collision_string))
2057 elif e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
2058 self.outf.write("TLN_EX: %-29s DNS[*.%s]\n" % (
2060 elif e.type == lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
2061 self.outf.write("DOM: %-32s DNS[%s] Netbios[%s] SID[%s]%s\n" % (
2062 self.entry_dom_status(flags),
2063 d.dns_domain_name.string,
2064 d.netbios_domain_name.string,
2065 d.domain_sid, collision_string))
2068 class cmd_domain_trust_list(DomainTrustCommand):
2069 """List domain trusts."""
2071 synopsis = "%prog [options]"
2073 takes_optiongroups = {
2074 "sambaopts": options.SambaOptions,
2075 "versionopts": options.VersionOptions,
2076 "localdcopts": LocalDCCredentialsOptions,
2082 def run(self, sambaopts=None, versionopts=None, localdcopts=None):
2084 local_server = self.setup_local_server(sambaopts, localdcopts)
2086 local_netlogon = self.new_local_netlogon_connection()
2087 except RuntimeError as error:
2088 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2091 local_netlogon_trusts = local_netlogon.netr_DsrEnumerateDomainTrusts(local_server,
2092 netlogon.NETR_TRUST_FLAG_IN_FOREST |
2093 netlogon.NETR_TRUST_FLAG_OUTBOUND |
2094 netlogon.NETR_TRUST_FLAG_INBOUND)
2095 except RuntimeError as error:
2096 if self.check_runtime_error(error, werror.WERR_RPC_S_PROCNUM_OUT_OF_RANGE):
2097 # TODO: we could implement a fallback to lsa.EnumTrustDom()
2098 raise CommandError("LOCAL_DC[%s]: netr_DsrEnumerateDomainTrusts not supported." % (
2100 raise self.LocalRuntimeError(self, error, "netr_DsrEnumerateDomainTrusts failed")
2102 a = local_netlogon_trusts.array
2104 if t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
2106 self.outf.write("%-14s %-15s %-19s %s\n" % (
2107 "Type[%s]" % self.netr_DomainTrust_to_type(a, t),
2108 "Transitive[%s]" % self.netr_DomainTrust_to_transitive(t),
2109 "Direction[%s]" % self.netr_DomainTrust_to_direction(t),
2110 "Name[%s]" % self.netr_DomainTrust_to_name(t)))
2113 class cmd_domain_trust_show(DomainTrustCommand):
2114 """Show trusted domain details."""
2116 synopsis = "%prog NAME [options]"
2118 takes_optiongroups = {
2119 "sambaopts": options.SambaOptions,
2120 "versionopts": options.VersionOptions,
2121 "localdcopts": LocalDCCredentialsOptions,
2127 takes_args = ["domain"]
2129 def run(self, domain, sambaopts=None, versionopts=None, localdcopts=None):
2131 local_server = self.setup_local_server(sambaopts, localdcopts)
2133 local_lsa = self.new_local_lsa_connection()
2134 except RuntimeError as error:
2135 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2138 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2139 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2140 except RuntimeError as error:
2141 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2143 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2144 local_lsa_info.name.string,
2145 local_lsa_info.dns_domain.string,
2146 local_lsa_info.sid))
2148 lsaString = lsa.String()
2149 lsaString.string = domain
2151 local_tdo_full = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2152 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2153 local_tdo_info = local_tdo_full.info_ex
2154 local_tdo_posix = local_tdo_full.posix_offset
2155 except NTSTATUSError as error:
2156 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2157 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2159 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(FULL_INFO) failed")
2162 local_tdo_enctypes = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2163 lsaString, lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES)
2164 except NTSTATUSError as error:
2165 if self.check_runtime_error(error, ntstatus.NT_STATUS_INVALID_PARAMETER):
2167 if self.check_runtime_error(error, ntstatus.NT_STATUS_INVALID_INFO_CLASS):
2170 if error is not None:
2171 raise self.LocalRuntimeError(self, error,
2172 "QueryTrustedDomainInfoByName(SUPPORTED_ENCRYPTION_TYPES) failed")
2174 local_tdo_enctypes = lsa.TrustDomainInfoSupportedEncTypes()
2175 local_tdo_enctypes.enc_types = 0
2178 local_tdo_forest = None
2179 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2180 local_tdo_forest = local_lsa.lsaRQueryForestTrustInformation(local_policy,
2181 lsaString, lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
2182 except RuntimeError as error:
2183 if self.check_runtime_error(error, ntstatus.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
2185 if self.check_runtime_error(error, ntstatus.NT_STATUS_NOT_FOUND):
2187 if error is not None:
2188 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation failed")
2190 local_tdo_forest = lsa.ForestTrustInformation()
2191 local_tdo_forest.count = 0
2192 local_tdo_forest.entries = []
2194 self.outf.write("TrustedDomain:\n\n");
2195 self.outf.write("NetbiosName: %s\n" % local_tdo_info.netbios_name.string)
2196 if local_tdo_info.netbios_name.string != local_tdo_info.domain_name.string:
2197 self.outf.write("DnsName: %s\n" % local_tdo_info.domain_name.string)
2198 self.outf.write("SID: %s\n" % local_tdo_info.sid)
2199 self.outf.write("Type: %s\n" % self.trustType_string(local_tdo_info.trust_type))
2200 self.outf.write("Direction: %s\n" % self.trustDirection_string(local_tdo_info.trust_direction))
2201 self.outf.write("Attributes: %s\n" % self.trustAttributes_string(local_tdo_info.trust_attributes))
2202 posix_offset_u32 = ctypes.c_uint32(local_tdo_posix.posix_offset).value
2203 posix_offset_i32 = ctypes.c_int32(local_tdo_posix.posix_offset).value
2204 self.outf.write("PosixOffset: 0x%08X (%d)\n" % (posix_offset_u32, posix_offset_i32))
2205 self.outf.write("kerb_EncTypes: %s\n" % self.kerb_EncTypes_string(local_tdo_enctypes.enc_types))
2207 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2208 self.write_forest_trust_info(local_tdo_forest,
2209 tln=local_tdo_info.domain_name.string)
2213 class cmd_domain_trust_create(DomainTrustCommand):
2214 """Create a domain or forest trust."""
2216 synopsis = "%prog DOMAIN [options]"
2218 takes_optiongroups = {
2219 "sambaopts": options.SambaOptions,
2220 "versionopts": options.VersionOptions,
2221 "credopts": options.CredentialsOptions,
2222 "localdcopts": LocalDCCredentialsOptions,
2226 Option("--type", type="choice", metavar="TYPE",
2227 choices=["external", "forest"],
2228 help="The type of the trust: 'external' or 'forest'.",
2230 default="external"),
2231 Option("--direction", type="choice", metavar="DIRECTION",
2232 choices=["incoming", "outgoing", "both"],
2233 help="The trust direction: 'incoming', 'outgoing' or 'both'.",
2234 dest='trust_direction',
2236 Option("--create-location", type="choice", metavar="LOCATION",
2237 choices=["local", "both"],
2238 help="Where to create the trusted domain object: 'local' or 'both'.",
2239 dest='create_location',
2241 Option("--cross-organisation", action="store_true",
2242 help="The related domains does not belong to the same organisation.",
2243 dest='cross_organisation',
2245 Option("--quarantined", type="choice", metavar="yes|no",
2246 choices=["yes", "no", None],
2247 help="Special SID filtering rules are applied to the trust. "
2248 "With --type=external the default is yes. "
2249 "With --type=forest the default is no.",
2250 dest='quarantined_arg',
2252 Option("--not-transitive", action="store_true",
2253 help="The forest trust is not transitive.",
2254 dest='not_transitive',
2256 Option("--treat-as-external", action="store_true",
2257 help="The treat the forest trust as external.",
2258 dest='treat_as_external',
2260 Option("--no-aes-keys", action="store_false",
2261 help="The trust uses aes kerberos keys.",
2262 dest='use_aes_keys',
2264 Option("--skip-validation", action="store_false",
2265 help="Skip validation of the trust.",
2270 takes_args = ["domain"]
2272 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2273 trust_type=None, trust_direction=None, create_location=None,
2274 cross_organisation=False, quarantined_arg=None,
2275 not_transitive=False, treat_as_external=False,
2276 use_aes_keys=False, validate=True):
2278 lsaString = lsa.String()
2281 if quarantined_arg is None:
2282 if trust_type == 'external':
2284 elif quarantined_arg == 'yes':
2287 if trust_type != 'forest':
2289 raise CommandError("--not-transitive requires --type=forest")
2290 if treat_as_external:
2291 raise CommandError("--treat-as-external requires --type=forest")
2295 enc_types = lsa.TrustDomainInfoSupportedEncTypes()
2296 enc_types.enc_types = security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96
2297 enc_types.enc_types |= security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96
2299 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2300 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2301 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2303 local_trust_info = lsa.TrustDomainInfoInfoEx()
2304 local_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2305 local_trust_info.trust_direction = 0
2306 if trust_direction == "both":
2307 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2308 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2309 elif trust_direction == "incoming":
2310 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2311 elif trust_direction == "outgoing":
2312 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2313 local_trust_info.trust_attributes = 0
2314 if cross_organisation:
2315 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2317 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2318 if trust_type == "forest":
2319 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2321 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2322 if treat_as_external:
2323 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2325 def get_password(name):
2328 if password is not None and password is not '':
2330 password = getpass("New %s Password: " % name)
2331 passwordverify = getpass("Retype %s Password: " % name)
2332 if not password == passwordverify:
2334 self.outf.write("Sorry, passwords do not match.\n")
2336 incoming_secret = None
2337 outgoing_secret = None
2338 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2339 if create_location == "local":
2340 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2341 incoming_password = get_password("Incoming Trust")
2342 incoming_secret = string_to_byte_array(incoming_password.encode('utf-16-le'))
2343 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2344 outgoing_password = get_password("Outgoing Trust")
2345 outgoing_secret = string_to_byte_array(outgoing_password.encode('utf-16-le'))
2347 remote_trust_info = None
2349 # We use 240 random bytes.
2350 # Windows uses 28 or 240 random bytes. I guess it's
2351 # based on the trust type external vs. forest.
2353 # The initial trust password can be up to 512 bytes
2354 # while the versioned passwords used for periodic updates
2355 # can only be up to 498 bytes, as netr_ServerPasswordSet2()
2356 # needs to pass the NL_PASSWORD_VERSION structure within the
2357 # 512 bytes and a 2 bytes confounder is required.
2359 def random_trust_secret(length):
2360 pw = samba.generate_random_machine_password(length//2, length//2)
2361 return string_to_byte_array(pw.encode('utf-16-le'))
2363 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2364 incoming_secret = random_trust_secret(240)
2365 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2366 outgoing_secret = random_trust_secret(240)
2368 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2369 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2371 remote_trust_info = lsa.TrustDomainInfoInfoEx()
2372 remote_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2373 remote_trust_info.trust_direction = 0
2374 if trust_direction == "both":
2375 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2376 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2377 elif trust_direction == "incoming":
2378 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2379 elif trust_direction == "outgoing":
2380 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2381 remote_trust_info.trust_attributes = 0
2382 if cross_organisation:
2383 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2385 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2386 if trust_type == "forest":
2387 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2389 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2390 if treat_as_external:
2391 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2393 local_server = self.setup_local_server(sambaopts, localdcopts)
2395 local_lsa = self.new_local_lsa_connection()
2396 except RuntimeError as error:
2397 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2400 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2401 except RuntimeError as error:
2402 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2404 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2405 local_lsa_info.name.string,
2406 local_lsa_info.dns_domain.string,
2407 local_lsa_info.sid))
2410 remote_server = self.setup_remote_server(credopts, domain)
2411 except RuntimeError as error:
2412 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2415 remote_lsa = self.new_remote_lsa_connection()
2416 except RuntimeError as error:
2417 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2420 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2421 except RuntimeError as error:
2422 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2424 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2425 remote_lsa_info.name.string,
2426 remote_lsa_info.dns_domain.string,
2427 remote_lsa_info.sid))
2429 local_trust_info.domain_name.string = remote_lsa_info.dns_domain.string
2430 local_trust_info.netbios_name.string = remote_lsa_info.name.string
2431 local_trust_info.sid = remote_lsa_info.sid
2433 if remote_trust_info:
2434 remote_trust_info.domain_name.string = local_lsa_info.dns_domain.string
2435 remote_trust_info.netbios_name.string = local_lsa_info.name.string
2436 remote_trust_info.sid = local_lsa_info.sid
2439 lsaString.string = local_trust_info.domain_name.string
2440 local_old_netbios = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2441 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2442 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2443 except NTSTATUSError as error:
2444 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2445 raise self.LocalRuntimeError(self, error,
2446 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2450 lsaString.string = local_trust_info.netbios_name.string
2451 local_old_dns = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2452 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2453 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2454 except NTSTATUSError as error:
2455 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2456 raise self.LocalRuntimeError(self, error,
2457 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2460 if remote_trust_info:
2462 lsaString.string = remote_trust_info.domain_name.string
2463 remote_old_netbios = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2464 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2465 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2466 except NTSTATUSError as error:
2467 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2468 raise self.RemoteRuntimeError(self, error,
2469 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2473 lsaString.string = remote_trust_info.netbios_name.string
2474 remote_old_dns = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2475 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2476 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2477 except NTSTATUSError as error:
2478 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2479 raise self.RemoteRuntimeError(self, error,
2480 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2484 local_netlogon = self.new_local_netlogon_connection()
2485 except RuntimeError as error:
2486 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2489 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
2490 except RuntimeError as error:
2491 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
2493 if remote_trust_info:
2495 remote_netlogon = self.new_remote_netlogon_connection()
2496 except RuntimeError as error:
2497 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2500 remote_netlogon_info = self.get_netlogon_dc_info(remote_netlogon, remote_server)
2501 except RuntimeError as error:
2502 raise self.RemoteRuntimeError(self, error, "failed to get netlogon dc info")
2504 def generate_AuthInOutBlob(secret, update_time):
2506 blob = drsblobs.trustAuthInOutBlob()
2511 clear = drsblobs.AuthInfoClear()
2512 clear.size = len(secret)
2513 clear.password = secret
2515 info = drsblobs.AuthenticationInformation()
2516 info.LastUpdateTime = samba.unix2nttime(update_time)
2517 info.AuthType = lsa.TRUST_AUTH_TYPE_CLEAR
2518 info.AuthInfo = clear
2520 array = drsblobs.AuthenticationInformationArray()
2522 array.array = [info]
2524 blob = drsblobs.trustAuthInOutBlob()
2526 blob.current = array
2530 def generate_AuthInfoInternal(session_key, incoming=None, outgoing=None):
2531 confounder = [0] * 512
2532 for i in range(len(confounder)):
2533 confounder[i] = random.randint(0, 255)
2535 trustpass = drsblobs.trustDomainPasswords()
2537 trustpass.confounder = confounder
2538 trustpass.outgoing = outgoing
2539 trustpass.incoming = incoming
2541 trustpass_blob = ndr_pack(trustpass)
2543 encrypted_trustpass = arcfour_encrypt(session_key, trustpass_blob)
2545 auth_blob = lsa.DATA_BUF2()
2546 auth_blob.size = len(encrypted_trustpass)
2547 auth_blob.data = string_to_byte_array(encrypted_trustpass)
2549 auth_info = lsa.TrustDomainInfoAuthInfoInternal()
2550 auth_info.auth_blob = auth_blob
2554 update_time = samba.current_unix_time()
2555 incoming_blob = generate_AuthInOutBlob(incoming_secret, update_time)
2556 outgoing_blob = generate_AuthInOutBlob(outgoing_secret, update_time)
2558 local_tdo_handle = None
2559 remote_tdo_handle = None
2561 local_auth_info = generate_AuthInfoInternal(local_lsa.session_key,
2562 incoming=incoming_blob,
2563 outgoing=outgoing_blob)
2564 if remote_trust_info:
2565 remote_auth_info = generate_AuthInfoInternal(remote_lsa.session_key,
2566 incoming=outgoing_blob,
2567 outgoing=incoming_blob)
2570 if remote_trust_info:
2571 self.outf.write("Creating remote TDO.\n")
2572 current_request = { "location": "remote", "name": "CreateTrustedDomainEx2"}
2573 remote_tdo_handle = remote_lsa.CreateTrustedDomainEx2(remote_policy,
2576 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2577 self.outf.write("Remote TDO created.\n")
2579 self.outf.write("Setting supported encryption types on remote TDO.\n")
2580 current_request = { "location": "remote", "name": "SetInformationTrustedDomain"}
2581 remote_lsa.SetInformationTrustedDomain(remote_tdo_handle,
2582 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2585 self.outf.write("Creating local TDO.\n")
2586 current_request = { "location": "local", "name": "CreateTrustedDomainEx2"}
2587 local_tdo_handle = local_lsa.CreateTrustedDomainEx2(local_policy,
2590 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2591 self.outf.write("Local TDO created\n")
2593 self.outf.write("Setting supported encryption types on local TDO.\n")
2594 current_request = { "location": "local", "name": "SetInformationTrustedDomain"}
2595 local_lsa.SetInformationTrustedDomain(local_tdo_handle,
2596 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2598 except RuntimeError as error:
2599 self.outf.write("Error: %s failed %sly - cleaning up\n" % (
2600 current_request['name'], current_request['location']))
2601 if remote_tdo_handle:
2602 self.outf.write("Deleting remote TDO.\n")
2603 remote_lsa.DeleteObject(remote_tdo_handle)
2604 remote_tdo_handle = None
2605 if local_tdo_handle:
2606 self.outf.write("Deleting local TDO.\n")
2607 local_lsa.DeleteObject(local_tdo_handle)
2608 local_tdo_handle = None
2609 if current_request['location'] is "remote":
2610 raise self.RemoteRuntimeError(self, error, "%s" % (
2611 current_request['name']))
2612 raise self.LocalRuntimeError(self, error, "%s" % (
2613 current_request['name']))
2616 if local_trust_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2617 self.outf.write("Setup local forest trust information...\n")
2619 # get all information about the remote trust
2620 # this triggers netr_GetForestTrustInformation to the remote domain
2621 # and lsaRSetForestTrustInformation() locally, but new top level
2622 # names are disabled by default.
2623 local_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
2624 remote_lsa_info.dns_domain.string,
2625 netlogon.DS_GFTI_UPDATE_TDO)
2626 except RuntimeError as error:
2627 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2630 # here we try to enable all top level names
2631 local_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
2632 remote_lsa_info.dns_domain,
2633 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2636 except RuntimeError as error:
2637 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2639 self.write_forest_trust_info(local_forest_info,
2640 tln=remote_lsa_info.dns_domain.string,
2641 collisions=local_forest_collision)
2643 if remote_trust_info:
2644 self.outf.write("Setup remote forest trust information...\n")
2646 # get all information about the local trust (from the perspective of the remote domain)
2647 # this triggers netr_GetForestTrustInformation to our domain.
2648 # and lsaRSetForestTrustInformation() remotely, but new top level
2649 # names are disabled by default.
2650 remote_forest_info = remote_netlogon.netr_DsRGetForestTrustInformation(remote_netlogon_info.dc_unc,
2651 local_lsa_info.dns_domain.string,
2652 netlogon.DS_GFTI_UPDATE_TDO)
2653 except RuntimeError as error:
2654 raise self.RemoteRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2657 # here we try to enable all top level names
2658 remote_forest_collision = remote_lsa.lsaRSetForestTrustInformation(remote_policy,
2659 local_lsa_info.dns_domain,
2660 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2663 except RuntimeError as error:
2664 raise self.RemoteRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2666 self.write_forest_trust_info(remote_forest_info,
2667 tln=local_lsa_info.dns_domain.string,
2668 collisions=remote_forest_collision)
2670 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2671 self.outf.write("Validating outgoing trust...\n")
2673 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_netlogon_info.dc_unc,
2674 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2676 remote_lsa_info.dns_domain.string)
2677 except RuntimeError as error:
2678 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2680 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2681 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2683 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2684 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2685 local_trust_verify.trusted_dc_name,
2686 local_trust_verify.tc_connection_status[1],
2687 local_trust_verify.pdc_connection_status[1])
2689 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2690 local_trust_verify.trusted_dc_name,
2691 local_trust_verify.tc_connection_status[1],
2692 local_trust_verify.pdc_connection_status[1])
2694 if local_trust_status != werror.WERR_SUCCESS or local_conn_status != werror.WERR_SUCCESS:
2695 raise CommandError(local_validation)
2697 self.outf.write("OK: %s\n" % local_validation)
2699 if remote_trust_info:
2700 if remote_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2701 self.outf.write("Validating incoming trust...\n")
2703 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_netlogon_info.dc_unc,
2704 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2706 local_lsa_info.dns_domain.string)
2707 except RuntimeError as error:
2708 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2710 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2711 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2713 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2714 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2715 remote_trust_verify.trusted_dc_name,
2716 remote_trust_verify.tc_connection_status[1],
2717 remote_trust_verify.pdc_connection_status[1])
2719 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2720 remote_trust_verify.trusted_dc_name,
2721 remote_trust_verify.tc_connection_status[1],
2722 remote_trust_verify.pdc_connection_status[1])
2724 if remote_trust_status != werror.WERR_SUCCESS or remote_conn_status != werror.WERR_SUCCESS:
2725 raise CommandError(remote_validation)
2727 self.outf.write("OK: %s\n" % remote_validation)
2729 if remote_tdo_handle is not None:
2731 remote_lsa.Close(remote_tdo_handle)
2732 except RuntimeError as error:
2734 remote_tdo_handle = None
2735 if local_tdo_handle is not None:
2737 local_lsa.Close(local_tdo_handle)
2738 except RuntimeError as error:
2740 local_tdo_handle = None
2742 self.outf.write("Success.\n")
2745 class cmd_domain_trust_delete(DomainTrustCommand):
2746 """Delete a domain trust."""
2748 synopsis = "%prog DOMAIN [options]"
2750 takes_optiongroups = {
2751 "sambaopts": options.SambaOptions,
2752 "versionopts": options.VersionOptions,
2753 "credopts": options.CredentialsOptions,
2754 "localdcopts": LocalDCCredentialsOptions,
2758 Option("--delete-location", type="choice", metavar="LOCATION",
2759 choices=["local", "both"],
2760 help="Where to delete the trusted domain object: 'local' or 'both'.",
2761 dest='delete_location',
2765 takes_args = ["domain"]
2767 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2768 delete_location=None):
2770 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2771 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2772 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2774 if delete_location == "local":
2775 remote_policy_access = None
2777 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2778 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2779 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2781 local_server = self.setup_local_server(sambaopts, localdcopts)
2783 local_lsa = self.new_local_lsa_connection()
2784 except RuntimeError as error:
2785 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2788 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2789 except RuntimeError as error:
2790 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2792 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2793 local_lsa_info.name.string,
2794 local_lsa_info.dns_domain.string,
2795 local_lsa_info.sid))
2797 local_tdo_info = None
2798 local_tdo_handle = None
2799 remote_tdo_info = None
2800 remote_tdo_handle = None
2802 lsaString = lsa.String()
2804 lsaString.string = domain
2805 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2806 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2807 except NTSTATUSError as error:
2808 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2809 raise CommandError("Failed to find trust for domain '%s'" % domain)
2810 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2813 if remote_policy_access is not None:
2815 remote_server = self.setup_remote_server(credopts, domain)
2816 except RuntimeError as error:
2817 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2820 remote_lsa = self.new_remote_lsa_connection()
2821 except RuntimeError as error:
2822 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2825 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2826 except RuntimeError as error:
2827 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2829 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2830 remote_lsa_info.name.string,
2831 remote_lsa_info.dns_domain.string,
2832 remote_lsa_info.sid))
2834 if remote_lsa_info.sid != local_tdo_info.sid or \
2835 remote_lsa_info.name.string != local_tdo_info.netbios_name.string or \
2836 remote_lsa_info.dns_domain.string != local_tdo_info.domain_name.string:
2837 raise CommandError("LocalTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2838 local_tdo_info.netbios_name.string,
2839 local_tdo_info.domain_name.string,
2840 local_tdo_info.sid))
2843 lsaString.string = local_lsa_info.dns_domain.string
2844 remote_tdo_info = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2845 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2846 except NTSTATUSError as error:
2847 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2848 raise self.RemoteRuntimeError(self, error, "QueryTrustedDomainInfoByName(%s)" % (
2852 if remote_tdo_info is not None:
2853 if local_lsa_info.sid != remote_tdo_info.sid or \
2854 local_lsa_info.name.string != remote_tdo_info.netbios_name.string or \
2855 local_lsa_info.dns_domain.string != remote_tdo_info.domain_name.string:
2856 raise CommandError("RemoteTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2857 remote_tdo_info.netbios_name.string,
2858 remote_tdo_info.domain_name.string,
2859 remote_tdo_info.sid))
2861 if local_tdo_info is not None:
2863 lsaString.string = local_tdo_info.domain_name.string
2864 local_tdo_handle = local_lsa.OpenTrustedDomainByName(local_policy,
2866 security.SEC_STD_DELETE)
2867 except RuntimeError as error:
2868 raise self.LocalRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2871 local_lsa.DeleteObject(local_tdo_handle)
2872 local_tdo_handle = None
2874 if remote_tdo_info is not None:
2876 lsaString.string = remote_tdo_info.domain_name.string
2877 remote_tdo_handle = remote_lsa.OpenTrustedDomainByName(remote_policy,
2879 security.SEC_STD_DELETE)
2880 except RuntimeError as error:
2881 raise self.RemoteRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2884 if remote_tdo_handle is not None:
2886 remote_lsa.DeleteObject(remote_tdo_handle)
2887 remote_tdo_handle = None
2888 self.outf.write("RemoteTDO deleted.\n")
2889 except RuntimeError as error:
2890 self.outf.write("%s\n" % self.RemoteRuntimeError(self, error, "DeleteObject() failed"))
2892 if local_tdo_handle is not None:
2894 local_lsa.DeleteObject(local_tdo_handle)
2895 local_tdo_handle = None
2896 self.outf.write("LocalTDO deleted.\n")
2897 except RuntimeError as error:
2898 self.outf.write("%s\n" % self.LocalRuntimeError(self, error, "DeleteObject() failed"))
2902 class cmd_domain_trust_validate(DomainTrustCommand):
2903 """Validate a domain trust."""
2905 synopsis = "%prog DOMAIN [options]"
2907 takes_optiongroups = {
2908 "sambaopts": options.SambaOptions,
2909 "versionopts": options.VersionOptions,
2910 "credopts": options.CredentialsOptions,
2911 "localdcopts": LocalDCCredentialsOptions,
2915 Option("--validate-location", type="choice", metavar="LOCATION",
2916 choices=["local", "both"],
2917 help="Where to validate the trusted domain object: 'local' or 'both'.",
2918 dest='validate_location',
2922 takes_args = ["domain"]
2924 def run(self, domain, sambaopts=None, versionopts=None, credopts=None, localdcopts=None,
2925 validate_location=None):
2927 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2929 local_server = self.setup_local_server(sambaopts, localdcopts)
2931 local_lsa = self.new_local_lsa_connection()
2932 except RuntimeError as error:
2933 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2936 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2937 except RuntimeError as error:
2938 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2940 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2941 local_lsa_info.name.string,
2942 local_lsa_info.dns_domain.string,
2943 local_lsa_info.sid))
2946 lsaString = lsa.String()
2947 lsaString.string = domain
2948 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2949 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2950 except NTSTATUSError as error:
2951 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2952 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2954 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
2956 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
2957 local_tdo_info.netbios_name.string,
2958 local_tdo_info.domain_name.string,
2959 local_tdo_info.sid))
2962 local_netlogon = self.new_local_netlogon_connection()
2963 except RuntimeError as error:
2964 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2967 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_server,
2968 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2970 local_tdo_info.domain_name.string)
2971 except RuntimeError as error:
2972 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2974 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2975 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2977 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2978 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2979 local_trust_verify.trusted_dc_name,
2980 local_trust_verify.tc_connection_status[1],
2981 local_trust_verify.pdc_connection_status[1])
2983 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2984 local_trust_verify.trusted_dc_name,
2985 local_trust_verify.tc_connection_status[1],
2986 local_trust_verify.pdc_connection_status[1])
2988 if local_trust_status != werror.WERR_SUCCESS or local_conn_status != werror.WERR_SUCCESS:
2989 raise CommandError(local_validation)
2991 self.outf.write("OK: %s\n" % local_validation)
2994 server = local_trust_verify.trusted_dc_name.replace('\\', '')
2995 domain_and_server = "%s\\%s" % (local_tdo_info.domain_name.string, server)
2996 local_trust_rediscover = local_netlogon.netr_LogonControl2Ex(local_server,
2997 netlogon.NETLOGON_CONTROL_REDISCOVER,
3000 except RuntimeError as error:
3001 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
3003 local_conn_status = self._uint32(local_trust_rediscover.tc_connection_status[0])
3004 local_rediscover = "LocalRediscover: DC[%s] CONNECTION[%s]" % (
3005 local_trust_rediscover.trusted_dc_name,
3006 local_trust_rediscover.tc_connection_status[1])
3008 if local_conn_status != werror.WERR_SUCCESS:
3009 raise CommandError(local_rediscover)
3011 self.outf.write("OK: %s\n" % local_rediscover)
3013 if validate_location != "local":
3015 remote_server = self.setup_remote_server(credopts, domain, require_pdc=False)
3016 except RuntimeError as error:
3017 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
3020 remote_netlogon = self.new_remote_netlogon_connection()
3021 except RuntimeError as error:
3022 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
3025 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_server,
3026 netlogon.NETLOGON_CONTROL_TC_VERIFY,
3028 local_lsa_info.dns_domain.string)
3029 except RuntimeError as error:
3030 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
3032 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
3033 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
3035 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
3036 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
3037 remote_trust_verify.trusted_dc_name,
3038 remote_trust_verify.tc_connection_status[1],
3039 remote_trust_verify.pdc_connection_status[1])
3041 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
3042 remote_trust_verify.trusted_dc_name,
3043 remote_trust_verify.tc_connection_status[1],
3044 remote_trust_verify.pdc_connection_status[1])
3046 if remote_trust_status != werror.WERR_SUCCESS or remote_conn_status != werror.WERR_SUCCESS:
3047 raise CommandError(remote_validation)
3049 self.outf.write("OK: %s\n" % remote_validation)
3052 server = remote_trust_verify.trusted_dc_name.replace('\\', '')
3053 domain_and_server = "%s\\%s" % (local_lsa_info.dns_domain.string, server)
3054 remote_trust_rediscover = remote_netlogon.netr_LogonControl2Ex(remote_server,
3055 netlogon.NETLOGON_CONTROL_REDISCOVER,
3058 except RuntimeError as error:
3059 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
3061 remote_conn_status = self._uint32(remote_trust_rediscover.tc_connection_status[0])
3063 remote_rediscover = "RemoteRediscover: DC[%s] CONNECTION[%s]" % (
3064 remote_trust_rediscover.trusted_dc_name,
3065 remote_trust_rediscover.tc_connection_status[1])
3067 if remote_conn_status != werror.WERR_SUCCESS:
3068 raise CommandError(remote_rediscover)
3070 self.outf.write("OK: %s\n" % remote_rediscover)
3074 class cmd_domain_trust_namespaces(DomainTrustCommand):
3075 """Manage forest trust namespaces."""
3077 synopsis = "%prog [DOMAIN] [options]"
3079 takes_optiongroups = {
3080 "sambaopts": options.SambaOptions,
3081 "versionopts": options.VersionOptions,
3082 "localdcopts": LocalDCCredentialsOptions,
3086 Option("--refresh", type="choice", metavar="check|store",
3087 choices=["check", "store", None],
3088 help="List and maybe store refreshed forest trust information: 'check' or 'store'.",
3091 Option("--enable-all", action="store_true",
3092 help="Try to update disabled entries, not allowed with --refresh=check.",
3095 Option("--enable-tln", action="append", metavar='DNSDOMAIN',
3096 help="Enable a top level name entry. Can be specified multiple times.",
3099 Option("--disable-tln", action="append", metavar='DNSDOMAIN',
3100 help="Disable a top level name entry. Can be specified multiple times.",
3103 Option("--add-tln-ex", action="append", metavar='DNSDOMAIN',
3104 help="Add a top level exclusion entry. Can be specified multiple times.",
3107 Option("--delete-tln-ex", action="append", metavar='DNSDOMAIN',
3108 help="Delete a top level exclusion entry. Can be specified multiple times.",
3109 dest='delete_tln_ex',
3111 Option("--enable-nb", action="append", metavar='NETBIOSDOMAIN',
3112 help="Enable a netbios name in a domain entry. Can be specified multiple times.",
3115 Option("--disable-nb", action="append", metavar='NETBIOSDOMAIN',
3116 help="Disable a netbios name in a domain entry. Can be specified multiple times.",
3119 Option("--enable-sid", action="append", metavar='DOMAINSID',
3120 help="Enable a SID in a domain entry. Can be specified multiple times.",
3121 dest='enable_sid_str',
3123 Option("--disable-sid", action="append", metavar='DOMAINSID',
3124 help="Disable a SID in a domain entry. Can be specified multiple times.",
3125 dest='disable_sid_str',
3127 Option("--add-upn-suffix", action="append", metavar='DNSDOMAIN',
3128 help="Add a new uPNSuffixes attribute for the local forest. Can be specified multiple times.",
3131 Option("--delete-upn-suffix", action="append", metavar='DNSDOMAIN',
3132 help="Delete an existing uPNSuffixes attribute of the local forest. Can be specified multiple times.",
3135 Option("--add-spn-suffix", action="append", metavar='DNSDOMAIN',
3136 help="Add a new msDS-SPNSuffixes attribute for the local forest. Can be specified multiple times.",
3139 Option("--delete-spn-suffix", action="append", metavar='DNSDOMAIN',
3140 help="Delete an existing msDS-SPNSuffixes attribute of the local forest. Can be specified multiple times.",
3145 takes_args = ["domain?"]
3147 def run(self, domain=None, sambaopts=None, localdcopts=None, versionopts=None,
3148 refresh=None, enable_all=False,
3149 enable_tln=[], disable_tln=[], add_tln_ex=[], delete_tln_ex=[],
3150 enable_sid_str=[], disable_sid_str=[], enable_nb=[], disable_nb=[],
3151 add_upn=[], delete_upn=[], add_spn=[], delete_spn=[]):
3153 require_update = False
3156 if refresh == "store":
3157 raise CommandError("--refresh=%s not allowed without DOMAIN" % refresh)
3160 raise CommandError("--enable-all not allowed without DOMAIN")
3162 if len(enable_tln) > 0:
3163 raise CommandError("--enable-tln not allowed without DOMAIN")
3164 if len(disable_tln) > 0:
3165 raise CommandError("--disable-tln not allowed without DOMAIN")
3167 if len(add_tln_ex) > 0:
3168 raise CommandError("--add-tln-ex not allowed without DOMAIN")
3169 if len(delete_tln_ex) > 0:
3170 raise CommandError("--delete-tln-ex not allowed without DOMAIN")
3172 if len(enable_nb) > 0:
3173 raise CommandError("--enable-nb not allowed without DOMAIN")
3174 if len(disable_nb) > 0:
3175 raise CommandError("--disable-nb not allowed without DOMAIN")
3177 if len(enable_sid_str) > 0:
3178 raise CommandError("--enable-sid not allowed without DOMAIN")
3179 if len(disable_sid_str) > 0:
3180 raise CommandError("--disable-sid not allowed without DOMAIN")
3182 if len(add_upn) > 0:
3184 if not n.startswith("*."):
3186 raise CommandError("value[%s] specified for --add-upn-suffix should not include with '*.'" % n)
3187 require_update = True
3188 if len(delete_upn) > 0:
3189 for n in delete_upn:
3190 if not n.startswith("*."):
3192 raise CommandError("value[%s] specified for --delete-upn-suffix should not include with '*.'" % n)
3193 require_update = True
3195 for d in delete_upn:
3196 if a.lower() != d.lower():
3198 raise CommandError("value[%s] specified for --add-upn-suffix and --delete-upn-suffix" % a)
3200 if len(add_spn) > 0:
3202 if not n.startswith("*."):
3204 raise CommandError("value[%s] specified for --add-spn-suffix should not include with '*.'" % n)
3205 require_update = True
3206 if len(delete_spn) > 0:
3207 for n in delete_spn:
3208 if not n.startswith("*."):
3210 raise CommandError("value[%s] specified for --delete-spn-suffix should not include with '*.'" % n)
3211 require_update = True
3213 for d in delete_spn:
3214 if a.lower() != d.lower():
3216 raise CommandError("value[%s] specified for --add-spn-suffix and --delete-spn-suffix" % a)
3218 if len(add_upn) > 0:
3219 raise CommandError("--add-upn-suffix not allowed together with DOMAIN")
3220 if len(delete_upn) > 0:
3221 raise CommandError("--delete-upn-suffix not allowed together with DOMAIN")
3222 if len(add_spn) > 0:
3223 raise CommandError("--add-spn-suffix not allowed together with DOMAIN")
3224 if len(delete_spn) > 0:
3225 raise CommandError("--delete-spn-suffix not allowed together with DOMAIN")
3227 if refresh is not None:
3228 if refresh == "store":
3229 require_update = True
3231 if enable_all and refresh != "store":
3232 raise CommandError("--enable-all not allowed together with --refresh=%s" % refresh)
3234 if len(enable_tln) > 0:
3235 raise CommandError("--enable-tln not allowed together with --refresh")
3236 if len(disable_tln) > 0:
3237 raise CommandError("--disable-tln not allowed together with --refresh")
3239 if len(add_tln_ex) > 0:
3240 raise CommandError("--add-tln-ex not allowed together with --refresh")
3241 if len(delete_tln_ex) > 0:
3242 raise CommandError("--delete-tln-ex not allowed together with --refresh")
3244 if len(enable_nb) > 0:
3245 raise CommandError("--enable-nb not allowed together with --refresh")
3246 if len(disable_nb) > 0:
3247 raise CommandError("--disable-nb not allowed together with --refresh")
3249 if len(enable_sid_str) > 0:
3250 raise CommandError("--enable-sid not allowed together with --refresh")
3251 if len(disable_sid_str) > 0:
3252 raise CommandError("--disable-sid not allowed together with --refresh")
3255 require_update = True
3257 if len(enable_tln) > 0:
3258 raise CommandError("--enable-tln not allowed together with --enable-all")
3260 if len(enable_nb) > 0:
3261 raise CommandError("--enable-nb not allowed together with --enable-all")
3263 if len(enable_sid_str) > 0:
3264 raise CommandError("--enable-sid not allowed together with --enable-all")
3266 if len(enable_tln) > 0:
3267 require_update = True
3268 if len(disable_tln) > 0:
3269 require_update = True
3270 for e in enable_tln:
3271 for d in disable_tln:
3272 if e.lower() != d.lower():
3274 raise CommandError("value[%s] specified for --enable-tln and --disable-tln" % e)
3276 if len(add_tln_ex) > 0:
3277 for n in add_tln_ex:
3278 if not n.startswith("*."):
3280 raise CommandError("value[%s] specified for --add-tln-ex should not include with '*.'" % n)
3281 require_update = True
3282 if len(delete_tln_ex) > 0:
3283 for n in delete_tln_ex:
3284 if not n.startswith("*."):
3286 raise CommandError("value[%s] specified for --delete-tln-ex should not include with '*.'" % n)
3287 require_update = True
3288 for a in add_tln_ex:
3289 for d in delete_tln_ex:
3290 if a.lower() != d.lower():
3292 raise CommandError("value[%s] specified for --add-tln-ex and --delete-tln-ex" % a)
3294 if len(enable_nb) > 0:
3295 require_update = True
3296 if len(disable_nb) > 0:
3297 require_update = True
3299 for d in disable_nb:
3300 if e.upper() != d.upper():
3302 raise CommandError("value[%s] specified for --enable-nb and --disable-nb" % e)
3305 for s in enable_sid_str:
3307 sid = security.dom_sid(s)
3308 except TypeError as error:
3309 raise CommandError("value[%s] specified for --enable-sid is not a valid SID" % s)
3310 enable_sid.append(sid)
3312 for s in disable_sid_str:
3314 sid = security.dom_sid(s)
3315 except TypeError as error:
3316 raise CommandError("value[%s] specified for --disable-sid is not a valid SID" % s)
3317 disable_sid.append(sid)
3318 if len(enable_sid) > 0:
3319 require_update = True
3320 if len(disable_sid) > 0:
3321 require_update = True
3322 for e in enable_sid:
3323 for d in disable_sid:
3326 raise CommandError("value[%s] specified for --enable-sid and --disable-sid" % e)
3328 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
3330 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
3332 local_server = self.setup_local_server(sambaopts, localdcopts)
3334 local_lsa = self.new_local_lsa_connection()
3335 except RuntimeError as error:
3336 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
3339 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
3340 except RuntimeError as error:
3341 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
3343 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
3344 local_lsa_info.name.string,
3345 local_lsa_info.dns_domain.string,
3346 local_lsa_info.sid))
3350 local_netlogon = self.new_local_netlogon_connection()
3351 except RuntimeError as error:
3352 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3355 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3356 except RuntimeError as error:
3357 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3359 if local_netlogon_info.domain_name != local_netlogon_info.forest_name:
3360 raise CommandError("The local domain [%s] is not the forest root [%s]" % (
3361 local_netlogon_info.domain_name,
3362 local_netlogon_info.forest_name))
3365 # get all information about our own forest
3366 own_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3368 except RuntimeError as error:
3369 if self.check_runtime_error(error, werror.WERR_RPC_S_PROCNUM_OUT_OF_RANGE):
3370 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3373 if self.check_runtime_error(error, werror.WERR_INVALID_FUNCTION):
3374 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3377 if self.check_runtime_error(error, werror.WERR_NERR_ACFNOTLOADED):
3378 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3381 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3383 self.outf.write("Own forest trust information...\n")
3384 self.write_forest_trust_info(own_forest_info,
3385 tln=local_lsa_info.dns_domain.string)
3388 local_samdb = self.new_local_ldap_connection()
3389 except RuntimeError as error:
3390 raise self.LocalRuntimeError(self, error, "failed to connect to SamDB")
3392 local_partitions_dn = "CN=Partitions,%s" % str(local_samdb.get_config_basedn())
3393 attrs = ['uPNSuffixes', 'msDS-SPNSuffixes']
3395 msgs = local_samdb.search(base=local_partitions_dn,
3396 scope=ldb.SCOPE_BASE,
3397 expression="(objectClass=crossRefContainer)",
3399 stored_msg = msgs[0]
3400 except ldb.LdbError as error:
3401 raise self.LocalLdbError(self, error, "failed to search partition dn")
3403 stored_upn_vals = []
3404 if 'uPNSuffixes' in stored_msg:
3405 stored_upn_vals.extend(stored_msg['uPNSuffixes'])
3407 stored_spn_vals = []
3408 if 'msDS-SPNSuffixes' in stored_msg:
3409 stored_spn_vals.extend(stored_msg['msDS-SPNSuffixes'])
3411 self.outf.write("Stored uPNSuffixes attributes[%d]:\n" % len(stored_upn_vals))
3412 for v in stored_upn_vals:
3413 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3414 self.outf.write("Stored msDS-SPNSuffixes attributes[%d]:\n" % len(stored_spn_vals))
3415 for v in stored_spn_vals:
3416 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3418 if not require_update:
3422 update_upn_vals = []
3423 update_upn_vals.extend(stored_upn_vals)
3426 update_spn_vals = []
3427 update_spn_vals.extend(stored_spn_vals)
3431 for i in xrange(0, len(update_upn_vals)):
3432 v = update_upn_vals[i]
3433 if v.lower() != upn.lower():
3438 raise CommandError("Entry already present for value[%s] specified for --add-upn-suffix" % upn)
3439 update_upn_vals.append(upn)
3442 for upn in delete_upn:
3444 for i in xrange(0, len(update_upn_vals)):
3445 v = update_upn_vals[i]
3446 if v.lower() != upn.lower():
3451 raise CommandError("Entry not found for value[%s] specified for --delete-upn-suffix" % upn)
3453 update_upn_vals.pop(idx)
3458 for i in xrange(0, len(update_spn_vals)):
3459 v = update_spn_vals[i]
3460 if v.lower() != spn.lower():
3465 raise CommandError("Entry already present for value[%s] specified for --add-spn-suffix" % spn)
3466 update_spn_vals.append(spn)
3469 for spn in delete_spn:
3471 for i in xrange(0, len(update_spn_vals)):
3472 v = update_spn_vals[i]
3473 if v.lower() != spn.lower():
3478 raise CommandError("Entry not found for value[%s] specified for --delete-spn-suffix" % spn)
3480 update_spn_vals.pop(idx)
3483 self.outf.write("Update uPNSuffixes attributes[%d]:\n" % len(update_upn_vals))
3484 for v in update_upn_vals:
3485 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3486 self.outf.write("Update msDS-SPNSuffixes attributes[%d]:\n" % len(update_spn_vals))
3487 for v in update_spn_vals:
3488 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3490 update_msg = ldb.Message()
3491 update_msg.dn = stored_msg.dn
3494 update_msg['uPNSuffixes'] = ldb.MessageElement(update_upn_vals,
3495 ldb.FLAG_MOD_REPLACE,
3498 update_msg['msDS-SPNSuffixes'] = ldb.MessageElement(update_spn_vals,
3499 ldb.FLAG_MOD_REPLACE,
3502 local_samdb.modify(update_msg)
3503 except ldb.LdbError as error:
3504 raise self.LocalLdbError(self, error, "failed to update partition dn")
3507 stored_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3509 except RuntimeError as error:
3510 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3512 self.outf.write("Stored forest trust information...\n")
3513 self.write_forest_trust_info(stored_forest_info,
3514 tln=local_lsa_info.dns_domain.string)
3518 lsaString = lsa.String()
3519 lsaString.string = domain
3520 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
3521 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
3522 except NTSTATUSError as error:
3523 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
3524 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
3526 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
3528 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
3529 local_tdo_info.netbios_name.string,
3530 local_tdo_info.domain_name.string,
3531 local_tdo_info.sid))
3533 if not local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
3534 raise CommandError("trusted domain object for domain [%s] is not marked as FOREST_TRANSITIVE." % domain)
3536 if refresh is not None:
3538 local_netlogon = self.new_local_netlogon_connection()
3539 except RuntimeError as error:
3540 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3543 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3544 except RuntimeError as error:
3545 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3547 lsa_update_check = 1
3548 if refresh == "store":
3549 netlogon_update_tdo = netlogon.DS_GFTI_UPDATE_TDO
3551 lsa_update_check = 0
3553 netlogon_update_tdo = 0
3556 # get all information about the remote trust
3557 # this triggers netr_GetForestTrustInformation to the remote domain
3558 # and lsaRSetForestTrustInformation() locally, but new top level
3559 # names are disabled by default.
3560 fresh_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3561 local_tdo_info.domain_name.string,
3562 netlogon_update_tdo)
3563 except RuntimeError as error:
3564 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3567 fresh_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3568 local_tdo_info.domain_name,
3569 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3572 except RuntimeError as error:
3573 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3575 self.outf.write("Fresh forest trust information...\n")
3576 self.write_forest_trust_info(fresh_forest_info,
3577 tln=local_tdo_info.domain_name.string,
3578 collisions=fresh_forest_collision)
3580 if refresh == "store":
3582 lsaString = lsa.String()
3583 lsaString.string = local_tdo_info.domain_name.string
3584 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3586 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3587 except RuntimeError as error:
3588 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3590 self.outf.write("Stored forest trust information...\n")
3591 self.write_forest_trust_info(stored_forest_info,
3592 tln=local_tdo_info.domain_name.string)
3597 # The none --refresh path
3601 lsaString = lsa.String()
3602 lsaString.string = local_tdo_info.domain_name.string
3603 local_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3605 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3606 except RuntimeError as error:
3607 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3609 self.outf.write("Local forest trust information...\n")
3610 self.write_forest_trust_info(local_forest_info,
3611 tln=local_tdo_info.domain_name.string)
3613 if not require_update:
3617 entries.extend(local_forest_info.entries)
3618 update_forest_info = lsa.ForestTrustInformation()
3619 update_forest_info.count = len(entries)
3620 update_forest_info.entries = entries
3623 for i in xrange(0, len(update_forest_info.entries)):
3624 r = update_forest_info.entries[i]
3625 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3627 if update_forest_info.entries[i].flags == 0:
3629 update_forest_info.entries[i].time = 0
3630 update_forest_info.entries[i].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3631 for i in xrange(0, len(update_forest_info.entries)):
3632 r = update_forest_info.entries[i]
3633 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3635 if update_forest_info.entries[i].flags == 0:
3637 update_forest_info.entries[i].time = 0
3638 update_forest_info.entries[i].flags &= ~lsa.LSA_NB_DISABLED_MASK
3639 update_forest_info.entries[i].flags &= ~lsa.LSA_SID_DISABLED_MASK
3641 for tln in enable_tln:
3643 for i in xrange(0, len(update_forest_info.entries)):
3644 r = update_forest_info.entries[i]
3645 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3647 if r.forest_trust_data.string.lower() != tln.lower():
3652 raise CommandError("Entry not found for value[%s] specified for --enable-tln" % tln)
3653 if not update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_MASK:
3654 raise CommandError("Entry found for value[%s] specified for --enable-tln is already enabled" % tln)
3655 update_forest_info.entries[idx].time = 0
3656 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3658 for tln in disable_tln:
3660 for i in xrange(0, len(update_forest_info.entries)):
3661 r = update_forest_info.entries[i]
3662 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3664 if r.forest_trust_data.string.lower() != tln.lower():
3669 raise CommandError("Entry not found for value[%s] specified for --disable-tln" % tln)
3670 if update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_ADMIN:
3671 raise CommandError("Entry found for value[%s] specified for --disable-tln is already disabled" % tln)
3672 update_forest_info.entries[idx].time = 0
3673 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3674 update_forest_info.entries[idx].flags |= lsa.LSA_TLN_DISABLED_ADMIN
3676 for tln_ex in add_tln_ex:
3678 for i in xrange(0, len(update_forest_info.entries)):
3679 r = update_forest_info.entries[i]
3680 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3682 if r.forest_trust_data.string.lower() != tln_ex.lower():
3687 raise CommandError("Entry already present for value[%s] specified for --add-tln-ex" % tln_ex)
3689 tln_dot = ".%s" % tln_ex.lower()
3691 for i in xrange(0, len(update_forest_info.entries)):
3692 r = update_forest_info.entries[i]
3693 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3695 r_dot = ".%s" % r.forest_trust_data.string.lower()
3696 if tln_dot == r_dot:
3697 raise CommandError("TLN entry present for value[%s] specified for --add-tln-ex" % tln_ex)
3698 if not tln_dot.endswith(r_dot):
3704 raise CommandError("No TLN parent present for value[%s] specified for --add-tln-ex" % tln_ex)
3706 r = lsa.ForestTrustRecord()
3707 r.type = lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX
3710 r.forest_trust_data.string = tln_ex
3713 entries.extend(update_forest_info.entries)
3714 entries.insert(idx + 1, r)
3715 update_forest_info.count = len(entries)
3716 update_forest_info.entries = entries
3718 for tln_ex in delete_tln_ex:
3720 for i in xrange(0, len(update_forest_info.entries)):
3721 r = update_forest_info.entries[i]
3722 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3724 if r.forest_trust_data.string.lower() != tln_ex.lower():
3729 raise CommandError("Entry not found for value[%s] specified for --delete-tln-ex" % tln_ex)
3732 entries.extend(update_forest_info.entries)
3734 update_forest_info.count = len(entries)
3735 update_forest_info.entries = entries
3737 for nb in enable_nb:
3739 for i in xrange(0, len(update_forest_info.entries)):
3740 r = update_forest_info.entries[i]
3741 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3743 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3748 raise CommandError("Entry not found for value[%s] specified for --enable-nb" % nb)
3749 if not update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_MASK:
3750 raise CommandError("Entry found for value[%s] specified for --enable-nb is already enabled" % nb)
3751 update_forest_info.entries[idx].time = 0
3752 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3754 for nb in disable_nb:
3756 for i in xrange(0, len(update_forest_info.entries)):
3757 r = update_forest_info.entries[i]
3758 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3760 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3765 raise CommandError("Entry not found for value[%s] specified for --delete-nb" % nb)
3766 if update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_ADMIN:
3767 raise CommandError("Entry found for value[%s] specified for --disable-nb is already disabled" % nb)
3768 update_forest_info.entries[idx].time = 0
3769 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3770 update_forest_info.entries[idx].flags |= lsa.LSA_NB_DISABLED_ADMIN
3772 for sid in enable_sid:
3774 for i in xrange(0, len(update_forest_info.entries)):
3775 r = update_forest_info.entries[i]
3776 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3778 if r.forest_trust_data.domain_sid != sid:
3783 raise CommandError("Entry not found for value[%s] specified for --enable-sid" % sid)
3784 if not update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_MASK:
3785 raise CommandError("Entry found for value[%s] specified for --enable-sid is already enabled" % nb)
3786 update_forest_info.entries[idx].time = 0
3787 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3789 for sid in disable_sid:
3791 for i in xrange(0, len(update_forest_info.entries)):
3792 r = update_forest_info.entries[i]
3793 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3795 if r.forest_trust_data.domain_sid != sid:
3800 raise CommandError("Entry not found for value[%s] specified for --delete-sid" % sid)
3801 if update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_ADMIN:
3802 raise CommandError("Entry found for value[%s] specified for --disable-sid is already disabled" % nb)
3803 update_forest_info.entries[idx].time = 0
3804 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3805 update_forest_info.entries[idx].flags |= lsa.LSA_SID_DISABLED_ADMIN
3808 update_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3809 local_tdo_info.domain_name,
3810 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3811 update_forest_info, 0)
3812 except RuntimeError as error:
3813 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3815 self.outf.write("Updated forest trust information...\n")
3816 self.write_forest_trust_info(update_forest_info,
3817 tln=local_tdo_info.domain_name.string,
3818 collisions=update_forest_collision)
3821 lsaString = lsa.String()
3822 lsaString.string = local_tdo_info.domain_name.string
3823 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3825 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3826 except RuntimeError as error:
3827 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3829 self.outf.write("Stored forest trust information...\n")
3830 self.write_forest_trust_info(stored_forest_info,
3831 tln=local_tdo_info.domain_name.string)
3834 class cmd_domain_tombstones_expunge(Command):
3835 """Expunge tombstones from the database.
3837 This command expunges tombstones from the database."""
3838 synopsis = "%prog NC [NC [...]] [options]"
3841 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
3842 metavar="URL", dest="H"),
3843 Option("--current-time",
3844 help="The current time to evaluate the tombstone lifetime from, expressed as YYYY-MM-DD",
3846 Option("--tombstone-lifetime", help="Number of days a tombstone should be preserved for", type=int),
3849 takes_args = ["nc*"]
3851 takes_optiongroups = {
3852 "sambaopts": options.SambaOptions,
3853 "credopts": options.CredentialsOptions,
3854 "versionopts": options.VersionOptions,
3857 def run(self, *ncs, **kwargs):
3858 sambaopts = kwargs.get("sambaopts")
3859 credopts = kwargs.get("credopts")
3860 versionpts = kwargs.get("versionopts")
3862 current_time_string = kwargs.get("current_time")
3863 tombstone_lifetime = kwargs.get("tombstone_lifetime")
3864 lp = sambaopts.get_loadparm()
3865 creds = credopts.get_credentials(lp)
3866 samdb = SamDB(url=H, session_info=system_session(),
3867 credentials=creds, lp=lp)
3869 if current_time_string is not None:
3870 current_time_obj = time.strptime(current_time_string, "%Y-%m-%d")
3871 current_time = long(time.mktime(current_time_obj))
3874 current_time = long(time.time())
3877 res = samdb.search(expression="", base="", scope=ldb.SCOPE_BASE,
3878 attrs=["namingContexts"])
3881 for nc in res[0]["namingContexts"]:
3886 started_transaction = False
3888 samdb.transaction_start()
3889 started_transaction = True
3891 removed_links) = samdb.garbage_collect_tombstones(ncs,
3892 current_time=current_time,
3893 tombstone_lifetime=tombstone_lifetime)
3895 except Exception as err:
3896 if started_transaction:
3897 samdb.transaction_cancel()
3898 raise CommandError("Failed to expunge / garbage collect tombstones", err)
3900 samdb.transaction_commit()
3902 self.outf.write("Removed %d objects and %d links successfully\n"
3903 % (removed_objects, removed_links))
3907 class cmd_domain_trust(SuperCommand):
3908 """Domain and forest trust management."""
3911 subcommands["list"] = cmd_domain_trust_list()
3912 subcommands["show"] = cmd_domain_trust_show()
3913 subcommands["create"] = cmd_domain_trust_create()
3914 subcommands["delete"] = cmd_domain_trust_delete()
3915 subcommands["validate"] = cmd_domain_trust_validate()
3916 subcommands["namespaces"] = cmd_domain_trust_namespaces()
3918 class cmd_domain_tombstones(SuperCommand):
3919 """Domain tombstone and recycled object management."""
3922 subcommands["expunge"] = cmd_domain_tombstones_expunge()
3924 class ldif_schema_update:
3925 """Helper class for applying LDIF schema updates"""
3928 self.is_defunct = False
3929 self.unknown_oid = None
3933 def _ldap_schemaUpdateNow(self, samdb):
3937 add: schemaUpdateNow
3940 samdb.modify_ldif(ldif)
3942 def can_ignore_failure(self, error):
3943 """Checks if we can safely ignore failure to apply an LDIF update"""
3944 (num, errstr) = error.args
3946 # Microsoft has marked objects as defunct that Samba doesn't know about
3947 if num == ldb.ERR_NO_SUCH_OBJECT and self.is_defunct:
3948 print("Defunct object %s doesn't exist, skipping" % self.dn)
3950 elif self.unknown_oid is not None:
3951 print("Skipping unknown OID %s for object %s" %(self.unknown_oid, self.dn))
3956 def apply(self, samdb):
3957 """Applies a single LDIF update to the schema"""
3961 samdb.modify_ldif(self.ldif, controls=['relax:0'])
3962 except ldb.LdbError as e:
3963 if e.args[0] == ldb.ERR_INVALID_ATTRIBUTE_SYNTAX:
3965 # REFRESH after a failed change
3967 # Otherwise the OID-to-attribute mapping in
3968 # _apply_updates_in_file() won't work, because it
3969 # can't lookup the new OID in the schema
3970 self._ldap_schemaUpdateNow(samdb)
3972 samdb.modify_ldif(self.ldif, controls=['relax:0'])
3975 except ldb.LdbError as e:
3976 if self.can_ignore_failure(e):
3979 print("Exception: %s" % e)
3980 print("Encountered while trying to apply the following LDIF")
3981 print("----------------------------------------------------")
3982 print("%s" % self.ldif)
3988 class cmd_domain_schema_upgrade(Command):
3989 """Domain schema upgrading"""
3991 synopsis = "%prog [options]"
3993 takes_optiongroups = {
3994 "sambaopts": options.SambaOptions,
3995 "versionopts": options.VersionOptions,
3996 "credopts": options.CredentialsOptions,
4000 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
4001 metavar="URL", dest="H"),
4002 Option("--quiet", help="Be quiet", action="store_true"),
4003 Option("--verbose", help="Be verbose", action="store_true"),
4004 Option("--schema", type="choice", metavar="SCHEMA",
4005 choices=["2012", "2012_R2"],
4006 help="The schema file to upgrade to. Default is (Windows) 2012_R2.",
4008 Option("--ldf-file", type=str, default=None,
4009 help="Just apply the schema updates in the adprep/.LDF file(s) specified"),
4010 Option("--base-dir", type=str, default=None,
4011 help="Location of ldf files Default is ${SETUPDIR}/adprep.")
4014 def _apply_updates_in_file(self, samdb, ldif_file):
4016 Applies a series of updates specified in an .LDIF file. The .LDIF file
4017 is based on the adprep Schema updates provided by Microsoft.
4020 ldif_op = ldif_schema_update()
4022 # parse the file line by line and work out each update operation to apply
4023 for line in ldif_file:
4025 line = line.rstrip()
4027 # the operations in the .LDIF file are separated by blank lines. If
4028 # we hit a blank line, try to apply the update we've parsed so far
4031 # keep going if we haven't parsed anything yet
4032 if ldif_op.ldif == '':
4035 # Apply the individual change
4036 count += ldif_op.apply(samdb)
4038 # start storing the next operation from scratch again
4039 ldif_op = ldif_schema_update()
4042 # replace the placeholder domain name in the .ldif file with the real domain
4043 if line.upper().endswith('DC=X'):
4044 line = line[:-len('DC=X')] + str(samdb.get_default_basedn())
4045 elif line.upper().endswith('CN=X'):
4046 line = line[:-len('CN=X')] + str(samdb.get_default_basedn())
4048 values = line.split(':')
4050 if values[0].lower() == 'dn':
4051 ldif_op.dn = values[1].strip()
4053 # replace the Windows-specific operation with the Samba one
4054 if values[0].lower() == 'changetype':
4055 line = line.lower().replace(': ntdsschemaadd',
4057 line = line.lower().replace(': ntdsschemamodify',
4060 if values[0].lower() in ['rdnattid', 'subclassof',
4061 'systemposssuperiors',
4063 'systemauxiliaryclass']:
4066 # The Microsoft updates contain some OIDs we don't recognize.
4067 # Query the DB to see if we can work out the OID this update is
4068 # referring to. If we find a match, then replace the OID with
4069 # the ldapDisplayname
4071 res = samdb.search(base=samdb.get_schema_basedn(),
4072 expression="(|(attributeId=%s)(governsId=%s))" %
4074 attrs=['ldapDisplayName'])
4077 ldif_op.unknown_oid = value
4079 display_name = res[0]['ldapDisplayName'][0]
4080 line = line.replace(value, ' ' + display_name)
4082 # Microsoft has marked objects as defunct that Samba doesn't know about
4083 if values[0].lower() == 'isdefunct' and values[1].strip().lower() == 'true':
4084 ldif_op.is_defunct = True
4086 # Samba has added the showInAdvancedViewOnly attribute to all objects,
4087 # so rather than doing an add, we need to do a replace
4088 if values[0].lower() == 'add' and values[1].strip().lower() == 'showinadvancedviewonly':
4089 line = 'replace: showInAdvancedViewOnly'
4091 # Add the line to the current LDIF operation (including the newline
4092 # we stripped off at the start of the loop)
4093 ldif_op.ldif += line + '\n'
4098 def _apply_update(self, samdb, update_file, base_dir):
4099 """Wrapper function for parsing an LDIF file and applying the updates"""
4101 print("Applying %s updates..." % update_file)
4105 ldif_file = open(os.path.join(base_dir, update_file))
4107 count = self._apply_updates_in_file(samdb, ldif_file)
4113 print("%u changes applied" % count)
4117 def run(self, **kwargs):
4118 from samba.ms_schema_markdown import read_ms_markdown
4119 from samba.schema import Schema
4121 updates_allowed_overriden = False
4122 sambaopts = kwargs.get("sambaopts")
4123 credopts = kwargs.get("credopts")
4124 versionpts = kwargs.get("versionopts")
4125 lp = sambaopts.get_loadparm()
4126 creds = credopts.get_credentials(lp)
4128 target_schema = kwargs.get("schema")
4129 ldf_files = kwargs.get("ldf_file")
4130 base_dir = kwargs.get("base_dir")
4134 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
4136 # we're not going to get far if the config doesn't allow schema updates
4137 if lp.get("dsdb:schema update allowed") is None:
4138 lp.set("dsdb:schema update allowed", "yes")
4139 print("Temporarily overriding 'dsdb:schema update allowed' setting")
4140 updates_allowed_overriden = True
4142 own_dn = ldb.Dn(samdb, samdb.get_dsServiceName())
4143 master = get_fsmo_roleowner(samdb, str(samdb.get_schema_basedn()),
4145 if own_dn != master:
4146 raise CommandError("This server is not the schema master.")
4148 # if specific LDIF files were specified, just apply them
4150 schema_updates = ldf_files.split(",")
4154 # work out the version of the target schema we're upgrading to
4155 end = Schema.get_version(target_schema)
4157 # work out the version of the schema we're currently using
4158 res = samdb.search(base=samdb.get_schema_basedn(),
4159 scope=ldb.SCOPE_BASE, attrs=['objectVersion'])
4162 raise CommandError('Could not determine current schema version')
4163 start = int(res[0]['objectVersion'][0]) + 1
4165 diff_dir = setup_path("adprep/WindowsServerDocs")
4166 if base_dir is None:
4167 # Read from the Schema-Updates.md file
4168 temp_folder = tempfile.mkdtemp()
4170 update_file = setup_path("adprep/WindowsServerDocs/Schema-Updates.md")
4173 read_ms_markdown(update_file, temp_folder)
4174 except Exception as e:
4175 print("Exception in markdown parsing: %s" % e)
4176 shutil.rmtree(temp_folder)
4177 raise CommandError('Failed to upgrade schema')
4179 base_dir = temp_folder
4181 for version in range(start, end + 1):
4182 update = 'Sch%d.ldf' % version
4183 schema_updates.append(update)
4185 # Apply patches if we parsed the Schema-Updates.md file
4186 diff = os.path.abspath(os.path.join(diff_dir, update + '.diff'))
4187 if temp_folder and os.path.exists(diff):
4189 p = subprocess.Popen(['patch', update, '-i', diff],
4190 stdout=subprocess.PIPE,
4191 stderr=subprocess.PIPE, cwd=temp_folder)
4192 except (OSError, IOError):
4193 shutil.rmtree(temp_folder)
4194 raise CommandError("Failed to upgrade schema. Check if 'patch' is installed.")
4196 stdout, stderr = p.communicate()
4199 print("Exception in patch: %s\n%s" % (stdout, stderr))
4200 shutil.rmtree(temp_folder)
4201 raise CommandError('Failed to upgrade schema')
4203 print("Patched %s using %s" % (update, diff))
4205 if base_dir is None:
4206 base_dir = setup_path("adprep")
4208 samdb.transaction_start()
4210 error_encountered = False
4213 # Apply the schema updates needed to move to the new schema version
4214 for ldif_file in schema_updates:
4215 count += self._apply_update(samdb, ldif_file, base_dir)
4218 samdb.transaction_commit()
4219 print("Schema successfully updated")
4221 print("No changes applied to schema")
4222 samdb.transaction_cancel()
4223 except Exception as e:
4224 print("Exception: %s" % e)
4225 print("Error encountered, aborting schema upgrade")
4226 samdb.transaction_cancel()
4227 error_encountered = True
4229 if updates_allowed_overriden:
4230 lp.set("dsdb:schema update allowed", "no")
4233 shutil.rmtree(temp_folder)
4235 if error_encountered:
4236 raise CommandError('Failed to upgrade schema')
4238 class cmd_domain_functional_prep(Command):
4239 """Domain functional level preparation"""
4241 synopsis = "%prog [options]"
4243 takes_optiongroups = {
4244 "sambaopts": options.SambaOptions,
4245 "versionopts": options.VersionOptions,
4246 "credopts": options.CredentialsOptions,
4250 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
4251 metavar="URL", dest="H"),
4252 Option("--quiet", help="Be quiet", action="store_true"),
4253 Option("--verbose", help="Be verbose", action="store_true"),
4254 Option("--function-level", type="choice", metavar="FUNCTION_LEVEL",
4255 choices=["2008_R2", "2012", "2012_R2"],
4256 help="The schema file to upgrade to. Default is (Windows) 2012_R2.",
4258 Option("--forest-prep", action="store_true",
4259 help="Run the forest prep (by default, both the domain and forest prep are run)."),
4260 Option("--domain-prep", action="store_true",
4261 help="Run the domain prep (by default, both the domain and forest prep are run).")
4264 def run(self, **kwargs):
4265 updates_allowed_overriden = False
4266 sambaopts = kwargs.get("sambaopts")
4267 credopts = kwargs.get("credopts")
4268 versionpts = kwargs.get("versionopts")
4269 lp = sambaopts.get_loadparm()
4270 creds = credopts.get_credentials(lp)
4272 target_level = string_version_to_constant[kwargs.get("function_level")]
4273 forest_prep = kwargs.get("forest_prep")
4274 domain_prep = kwargs.get("domain_prep")
4276 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
4278 # we're not going to get far if the config doesn't allow schema updates
4279 if lp.get("dsdb:schema update allowed") is None:
4280 lp.set("dsdb:schema update allowed", "yes")
4281 print("Temporarily overriding 'dsdb:schema update allowed' setting")
4282 updates_allowed_overriden = True
4284 if forest_prep is None and domain_prep is None:
4288 own_dn = ldb.Dn(samdb, samdb.get_dsServiceName())
4290 master = get_fsmo_roleowner(samdb, str(samdb.get_schema_basedn()),
4292 if own_dn != master:
4293 raise CommandError("This server is not the schema master.")
4296 domain_dn = samdb.domain_dn()
4297 infrastructure_dn = "CN=Infrastructure," + domain_dn
4298 master = get_fsmo_roleowner(samdb, infrastructure_dn,
4300 if own_dn != master:
4301 raise CommandError("This server is not the infrastructure master.")
4304 samdb.transaction_start()
4305 error_encountered = False
4307 from samba.forest_update import ForestUpdate
4308 forest = ForestUpdate(samdb, fix=True)
4310 forest.check_updates_iterator([53, 79, 80, 81, 82, 83])
4311 forest.check_updates_functional_level(target_level,
4312 DS_DOMAIN_FUNCTION_2008_R2,
4313 update_revision=True)
4315 samdb.transaction_commit()
4316 except Exception as e:
4317 print("Exception: %s" % e)
4318 samdb.transaction_cancel()
4319 error_encountered = True
4322 samdb.transaction_start()
4323 error_encountered = False
4325 from samba.domain_update import DomainUpdate
4327 domain = DomainUpdate(samdb, fix=True)
4328 domain.check_updates_functional_level(target_level,
4329 DS_DOMAIN_FUNCTION_2008,
4330 update_revision=True)
4332 samdb.transaction_commit()
4333 except Exception as e:
4334 print("Exception: %s" % e)
4335 samdb.transaction_cancel()
4336 error_encountered = True
4338 if updates_allowed_overriden:
4339 lp.set("dsdb:schema update allowed", "no")
4341 if error_encountered:
4342 raise CommandError('Failed to perform functional prep')
4344 class cmd_domain(SuperCommand):
4345 """Domain management."""
4348 subcommands["demote"] = cmd_domain_demote()
4349 if cmd_domain_export_keytab is not None:
4350 subcommands["exportkeytab"] = cmd_domain_export_keytab()
4351 subcommands["info"] = cmd_domain_info()
4352 subcommands["provision"] = cmd_domain_provision()
4353 subcommands["join"] = cmd_domain_join()
4354 subcommands["dcpromo"] = cmd_domain_dcpromo()
4355 subcommands["level"] = cmd_domain_level()
4356 subcommands["passwordsettings"] = cmd_domain_passwordsettings()
4357 subcommands["classicupgrade"] = cmd_domain_classicupgrade()
4358 subcommands["samba3upgrade"] = cmd_domain_samba3upgrade()
4359 subcommands["trust"] = cmd_domain_trust()
4360 subcommands["tombstones"] = cmd_domain_tombstones()
4361 subcommands["schemaupgrade"] = cmd_domain_schema_upgrade()
4362 subcommands["functionalprep"] = cmd_domain_functional_prep()