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
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 import samba.getopt as options
34 from getpass import getpass
35 from samba.net import Net, LIBNET_JOIN_AUTOMATIC
37 from samba.join import join_RODC, join_DC, join_subdomain
38 from samba.auth import system_session
39 from samba.samdb import SamDB
40 from samba.ndr import ndr_unpack, ndr_pack, ndr_print
41 from samba.dcerpc import drsuapi
42 from samba.dcerpc import drsblobs
43 from samba.dcerpc import lsa
44 from samba.dcerpc import netlogon
45 from samba.dcerpc import security
46 from samba.dcerpc import nbt
47 from samba.dcerpc.samr import DOMAIN_PASSWORD_COMPLEX, DOMAIN_PASSWORD_STORE_CLEARTEXT
48 from samba.netcmd import (
54 from samba.netcmd.common import netcmd_get_domain_infos_via_cldap
55 from samba.samba3 import Samba3
56 from samba.samba3 import param as s3param
57 from samba.upgrade import upgrade_from_samba3
58 from samba.drs_utils import (
59 sendDsReplicaSync, drsuapi_connect, drsException,
63 from samba.dsdb import (
64 DS_DOMAIN_FUNCTION_2000,
65 DS_DOMAIN_FUNCTION_2003,
66 DS_DOMAIN_FUNCTION_2003_MIXED,
67 DS_DOMAIN_FUNCTION_2008,
68 DS_DOMAIN_FUNCTION_2008_R2,
69 DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL,
70 DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL,
71 UF_WORKSTATION_TRUST_ACCOUNT,
72 UF_SERVER_TRUST_ACCOUNT,
73 UF_TRUSTED_FOR_DELEGATION
76 from samba.provision import (
81 from samba.provision.common import (
87 def get_testparm_var(testparm, smbconf, varname):
88 cmd = "%s -s -l --parameter-name='%s' %s 2>/dev/null" % (testparm, varname, smbconf)
89 output = os.popen(cmd, 'r').readline()
95 cmd_domain_export_keytab = None
97 class cmd_domain_export_keytab(Command):
98 """Dump Kerberos keys of the domain into a keytab."""
100 synopsis = "%prog <keytab> [options]"
102 takes_optiongroups = {
103 "sambaopts": options.SambaOptions,
104 "credopts": options.CredentialsOptions,
105 "versionopts": options.VersionOptions,
109 Option("--principal", help="extract only this principal", type=str),
112 takes_args = ["keytab"]
114 def run(self, keytab, credopts=None, sambaopts=None, versionopts=None, principal=None):
115 lp = sambaopts.get_loadparm()
117 net.export_keytab(keytab=keytab, principal=principal)
120 class cmd_domain_info(Command):
121 """Print basic info about a domain and the DC passed as parameter."""
123 synopsis = "%prog <ip_address> [options]"
128 takes_optiongroups = {
129 "sambaopts": options.SambaOptions,
130 "credopts": options.CredentialsOptions,
131 "versionopts": options.VersionOptions,
134 takes_args = ["address"]
136 def run(self, address, credopts=None, sambaopts=None, versionopts=None):
137 lp = sambaopts.get_loadparm()
139 res = netcmd_get_domain_infos_via_cldap(lp, None, address)
141 raise CommandError("Invalid IP address '" + address + "'!")
142 self.outf.write("Forest : %s\n" % res.forest)
143 self.outf.write("Domain : %s\n" % res.dns_domain)
144 self.outf.write("Netbios domain : %s\n" % res.domain_name)
145 self.outf.write("DC name : %s\n" % res.pdc_dns_name)
146 self.outf.write("DC netbios name : %s\n" % res.pdc_name)
147 self.outf.write("Server site : %s\n" % res.server_site)
148 self.outf.write("Client site : %s\n" % res.client_site)
151 class cmd_domain_provision(Command):
152 """Provision a domain."""
154 synopsis = "%prog [options]"
156 takes_optiongroups = {
157 "sambaopts": options.SambaOptions,
158 "versionopts": options.VersionOptions,
162 Option("--interactive", help="Ask for names", action="store_true"),
163 Option("--domain", type="string", metavar="DOMAIN",
165 Option("--domain-guid", type="string", metavar="GUID",
166 help="set domainguid (otherwise random)"),
167 Option("--domain-sid", type="string", metavar="SID",
168 help="set domainsid (otherwise random)"),
169 Option("--ntds-guid", type="string", metavar="GUID",
170 help="set NTDS object GUID (otherwise random)"),
171 Option("--invocationid", type="string", metavar="GUID",
172 help="set invocationid (otherwise random)"),
173 Option("--host-name", type="string", metavar="HOSTNAME",
174 help="set hostname"),
175 Option("--host-ip", type="string", metavar="IPADDRESS",
176 help="set IPv4 ipaddress"),
177 Option("--host-ip6", type="string", metavar="IP6ADDRESS",
178 help="set IPv6 ipaddress"),
179 Option("--site", type="string", metavar="SITENAME",
180 help="set site name"),
181 Option("--adminpass", type="string", metavar="PASSWORD",
182 help="choose admin password (otherwise random)"),
183 Option("--krbtgtpass", type="string", metavar="PASSWORD",
184 help="choose krbtgt password (otherwise random)"),
185 Option("--machinepass", type="string", metavar="PASSWORD",
186 help="choose machine password (otherwise random)"),
187 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
188 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
189 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
190 "BIND9_FLATFILE uses bind9 text database to store zone information, "
191 "BIND9_DLZ uses samba4 AD to store zone information, "
192 "NONE skips the DNS setup entirely (not recommended)",
193 default="SAMBA_INTERNAL"),
194 Option("--dnspass", type="string", metavar="PASSWORD",
195 help="choose dns password (otherwise random)"),
196 Option("--ldapadminpass", type="string", metavar="PASSWORD",
197 help="choose password to set between Samba and its LDAP backend (otherwise random)"),
198 Option("--root", type="string", metavar="USERNAME",
199 help="choose 'root' unix username"),
200 Option("--nobody", type="string", metavar="USERNAME",
201 help="choose 'nobody' user"),
202 Option("--users", type="string", metavar="GROUPNAME",
203 help="choose 'users' group"),
204 Option("--quiet", help="Be quiet", action="store_true"),
205 Option("--blank", action="store_true",
206 help="do not add users or groups, just the structure"),
207 Option("--ldap-backend-type", type="choice", metavar="LDAP-BACKEND-TYPE",
208 help="Test initialisation support for unsupported LDAP backend type (fedora-ds or openldap) DO NOT USE",
209 choices=["fedora-ds", "openldap"]),
210 Option("--server-role", type="choice", metavar="ROLE",
211 choices=["domain controller", "dc", "member server", "member", "standalone"],
212 help="The server role (domain controller | dc | member server | member | standalone). Default is dc.",
213 default="domain controller"),
214 Option("--function-level", type="choice", metavar="FOR-FUN-LEVEL",
215 choices=["2000", "2003", "2008", "2008_R2"],
216 help="The domain and forest function level (2000 | 2003 | 2008 | 2008_R2 - always native). Default is (Windows) 2008_R2 Native.",
218 Option("--next-rid", type="int", metavar="NEXTRID", default=1000,
219 help="The initial nextRid value (only needed for upgrades). Default is 1000."),
220 Option("--partitions-only",
221 help="Configure Samba's partitions, but do not modify them (ie, join a BDC)", action="store_true"),
222 Option("--targetdir", type="string", metavar="DIR",
223 help="Set target directory"),
224 Option("--ol-mmr-urls", type="string", metavar="LDAPSERVER",
225 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\""),
226 Option("--use-xattrs", type="choice", choices=["yes", "no", "auto"], help="Define if we should use the native fs capabilities or a tdb file for storing attributes likes ntacl, auto tries to make an inteligent guess based on the user rights and system capabilities", default="auto"),
228 Option("--use-rfc2307", action="store_true", help="Use AD to store posix attributes (default = no)"),
232 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",
233 action="store_true"),
234 Option("--slapd-path", type="string", metavar="SLAPD-PATH",
235 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."),
236 Option("--ldap-backend-extra-port", type="int", metavar="LDAP-BACKEND-EXTRA-PORT", help="Additional TCP port for LDAP backend server (to use for replication)"),
237 Option("--ldap-backend-forced-uri", type="string", metavar="LDAP-BACKEND-FORCED-URI",
238 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"),
239 Option("--ldap-backend-nosync", help="Configure LDAP backend not to call fsync() (for performance in test environments)", action="store_true"),
243 Option("--use-ntvfs", action="store_true", help="Use NTVFS for the fileserver (default = no)"),
246 if os.getenv('TEST_LDAP', "no") == "yes":
247 takes_options.extend(openldap_options)
249 if samba.is_ntvfs_fileserver_built():
250 takes_options.extend(ntvfs_options)
254 def run(self, sambaopts=None, versionopts=None,
277 ldap_backend_type=None,
281 partitions_only=None,
288 ldap_backend_nosync=None,
289 ldap_backend_extra_port=None,
290 ldap_backend_forced_uri=None,
291 ldap_dryrun_mode=None):
293 self.logger = self.get_logger("provision")
295 self.logger.setLevel(logging.WARNING)
297 self.logger.setLevel(logging.INFO)
299 lp = sambaopts.get_loadparm()
300 smbconf = lp.configfile
302 if dns_forwarder is not None:
303 suggested_forwarder = dns_forwarder
305 suggested_forwarder = self._get_nameserver_ip()
306 if suggested_forwarder is None:
307 suggested_forwarder = "none"
309 if len(self.raw_argv) == 1:
313 from getpass import getpass
316 def ask(prompt, default=None):
317 if default is not None:
318 print "%s [%s]: " % (prompt, default),
320 print "%s: " % (prompt,),
321 return sys.stdin.readline().rstrip("\n") or default
324 default = socket.getfqdn().split(".", 1)[1].upper()
327 realm = ask("Realm", default)
328 if realm in (None, ""):
329 raise CommandError("No realm set!")
332 default = realm.split(".")[0]
335 domain = ask("Domain", default)
337 raise CommandError("No domain set!")
339 server_role = ask("Server Role (dc, member, standalone)", "dc")
341 dns_backend = ask("DNS backend (SAMBA_INTERNAL, BIND9_FLATFILE, BIND9_DLZ, NONE)", "SAMBA_INTERNAL")
342 if dns_backend in (None, ''):
343 raise CommandError("No DNS backend set!")
345 if dns_backend == "SAMBA_INTERNAL":
346 dns_forwarder = ask("DNS forwarder IP address (write 'none' to disable forwarding)", suggested_forwarder)
347 if dns_forwarder.lower() in (None, 'none'):
348 suggested_forwarder = None
352 adminpassplain = getpass("Administrator password: ")
353 if not adminpassplain:
354 self.errf.write("Invalid administrator password.\n")
356 adminpassverify = getpass("Retype password: ")
357 if not adminpassplain == adminpassverify:
358 self.errf.write("Sorry, passwords do not match.\n")
360 adminpass = adminpassplain
364 realm = sambaopts._lp.get('realm')
366 raise CommandError("No realm set!")
368 raise CommandError("No domain set!")
371 self.logger.info("Administrator password will be set randomly!")
373 if function_level == "2000":
374 dom_for_fun_level = DS_DOMAIN_FUNCTION_2000
375 elif function_level == "2003":
376 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
377 elif function_level == "2008":
378 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008
379 elif function_level == "2008_R2":
380 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
382 if dns_backend == "SAMBA_INTERNAL" and dns_forwarder is None:
383 dns_forwarder = suggested_forwarder
385 samdb_fill = FILL_FULL
387 samdb_fill = FILL_NT4SYNC
388 elif partitions_only:
389 samdb_fill = FILL_DRS
391 if targetdir is not None:
392 if not os.path.isdir(targetdir):
397 if use_xattrs == "yes":
399 elif use_xattrs == "auto" and not lp.get("posix:eadb"):
401 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
403 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
406 samba.ntacls.setntacl(lp, file.name,
407 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
410 self.logger.info("You are not root or your system do not support xattr, using tdb backend for attributes. ")
415 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.")
416 if ldap_backend_type == "existing":
417 if ldap_backend_forced_uri is not None:
418 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)
420 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")
422 if ldap_backend_forced_uri is not None:
423 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")
425 if domain_sid is not None:
426 domain_sid = security.dom_sid(domain_sid)
428 session = system_session()
430 result = provision(self.logger,
431 session, smbconf=smbconf, targetdir=targetdir,
432 samdb_fill=samdb_fill, realm=realm, domain=domain,
433 domainguid=domain_guid, domainsid=domain_sid,
435 hostip=host_ip, hostip6=host_ip6,
436 sitename=site, ntdsguid=ntds_guid,
437 invocationid=invocationid, adminpass=adminpass,
438 krbtgtpass=krbtgtpass, machinepass=machinepass,
439 dns_backend=dns_backend, dns_forwarder=dns_forwarder,
440 dnspass=dnspass, root=root, nobody=nobody,
442 serverrole=server_role, dom_for_fun_level=dom_for_fun_level,
443 backend_type=ldap_backend_type,
444 ldapadminpass=ldapadminpass, ol_mmr_urls=ol_mmr_urls, slapd_path=slapd_path,
445 useeadb=eadb, next_rid=next_rid, lp=lp, use_ntvfs=use_ntvfs,
446 use_rfc2307=use_rfc2307, skip_sysvolacl=False,
447 ldap_backend_extra_port=ldap_backend_extra_port,
448 ldap_backend_forced_uri=ldap_backend_forced_uri,
449 nosync=ldap_backend_nosync, ldap_dryrun_mode=ldap_dryrun_mode)
451 except ProvisioningError, e:
452 raise CommandError("Provision failed", e)
454 result.report_logger(self.logger)
456 def _get_nameserver_ip(self):
457 """Grab the nameserver IP address from /etc/resolv.conf."""
459 RESOLV_CONF="/etc/resolv.conf"
461 if not path.isfile(RESOLV_CONF):
462 self.logger.warning("Failed to locate %s" % RESOLV_CONF)
467 handle = open(RESOLV_CONF, 'r')
469 if not line.startswith('nameserver'):
471 # we want the last non-space continuous string of the line
472 return line.strip().split()[-1]
474 if handle is not None:
477 self.logger.warning("No nameserver found in %s" % RESOLV_CONF)
480 class cmd_domain_dcpromo(Command):
481 """Promote an existing domain member or NT4 PDC to an AD DC."""
483 synopsis = "%prog <dnsdomain> [DC|RODC] [options]"
485 takes_optiongroups = {
486 "sambaopts": options.SambaOptions,
487 "versionopts": options.VersionOptions,
488 "credopts": options.CredentialsOptions,
492 Option("--server", help="DC to join", type=str),
493 Option("--site", help="site to join", type=str),
494 Option("--targetdir", help="where to store provision", type=str),
495 Option("--domain-critical-only",
496 help="only replicate critical domain objects",
497 action="store_true"),
498 Option("--machinepass", type=str, metavar="PASSWORD",
499 help="choose machine password (otherwise random)"),
500 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
501 choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
502 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
503 "BIND9_DLZ uses samba4 AD to store zone information, "
504 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
505 default="SAMBA_INTERNAL"),
506 Option("--quiet", help="Be quiet", action="store_true"),
507 Option("--verbose", help="Be verbose", action="store_true")
511 Option("--use-ntvfs", action="store_true", help="Use NTVFS for the fileserver (default = no)"),
514 if samba.is_ntvfs_fileserver_built():
515 takes_options.extend(ntvfs_options)
518 takes_args = ["domain", "role?"]
520 def run(self, domain, role=None, sambaopts=None, credopts=None,
521 versionopts=None, server=None, site=None, targetdir=None,
522 domain_critical_only=False, parent_domain=None, machinepass=None,
523 use_ntvfs=False, dns_backend=None,
524 quiet=False, verbose=False):
525 lp = sambaopts.get_loadparm()
526 creds = credopts.get_credentials(lp)
527 net = Net(creds, lp, server=credopts.ipaddress)
530 site = "Default-First-Site-Name"
532 logger = self.get_logger()
534 logger.setLevel(logging.DEBUG)
536 logger.setLevel(logging.WARNING)
538 logger.setLevel(logging.INFO)
540 netbios_name = lp.get("netbios name")
546 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
547 site=site, netbios_name=netbios_name, targetdir=targetdir,
548 domain_critical_only=domain_critical_only,
549 machinepass=machinepass, use_ntvfs=use_ntvfs,
550 dns_backend=dns_backend,
551 promote_existing=True)
553 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
554 site=site, netbios_name=netbios_name, targetdir=targetdir,
555 domain_critical_only=domain_critical_only,
556 machinepass=machinepass, use_ntvfs=use_ntvfs, dns_backend=dns_backend,
557 promote_existing=True)
559 raise CommandError("Invalid role '%s' (possible values: DC, RODC)" % role)
562 class cmd_domain_join(Command):
563 """Join domain as either member or backup domain controller."""
565 synopsis = "%prog <dnsdomain> [DC|RODC|MEMBER|SUBDOMAIN] [options]"
567 takes_optiongroups = {
568 "sambaopts": options.SambaOptions,
569 "versionopts": options.VersionOptions,
570 "credopts": options.CredentialsOptions,
574 Option("--server", help="DC to join", type=str),
575 Option("--site", help="site to join", type=str),
576 Option("--targetdir", help="where to store provision", type=str),
577 Option("--parent-domain", help="parent domain to create subdomain under", type=str),
578 Option("--domain-critical-only",
579 help="only replicate critical domain objects",
580 action="store_true"),
581 Option("--machinepass", type=str, metavar="PASSWORD",
582 help="choose machine password (otherwise random)"),
583 Option("--adminpass", type="string", metavar="PASSWORD",
584 help="choose adminstrator password when joining as a subdomain (otherwise random)"),
585 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
586 choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
587 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
588 "BIND9_DLZ uses samba4 AD to store zone information, "
589 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
590 default="SAMBA_INTERNAL"),
591 Option("--quiet", help="Be quiet", action="store_true"),
592 Option("--verbose", help="Be verbose", action="store_true")
596 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
599 if samba.is_ntvfs_fileserver_built():
600 takes_options.extend(ntvfs_options)
602 takes_args = ["domain", "role?"]
604 def run(self, domain, role=None, sambaopts=None, credopts=None,
605 versionopts=None, server=None, site=None, targetdir=None,
606 domain_critical_only=False, parent_domain=None, machinepass=None,
607 use_ntvfs=False, dns_backend=None, adminpass=None,
608 quiet=False, verbose=False):
609 lp = sambaopts.get_loadparm()
610 creds = credopts.get_credentials(lp)
611 net = Net(creds, lp, server=credopts.ipaddress)
614 site = "Default-First-Site-Name"
616 logger = self.get_logger()
618 logger.setLevel(logging.DEBUG)
620 logger.setLevel(logging.WARNING)
622 logger.setLevel(logging.INFO)
624 netbios_name = lp.get("netbios name")
629 if role is None or role == "MEMBER":
630 (join_password, sid, domain_name) = net.join_member(
631 domain, netbios_name, LIBNET_JOIN_AUTOMATIC,
632 machinepass=machinepass)
634 self.errf.write("Joined domain %s (%s)\n" % (domain_name, sid))
636 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
637 site=site, netbios_name=netbios_name, targetdir=targetdir,
638 domain_critical_only=domain_critical_only,
639 machinepass=machinepass, use_ntvfs=use_ntvfs, dns_backend=dns_backend)
641 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
642 site=site, netbios_name=netbios_name, targetdir=targetdir,
643 domain_critical_only=domain_critical_only,
644 machinepass=machinepass, use_ntvfs=use_ntvfs,
645 dns_backend=dns_backend)
646 elif role == "SUBDOMAIN":
648 logger.info("Administrator password will be set randomly!")
650 netbios_domain = lp.get("workgroup")
651 if parent_domain is None:
652 parent_domain = ".".join(domain.split(".")[1:])
653 join_subdomain(logger=logger, server=server, creds=creds, lp=lp, dnsdomain=domain,
654 parent_domain=parent_domain, site=site,
655 netbios_name=netbios_name, netbios_domain=netbios_domain,
656 targetdir=targetdir, machinepass=machinepass,
657 use_ntvfs=use_ntvfs, dns_backend=dns_backend,
660 raise CommandError("Invalid role '%s' (possible values: MEMBER, DC, RODC, SUBDOMAIN)" % role)
663 class cmd_domain_demote(Command):
664 """Demote ourselves from the role of Domain Controller."""
666 synopsis = "%prog [options]"
669 Option("--server", help="DC to force replication before demote", type=str),
670 Option("--targetdir", help="where provision is stored", type=str),
673 takes_optiongroups = {
674 "sambaopts": options.SambaOptions,
675 "credopts": options.CredentialsOptions,
676 "versionopts": options.VersionOptions,
679 def run(self, sambaopts=None, credopts=None,
680 versionopts=None, server=None, targetdir=None):
681 lp = sambaopts.get_loadparm()
682 creds = credopts.get_credentials(lp)
683 net = Net(creds, lp, server=credopts.ipaddress)
685 netbios_name = lp.get("netbios name")
686 samdb = SamDB(session_info=system_session(), credentials=creds, lp=lp)
688 res = samdb.search(expression='(&(objectClass=computer)(serverReferenceBL=*))', attrs=["dnsHostName", "name"])
690 raise CommandError("Unable to search for servers")
693 raise CommandError("You are the latest server in the domain")
697 if str(e["name"]).lower() != netbios_name.lower():
698 server = e["dnsHostName"]
701 ntds_guid = samdb.get_ntds_GUID()
702 msg = samdb.search(base=str(samdb.get_config_basedn()),
703 scope=ldb.SCOPE_SUBTREE, expression="(objectGUID=%s)" % ntds_guid,
705 if len(msg) == 0 or "options" not in msg[0]:
706 raise CommandError("Failed to find options on %s" % ntds_guid)
709 dsa_options = int(str(msg[0]['options']))
711 res = samdb.search(expression="(fSMORoleOwner=%s)" % str(ntds_dn),
712 controls=["search_options:1:2"])
715 raise CommandError("Current DC is still the owner of %d role(s), use the role command to transfer roles to another DC" % len(res))
717 self.errf.write("Using %s as partner server for the demotion\n" %
719 (drsuapiBind, drsuapi_handle, supportedExtensions) = drsuapi_connect(server, lp, creds)
721 self.errf.write("Deactivating inbound replication\n")
726 dsa_options |= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
727 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
730 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
732 self.errf.write("Asking partner server %s to synchronize from us\n"
734 for part in (samdb.get_schema_basedn(),
735 samdb.get_config_basedn(),
736 samdb.get_root_basedn()):
738 sendDsReplicaSync(drsuapiBind, drsuapi_handle, ntds_guid, str(part), drsuapi.DRSUAPI_DRS_WRIT_REP)
739 except drsException, e:
741 "Error while demoting, "
742 "re-enabling inbound replication\n")
743 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
744 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
746 raise CommandError("Error while sending a DsReplicaSync for partion %s" % str(part), e)
748 remote_samdb = SamDB(url="ldap://%s" % server,
749 session_info=system_session(),
750 credentials=creds, lp=lp)
752 self.errf.write("Changing userControl and container\n")
753 res = remote_samdb.search(base=str(remote_samdb.get_root_basedn()),
754 expression="(&(objectClass=user)(sAMAccountName=%s$))" %
755 netbios_name.upper(),
756 attrs=["userAccountControl"])
758 uac = int(str(res[0]["userAccountControl"]))
762 "Error while demoting, re-enabling inbound replication\n")
763 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
764 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
766 raise CommandError("Error while changing account control", e)
770 "Error while demoting, re-enabling inbound replication")
771 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
772 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
774 raise CommandError("Unable to find object with samaccountName = %s$"
775 " in the remote dc" % netbios_name.upper())
779 uac ^= (UF_SERVER_TRUST_ACCOUNT|UF_TRUSTED_FOR_DELEGATION)
780 uac |= UF_WORKSTATION_TRUST_ACCOUNT
785 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
786 ldb.FLAG_MOD_REPLACE,
787 "userAccountControl")
789 remote_samdb.modify(msg)
792 "Error while demoting, re-enabling inbound replication")
793 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
794 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
797 raise CommandError("Error while changing account control", e)
799 parent = msg.dn.parent()
801 rdn = string.replace(rdn, ",%s" % str(parent), "")
802 # Let's move to the Computer container
806 computer_dn = ldb.Dn(remote_samdb, "CN=Computers,%s" % str(remote_samdb.get_root_basedn()))
807 res = remote_samdb.search(base=computer_dn, expression=rdn, scope=ldb.SCOPE_ONELEVEL)
810 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
811 scope=ldb.SCOPE_ONELEVEL)
812 while(len(res) != 0 and i < 100):
814 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
815 scope=ldb.SCOPE_ONELEVEL)
819 "Error while demoting, re-enabling inbound replication\n")
820 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
821 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
827 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
828 ldb.FLAG_MOD_REPLACE,
829 "userAccountControl")
831 remote_samdb.modify(msg)
833 raise CommandError("Unable to find a slot for renaming %s,"
834 " all names from %s-1 to %s-%d seemed used" %
835 (str(dc_dn), rdn, rdn, i - 9))
837 newrdn = "%s-%d" % (rdn, i)
840 newdn = ldb.Dn(remote_samdb, "%s,%s" % (newrdn, str(computer_dn)))
841 remote_samdb.rename(dc_dn, newdn)
844 "Error while demoting, re-enabling inbound replication\n")
845 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
846 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
852 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
853 ldb.FLAG_MOD_REPLACE,
854 "userAccountControl")
856 remote_samdb.modify(msg)
857 raise CommandError("Error while renaming %s to %s" % (str(dc_dn), str(newdn)), e)
860 server_dsa_dn = samdb.get_serverName()
861 domain = remote_samdb.get_root_basedn()
864 sendRemoveDsServer(drsuapiBind, drsuapi_handle, server_dsa_dn, domain)
865 except drsException, e:
867 "Error while demoting, re-enabling inbound replication\n")
868 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
869 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
875 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
876 ldb.FLAG_MOD_REPLACE,
877 "userAccountControl")
879 remote_samdb.modify(msg)
880 remote_samdb.rename(newdn, dc_dn)
881 raise CommandError("Error while sending a removeDsServer", e)
883 for s in ("CN=Enterprise,CN=Microsoft System Volumes,CN=System,CN=Configuration",
884 "CN=%s,CN=Microsoft System Volumes,CN=System,CN=Configuration" % lp.get("realm"),
885 "CN=Domain System Volumes (SYSVOL share),CN=File Replication Service,CN=System"):
887 remote_samdb.delete(ldb.Dn(remote_samdb,
888 "%s,%s,%s" % (str(rdn), s, str(remote_samdb.get_root_basedn()))))
889 except ldb.LdbError, l:
892 for s in ("CN=Enterprise,CN=NTFRS Subscriptions",
893 "CN=%s, CN=NTFRS Subscriptions" % lp.get("realm"),
894 "CN=Domain system Volumes (SYSVOL Share), CN=NTFRS Subscriptions",
895 "CN=NTFRS Subscriptions"):
897 remote_samdb.delete(ldb.Dn(remote_samdb,
898 "%s,%s" % (s, str(newdn))))
899 except ldb.LdbError, l:
902 self.errf.write("Demote successful\n")
905 class cmd_domain_level(Command):
906 """Raise domain and forest function levels."""
908 synopsis = "%prog (show|raise <options>) [options]"
910 takes_optiongroups = {
911 "sambaopts": options.SambaOptions,
912 "credopts": options.CredentialsOptions,
913 "versionopts": options.VersionOptions,
917 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
918 metavar="URL", dest="H"),
919 Option("--quiet", help="Be quiet", action="store_true"),
920 Option("--forest-level", type="choice", choices=["2003", "2008", "2008_R2"],
921 help="The forest function level (2003 | 2008 | 2008_R2)"),
922 Option("--domain-level", type="choice", choices=["2003", "2008", "2008_R2"],
923 help="The domain function level (2003 | 2008 | 2008_R2)")
926 takes_args = ["subcommand"]
928 def run(self, subcommand, H=None, forest_level=None, domain_level=None,
929 quiet=False, credopts=None, sambaopts=None, versionopts=None):
930 lp = sambaopts.get_loadparm()
931 creds = credopts.get_credentials(lp, fallback_machine=True)
933 samdb = SamDB(url=H, session_info=system_session(),
934 credentials=creds, lp=lp)
936 domain_dn = samdb.domain_dn()
938 res_forest = samdb.search("CN=Partitions,%s" % samdb.get_config_basedn(),
939 scope=ldb.SCOPE_BASE, attrs=["msDS-Behavior-Version"])
940 assert len(res_forest) == 1
942 res_domain = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
943 attrs=["msDS-Behavior-Version", "nTMixedDomain"])
944 assert len(res_domain) == 1
946 res_dc_s = samdb.search("CN=Sites,%s" % samdb.get_config_basedn(),
947 scope=ldb.SCOPE_SUBTREE, expression="(objectClass=nTDSDSA)",
948 attrs=["msDS-Behavior-Version"])
949 assert len(res_dc_s) >= 1
952 level_forest = int(res_forest[0]["msDS-Behavior-Version"][0])
953 level_domain = int(res_domain[0]["msDS-Behavior-Version"][0])
954 level_domain_mixed = int(res_domain[0]["nTMixedDomain"][0])
956 min_level_dc = int(res_dc_s[0]["msDS-Behavior-Version"][0]) # Init value
958 if int(msg["msDS-Behavior-Version"][0]) < min_level_dc:
959 min_level_dc = int(msg["msDS-Behavior-Version"][0])
961 if level_forest < 0 or level_domain < 0:
962 raise CommandError("Domain and/or forest function level(s) is/are invalid. Correct them or reprovision!")
964 raise CommandError("Lowest function level of a DC is invalid. Correct this or reprovision!")
965 if level_forest > level_domain:
966 raise CommandError("Forest function level is higher than the domain level(s). Correct this or reprovision!")
967 if level_domain > min_level_dc:
968 raise CommandError("Domain function level is higher than the lowest function level of a DC. Correct this or reprovision!")
971 raise CommandError("Could not retrieve the actual domain, forest level and/or lowest DC function level!")
973 if subcommand == "show":
974 self.message("Domain and forest function level for domain '%s'" % domain_dn)
975 if level_forest == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
976 self.message("\nATTENTION: You run SAMBA 4 on a forest function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
977 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
978 self.message("\nATTENTION: You run SAMBA 4 on a domain function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
979 if min_level_dc == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
980 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)!")
984 if level_forest == DS_DOMAIN_FUNCTION_2000:
986 elif level_forest == DS_DOMAIN_FUNCTION_2003_MIXED:
987 outstr = "2003 with mixed domains/interim (NT4 DC support)"
988 elif level_forest == DS_DOMAIN_FUNCTION_2003:
990 elif level_forest == DS_DOMAIN_FUNCTION_2008:
992 elif level_forest == DS_DOMAIN_FUNCTION_2008_R2:
995 outstr = "higher than 2008 R2"
996 self.message("Forest function level: (Windows) " + outstr)
998 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
999 outstr = "2000 mixed (NT4 DC support)"
1000 elif level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed == 0:
1002 elif level_domain == DS_DOMAIN_FUNCTION_2003_MIXED:
1003 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1004 elif level_domain == DS_DOMAIN_FUNCTION_2003:
1006 elif level_domain == DS_DOMAIN_FUNCTION_2008:
1008 elif level_domain == DS_DOMAIN_FUNCTION_2008_R2:
1011 outstr = "higher than 2008 R2"
1012 self.message("Domain function level: (Windows) " + outstr)
1014 if min_level_dc == DS_DOMAIN_FUNCTION_2000:
1016 elif min_level_dc == DS_DOMAIN_FUNCTION_2003:
1018 elif min_level_dc == DS_DOMAIN_FUNCTION_2008:
1020 elif min_level_dc == DS_DOMAIN_FUNCTION_2008_R2:
1023 outstr = "higher than 2008 R2"
1024 self.message("Lowest function level of a DC: (Windows) " + outstr)
1026 elif subcommand == "raise":
1029 if domain_level is not None:
1030 if domain_level == "2003":
1031 new_level_domain = DS_DOMAIN_FUNCTION_2003
1032 elif domain_level == "2008":
1033 new_level_domain = DS_DOMAIN_FUNCTION_2008
1034 elif domain_level == "2008_R2":
1035 new_level_domain = DS_DOMAIN_FUNCTION_2008_R2
1037 if new_level_domain <= level_domain and level_domain_mixed == 0:
1038 raise CommandError("Domain function level can't be smaller than or equal to the actual one!")
1040 if new_level_domain > min_level_dc:
1041 raise CommandError("Domain function level can't be higher than the lowest function level of a DC!")
1043 # Deactivate mixed/interim domain support
1044 if level_domain_mixed != 0:
1045 # Directly on the base DN
1047 m.dn = ldb.Dn(samdb, domain_dn)
1048 m["nTMixedDomain"] = ldb.MessageElement("0",
1049 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1053 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup") + ",CN=Partitions,%s" % samdb.get_config_basedn())
1054 m["nTMixedDomain"] = ldb.MessageElement("0",
1055 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1058 except ldb.LdbError, (enum, emsg):
1059 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1062 # Directly on the base DN
1064 m.dn = ldb.Dn(samdb, domain_dn)
1065 m["msDS-Behavior-Version"]= ldb.MessageElement(
1066 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1067 "msDS-Behavior-Version")
1071 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup")
1072 + ",CN=Partitions,%s" % samdb.get_config_basedn())
1073 m["msDS-Behavior-Version"]= ldb.MessageElement(
1074 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1075 "msDS-Behavior-Version")
1078 except ldb.LdbError, (enum, emsg):
1079 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1082 level_domain = new_level_domain
1083 msgs.append("Domain function level changed!")
1085 if forest_level is not None:
1086 if forest_level == "2003":
1087 new_level_forest = DS_DOMAIN_FUNCTION_2003
1088 elif forest_level == "2008":
1089 new_level_forest = DS_DOMAIN_FUNCTION_2008
1090 elif forest_level == "2008_R2":
1091 new_level_forest = DS_DOMAIN_FUNCTION_2008_R2
1092 if new_level_forest <= level_forest:
1093 raise CommandError("Forest function level can't be smaller than or equal to the actual one!")
1094 if new_level_forest > level_domain:
1095 raise CommandError("Forest function level can't be higher than the domain function level(s). Please raise it/them first!")
1097 m.dn = ldb.Dn(samdb, "CN=Partitions,%s" % samdb.get_config_basedn())
1098 m["msDS-Behavior-Version"]= ldb.MessageElement(
1099 str(new_level_forest), ldb.FLAG_MOD_REPLACE,
1100 "msDS-Behavior-Version")
1102 msgs.append("Forest function level changed!")
1103 msgs.append("All changes applied successfully!")
1104 self.message("\n".join(msgs))
1106 raise CommandError("invalid argument: '%s' (choose from 'show', 'raise')" % subcommand)
1109 class cmd_domain_passwordsettings(Command):
1110 """Set password settings.
1112 Password complexity, password lockout policy, history length,
1113 minimum password length, the minimum and maximum password age) on
1114 a Samba AD DC server.
1116 Use against a Windows DC is possible, but group policy will override it.
1119 synopsis = "%prog (show|set <options>) [options]"
1121 takes_optiongroups = {
1122 "sambaopts": options.SambaOptions,
1123 "versionopts": options.VersionOptions,
1124 "credopts": options.CredentialsOptions,
1128 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1129 metavar="URL", dest="H"),
1130 Option("--quiet", help="Be quiet", action="store_true"),
1131 Option("--complexity", type="choice", choices=["on","off","default"],
1132 help="The password complexity (on | off | default). Default is 'on'"),
1133 Option("--store-plaintext", type="choice", choices=["on","off","default"],
1134 help="Store plaintext passwords where account have 'store passwords with reversible encryption' set (on | off | default). Default is 'off'"),
1135 Option("--history-length",
1136 help="The password history length (<integer> | default). Default is 24.", type=str),
1137 Option("--min-pwd-length",
1138 help="The minimum password length (<integer> | default). Default is 7.", type=str),
1139 Option("--min-pwd-age",
1140 help="The minimum password age (<integer in days> | default). Default is 1.", type=str),
1141 Option("--max-pwd-age",
1142 help="The maximum password age (<integer in days> | default). Default is 43.", type=str),
1143 Option("--account-lockout-duration",
1144 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),
1145 Option("--account-lockout-threshold",
1146 help="The number of bad password attempts allowed before locking out the account (<integer> | default). Default is 0 (never lock out).", type=str),
1147 Option("--reset-account-lockout-after",
1148 help="After this time is elapsed, the recorded number of attempts restarts from zero (<integer> | default). Default is 30.", type=str),
1151 takes_args = ["subcommand"]
1153 def run(self, subcommand, H=None, min_pwd_age=None, max_pwd_age=None,
1154 quiet=False, complexity=None, store_plaintext=None, history_length=None,
1155 min_pwd_length=None, account_lockout_duration=None, account_lockout_threshold=None,
1156 reset_account_lockout_after=None, credopts=None, sambaopts=None,
1158 lp = sambaopts.get_loadparm()
1159 creds = credopts.get_credentials(lp)
1161 samdb = SamDB(url=H, session_info=system_session(),
1162 credentials=creds, lp=lp)
1164 domain_dn = samdb.domain_dn()
1165 res = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1166 attrs=["pwdProperties", "pwdHistoryLength", "minPwdLength",
1167 "minPwdAge", "maxPwdAge", "lockoutDuration", "lockoutThreshold",
1168 "lockOutObservationWindow"])
1169 assert(len(res) == 1)
1171 pwd_props = int(res[0]["pwdProperties"][0])
1172 pwd_hist_len = int(res[0]["pwdHistoryLength"][0])
1173 cur_min_pwd_len = int(res[0]["minPwdLength"][0])
1175 cur_min_pwd_age = int(abs(int(res[0]["minPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1176 if int(res[0]["maxPwdAge"][0]) == -0x8000000000000000:
1179 cur_max_pwd_age = int(abs(int(res[0]["maxPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1180 cur_account_lockout_threshold = int(res[0]["lockoutThreshold"][0])
1182 if int(res[0]["lockoutDuration"][0]) == -0x8000000000000000:
1183 cur_account_lockout_duration = 0
1185 cur_account_lockout_duration = abs(int(res[0]["lockoutDuration"][0])) / (1e7 * 60)
1186 cur_reset_account_lockout_after = abs(int(res[0]["lockOutObservationWindow"][0])) / (1e7 * 60)
1187 except Exception, e:
1188 raise CommandError("Could not retrieve password properties!", e)
1190 if subcommand == "show":
1191 self.message("Password informations for domain '%s'" % domain_dn)
1193 if pwd_props & DOMAIN_PASSWORD_COMPLEX != 0:
1194 self.message("Password complexity: on")
1196 self.message("Password complexity: off")
1197 if pwd_props & DOMAIN_PASSWORD_STORE_CLEARTEXT != 0:
1198 self.message("Store plaintext passwords: on")
1200 self.message("Store plaintext passwords: off")
1201 self.message("Password history length: %d" % pwd_hist_len)
1202 self.message("Minimum password length: %d" % cur_min_pwd_len)
1203 self.message("Minimum password age (days): %d" % cur_min_pwd_age)
1204 self.message("Maximum password age (days): %d" % cur_max_pwd_age)
1205 self.message("Account lockout duration (mins): %d" % cur_account_lockout_duration)
1206 self.message("Account lockout threshold (attempts): %d" % cur_account_lockout_threshold)
1207 self.message("Reset account lockout after (mins): %d" % cur_reset_account_lockout_after)
1208 elif subcommand == "set":
1211 m.dn = ldb.Dn(samdb, domain_dn)
1213 if complexity is not None:
1214 if complexity == "on" or complexity == "default":
1215 pwd_props = pwd_props | DOMAIN_PASSWORD_COMPLEX
1216 msgs.append("Password complexity activated!")
1217 elif complexity == "off":
1218 pwd_props = pwd_props & (~DOMAIN_PASSWORD_COMPLEX)
1219 msgs.append("Password complexity deactivated!")
1221 if store_plaintext is not None:
1222 if store_plaintext == "on" or store_plaintext == "default":
1223 pwd_props = pwd_props | DOMAIN_PASSWORD_STORE_CLEARTEXT
1224 msgs.append("Plaintext password storage for changed passwords activated!")
1225 elif store_plaintext == "off":
1226 pwd_props = pwd_props & (~DOMAIN_PASSWORD_STORE_CLEARTEXT)
1227 msgs.append("Plaintext password storage for changed passwords deactivated!")
1229 if complexity is not None or store_plaintext is not None:
1230 m["pwdProperties"] = ldb.MessageElement(str(pwd_props),
1231 ldb.FLAG_MOD_REPLACE, "pwdProperties")
1233 if history_length is not None:
1234 if history_length == "default":
1237 pwd_hist_len = int(history_length)
1239 if pwd_hist_len < 0 or pwd_hist_len > 24:
1240 raise CommandError("Password history length must be in the range of 0 to 24!")
1242 m["pwdHistoryLength"] = ldb.MessageElement(str(pwd_hist_len),
1243 ldb.FLAG_MOD_REPLACE, "pwdHistoryLength")
1244 msgs.append("Password history length changed!")
1246 if min_pwd_length is not None:
1247 if min_pwd_length == "default":
1250 min_pwd_len = int(min_pwd_length)
1252 if min_pwd_len < 0 or min_pwd_len > 14:
1253 raise CommandError("Minimum password length must be in the range of 0 to 14!")
1255 m["minPwdLength"] = ldb.MessageElement(str(min_pwd_len),
1256 ldb.FLAG_MOD_REPLACE, "minPwdLength")
1257 msgs.append("Minimum password length changed!")
1259 if min_pwd_age is not None:
1260 if min_pwd_age == "default":
1263 min_pwd_age = int(min_pwd_age)
1265 if min_pwd_age < 0 or min_pwd_age > 998:
1266 raise CommandError("Minimum password age must be in the range of 0 to 998!")
1269 min_pwd_age_ticks = -int(min_pwd_age * (24 * 60 * 60 * 1e7))
1271 m["minPwdAge"] = ldb.MessageElement(str(min_pwd_age_ticks),
1272 ldb.FLAG_MOD_REPLACE, "minPwdAge")
1273 msgs.append("Minimum password age changed!")
1275 if max_pwd_age is not None:
1276 if max_pwd_age == "default":
1279 max_pwd_age = int(max_pwd_age)
1281 if max_pwd_age < 0 or max_pwd_age > 999:
1282 raise CommandError("Maximum password age must be in the range of 0 to 999!")
1285 if max_pwd_age == 0:
1286 max_pwd_age_ticks = -0x8000000000000000
1288 max_pwd_age_ticks = -int(max_pwd_age * (24 * 60 * 60 * 1e7))
1290 m["maxPwdAge"] = ldb.MessageElement(str(max_pwd_age_ticks),
1291 ldb.FLAG_MOD_REPLACE, "maxPwdAge")
1292 msgs.append("Maximum password age changed!")
1294 if account_lockout_duration is not None:
1295 if account_lockout_duration == "default":
1296 account_lockout_duration = 30
1298 account_lockout_duration = int(account_lockout_duration)
1300 if account_lockout_duration < 0 or account_lockout_duration > 99999:
1301 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1304 if account_lockout_duration == 0:
1305 account_lockout_duration_ticks = -0x8000000000000000
1307 account_lockout_duration_ticks = -int(account_lockout_duration * (60 * 1e7))
1309 m["lockoutDuration"] = ldb.MessageElement(str(account_lockout_duration_ticks),
1310 ldb.FLAG_MOD_REPLACE, "lockoutDuration")
1311 msgs.append("Account lockout duration changed!")
1313 if account_lockout_threshold is not None:
1314 if account_lockout_threshold == "default":
1315 account_lockout_threshold = 0
1317 account_lockout_threshold = int(account_lockout_threshold)
1319 m["lockoutThreshold"] = ldb.MessageElement(str(account_lockout_threshold),
1320 ldb.FLAG_MOD_REPLACE, "lockoutThreshold")
1321 msgs.append("Account lockout threshold changed!")
1323 if reset_account_lockout_after is not None:
1324 if reset_account_lockout_after == "default":
1325 reset_account_lockout_after = 30
1327 reset_account_lockout_after = int(reset_account_lockout_after)
1329 if reset_account_lockout_after < 0 or reset_account_lockout_after > 99999:
1330 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1333 if reset_account_lockout_after == 0:
1334 reset_account_lockout_after_ticks = -0x8000000000000000
1336 reset_account_lockout_after_ticks = -int(reset_account_lockout_after * (60 * 1e7))
1338 m["lockOutObservationWindow"] = ldb.MessageElement(str(reset_account_lockout_after_ticks),
1339 ldb.FLAG_MOD_REPLACE, "lockOutObservationWindow")
1340 msgs.append("Duration to reset account lockout after changed!")
1342 if max_pwd_age > 0 and min_pwd_age >= max_pwd_age:
1343 raise CommandError("Maximum password age (%d) must be greater than minimum password age (%d)!" % (max_pwd_age, min_pwd_age))
1346 raise CommandError("You must specify at least one option to set. Try --help")
1348 msgs.append("All changes applied successfully!")
1349 self.message("\n".join(msgs))
1351 raise CommandError("Wrong argument '%s'!" % subcommand)
1354 class cmd_domain_classicupgrade(Command):
1355 """Upgrade from Samba classic (NT4-like) database to Samba AD DC database.
1357 Specify either a directory with all Samba classic DC databases and state files (with --dbdir) or
1358 the testparm utility from your classic installation (with --testparm).
1361 synopsis = "%prog [options] <classic_smb_conf>"
1363 takes_optiongroups = {
1364 "sambaopts": options.SambaOptions,
1365 "versionopts": options.VersionOptions
1369 Option("--dbdir", type="string", metavar="DIR",
1370 help="Path to samba classic DC database directory"),
1371 Option("--testparm", type="string", metavar="PATH",
1372 help="Path to samba classic DC testparm utility from the previous installation. This allows the default paths of the previous installation to be followed"),
1373 Option("--targetdir", type="string", metavar="DIR",
1374 help="Path prefix where the new Samba 4.0 AD domain should be initialised"),
1375 Option("--quiet", help="Be quiet", action="store_true"),
1376 Option("--verbose", help="Be verbose", action="store_true"),
1377 Option("--use-xattrs", type="choice", choices=["yes","no","auto"], metavar="[yes|no|auto]",
1378 help="Define if we should use the native fs capabilities or a tdb file for storing attributes likes ntacl, auto tries to make an inteligent guess based on the user rights and system capabilities", default="auto"),
1379 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
1380 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
1381 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
1382 "BIND9_FLATFILE uses bind9 text database to store zone information, "
1383 "BIND9_DLZ uses samba4 AD to store zone information, "
1384 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
1385 default="SAMBA_INTERNAL")
1389 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
1390 action="store_true")
1392 if samba.is_ntvfs_fileserver_built():
1393 takes_options.extend(ntvfs_options)
1395 takes_args = ["smbconf"]
1397 def run(self, smbconf=None, targetdir=None, dbdir=None, testparm=None,
1398 quiet=False, verbose=False, use_xattrs=None, sambaopts=None, versionopts=None,
1399 dns_backend=None, use_ntvfs=False):
1401 if not os.path.exists(smbconf):
1402 raise CommandError("File %s does not exist" % smbconf)
1404 if testparm and not os.path.exists(testparm):
1405 raise CommandError("Testparm utility %s does not exist" % testparm)
1407 if dbdir and not os.path.exists(dbdir):
1408 raise CommandError("Directory %s does not exist" % dbdir)
1410 if not dbdir and not testparm:
1411 raise CommandError("Please specify either dbdir or testparm")
1413 logger = self.get_logger()
1415 logger.setLevel(logging.DEBUG)
1417 logger.setLevel(logging.WARNING)
1419 logger.setLevel(logging.INFO)
1421 if dbdir and testparm:
1422 logger.warning("both dbdir and testparm specified, ignoring dbdir.")
1425 lp = sambaopts.get_loadparm()
1427 s3conf = s3param.get_context()
1430 s3conf.set("realm", sambaopts.realm)
1432 if targetdir is not None:
1433 if not os.path.isdir(targetdir):
1437 if use_xattrs == "yes":
1439 elif use_xattrs == "auto" and not s3conf.get("posix:eadb"):
1441 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
1443 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
1446 samba.ntacls.setntacl(lp, tmpfile.name,
1447 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
1450 # FIXME: Don't catch all exceptions here
1451 logger.info("You are not root or your system do not support xattr, using tdb backend for attributes. "
1452 "If you intend to use this provision in production, rerun the script as root on a system supporting xattrs.")
1456 # Set correct default values from dbdir or testparm
1459 paths["state directory"] = dbdir
1460 paths["private dir"] = dbdir
1461 paths["lock directory"] = dbdir
1462 paths["smb passwd file"] = dbdir + "/smbpasswd"
1464 paths["state directory"] = get_testparm_var(testparm, smbconf, "state directory")
1465 paths["private dir"] = get_testparm_var(testparm, smbconf, "private dir")
1466 paths["smb passwd file"] = get_testparm_var(testparm, smbconf, "smb passwd file")
1467 paths["lock directory"] = get_testparm_var(testparm, smbconf, "lock directory")
1468 # "testparm" from Samba 3 < 3.4.x is not aware of the parameter
1469 # "state directory", instead make use of "lock directory"
1470 if len(paths["state directory"]) == 0:
1471 paths["state directory"] = paths["lock directory"]
1474 s3conf.set(p, paths[p])
1476 # load smb.conf parameters
1477 logger.info("Reading smb.conf")
1478 s3conf.load(smbconf)
1479 samba3 = Samba3(smbconf, s3conf)
1481 logger.info("Provisioning")
1482 upgrade_from_samba3(samba3, logger, targetdir, session_info=system_session(),
1483 useeadb=eadb, dns_backend=dns_backend, use_ntvfs=use_ntvfs)
1486 class cmd_domain_samba3upgrade(cmd_domain_classicupgrade):
1487 __doc__ = cmd_domain_classicupgrade.__doc__
1489 # This command is present for backwards compatibility only,
1490 # and should not be shown.
1494 class LocalDCCredentialsOptions(options.CredentialsOptions):
1495 def __init__(self, parser):
1496 options.CredentialsOptions.__init__(self, parser, special_name="local-dc")
1498 class DomainTrustCommand(Command):
1499 """List domain trusts."""
1502 Command.__init__(self)
1503 self.local_lp = None
1505 self.local_server = None
1506 self.local_binding_string = None
1507 self.local_creds = None
1509 self.remote_server = None
1510 self.remote_binding_string = None
1511 self.remote_creds = None
1513 WERR_OK = 0x00000000
1514 WERR_INVALID_FUNCTION = 0x00000001
1515 WERR_NERR_ACFNOTLOADED = 0x000008B3
1517 NT_STATUS_NOT_FOUND = 0xC0000225
1518 NT_STATUS_OBJECT_NAME_NOT_FOUND = 0xC0000034
1519 NT_STATUS_INVALID_PARAMETER = 0xC000000D
1520 NT_STATUS_INVALID_INFO_CLASS = 0xC0000003
1521 NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE = 0xC002002E
1523 def _uint32(self, v):
1524 return ctypes.c_uint32(v).value
1526 def check_runtime_error(self, runtime, val):
1530 err32 = self._uint32(runtime[0])
1536 class LocalRuntimeError(CommandError):
1537 def __init__(exception_self, self, runtime, message):
1538 err32 = self._uint32(runtime[0])
1540 msg = "LOCAL_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1541 self.local_server, message, err32, errstr)
1542 CommandError.__init__(exception_self, msg)
1544 class RemoteRuntimeError(CommandError):
1545 def __init__(exception_self, self, runtime, message):
1546 err32 = self._uint32(runtime[0])
1548 msg = "REMOTE_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1549 self.remote_server, message, err32, errstr)
1550 CommandError.__init__(exception_self, msg)
1552 class LocalLdbError(CommandError):
1553 def __init__(exception_self, self, ldb_error, message):
1554 errval = ldb_error[0]
1555 errstr = ldb_error[1]
1556 msg = "LOCAL_DC[%s]: %s - ERROR(%d) - %s" % (
1557 self.local_server, message, errval, errstr)
1558 CommandError.__init__(exception_self, msg)
1560 def setup_local_server(self, sambaopts, localdcopts):
1561 if self.local_server is not None:
1562 return self.local_server
1564 lp = sambaopts.get_loadparm()
1566 local_server = localdcopts.ipaddress
1567 if local_server is None:
1568 server_role = lp.server_role()
1569 if server_role != "ROLE_ACTIVE_DIRECTORY_DC":
1570 raise CommandError("Invalid server_role %s" % (server_role))
1571 local_server = lp.get('netbios name')
1572 local_transport = "ncalrpc"
1573 local_binding_options = ""
1574 local_binding_options += ",auth_type=ncalrpc_as_system"
1575 local_ldap_url = None
1578 local_transport = "ncacn_np"
1579 local_binding_options = ""
1580 local_ldap_url = "ldap://%s" % local_server
1581 local_creds = localdcopts.get_credentials(lp)
1585 self.local_server = local_server
1586 self.local_binding_string = "%s:%s[%s]" % (local_transport, local_server, local_binding_options)
1587 self.local_ldap_url = local_ldap_url
1588 self.local_creds = local_creds
1589 return self.local_server
1591 def new_local_lsa_connection(self):
1592 return lsa.lsarpc(self.local_binding_string, self.local_lp, self.local_creds)
1594 def new_local_netlogon_connection(self):
1595 return netlogon.netlogon(self.local_binding_string, self.local_lp, self.local_creds)
1597 def new_local_ldap_connection(self):
1598 return SamDB(url=self.local_ldap_url,
1599 session_info=system_session(),
1600 credentials=self.local_creds,
1603 def setup_remote_server(self, credopts, domain,
1605 require_writable=True):
1608 assert require_writable
1610 if self.remote_server is not None:
1611 return self.remote_server
1613 self.remote_server = "__unknown__remote_server__.%s" % domain
1614 assert self.local_server is not None
1616 remote_creds = credopts.get_credentials(self.local_lp)
1617 remote_server = credopts.ipaddress
1618 remote_binding_options = ""
1620 # TODO: we should also support NT4 domains
1621 # we could use local_netlogon.netr_DsRGetDCNameEx2() with the remote domain name
1622 # and delegate NBT or CLDAP to the local netlogon server
1624 remote_net = Net(remote_creds, self.local_lp, server=remote_server)
1625 remote_flags = nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS
1626 if require_writable:
1627 remote_flags |= nbt.NBT_SERVER_WRITABLE
1629 remote_flags |= nbt.NBT_SERVER_PDC
1630 remote_info = remote_net.finddc(flags=remote_flags, domain=domain, address=remote_server)
1632 raise CommandError("Failed to find a writeable DC for domain '%s'" % domain)
1634 nbt.NBT_SERVER_PDC: "PDC",
1635 nbt.NBT_SERVER_GC: "GC",
1636 nbt.NBT_SERVER_LDAP: "LDAP",
1637 nbt.NBT_SERVER_DS: "DS",
1638 nbt.NBT_SERVER_KDC: "KDC",
1639 nbt.NBT_SERVER_TIMESERV: "TIMESERV",
1640 nbt.NBT_SERVER_CLOSEST: "CLOSEST",
1641 nbt.NBT_SERVER_WRITABLE: "WRITABLE",
1642 nbt.NBT_SERVER_GOOD_TIMESERV: "GOOD_TIMESERV",
1643 nbt.NBT_SERVER_NDNC: "NDNC",
1644 nbt.NBT_SERVER_SELECT_SECRET_DOMAIN_6: "SELECT_SECRET_DOMAIN_6",
1645 nbt.NBT_SERVER_FULL_SECRET_DOMAIN_6: "FULL_SECRET_DOMAIN_6",
1646 nbt.NBT_SERVER_ADS_WEB_SERVICE: "ADS_WEB_SERVICE",
1647 nbt.NBT_SERVER_DS_8: "DS_8",
1648 nbt.NBT_SERVER_HAS_DNS_NAME: "HAS_DNS_NAME",
1649 nbt.NBT_SERVER_IS_DEFAULT_NC: "IS_DEFAULT_NC",
1650 nbt.NBT_SERVER_FOREST_ROOT: "FOREST_ROOT",
1652 server_type_string = self.generic_bitmap_to_string(flag_map,
1653 remote_info.server_type, names_only=True)
1654 self.outf.write("RemoteDC Netbios[%s] DNS[%s] ServerType[%s]\n" % (
1655 remote_info.pdc_name,
1656 remote_info.pdc_dns_name,
1657 server_type_string))
1659 self.remote_server = remote_info.pdc_dns_name
1660 self.remote_binding_string="ncacn_np:%s[%s]" % (self.remote_server, remote_binding_options)
1661 self.remote_creds = remote_creds
1662 return self.remote_server
1664 def new_remote_lsa_connection(self):
1665 return lsa.lsarpc(self.remote_binding_string, self.local_lp, self.remote_creds)
1667 def new_remote_netlogon_connection(self):
1668 return netlogon.netlogon(self.remote_binding_string, self.local_lp, self.remote_creds)
1670 def get_lsa_info(self, conn, policy_access):
1671 objectAttr = lsa.ObjectAttribute()
1672 objectAttr.sec_qos = lsa.QosInfo()
1674 policy = conn.OpenPolicy2(''.decode('utf-8'),
1675 objectAttr, policy_access)
1677 info = conn.QueryInfoPolicy2(policy, lsa.LSA_POLICY_INFO_DNS)
1679 return (policy, info)
1681 def get_netlogon_dc_info(self, conn, server):
1682 info = conn.netr_DsRGetDCNameEx2(server,
1683 None, 0, None, None, None,
1684 netlogon.DS_RETURN_DNS_NAME)
1687 def netr_DomainTrust_to_name(self, t):
1688 if t.trust_type == lsa.LSA_TRUST_TYPE_DOWNLEVEL:
1689 return t.netbios_name
1693 def netr_DomainTrust_to_type(self, a, t):
1695 primary_parent = None
1697 if _t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
1699 if not _t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1700 primary_parent = a[_t.parent_index]
1703 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1704 if t is primary_parent:
1707 if t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1710 parent = a[t.parent_index]
1711 if parent is primary:
1716 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1721 def netr_DomainTrust_to_transitive(self, t):
1722 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1725 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE:
1728 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1733 def netr_DomainTrust_to_direction(self, t):
1734 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND and \
1735 t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1738 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND:
1741 if t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1746 def generic_enum_to_string(self, e_dict, v, names_only=False):
1750 v32 = self._uint32(v)
1751 w = "__unknown__%08X__" % v32
1753 r = "0x%x (%s)" % (v, w)
1756 def generic_bitmap_to_string(self, b_dict, v, names_only=False):
1761 for b in sorted(b_dict.keys()):
1768 c32 = self._uint32(c)
1769 s += ["__unknown_%08X__" % c32]
1774 r = "0x%x (%s)" % (v, w)
1777 def trustType_string(self, v):
1779 lsa.LSA_TRUST_TYPE_DOWNLEVEL : "DOWNLEVEL",
1780 lsa.LSA_TRUST_TYPE_UPLEVEL : "UPLEVEL",
1781 lsa.LSA_TRUST_TYPE_MIT : "MIT",
1782 lsa.LSA_TRUST_TYPE_DCE : "DCE",
1784 return self.generic_enum_to_string(types, v)
1786 def trustDirection_string(self, v):
1788 lsa.LSA_TRUST_DIRECTION_INBOUND |
1789 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "BOTH",
1790 lsa.LSA_TRUST_DIRECTION_INBOUND : "INBOUND",
1791 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "OUTBOUND",
1793 return self.generic_enum_to_string(directions, v)
1795 def trustAttributes_string(self, v):
1797 lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE : "NON_TRANSITIVE",
1798 lsa.LSA_TRUST_ATTRIBUTE_UPLEVEL_ONLY : "UPLEVEL_ONLY",
1799 lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN : "QUARANTINED_DOMAIN",
1800 lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE : "FOREST_TRANSITIVE",
1801 lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION : "CROSS_ORGANIZATION",
1802 lsa.LSA_TRUST_ATTRIBUTE_WITHIN_FOREST : "WITHIN_FOREST",
1803 lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL : "TREAT_AS_EXTERNAL",
1804 lsa.LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION : "USES_RC4_ENCRYPTION",
1806 return self.generic_bitmap_to_string(attributes, v)
1808 def kerb_EncTypes_string(self, v):
1810 security.KERB_ENCTYPE_DES_CBC_CRC : "DES_CBC_CRC",
1811 security.KERB_ENCTYPE_DES_CBC_MD5 : "DES_CBC_MD5",
1812 security.KERB_ENCTYPE_RC4_HMAC_MD5 : "RC4_HMAC_MD5",
1813 security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96 : "AES128_CTS_HMAC_SHA1_96",
1814 security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96 : "AES256_CTS_HMAC_SHA1_96",
1815 security.KERB_ENCTYPE_FAST_SUPPORTED : "FAST_SUPPORTED",
1816 security.KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED : "COMPOUND_IDENTITY_SUPPORTED",
1817 security.KERB_ENCTYPE_CLAIMS_SUPPORTED : "CLAIMS_SUPPORTED",
1818 security.KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED : "RESOURCE_SID_COMPRESSION_DISABLED",
1820 return self.generic_bitmap_to_string(enctypes, v)
1822 def entry_tln_status(self, e_flags, ):
1824 return "Status[Enabled]"
1827 lsa.LSA_TLN_DISABLED_NEW : "Disabled-New",
1828 lsa.LSA_TLN_DISABLED_ADMIN : "Disabled",
1829 lsa.LSA_TLN_DISABLED_CONFLICT : "Disabled-Conflicting",
1831 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
1833 def entry_dom_status(self, e_flags):
1835 return "Status[Enabled]"
1838 lsa.LSA_SID_DISABLED_ADMIN : "Disabled-SID",
1839 lsa.LSA_SID_DISABLED_CONFLICT : "Disabled-SID-Conflicting",
1840 lsa.LSA_NB_DISABLED_ADMIN : "Disabled-NB",
1841 lsa.LSA_NB_DISABLED_CONFLICT : "Disabled-NB-Conflicting",
1843 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
1845 def write_forest_trust_info(self, fti, tln=None, collisions=None):
1847 tln_string = " TDO[%s]" % tln
1851 self.outf.write("Namespaces[%d]%s:\n" % (
1852 len(fti.entries), tln_string))
1854 for i in xrange(0, len(fti.entries)):
1858 collision_string = ""
1860 if collisions is not None:
1861 for c in collisions.entries:
1865 collision_string = " Collision[%s]" % (c.name.string)
1867 d = e.forest_trust_data
1868 if e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
1869 self.outf.write("TLN: %-32s DNS[*.%s]%s\n" % (
1870 self.entry_tln_status(flags),
1871 d.string, collision_string))
1872 elif e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
1873 self.outf.write("TLN_EX: %-29s DNS[*.%s]\n" % (
1875 elif e.type == lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
1876 self.outf.write("DOM: %-32s DNS[%s] Netbios[%s] SID[%s]%s\n" % (
1877 self.entry_dom_status(flags),
1878 d.dns_domain_name.string,
1879 d.netbios_domain_name.string,
1880 d.domain_sid, collision_string))
1883 class cmd_domain_trust_list(DomainTrustCommand):
1884 """List domain trusts."""
1886 synopsis = "%prog [options]"
1888 takes_optiongroups = {
1889 "sambaopts": options.SambaOptions,
1890 "versionopts": options.VersionOptions,
1891 "localdcopts": LocalDCCredentialsOptions,
1897 def run(self, sambaopts=None, versionopts=None, localdcopts=None):
1899 local_server = self.setup_local_server(sambaopts, localdcopts)
1901 local_netlogon = self.new_local_netlogon_connection()
1902 except RuntimeError as error:
1903 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
1906 local_netlogon_trusts = local_netlogon.netr_DsrEnumerateDomainTrusts(local_server,
1907 netlogon.NETR_TRUST_FLAG_IN_FOREST |
1908 netlogon.NETR_TRUST_FLAG_OUTBOUND |
1909 netlogon.NETR_TRUST_FLAG_INBOUND)
1910 except RuntimeError as error:
1911 if self.check_runtime_error(error, self.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
1912 # TODO: we could implement a fallback to lsa.EnumTrustDom()
1913 raise CommandError("LOCAL_DC[%s]: netr_DsrEnumerateDomainTrusts not supported." % (
1915 raise self.LocalRuntimeError(self, error, "netr_DsrEnumerateDomainTrusts failed")
1917 a = local_netlogon_trusts.array
1919 if t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
1921 self.outf.write("%-14s %-15s %-19s %s\n" % (
1922 "Type[%s]" % self.netr_DomainTrust_to_type(a, t),
1923 "Transitive[%s]" % self.netr_DomainTrust_to_transitive(t),
1924 "Direction[%s]" % self.netr_DomainTrust_to_direction(t),
1925 "Name[%s]" % self.netr_DomainTrust_to_name(t)))
1928 class cmd_domain_trust_show(DomainTrustCommand):
1929 """Show trusted domain details."""
1931 synopsis = "%prog NAME [options]"
1933 takes_optiongroups = {
1934 "sambaopts": options.SambaOptions,
1935 "versionopts": options.VersionOptions,
1936 "localdcopts": LocalDCCredentialsOptions,
1942 takes_args = ["domain"]
1944 def run(self, domain, sambaopts=None, versionopts=None, localdcopts=None):
1946 local_server = self.setup_local_server(sambaopts, localdcopts)
1948 local_lsa = self.new_local_lsa_connection()
1949 except RuntimeError as error:
1950 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
1953 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
1954 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
1955 except RuntimeError as error:
1956 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
1958 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
1959 local_lsa_info.name.string,
1960 local_lsa_info.dns_domain.string,
1961 local_lsa_info.sid))
1963 lsaString = lsa.String()
1964 lsaString.string = domain
1966 local_tdo_full = local_lsa.QueryTrustedDomainInfoByName(local_policy,
1967 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
1968 local_tdo_info = local_tdo_full.info_ex
1969 local_tdo_posix = local_tdo_full.posix_offset
1970 except RuntimeError as error:
1971 if self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
1972 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
1974 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(FULL_INFO) failed")
1977 local_tdo_enctypes = local_lsa.QueryTrustedDomainInfoByName(local_policy,
1978 lsaString, lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES)
1979 except RuntimeError as error:
1980 if self.check_runtime_error(error, self.NT_STATUS_INVALID_PARAMETER):
1982 if self.check_runtime_error(error, self.NT_STATUS_INVALID_INFO_CLASS):
1985 if error is not None:
1986 raise self.LocalRuntimeError(self, error,
1987 "QueryTrustedDomainInfoByName(SUPPORTED_ENCRYPTION_TYPES) failed")
1989 local_tdo_enctypes = lsa.TrustDomainInfoSupportedEncTypes()
1990 local_tdo_enctypes.enc_types = 0
1993 local_tdo_forest = None
1994 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1995 local_tdo_forest = local_lsa.lsaRQueryForestTrustInformation(local_policy,
1996 lsaString, lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
1997 except RuntimeError as error:
1998 if self.check_runtime_error(error, self.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
2000 if self.check_runtime_error(error, self.NT_STATUS_NOT_FOUND):
2002 if error is not None:
2003 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation failed")
2005 local_tdo_forest = lsa.ForestTrustInformation()
2006 local_tdo_forest.count = 0
2007 local_tdo_forest.entries = []
2009 self.outf.write("TrusteDomain:\n\n");
2010 self.outf.write("NetbiosName: %s\n" % local_tdo_info.netbios_name.string)
2011 if local_tdo_info.netbios_name.string != local_tdo_info.domain_name.string:
2012 self.outf.write("DnsName: %s\n" % local_tdo_info.domain_name.string)
2013 self.outf.write("SID: %s\n" % local_tdo_info.sid)
2014 self.outf.write("Type: %s\n" % self.trustType_string(local_tdo_info.trust_type))
2015 self.outf.write("Direction: %s\n" % self.trustDirection_string(local_tdo_info.trust_direction))
2016 self.outf.write("Attributes: %s\n" % self.trustAttributes_string(local_tdo_info.trust_attributes))
2017 posix_offset_u32 = ctypes.c_uint32(local_tdo_posix.posix_offset).value
2018 posix_offset_i32 = ctypes.c_int32(local_tdo_posix.posix_offset).value
2019 self.outf.write("PosixOffset: 0x%08X (%d)\n" % (posix_offset_u32, posix_offset_i32))
2020 self.outf.write("kerb_EncTypes: %s\n" % self.kerb_EncTypes_string(local_tdo_enctypes.enc_types))
2022 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2023 self.write_forest_trust_info(local_tdo_forest,
2024 tln=local_tdo_info.domain_name.string)
2028 class cmd_domain_trust_create(DomainTrustCommand):
2029 """Create a domain or forest trust."""
2031 synopsis = "%prog DOMAIN [options]"
2033 takes_optiongroups = {
2034 "sambaopts": options.SambaOptions,
2035 "versionopts": options.VersionOptions,
2036 "credopts": options.CredentialsOptions,
2037 "localdcopts": LocalDCCredentialsOptions,
2041 Option("--type", type="choice", metavar="TYPE",
2042 choices=["external", "forest"],
2043 help="The type of the trust: 'external' or 'forest'.",
2045 default="external"),
2046 Option("--direction", type="choice", metavar="DIRECTION",
2047 choices=["incoming", "outgoing", "both"],
2048 help="The trust direction: 'incoming', 'outgoing' or 'both'.",
2049 dest='trust_direction',
2051 Option("--create-location", type="choice", metavar="LOCATION",
2052 choices=["local", "both"],
2053 help="Where to create the trusted domain object: 'local' or 'both'.",
2054 dest='create_location',
2056 Option("--cross-organisation", action="store_true",
2057 help="The related domains does not belong to the same organisation.",
2058 dest='cross_organisation',
2060 Option("--quarantined", type="choice", metavar="yes|no",
2061 choices=["yes", "no", None],
2062 help="Special SID filtering rules are applied to the trust. "
2063 "With --type=external the default is yes. "
2064 "With --type=forest the default is no.",
2065 dest='quarantined_arg',
2067 Option("--not-transitive", action="store_true",
2068 help="The forest trust is not transitive.",
2069 dest='not_transitive',
2071 Option("--treat-as-external", action="store_true",
2072 help="The treat the forest trust as external.",
2073 dest='treat_as_external',
2075 Option("--no-aes-keys", action="store_false",
2076 help="The trust uses aes kerberos keys.",
2077 dest='use_aes_keys',
2079 Option("--skip-validation", action="store_false",
2080 help="Skip validation of the trust.",
2085 takes_args = ["domain"]
2087 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2088 trust_type=None, trust_direction=None, create_location=None,
2089 cross_organisation=False, quarantined_arg=None,
2090 not_transitive=False, treat_as_external=False,
2091 use_aes_keys=False, validate=True):
2093 lsaString = lsa.String()
2096 if quarantined_arg is None:
2097 if trust_type == 'external':
2099 elif quarantined_arg == 'yes':
2102 if trust_type != 'forest':
2104 raise CommandError("--not-transitive requires --type=forest")
2105 if treat_as_external:
2106 raise CommandError("--treat-as-external requires --type=forest")
2110 enc_types = lsa.TrustDomainInfoSupportedEncTypes()
2111 enc_types.enc_types = security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96
2112 enc_types.enc_types |= security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96
2114 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2115 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2116 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2118 local_trust_info = lsa.TrustDomainInfoInfoEx()
2119 local_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2120 local_trust_info.trust_direction = 0
2121 if trust_direction == "both":
2122 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2123 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2124 elif trust_direction == "incoming":
2125 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2126 elif trust_direction == "outgoing":
2127 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2128 local_trust_info.trust_attributes = 0
2129 if cross_organisation:
2130 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2132 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2133 if trust_type == "forest":
2134 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2136 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2137 if treat_as_external:
2138 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2140 def get_password(name):
2143 if password is not None and password is not '':
2145 password = getpass("New %s Password: " % name)
2146 passwordverify = getpass("Retype %s Password: " % name)
2147 if not password == passwordverify:
2149 self.outf.write("Sorry, passwords do not match.\n")
2151 def string_to_array(string):
2152 blob = [0] * len(string)
2154 for i in range(len(string)):
2155 blob[i] = ord(string[i])
2159 incoming_secret = None
2160 outgoing_secret = None
2161 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2162 if create_location == "local":
2163 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2164 incoming_password = get_password("Incoming Trust")
2165 incoming_secret = string_to_array(incoming_password.encode('utf-16-le'))
2166 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2167 outgoing_password = get_password("Outgoing Trust")
2168 outgoing_secret = string_to_array(outgoing_password.encode('utf-16-le'))
2170 remote_trust_info = None
2172 # We use 240 random bytes.
2173 # Windows uses 28 or 240 random bytes. I guess it's
2174 # based on the trust type external vs. forest.
2176 # The initial trust password can be up to 512 bytes
2177 # while the versioned passwords used for periodic updates
2178 # can only be up to 498 bytes, as netr_ServerPasswordSet2()
2179 # needs to pass the NL_PASSWORD_VERSION structure within the
2180 # 512 bytes and a 2 bytes confounder is required.
2182 def random_trust_secret(length, use_aes_keys=True):
2183 secret = [0] * length
2185 pw1 = samba.generate_random_password(length/2, length/2)
2186 if not use_aes_keys:
2187 # With arcfour-hmac-md5 we have to use valid utf16
2188 # in order to generate the correct pre-auth key
2189 # based on a utf8 password.
2191 # We can remove this once our client libraries
2192 # support using the correct NTHASH.
2193 return string_to_array(pw1.encode('utf-16-le'))
2195 # We mix characters from generate_random_password
2196 # with random numbers from random.randint()
2197 for i in range(len(secret)):
2199 secret[i] = ord(pw1[i])
2201 secret[i] = random.randint(0, 255)
2205 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2206 incoming_secret = random_trust_secret(240, use_aes_keys=use_aes_keys)
2207 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2208 outgoing_secret = random_trust_secret(240, use_aes_keys=use_aes_keys)
2210 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2211 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2213 remote_trust_info = lsa.TrustDomainInfoInfoEx()
2214 remote_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2215 remote_trust_info.trust_direction = 0
2216 if trust_direction == "both":
2217 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2218 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2219 elif trust_direction == "incoming":
2220 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2221 elif trust_direction == "outgoing":
2222 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2223 remote_trust_info.trust_attributes = 0
2224 if cross_organisation:
2225 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2227 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2228 if trust_type == "forest":
2229 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2231 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2232 if treat_as_external:
2233 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2235 local_server = self.setup_local_server(sambaopts, localdcopts)
2237 local_lsa = self.new_local_lsa_connection()
2238 except RuntimeError as error:
2239 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2242 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2243 except RuntimeError as error:
2244 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2246 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2247 local_lsa_info.name.string,
2248 local_lsa_info.dns_domain.string,
2249 local_lsa_info.sid))
2252 remote_server = self.setup_remote_server(credopts, domain)
2253 except RuntimeError as error:
2254 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2257 remote_lsa = self.new_remote_lsa_connection()
2258 except RuntimeError as error:
2259 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2262 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2263 except RuntimeError as error:
2264 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2266 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2267 remote_lsa_info.name.string,
2268 remote_lsa_info.dns_domain.string,
2269 remote_lsa_info.sid))
2271 local_trust_info.domain_name.string = remote_lsa_info.dns_domain.string
2272 local_trust_info.netbios_name.string = remote_lsa_info.name.string
2273 local_trust_info.sid = remote_lsa_info.sid
2275 if remote_trust_info:
2276 remote_trust_info.domain_name.string = local_lsa_info.dns_domain.string
2277 remote_trust_info.netbios_name.string = local_lsa_info.name.string
2278 remote_trust_info.sid = local_lsa_info.sid
2281 lsaString.string = local_trust_info.domain_name.string
2282 local_old_netbios = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2283 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2284 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2285 except RuntimeError as error:
2286 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2287 raise self.LocalRuntimeError(self, error,
2288 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2292 lsaString.string = local_trust_info.netbios_name.string
2293 local_old_dns = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2294 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2295 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2296 except RuntimeError as error:
2297 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2298 raise self.LocalRuntimeError(self, error,
2299 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2302 if remote_trust_info:
2304 lsaString.string = remote_trust_info.domain_name.string
2305 remote_old_netbios = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2306 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2307 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2308 except RuntimeError as error:
2309 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2310 raise self.RemoteRuntimeError(self, error,
2311 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2315 lsaString.string = remote_trust_info.netbios_name.string
2316 remote_old_dns = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2317 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2318 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2319 except RuntimeError as error:
2320 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2321 raise self.RemoteRuntimeError(self, error,
2322 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2326 local_netlogon = self.new_local_netlogon_connection()
2327 except RuntimeError as error:
2328 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2331 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
2332 except RuntimeError as error:
2333 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
2335 if remote_trust_info:
2337 remote_netlogon = self.new_remote_netlogon_connection()
2338 except RuntimeError as error:
2339 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2342 remote_netlogon_info = self.get_netlogon_dc_info(remote_netlogon, remote_server)
2343 except RuntimeError as error:
2344 raise self.RemoteRuntimeError(self, error, "failed to get netlogon dc info")
2346 def arcfour_encrypt(key, data):
2347 from Crypto.Cipher import ARC4
2349 return c.encrypt(data)
2351 def generate_AuthInOutBlob(secret, update_time):
2353 blob = drsblobs.trustAuthInOutBlob()
2358 clear = drsblobs.AuthInfoClear()
2359 clear.size = len(secret)
2360 clear.password = secret
2362 info = drsblobs.AuthenticationInformation()
2363 info.LastUpdateTime = samba.unix2nttime(update_time)
2364 info.AuthType = lsa.TRUST_AUTH_TYPE_CLEAR
2365 info.AuthInfo = clear
2367 array = drsblobs.AuthenticationInformationArray()
2369 array.array = [info]
2371 blob = drsblobs.trustAuthInOutBlob()
2373 blob.current = array
2377 def generate_AuthInfoInternal(session_key, incoming=None, outgoing=None):
2378 confounder = [0] * 512
2379 for i in range(len(confounder)):
2380 confounder[i] = random.randint(0, 255)
2382 trustpass = drsblobs.trustDomainPasswords()
2384 trustpass.confounder = confounder
2385 trustpass.outgoing = outgoing
2386 trustpass.incoming = incoming
2388 trustpass_blob = ndr_pack(trustpass)
2390 encrypted_trustpass = arcfour_encrypt(session_key, trustpass_blob)
2392 auth_blob = lsa.DATA_BUF2()
2393 auth_blob.size = len(encrypted_trustpass)
2394 auth_blob.data = string_to_array(encrypted_trustpass)
2396 auth_info = lsa.TrustDomainInfoAuthInfoInternal()
2397 auth_info.auth_blob = auth_blob
2401 update_time = samba.current_unix_time()
2402 incoming_blob = generate_AuthInOutBlob(incoming_secret, update_time)
2403 outgoing_blob = generate_AuthInOutBlob(outgoing_secret, update_time)
2405 local_tdo_handle = None
2406 remote_tdo_handle = None
2408 local_auth_info = generate_AuthInfoInternal(local_lsa.session_key,
2409 incoming=incoming_blob,
2410 outgoing=outgoing_blob)
2411 if remote_trust_info:
2412 remote_auth_info = generate_AuthInfoInternal(remote_lsa.session_key,
2413 incoming=outgoing_blob,
2414 outgoing=incoming_blob)
2417 if remote_trust_info:
2418 self.outf.write("Creating remote TDO.\n")
2419 current_request = { "location": "remote", "name": "CreateTrustedDomainEx2"}
2420 remote_tdo_handle = remote_lsa.CreateTrustedDomainEx2(remote_policy,
2423 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2424 self.outf.write("Remote TDO created.\n")
2426 self.outf.write("Setting supported encryption types on remote TDO.\n")
2427 current_request = { "location": "remote", "name": "SetInformationTrustedDomain"}
2428 remote_lsa.SetInformationTrustedDomain(remote_tdo_handle,
2429 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2432 self.outf.write("Creating local TDO.\n")
2433 current_request = { "location": "local", "name": "CreateTrustedDomainEx2"}
2434 local_tdo_handle = local_lsa.CreateTrustedDomainEx2(local_policy,
2437 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2438 self.outf.write("Local TDO created\n")
2440 self.outf.write("Setting supported encryption types on local TDO.\n")
2441 current_request = { "location": "local", "name": "SetInformationTrustedDomain"}
2442 local_lsa.SetInformationTrustedDomain(local_tdo_handle,
2443 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2445 except RuntimeError as error:
2446 self.outf.write("Error: %s failed %sly - cleaning up\n" % (
2447 current_request['name'], current_request['location']))
2448 if remote_tdo_handle:
2449 self.outf.write("Deleting remote TDO.\n")
2450 remote_lsa.DeleteObject(remote_tdo_handle)
2451 remote_tdo_handle = None
2452 if local_tdo_handle:
2453 self.outf.write("Deleting local TDO.\n")
2454 local_lsa.DeleteObject(local_tdo_handle)
2455 local_tdo_handle = None
2456 if current_request['location'] is "remote":
2457 raise self.RemoteRuntimeError(self, error, "%s" % (
2458 current_request['name']))
2459 raise self.LocalRuntimeError(self, error, "%s" % (
2460 current_request['name']))
2463 if local_trust_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2464 self.outf.write("Setup local forest trust information...\n")
2466 # get all information about the remote trust
2467 # this triggers netr_GetForestTrustInformation to the remote domain
2468 # and lsaRSetForestTrustInformation() locally, but new top level
2469 # names are disabled by default.
2470 local_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
2471 remote_lsa_info.dns_domain.string,
2472 netlogon.DS_GFTI_UPDATE_TDO)
2473 except RuntimeError as error:
2474 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2477 # here we try to enable all top level names
2478 local_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
2479 remote_lsa_info.dns_domain,
2480 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2483 except RuntimeError as error:
2484 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2486 self.write_forest_trust_info(local_forest_info,
2487 tln=remote_lsa_info.dns_domain.string,
2488 collisions=local_forest_collision)
2490 if remote_trust_info:
2491 self.outf.write("Setup remote forest trust information...\n")
2493 # get all information about the local trust (from the perspective of the remote domain)
2494 # this triggers netr_GetForestTrustInformation to our domain.
2495 # and lsaRSetForestTrustInformation() remotely, but new top level
2496 # names are disabled by default.
2497 remote_forest_info = remote_netlogon.netr_DsRGetForestTrustInformation(remote_netlogon_info.dc_unc,
2498 local_lsa_info.dns_domain.string,
2499 netlogon.DS_GFTI_UPDATE_TDO)
2500 except RuntimeError as error:
2501 raise self.RemoteRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2504 # here we try to enable all top level names
2505 remote_forest_collision = remote_lsa.lsaRSetForestTrustInformation(remote_policy,
2506 local_lsa_info.dns_domain,
2507 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2510 except RuntimeError as error:
2511 raise self.RemoteRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2513 self.write_forest_trust_info(remote_forest_info,
2514 tln=local_lsa_info.dns_domain.string,
2515 collisions=remote_forest_collision)
2517 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2518 self.outf.write("Validating outgoing trust...\n")
2520 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_netlogon_info.dc_unc,
2521 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2523 remote_lsa_info.dns_domain.string)
2524 except RuntimeError as error:
2525 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2527 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2528 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2530 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2531 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2532 local_trust_verify.trusted_dc_name,
2533 local_trust_verify.tc_connection_status[1],
2534 local_trust_verify.pdc_connection_status[1])
2536 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2537 local_trust_verify.trusted_dc_name,
2538 local_trust_verify.tc_connection_status[1],
2539 local_trust_verify.pdc_connection_status[1])
2541 if local_trust_status != self.WERR_OK or local_conn_status != self.WERR_OK:
2542 raise CommandError(local_validation)
2544 self.outf.write("OK: %s\n" % local_validation)
2546 if remote_trust_info:
2547 if remote_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2548 self.outf.write("Validating incoming trust...\n")
2550 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_netlogon_info.dc_unc,
2551 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2553 local_lsa_info.dns_domain.string)
2554 except RuntimeError as error:
2555 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2557 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2558 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2560 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2561 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2562 remote_trust_verify.trusted_dc_name,
2563 remote_trust_verify.tc_connection_status[1],
2564 remote_trust_verify.pdc_connection_status[1])
2566 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2567 remote_trust_verify.trusted_dc_name,
2568 remote_trust_verify.tc_connection_status[1],
2569 remote_trust_verify.pdc_connection_status[1])
2571 if remote_trust_status != self.WERR_OK or remote_conn_status != self.WERR_OK:
2572 raise CommandError(remote_validation)
2574 self.outf.write("OK: %s\n" % remote_validation)
2576 if remote_tdo_handle is not None:
2578 remote_lsa.Close(remote_tdo_handle)
2579 except RuntimeError as error:
2581 remote_tdo_handle = None
2582 if local_tdo_handle is not None:
2584 local_lsa.Close(local_tdo_handle)
2585 except RuntimeError as error:
2587 local_tdo_handle = None
2589 self.outf.write("Success.\n")
2592 class cmd_domain_trust_delete(DomainTrustCommand):
2593 """Delete a domain trust."""
2595 synopsis = "%prog DOMAIN [options]"
2597 takes_optiongroups = {
2598 "sambaopts": options.SambaOptions,
2599 "versionopts": options.VersionOptions,
2600 "credopts": options.CredentialsOptions,
2601 "localdcopts": LocalDCCredentialsOptions,
2605 Option("--delete-location", type="choice", metavar="LOCATION",
2606 choices=["local", "both"],
2607 help="Where to delete the trusted domain object: 'local' or 'both'.",
2608 dest='delete_location',
2612 takes_args = ["domain"]
2614 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2615 delete_location=None):
2617 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2618 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2619 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2621 if delete_location == "local":
2622 remote_policy_access = None
2624 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2625 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2626 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2628 local_server = self.setup_local_server(sambaopts, localdcopts)
2630 local_lsa = self.new_local_lsa_connection()
2631 except RuntimeError as error:
2632 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2635 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2636 except RuntimeError as error:
2637 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2639 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2640 local_lsa_info.name.string,
2641 local_lsa_info.dns_domain.string,
2642 local_lsa_info.sid))
2644 local_tdo_info = None
2645 local_tdo_handle = None
2646 remote_tdo_info = None
2647 remote_tdo_handle = None
2649 lsaString = lsa.String()
2651 lsaString.string = domain
2652 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2653 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2654 except RuntimeError as error:
2655 if self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2656 raise CommandError("Failed to find trust for domain '%s'" % domain)
2657 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2660 if remote_policy_access is not None:
2662 remote_server = self.setup_remote_server(credopts, domain)
2663 except RuntimeError as error:
2664 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2667 remote_lsa = self.new_remote_lsa_connection()
2668 except RuntimeError as error:
2669 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2672 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2673 except RuntimeError as error:
2674 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2676 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2677 remote_lsa_info.name.string,
2678 remote_lsa_info.dns_domain.string,
2679 remote_lsa_info.sid))
2681 if remote_lsa_info.sid != local_tdo_info.sid or \
2682 remote_lsa_info.name.string != local_tdo_info.netbios_name.string or \
2683 remote_lsa_info.dns_domain.string != local_tdo_info.domain_name.string:
2684 raise CommandError("LocalTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2685 local_tdo_info.netbios_name.string,
2686 local_tdo_info.domain_name.string,
2687 local_tdo_info.sid))
2690 lsaString.string = local_lsa_info.dns_domain.string
2691 remote_tdo_info = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2692 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2693 except RuntimeError as error:
2694 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2695 raise self.RemoteRuntimeError(self, error, "QueryTrustedDomainInfoByName(%s)" % (
2699 if remote_tdo_info is not None:
2700 if local_lsa_info.sid != remote_tdo_info.sid or \
2701 local_lsa_info.name.string != remote_tdo_info.netbios_name.string or \
2702 local_lsa_info.dns_domain.string != remote_tdo_info.domain_name.string:
2703 raise CommandError("RemoteTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2704 remote_tdo_info.netbios_name.string,
2705 remote_tdo_info.domain_name.string,
2706 remote_tdo_info.sid))
2708 if local_tdo_info is not None:
2710 lsaString.string = local_tdo_info.domain_name.string
2711 local_tdo_handle = local_lsa.OpenTrustedDomainByName(local_policy,
2713 security.SEC_STD_DELETE)
2714 except RuntimeError as error:
2715 raise self.LocalRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2718 local_lsa.DeleteObject(local_tdo_handle)
2719 local_tdo_handle = None
2721 if remote_tdo_info is not None:
2723 lsaString.string = remote_tdo_info.domain_name.string
2724 remote_tdo_handle = remote_lsa.OpenTrustedDomainByName(remote_policy,
2726 security.SEC_STD_DELETE)
2727 except RuntimeError as error:
2728 raise self.RemoteRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2731 if remote_tdo_handle is not None:
2733 remote_lsa.DeleteObject(remote_tdo_handle)
2734 remote_tdo_handle = None
2735 self.outf.write("RemoteTDO deleted.\n")
2736 except RuntimeError as error:
2737 self.outf.write("%s\n" % self.RemoteRuntimeError(self, error, "DeleteObject() failed"))
2739 if local_tdo_handle is not None:
2741 local_lsa.DeleteObject(local_tdo_handle)
2742 local_tdo_handle = None
2743 self.outf.write("LocalTDO deleted.\n")
2744 except RuntimeError as error:
2745 self.outf.write("%s\n" % self.LocalRuntimeError(self, error, "DeleteObject() failed"))
2749 class cmd_domain_trust_validate(DomainTrustCommand):
2750 """Validate a domain trust."""
2752 synopsis = "%prog DOMAIN [options]"
2754 takes_optiongroups = {
2755 "sambaopts": options.SambaOptions,
2756 "versionopts": options.VersionOptions,
2757 "credopts": options.CredentialsOptions,
2758 "localdcopts": LocalDCCredentialsOptions,
2762 Option("--validate-location", type="choice", metavar="LOCATION",
2763 choices=["local", "both"],
2764 help="Where to validate the trusted domain object: 'local' or 'both'.",
2765 dest='validate_location',
2769 takes_args = ["domain"]
2771 def run(self, domain, sambaopts=None, versionopts=None, credopts=None, localdcopts=None,
2772 validate_location=None):
2774 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2776 local_server = self.setup_local_server(sambaopts, localdcopts)
2778 local_lsa = self.new_local_lsa_connection()
2779 except RuntimeError as error:
2780 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2783 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2784 except RuntimeError as error:
2785 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2787 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2788 local_lsa_info.name.string,
2789 local_lsa_info.dns_domain.string,
2790 local_lsa_info.sid))
2793 lsaString = lsa.String()
2794 lsaString.string = domain
2795 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2796 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2797 except RuntimeError as error:
2798 if self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2799 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2801 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
2803 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
2804 local_tdo_info.netbios_name.string,
2805 local_tdo_info.domain_name.string,
2806 local_tdo_info.sid))
2809 local_netlogon = self.new_local_netlogon_connection()
2810 except RuntimeError as error:
2811 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2814 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_server,
2815 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2817 local_tdo_info.domain_name.string)
2818 except RuntimeError as error:
2819 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2821 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2822 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2824 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2825 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2826 local_trust_verify.trusted_dc_name,
2827 local_trust_verify.tc_connection_status[1],
2828 local_trust_verify.pdc_connection_status[1])
2830 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2831 local_trust_verify.trusted_dc_name,
2832 local_trust_verify.tc_connection_status[1],
2833 local_trust_verify.pdc_connection_status[1])
2835 if local_trust_status != self.WERR_OK or local_conn_status != self.WERR_OK:
2836 raise CommandError(local_validation)
2838 self.outf.write("OK: %s\n" % local_validation)
2841 server = local_trust_verify.trusted_dc_name.replace('\\', '')
2842 domain_and_server = "%s\\%s" % (local_tdo_info.domain_name.string, server)
2843 local_trust_rediscover = local_netlogon.netr_LogonControl2Ex(local_server,
2844 netlogon.NETLOGON_CONTROL_REDISCOVER,
2847 except RuntimeError as error:
2848 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
2850 local_conn_status = self._uint32(local_trust_rediscover.tc_connection_status[0])
2851 local_rediscover = "LocalRediscover: DC[%s] CONNECTION[%s]" % (
2852 local_trust_rediscover.trusted_dc_name,
2853 local_trust_rediscover.tc_connection_status[1])
2855 if local_conn_status != self.WERR_OK:
2856 raise CommandError(local_rediscover)
2858 self.outf.write("OK: %s\n" % local_rediscover)
2860 if validate_location != "local":
2862 remote_server = self.setup_remote_server(credopts, domain, require_pdc=False)
2863 except RuntimeError as error:
2864 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2867 remote_netlogon = self.new_remote_netlogon_connection()
2868 except RuntimeError as error:
2869 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2872 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_server,
2873 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2875 local_lsa_info.dns_domain.string)
2876 except RuntimeError as error:
2877 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2879 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2880 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2882 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2883 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2884 remote_trust_verify.trusted_dc_name,
2885 remote_trust_verify.tc_connection_status[1],
2886 remote_trust_verify.pdc_connection_status[1])
2888 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2889 remote_trust_verify.trusted_dc_name,
2890 remote_trust_verify.tc_connection_status[1],
2891 remote_trust_verify.pdc_connection_status[1])
2893 if remote_trust_status != self.WERR_OK or remote_conn_status != self.WERR_OK:
2894 raise CommandError(remote_validation)
2896 self.outf.write("OK: %s\n" % remote_validation)
2899 server = remote_trust_verify.trusted_dc_name.replace('\\', '')
2900 domain_and_server = "%s\\%s" % (local_lsa_info.dns_domain.string, server)
2901 remote_trust_rediscover = remote_netlogon.netr_LogonControl2Ex(remote_server,
2902 netlogon.NETLOGON_CONTROL_REDISCOVER,
2905 except RuntimeError as error:
2906 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
2908 remote_conn_status = self._uint32(remote_trust_rediscover.tc_connection_status[0])
2910 remote_rediscover = "RemoteRediscover: DC[%s] CONNECTION[%s]" % (
2911 remote_trust_rediscover.trusted_dc_name,
2912 remote_trust_rediscover.tc_connection_status[1])
2914 if remote_conn_status != self.WERR_OK:
2915 raise CommandError(remote_rediscover)
2917 self.outf.write("OK: %s\n" % remote_rediscover)
2921 class cmd_domain_trust_namespaces(DomainTrustCommand):
2922 """Manage forest trust namespaces."""
2924 synopsis = "%prog [DOMAIN] [options]"
2926 takes_optiongroups = {
2927 "sambaopts": options.SambaOptions,
2928 "versionopts": options.VersionOptions,
2929 "localdcopts": LocalDCCredentialsOptions,
2933 Option("--refresh", type="choice", metavar="check|store",
2934 choices=["check", "store", None],
2935 help="List and maybe store refreshed forest trust information: 'check' or 'store'.",
2938 Option("--enable-all", action="store_true",
2939 help="Try to update disabled entries, not allowed with --refresh=check.",
2942 Option("--enable-tln", action="append", metavar='DNSDOMAIN',
2943 help="Enable a top level name entry. Can be specified multiple times.",
2946 Option("--disable-tln", action="append", metavar='DNSDOMAIN',
2947 help="Disable a top level name entry. Can be specified multiple times.",
2950 Option("--add-tln-ex", action="append", metavar='DNSDOMAIN',
2951 help="Add a top level exclusion entry. Can be specified multiple times.",
2954 Option("--delete-tln-ex", action="append", metavar='DNSDOMAIN',
2955 help="Delete a top level exclusion entry. Can be specified multiple times.",
2956 dest='delete_tln_ex',
2958 Option("--enable-nb", action="append", metavar='NETBIOSDOMAIN',
2959 help="Enable a netbios name in a domain entry. Can be specified multiple times.",
2962 Option("--disable-nb", action="append", metavar='NETBIOSDOMAIN',
2963 help="Disable a netbios name in a domain entry. Can be specified multiple times.",
2966 Option("--enable-sid", action="append", metavar='DOMAINSID',
2967 help="Enable a SID in a domain entry. Can be specified multiple times.",
2968 dest='enable_sid_str',
2970 Option("--disable-sid", action="append", metavar='DOMAINSID',
2971 help="Disable a SID in a domain entry. Can be specified multiple times.",
2972 dest='disable_sid_str',
2974 Option("--add-upn-suffix", action="append", metavar='DNSDOMAIN',
2975 help="Add a new uPNSuffixes attribute for the local forest. Can be specified multiple times.",
2978 Option("--delete-upn-suffix", action="append", metavar='DNSDOMAIN',
2979 help="Delete an existing uPNSuffixes attribute of the local forest. Can be specified multiple times.",
2982 Option("--add-spn-suffix", action="append", metavar='DNSDOMAIN',
2983 help="Add a new msDS-SPNSuffixes attribute for the local forest. Can be specified multiple times.",
2986 Option("--delete-spn-suffix", action="append", metavar='DNSDOMAIN',
2987 help="Delete an existing msDS-SPNSuffixes attribute of the local forest. Can be specified multiple times.",
2992 takes_args = ["domain?"]
2994 def run(self, domain=None, sambaopts=None, localdcopts=None, versionopts=None,
2995 refresh=None, enable_all=False,
2996 enable_tln=[], disable_tln=[], add_tln_ex=[], delete_tln_ex=[],
2997 enable_sid_str=[], disable_sid_str=[], enable_nb=[], disable_nb=[],
2998 add_upn=[], delete_upn=[], add_spn=[], delete_spn=[]):
3000 require_update = False
3003 if refresh == "store":
3004 raise CommandError("--refresh=%s not allowed without DOMAIN" % refresh)
3007 raise CommandError("--enable-all not allowed without DOMAIN")
3009 if len(enable_tln) > 0:
3010 raise CommandError("--enable-tln not allowed without DOMAIN")
3011 if len(disable_tln) > 0:
3012 raise CommandError("--disable-tln not allowed without DOMAIN")
3014 if len(add_tln_ex) > 0:
3015 raise CommandError("--add-tln-ex not allowed without DOMAIN")
3016 if len(delete_tln_ex) > 0:
3017 raise CommandError("--delete-tln-ex not allowed without DOMAIN")
3019 if len(enable_nb) > 0:
3020 raise CommandError("--enable-nb not allowed without DOMAIN")
3021 if len(disable_nb) > 0:
3022 raise CommandError("--disable-nb not allowed without DOMAIN")
3024 if len(enable_sid_str) > 0:
3025 raise CommandError("--enable-sid not allowed without DOMAIN")
3026 if len(disable_sid_str) > 0:
3027 raise CommandError("--disable-sid not allowed without DOMAIN")
3029 if len(add_upn) > 0:
3031 if not n.startswith("*."):
3033 raise CommandError("value[%s] specified for --add-upn-suffix should not include with '*.'" % n)
3034 require_update = True
3035 if len(delete_upn) > 0:
3036 for n in delete_upn:
3037 if not n.startswith("*."):
3039 raise CommandError("value[%s] specified for --delete-upn-suffix should not include with '*.'" % n)
3040 require_update = True
3042 for d in delete_upn:
3043 if a.lower() != d.lower():
3045 raise CommandError("value[%s] specified for --add-upn-suffix and --delete-upn-suffix" % a)
3047 if len(add_spn) > 0:
3049 if not n.startswith("*."):
3051 raise CommandError("value[%s] specified for --add-spn-suffix should not include with '*.'" % n)
3052 require_update = True
3053 if len(delete_spn) > 0:
3054 for n in delete_spn:
3055 if not n.startswith("*."):
3057 raise CommandError("value[%s] specified for --delete-spn-suffix should not include with '*.'" % n)
3058 require_update = True
3060 for d in delete_spn:
3061 if a.lower() != d.lower():
3063 raise CommandError("value[%s] specified for --add-spn-suffix and --delete-spn-suffix" % a)
3065 if len(add_upn) > 0:
3066 raise CommandError("--add-upn-suffix not allowed together with DOMAIN")
3067 if len(delete_upn) > 0:
3068 raise CommandError("--delete-upn-suffix not allowed together with DOMAIN")
3069 if len(add_spn) > 0:
3070 raise CommandError("--add-spn-suffix not allowed together with DOMAIN")
3071 if len(delete_spn) > 0:
3072 raise CommandError("--delete-spn-suffix not allowed together with DOMAIN")
3074 if refresh is not None:
3075 if refresh == "store":
3076 require_update = True
3078 if enable_all and refresh != "store":
3079 raise CommandError("--enable-all not allowed together with --refresh=%s" % refresh)
3081 if len(enable_tln) > 0:
3082 raise CommandError("--enable-tln not allowed together with --refresh")
3083 if len(disable_tln) > 0:
3084 raise CommandError("--disable-tln not allowed together with --refresh")
3086 if len(add_tln_ex) > 0:
3087 raise CommandError("--add-tln-ex not allowed together with --refresh")
3088 if len(delete_tln_ex) > 0:
3089 raise CommandError("--delete-tln-ex not allowed together with --refresh")
3091 if len(enable_nb) > 0:
3092 raise CommandError("--enable-nb not allowed together with --refresh")
3093 if len(disable_nb) > 0:
3094 raise CommandError("--disable-nb not allowed together with --refresh")
3096 if len(enable_sid_str) > 0:
3097 raise CommandError("--enable-sid not allowed together with --refresh")
3098 if len(disable_sid_str) > 0:
3099 raise CommandError("--disable-sid not allowed together with --refresh")
3102 require_update = True
3104 if len(enable_tln) > 0:
3105 raise CommandError("--enable-tln not allowed together with --enable-all")
3107 if len(enable_nb) > 0:
3108 raise CommandError("--enable-nb not allowed together with --enable-all")
3110 if len(enable_sid) > 0:
3111 raise CommandError("--enable-sid not allowed together with --enable-all")
3113 if len(enable_tln) > 0:
3114 require_update = True
3115 if len(disable_tln) > 0:
3116 require_update = True
3117 for e in enable_tln:
3118 for d in disable_tln:
3119 if e.lower() != d.lower():
3121 raise CommandError("value[%s] specified for --enable-tln and --disable-tln" % e)
3123 if len(add_tln_ex) > 0:
3124 for n in add_tln_ex:
3125 if not n.startswith("*."):
3127 raise CommandError("value[%s] specified for --add-tln-ex should not include with '*.'" % n)
3128 require_update = True
3129 if len(delete_tln_ex) > 0:
3130 for n in delete_tln_ex:
3131 if not n.startswith("*."):
3133 raise CommandError("value[%s] specified for --delete-tln-ex should not include with '*.'" % n)
3134 require_update = True
3135 for a in add_tln_ex:
3136 for d in delete_tln_ex:
3137 if a.lower() != d.lower():
3139 raise CommandError("value[%s] specified for --add-tln-ex and --delete-tln-ex" % a)
3141 if len(enable_nb) > 0:
3142 require_update = True
3143 if len(disable_nb) > 0:
3144 require_update = True
3146 for d in disable_nb:
3147 if e.upper() != d.upper():
3149 raise CommandError("value[%s] specified for --enable-nb and --disable-nb" % e)
3152 for s in enable_sid_str:
3154 sid = security.dom_sid(s)
3155 except TypeError as error:
3156 raise CommandError("value[%s] specified for --enable-sid is not a valid SID" % s)
3157 enable_sid.append(sid)
3159 for s in disable_sid_str:
3161 sid = security.dom_sid(s)
3162 except TypeError as error:
3163 raise CommandError("value[%s] specified for --disable-sid is not a valid SID" % s)
3164 disable_sid.append(sid)
3165 if len(enable_sid) > 0:
3166 require_update = True
3167 if len(disable_sid) > 0:
3168 require_update = True
3169 for e in enable_sid:
3170 for d in disable_sid:
3173 raise CommandError("value[%s] specified for --enable-sid and --disable-sid" % e)
3175 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
3177 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
3179 local_server = self.setup_local_server(sambaopts, localdcopts)
3181 local_lsa = self.new_local_lsa_connection()
3182 except RuntimeError as error:
3183 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
3186 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
3187 except RuntimeError as error:
3188 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
3190 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
3191 local_lsa_info.name.string,
3192 local_lsa_info.dns_domain.string,
3193 local_lsa_info.sid))
3197 local_netlogon = self.new_local_netlogon_connection()
3198 except RuntimeError as error:
3199 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3202 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3203 except RuntimeError as error:
3204 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3206 if local_netlogon_info.domain_name != local_netlogon_info.forest_name:
3207 raise CommandError("The local domain [%s] is not the forest root [%s]" % (
3208 local_netlogon_info.domain_name,
3209 local_netlogon_info.forest_name))
3212 # get all information about our own forest
3213 own_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3215 except RuntimeError as error:
3216 if self.check_runtime_error(error, self.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
3217 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3220 if self.check_runtime_error(error, self.WERR_INVALID_FUNCTION):
3221 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3224 if self.check_runtime_error(error, self.WERR_NERR_ACFNOTLOADED):
3225 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3228 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3230 self.outf.write("Own forest trust information...\n")
3231 self.write_forest_trust_info(own_forest_info,
3232 tln=local_lsa_info.dns_domain.string)
3235 local_samdb = self.new_local_ldap_connection()
3236 except RuntimeError as error:
3237 raise self.LocalRuntimeError(self, error, "failed to connect to SamDB")
3239 local_partitions_dn = "CN=Partitions,%s" % str(local_samdb.get_config_basedn())
3240 attrs = ['uPNSuffixes', 'msDS-SPNSuffixes']
3242 msgs = local_samdb.search(base=local_partitions_dn,
3243 scope=ldb.SCOPE_BASE,
3244 expression="(objectClass=crossRefContainer)",
3246 stored_msg = msgs[0]
3247 except ldb.LdbError as error:
3248 raise self.LocalLdbError(self, error, "failed to search partition dn")
3250 stored_upn_vals = []
3251 if 'uPNSuffixes' in stored_msg:
3252 stored_upn_vals.extend(stored_msg['uPNSuffixes'])
3254 stored_spn_vals = []
3255 if 'msDS-SPNSuffixes' in stored_msg:
3256 stored_spn_vals.extend(stored_msg['msDS-SPNSuffixes'])
3258 self.outf.write("Stored uPNSuffixes attributes[%d]:\n" % len(stored_upn_vals))
3259 for v in stored_upn_vals:
3260 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3261 self.outf.write("Stored msDS-SPNSuffixes attributes[%d]:\n" % len(stored_spn_vals))
3262 for v in stored_spn_vals:
3263 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3265 if not require_update:
3269 update_upn_vals = []
3270 update_upn_vals.extend(stored_upn_vals)
3273 update_spn_vals = []
3274 update_spn_vals.extend(stored_spn_vals)
3278 for i in xrange(0, len(update_upn_vals)):
3279 v = update_upn_vals[i]
3280 if v.lower() != upn.lower():
3285 raise CommandError("Entry already present for value[%s] specified for --add-upn-suffix" % upn)
3286 update_upn_vals.append(upn)
3289 for upn in delete_upn:
3291 for i in xrange(0, len(update_upn_vals)):
3292 v = update_upn_vals[i]
3293 if v.lower() != upn.lower():
3298 raise CommandError("Entry not found for value[%s] specified for --delete-upn-suffix" % upn)
3300 update_upn_vals.pop(idx)
3305 for i in xrange(0, len(update_spn_vals)):
3306 v = update_spn_vals[i]
3307 if v.lower() != spn.lower():
3312 raise CommandError("Entry already present for value[%s] specified for --add-spn-suffix" % spn)
3313 update_spn_vals.append(spn)
3316 for spn in delete_spn:
3318 for i in xrange(0, len(update_spn_vals)):
3319 v = update_spn_vals[i]
3320 if v.lower() != spn.lower():
3325 raise CommandError("Entry not found for value[%s] specified for --delete-spn-suffix" % spn)
3327 update_spn_vals.pop(idx)
3330 self.outf.write("Update uPNSuffixes attributes[%d]:\n" % len(update_upn_vals))
3331 for v in update_upn_vals:
3332 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3333 self.outf.write("Update msDS-SPNSuffixes attributes[%d]:\n" % len(update_spn_vals))
3334 for v in update_spn_vals:
3335 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3337 update_msg = ldb.Message()
3338 update_msg.dn = stored_msg.dn
3341 update_msg['uPNSuffixes'] = ldb.MessageElement(update_upn_vals,
3342 ldb.FLAG_MOD_REPLACE,
3345 update_msg['msDS-SPNSuffixes'] = ldb.MessageElement(update_spn_vals,
3346 ldb.FLAG_MOD_REPLACE,
3349 local_samdb.modify(update_msg)
3350 except ldb.LdbError as error:
3351 raise self.LocalLdbError(self, error, "failed to update partition dn")
3354 stored_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3356 except RuntimeError as error:
3357 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3359 self.outf.write("Stored forest trust information...\n")
3360 self.write_forest_trust_info(stored_forest_info,
3361 tln=local_lsa_info.dns_domain.string)
3365 lsaString = lsa.String()
3366 lsaString.string = domain
3367 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
3368 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
3369 except RuntimeError as error:
3370 if self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
3371 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
3373 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
3375 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
3376 local_tdo_info.netbios_name.string,
3377 local_tdo_info.domain_name.string,
3378 local_tdo_info.sid))
3380 if not local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
3381 raise CommandError("trusted domain object for domain [%s] is not marked as FOREST_TRANSITIVE." % domain)
3383 if refresh is not None:
3385 local_netlogon = self.new_local_netlogon_connection()
3386 except RuntimeError as error:
3387 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3390 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3391 except RuntimeError as error:
3392 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3394 lsa_update_check = 1
3395 if refresh == "store":
3396 netlogon_update_tdo = netlogon.DS_GFTI_UPDATE_TDO
3398 lsa_update_check = 0
3400 netlogon_update_tdo = 0
3403 # get all information about the remote trust
3404 # this triggers netr_GetForestTrustInformation to the remote domain
3405 # and lsaRSetForestTrustInformation() locally, but new top level
3406 # names are disabled by default.
3407 fresh_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3408 local_tdo_info.domain_name.string,
3409 netlogon_update_tdo)
3410 except RuntimeError as error:
3411 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3414 fresh_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3415 local_tdo_info.domain_name,
3416 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3419 except RuntimeError as error:
3420 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3422 self.outf.write("Fresh forest trust information...\n")
3423 self.write_forest_trust_info(fresh_forest_info,
3424 tln=local_tdo_info.domain_name.string,
3425 collisions=fresh_forest_collision)
3427 if refresh == "store":
3429 lsaString = lsa.String()
3430 lsaString.string = local_tdo_info.domain_name.string
3431 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3433 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3434 except RuntimeError as error:
3435 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3437 self.outf.write("Stored forest trust information...\n")
3438 self.write_forest_trust_info(stored_forest_info,
3439 tln=local_tdo_info.domain_name.string)
3444 # The none --refresh path
3448 lsaString = lsa.String()
3449 lsaString.string = local_tdo_info.domain_name.string
3450 local_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3452 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3453 except RuntimeError as error:
3454 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3456 self.outf.write("Local forest trust information...\n")
3457 self.write_forest_trust_info(local_forest_info,
3458 tln=local_tdo_info.domain_name.string)
3460 if not require_update:
3464 entries.extend(local_forest_info.entries)
3465 update_forest_info = lsa.ForestTrustInformation()
3466 update_forest_info.count = len(entries)
3467 update_forest_info.entries = entries
3470 for i in xrange(0, len(update_forest_info.entries)):
3471 r = update_forest_info.entries[i]
3472 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3474 if update_forest_info.entries[i].flags == 0:
3476 update_forest_info.entries[i].time = 0
3477 update_forest_info.entries[i].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3478 for i in xrange(0, len(update_forest_info.entries)):
3479 r = update_forest_info.entries[i]
3480 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3482 if update_forest_info.entries[i].flags == 0:
3484 update_forest_info.entries[i].time = 0
3485 update_forest_info.entries[i].flags &= ~lsa.LSA_NB_DISABLED_MASK
3486 update_forest_info.entries[i].flags &= ~lsa.LSA_SID_DISABLED_MASK
3488 for tln in enable_tln:
3490 for i in xrange(0, len(update_forest_info.entries)):
3491 r = update_forest_info.entries[i]
3492 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3494 if r.forest_trust_data.string.lower() != tln.lower():
3499 raise CommandError("Entry not found for value[%s] specified for --enable-tln" % tln)
3500 if not update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_MASK:
3501 raise CommandError("Entry found for value[%s] specified for --enable-tln is already enabled" % tln)
3502 update_forest_info.entries[idx].time = 0
3503 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3505 for tln in disable_tln:
3507 for i in xrange(0, len(update_forest_info.entries)):
3508 r = update_forest_info.entries[i]
3509 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3511 if r.forest_trust_data.string.lower() != tln.lower():
3516 raise CommandError("Entry not found for value[%s] specified for --disable-tln" % tln)
3517 if update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_ADMIN:
3518 raise CommandError("Entry found for value[%s] specified for --disable-tln is already disabled" % tln)
3519 update_forest_info.entries[idx].time = 0
3520 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3521 update_forest_info.entries[idx].flags |= lsa.LSA_TLN_DISABLED_ADMIN
3523 for tln_ex in add_tln_ex:
3525 for i in xrange(0, len(update_forest_info.entries)):
3526 r = update_forest_info.entries[i]
3527 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3529 if r.forest_trust_data.string.lower() != tln_ex.lower():
3534 raise CommandError("Entry already present for value[%s] specified for --add-tln-ex" % tln_ex)
3536 tln_dot = ".%s" % tln_ex.lower()
3538 for i in xrange(0, len(update_forest_info.entries)):
3539 r = update_forest_info.entries[i]
3540 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3542 r_dot = ".%s" % r.forest_trust_data.string.lower()
3543 if tln_dot == r_dot:
3544 raise CommandError("TLN entry present for value[%s] specified for --add-tln-ex" % tln_ex)
3545 if not tln_dot.endswith(r_dot):
3551 raise CommandError("No TLN parent present for value[%s] specified for --add-tln-ex" % tln_ex)
3553 r = lsa.ForestTrustRecord()
3554 r.type = lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX
3557 r.forest_trust_data.string = tln_ex
3560 entries.extend(update_forest_info.entries)
3561 entries.insert(idx + 1, r)
3562 update_forest_info.count = len(entries)
3563 update_forest_info.entries = entries
3565 for tln_ex in delete_tln_ex:
3567 for i in xrange(0, len(update_forest_info.entries)):
3568 r = update_forest_info.entries[i]
3569 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3571 if r.forest_trust_data.string.lower() != tln_ex.lower():
3576 raise CommandError("Entry not found for value[%s] specified for --delete-tln-ex" % tln_ex)
3579 entries.extend(update_forest_info.entries)
3581 update_forest_info.count = len(entries)
3582 update_forest_info.entries = entries
3584 for nb in enable_nb:
3586 for i in xrange(0, len(update_forest_info.entries)):
3587 r = update_forest_info.entries[i]
3588 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3590 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3595 raise CommandError("Entry not found for value[%s] specified for --enable-nb" % nb)
3596 if not update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_MASK:
3597 raise CommandError("Entry found for value[%s] specified for --enable-nb is already enabled" % nb)
3598 update_forest_info.entries[idx].time = 0
3599 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3601 for nb in disable_nb:
3603 for i in xrange(0, len(update_forest_info.entries)):
3604 r = update_forest_info.entries[i]
3605 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3607 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3612 raise CommandError("Entry not found for value[%s] specified for --delete-nb" % nb)
3613 if update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_ADMIN:
3614 raise CommandError("Entry found for value[%s] specified for --disable-nb is already disabled" % nb)
3615 update_forest_info.entries[idx].time = 0
3616 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3617 update_forest_info.entries[idx].flags |= lsa.LSA_NB_DISABLED_ADMIN
3619 for sid in enable_sid:
3621 for i in xrange(0, len(update_forest_info.entries)):
3622 r = update_forest_info.entries[i]
3623 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3625 if r.forest_trust_data.domain_sid != sid:
3630 raise CommandError("Entry not found for value[%s] specified for --enable-sid" % sid)
3631 if not update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_MASK:
3632 raise CommandError("Entry found for value[%s] specified for --enable-sid is already enabled" % nb)
3633 update_forest_info.entries[idx].time = 0
3634 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3636 for sid in disable_sid:
3638 for i in xrange(0, len(update_forest_info.entries)):
3639 r = update_forest_info.entries[i]
3640 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3642 if r.forest_trust_data.domain_sid != sid:
3647 raise CommandError("Entry not found for value[%s] specified for --delete-sid" % sid)
3648 if update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_ADMIN:
3649 raise CommandError("Entry found for value[%s] specified for --disable-sid is already disabled" % nb)
3650 update_forest_info.entries[idx].time = 0
3651 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3652 update_forest_info.entries[idx].flags |= lsa.LSA_SID_DISABLED_ADMIN
3655 update_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3656 local_tdo_info.domain_name,
3657 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3658 update_forest_info, 0)
3659 except RuntimeError as error:
3660 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3662 self.outf.write("Updated forest trust information...\n")
3663 self.write_forest_trust_info(update_forest_info,
3664 tln=local_tdo_info.domain_name.string,
3665 collisions=update_forest_collision)
3668 lsaString = lsa.String()
3669 lsaString.string = local_tdo_info.domain_name.string
3670 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3672 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3673 except RuntimeError as error:
3674 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3676 self.outf.write("Stored forest trust information...\n")
3677 self.write_forest_trust_info(stored_forest_info,
3678 tln=local_tdo_info.domain_name.string)
3681 class cmd_domain_trust(SuperCommand):
3682 """Domain and forest trust management."""
3685 subcommands["list"] = cmd_domain_trust_list()
3686 subcommands["show"] = cmd_domain_trust_show()
3687 subcommands["create"] = cmd_domain_trust_create()
3688 subcommands["delete"] = cmd_domain_trust_delete()
3689 subcommands["validate"] = cmd_domain_trust_validate()
3690 subcommands["namespaces"] = cmd_domain_trust_namespaces()
3692 class cmd_domain(SuperCommand):
3693 """Domain management."""
3696 subcommands["demote"] = cmd_domain_demote()
3697 if cmd_domain_export_keytab is not None:
3698 subcommands["exportkeytab"] = cmd_domain_export_keytab()
3699 subcommands["info"] = cmd_domain_info()
3700 subcommands["provision"] = cmd_domain_provision()
3701 subcommands["join"] = cmd_domain_join()
3702 subcommands["dcpromo"] = cmd_domain_dcpromo()
3703 subcommands["level"] = cmd_domain_level()
3704 subcommands["passwordsettings"] = cmd_domain_passwordsettings()
3705 subcommands["classicupgrade"] = cmd_domain_classicupgrade()
3706 subcommands["samba3upgrade"] = cmd_domain_samba3upgrade()
3707 subcommands["trust"] = cmd_domain_trust()