3 # Copyright Matthias Dieter Wallnoefer 2009
4 # Copyright Andrew Kroeger 2009
5 # Copyright Jelmer Vernooij 2007-2012
6 # Copyright Giampaolo Lauria 2011
7 # Copyright Matthieu Patou <mat@matws.net> 2011
8 # Copyright Andrew Bartlett 2008-2015
9 # Copyright Stefan Metzmacher 2012
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 3 of the License, or
14 # (at your option) any later version.
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 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 import misc
48 from samba.dcerpc.samr import DOMAIN_PASSWORD_COMPLEX, DOMAIN_PASSWORD_STORE_CLEARTEXT
49 from samba.netcmd import (
55 from samba.netcmd.common import netcmd_get_domain_infos_via_cldap
56 from samba.samba3 import Samba3
57 from samba.samba3 import param as s3param
58 from samba.upgrade import upgrade_from_samba3
59 from samba.drs_utils import (
60 sendDsReplicaSync, drsuapi_connect, drsException,
62 from samba import remove_dc
64 from samba.dsdb import (
65 DS_DOMAIN_FUNCTION_2000,
66 DS_DOMAIN_FUNCTION_2003,
67 DS_DOMAIN_FUNCTION_2003_MIXED,
68 DS_DOMAIN_FUNCTION_2008,
69 DS_DOMAIN_FUNCTION_2008_R2,
70 DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL,
71 DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL,
72 UF_WORKSTATION_TRUST_ACCOUNT,
73 UF_SERVER_TRUST_ACCOUNT,
74 UF_TRUSTED_FOR_DELEGATION,
75 UF_PARTIAL_SECRETS_ACCOUNT
78 from samba.provision import (
83 from samba.provision.common import (
89 def get_testparm_var(testparm, smbconf, varname):
90 cmd = "%s -s -l --parameter-name='%s' %s 2>/dev/null" % (testparm, varname, smbconf)
91 output = os.popen(cmd, 'r').readline()
97 cmd_domain_export_keytab = None
99 class cmd_domain_export_keytab(Command):
100 """Dump Kerberos keys of the domain into a keytab."""
102 synopsis = "%prog <keytab> [options]"
104 takes_optiongroups = {
105 "sambaopts": options.SambaOptions,
106 "credopts": options.CredentialsOptions,
107 "versionopts": options.VersionOptions,
111 Option("--principal", help="extract only this principal", type=str),
114 takes_args = ["keytab"]
116 def run(self, keytab, credopts=None, sambaopts=None, versionopts=None, principal=None):
117 lp = sambaopts.get_loadparm()
119 net.export_keytab(keytab=keytab, principal=principal)
122 class cmd_domain_info(Command):
123 """Print basic info about a domain and the DC passed as parameter."""
125 synopsis = "%prog <ip_address> [options]"
130 takes_optiongroups = {
131 "sambaopts": options.SambaOptions,
132 "credopts": options.CredentialsOptions,
133 "versionopts": options.VersionOptions,
136 takes_args = ["address"]
138 def run(self, address, credopts=None, sambaopts=None, versionopts=None):
139 lp = sambaopts.get_loadparm()
141 res = netcmd_get_domain_infos_via_cldap(lp, None, address)
143 raise CommandError("Invalid IP address '" + address + "'!")
144 self.outf.write("Forest : %s\n" % res.forest)
145 self.outf.write("Domain : %s\n" % res.dns_domain)
146 self.outf.write("Netbios domain : %s\n" % res.domain_name)
147 self.outf.write("DC name : %s\n" % res.pdc_dns_name)
148 self.outf.write("DC netbios name : %s\n" % res.pdc_name)
149 self.outf.write("Server site : %s\n" % res.server_site)
150 self.outf.write("Client site : %s\n" % res.client_site)
153 class cmd_domain_provision(Command):
154 """Provision a domain."""
156 synopsis = "%prog [options]"
158 takes_optiongroups = {
159 "sambaopts": options.SambaOptions,
160 "versionopts": options.VersionOptions,
164 Option("--interactive", help="Ask for names", action="store_true"),
165 Option("--domain", type="string", metavar="DOMAIN",
167 Option("--domain-guid", type="string", metavar="GUID",
168 help="set domainguid (otherwise random)"),
169 Option("--domain-sid", type="string", metavar="SID",
170 help="set domainsid (otherwise random)"),
171 Option("--ntds-guid", type="string", metavar="GUID",
172 help="set NTDS object GUID (otherwise random)"),
173 Option("--invocationid", type="string", metavar="GUID",
174 help="set invocationid (otherwise random)"),
175 Option("--host-name", type="string", metavar="HOSTNAME",
176 help="set hostname"),
177 Option("--host-ip", type="string", metavar="IPADDRESS",
178 help="set IPv4 ipaddress"),
179 Option("--host-ip6", type="string", metavar="IP6ADDRESS",
180 help="set IPv6 ipaddress"),
181 Option("--site", type="string", metavar="SITENAME",
182 help="set site name"),
183 Option("--adminpass", type="string", metavar="PASSWORD",
184 help="choose admin password (otherwise random)"),
185 Option("--krbtgtpass", type="string", metavar="PASSWORD",
186 help="choose krbtgt password (otherwise random)"),
187 Option("--machinepass", type="string", metavar="PASSWORD",
188 help="choose machine password (otherwise random)"),
189 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
190 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
191 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
192 "BIND9_FLATFILE uses bind9 text database to store zone information, "
193 "BIND9_DLZ uses samba4 AD to store zone information, "
194 "NONE skips the DNS setup entirely (not recommended)",
195 default="SAMBA_INTERNAL"),
196 Option("--dnspass", type="string", metavar="PASSWORD",
197 help="choose dns password (otherwise random)"),
198 Option("--ldapadminpass", type="string", metavar="PASSWORD",
199 help="choose password to set between Samba and its LDAP backend (otherwise random)"),
200 Option("--root", type="string", metavar="USERNAME",
201 help="choose 'root' unix username"),
202 Option("--nobody", type="string", metavar="USERNAME",
203 help="choose 'nobody' user"),
204 Option("--users", type="string", metavar="GROUPNAME",
205 help="choose 'users' group"),
206 Option("--quiet", help="Be quiet", action="store_true"),
207 Option("--blank", action="store_true",
208 help="do not add users or groups, just the structure"),
209 Option("--ldap-backend-type", type="choice", metavar="LDAP-BACKEND-TYPE",
210 help="Test initialisation support for unsupported LDAP backend type (fedora-ds or openldap) DO NOT USE",
211 choices=["fedora-ds", "openldap"]),
212 Option("--server-role", type="choice", metavar="ROLE",
213 choices=["domain controller", "dc", "member server", "member", "standalone"],
214 help="The server role (domain controller | dc | member server | member | standalone). Default is dc.",
215 default="domain controller"),
216 Option("--function-level", type="choice", metavar="FOR-FUN-LEVEL",
217 choices=["2000", "2003", "2008", "2008_R2"],
218 help="The domain and forest function level (2000 | 2003 | 2008 | 2008_R2 - always native). Default is (Windows) 2008_R2 Native.",
220 Option("--next-rid", type="int", metavar="NEXTRID", default=1000,
221 help="The initial nextRid value (only needed for upgrades). Default is 1000."),
222 Option("--partitions-only",
223 help="Configure Samba's partitions, but do not modify them (ie, join a BDC)", action="store_true"),
224 Option("--targetdir", type="string", metavar="DIR",
225 help="Set target directory"),
226 Option("--ol-mmr-urls", type="string", metavar="LDAPSERVER",
227 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\""),
228 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"),
230 Option("--use-rfc2307", action="store_true", help="Use AD to store posix attributes (default = no)"),
234 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",
235 action="store_true"),
236 Option("--slapd-path", type="string", metavar="SLAPD-PATH",
237 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."),
238 Option("--ldap-backend-extra-port", type="int", metavar="LDAP-BACKEND-EXTRA-PORT", help="Additional TCP port for LDAP backend server (to use for replication)"),
239 Option("--ldap-backend-forced-uri", type="string", metavar="LDAP-BACKEND-FORCED-URI",
240 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"),
241 Option("--ldap-backend-nosync", help="Configure LDAP backend not to call fsync() (for performance in test environments)", action="store_true"),
245 Option("--use-ntvfs", action="store_true", help="Use NTVFS for the fileserver (default = no)"),
248 if os.getenv('TEST_LDAP', "no") == "yes":
249 takes_options.extend(openldap_options)
251 if samba.is_ntvfs_fileserver_built():
252 takes_options.extend(ntvfs_options)
256 def run(self, sambaopts=None, versionopts=None,
279 ldap_backend_type=None,
283 partitions_only=None,
290 ldap_backend_nosync=None,
291 ldap_backend_extra_port=None,
292 ldap_backend_forced_uri=None,
293 ldap_dryrun_mode=None):
295 self.logger = self.get_logger("provision")
297 self.logger.setLevel(logging.WARNING)
299 self.logger.setLevel(logging.INFO)
301 lp = sambaopts.get_loadparm()
302 smbconf = lp.configfile
304 if dns_forwarder is not None:
305 suggested_forwarder = dns_forwarder
307 suggested_forwarder = self._get_nameserver_ip()
308 if suggested_forwarder is None:
309 suggested_forwarder = "none"
311 if len(self.raw_argv) == 1:
315 from getpass import getpass
318 def ask(prompt, default=None):
319 if default is not None:
320 print "%s [%s]: " % (prompt, default),
322 print "%s: " % (prompt,),
323 return sys.stdin.readline().rstrip("\n") or default
326 default = socket.getfqdn().split(".", 1)[1].upper()
329 realm = ask("Realm", default)
330 if realm in (None, ""):
331 raise CommandError("No realm set!")
334 default = realm.split(".")[0]
337 domain = ask("Domain", default)
339 raise CommandError("No domain set!")
341 server_role = ask("Server Role (dc, member, standalone)", "dc")
343 dns_backend = ask("DNS backend (SAMBA_INTERNAL, BIND9_FLATFILE, BIND9_DLZ, NONE)", "SAMBA_INTERNAL")
344 if dns_backend in (None, ''):
345 raise CommandError("No DNS backend set!")
347 if dns_backend == "SAMBA_INTERNAL":
348 dns_forwarder = ask("DNS forwarder IP address (write 'none' to disable forwarding)", suggested_forwarder)
349 if dns_forwarder.lower() in (None, 'none'):
350 suggested_forwarder = None
354 adminpassplain = getpass("Administrator password: ")
355 if not adminpassplain:
356 self.errf.write("Invalid administrator password.\n")
358 adminpassverify = getpass("Retype password: ")
359 if not adminpassplain == adminpassverify:
360 self.errf.write("Sorry, passwords do not match.\n")
362 adminpass = adminpassplain
366 realm = sambaopts._lp.get('realm')
368 raise CommandError("No realm set!")
370 raise CommandError("No domain set!")
373 self.logger.info("Administrator password will be set randomly!")
375 if function_level == "2000":
376 dom_for_fun_level = DS_DOMAIN_FUNCTION_2000
377 elif function_level == "2003":
378 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
379 elif function_level == "2008":
380 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008
381 elif function_level == "2008_R2":
382 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
384 if dns_backend == "SAMBA_INTERNAL" and dns_forwarder is None:
385 dns_forwarder = suggested_forwarder
387 samdb_fill = FILL_FULL
389 samdb_fill = FILL_NT4SYNC
390 elif partitions_only:
391 samdb_fill = FILL_DRS
393 if targetdir is not None:
394 if not os.path.isdir(targetdir):
399 if use_xattrs == "yes":
401 elif use_xattrs == "auto" and not lp.get("posix:eadb"):
403 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
405 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
408 samba.ntacls.setntacl(lp, file.name,
409 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
412 self.logger.info("You are not root or your system do not support xattr, using tdb backend for attributes. ")
417 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.")
418 if ldap_backend_type == "existing":
419 if ldap_backend_forced_uri is not None:
420 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)
422 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")
424 if ldap_backend_forced_uri is not None:
425 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")
427 if domain_sid is not None:
428 domain_sid = security.dom_sid(domain_sid)
430 session = system_session()
432 result = provision(self.logger,
433 session, smbconf=smbconf, targetdir=targetdir,
434 samdb_fill=samdb_fill, realm=realm, domain=domain,
435 domainguid=domain_guid, domainsid=domain_sid,
437 hostip=host_ip, hostip6=host_ip6,
438 sitename=site, ntdsguid=ntds_guid,
439 invocationid=invocationid, adminpass=adminpass,
440 krbtgtpass=krbtgtpass, machinepass=machinepass,
441 dns_backend=dns_backend, dns_forwarder=dns_forwarder,
442 dnspass=dnspass, root=root, nobody=nobody,
444 serverrole=server_role, dom_for_fun_level=dom_for_fun_level,
445 backend_type=ldap_backend_type,
446 ldapadminpass=ldapadminpass, ol_mmr_urls=ol_mmr_urls, slapd_path=slapd_path,
447 useeadb=eadb, next_rid=next_rid, lp=lp, use_ntvfs=use_ntvfs,
448 use_rfc2307=use_rfc2307, skip_sysvolacl=False,
449 ldap_backend_extra_port=ldap_backend_extra_port,
450 ldap_backend_forced_uri=ldap_backend_forced_uri,
451 nosync=ldap_backend_nosync, ldap_dryrun_mode=ldap_dryrun_mode)
453 except ProvisioningError, e:
454 raise CommandError("Provision failed", e)
456 result.report_logger(self.logger)
458 def _get_nameserver_ip(self):
459 """Grab the nameserver IP address from /etc/resolv.conf."""
461 RESOLV_CONF="/etc/resolv.conf"
463 if not path.isfile(RESOLV_CONF):
464 self.logger.warning("Failed to locate %s" % RESOLV_CONF)
469 handle = open(RESOLV_CONF, 'r')
471 if not line.startswith('nameserver'):
473 # we want the last non-space continuous string of the line
474 return line.strip().split()[-1]
476 if handle is not None:
479 self.logger.warning("No nameserver found in %s" % RESOLV_CONF)
482 class cmd_domain_dcpromo(Command):
483 """Promote an existing domain member or NT4 PDC to an AD DC."""
485 synopsis = "%prog <dnsdomain> [DC|RODC] [options]"
487 takes_optiongroups = {
488 "sambaopts": options.SambaOptions,
489 "versionopts": options.VersionOptions,
490 "credopts": options.CredentialsOptions,
494 Option("--server", help="DC to join", type=str),
495 Option("--site", help="site to join", type=str),
496 Option("--targetdir", help="where to store provision", type=str),
497 Option("--domain-critical-only",
498 help="only replicate critical domain objects",
499 action="store_true"),
500 Option("--machinepass", type=str, metavar="PASSWORD",
501 help="choose machine password (otherwise random)"),
502 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
503 choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
504 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
505 "BIND9_DLZ uses samba4 AD to store zone information, "
506 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
507 default="SAMBA_INTERNAL"),
508 Option("--quiet", help="Be quiet", action="store_true"),
509 Option("--verbose", help="Be verbose", action="store_true")
513 Option("--use-ntvfs", action="store_true", help="Use NTVFS for the fileserver (default = no)"),
516 if samba.is_ntvfs_fileserver_built():
517 takes_options.extend(ntvfs_options)
520 takes_args = ["domain", "role?"]
522 def run(self, domain, role=None, sambaopts=None, credopts=None,
523 versionopts=None, server=None, site=None, targetdir=None,
524 domain_critical_only=False, parent_domain=None, machinepass=None,
525 use_ntvfs=False, dns_backend=None,
526 quiet=False, verbose=False):
527 lp = sambaopts.get_loadparm()
528 creds = credopts.get_credentials(lp)
529 net = Net(creds, lp, server=credopts.ipaddress)
532 site = "Default-First-Site-Name"
534 logger = self.get_logger()
536 logger.setLevel(logging.DEBUG)
538 logger.setLevel(logging.WARNING)
540 logger.setLevel(logging.INFO)
542 netbios_name = lp.get("netbios name")
548 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
549 site=site, netbios_name=netbios_name, targetdir=targetdir,
550 domain_critical_only=domain_critical_only,
551 machinepass=machinepass, use_ntvfs=use_ntvfs,
552 dns_backend=dns_backend,
553 promote_existing=True)
555 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
556 site=site, netbios_name=netbios_name, targetdir=targetdir,
557 domain_critical_only=domain_critical_only,
558 machinepass=machinepass, use_ntvfs=use_ntvfs, dns_backend=dns_backend,
559 promote_existing=True)
561 raise CommandError("Invalid role '%s' (possible values: DC, RODC)" % role)
564 class cmd_domain_join(Command):
565 """Join domain as either member or backup domain controller."""
567 synopsis = "%prog <dnsdomain> [DC|RODC|MEMBER|SUBDOMAIN] [options]"
569 takes_optiongroups = {
570 "sambaopts": options.SambaOptions,
571 "versionopts": options.VersionOptions,
572 "credopts": options.CredentialsOptions,
576 Option("--server", help="DC to join", type=str),
577 Option("--site", help="site to join", type=str),
578 Option("--targetdir", help="where to store provision", type=str),
579 Option("--parent-domain", help="parent domain to create subdomain under", type=str),
580 Option("--domain-critical-only",
581 help="only replicate critical domain objects",
582 action="store_true"),
583 Option("--machinepass", type=str, metavar="PASSWORD",
584 help="choose machine password (otherwise random)"),
585 Option("--adminpass", type="string", metavar="PASSWORD",
586 help="choose adminstrator password when joining as a subdomain (otherwise random)"),
587 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
588 choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
589 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
590 "BIND9_DLZ uses samba4 AD to store zone information, "
591 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
592 default="SAMBA_INTERNAL"),
593 Option("--quiet", help="Be quiet", action="store_true"),
594 Option("--verbose", help="Be verbose", action="store_true")
598 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
601 if samba.is_ntvfs_fileserver_built():
602 takes_options.extend(ntvfs_options)
604 takes_args = ["domain", "role?"]
606 def run(self, domain, role=None, sambaopts=None, credopts=None,
607 versionopts=None, server=None, site=None, targetdir=None,
608 domain_critical_only=False, parent_domain=None, machinepass=None,
609 use_ntvfs=False, dns_backend=None, adminpass=None,
610 quiet=False, verbose=False):
611 lp = sambaopts.get_loadparm()
612 creds = credopts.get_credentials(lp)
613 net = Net(creds, lp, server=credopts.ipaddress)
616 site = "Default-First-Site-Name"
618 logger = self.get_logger()
620 logger.setLevel(logging.DEBUG)
622 logger.setLevel(logging.WARNING)
624 logger.setLevel(logging.INFO)
626 netbios_name = lp.get("netbios name")
631 if role is None or role == "MEMBER":
632 (join_password, sid, domain_name) = net.join_member(
633 domain, netbios_name, LIBNET_JOIN_AUTOMATIC,
634 machinepass=machinepass)
636 self.errf.write("Joined domain %s (%s)\n" % (domain_name, sid))
638 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
639 site=site, netbios_name=netbios_name, targetdir=targetdir,
640 domain_critical_only=domain_critical_only,
641 machinepass=machinepass, use_ntvfs=use_ntvfs, dns_backend=dns_backend)
643 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
644 site=site, netbios_name=netbios_name, targetdir=targetdir,
645 domain_critical_only=domain_critical_only,
646 machinepass=machinepass, use_ntvfs=use_ntvfs,
647 dns_backend=dns_backend)
648 elif role == "SUBDOMAIN":
650 logger.info("Administrator password will be set randomly!")
652 netbios_domain = lp.get("workgroup")
653 if parent_domain is None:
654 parent_domain = ".".join(domain.split(".")[1:])
655 join_subdomain(logger=logger, server=server, creds=creds, lp=lp, dnsdomain=domain,
656 parent_domain=parent_domain, site=site,
657 netbios_name=netbios_name, netbios_domain=netbios_domain,
658 targetdir=targetdir, machinepass=machinepass,
659 use_ntvfs=use_ntvfs, dns_backend=dns_backend,
662 raise CommandError("Invalid role '%s' (possible values: MEMBER, DC, RODC, SUBDOMAIN)" % role)
665 class cmd_domain_demote(Command):
666 """Demote ourselves from the role of Domain Controller."""
668 synopsis = "%prog [options]"
671 Option("--server", help="writable DC to write demotion changes on", type=str),
672 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
673 metavar="URL", dest="H"),
674 Option("--remove-other-dead-server", help="Dead DC (name or NTDS GUID) "
675 "to remove ALL references to (rather than this DC)", type=str),
676 Option("--quiet", help="Be quiet", action="store_true"),
677 Option("--verbose", help="Be verbose", action="store_true"),
680 takes_optiongroups = {
681 "sambaopts": options.SambaOptions,
682 "credopts": options.CredentialsOptions,
683 "versionopts": options.VersionOptions,
686 def run(self, sambaopts=None, credopts=None,
687 versionopts=None, server=None,
688 remove_other_dead_server=None, H=None,
689 verbose=False, quiet=False):
690 lp = sambaopts.get_loadparm()
691 creds = credopts.get_credentials(lp)
692 net = Net(creds, lp, server=credopts.ipaddress)
694 logger = self.get_logger()
696 logger.setLevel(logging.DEBUG)
698 logger.setLevel(logging.WARNING)
700 logger.setLevel(logging.INFO)
702 if remove_other_dead_server is not None:
703 if server is not None:
704 samdb = SamDB(url="ldap://%s" % server,
705 session_info=system_session(),
706 credentials=creds, lp=lp)
708 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
710 remove_dc.remove_dc(samdb, logger, remove_other_dead_server)
711 except remove_dc.DemoteException as err:
712 raise CommandError("Demote failed: %s" % err)
715 netbios_name = lp.get("netbios name")
716 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
718 res = samdb.search(expression='(&(objectClass=computer)(serverReferenceBL=*))', attrs=["dnsHostName", "name"])
720 raise CommandError("Unable to search for servers")
723 raise CommandError("You are the latest server in the domain")
727 if str(e["name"]).lower() != netbios_name.lower():
728 server = e["dnsHostName"]
731 ntds_guid = samdb.get_ntds_GUID()
732 msg = samdb.search(base=str(samdb.get_config_basedn()),
733 scope=ldb.SCOPE_SUBTREE, expression="(objectGUID=%s)" % ntds_guid,
735 if len(msg) == 0 or "options" not in msg[0]:
736 raise CommandError("Failed to find options on %s" % ntds_guid)
739 dsa_options = int(str(msg[0]['options']))
741 res = samdb.search(expression="(fSMORoleOwner=%s)" % str(ntds_dn),
742 controls=["search_options:1:2"])
745 raise CommandError("Current DC is still the owner of %d role(s), use the role command to transfer roles to another DC" % len(res))
747 self.errf.write("Using %s as partner server for the demotion\n" %
749 (drsuapiBind, drsuapi_handle, supportedExtensions) = drsuapi_connect(server, lp, creds)
751 self.errf.write("Deactivating inbound replication\n")
753 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
757 dsa_options |= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
758 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
762 self.errf.write("Asking partner server %s to synchronize from us\n"
764 for part in (samdb.get_schema_basedn(),
765 samdb.get_config_basedn(),
766 samdb.get_root_basedn()):
767 nc = drsuapi.DsReplicaObjectIdentifier()
770 req1 = drsuapi.DsReplicaSyncRequest1()
771 req1.naming_context = nc;
772 req1.options = drsuapi.DRSUAPI_DRS_WRIT_REP
773 req1.source_dsa_guid = misc.GUID(ntds_guid)
776 drsuapiBind.DsReplicaSync(drsuapi_handle, 1, req1)
777 except RuntimeError as (werr, string):
778 if werr == 8452: #WERR_DS_DRA_NO_REPLICA
782 "Error while demoting, "
783 "re-enabling inbound replication\n")
784 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
785 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
787 raise CommandError("Error while sending a DsReplicaSync for partion %s" % str(part), e)
789 remote_samdb = SamDB(url="ldap://%s" % server,
790 session_info=system_session(),
791 credentials=creds, lp=lp)
793 self.errf.write("Changing userControl and container\n")
794 res = remote_samdb.search(base=str(remote_samdb.domain_dn()),
795 expression="(&(objectClass=user)(sAMAccountName=%s$))" %
796 netbios_name.upper(),
797 attrs=["userAccountControl"])
799 uac = int(str(res[0]["userAccountControl"]))
803 "Error while demoting, re-enabling inbound replication\n")
804 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
805 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
807 raise CommandError("Error while changing account control", e)
811 "Error while demoting, re-enabling inbound replication")
812 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
813 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
815 raise CommandError("Unable to find object with samaccountName = %s$"
816 " in the remote dc" % netbios_name.upper())
820 uac &= ~(UF_SERVER_TRUST_ACCOUNT|UF_TRUSTED_FOR_DELEGATION|UF_PARTIAL_SECRETS_ACCOUNT)
821 uac |= UF_WORKSTATION_TRUST_ACCOUNT
826 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
827 ldb.FLAG_MOD_REPLACE,
828 "userAccountControl")
830 remote_samdb.modify(msg)
833 "Error while demoting, re-enabling inbound replication")
834 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
835 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
838 raise CommandError("Error while changing account control", e)
840 parent = msg.dn.parent()
841 dc_name = res[0].dn.get_rdn_value()
842 rdn = "CN=%s" % dc_name
844 # Let's move to the Computer container
848 computer_dn = ldb.Dn(remote_samdb, "CN=Computers,%s" % str(remote_samdb.domain_dn()))
849 res = remote_samdb.search(base=computer_dn, expression=rdn, scope=ldb.SCOPE_ONELEVEL)
852 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
853 scope=ldb.SCOPE_ONELEVEL)
854 while(len(res) != 0 and i < 100):
856 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
857 scope=ldb.SCOPE_ONELEVEL)
861 "Error while demoting, re-enabling inbound replication\n")
862 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
863 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
869 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
870 ldb.FLAG_MOD_REPLACE,
871 "userAccountControl")
873 remote_samdb.modify(msg)
875 raise CommandError("Unable to find a slot for renaming %s,"
876 " all names from %s-1 to %s-%d seemed used" %
877 (str(dc_dn), rdn, rdn, i - 9))
879 newrdn = "%s-%d" % (rdn, i)
882 newdn = ldb.Dn(remote_samdb, "%s,%s" % (newrdn, str(computer_dn)))
883 remote_samdb.rename(dc_dn, newdn)
886 "Error while demoting, re-enabling inbound replication\n")
887 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
888 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
894 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
895 ldb.FLAG_MOD_REPLACE,
896 "userAccountControl")
898 remote_samdb.modify(msg)
899 raise CommandError("Error while renaming %s to %s" % (str(dc_dn), str(newdn)), e)
902 server_dsa_dn = samdb.get_serverName()
903 domain = remote_samdb.get_root_basedn()
906 req1 = drsuapi.DsRemoveDSServerRequest1()
907 req1.server_dn = str(server_dsa_dn)
908 req1.domain_dn = str(domain)
911 drsuapiBind.DsRemoveDSServer(drsuapi_handle, 1, req1)
912 except RuntimeError as (werr, string):
913 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
915 "Error while demoting, re-enabling inbound replication\n")
916 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
917 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
923 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
924 ldb.FLAG_MOD_REPLACE,
925 "userAccountControl")
926 remote_samdb.modify(msg)
927 remote_samdb.rename(newdn, dc_dn)
928 if werr == 8452: #WERR_DS_DRA_NO_REPLICA
929 raise CommandError("The DC %s is not present on (already removed from) the remote server: " % server_dsa_dn, e)
931 raise CommandError("Error while sending a removeDsServer of %s: " % server_dsa_dn, e)
933 remove_dc.remove_sysvol_references(remote_samdb, dc_name)
935 # These are objects under the computer account that should be deleted
936 for s in ("CN=Enterprise,CN=NTFRS Subscriptions",
937 "CN=%s, CN=NTFRS Subscriptions" % lp.get("realm"),
938 "CN=Domain system Volumes (SYSVOL Share), CN=NTFRS Subscriptions",
939 "CN=NTFRS Subscriptions"):
941 remote_samdb.delete(ldb.Dn(remote_samdb,
942 "%s,%s" % (s, str(newdn))))
943 except ldb.LdbError, l:
946 self.errf.write("Demote successful\n")
949 class cmd_domain_level(Command):
950 """Raise domain and forest function levels."""
952 synopsis = "%prog (show|raise <options>) [options]"
954 takes_optiongroups = {
955 "sambaopts": options.SambaOptions,
956 "credopts": options.CredentialsOptions,
957 "versionopts": options.VersionOptions,
961 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
962 metavar="URL", dest="H"),
963 Option("--quiet", help="Be quiet", action="store_true"),
964 Option("--forest-level", type="choice", choices=["2003", "2008", "2008_R2"],
965 help="The forest function level (2003 | 2008 | 2008_R2)"),
966 Option("--domain-level", type="choice", choices=["2003", "2008", "2008_R2"],
967 help="The domain function level (2003 | 2008 | 2008_R2)")
970 takes_args = ["subcommand"]
972 def run(self, subcommand, H=None, forest_level=None, domain_level=None,
973 quiet=False, credopts=None, sambaopts=None, versionopts=None):
974 lp = sambaopts.get_loadparm()
975 creds = credopts.get_credentials(lp, fallback_machine=True)
977 samdb = SamDB(url=H, session_info=system_session(),
978 credentials=creds, lp=lp)
980 domain_dn = samdb.domain_dn()
982 res_forest = samdb.search("CN=Partitions,%s" % samdb.get_config_basedn(),
983 scope=ldb.SCOPE_BASE, attrs=["msDS-Behavior-Version"])
984 assert len(res_forest) == 1
986 res_domain = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
987 attrs=["msDS-Behavior-Version", "nTMixedDomain"])
988 assert len(res_domain) == 1
990 res_dc_s = samdb.search("CN=Sites,%s" % samdb.get_config_basedn(),
991 scope=ldb.SCOPE_SUBTREE, expression="(objectClass=nTDSDSA)",
992 attrs=["msDS-Behavior-Version"])
993 assert len(res_dc_s) >= 1
996 level_forest = int(res_forest[0]["msDS-Behavior-Version"][0])
997 level_domain = int(res_domain[0]["msDS-Behavior-Version"][0])
998 level_domain_mixed = int(res_domain[0]["nTMixedDomain"][0])
1000 min_level_dc = int(res_dc_s[0]["msDS-Behavior-Version"][0]) # Init value
1001 for msg in res_dc_s:
1002 if int(msg["msDS-Behavior-Version"][0]) < min_level_dc:
1003 min_level_dc = int(msg["msDS-Behavior-Version"][0])
1005 if level_forest < 0 or level_domain < 0:
1006 raise CommandError("Domain and/or forest function level(s) is/are invalid. Correct them or reprovision!")
1007 if min_level_dc < 0:
1008 raise CommandError("Lowest function level of a DC is invalid. Correct this or reprovision!")
1009 if level_forest > level_domain:
1010 raise CommandError("Forest function level is higher than the domain level(s). Correct this or reprovision!")
1011 if level_domain > min_level_dc:
1012 raise CommandError("Domain function level is higher than the lowest function level of a DC. Correct this or reprovision!")
1015 raise CommandError("Could not retrieve the actual domain, forest level and/or lowest DC function level!")
1017 if subcommand == "show":
1018 self.message("Domain and forest function level for domain '%s'" % domain_dn)
1019 if level_forest == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1020 self.message("\nATTENTION: You run SAMBA 4 on a forest function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1021 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1022 self.message("\nATTENTION: You run SAMBA 4 on a domain function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1023 if min_level_dc == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1024 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)!")
1028 if level_forest == DS_DOMAIN_FUNCTION_2000:
1030 elif level_forest == DS_DOMAIN_FUNCTION_2003_MIXED:
1031 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1032 elif level_forest == DS_DOMAIN_FUNCTION_2003:
1034 elif level_forest == DS_DOMAIN_FUNCTION_2008:
1036 elif level_forest == DS_DOMAIN_FUNCTION_2008_R2:
1039 outstr = "higher than 2008 R2"
1040 self.message("Forest function level: (Windows) " + outstr)
1042 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1043 outstr = "2000 mixed (NT4 DC support)"
1044 elif level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed == 0:
1046 elif level_domain == DS_DOMAIN_FUNCTION_2003_MIXED:
1047 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1048 elif level_domain == DS_DOMAIN_FUNCTION_2003:
1050 elif level_domain == DS_DOMAIN_FUNCTION_2008:
1052 elif level_domain == DS_DOMAIN_FUNCTION_2008_R2:
1055 outstr = "higher than 2008 R2"
1056 self.message("Domain function level: (Windows) " + outstr)
1058 if min_level_dc == DS_DOMAIN_FUNCTION_2000:
1060 elif min_level_dc == DS_DOMAIN_FUNCTION_2003:
1062 elif min_level_dc == DS_DOMAIN_FUNCTION_2008:
1064 elif min_level_dc == DS_DOMAIN_FUNCTION_2008_R2:
1067 outstr = "higher than 2008 R2"
1068 self.message("Lowest function level of a DC: (Windows) " + outstr)
1070 elif subcommand == "raise":
1073 if domain_level is not None:
1074 if domain_level == "2003":
1075 new_level_domain = DS_DOMAIN_FUNCTION_2003
1076 elif domain_level == "2008":
1077 new_level_domain = DS_DOMAIN_FUNCTION_2008
1078 elif domain_level == "2008_R2":
1079 new_level_domain = DS_DOMAIN_FUNCTION_2008_R2
1081 if new_level_domain <= level_domain and level_domain_mixed == 0:
1082 raise CommandError("Domain function level can't be smaller than or equal to the actual one!")
1084 if new_level_domain > min_level_dc:
1085 raise CommandError("Domain function level can't be higher than the lowest function level of a DC!")
1087 # Deactivate mixed/interim domain support
1088 if level_domain_mixed != 0:
1089 # Directly on the base DN
1091 m.dn = ldb.Dn(samdb, domain_dn)
1092 m["nTMixedDomain"] = ldb.MessageElement("0",
1093 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1097 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup") + ",CN=Partitions,%s" % samdb.get_config_basedn())
1098 m["nTMixedDomain"] = ldb.MessageElement("0",
1099 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1102 except ldb.LdbError, (enum, emsg):
1103 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1106 # Directly on the base DN
1108 m.dn = ldb.Dn(samdb, domain_dn)
1109 m["msDS-Behavior-Version"]= ldb.MessageElement(
1110 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1111 "msDS-Behavior-Version")
1115 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup")
1116 + ",CN=Partitions,%s" % samdb.get_config_basedn())
1117 m["msDS-Behavior-Version"]= ldb.MessageElement(
1118 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1119 "msDS-Behavior-Version")
1122 except ldb.LdbError, (enum, emsg):
1123 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1126 level_domain = new_level_domain
1127 msgs.append("Domain function level changed!")
1129 if forest_level is not None:
1130 if forest_level == "2003":
1131 new_level_forest = DS_DOMAIN_FUNCTION_2003
1132 elif forest_level == "2008":
1133 new_level_forest = DS_DOMAIN_FUNCTION_2008
1134 elif forest_level == "2008_R2":
1135 new_level_forest = DS_DOMAIN_FUNCTION_2008_R2
1136 if new_level_forest <= level_forest:
1137 raise CommandError("Forest function level can't be smaller than or equal to the actual one!")
1138 if new_level_forest > level_domain:
1139 raise CommandError("Forest function level can't be higher than the domain function level(s). Please raise it/them first!")
1141 m.dn = ldb.Dn(samdb, "CN=Partitions,%s" % samdb.get_config_basedn())
1142 m["msDS-Behavior-Version"]= ldb.MessageElement(
1143 str(new_level_forest), ldb.FLAG_MOD_REPLACE,
1144 "msDS-Behavior-Version")
1146 msgs.append("Forest function level changed!")
1147 msgs.append("All changes applied successfully!")
1148 self.message("\n".join(msgs))
1150 raise CommandError("invalid argument: '%s' (choose from 'show', 'raise')" % subcommand)
1153 class cmd_domain_passwordsettings(Command):
1154 """Set password settings.
1156 Password complexity, password lockout policy, history length,
1157 minimum password length, the minimum and maximum password age) on
1158 a Samba AD DC server.
1160 Use against a Windows DC is possible, but group policy will override it.
1163 synopsis = "%prog (show|set <options>) [options]"
1165 takes_optiongroups = {
1166 "sambaopts": options.SambaOptions,
1167 "versionopts": options.VersionOptions,
1168 "credopts": options.CredentialsOptions,
1172 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1173 metavar="URL", dest="H"),
1174 Option("--quiet", help="Be quiet", action="store_true"),
1175 Option("--complexity", type="choice", choices=["on","off","default"],
1176 help="The password complexity (on | off | default). Default is 'on'"),
1177 Option("--store-plaintext", type="choice", choices=["on","off","default"],
1178 help="Store plaintext passwords where account have 'store passwords with reversible encryption' set (on | off | default). Default is 'off'"),
1179 Option("--history-length",
1180 help="The password history length (<integer> | default). Default is 24.", type=str),
1181 Option("--min-pwd-length",
1182 help="The minimum password length (<integer> | default). Default is 7.", type=str),
1183 Option("--min-pwd-age",
1184 help="The minimum password age (<integer in days> | default). Default is 1.", type=str),
1185 Option("--max-pwd-age",
1186 help="The maximum password age (<integer in days> | default). Default is 43.", type=str),
1187 Option("--account-lockout-duration",
1188 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),
1189 Option("--account-lockout-threshold",
1190 help="The number of bad password attempts allowed before locking out the account (<integer> | default). Default is 0 (never lock out).", type=str),
1191 Option("--reset-account-lockout-after",
1192 help="After this time is elapsed, the recorded number of attempts restarts from zero (<integer> | default). Default is 30.", type=str),
1195 takes_args = ["subcommand"]
1197 def run(self, subcommand, H=None, min_pwd_age=None, max_pwd_age=None,
1198 quiet=False, complexity=None, store_plaintext=None, history_length=None,
1199 min_pwd_length=None, account_lockout_duration=None, account_lockout_threshold=None,
1200 reset_account_lockout_after=None, credopts=None, sambaopts=None,
1202 lp = sambaopts.get_loadparm()
1203 creds = credopts.get_credentials(lp)
1205 samdb = SamDB(url=H, session_info=system_session(),
1206 credentials=creds, lp=lp)
1208 domain_dn = samdb.domain_dn()
1209 res = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1210 attrs=["pwdProperties", "pwdHistoryLength", "minPwdLength",
1211 "minPwdAge", "maxPwdAge", "lockoutDuration", "lockoutThreshold",
1212 "lockOutObservationWindow"])
1213 assert(len(res) == 1)
1215 pwd_props = int(res[0]["pwdProperties"][0])
1216 pwd_hist_len = int(res[0]["pwdHistoryLength"][0])
1217 cur_min_pwd_len = int(res[0]["minPwdLength"][0])
1219 cur_min_pwd_age = int(abs(int(res[0]["minPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1220 if int(res[0]["maxPwdAge"][0]) == -0x8000000000000000:
1223 cur_max_pwd_age = int(abs(int(res[0]["maxPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1224 cur_account_lockout_threshold = int(res[0]["lockoutThreshold"][0])
1226 if int(res[0]["lockoutDuration"][0]) == -0x8000000000000000:
1227 cur_account_lockout_duration = 0
1229 cur_account_lockout_duration = abs(int(res[0]["lockoutDuration"][0])) / (1e7 * 60)
1230 cur_reset_account_lockout_after = abs(int(res[0]["lockOutObservationWindow"][0])) / (1e7 * 60)
1231 except Exception, e:
1232 raise CommandError("Could not retrieve password properties!", e)
1234 if subcommand == "show":
1235 self.message("Password informations for domain '%s'" % domain_dn)
1237 if pwd_props & DOMAIN_PASSWORD_COMPLEX != 0:
1238 self.message("Password complexity: on")
1240 self.message("Password complexity: off")
1241 if pwd_props & DOMAIN_PASSWORD_STORE_CLEARTEXT != 0:
1242 self.message("Store plaintext passwords: on")
1244 self.message("Store plaintext passwords: off")
1245 self.message("Password history length: %d" % pwd_hist_len)
1246 self.message("Minimum password length: %d" % cur_min_pwd_len)
1247 self.message("Minimum password age (days): %d" % cur_min_pwd_age)
1248 self.message("Maximum password age (days): %d" % cur_max_pwd_age)
1249 self.message("Account lockout duration (mins): %d" % cur_account_lockout_duration)
1250 self.message("Account lockout threshold (attempts): %d" % cur_account_lockout_threshold)
1251 self.message("Reset account lockout after (mins): %d" % cur_reset_account_lockout_after)
1252 elif subcommand == "set":
1255 m.dn = ldb.Dn(samdb, domain_dn)
1257 if complexity is not None:
1258 if complexity == "on" or complexity == "default":
1259 pwd_props = pwd_props | DOMAIN_PASSWORD_COMPLEX
1260 msgs.append("Password complexity activated!")
1261 elif complexity == "off":
1262 pwd_props = pwd_props & (~DOMAIN_PASSWORD_COMPLEX)
1263 msgs.append("Password complexity deactivated!")
1265 if store_plaintext is not None:
1266 if store_plaintext == "on" or store_plaintext == "default":
1267 pwd_props = pwd_props | DOMAIN_PASSWORD_STORE_CLEARTEXT
1268 msgs.append("Plaintext password storage for changed passwords activated!")
1269 elif store_plaintext == "off":
1270 pwd_props = pwd_props & (~DOMAIN_PASSWORD_STORE_CLEARTEXT)
1271 msgs.append("Plaintext password storage for changed passwords deactivated!")
1273 if complexity is not None or store_plaintext is not None:
1274 m["pwdProperties"] = ldb.MessageElement(str(pwd_props),
1275 ldb.FLAG_MOD_REPLACE, "pwdProperties")
1277 if history_length is not None:
1278 if history_length == "default":
1281 pwd_hist_len = int(history_length)
1283 if pwd_hist_len < 0 or pwd_hist_len > 24:
1284 raise CommandError("Password history length must be in the range of 0 to 24!")
1286 m["pwdHistoryLength"] = ldb.MessageElement(str(pwd_hist_len),
1287 ldb.FLAG_MOD_REPLACE, "pwdHistoryLength")
1288 msgs.append("Password history length changed!")
1290 if min_pwd_length is not None:
1291 if min_pwd_length == "default":
1294 min_pwd_len = int(min_pwd_length)
1296 if min_pwd_len < 0 or min_pwd_len > 14:
1297 raise CommandError("Minimum password length must be in the range of 0 to 14!")
1299 m["minPwdLength"] = ldb.MessageElement(str(min_pwd_len),
1300 ldb.FLAG_MOD_REPLACE, "minPwdLength")
1301 msgs.append("Minimum password length changed!")
1303 if min_pwd_age is not None:
1304 if min_pwd_age == "default":
1307 min_pwd_age = int(min_pwd_age)
1309 if min_pwd_age < 0 or min_pwd_age > 998:
1310 raise CommandError("Minimum password age must be in the range of 0 to 998!")
1313 min_pwd_age_ticks = -int(min_pwd_age * (24 * 60 * 60 * 1e7))
1315 m["minPwdAge"] = ldb.MessageElement(str(min_pwd_age_ticks),
1316 ldb.FLAG_MOD_REPLACE, "minPwdAge")
1317 msgs.append("Minimum password age changed!")
1319 if max_pwd_age is not None:
1320 if max_pwd_age == "default":
1323 max_pwd_age = int(max_pwd_age)
1325 if max_pwd_age < 0 or max_pwd_age > 999:
1326 raise CommandError("Maximum password age must be in the range of 0 to 999!")
1329 if max_pwd_age == 0:
1330 max_pwd_age_ticks = -0x8000000000000000
1332 max_pwd_age_ticks = -int(max_pwd_age * (24 * 60 * 60 * 1e7))
1334 m["maxPwdAge"] = ldb.MessageElement(str(max_pwd_age_ticks),
1335 ldb.FLAG_MOD_REPLACE, "maxPwdAge")
1336 msgs.append("Maximum password age changed!")
1338 if account_lockout_duration is not None:
1339 if account_lockout_duration == "default":
1340 account_lockout_duration = 30
1342 account_lockout_duration = int(account_lockout_duration)
1344 if account_lockout_duration < 0 or account_lockout_duration > 99999:
1345 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1348 if account_lockout_duration == 0:
1349 account_lockout_duration_ticks = -0x8000000000000000
1351 account_lockout_duration_ticks = -int(account_lockout_duration * (60 * 1e7))
1353 m["lockoutDuration"] = ldb.MessageElement(str(account_lockout_duration_ticks),
1354 ldb.FLAG_MOD_REPLACE, "lockoutDuration")
1355 msgs.append("Account lockout duration changed!")
1357 if account_lockout_threshold is not None:
1358 if account_lockout_threshold == "default":
1359 account_lockout_threshold = 0
1361 account_lockout_threshold = int(account_lockout_threshold)
1363 m["lockoutThreshold"] = ldb.MessageElement(str(account_lockout_threshold),
1364 ldb.FLAG_MOD_REPLACE, "lockoutThreshold")
1365 msgs.append("Account lockout threshold changed!")
1367 if reset_account_lockout_after is not None:
1368 if reset_account_lockout_after == "default":
1369 reset_account_lockout_after = 30
1371 reset_account_lockout_after = int(reset_account_lockout_after)
1373 if reset_account_lockout_after < 0 or reset_account_lockout_after > 99999:
1374 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1377 if reset_account_lockout_after == 0:
1378 reset_account_lockout_after_ticks = -0x8000000000000000
1380 reset_account_lockout_after_ticks = -int(reset_account_lockout_after * (60 * 1e7))
1382 m["lockOutObservationWindow"] = ldb.MessageElement(str(reset_account_lockout_after_ticks),
1383 ldb.FLAG_MOD_REPLACE, "lockOutObservationWindow")
1384 msgs.append("Duration to reset account lockout after changed!")
1386 if max_pwd_age > 0 and min_pwd_age >= max_pwd_age:
1387 raise CommandError("Maximum password age (%d) must be greater than minimum password age (%d)!" % (max_pwd_age, min_pwd_age))
1390 raise CommandError("You must specify at least one option to set. Try --help")
1392 msgs.append("All changes applied successfully!")
1393 self.message("\n".join(msgs))
1395 raise CommandError("Wrong argument '%s'!" % subcommand)
1398 class cmd_domain_classicupgrade(Command):
1399 """Upgrade from Samba classic (NT4-like) database to Samba AD DC database.
1401 Specify either a directory with all Samba classic DC databases and state files (with --dbdir) or
1402 the testparm utility from your classic installation (with --testparm).
1405 synopsis = "%prog [options] <classic_smb_conf>"
1407 takes_optiongroups = {
1408 "sambaopts": options.SambaOptions,
1409 "versionopts": options.VersionOptions
1413 Option("--dbdir", type="string", metavar="DIR",
1414 help="Path to samba classic DC database directory"),
1415 Option("--testparm", type="string", metavar="PATH",
1416 help="Path to samba classic DC testparm utility from the previous installation. This allows the default paths of the previous installation to be followed"),
1417 Option("--targetdir", type="string", metavar="DIR",
1418 help="Path prefix where the new Samba 4.0 AD domain should be initialised"),
1419 Option("--quiet", help="Be quiet", action="store_true"),
1420 Option("--verbose", help="Be verbose", action="store_true"),
1421 Option("--use-xattrs", type="choice", choices=["yes","no","auto"], metavar="[yes|no|auto]",
1422 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"),
1423 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
1424 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
1425 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
1426 "BIND9_FLATFILE uses bind9 text database to store zone information, "
1427 "BIND9_DLZ uses samba4 AD to store zone information, "
1428 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
1429 default="SAMBA_INTERNAL")
1433 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
1434 action="store_true")
1436 if samba.is_ntvfs_fileserver_built():
1437 takes_options.extend(ntvfs_options)
1439 takes_args = ["smbconf"]
1441 def run(self, smbconf=None, targetdir=None, dbdir=None, testparm=None,
1442 quiet=False, verbose=False, use_xattrs=None, sambaopts=None, versionopts=None,
1443 dns_backend=None, use_ntvfs=False):
1445 if not os.path.exists(smbconf):
1446 raise CommandError("File %s does not exist" % smbconf)
1448 if testparm and not os.path.exists(testparm):
1449 raise CommandError("Testparm utility %s does not exist" % testparm)
1451 if dbdir and not os.path.exists(dbdir):
1452 raise CommandError("Directory %s does not exist" % dbdir)
1454 if not dbdir and not testparm:
1455 raise CommandError("Please specify either dbdir or testparm")
1457 logger = self.get_logger()
1459 logger.setLevel(logging.DEBUG)
1461 logger.setLevel(logging.WARNING)
1463 logger.setLevel(logging.INFO)
1465 if dbdir and testparm:
1466 logger.warning("both dbdir and testparm specified, ignoring dbdir.")
1469 lp = sambaopts.get_loadparm()
1471 s3conf = s3param.get_context()
1474 s3conf.set("realm", sambaopts.realm)
1476 if targetdir is not None:
1477 if not os.path.isdir(targetdir):
1481 if use_xattrs == "yes":
1483 elif use_xattrs == "auto" and not s3conf.get("posix:eadb"):
1485 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
1487 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
1490 samba.ntacls.setntacl(lp, tmpfile.name,
1491 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
1494 # FIXME: Don't catch all exceptions here
1495 logger.info("You are not root or your system do not support xattr, using tdb backend for attributes. "
1496 "If you intend to use this provision in production, rerun the script as root on a system supporting xattrs.")
1500 # Set correct default values from dbdir or testparm
1503 paths["state directory"] = dbdir
1504 paths["private dir"] = dbdir
1505 paths["lock directory"] = dbdir
1506 paths["smb passwd file"] = dbdir + "/smbpasswd"
1508 paths["state directory"] = get_testparm_var(testparm, smbconf, "state directory")
1509 paths["private dir"] = get_testparm_var(testparm, smbconf, "private dir")
1510 paths["smb passwd file"] = get_testparm_var(testparm, smbconf, "smb passwd file")
1511 paths["lock directory"] = get_testparm_var(testparm, smbconf, "lock directory")
1512 # "testparm" from Samba 3 < 3.4.x is not aware of the parameter
1513 # "state directory", instead make use of "lock directory"
1514 if len(paths["state directory"]) == 0:
1515 paths["state directory"] = paths["lock directory"]
1518 s3conf.set(p, paths[p])
1520 # load smb.conf parameters
1521 logger.info("Reading smb.conf")
1522 s3conf.load(smbconf)
1523 samba3 = Samba3(smbconf, s3conf)
1525 logger.info("Provisioning")
1526 upgrade_from_samba3(samba3, logger, targetdir, session_info=system_session(),
1527 useeadb=eadb, dns_backend=dns_backend, use_ntvfs=use_ntvfs)
1530 class cmd_domain_samba3upgrade(cmd_domain_classicupgrade):
1531 __doc__ = cmd_domain_classicupgrade.__doc__
1533 # This command is present for backwards compatibility only,
1534 # and should not be shown.
1538 class LocalDCCredentialsOptions(options.CredentialsOptions):
1539 def __init__(self, parser):
1540 options.CredentialsOptions.__init__(self, parser, special_name="local-dc")
1542 class DomainTrustCommand(Command):
1543 """List domain trusts."""
1546 Command.__init__(self)
1547 self.local_lp = None
1549 self.local_server = None
1550 self.local_binding_string = None
1551 self.local_creds = None
1553 self.remote_server = None
1554 self.remote_binding_string = None
1555 self.remote_creds = None
1557 WERR_OK = 0x00000000
1558 WERR_INVALID_FUNCTION = 0x00000001
1559 WERR_NERR_ACFNOTLOADED = 0x000008B3
1561 NT_STATUS_NOT_FOUND = 0xC0000225
1562 NT_STATUS_OBJECT_NAME_NOT_FOUND = 0xC0000034
1563 NT_STATUS_INVALID_PARAMETER = 0xC000000D
1564 NT_STATUS_INVALID_INFO_CLASS = 0xC0000003
1565 NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE = 0xC002002E
1567 def _uint32(self, v):
1568 return ctypes.c_uint32(v).value
1570 def check_runtime_error(self, runtime, val):
1574 err32 = self._uint32(runtime[0])
1580 class LocalRuntimeError(CommandError):
1581 def __init__(exception_self, self, runtime, message):
1582 err32 = self._uint32(runtime[0])
1584 msg = "LOCAL_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1585 self.local_server, message, err32, errstr)
1586 CommandError.__init__(exception_self, msg)
1588 class RemoteRuntimeError(CommandError):
1589 def __init__(exception_self, self, runtime, message):
1590 err32 = self._uint32(runtime[0])
1592 msg = "REMOTE_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1593 self.remote_server, message, err32, errstr)
1594 CommandError.__init__(exception_self, msg)
1596 class LocalLdbError(CommandError):
1597 def __init__(exception_self, self, ldb_error, message):
1598 errval = ldb_error[0]
1599 errstr = ldb_error[1]
1600 msg = "LOCAL_DC[%s]: %s - ERROR(%d) - %s" % (
1601 self.local_server, message, errval, errstr)
1602 CommandError.__init__(exception_self, msg)
1604 def setup_local_server(self, sambaopts, localdcopts):
1605 if self.local_server is not None:
1606 return self.local_server
1608 lp = sambaopts.get_loadparm()
1610 local_server = localdcopts.ipaddress
1611 if local_server is None:
1612 server_role = lp.server_role()
1613 if server_role != "ROLE_ACTIVE_DIRECTORY_DC":
1614 raise CommandError("Invalid server_role %s" % (server_role))
1615 local_server = lp.get('netbios name')
1616 local_transport = "ncalrpc"
1617 local_binding_options = ""
1618 local_binding_options += ",auth_type=ncalrpc_as_system"
1619 local_ldap_url = None
1622 local_transport = "ncacn_np"
1623 local_binding_options = ""
1624 local_ldap_url = "ldap://%s" % local_server
1625 local_creds = localdcopts.get_credentials(lp)
1629 self.local_server = local_server
1630 self.local_binding_string = "%s:%s[%s]" % (local_transport, local_server, local_binding_options)
1631 self.local_ldap_url = local_ldap_url
1632 self.local_creds = local_creds
1633 return self.local_server
1635 def new_local_lsa_connection(self):
1636 return lsa.lsarpc(self.local_binding_string, self.local_lp, self.local_creds)
1638 def new_local_netlogon_connection(self):
1639 return netlogon.netlogon(self.local_binding_string, self.local_lp, self.local_creds)
1641 def new_local_ldap_connection(self):
1642 return SamDB(url=self.local_ldap_url,
1643 session_info=system_session(),
1644 credentials=self.local_creds,
1647 def setup_remote_server(self, credopts, domain,
1649 require_writable=True):
1652 assert require_writable
1654 if self.remote_server is not None:
1655 return self.remote_server
1657 self.remote_server = "__unknown__remote_server__.%s" % domain
1658 assert self.local_server is not None
1660 remote_creds = credopts.get_credentials(self.local_lp)
1661 remote_server = credopts.ipaddress
1662 remote_binding_options = ""
1664 # TODO: we should also support NT4 domains
1665 # we could use local_netlogon.netr_DsRGetDCNameEx2() with the remote domain name
1666 # and delegate NBT or CLDAP to the local netlogon server
1668 remote_net = Net(remote_creds, self.local_lp, server=remote_server)
1669 remote_flags = nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS
1670 if require_writable:
1671 remote_flags |= nbt.NBT_SERVER_WRITABLE
1673 remote_flags |= nbt.NBT_SERVER_PDC
1674 remote_info = remote_net.finddc(flags=remote_flags, domain=domain, address=remote_server)
1676 raise CommandError("Failed to find a writeable DC for domain '%s'" % domain)
1678 nbt.NBT_SERVER_PDC: "PDC",
1679 nbt.NBT_SERVER_GC: "GC",
1680 nbt.NBT_SERVER_LDAP: "LDAP",
1681 nbt.NBT_SERVER_DS: "DS",
1682 nbt.NBT_SERVER_KDC: "KDC",
1683 nbt.NBT_SERVER_TIMESERV: "TIMESERV",
1684 nbt.NBT_SERVER_CLOSEST: "CLOSEST",
1685 nbt.NBT_SERVER_WRITABLE: "WRITABLE",
1686 nbt.NBT_SERVER_GOOD_TIMESERV: "GOOD_TIMESERV",
1687 nbt.NBT_SERVER_NDNC: "NDNC",
1688 nbt.NBT_SERVER_SELECT_SECRET_DOMAIN_6: "SELECT_SECRET_DOMAIN_6",
1689 nbt.NBT_SERVER_FULL_SECRET_DOMAIN_6: "FULL_SECRET_DOMAIN_6",
1690 nbt.NBT_SERVER_ADS_WEB_SERVICE: "ADS_WEB_SERVICE",
1691 nbt.NBT_SERVER_DS_8: "DS_8",
1692 nbt.NBT_SERVER_HAS_DNS_NAME: "HAS_DNS_NAME",
1693 nbt.NBT_SERVER_IS_DEFAULT_NC: "IS_DEFAULT_NC",
1694 nbt.NBT_SERVER_FOREST_ROOT: "FOREST_ROOT",
1696 server_type_string = self.generic_bitmap_to_string(flag_map,
1697 remote_info.server_type, names_only=True)
1698 self.outf.write("RemoteDC Netbios[%s] DNS[%s] ServerType[%s]\n" % (
1699 remote_info.pdc_name,
1700 remote_info.pdc_dns_name,
1701 server_type_string))
1703 self.remote_server = remote_info.pdc_dns_name
1704 self.remote_binding_string="ncacn_np:%s[%s]" % (self.remote_server, remote_binding_options)
1705 self.remote_creds = remote_creds
1706 return self.remote_server
1708 def new_remote_lsa_connection(self):
1709 return lsa.lsarpc(self.remote_binding_string, self.local_lp, self.remote_creds)
1711 def new_remote_netlogon_connection(self):
1712 return netlogon.netlogon(self.remote_binding_string, self.local_lp, self.remote_creds)
1714 def get_lsa_info(self, conn, policy_access):
1715 objectAttr = lsa.ObjectAttribute()
1716 objectAttr.sec_qos = lsa.QosInfo()
1718 policy = conn.OpenPolicy2(''.decode('utf-8'),
1719 objectAttr, policy_access)
1721 info = conn.QueryInfoPolicy2(policy, lsa.LSA_POLICY_INFO_DNS)
1723 return (policy, info)
1725 def get_netlogon_dc_info(self, conn, server):
1726 info = conn.netr_DsRGetDCNameEx2(server,
1727 None, 0, None, None, None,
1728 netlogon.DS_RETURN_DNS_NAME)
1731 def netr_DomainTrust_to_name(self, t):
1732 if t.trust_type == lsa.LSA_TRUST_TYPE_DOWNLEVEL:
1733 return t.netbios_name
1737 def netr_DomainTrust_to_type(self, a, t):
1739 primary_parent = None
1741 if _t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
1743 if not _t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1744 primary_parent = a[_t.parent_index]
1747 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1748 if t is primary_parent:
1751 if t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1754 parent = a[t.parent_index]
1755 if parent is primary:
1760 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1765 def netr_DomainTrust_to_transitive(self, t):
1766 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1769 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE:
1772 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1777 def netr_DomainTrust_to_direction(self, t):
1778 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND and \
1779 t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1782 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND:
1785 if t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1790 def generic_enum_to_string(self, e_dict, v, names_only=False):
1794 v32 = self._uint32(v)
1795 w = "__unknown__%08X__" % v32
1797 r = "0x%x (%s)" % (v, w)
1800 def generic_bitmap_to_string(self, b_dict, v, names_only=False):
1805 for b in sorted(b_dict.keys()):
1812 c32 = self._uint32(c)
1813 s += ["__unknown_%08X__" % c32]
1818 r = "0x%x (%s)" % (v, w)
1821 def trustType_string(self, v):
1823 lsa.LSA_TRUST_TYPE_DOWNLEVEL : "DOWNLEVEL",
1824 lsa.LSA_TRUST_TYPE_UPLEVEL : "UPLEVEL",
1825 lsa.LSA_TRUST_TYPE_MIT : "MIT",
1826 lsa.LSA_TRUST_TYPE_DCE : "DCE",
1828 return self.generic_enum_to_string(types, v)
1830 def trustDirection_string(self, v):
1832 lsa.LSA_TRUST_DIRECTION_INBOUND |
1833 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "BOTH",
1834 lsa.LSA_TRUST_DIRECTION_INBOUND : "INBOUND",
1835 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "OUTBOUND",
1837 return self.generic_enum_to_string(directions, v)
1839 def trustAttributes_string(self, v):
1841 lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE : "NON_TRANSITIVE",
1842 lsa.LSA_TRUST_ATTRIBUTE_UPLEVEL_ONLY : "UPLEVEL_ONLY",
1843 lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN : "QUARANTINED_DOMAIN",
1844 lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE : "FOREST_TRANSITIVE",
1845 lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION : "CROSS_ORGANIZATION",
1846 lsa.LSA_TRUST_ATTRIBUTE_WITHIN_FOREST : "WITHIN_FOREST",
1847 lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL : "TREAT_AS_EXTERNAL",
1848 lsa.LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION : "USES_RC4_ENCRYPTION",
1850 return self.generic_bitmap_to_string(attributes, v)
1852 def kerb_EncTypes_string(self, v):
1854 security.KERB_ENCTYPE_DES_CBC_CRC : "DES_CBC_CRC",
1855 security.KERB_ENCTYPE_DES_CBC_MD5 : "DES_CBC_MD5",
1856 security.KERB_ENCTYPE_RC4_HMAC_MD5 : "RC4_HMAC_MD5",
1857 security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96 : "AES128_CTS_HMAC_SHA1_96",
1858 security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96 : "AES256_CTS_HMAC_SHA1_96",
1859 security.KERB_ENCTYPE_FAST_SUPPORTED : "FAST_SUPPORTED",
1860 security.KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED : "COMPOUND_IDENTITY_SUPPORTED",
1861 security.KERB_ENCTYPE_CLAIMS_SUPPORTED : "CLAIMS_SUPPORTED",
1862 security.KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED : "RESOURCE_SID_COMPRESSION_DISABLED",
1864 return self.generic_bitmap_to_string(enctypes, v)
1866 def entry_tln_status(self, e_flags, ):
1868 return "Status[Enabled]"
1871 lsa.LSA_TLN_DISABLED_NEW : "Disabled-New",
1872 lsa.LSA_TLN_DISABLED_ADMIN : "Disabled",
1873 lsa.LSA_TLN_DISABLED_CONFLICT : "Disabled-Conflicting",
1875 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
1877 def entry_dom_status(self, e_flags):
1879 return "Status[Enabled]"
1882 lsa.LSA_SID_DISABLED_ADMIN : "Disabled-SID",
1883 lsa.LSA_SID_DISABLED_CONFLICT : "Disabled-SID-Conflicting",
1884 lsa.LSA_NB_DISABLED_ADMIN : "Disabled-NB",
1885 lsa.LSA_NB_DISABLED_CONFLICT : "Disabled-NB-Conflicting",
1887 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
1889 def write_forest_trust_info(self, fti, tln=None, collisions=None):
1891 tln_string = " TDO[%s]" % tln
1895 self.outf.write("Namespaces[%d]%s:\n" % (
1896 len(fti.entries), tln_string))
1898 for i in xrange(0, len(fti.entries)):
1902 collision_string = ""
1904 if collisions is not None:
1905 for c in collisions.entries:
1909 collision_string = " Collision[%s]" % (c.name.string)
1911 d = e.forest_trust_data
1912 if e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
1913 self.outf.write("TLN: %-32s DNS[*.%s]%s\n" % (
1914 self.entry_tln_status(flags),
1915 d.string, collision_string))
1916 elif e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
1917 self.outf.write("TLN_EX: %-29s DNS[*.%s]\n" % (
1919 elif e.type == lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
1920 self.outf.write("DOM: %-32s DNS[%s] Netbios[%s] SID[%s]%s\n" % (
1921 self.entry_dom_status(flags),
1922 d.dns_domain_name.string,
1923 d.netbios_domain_name.string,
1924 d.domain_sid, collision_string))
1927 class cmd_domain_trust_list(DomainTrustCommand):
1928 """List domain trusts."""
1930 synopsis = "%prog [options]"
1932 takes_optiongroups = {
1933 "sambaopts": options.SambaOptions,
1934 "versionopts": options.VersionOptions,
1935 "localdcopts": LocalDCCredentialsOptions,
1941 def run(self, sambaopts=None, versionopts=None, localdcopts=None):
1943 local_server = self.setup_local_server(sambaopts, localdcopts)
1945 local_netlogon = self.new_local_netlogon_connection()
1946 except RuntimeError as error:
1947 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
1950 local_netlogon_trusts = local_netlogon.netr_DsrEnumerateDomainTrusts(local_server,
1951 netlogon.NETR_TRUST_FLAG_IN_FOREST |
1952 netlogon.NETR_TRUST_FLAG_OUTBOUND |
1953 netlogon.NETR_TRUST_FLAG_INBOUND)
1954 except RuntimeError as error:
1955 if self.check_runtime_error(error, self.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
1956 # TODO: we could implement a fallback to lsa.EnumTrustDom()
1957 raise CommandError("LOCAL_DC[%s]: netr_DsrEnumerateDomainTrusts not supported." % (
1959 raise self.LocalRuntimeError(self, error, "netr_DsrEnumerateDomainTrusts failed")
1961 a = local_netlogon_trusts.array
1963 if t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
1965 self.outf.write("%-14s %-15s %-19s %s\n" % (
1966 "Type[%s]" % self.netr_DomainTrust_to_type(a, t),
1967 "Transitive[%s]" % self.netr_DomainTrust_to_transitive(t),
1968 "Direction[%s]" % self.netr_DomainTrust_to_direction(t),
1969 "Name[%s]" % self.netr_DomainTrust_to_name(t)))
1972 class cmd_domain_trust_show(DomainTrustCommand):
1973 """Show trusted domain details."""
1975 synopsis = "%prog NAME [options]"
1977 takes_optiongroups = {
1978 "sambaopts": options.SambaOptions,
1979 "versionopts": options.VersionOptions,
1980 "localdcopts": LocalDCCredentialsOptions,
1986 takes_args = ["domain"]
1988 def run(self, domain, sambaopts=None, versionopts=None, localdcopts=None):
1990 local_server = self.setup_local_server(sambaopts, localdcopts)
1992 local_lsa = self.new_local_lsa_connection()
1993 except RuntimeError as error:
1994 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
1997 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
1998 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
1999 except RuntimeError as error:
2000 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2002 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2003 local_lsa_info.name.string,
2004 local_lsa_info.dns_domain.string,
2005 local_lsa_info.sid))
2007 lsaString = lsa.String()
2008 lsaString.string = domain
2010 local_tdo_full = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2011 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2012 local_tdo_info = local_tdo_full.info_ex
2013 local_tdo_posix = local_tdo_full.posix_offset
2014 except RuntimeError as error:
2015 if self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2016 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2018 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(FULL_INFO) failed")
2021 local_tdo_enctypes = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2022 lsaString, lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES)
2023 except RuntimeError as error:
2024 if self.check_runtime_error(error, self.NT_STATUS_INVALID_PARAMETER):
2026 if self.check_runtime_error(error, self.NT_STATUS_INVALID_INFO_CLASS):
2029 if error is not None:
2030 raise self.LocalRuntimeError(self, error,
2031 "QueryTrustedDomainInfoByName(SUPPORTED_ENCRYPTION_TYPES) failed")
2033 local_tdo_enctypes = lsa.TrustDomainInfoSupportedEncTypes()
2034 local_tdo_enctypes.enc_types = 0
2037 local_tdo_forest = None
2038 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2039 local_tdo_forest = local_lsa.lsaRQueryForestTrustInformation(local_policy,
2040 lsaString, lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
2041 except RuntimeError as error:
2042 if self.check_runtime_error(error, self.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
2044 if self.check_runtime_error(error, self.NT_STATUS_NOT_FOUND):
2046 if error is not None:
2047 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation failed")
2049 local_tdo_forest = lsa.ForestTrustInformation()
2050 local_tdo_forest.count = 0
2051 local_tdo_forest.entries = []
2053 self.outf.write("TrusteDomain:\n\n");
2054 self.outf.write("NetbiosName: %s\n" % local_tdo_info.netbios_name.string)
2055 if local_tdo_info.netbios_name.string != local_tdo_info.domain_name.string:
2056 self.outf.write("DnsName: %s\n" % local_tdo_info.domain_name.string)
2057 self.outf.write("SID: %s\n" % local_tdo_info.sid)
2058 self.outf.write("Type: %s\n" % self.trustType_string(local_tdo_info.trust_type))
2059 self.outf.write("Direction: %s\n" % self.trustDirection_string(local_tdo_info.trust_direction))
2060 self.outf.write("Attributes: %s\n" % self.trustAttributes_string(local_tdo_info.trust_attributes))
2061 posix_offset_u32 = ctypes.c_uint32(local_tdo_posix.posix_offset).value
2062 posix_offset_i32 = ctypes.c_int32(local_tdo_posix.posix_offset).value
2063 self.outf.write("PosixOffset: 0x%08X (%d)\n" % (posix_offset_u32, posix_offset_i32))
2064 self.outf.write("kerb_EncTypes: %s\n" % self.kerb_EncTypes_string(local_tdo_enctypes.enc_types))
2066 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2067 self.write_forest_trust_info(local_tdo_forest,
2068 tln=local_tdo_info.domain_name.string)
2072 class cmd_domain_trust_create(DomainTrustCommand):
2073 """Create a domain or forest trust."""
2075 synopsis = "%prog DOMAIN [options]"
2077 takes_optiongroups = {
2078 "sambaopts": options.SambaOptions,
2079 "versionopts": options.VersionOptions,
2080 "credopts": options.CredentialsOptions,
2081 "localdcopts": LocalDCCredentialsOptions,
2085 Option("--type", type="choice", metavar="TYPE",
2086 choices=["external", "forest"],
2087 help="The type of the trust: 'external' or 'forest'.",
2089 default="external"),
2090 Option("--direction", type="choice", metavar="DIRECTION",
2091 choices=["incoming", "outgoing", "both"],
2092 help="The trust direction: 'incoming', 'outgoing' or 'both'.",
2093 dest='trust_direction',
2095 Option("--create-location", type="choice", metavar="LOCATION",
2096 choices=["local", "both"],
2097 help="Where to create the trusted domain object: 'local' or 'both'.",
2098 dest='create_location',
2100 Option("--cross-organisation", action="store_true",
2101 help="The related domains does not belong to the same organisation.",
2102 dest='cross_organisation',
2104 Option("--quarantined", type="choice", metavar="yes|no",
2105 choices=["yes", "no", None],
2106 help="Special SID filtering rules are applied to the trust. "
2107 "With --type=external the default is yes. "
2108 "With --type=forest the default is no.",
2109 dest='quarantined_arg',
2111 Option("--not-transitive", action="store_true",
2112 help="The forest trust is not transitive.",
2113 dest='not_transitive',
2115 Option("--treat-as-external", action="store_true",
2116 help="The treat the forest trust as external.",
2117 dest='treat_as_external',
2119 Option("--no-aes-keys", action="store_false",
2120 help="The trust uses aes kerberos keys.",
2121 dest='use_aes_keys',
2123 Option("--skip-validation", action="store_false",
2124 help="Skip validation of the trust.",
2129 takes_args = ["domain"]
2131 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2132 trust_type=None, trust_direction=None, create_location=None,
2133 cross_organisation=False, quarantined_arg=None,
2134 not_transitive=False, treat_as_external=False,
2135 use_aes_keys=False, validate=True):
2137 lsaString = lsa.String()
2140 if quarantined_arg is None:
2141 if trust_type == 'external':
2143 elif quarantined_arg == 'yes':
2146 if trust_type != 'forest':
2148 raise CommandError("--not-transitive requires --type=forest")
2149 if treat_as_external:
2150 raise CommandError("--treat-as-external requires --type=forest")
2154 enc_types = lsa.TrustDomainInfoSupportedEncTypes()
2155 enc_types.enc_types = security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96
2156 enc_types.enc_types |= security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96
2158 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2159 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2160 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2162 local_trust_info = lsa.TrustDomainInfoInfoEx()
2163 local_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2164 local_trust_info.trust_direction = 0
2165 if trust_direction == "both":
2166 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2167 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2168 elif trust_direction == "incoming":
2169 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2170 elif trust_direction == "outgoing":
2171 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2172 local_trust_info.trust_attributes = 0
2173 if cross_organisation:
2174 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2176 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2177 if trust_type == "forest":
2178 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2180 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2181 if treat_as_external:
2182 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2184 def get_password(name):
2187 if password is not None and password is not '':
2189 password = getpass("New %s Password: " % name)
2190 passwordverify = getpass("Retype %s Password: " % name)
2191 if not password == passwordverify:
2193 self.outf.write("Sorry, passwords do not match.\n")
2195 def string_to_array(string):
2196 blob = [0] * len(string)
2198 for i in range(len(string)):
2199 blob[i] = ord(string[i])
2203 incoming_secret = None
2204 outgoing_secret = None
2205 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2206 if create_location == "local":
2207 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2208 incoming_password = get_password("Incoming Trust")
2209 incoming_secret = string_to_array(incoming_password.encode('utf-16-le'))
2210 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2211 outgoing_password = get_password("Outgoing Trust")
2212 outgoing_secret = string_to_array(outgoing_password.encode('utf-16-le'))
2214 remote_trust_info = None
2216 # We use 240 random bytes.
2217 # Windows uses 28 or 240 random bytes. I guess it's
2218 # based on the trust type external vs. forest.
2220 # The initial trust password can be up to 512 bytes
2221 # while the versioned passwords used for periodic updates
2222 # can only be up to 498 bytes, as netr_ServerPasswordSet2()
2223 # needs to pass the NL_PASSWORD_VERSION structure within the
2224 # 512 bytes and a 2 bytes confounder is required.
2226 def random_trust_secret(length, use_aes_keys=True):
2227 secret = [0] * length
2229 pw1 = samba.generate_random_password(length/2, length/2)
2230 if not use_aes_keys:
2231 # With arcfour-hmac-md5 we have to use valid utf16
2232 # in order to generate the correct pre-auth key
2233 # based on a utf8 password.
2235 # We can remove this once our client libraries
2236 # support using the correct NTHASH.
2237 return string_to_array(pw1.encode('utf-16-le'))
2239 # We mix characters from generate_random_password
2240 # with random numbers from random.randint()
2241 for i in range(len(secret)):
2243 secret[i] = ord(pw1[i])
2245 secret[i] = random.randint(0, 255)
2249 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2250 incoming_secret = random_trust_secret(240, use_aes_keys=use_aes_keys)
2251 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2252 outgoing_secret = random_trust_secret(240, use_aes_keys=use_aes_keys)
2254 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2255 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2257 remote_trust_info = lsa.TrustDomainInfoInfoEx()
2258 remote_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2259 remote_trust_info.trust_direction = 0
2260 if trust_direction == "both":
2261 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2262 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2263 elif trust_direction == "incoming":
2264 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2265 elif trust_direction == "outgoing":
2266 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2267 remote_trust_info.trust_attributes = 0
2268 if cross_organisation:
2269 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2271 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2272 if trust_type == "forest":
2273 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2275 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2276 if treat_as_external:
2277 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2279 local_server = self.setup_local_server(sambaopts, localdcopts)
2281 local_lsa = self.new_local_lsa_connection()
2282 except RuntimeError as error:
2283 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2286 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2287 except RuntimeError as error:
2288 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2290 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2291 local_lsa_info.name.string,
2292 local_lsa_info.dns_domain.string,
2293 local_lsa_info.sid))
2296 remote_server = self.setup_remote_server(credopts, domain)
2297 except RuntimeError as error:
2298 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2301 remote_lsa = self.new_remote_lsa_connection()
2302 except RuntimeError as error:
2303 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2306 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2307 except RuntimeError as error:
2308 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2310 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2311 remote_lsa_info.name.string,
2312 remote_lsa_info.dns_domain.string,
2313 remote_lsa_info.sid))
2315 local_trust_info.domain_name.string = remote_lsa_info.dns_domain.string
2316 local_trust_info.netbios_name.string = remote_lsa_info.name.string
2317 local_trust_info.sid = remote_lsa_info.sid
2319 if remote_trust_info:
2320 remote_trust_info.domain_name.string = local_lsa_info.dns_domain.string
2321 remote_trust_info.netbios_name.string = local_lsa_info.name.string
2322 remote_trust_info.sid = local_lsa_info.sid
2325 lsaString.string = local_trust_info.domain_name.string
2326 local_old_netbios = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2327 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2328 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2329 except RuntimeError as error:
2330 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2331 raise self.LocalRuntimeError(self, error,
2332 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2336 lsaString.string = local_trust_info.netbios_name.string
2337 local_old_dns = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2338 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2339 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2340 except RuntimeError as error:
2341 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2342 raise self.LocalRuntimeError(self, error,
2343 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2346 if remote_trust_info:
2348 lsaString.string = remote_trust_info.domain_name.string
2349 remote_old_netbios = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2350 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2351 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2352 except RuntimeError as error:
2353 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2354 raise self.RemoteRuntimeError(self, error,
2355 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2359 lsaString.string = remote_trust_info.netbios_name.string
2360 remote_old_dns = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2361 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2362 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2363 except RuntimeError as error:
2364 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2365 raise self.RemoteRuntimeError(self, error,
2366 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2370 local_netlogon = self.new_local_netlogon_connection()
2371 except RuntimeError as error:
2372 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2375 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
2376 except RuntimeError as error:
2377 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
2379 if remote_trust_info:
2381 remote_netlogon = self.new_remote_netlogon_connection()
2382 except RuntimeError as error:
2383 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2386 remote_netlogon_info = self.get_netlogon_dc_info(remote_netlogon, remote_server)
2387 except RuntimeError as error:
2388 raise self.RemoteRuntimeError(self, error, "failed to get netlogon dc info")
2390 def arcfour_encrypt(key, data):
2391 from Crypto.Cipher import ARC4
2393 return c.encrypt(data)
2395 def generate_AuthInOutBlob(secret, update_time):
2397 blob = drsblobs.trustAuthInOutBlob()
2402 clear = drsblobs.AuthInfoClear()
2403 clear.size = len(secret)
2404 clear.password = secret
2406 info = drsblobs.AuthenticationInformation()
2407 info.LastUpdateTime = samba.unix2nttime(update_time)
2408 info.AuthType = lsa.TRUST_AUTH_TYPE_CLEAR
2409 info.AuthInfo = clear
2411 array = drsblobs.AuthenticationInformationArray()
2413 array.array = [info]
2415 blob = drsblobs.trustAuthInOutBlob()
2417 blob.current = array
2421 def generate_AuthInfoInternal(session_key, incoming=None, outgoing=None):
2422 confounder = [0] * 512
2423 for i in range(len(confounder)):
2424 confounder[i] = random.randint(0, 255)
2426 trustpass = drsblobs.trustDomainPasswords()
2428 trustpass.confounder = confounder
2429 trustpass.outgoing = outgoing
2430 trustpass.incoming = incoming
2432 trustpass_blob = ndr_pack(trustpass)
2434 encrypted_trustpass = arcfour_encrypt(session_key, trustpass_blob)
2436 auth_blob = lsa.DATA_BUF2()
2437 auth_blob.size = len(encrypted_trustpass)
2438 auth_blob.data = string_to_array(encrypted_trustpass)
2440 auth_info = lsa.TrustDomainInfoAuthInfoInternal()
2441 auth_info.auth_blob = auth_blob
2445 update_time = samba.current_unix_time()
2446 incoming_blob = generate_AuthInOutBlob(incoming_secret, update_time)
2447 outgoing_blob = generate_AuthInOutBlob(outgoing_secret, update_time)
2449 local_tdo_handle = None
2450 remote_tdo_handle = None
2452 local_auth_info = generate_AuthInfoInternal(local_lsa.session_key,
2453 incoming=incoming_blob,
2454 outgoing=outgoing_blob)
2455 if remote_trust_info:
2456 remote_auth_info = generate_AuthInfoInternal(remote_lsa.session_key,
2457 incoming=outgoing_blob,
2458 outgoing=incoming_blob)
2461 if remote_trust_info:
2462 self.outf.write("Creating remote TDO.\n")
2463 current_request = { "location": "remote", "name": "CreateTrustedDomainEx2"}
2464 remote_tdo_handle = remote_lsa.CreateTrustedDomainEx2(remote_policy,
2467 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2468 self.outf.write("Remote TDO created.\n")
2470 self.outf.write("Setting supported encryption types on remote TDO.\n")
2471 current_request = { "location": "remote", "name": "SetInformationTrustedDomain"}
2472 remote_lsa.SetInformationTrustedDomain(remote_tdo_handle,
2473 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2476 self.outf.write("Creating local TDO.\n")
2477 current_request = { "location": "local", "name": "CreateTrustedDomainEx2"}
2478 local_tdo_handle = local_lsa.CreateTrustedDomainEx2(local_policy,
2481 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2482 self.outf.write("Local TDO created\n")
2484 self.outf.write("Setting supported encryption types on local TDO.\n")
2485 current_request = { "location": "local", "name": "SetInformationTrustedDomain"}
2486 local_lsa.SetInformationTrustedDomain(local_tdo_handle,
2487 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2489 except RuntimeError as error:
2490 self.outf.write("Error: %s failed %sly - cleaning up\n" % (
2491 current_request['name'], current_request['location']))
2492 if remote_tdo_handle:
2493 self.outf.write("Deleting remote TDO.\n")
2494 remote_lsa.DeleteObject(remote_tdo_handle)
2495 remote_tdo_handle = None
2496 if local_tdo_handle:
2497 self.outf.write("Deleting local TDO.\n")
2498 local_lsa.DeleteObject(local_tdo_handle)
2499 local_tdo_handle = None
2500 if current_request['location'] is "remote":
2501 raise self.RemoteRuntimeError(self, error, "%s" % (
2502 current_request['name']))
2503 raise self.LocalRuntimeError(self, error, "%s" % (
2504 current_request['name']))
2507 if local_trust_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2508 self.outf.write("Setup local forest trust information...\n")
2510 # get all information about the remote trust
2511 # this triggers netr_GetForestTrustInformation to the remote domain
2512 # and lsaRSetForestTrustInformation() locally, but new top level
2513 # names are disabled by default.
2514 local_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
2515 remote_lsa_info.dns_domain.string,
2516 netlogon.DS_GFTI_UPDATE_TDO)
2517 except RuntimeError as error:
2518 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2521 # here we try to enable all top level names
2522 local_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
2523 remote_lsa_info.dns_domain,
2524 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2527 except RuntimeError as error:
2528 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2530 self.write_forest_trust_info(local_forest_info,
2531 tln=remote_lsa_info.dns_domain.string,
2532 collisions=local_forest_collision)
2534 if remote_trust_info:
2535 self.outf.write("Setup remote forest trust information...\n")
2537 # get all information about the local trust (from the perspective of the remote domain)
2538 # this triggers netr_GetForestTrustInformation to our domain.
2539 # and lsaRSetForestTrustInformation() remotely, but new top level
2540 # names are disabled by default.
2541 remote_forest_info = remote_netlogon.netr_DsRGetForestTrustInformation(remote_netlogon_info.dc_unc,
2542 local_lsa_info.dns_domain.string,
2543 netlogon.DS_GFTI_UPDATE_TDO)
2544 except RuntimeError as error:
2545 raise self.RemoteRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2548 # here we try to enable all top level names
2549 remote_forest_collision = remote_lsa.lsaRSetForestTrustInformation(remote_policy,
2550 local_lsa_info.dns_domain,
2551 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2554 except RuntimeError as error:
2555 raise self.RemoteRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2557 self.write_forest_trust_info(remote_forest_info,
2558 tln=local_lsa_info.dns_domain.string,
2559 collisions=remote_forest_collision)
2561 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2562 self.outf.write("Validating outgoing trust...\n")
2564 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_netlogon_info.dc_unc,
2565 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2567 remote_lsa_info.dns_domain.string)
2568 except RuntimeError as error:
2569 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2571 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2572 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2574 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2575 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2576 local_trust_verify.trusted_dc_name,
2577 local_trust_verify.tc_connection_status[1],
2578 local_trust_verify.pdc_connection_status[1])
2580 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2581 local_trust_verify.trusted_dc_name,
2582 local_trust_verify.tc_connection_status[1],
2583 local_trust_verify.pdc_connection_status[1])
2585 if local_trust_status != self.WERR_OK or local_conn_status != self.WERR_OK:
2586 raise CommandError(local_validation)
2588 self.outf.write("OK: %s\n" % local_validation)
2590 if remote_trust_info:
2591 if remote_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2592 self.outf.write("Validating incoming trust...\n")
2594 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_netlogon_info.dc_unc,
2595 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2597 local_lsa_info.dns_domain.string)
2598 except RuntimeError as error:
2599 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2601 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2602 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2604 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2605 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2606 remote_trust_verify.trusted_dc_name,
2607 remote_trust_verify.tc_connection_status[1],
2608 remote_trust_verify.pdc_connection_status[1])
2610 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2611 remote_trust_verify.trusted_dc_name,
2612 remote_trust_verify.tc_connection_status[1],
2613 remote_trust_verify.pdc_connection_status[1])
2615 if remote_trust_status != self.WERR_OK or remote_conn_status != self.WERR_OK:
2616 raise CommandError(remote_validation)
2618 self.outf.write("OK: %s\n" % remote_validation)
2620 if remote_tdo_handle is not None:
2622 remote_lsa.Close(remote_tdo_handle)
2623 except RuntimeError as error:
2625 remote_tdo_handle = None
2626 if local_tdo_handle is not None:
2628 local_lsa.Close(local_tdo_handle)
2629 except RuntimeError as error:
2631 local_tdo_handle = None
2633 self.outf.write("Success.\n")
2636 class cmd_domain_trust_delete(DomainTrustCommand):
2637 """Delete a domain trust."""
2639 synopsis = "%prog DOMAIN [options]"
2641 takes_optiongroups = {
2642 "sambaopts": options.SambaOptions,
2643 "versionopts": options.VersionOptions,
2644 "credopts": options.CredentialsOptions,
2645 "localdcopts": LocalDCCredentialsOptions,
2649 Option("--delete-location", type="choice", metavar="LOCATION",
2650 choices=["local", "both"],
2651 help="Where to delete the trusted domain object: 'local' or 'both'.",
2652 dest='delete_location',
2656 takes_args = ["domain"]
2658 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2659 delete_location=None):
2661 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2662 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2663 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2665 if delete_location == "local":
2666 remote_policy_access = None
2668 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2669 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2670 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2672 local_server = self.setup_local_server(sambaopts, localdcopts)
2674 local_lsa = self.new_local_lsa_connection()
2675 except RuntimeError as error:
2676 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2679 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2680 except RuntimeError as error:
2681 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2683 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2684 local_lsa_info.name.string,
2685 local_lsa_info.dns_domain.string,
2686 local_lsa_info.sid))
2688 local_tdo_info = None
2689 local_tdo_handle = None
2690 remote_tdo_info = None
2691 remote_tdo_handle = None
2693 lsaString = lsa.String()
2695 lsaString.string = domain
2696 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2697 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2698 except RuntimeError as error:
2699 if self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2700 raise CommandError("Failed to find trust for domain '%s'" % domain)
2701 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2704 if remote_policy_access is not None:
2706 remote_server = self.setup_remote_server(credopts, domain)
2707 except RuntimeError as error:
2708 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2711 remote_lsa = self.new_remote_lsa_connection()
2712 except RuntimeError as error:
2713 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2716 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2717 except RuntimeError as error:
2718 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2720 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2721 remote_lsa_info.name.string,
2722 remote_lsa_info.dns_domain.string,
2723 remote_lsa_info.sid))
2725 if remote_lsa_info.sid != local_tdo_info.sid or \
2726 remote_lsa_info.name.string != local_tdo_info.netbios_name.string or \
2727 remote_lsa_info.dns_domain.string != local_tdo_info.domain_name.string:
2728 raise CommandError("LocalTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2729 local_tdo_info.netbios_name.string,
2730 local_tdo_info.domain_name.string,
2731 local_tdo_info.sid))
2734 lsaString.string = local_lsa_info.dns_domain.string
2735 remote_tdo_info = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2736 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2737 except RuntimeError as error:
2738 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2739 raise self.RemoteRuntimeError(self, error, "QueryTrustedDomainInfoByName(%s)" % (
2743 if remote_tdo_info is not None:
2744 if local_lsa_info.sid != remote_tdo_info.sid or \
2745 local_lsa_info.name.string != remote_tdo_info.netbios_name.string or \
2746 local_lsa_info.dns_domain.string != remote_tdo_info.domain_name.string:
2747 raise CommandError("RemoteTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2748 remote_tdo_info.netbios_name.string,
2749 remote_tdo_info.domain_name.string,
2750 remote_tdo_info.sid))
2752 if local_tdo_info is not None:
2754 lsaString.string = local_tdo_info.domain_name.string
2755 local_tdo_handle = local_lsa.OpenTrustedDomainByName(local_policy,
2757 security.SEC_STD_DELETE)
2758 except RuntimeError as error:
2759 raise self.LocalRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2762 local_lsa.DeleteObject(local_tdo_handle)
2763 local_tdo_handle = None
2765 if remote_tdo_info is not None:
2767 lsaString.string = remote_tdo_info.domain_name.string
2768 remote_tdo_handle = remote_lsa.OpenTrustedDomainByName(remote_policy,
2770 security.SEC_STD_DELETE)
2771 except RuntimeError as error:
2772 raise self.RemoteRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2775 if remote_tdo_handle is not None:
2777 remote_lsa.DeleteObject(remote_tdo_handle)
2778 remote_tdo_handle = None
2779 self.outf.write("RemoteTDO deleted.\n")
2780 except RuntimeError as error:
2781 self.outf.write("%s\n" % self.RemoteRuntimeError(self, error, "DeleteObject() failed"))
2783 if local_tdo_handle is not None:
2785 local_lsa.DeleteObject(local_tdo_handle)
2786 local_tdo_handle = None
2787 self.outf.write("LocalTDO deleted.\n")
2788 except RuntimeError as error:
2789 self.outf.write("%s\n" % self.LocalRuntimeError(self, error, "DeleteObject() failed"))
2793 class cmd_domain_trust_validate(DomainTrustCommand):
2794 """Validate a domain trust."""
2796 synopsis = "%prog DOMAIN [options]"
2798 takes_optiongroups = {
2799 "sambaopts": options.SambaOptions,
2800 "versionopts": options.VersionOptions,
2801 "credopts": options.CredentialsOptions,
2802 "localdcopts": LocalDCCredentialsOptions,
2806 Option("--validate-location", type="choice", metavar="LOCATION",
2807 choices=["local", "both"],
2808 help="Where to validate the trusted domain object: 'local' or 'both'.",
2809 dest='validate_location',
2813 takes_args = ["domain"]
2815 def run(self, domain, sambaopts=None, versionopts=None, credopts=None, localdcopts=None,
2816 validate_location=None):
2818 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2820 local_server = self.setup_local_server(sambaopts, localdcopts)
2822 local_lsa = self.new_local_lsa_connection()
2823 except RuntimeError as error:
2824 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2827 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2828 except RuntimeError as error:
2829 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2831 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2832 local_lsa_info.name.string,
2833 local_lsa_info.dns_domain.string,
2834 local_lsa_info.sid))
2837 lsaString = lsa.String()
2838 lsaString.string = domain
2839 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2840 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2841 except RuntimeError as error:
2842 if self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2843 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2845 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
2847 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
2848 local_tdo_info.netbios_name.string,
2849 local_tdo_info.domain_name.string,
2850 local_tdo_info.sid))
2853 local_netlogon = self.new_local_netlogon_connection()
2854 except RuntimeError as error:
2855 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2858 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_server,
2859 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2861 local_tdo_info.domain_name.string)
2862 except RuntimeError as error:
2863 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2865 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2866 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2868 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2869 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2870 local_trust_verify.trusted_dc_name,
2871 local_trust_verify.tc_connection_status[1],
2872 local_trust_verify.pdc_connection_status[1])
2874 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2875 local_trust_verify.trusted_dc_name,
2876 local_trust_verify.tc_connection_status[1],
2877 local_trust_verify.pdc_connection_status[1])
2879 if local_trust_status != self.WERR_OK or local_conn_status != self.WERR_OK:
2880 raise CommandError(local_validation)
2882 self.outf.write("OK: %s\n" % local_validation)
2885 server = local_trust_verify.trusted_dc_name.replace('\\', '')
2886 domain_and_server = "%s\\%s" % (local_tdo_info.domain_name.string, server)
2887 local_trust_rediscover = local_netlogon.netr_LogonControl2Ex(local_server,
2888 netlogon.NETLOGON_CONTROL_REDISCOVER,
2891 except RuntimeError as error:
2892 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
2894 local_conn_status = self._uint32(local_trust_rediscover.tc_connection_status[0])
2895 local_rediscover = "LocalRediscover: DC[%s] CONNECTION[%s]" % (
2896 local_trust_rediscover.trusted_dc_name,
2897 local_trust_rediscover.tc_connection_status[1])
2899 if local_conn_status != self.WERR_OK:
2900 raise CommandError(local_rediscover)
2902 self.outf.write("OK: %s\n" % local_rediscover)
2904 if validate_location != "local":
2906 remote_server = self.setup_remote_server(credopts, domain, require_pdc=False)
2907 except RuntimeError as error:
2908 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2911 remote_netlogon = self.new_remote_netlogon_connection()
2912 except RuntimeError as error:
2913 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2916 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_server,
2917 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2919 local_lsa_info.dns_domain.string)
2920 except RuntimeError as error:
2921 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2923 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2924 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2926 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2927 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2928 remote_trust_verify.trusted_dc_name,
2929 remote_trust_verify.tc_connection_status[1],
2930 remote_trust_verify.pdc_connection_status[1])
2932 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2933 remote_trust_verify.trusted_dc_name,
2934 remote_trust_verify.tc_connection_status[1],
2935 remote_trust_verify.pdc_connection_status[1])
2937 if remote_trust_status != self.WERR_OK or remote_conn_status != self.WERR_OK:
2938 raise CommandError(remote_validation)
2940 self.outf.write("OK: %s\n" % remote_validation)
2943 server = remote_trust_verify.trusted_dc_name.replace('\\', '')
2944 domain_and_server = "%s\\%s" % (local_lsa_info.dns_domain.string, server)
2945 remote_trust_rediscover = remote_netlogon.netr_LogonControl2Ex(remote_server,
2946 netlogon.NETLOGON_CONTROL_REDISCOVER,
2949 except RuntimeError as error:
2950 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
2952 remote_conn_status = self._uint32(remote_trust_rediscover.tc_connection_status[0])
2954 remote_rediscover = "RemoteRediscover: DC[%s] CONNECTION[%s]" % (
2955 remote_trust_rediscover.trusted_dc_name,
2956 remote_trust_rediscover.tc_connection_status[1])
2958 if remote_conn_status != self.WERR_OK:
2959 raise CommandError(remote_rediscover)
2961 self.outf.write("OK: %s\n" % remote_rediscover)
2965 class cmd_domain_trust_namespaces(DomainTrustCommand):
2966 """Manage forest trust namespaces."""
2968 synopsis = "%prog [DOMAIN] [options]"
2970 takes_optiongroups = {
2971 "sambaopts": options.SambaOptions,
2972 "versionopts": options.VersionOptions,
2973 "localdcopts": LocalDCCredentialsOptions,
2977 Option("--refresh", type="choice", metavar="check|store",
2978 choices=["check", "store", None],
2979 help="List and maybe store refreshed forest trust information: 'check' or 'store'.",
2982 Option("--enable-all", action="store_true",
2983 help="Try to update disabled entries, not allowed with --refresh=check.",
2986 Option("--enable-tln", action="append", metavar='DNSDOMAIN',
2987 help="Enable a top level name entry. Can be specified multiple times.",
2990 Option("--disable-tln", action="append", metavar='DNSDOMAIN',
2991 help="Disable a top level name entry. Can be specified multiple times.",
2994 Option("--add-tln-ex", action="append", metavar='DNSDOMAIN',
2995 help="Add a top level exclusion entry. Can be specified multiple times.",
2998 Option("--delete-tln-ex", action="append", metavar='DNSDOMAIN',
2999 help="Delete a top level exclusion entry. Can be specified multiple times.",
3000 dest='delete_tln_ex',
3002 Option("--enable-nb", action="append", metavar='NETBIOSDOMAIN',
3003 help="Enable a netbios name in a domain entry. Can be specified multiple times.",
3006 Option("--disable-nb", action="append", metavar='NETBIOSDOMAIN',
3007 help="Disable a netbios name in a domain entry. Can be specified multiple times.",
3010 Option("--enable-sid", action="append", metavar='DOMAINSID',
3011 help="Enable a SID in a domain entry. Can be specified multiple times.",
3012 dest='enable_sid_str',
3014 Option("--disable-sid", action="append", metavar='DOMAINSID',
3015 help="Disable a SID in a domain entry. Can be specified multiple times.",
3016 dest='disable_sid_str',
3018 Option("--add-upn-suffix", action="append", metavar='DNSDOMAIN',
3019 help="Add a new uPNSuffixes attribute for the local forest. Can be specified multiple times.",
3022 Option("--delete-upn-suffix", action="append", metavar='DNSDOMAIN',
3023 help="Delete an existing uPNSuffixes attribute of the local forest. Can be specified multiple times.",
3026 Option("--add-spn-suffix", action="append", metavar='DNSDOMAIN',
3027 help="Add a new msDS-SPNSuffixes attribute for the local forest. Can be specified multiple times.",
3030 Option("--delete-spn-suffix", action="append", metavar='DNSDOMAIN',
3031 help="Delete an existing msDS-SPNSuffixes attribute of the local forest. Can be specified multiple times.",
3036 takes_args = ["domain?"]
3038 def run(self, domain=None, sambaopts=None, localdcopts=None, versionopts=None,
3039 refresh=None, enable_all=False,
3040 enable_tln=[], disable_tln=[], add_tln_ex=[], delete_tln_ex=[],
3041 enable_sid_str=[], disable_sid_str=[], enable_nb=[], disable_nb=[],
3042 add_upn=[], delete_upn=[], add_spn=[], delete_spn=[]):
3044 require_update = False
3047 if refresh == "store":
3048 raise CommandError("--refresh=%s not allowed without DOMAIN" % refresh)
3051 raise CommandError("--enable-all not allowed without DOMAIN")
3053 if len(enable_tln) > 0:
3054 raise CommandError("--enable-tln not allowed without DOMAIN")
3055 if len(disable_tln) > 0:
3056 raise CommandError("--disable-tln not allowed without DOMAIN")
3058 if len(add_tln_ex) > 0:
3059 raise CommandError("--add-tln-ex not allowed without DOMAIN")
3060 if len(delete_tln_ex) > 0:
3061 raise CommandError("--delete-tln-ex not allowed without DOMAIN")
3063 if len(enable_nb) > 0:
3064 raise CommandError("--enable-nb not allowed without DOMAIN")
3065 if len(disable_nb) > 0:
3066 raise CommandError("--disable-nb not allowed without DOMAIN")
3068 if len(enable_sid_str) > 0:
3069 raise CommandError("--enable-sid not allowed without DOMAIN")
3070 if len(disable_sid_str) > 0:
3071 raise CommandError("--disable-sid not allowed without DOMAIN")
3073 if len(add_upn) > 0:
3075 if not n.startswith("*."):
3077 raise CommandError("value[%s] specified for --add-upn-suffix should not include with '*.'" % n)
3078 require_update = True
3079 if len(delete_upn) > 0:
3080 for n in delete_upn:
3081 if not n.startswith("*."):
3083 raise CommandError("value[%s] specified for --delete-upn-suffix should not include with '*.'" % n)
3084 require_update = True
3086 for d in delete_upn:
3087 if a.lower() != d.lower():
3089 raise CommandError("value[%s] specified for --add-upn-suffix and --delete-upn-suffix" % a)
3091 if len(add_spn) > 0:
3093 if not n.startswith("*."):
3095 raise CommandError("value[%s] specified for --add-spn-suffix should not include with '*.'" % n)
3096 require_update = True
3097 if len(delete_spn) > 0:
3098 for n in delete_spn:
3099 if not n.startswith("*."):
3101 raise CommandError("value[%s] specified for --delete-spn-suffix should not include with '*.'" % n)
3102 require_update = True
3104 for d in delete_spn:
3105 if a.lower() != d.lower():
3107 raise CommandError("value[%s] specified for --add-spn-suffix and --delete-spn-suffix" % a)
3109 if len(add_upn) > 0:
3110 raise CommandError("--add-upn-suffix not allowed together with DOMAIN")
3111 if len(delete_upn) > 0:
3112 raise CommandError("--delete-upn-suffix not allowed together with DOMAIN")
3113 if len(add_spn) > 0:
3114 raise CommandError("--add-spn-suffix not allowed together with DOMAIN")
3115 if len(delete_spn) > 0:
3116 raise CommandError("--delete-spn-suffix not allowed together with DOMAIN")
3118 if refresh is not None:
3119 if refresh == "store":
3120 require_update = True
3122 if enable_all and refresh != "store":
3123 raise CommandError("--enable-all not allowed together with --refresh=%s" % refresh)
3125 if len(enable_tln) > 0:
3126 raise CommandError("--enable-tln not allowed together with --refresh")
3127 if len(disable_tln) > 0:
3128 raise CommandError("--disable-tln not allowed together with --refresh")
3130 if len(add_tln_ex) > 0:
3131 raise CommandError("--add-tln-ex not allowed together with --refresh")
3132 if len(delete_tln_ex) > 0:
3133 raise CommandError("--delete-tln-ex not allowed together with --refresh")
3135 if len(enable_nb) > 0:
3136 raise CommandError("--enable-nb not allowed together with --refresh")
3137 if len(disable_nb) > 0:
3138 raise CommandError("--disable-nb not allowed together with --refresh")
3140 if len(enable_sid_str) > 0:
3141 raise CommandError("--enable-sid not allowed together with --refresh")
3142 if len(disable_sid_str) > 0:
3143 raise CommandError("--disable-sid not allowed together with --refresh")
3146 require_update = True
3148 if len(enable_tln) > 0:
3149 raise CommandError("--enable-tln not allowed together with --enable-all")
3151 if len(enable_nb) > 0:
3152 raise CommandError("--enable-nb not allowed together with --enable-all")
3154 if len(enable_sid) > 0:
3155 raise CommandError("--enable-sid not allowed together with --enable-all")
3157 if len(enable_tln) > 0:
3158 require_update = True
3159 if len(disable_tln) > 0:
3160 require_update = True
3161 for e in enable_tln:
3162 for d in disable_tln:
3163 if e.lower() != d.lower():
3165 raise CommandError("value[%s] specified for --enable-tln and --disable-tln" % e)
3167 if len(add_tln_ex) > 0:
3168 for n in add_tln_ex:
3169 if not n.startswith("*."):
3171 raise CommandError("value[%s] specified for --add-tln-ex should not include with '*.'" % n)
3172 require_update = True
3173 if len(delete_tln_ex) > 0:
3174 for n in delete_tln_ex:
3175 if not n.startswith("*."):
3177 raise CommandError("value[%s] specified for --delete-tln-ex should not include with '*.'" % n)
3178 require_update = True
3179 for a in add_tln_ex:
3180 for d in delete_tln_ex:
3181 if a.lower() != d.lower():
3183 raise CommandError("value[%s] specified for --add-tln-ex and --delete-tln-ex" % a)
3185 if len(enable_nb) > 0:
3186 require_update = True
3187 if len(disable_nb) > 0:
3188 require_update = True
3190 for d in disable_nb:
3191 if e.upper() != d.upper():
3193 raise CommandError("value[%s] specified for --enable-nb and --disable-nb" % e)
3196 for s in enable_sid_str:
3198 sid = security.dom_sid(s)
3199 except TypeError as error:
3200 raise CommandError("value[%s] specified for --enable-sid is not a valid SID" % s)
3201 enable_sid.append(sid)
3203 for s in disable_sid_str:
3205 sid = security.dom_sid(s)
3206 except TypeError as error:
3207 raise CommandError("value[%s] specified for --disable-sid is not a valid SID" % s)
3208 disable_sid.append(sid)
3209 if len(enable_sid) > 0:
3210 require_update = True
3211 if len(disable_sid) > 0:
3212 require_update = True
3213 for e in enable_sid:
3214 for d in disable_sid:
3217 raise CommandError("value[%s] specified for --enable-sid and --disable-sid" % e)
3219 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
3221 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
3223 local_server = self.setup_local_server(sambaopts, localdcopts)
3225 local_lsa = self.new_local_lsa_connection()
3226 except RuntimeError as error:
3227 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
3230 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
3231 except RuntimeError as error:
3232 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
3234 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
3235 local_lsa_info.name.string,
3236 local_lsa_info.dns_domain.string,
3237 local_lsa_info.sid))
3241 local_netlogon = self.new_local_netlogon_connection()
3242 except RuntimeError as error:
3243 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3246 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3247 except RuntimeError as error:
3248 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3250 if local_netlogon_info.domain_name != local_netlogon_info.forest_name:
3251 raise CommandError("The local domain [%s] is not the forest root [%s]" % (
3252 local_netlogon_info.domain_name,
3253 local_netlogon_info.forest_name))
3256 # get all information about our own forest
3257 own_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3259 except RuntimeError as error:
3260 if self.check_runtime_error(error, self.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
3261 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3264 if self.check_runtime_error(error, self.WERR_INVALID_FUNCTION):
3265 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3268 if self.check_runtime_error(error, self.WERR_NERR_ACFNOTLOADED):
3269 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3272 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3274 self.outf.write("Own forest trust information...\n")
3275 self.write_forest_trust_info(own_forest_info,
3276 tln=local_lsa_info.dns_domain.string)
3279 local_samdb = self.new_local_ldap_connection()
3280 except RuntimeError as error:
3281 raise self.LocalRuntimeError(self, error, "failed to connect to SamDB")
3283 local_partitions_dn = "CN=Partitions,%s" % str(local_samdb.get_config_basedn())
3284 attrs = ['uPNSuffixes', 'msDS-SPNSuffixes']
3286 msgs = local_samdb.search(base=local_partitions_dn,
3287 scope=ldb.SCOPE_BASE,
3288 expression="(objectClass=crossRefContainer)",
3290 stored_msg = msgs[0]
3291 except ldb.LdbError as error:
3292 raise self.LocalLdbError(self, error, "failed to search partition dn")
3294 stored_upn_vals = []
3295 if 'uPNSuffixes' in stored_msg:
3296 stored_upn_vals.extend(stored_msg['uPNSuffixes'])
3298 stored_spn_vals = []
3299 if 'msDS-SPNSuffixes' in stored_msg:
3300 stored_spn_vals.extend(stored_msg['msDS-SPNSuffixes'])
3302 self.outf.write("Stored uPNSuffixes attributes[%d]:\n" % len(stored_upn_vals))
3303 for v in stored_upn_vals:
3304 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3305 self.outf.write("Stored msDS-SPNSuffixes attributes[%d]:\n" % len(stored_spn_vals))
3306 for v in stored_spn_vals:
3307 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3309 if not require_update:
3313 update_upn_vals = []
3314 update_upn_vals.extend(stored_upn_vals)
3317 update_spn_vals = []
3318 update_spn_vals.extend(stored_spn_vals)
3322 for i in xrange(0, len(update_upn_vals)):
3323 v = update_upn_vals[i]
3324 if v.lower() != upn.lower():
3329 raise CommandError("Entry already present for value[%s] specified for --add-upn-suffix" % upn)
3330 update_upn_vals.append(upn)
3333 for upn in delete_upn:
3335 for i in xrange(0, len(update_upn_vals)):
3336 v = update_upn_vals[i]
3337 if v.lower() != upn.lower():
3342 raise CommandError("Entry not found for value[%s] specified for --delete-upn-suffix" % upn)
3344 update_upn_vals.pop(idx)
3349 for i in xrange(0, len(update_spn_vals)):
3350 v = update_spn_vals[i]
3351 if v.lower() != spn.lower():
3356 raise CommandError("Entry already present for value[%s] specified for --add-spn-suffix" % spn)
3357 update_spn_vals.append(spn)
3360 for spn in delete_spn:
3362 for i in xrange(0, len(update_spn_vals)):
3363 v = update_spn_vals[i]
3364 if v.lower() != spn.lower():
3369 raise CommandError("Entry not found for value[%s] specified for --delete-spn-suffix" % spn)
3371 update_spn_vals.pop(idx)
3374 self.outf.write("Update uPNSuffixes attributes[%d]:\n" % len(update_upn_vals))
3375 for v in update_upn_vals:
3376 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3377 self.outf.write("Update msDS-SPNSuffixes attributes[%d]:\n" % len(update_spn_vals))
3378 for v in update_spn_vals:
3379 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3381 update_msg = ldb.Message()
3382 update_msg.dn = stored_msg.dn
3385 update_msg['uPNSuffixes'] = ldb.MessageElement(update_upn_vals,
3386 ldb.FLAG_MOD_REPLACE,
3389 update_msg['msDS-SPNSuffixes'] = ldb.MessageElement(update_spn_vals,
3390 ldb.FLAG_MOD_REPLACE,
3393 local_samdb.modify(update_msg)
3394 except ldb.LdbError as error:
3395 raise self.LocalLdbError(self, error, "failed to update partition dn")
3398 stored_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3400 except RuntimeError as error:
3401 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3403 self.outf.write("Stored forest trust information...\n")
3404 self.write_forest_trust_info(stored_forest_info,
3405 tln=local_lsa_info.dns_domain.string)
3409 lsaString = lsa.String()
3410 lsaString.string = domain
3411 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
3412 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
3413 except RuntimeError as error:
3414 if self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
3415 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
3417 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
3419 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
3420 local_tdo_info.netbios_name.string,
3421 local_tdo_info.domain_name.string,
3422 local_tdo_info.sid))
3424 if not local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
3425 raise CommandError("trusted domain object for domain [%s] is not marked as FOREST_TRANSITIVE." % domain)
3427 if refresh is not None:
3429 local_netlogon = self.new_local_netlogon_connection()
3430 except RuntimeError as error:
3431 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3434 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3435 except RuntimeError as error:
3436 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3438 lsa_update_check = 1
3439 if refresh == "store":
3440 netlogon_update_tdo = netlogon.DS_GFTI_UPDATE_TDO
3442 lsa_update_check = 0
3444 netlogon_update_tdo = 0
3447 # get all information about the remote trust
3448 # this triggers netr_GetForestTrustInformation to the remote domain
3449 # and lsaRSetForestTrustInformation() locally, but new top level
3450 # names are disabled by default.
3451 fresh_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3452 local_tdo_info.domain_name.string,
3453 netlogon_update_tdo)
3454 except RuntimeError as error:
3455 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3458 fresh_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3459 local_tdo_info.domain_name,
3460 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3463 except RuntimeError as error:
3464 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3466 self.outf.write("Fresh forest trust information...\n")
3467 self.write_forest_trust_info(fresh_forest_info,
3468 tln=local_tdo_info.domain_name.string,
3469 collisions=fresh_forest_collision)
3471 if refresh == "store":
3473 lsaString = lsa.String()
3474 lsaString.string = local_tdo_info.domain_name.string
3475 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3477 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3478 except RuntimeError as error:
3479 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3481 self.outf.write("Stored forest trust information...\n")
3482 self.write_forest_trust_info(stored_forest_info,
3483 tln=local_tdo_info.domain_name.string)
3488 # The none --refresh path
3492 lsaString = lsa.String()
3493 lsaString.string = local_tdo_info.domain_name.string
3494 local_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3496 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3497 except RuntimeError as error:
3498 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3500 self.outf.write("Local forest trust information...\n")
3501 self.write_forest_trust_info(local_forest_info,
3502 tln=local_tdo_info.domain_name.string)
3504 if not require_update:
3508 entries.extend(local_forest_info.entries)
3509 update_forest_info = lsa.ForestTrustInformation()
3510 update_forest_info.count = len(entries)
3511 update_forest_info.entries = entries
3514 for i in xrange(0, len(update_forest_info.entries)):
3515 r = update_forest_info.entries[i]
3516 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3518 if update_forest_info.entries[i].flags == 0:
3520 update_forest_info.entries[i].time = 0
3521 update_forest_info.entries[i].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3522 for i in xrange(0, len(update_forest_info.entries)):
3523 r = update_forest_info.entries[i]
3524 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3526 if update_forest_info.entries[i].flags == 0:
3528 update_forest_info.entries[i].time = 0
3529 update_forest_info.entries[i].flags &= ~lsa.LSA_NB_DISABLED_MASK
3530 update_forest_info.entries[i].flags &= ~lsa.LSA_SID_DISABLED_MASK
3532 for tln in enable_tln:
3534 for i in xrange(0, len(update_forest_info.entries)):
3535 r = update_forest_info.entries[i]
3536 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3538 if r.forest_trust_data.string.lower() != tln.lower():
3543 raise CommandError("Entry not found for value[%s] specified for --enable-tln" % tln)
3544 if not update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_MASK:
3545 raise CommandError("Entry found for value[%s] specified for --enable-tln is already enabled" % tln)
3546 update_forest_info.entries[idx].time = 0
3547 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3549 for tln in disable_tln:
3551 for i in xrange(0, len(update_forest_info.entries)):
3552 r = update_forest_info.entries[i]
3553 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3555 if r.forest_trust_data.string.lower() != tln.lower():
3560 raise CommandError("Entry not found for value[%s] specified for --disable-tln" % tln)
3561 if update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_ADMIN:
3562 raise CommandError("Entry found for value[%s] specified for --disable-tln is already disabled" % tln)
3563 update_forest_info.entries[idx].time = 0
3564 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3565 update_forest_info.entries[idx].flags |= lsa.LSA_TLN_DISABLED_ADMIN
3567 for tln_ex in add_tln_ex:
3569 for i in xrange(0, len(update_forest_info.entries)):
3570 r = update_forest_info.entries[i]
3571 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3573 if r.forest_trust_data.string.lower() != tln_ex.lower():
3578 raise CommandError("Entry already present for value[%s] specified for --add-tln-ex" % tln_ex)
3580 tln_dot = ".%s" % tln_ex.lower()
3582 for i in xrange(0, len(update_forest_info.entries)):
3583 r = update_forest_info.entries[i]
3584 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3586 r_dot = ".%s" % r.forest_trust_data.string.lower()
3587 if tln_dot == r_dot:
3588 raise CommandError("TLN entry present for value[%s] specified for --add-tln-ex" % tln_ex)
3589 if not tln_dot.endswith(r_dot):
3595 raise CommandError("No TLN parent present for value[%s] specified for --add-tln-ex" % tln_ex)
3597 r = lsa.ForestTrustRecord()
3598 r.type = lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX
3601 r.forest_trust_data.string = tln_ex
3604 entries.extend(update_forest_info.entries)
3605 entries.insert(idx + 1, r)
3606 update_forest_info.count = len(entries)
3607 update_forest_info.entries = entries
3609 for tln_ex in delete_tln_ex:
3611 for i in xrange(0, len(update_forest_info.entries)):
3612 r = update_forest_info.entries[i]
3613 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3615 if r.forest_trust_data.string.lower() != tln_ex.lower():
3620 raise CommandError("Entry not found for value[%s] specified for --delete-tln-ex" % tln_ex)
3623 entries.extend(update_forest_info.entries)
3625 update_forest_info.count = len(entries)
3626 update_forest_info.entries = entries
3628 for nb in enable_nb:
3630 for i in xrange(0, len(update_forest_info.entries)):
3631 r = update_forest_info.entries[i]
3632 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3634 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3639 raise CommandError("Entry not found for value[%s] specified for --enable-nb" % nb)
3640 if not update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_MASK:
3641 raise CommandError("Entry found for value[%s] specified for --enable-nb is already enabled" % nb)
3642 update_forest_info.entries[idx].time = 0
3643 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3645 for nb in disable_nb:
3647 for i in xrange(0, len(update_forest_info.entries)):
3648 r = update_forest_info.entries[i]
3649 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3651 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3656 raise CommandError("Entry not found for value[%s] specified for --delete-nb" % nb)
3657 if update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_ADMIN:
3658 raise CommandError("Entry found for value[%s] specified for --disable-nb is already disabled" % nb)
3659 update_forest_info.entries[idx].time = 0
3660 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3661 update_forest_info.entries[idx].flags |= lsa.LSA_NB_DISABLED_ADMIN
3663 for sid in enable_sid:
3665 for i in xrange(0, len(update_forest_info.entries)):
3666 r = update_forest_info.entries[i]
3667 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3669 if r.forest_trust_data.domain_sid != sid:
3674 raise CommandError("Entry not found for value[%s] specified for --enable-sid" % sid)
3675 if not update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_MASK:
3676 raise CommandError("Entry found for value[%s] specified for --enable-sid is already enabled" % nb)
3677 update_forest_info.entries[idx].time = 0
3678 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3680 for sid in disable_sid:
3682 for i in xrange(0, len(update_forest_info.entries)):
3683 r = update_forest_info.entries[i]
3684 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3686 if r.forest_trust_data.domain_sid != sid:
3691 raise CommandError("Entry not found for value[%s] specified for --delete-sid" % sid)
3692 if update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_ADMIN:
3693 raise CommandError("Entry found for value[%s] specified for --disable-sid is already disabled" % nb)
3694 update_forest_info.entries[idx].time = 0
3695 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3696 update_forest_info.entries[idx].flags |= lsa.LSA_SID_DISABLED_ADMIN
3699 update_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3700 local_tdo_info.domain_name,
3701 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3702 update_forest_info, 0)
3703 except RuntimeError as error:
3704 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3706 self.outf.write("Updated forest trust information...\n")
3707 self.write_forest_trust_info(update_forest_info,
3708 tln=local_tdo_info.domain_name.string,
3709 collisions=update_forest_collision)
3712 lsaString = lsa.String()
3713 lsaString.string = local_tdo_info.domain_name.string
3714 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3716 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3717 except RuntimeError as error:
3718 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3720 self.outf.write("Stored forest trust information...\n")
3721 self.write_forest_trust_info(stored_forest_info,
3722 tln=local_tdo_info.domain_name.string)
3725 class cmd_domain_trust(SuperCommand):
3726 """Domain and forest trust management."""
3729 subcommands["list"] = cmd_domain_trust_list()
3730 subcommands["show"] = cmd_domain_trust_show()
3731 subcommands["create"] = cmd_domain_trust_create()
3732 subcommands["delete"] = cmd_domain_trust_delete()
3733 subcommands["validate"] = cmd_domain_trust_validate()
3734 subcommands["namespaces"] = cmd_domain_trust_namespaces()
3736 class cmd_domain(SuperCommand):
3737 """Domain management."""
3740 subcommands["demote"] = cmd_domain_demote()
3741 if cmd_domain_export_keytab is not None:
3742 subcommands["exportkeytab"] = cmd_domain_export_keytab()
3743 subcommands["info"] = cmd_domain_info()
3744 subcommands["provision"] = cmd_domain_provision()
3745 subcommands["join"] = cmd_domain_join()
3746 subcommands["dcpromo"] = cmd_domain_dcpromo()
3747 subcommands["level"] = cmd_domain_level()
3748 subcommands["passwordsettings"] = cmd_domain_passwordsettings()
3749 subcommands["classicupgrade"] = cmd_domain_classicupgrade()
3750 subcommands["samba3upgrade"] = cmd_domain_samba3upgrade()
3751 subcommands["trust"] = cmd_domain_trust()