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
35 from getpass import getpass
36 from samba.net import Net, LIBNET_JOIN_AUTOMATIC
38 from samba.join import join_RODC, join_DC, join_subdomain
39 from samba.auth import system_session
40 from samba.samdb import SamDB
41 from samba.ndr import ndr_unpack, ndr_pack, ndr_print
42 from samba.dcerpc import drsuapi
43 from samba.dcerpc import drsblobs
44 from samba.dcerpc import lsa
45 from samba.dcerpc import netlogon
46 from samba.dcerpc import security
47 from samba.dcerpc import nbt
48 from samba.dcerpc import misc
49 from samba.dcerpc.samr import DOMAIN_PASSWORD_COMPLEX, DOMAIN_PASSWORD_STORE_CLEARTEXT
50 from samba.netcmd import (
56 from samba.netcmd.common import netcmd_get_domain_infos_via_cldap
57 from samba.samba3 import Samba3
58 from samba.samba3 import param as s3param
59 from samba.upgrade import upgrade_from_samba3
60 from samba.drs_utils import (
61 sendDsReplicaSync, drsuapi_connect, drsException,
63 from samba import remove_dc
65 from samba.dsdb import (
66 DS_DOMAIN_FUNCTION_2000,
67 DS_DOMAIN_FUNCTION_2003,
68 DS_DOMAIN_FUNCTION_2003_MIXED,
69 DS_DOMAIN_FUNCTION_2008,
70 DS_DOMAIN_FUNCTION_2008_R2,
71 DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL,
72 DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL,
73 UF_WORKSTATION_TRUST_ACCOUNT,
74 UF_SERVER_TRUST_ACCOUNT,
75 UF_TRUSTED_FOR_DELEGATION,
76 UF_PARTIAL_SECRETS_ACCOUNT
79 from samba.provision import (
84 from samba.provision.common import (
90 def get_testparm_var(testparm, smbconf, varname):
91 errfile = open(os.devnull, 'w')
92 p = subprocess.Popen([testparm, '-s', '-l',
93 '--parameter-name=%s' % varname, smbconf],
94 stdout=subprocess.PIPE, stderr=errfile)
95 (out,err) = p.communicate()
97 lines = out.split('\n')
99 return lines[0].strip()
103 import samba.dckeytab
105 cmd_domain_export_keytab = None
107 class cmd_domain_export_keytab(Command):
108 """Dump Kerberos keys of the domain into a keytab."""
110 synopsis = "%prog <keytab> [options]"
112 takes_optiongroups = {
113 "sambaopts": options.SambaOptions,
114 "credopts": options.CredentialsOptions,
115 "versionopts": options.VersionOptions,
119 Option("--principal", help="extract only this principal", type=str),
122 takes_args = ["keytab"]
124 def run(self, keytab, credopts=None, sambaopts=None, versionopts=None, principal=None):
125 lp = sambaopts.get_loadparm()
127 net.export_keytab(keytab=keytab, principal=principal)
130 class cmd_domain_info(Command):
131 """Print basic info about a domain and the DC passed as parameter."""
133 synopsis = "%prog <ip_address> [options]"
138 takes_optiongroups = {
139 "sambaopts": options.SambaOptions,
140 "credopts": options.CredentialsOptions,
141 "versionopts": options.VersionOptions,
144 takes_args = ["address"]
146 def run(self, address, credopts=None, sambaopts=None, versionopts=None):
147 lp = sambaopts.get_loadparm()
149 res = netcmd_get_domain_infos_via_cldap(lp, None, address)
151 raise CommandError("Invalid IP address '" + address + "'!")
152 self.outf.write("Forest : %s\n" % res.forest)
153 self.outf.write("Domain : %s\n" % res.dns_domain)
154 self.outf.write("Netbios domain : %s\n" % res.domain_name)
155 self.outf.write("DC name : %s\n" % res.pdc_dns_name)
156 self.outf.write("DC netbios name : %s\n" % res.pdc_name)
157 self.outf.write("Server site : %s\n" % res.server_site)
158 self.outf.write("Client site : %s\n" % res.client_site)
161 class cmd_domain_provision(Command):
162 """Provision a domain."""
164 synopsis = "%prog [options]"
166 takes_optiongroups = {
167 "sambaopts": options.SambaOptions,
168 "versionopts": options.VersionOptions,
172 Option("--interactive", help="Ask for names", action="store_true"),
173 Option("--domain", type="string", metavar="DOMAIN",
175 Option("--domain-guid", type="string", metavar="GUID",
176 help="set domainguid (otherwise random)"),
177 Option("--domain-sid", type="string", metavar="SID",
178 help="set domainsid (otherwise random)"),
179 Option("--ntds-guid", type="string", metavar="GUID",
180 help="set NTDS object GUID (otherwise random)"),
181 Option("--invocationid", type="string", metavar="GUID",
182 help="set invocationid (otherwise random)"),
183 Option("--host-name", type="string", metavar="HOSTNAME",
184 help="set hostname"),
185 Option("--host-ip", type="string", metavar="IPADDRESS",
186 help="set IPv4 ipaddress"),
187 Option("--host-ip6", type="string", metavar="IP6ADDRESS",
188 help="set IPv6 ipaddress"),
189 Option("--site", type="string", metavar="SITENAME",
190 help="set site name"),
191 Option("--adminpass", type="string", metavar="PASSWORD",
192 help="choose admin password (otherwise random)"),
193 Option("--krbtgtpass", type="string", metavar="PASSWORD",
194 help="choose krbtgt password (otherwise random)"),
195 Option("--machinepass", type="string", metavar="PASSWORD",
196 help="choose machine password (otherwise random)"),
197 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
198 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
199 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
200 "BIND9_FLATFILE uses bind9 text database to store zone information, "
201 "BIND9_DLZ uses samba4 AD to store zone information, "
202 "NONE skips the DNS setup entirely (not recommended)",
203 default="SAMBA_INTERNAL"),
204 Option("--dnspass", type="string", metavar="PASSWORD",
205 help="choose dns password (otherwise random)"),
206 Option("--ldapadminpass", type="string", metavar="PASSWORD",
207 help="choose password to set between Samba and its LDAP backend (otherwise random)"),
208 Option("--root", type="string", metavar="USERNAME",
209 help="choose 'root' unix username"),
210 Option("--nobody", type="string", metavar="USERNAME",
211 help="choose 'nobody' user"),
212 Option("--users", type="string", metavar="GROUPNAME",
213 help="choose 'users' group"),
214 Option("--quiet", help="Be quiet", action="store_true"),
215 Option("--blank", action="store_true",
216 help="do not add users or groups, just the structure"),
217 Option("--ldap-backend-type", type="choice", metavar="LDAP-BACKEND-TYPE",
218 help="Test initialisation support for unsupported LDAP backend type (fedora-ds or openldap) DO NOT USE",
219 choices=["fedora-ds", "openldap"]),
220 Option("--server-role", type="choice", metavar="ROLE",
221 choices=["domain controller", "dc", "member server", "member", "standalone"],
222 help="The server role (domain controller | dc | member server | member | standalone). Default is dc.",
223 default="domain controller"),
224 Option("--function-level", type="choice", metavar="FOR-FUN-LEVEL",
225 choices=["2000", "2003", "2008", "2008_R2"],
226 help="The domain and forest function level (2000 | 2003 | 2008 | 2008_R2 - always native). Default is (Windows) 2008_R2 Native.",
228 Option("--next-rid", type="int", metavar="NEXTRID", default=1000,
229 help="The initial nextRid value (only needed for upgrades). Default is 1000."),
230 Option("--partitions-only",
231 help="Configure Samba's partitions, but do not modify them (ie, join a BDC)", action="store_true"),
232 Option("--targetdir", type="string", metavar="DIR",
233 help="Set target directory"),
234 Option("--ol-mmr-urls", type="string", metavar="LDAPSERVER",
235 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\""),
236 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"),
238 Option("--use-rfc2307", action="store_true", help="Use AD to store posix attributes (default = no)"),
242 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",
243 action="store_true"),
244 Option("--slapd-path", type="string", metavar="SLAPD-PATH",
245 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."),
246 Option("--ldap-backend-extra-port", type="int", metavar="LDAP-BACKEND-EXTRA-PORT", help="Additional TCP port for LDAP backend server (to use for replication)"),
247 Option("--ldap-backend-forced-uri", type="string", metavar="LDAP-BACKEND-FORCED-URI",
248 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"),
249 Option("--ldap-backend-nosync", help="Configure LDAP backend not to call fsync() (for performance in test environments)", action="store_true"),
253 Option("--use-ntvfs", action="store_true", help="Use NTVFS for the fileserver (default = no)"),
256 if os.getenv('TEST_LDAP', "no") == "yes":
257 takes_options.extend(openldap_options)
259 if samba.is_ntvfs_fileserver_built():
260 takes_options.extend(ntvfs_options)
264 def run(self, sambaopts=None, versionopts=None,
287 ldap_backend_type=None,
291 partitions_only=None,
298 ldap_backend_nosync=None,
299 ldap_backend_extra_port=None,
300 ldap_backend_forced_uri=None,
301 ldap_dryrun_mode=None):
303 self.logger = self.get_logger("provision")
305 self.logger.setLevel(logging.WARNING)
307 self.logger.setLevel(logging.INFO)
309 lp = sambaopts.get_loadparm()
310 smbconf = lp.configfile
312 if dns_forwarder is not None:
313 suggested_forwarder = dns_forwarder
315 suggested_forwarder = self._get_nameserver_ip()
316 if suggested_forwarder is None:
317 suggested_forwarder = "none"
319 if len(self.raw_argv) == 1:
323 from getpass import getpass
326 def ask(prompt, default=None):
327 if default is not None:
328 print "%s [%s]: " % (prompt, default),
330 print "%s: " % (prompt,),
331 return sys.stdin.readline().rstrip("\n") or default
334 default = socket.getfqdn().split(".", 1)[1].upper()
337 realm = ask("Realm", default)
338 if realm in (None, ""):
339 raise CommandError("No realm set!")
342 default = realm.split(".")[0]
345 domain = ask("Domain", default)
347 raise CommandError("No domain set!")
349 server_role = ask("Server Role (dc, member, standalone)", "dc")
351 dns_backend = ask("DNS backend (SAMBA_INTERNAL, BIND9_FLATFILE, BIND9_DLZ, NONE)", "SAMBA_INTERNAL")
352 if dns_backend in (None, ''):
353 raise CommandError("No DNS backend set!")
355 if dns_backend == "SAMBA_INTERNAL":
356 dns_forwarder = ask("DNS forwarder IP address (write 'none' to disable forwarding)", suggested_forwarder)
357 if dns_forwarder.lower() in (None, 'none'):
358 suggested_forwarder = None
362 adminpassplain = getpass("Administrator password: ")
363 if not adminpassplain:
364 self.errf.write("Invalid administrator password.\n")
366 adminpassverify = getpass("Retype password: ")
367 if not adminpassplain == adminpassverify:
368 self.errf.write("Sorry, passwords do not match.\n")
370 adminpass = adminpassplain
374 realm = sambaopts._lp.get('realm')
376 raise CommandError("No realm set!")
378 raise CommandError("No domain set!")
381 self.logger.info("Administrator password will be set randomly!")
383 if function_level == "2000":
384 dom_for_fun_level = DS_DOMAIN_FUNCTION_2000
385 elif function_level == "2003":
386 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
387 elif function_level == "2008":
388 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008
389 elif function_level == "2008_R2":
390 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
392 if dns_backend == "SAMBA_INTERNAL" and dns_forwarder is None:
393 dns_forwarder = suggested_forwarder
395 samdb_fill = FILL_FULL
397 samdb_fill = FILL_NT4SYNC
398 elif partitions_only:
399 samdb_fill = FILL_DRS
401 if targetdir is not None:
402 if not os.path.isdir(targetdir):
407 if use_xattrs == "yes":
409 elif use_xattrs == "auto" and not lp.get("posix:eadb"):
411 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
413 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
416 samba.ntacls.setntacl(lp, file.name,
417 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
420 self.logger.info("You are not root or your system do not support xattr, using tdb backend for attributes. ")
425 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.")
426 if ldap_backend_type == "existing":
427 if ldap_backend_forced_uri is not None:
428 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)
430 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")
432 if ldap_backend_forced_uri is not None:
433 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")
435 if domain_sid is not None:
436 domain_sid = security.dom_sid(domain_sid)
438 session = system_session()
440 result = provision(self.logger,
441 session, smbconf=smbconf, targetdir=targetdir,
442 samdb_fill=samdb_fill, realm=realm, domain=domain,
443 domainguid=domain_guid, domainsid=domain_sid,
445 hostip=host_ip, hostip6=host_ip6,
446 sitename=site, ntdsguid=ntds_guid,
447 invocationid=invocationid, adminpass=adminpass,
448 krbtgtpass=krbtgtpass, machinepass=machinepass,
449 dns_backend=dns_backend, dns_forwarder=dns_forwarder,
450 dnspass=dnspass, root=root, nobody=nobody,
452 serverrole=server_role, dom_for_fun_level=dom_for_fun_level,
453 backend_type=ldap_backend_type,
454 ldapadminpass=ldapadminpass, ol_mmr_urls=ol_mmr_urls, slapd_path=slapd_path,
455 useeadb=eadb, next_rid=next_rid, lp=lp, use_ntvfs=use_ntvfs,
456 use_rfc2307=use_rfc2307, skip_sysvolacl=False,
457 ldap_backend_extra_port=ldap_backend_extra_port,
458 ldap_backend_forced_uri=ldap_backend_forced_uri,
459 nosync=ldap_backend_nosync, ldap_dryrun_mode=ldap_dryrun_mode)
461 except ProvisioningError, e:
462 raise CommandError("Provision failed", e)
464 result.report_logger(self.logger)
466 def _get_nameserver_ip(self):
467 """Grab the nameserver IP address from /etc/resolv.conf."""
469 RESOLV_CONF="/etc/resolv.conf"
471 if not path.isfile(RESOLV_CONF):
472 self.logger.warning("Failed to locate %s" % RESOLV_CONF)
477 handle = open(RESOLV_CONF, 'r')
479 if not line.startswith('nameserver'):
481 # we want the last non-space continuous string of the line
482 return line.strip().split()[-1]
484 if handle is not None:
487 self.logger.warning("No nameserver found in %s" % RESOLV_CONF)
490 class cmd_domain_dcpromo(Command):
491 """Promote an existing domain member or NT4 PDC to an AD DC."""
493 synopsis = "%prog <dnsdomain> [DC|RODC] [options]"
495 takes_optiongroups = {
496 "sambaopts": options.SambaOptions,
497 "versionopts": options.VersionOptions,
498 "credopts": options.CredentialsOptions,
502 Option("--server", help="DC to join", type=str),
503 Option("--site", help="site to join", type=str),
504 Option("--targetdir", help="where to store provision", type=str),
505 Option("--domain-critical-only",
506 help="only replicate critical domain objects",
507 action="store_true"),
508 Option("--machinepass", type=str, metavar="PASSWORD",
509 help="choose machine password (otherwise random)"),
510 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
511 choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
512 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
513 "BIND9_DLZ uses samba4 AD to store zone information, "
514 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
515 default="SAMBA_INTERNAL"),
516 Option("--quiet", help="Be quiet", action="store_true"),
517 Option("--verbose", help="Be verbose", action="store_true")
521 Option("--use-ntvfs", action="store_true", help="Use NTVFS for the fileserver (default = no)"),
524 if samba.is_ntvfs_fileserver_built():
525 takes_options.extend(ntvfs_options)
528 takes_args = ["domain", "role?"]
530 def run(self, domain, role=None, sambaopts=None, credopts=None,
531 versionopts=None, server=None, site=None, targetdir=None,
532 domain_critical_only=False, parent_domain=None, machinepass=None,
533 use_ntvfs=False, dns_backend=None,
534 quiet=False, verbose=False):
535 lp = sambaopts.get_loadparm()
536 creds = credopts.get_credentials(lp)
537 net = Net(creds, lp, server=credopts.ipaddress)
540 site = "Default-First-Site-Name"
542 logger = self.get_logger()
544 logger.setLevel(logging.DEBUG)
546 logger.setLevel(logging.WARNING)
548 logger.setLevel(logging.INFO)
550 netbios_name = lp.get("netbios name")
556 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
557 site=site, netbios_name=netbios_name, targetdir=targetdir,
558 domain_critical_only=domain_critical_only,
559 machinepass=machinepass, use_ntvfs=use_ntvfs,
560 dns_backend=dns_backend,
561 promote_existing=True)
563 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
564 site=site, netbios_name=netbios_name, targetdir=targetdir,
565 domain_critical_only=domain_critical_only,
566 machinepass=machinepass, use_ntvfs=use_ntvfs, dns_backend=dns_backend,
567 promote_existing=True)
569 raise CommandError("Invalid role '%s' (possible values: DC, RODC)" % role)
572 class cmd_domain_join(Command):
573 """Join domain as either member or backup domain controller."""
575 synopsis = "%prog <dnsdomain> [DC|RODC|MEMBER|SUBDOMAIN] [options]"
577 takes_optiongroups = {
578 "sambaopts": options.SambaOptions,
579 "versionopts": options.VersionOptions,
580 "credopts": options.CredentialsOptions,
584 Option("--server", help="DC to join", type=str),
585 Option("--site", help="site to join", type=str),
586 Option("--targetdir", help="where to store provision", type=str),
587 Option("--parent-domain", help="parent domain to create subdomain under", type=str),
588 Option("--domain-critical-only",
589 help="only replicate critical domain objects",
590 action="store_true"),
591 Option("--machinepass", type=str, metavar="PASSWORD",
592 help="choose machine password (otherwise random)"),
593 Option("--adminpass", type="string", metavar="PASSWORD",
594 help="choose adminstrator password when joining as a subdomain (otherwise random)"),
595 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
596 choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
597 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
598 "BIND9_DLZ uses samba4 AD to store zone information, "
599 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
600 default="SAMBA_INTERNAL"),
601 Option("--quiet", help="Be quiet", action="store_true"),
602 Option("--verbose", help="Be verbose", action="store_true")
606 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
609 if samba.is_ntvfs_fileserver_built():
610 takes_options.extend(ntvfs_options)
612 takes_args = ["domain", "role?"]
614 def run(self, domain, role=None, sambaopts=None, credopts=None,
615 versionopts=None, server=None, site=None, targetdir=None,
616 domain_critical_only=False, parent_domain=None, machinepass=None,
617 use_ntvfs=False, dns_backend=None, adminpass=None,
618 quiet=False, verbose=False):
619 lp = sambaopts.get_loadparm()
620 creds = credopts.get_credentials(lp)
621 net = Net(creds, lp, server=credopts.ipaddress)
624 site = "Default-First-Site-Name"
626 logger = self.get_logger()
628 logger.setLevel(logging.DEBUG)
630 logger.setLevel(logging.WARNING)
632 logger.setLevel(logging.INFO)
634 netbios_name = lp.get("netbios name")
639 if role is None or role == "MEMBER":
640 (join_password, sid, domain_name) = net.join_member(
641 domain, netbios_name, LIBNET_JOIN_AUTOMATIC,
642 machinepass=machinepass)
644 self.errf.write("Joined domain %s (%s)\n" % (domain_name, sid))
646 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
647 site=site, netbios_name=netbios_name, targetdir=targetdir,
648 domain_critical_only=domain_critical_only,
649 machinepass=machinepass, use_ntvfs=use_ntvfs, dns_backend=dns_backend)
651 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
652 site=site, netbios_name=netbios_name, targetdir=targetdir,
653 domain_critical_only=domain_critical_only,
654 machinepass=machinepass, use_ntvfs=use_ntvfs,
655 dns_backend=dns_backend)
656 elif role == "SUBDOMAIN":
658 logger.info("Administrator password will be set randomly!")
660 netbios_domain = lp.get("workgroup")
661 if parent_domain is None:
662 parent_domain = ".".join(domain.split(".")[1:])
663 join_subdomain(logger=logger, server=server, creds=creds, lp=lp, dnsdomain=domain,
664 parent_domain=parent_domain, site=site,
665 netbios_name=netbios_name, netbios_domain=netbios_domain,
666 targetdir=targetdir, machinepass=machinepass,
667 use_ntvfs=use_ntvfs, dns_backend=dns_backend,
670 raise CommandError("Invalid role '%s' (possible values: MEMBER, DC, RODC, SUBDOMAIN)" % role)
673 class cmd_domain_demote(Command):
674 """Demote ourselves from the role of Domain Controller."""
676 synopsis = "%prog [options]"
679 Option("--server", help="writable DC to write demotion changes on", type=str),
680 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
681 metavar="URL", dest="H"),
682 Option("--remove-other-dead-server", help="Dead DC (name or NTDS GUID) "
683 "to remove ALL references to (rather than this DC)", type=str),
684 Option("--quiet", help="Be quiet", action="store_true"),
685 Option("--verbose", help="Be verbose", action="store_true"),
688 takes_optiongroups = {
689 "sambaopts": options.SambaOptions,
690 "credopts": options.CredentialsOptions,
691 "versionopts": options.VersionOptions,
694 def run(self, sambaopts=None, credopts=None,
695 versionopts=None, server=None,
696 remove_other_dead_server=None, H=None,
697 verbose=False, quiet=False):
698 lp = sambaopts.get_loadparm()
699 creds = credopts.get_credentials(lp)
700 net = Net(creds, lp, server=credopts.ipaddress)
702 logger = self.get_logger()
704 logger.setLevel(logging.DEBUG)
706 logger.setLevel(logging.WARNING)
708 logger.setLevel(logging.INFO)
710 if remove_other_dead_server is not None:
711 if server is not None:
712 samdb = SamDB(url="ldap://%s" % server,
713 session_info=system_session(),
714 credentials=creds, lp=lp)
716 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
718 remove_dc.remove_dc(samdb, logger, remove_other_dead_server)
719 except remove_dc.DemoteException as err:
720 raise CommandError("Demote failed: %s" % err)
723 netbios_name = lp.get("netbios name")
724 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
726 res = samdb.search(expression='(&(objectClass=computer)(serverReferenceBL=*))', attrs=["dnsHostName", "name"])
728 raise CommandError("Unable to search for servers")
731 raise CommandError("You are the latest server in the domain")
735 if str(e["name"]).lower() != netbios_name.lower():
736 server = e["dnsHostName"]
739 ntds_guid = samdb.get_ntds_GUID()
740 msg = samdb.search(base=str(samdb.get_config_basedn()),
741 scope=ldb.SCOPE_SUBTREE, expression="(objectGUID=%s)" % ntds_guid,
743 if len(msg) == 0 or "options" not in msg[0]:
744 raise CommandError("Failed to find options on %s" % ntds_guid)
747 dsa_options = int(str(msg[0]['options']))
749 res = samdb.search(expression="(fSMORoleOwner=%s)" % str(ntds_dn),
750 controls=["search_options:1:2"])
753 raise CommandError("Current DC is still the owner of %d role(s), use the role command to transfer roles to another DC" % len(res))
755 self.errf.write("Using %s as partner server for the demotion\n" %
757 (drsuapiBind, drsuapi_handle, supportedExtensions) = drsuapi_connect(server, lp, creds)
759 self.errf.write("Deactivating inbound replication\n")
761 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
765 dsa_options |= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
766 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
770 self.errf.write("Asking partner server %s to synchronize from us\n"
772 for part in (samdb.get_schema_basedn(),
773 samdb.get_config_basedn(),
774 samdb.get_root_basedn()):
775 nc = drsuapi.DsReplicaObjectIdentifier()
778 req1 = drsuapi.DsReplicaSyncRequest1()
779 req1.naming_context = nc;
780 req1.options = drsuapi.DRSUAPI_DRS_WRIT_REP
781 req1.source_dsa_guid = misc.GUID(ntds_guid)
784 drsuapiBind.DsReplicaSync(drsuapi_handle, 1, req1)
785 except RuntimeError as (werr, string):
786 if werr == 8452: #WERR_DS_DRA_NO_REPLICA
790 "Error while demoting, "
791 "re-enabling inbound replication\n")
792 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
793 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
795 raise CommandError("Error while sending a DsReplicaSync for partion %s" % str(part), e)
797 remote_samdb = SamDB(url="ldap://%s" % server,
798 session_info=system_session(),
799 credentials=creds, lp=lp)
801 self.errf.write("Changing userControl and container\n")
802 res = remote_samdb.search(base=str(remote_samdb.domain_dn()),
803 expression="(&(objectClass=user)(sAMAccountName=%s$))" %
804 netbios_name.upper(),
805 attrs=["userAccountControl"])
807 uac = int(str(res[0]["userAccountControl"]))
811 "Error while demoting, re-enabling inbound replication\n")
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("Error while changing account control", e)
819 "Error while demoting, re-enabling inbound replication")
820 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
821 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
823 raise CommandError("Unable to find object with samaccountName = %s$"
824 " in the remote dc" % netbios_name.upper())
828 uac &= ~(UF_SERVER_TRUST_ACCOUNT|UF_TRUSTED_FOR_DELEGATION|UF_PARTIAL_SECRETS_ACCOUNT)
829 uac |= UF_WORKSTATION_TRUST_ACCOUNT
834 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
835 ldb.FLAG_MOD_REPLACE,
836 "userAccountControl")
838 remote_samdb.modify(msg)
841 "Error while demoting, re-enabling inbound replication")
842 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
843 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
846 raise CommandError("Error while changing account control", e)
848 parent = msg.dn.parent()
849 dc_name = res[0].dn.get_rdn_value()
850 rdn = "CN=%s" % dc_name
852 # Let's move to the Computer container
856 computer_dn = ldb.Dn(remote_samdb, "CN=Computers,%s" % str(remote_samdb.domain_dn()))
857 res = remote_samdb.search(base=computer_dn, expression=rdn, scope=ldb.SCOPE_ONELEVEL)
860 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
861 scope=ldb.SCOPE_ONELEVEL)
862 while(len(res) != 0 and i < 100):
864 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
865 scope=ldb.SCOPE_ONELEVEL)
869 "Error while demoting, re-enabling inbound replication\n")
870 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
871 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
877 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
878 ldb.FLAG_MOD_REPLACE,
879 "userAccountControl")
881 remote_samdb.modify(msg)
883 raise CommandError("Unable to find a slot for renaming %s,"
884 " all names from %s-1 to %s-%d seemed used" %
885 (str(dc_dn), rdn, rdn, i - 9))
887 newrdn = "%s-%d" % (rdn, i)
890 newdn = ldb.Dn(remote_samdb, "%s,%s" % (newrdn, str(computer_dn)))
891 remote_samdb.rename(dc_dn, newdn)
894 "Error while demoting, re-enabling inbound replication\n")
895 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
896 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
902 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
903 ldb.FLAG_MOD_REPLACE,
904 "userAccountControl")
906 remote_samdb.modify(msg)
907 raise CommandError("Error while renaming %s to %s" % (str(dc_dn), str(newdn)), e)
910 server_dsa_dn = samdb.get_serverName()
911 domain = remote_samdb.get_root_basedn()
914 req1 = drsuapi.DsRemoveDSServerRequest1()
915 req1.server_dn = str(server_dsa_dn)
916 req1.domain_dn = str(domain)
919 drsuapiBind.DsRemoveDSServer(drsuapi_handle, 1, req1)
920 except RuntimeError as (werr, string):
921 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
923 "Error while demoting, re-enabling inbound replication\n")
924 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
925 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
931 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
932 ldb.FLAG_MOD_REPLACE,
933 "userAccountControl")
934 remote_samdb.modify(msg)
935 remote_samdb.rename(newdn, dc_dn)
936 if werr == 8452: #WERR_DS_DRA_NO_REPLICA
937 raise CommandError("The DC %s is not present on (already removed from) the remote server: " % server_dsa_dn, e)
939 raise CommandError("Error while sending a removeDsServer of %s: " % server_dsa_dn, e)
941 remove_dc.remove_sysvol_references(remote_samdb, dc_name)
943 # These are objects under the computer account that should be deleted
944 for s in ("CN=Enterprise,CN=NTFRS Subscriptions",
945 "CN=%s, CN=NTFRS Subscriptions" % lp.get("realm"),
946 "CN=Domain system Volumes (SYSVOL Share), CN=NTFRS Subscriptions",
947 "CN=NTFRS Subscriptions"):
949 remote_samdb.delete(ldb.Dn(remote_samdb,
950 "%s,%s" % (s, str(newdn))))
951 except ldb.LdbError, l:
954 self.errf.write("Demote successful\n")
957 class cmd_domain_level(Command):
958 """Raise domain and forest function levels."""
960 synopsis = "%prog (show|raise <options>) [options]"
962 takes_optiongroups = {
963 "sambaopts": options.SambaOptions,
964 "credopts": options.CredentialsOptions,
965 "versionopts": options.VersionOptions,
969 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
970 metavar="URL", dest="H"),
971 Option("--quiet", help="Be quiet", action="store_true"),
972 Option("--forest-level", type="choice", choices=["2003", "2008", "2008_R2"],
973 help="The forest function level (2003 | 2008 | 2008_R2)"),
974 Option("--domain-level", type="choice", choices=["2003", "2008", "2008_R2"],
975 help="The domain function level (2003 | 2008 | 2008_R2)")
978 takes_args = ["subcommand"]
980 def run(self, subcommand, H=None, forest_level=None, domain_level=None,
981 quiet=False, credopts=None, sambaopts=None, versionopts=None):
982 lp = sambaopts.get_loadparm()
983 creds = credopts.get_credentials(lp, fallback_machine=True)
985 samdb = SamDB(url=H, session_info=system_session(),
986 credentials=creds, lp=lp)
988 domain_dn = samdb.domain_dn()
990 res_forest = samdb.search("CN=Partitions,%s" % samdb.get_config_basedn(),
991 scope=ldb.SCOPE_BASE, attrs=["msDS-Behavior-Version"])
992 assert len(res_forest) == 1
994 res_domain = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
995 attrs=["msDS-Behavior-Version", "nTMixedDomain"])
996 assert len(res_domain) == 1
998 res_dc_s = samdb.search("CN=Sites,%s" % samdb.get_config_basedn(),
999 scope=ldb.SCOPE_SUBTREE, expression="(objectClass=nTDSDSA)",
1000 attrs=["msDS-Behavior-Version"])
1001 assert len(res_dc_s) >= 1
1004 level_forest = int(res_forest[0]["msDS-Behavior-Version"][0])
1005 level_domain = int(res_domain[0]["msDS-Behavior-Version"][0])
1006 level_domain_mixed = int(res_domain[0]["nTMixedDomain"][0])
1008 min_level_dc = int(res_dc_s[0]["msDS-Behavior-Version"][0]) # Init value
1009 for msg in res_dc_s:
1010 if int(msg["msDS-Behavior-Version"][0]) < min_level_dc:
1011 min_level_dc = int(msg["msDS-Behavior-Version"][0])
1013 if level_forest < 0 or level_domain < 0:
1014 raise CommandError("Domain and/or forest function level(s) is/are invalid. Correct them or reprovision!")
1015 if min_level_dc < 0:
1016 raise CommandError("Lowest function level of a DC is invalid. Correct this or reprovision!")
1017 if level_forest > level_domain:
1018 raise CommandError("Forest function level is higher than the domain level(s). Correct this or reprovision!")
1019 if level_domain > min_level_dc:
1020 raise CommandError("Domain function level is higher than the lowest function level of a DC. Correct this or reprovision!")
1023 raise CommandError("Could not retrieve the actual domain, forest level and/or lowest DC function level!")
1025 if subcommand == "show":
1026 self.message("Domain and forest function level for domain '%s'" % domain_dn)
1027 if level_forest == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1028 self.message("\nATTENTION: You run SAMBA 4 on a forest function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1029 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1030 self.message("\nATTENTION: You run SAMBA 4 on a domain function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1031 if min_level_dc == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1032 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)!")
1036 if level_forest == DS_DOMAIN_FUNCTION_2000:
1038 elif level_forest == DS_DOMAIN_FUNCTION_2003_MIXED:
1039 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1040 elif level_forest == DS_DOMAIN_FUNCTION_2003:
1042 elif level_forest == DS_DOMAIN_FUNCTION_2008:
1044 elif level_forest == DS_DOMAIN_FUNCTION_2008_R2:
1047 outstr = "higher than 2008 R2"
1048 self.message("Forest function level: (Windows) " + outstr)
1050 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1051 outstr = "2000 mixed (NT4 DC support)"
1052 elif level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed == 0:
1054 elif level_domain == DS_DOMAIN_FUNCTION_2003_MIXED:
1055 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1056 elif level_domain == DS_DOMAIN_FUNCTION_2003:
1058 elif level_domain == DS_DOMAIN_FUNCTION_2008:
1060 elif level_domain == DS_DOMAIN_FUNCTION_2008_R2:
1063 outstr = "higher than 2008 R2"
1064 self.message("Domain function level: (Windows) " + outstr)
1066 if min_level_dc == DS_DOMAIN_FUNCTION_2000:
1068 elif min_level_dc == DS_DOMAIN_FUNCTION_2003:
1070 elif min_level_dc == DS_DOMAIN_FUNCTION_2008:
1072 elif min_level_dc == DS_DOMAIN_FUNCTION_2008_R2:
1075 outstr = "higher than 2008 R2"
1076 self.message("Lowest function level of a DC: (Windows) " + outstr)
1078 elif subcommand == "raise":
1081 if domain_level is not None:
1082 if domain_level == "2003":
1083 new_level_domain = DS_DOMAIN_FUNCTION_2003
1084 elif domain_level == "2008":
1085 new_level_domain = DS_DOMAIN_FUNCTION_2008
1086 elif domain_level == "2008_R2":
1087 new_level_domain = DS_DOMAIN_FUNCTION_2008_R2
1089 if new_level_domain <= level_domain and level_domain_mixed == 0:
1090 raise CommandError("Domain function level can't be smaller than or equal to the actual one!")
1092 if new_level_domain > min_level_dc:
1093 raise CommandError("Domain function level can't be higher than the lowest function level of a DC!")
1095 # Deactivate mixed/interim domain support
1096 if level_domain_mixed != 0:
1097 # Directly on the base DN
1099 m.dn = ldb.Dn(samdb, domain_dn)
1100 m["nTMixedDomain"] = ldb.MessageElement("0",
1101 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1105 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup") + ",CN=Partitions,%s" % samdb.get_config_basedn())
1106 m["nTMixedDomain"] = ldb.MessageElement("0",
1107 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1110 except ldb.LdbError, (enum, emsg):
1111 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1114 # Directly on the base DN
1116 m.dn = ldb.Dn(samdb, domain_dn)
1117 m["msDS-Behavior-Version"]= ldb.MessageElement(
1118 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1119 "msDS-Behavior-Version")
1123 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup")
1124 + ",CN=Partitions,%s" % samdb.get_config_basedn())
1125 m["msDS-Behavior-Version"]= ldb.MessageElement(
1126 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1127 "msDS-Behavior-Version")
1130 except ldb.LdbError, (enum, emsg):
1131 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1134 level_domain = new_level_domain
1135 msgs.append("Domain function level changed!")
1137 if forest_level is not None:
1138 if forest_level == "2003":
1139 new_level_forest = DS_DOMAIN_FUNCTION_2003
1140 elif forest_level == "2008":
1141 new_level_forest = DS_DOMAIN_FUNCTION_2008
1142 elif forest_level == "2008_R2":
1143 new_level_forest = DS_DOMAIN_FUNCTION_2008_R2
1144 if new_level_forest <= level_forest:
1145 raise CommandError("Forest function level can't be smaller than or equal to the actual one!")
1146 if new_level_forest > level_domain:
1147 raise CommandError("Forest function level can't be higher than the domain function level(s). Please raise it/them first!")
1149 m.dn = ldb.Dn(samdb, "CN=Partitions,%s" % samdb.get_config_basedn())
1150 m["msDS-Behavior-Version"]= ldb.MessageElement(
1151 str(new_level_forest), ldb.FLAG_MOD_REPLACE,
1152 "msDS-Behavior-Version")
1154 msgs.append("Forest function level changed!")
1155 msgs.append("All changes applied successfully!")
1156 self.message("\n".join(msgs))
1158 raise CommandError("invalid argument: '%s' (choose from 'show', 'raise')" % subcommand)
1161 class cmd_domain_passwordsettings(Command):
1162 """Set password settings.
1164 Password complexity, password lockout policy, history length,
1165 minimum password length, the minimum and maximum password age) on
1166 a Samba AD DC server.
1168 Use against a Windows DC is possible, but group policy will override it.
1171 synopsis = "%prog (show|set <options>) [options]"
1173 takes_optiongroups = {
1174 "sambaopts": options.SambaOptions,
1175 "versionopts": options.VersionOptions,
1176 "credopts": options.CredentialsOptions,
1180 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1181 metavar="URL", dest="H"),
1182 Option("--quiet", help="Be quiet", action="store_true"),
1183 Option("--complexity", type="choice", choices=["on","off","default"],
1184 help="The password complexity (on | off | default). Default is 'on'"),
1185 Option("--store-plaintext", type="choice", choices=["on","off","default"],
1186 help="Store plaintext passwords where account have 'store passwords with reversible encryption' set (on | off | default). Default is 'off'"),
1187 Option("--history-length",
1188 help="The password history length (<integer> | default). Default is 24.", type=str),
1189 Option("--min-pwd-length",
1190 help="The minimum password length (<integer> | default). Default is 7.", type=str),
1191 Option("--min-pwd-age",
1192 help="The minimum password age (<integer in days> | default). Default is 1.", type=str),
1193 Option("--max-pwd-age",
1194 help="The maximum password age (<integer in days> | default). Default is 43.", type=str),
1195 Option("--account-lockout-duration",
1196 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),
1197 Option("--account-lockout-threshold",
1198 help="The number of bad password attempts allowed before locking out the account (<integer> | default). Default is 0 (never lock out).", type=str),
1199 Option("--reset-account-lockout-after",
1200 help="After this time is elapsed, the recorded number of attempts restarts from zero (<integer> | default). Default is 30.", type=str),
1203 takes_args = ["subcommand"]
1205 def run(self, subcommand, H=None, min_pwd_age=None, max_pwd_age=None,
1206 quiet=False, complexity=None, store_plaintext=None, history_length=None,
1207 min_pwd_length=None, account_lockout_duration=None, account_lockout_threshold=None,
1208 reset_account_lockout_after=None, credopts=None, sambaopts=None,
1210 lp = sambaopts.get_loadparm()
1211 creds = credopts.get_credentials(lp)
1213 samdb = SamDB(url=H, session_info=system_session(),
1214 credentials=creds, lp=lp)
1216 domain_dn = samdb.domain_dn()
1217 res = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1218 attrs=["pwdProperties", "pwdHistoryLength", "minPwdLength",
1219 "minPwdAge", "maxPwdAge", "lockoutDuration", "lockoutThreshold",
1220 "lockOutObservationWindow"])
1221 assert(len(res) == 1)
1223 pwd_props = int(res[0]["pwdProperties"][0])
1224 pwd_hist_len = int(res[0]["pwdHistoryLength"][0])
1225 cur_min_pwd_len = int(res[0]["minPwdLength"][0])
1227 cur_min_pwd_age = int(abs(int(res[0]["minPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1228 if int(res[0]["maxPwdAge"][0]) == -0x8000000000000000:
1231 cur_max_pwd_age = int(abs(int(res[0]["maxPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1232 cur_account_lockout_threshold = int(res[0]["lockoutThreshold"][0])
1234 if int(res[0]["lockoutDuration"][0]) == -0x8000000000000000:
1235 cur_account_lockout_duration = 0
1237 cur_account_lockout_duration = abs(int(res[0]["lockoutDuration"][0])) / (1e7 * 60)
1238 cur_reset_account_lockout_after = abs(int(res[0]["lockOutObservationWindow"][0])) / (1e7 * 60)
1239 except Exception, e:
1240 raise CommandError("Could not retrieve password properties!", e)
1242 if subcommand == "show":
1243 self.message("Password informations for domain '%s'" % domain_dn)
1245 if pwd_props & DOMAIN_PASSWORD_COMPLEX != 0:
1246 self.message("Password complexity: on")
1248 self.message("Password complexity: off")
1249 if pwd_props & DOMAIN_PASSWORD_STORE_CLEARTEXT != 0:
1250 self.message("Store plaintext passwords: on")
1252 self.message("Store plaintext passwords: off")
1253 self.message("Password history length: %d" % pwd_hist_len)
1254 self.message("Minimum password length: %d" % cur_min_pwd_len)
1255 self.message("Minimum password age (days): %d" % cur_min_pwd_age)
1256 self.message("Maximum password age (days): %d" % cur_max_pwd_age)
1257 self.message("Account lockout duration (mins): %d" % cur_account_lockout_duration)
1258 self.message("Account lockout threshold (attempts): %d" % cur_account_lockout_threshold)
1259 self.message("Reset account lockout after (mins): %d" % cur_reset_account_lockout_after)
1260 elif subcommand == "set":
1263 m.dn = ldb.Dn(samdb, domain_dn)
1265 if complexity is not None:
1266 if complexity == "on" or complexity == "default":
1267 pwd_props = pwd_props | DOMAIN_PASSWORD_COMPLEX
1268 msgs.append("Password complexity activated!")
1269 elif complexity == "off":
1270 pwd_props = pwd_props & (~DOMAIN_PASSWORD_COMPLEX)
1271 msgs.append("Password complexity deactivated!")
1273 if store_plaintext is not None:
1274 if store_plaintext == "on" or store_plaintext == "default":
1275 pwd_props = pwd_props | DOMAIN_PASSWORD_STORE_CLEARTEXT
1276 msgs.append("Plaintext password storage for changed passwords activated!")
1277 elif store_plaintext == "off":
1278 pwd_props = pwd_props & (~DOMAIN_PASSWORD_STORE_CLEARTEXT)
1279 msgs.append("Plaintext password storage for changed passwords deactivated!")
1281 if complexity is not None or store_plaintext is not None:
1282 m["pwdProperties"] = ldb.MessageElement(str(pwd_props),
1283 ldb.FLAG_MOD_REPLACE, "pwdProperties")
1285 if history_length is not None:
1286 if history_length == "default":
1289 pwd_hist_len = int(history_length)
1291 if pwd_hist_len < 0 or pwd_hist_len > 24:
1292 raise CommandError("Password history length must be in the range of 0 to 24!")
1294 m["pwdHistoryLength"] = ldb.MessageElement(str(pwd_hist_len),
1295 ldb.FLAG_MOD_REPLACE, "pwdHistoryLength")
1296 msgs.append("Password history length changed!")
1298 if min_pwd_length is not None:
1299 if min_pwd_length == "default":
1302 min_pwd_len = int(min_pwd_length)
1304 if min_pwd_len < 0 or min_pwd_len > 14:
1305 raise CommandError("Minimum password length must be in the range of 0 to 14!")
1307 m["minPwdLength"] = ldb.MessageElement(str(min_pwd_len),
1308 ldb.FLAG_MOD_REPLACE, "minPwdLength")
1309 msgs.append("Minimum password length changed!")
1311 if min_pwd_age is not None:
1312 if min_pwd_age == "default":
1315 min_pwd_age = int(min_pwd_age)
1317 if min_pwd_age < 0 or min_pwd_age > 998:
1318 raise CommandError("Minimum password age must be in the range of 0 to 998!")
1321 min_pwd_age_ticks = -int(min_pwd_age * (24 * 60 * 60 * 1e7))
1323 m["minPwdAge"] = ldb.MessageElement(str(min_pwd_age_ticks),
1324 ldb.FLAG_MOD_REPLACE, "minPwdAge")
1325 msgs.append("Minimum password age changed!")
1327 if max_pwd_age is not None:
1328 if max_pwd_age == "default":
1331 max_pwd_age = int(max_pwd_age)
1333 if max_pwd_age < 0 or max_pwd_age > 999:
1334 raise CommandError("Maximum password age must be in the range of 0 to 999!")
1337 if max_pwd_age == 0:
1338 max_pwd_age_ticks = -0x8000000000000000
1340 max_pwd_age_ticks = -int(max_pwd_age * (24 * 60 * 60 * 1e7))
1342 m["maxPwdAge"] = ldb.MessageElement(str(max_pwd_age_ticks),
1343 ldb.FLAG_MOD_REPLACE, "maxPwdAge")
1344 msgs.append("Maximum password age changed!")
1346 if account_lockout_duration is not None:
1347 if account_lockout_duration == "default":
1348 account_lockout_duration = 30
1350 account_lockout_duration = int(account_lockout_duration)
1352 if account_lockout_duration < 0 or account_lockout_duration > 99999:
1353 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1356 if account_lockout_duration == 0:
1357 account_lockout_duration_ticks = -0x8000000000000000
1359 account_lockout_duration_ticks = -int(account_lockout_duration * (60 * 1e7))
1361 m["lockoutDuration"] = ldb.MessageElement(str(account_lockout_duration_ticks),
1362 ldb.FLAG_MOD_REPLACE, "lockoutDuration")
1363 msgs.append("Account lockout duration changed!")
1365 if account_lockout_threshold is not None:
1366 if account_lockout_threshold == "default":
1367 account_lockout_threshold = 0
1369 account_lockout_threshold = int(account_lockout_threshold)
1371 m["lockoutThreshold"] = ldb.MessageElement(str(account_lockout_threshold),
1372 ldb.FLAG_MOD_REPLACE, "lockoutThreshold")
1373 msgs.append("Account lockout threshold changed!")
1375 if reset_account_lockout_after is not None:
1376 if reset_account_lockout_after == "default":
1377 reset_account_lockout_after = 30
1379 reset_account_lockout_after = int(reset_account_lockout_after)
1381 if reset_account_lockout_after < 0 or reset_account_lockout_after > 99999:
1382 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1385 if reset_account_lockout_after == 0:
1386 reset_account_lockout_after_ticks = -0x8000000000000000
1388 reset_account_lockout_after_ticks = -int(reset_account_lockout_after * (60 * 1e7))
1390 m["lockOutObservationWindow"] = ldb.MessageElement(str(reset_account_lockout_after_ticks),
1391 ldb.FLAG_MOD_REPLACE, "lockOutObservationWindow")
1392 msgs.append("Duration to reset account lockout after changed!")
1394 if max_pwd_age > 0 and min_pwd_age >= max_pwd_age:
1395 raise CommandError("Maximum password age (%d) must be greater than minimum password age (%d)!" % (max_pwd_age, min_pwd_age))
1398 raise CommandError("You must specify at least one option to set. Try --help")
1400 msgs.append("All changes applied successfully!")
1401 self.message("\n".join(msgs))
1403 raise CommandError("Wrong argument '%s'!" % subcommand)
1406 class cmd_domain_classicupgrade(Command):
1407 """Upgrade from Samba classic (NT4-like) database to Samba AD DC database.
1409 Specify either a directory with all Samba classic DC databases and state files (with --dbdir) or
1410 the testparm utility from your classic installation (with --testparm).
1413 synopsis = "%prog [options] <classic_smb_conf>"
1415 takes_optiongroups = {
1416 "sambaopts": options.SambaOptions,
1417 "versionopts": options.VersionOptions
1421 Option("--dbdir", type="string", metavar="DIR",
1422 help="Path to samba classic DC database directory"),
1423 Option("--testparm", type="string", metavar="PATH",
1424 help="Path to samba classic DC testparm utility from the previous installation. This allows the default paths of the previous installation to be followed"),
1425 Option("--targetdir", type="string", metavar="DIR",
1426 help="Path prefix where the new Samba 4.0 AD domain should be initialised"),
1427 Option("--quiet", help="Be quiet", action="store_true"),
1428 Option("--verbose", help="Be verbose", action="store_true"),
1429 Option("--use-xattrs", type="choice", choices=["yes","no","auto"], metavar="[yes|no|auto]",
1430 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"),
1431 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
1432 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
1433 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
1434 "BIND9_FLATFILE uses bind9 text database to store zone information, "
1435 "BIND9_DLZ uses samba4 AD to store zone information, "
1436 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
1437 default="SAMBA_INTERNAL")
1441 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
1442 action="store_true")
1444 if samba.is_ntvfs_fileserver_built():
1445 takes_options.extend(ntvfs_options)
1447 takes_args = ["smbconf"]
1449 def run(self, smbconf=None, targetdir=None, dbdir=None, testparm=None,
1450 quiet=False, verbose=False, use_xattrs=None, sambaopts=None, versionopts=None,
1451 dns_backend=None, use_ntvfs=False):
1453 if not os.path.exists(smbconf):
1454 raise CommandError("File %s does not exist" % smbconf)
1456 if testparm and not os.path.exists(testparm):
1457 raise CommandError("Testparm utility %s does not exist" % testparm)
1459 if dbdir and not os.path.exists(dbdir):
1460 raise CommandError("Directory %s does not exist" % dbdir)
1462 if not dbdir and not testparm:
1463 raise CommandError("Please specify either dbdir or testparm")
1465 logger = self.get_logger()
1467 logger.setLevel(logging.DEBUG)
1469 logger.setLevel(logging.WARNING)
1471 logger.setLevel(logging.INFO)
1473 if dbdir and testparm:
1474 logger.warning("both dbdir and testparm specified, ignoring dbdir.")
1477 lp = sambaopts.get_loadparm()
1479 s3conf = s3param.get_context()
1482 s3conf.set("realm", sambaopts.realm)
1484 if targetdir is not None:
1485 if not os.path.isdir(targetdir):
1489 if use_xattrs == "yes":
1491 elif use_xattrs == "auto" and not s3conf.get("posix:eadb"):
1493 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
1495 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
1498 samba.ntacls.setntacl(lp, tmpfile.name,
1499 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
1502 # FIXME: Don't catch all exceptions here
1503 logger.info("You are not root or your system do not support xattr, using tdb backend for attributes. "
1504 "If you intend to use this provision in production, rerun the script as root on a system supporting xattrs.")
1508 # Set correct default values from dbdir or testparm
1511 paths["state directory"] = dbdir
1512 paths["private dir"] = dbdir
1513 paths["lock directory"] = dbdir
1514 paths["smb passwd file"] = dbdir + "/smbpasswd"
1516 paths["state directory"] = get_testparm_var(testparm, smbconf, "state directory")
1517 paths["private dir"] = get_testparm_var(testparm, smbconf, "private dir")
1518 paths["smb passwd file"] = get_testparm_var(testparm, smbconf, "smb passwd file")
1519 paths["lock directory"] = get_testparm_var(testparm, smbconf, "lock directory")
1520 # "testparm" from Samba 3 < 3.4.x is not aware of the parameter
1521 # "state directory", instead make use of "lock directory"
1522 if len(paths["state directory"]) == 0:
1523 paths["state directory"] = paths["lock directory"]
1526 s3conf.set(p, paths[p])
1528 # load smb.conf parameters
1529 logger.info("Reading smb.conf")
1530 s3conf.load(smbconf)
1531 samba3 = Samba3(smbconf, s3conf)
1533 logger.info("Provisioning")
1534 upgrade_from_samba3(samba3, logger, targetdir, session_info=system_session(),
1535 useeadb=eadb, dns_backend=dns_backend, use_ntvfs=use_ntvfs)
1538 class cmd_domain_samba3upgrade(cmd_domain_classicupgrade):
1539 __doc__ = cmd_domain_classicupgrade.__doc__
1541 # This command is present for backwards compatibility only,
1542 # and should not be shown.
1546 class LocalDCCredentialsOptions(options.CredentialsOptions):
1547 def __init__(self, parser):
1548 options.CredentialsOptions.__init__(self, parser, special_name="local-dc")
1550 class DomainTrustCommand(Command):
1551 """List domain trusts."""
1554 Command.__init__(self)
1555 self.local_lp = None
1557 self.local_server = None
1558 self.local_binding_string = None
1559 self.local_creds = None
1561 self.remote_server = None
1562 self.remote_binding_string = None
1563 self.remote_creds = None
1565 WERR_OK = 0x00000000
1566 WERR_INVALID_FUNCTION = 0x00000001
1567 WERR_NERR_ACFNOTLOADED = 0x000008B3
1569 NT_STATUS_NOT_FOUND = 0xC0000225
1570 NT_STATUS_OBJECT_NAME_NOT_FOUND = 0xC0000034
1571 NT_STATUS_INVALID_PARAMETER = 0xC000000D
1572 NT_STATUS_INVALID_INFO_CLASS = 0xC0000003
1573 NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE = 0xC002002E
1575 def _uint32(self, v):
1576 return ctypes.c_uint32(v).value
1578 def check_runtime_error(self, runtime, val):
1582 err32 = self._uint32(runtime[0])
1588 class LocalRuntimeError(CommandError):
1589 def __init__(exception_self, self, runtime, message):
1590 err32 = self._uint32(runtime[0])
1592 msg = "LOCAL_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1593 self.local_server, message, err32, errstr)
1594 CommandError.__init__(exception_self, msg)
1596 class RemoteRuntimeError(CommandError):
1597 def __init__(exception_self, self, runtime, message):
1598 err32 = self._uint32(runtime[0])
1600 msg = "REMOTE_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1601 self.remote_server, message, err32, errstr)
1602 CommandError.__init__(exception_self, msg)
1604 class LocalLdbError(CommandError):
1605 def __init__(exception_self, self, ldb_error, message):
1606 errval = ldb_error[0]
1607 errstr = ldb_error[1]
1608 msg = "LOCAL_DC[%s]: %s - ERROR(%d) - %s" % (
1609 self.local_server, message, errval, errstr)
1610 CommandError.__init__(exception_self, msg)
1612 def setup_local_server(self, sambaopts, localdcopts):
1613 if self.local_server is not None:
1614 return self.local_server
1616 lp = sambaopts.get_loadparm()
1618 local_server = localdcopts.ipaddress
1619 if local_server is None:
1620 server_role = lp.server_role()
1621 if server_role != "ROLE_ACTIVE_DIRECTORY_DC":
1622 raise CommandError("Invalid server_role %s" % (server_role))
1623 local_server = lp.get('netbios name')
1624 local_transport = "ncalrpc"
1625 local_binding_options = ""
1626 local_binding_options += ",auth_type=ncalrpc_as_system"
1627 local_ldap_url = None
1630 local_transport = "ncacn_np"
1631 local_binding_options = ""
1632 local_ldap_url = "ldap://%s" % local_server
1633 local_creds = localdcopts.get_credentials(lp)
1637 self.local_server = local_server
1638 self.local_binding_string = "%s:%s[%s]" % (local_transport, local_server, local_binding_options)
1639 self.local_ldap_url = local_ldap_url
1640 self.local_creds = local_creds
1641 return self.local_server
1643 def new_local_lsa_connection(self):
1644 return lsa.lsarpc(self.local_binding_string, self.local_lp, self.local_creds)
1646 def new_local_netlogon_connection(self):
1647 return netlogon.netlogon(self.local_binding_string, self.local_lp, self.local_creds)
1649 def new_local_ldap_connection(self):
1650 return SamDB(url=self.local_ldap_url,
1651 session_info=system_session(),
1652 credentials=self.local_creds,
1655 def setup_remote_server(self, credopts, domain,
1657 require_writable=True):
1660 assert require_writable
1662 if self.remote_server is not None:
1663 return self.remote_server
1665 self.remote_server = "__unknown__remote_server__.%s" % domain
1666 assert self.local_server is not None
1668 remote_creds = credopts.get_credentials(self.local_lp)
1669 remote_server = credopts.ipaddress
1670 remote_binding_options = ""
1672 # TODO: we should also support NT4 domains
1673 # we could use local_netlogon.netr_DsRGetDCNameEx2() with the remote domain name
1674 # and delegate NBT or CLDAP to the local netlogon server
1676 remote_net = Net(remote_creds, self.local_lp, server=remote_server)
1677 remote_flags = nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS
1678 if require_writable:
1679 remote_flags |= nbt.NBT_SERVER_WRITABLE
1681 remote_flags |= nbt.NBT_SERVER_PDC
1682 remote_info = remote_net.finddc(flags=remote_flags, domain=domain, address=remote_server)
1684 raise CommandError("Failed to find a writeable DC for domain '%s'" % domain)
1686 nbt.NBT_SERVER_PDC: "PDC",
1687 nbt.NBT_SERVER_GC: "GC",
1688 nbt.NBT_SERVER_LDAP: "LDAP",
1689 nbt.NBT_SERVER_DS: "DS",
1690 nbt.NBT_SERVER_KDC: "KDC",
1691 nbt.NBT_SERVER_TIMESERV: "TIMESERV",
1692 nbt.NBT_SERVER_CLOSEST: "CLOSEST",
1693 nbt.NBT_SERVER_WRITABLE: "WRITABLE",
1694 nbt.NBT_SERVER_GOOD_TIMESERV: "GOOD_TIMESERV",
1695 nbt.NBT_SERVER_NDNC: "NDNC",
1696 nbt.NBT_SERVER_SELECT_SECRET_DOMAIN_6: "SELECT_SECRET_DOMAIN_6",
1697 nbt.NBT_SERVER_FULL_SECRET_DOMAIN_6: "FULL_SECRET_DOMAIN_6",
1698 nbt.NBT_SERVER_ADS_WEB_SERVICE: "ADS_WEB_SERVICE",
1699 nbt.NBT_SERVER_DS_8: "DS_8",
1700 nbt.NBT_SERVER_HAS_DNS_NAME: "HAS_DNS_NAME",
1701 nbt.NBT_SERVER_IS_DEFAULT_NC: "IS_DEFAULT_NC",
1702 nbt.NBT_SERVER_FOREST_ROOT: "FOREST_ROOT",
1704 server_type_string = self.generic_bitmap_to_string(flag_map,
1705 remote_info.server_type, names_only=True)
1706 self.outf.write("RemoteDC Netbios[%s] DNS[%s] ServerType[%s]\n" % (
1707 remote_info.pdc_name,
1708 remote_info.pdc_dns_name,
1709 server_type_string))
1711 self.remote_server = remote_info.pdc_dns_name
1712 self.remote_binding_string="ncacn_np:%s[%s]" % (self.remote_server, remote_binding_options)
1713 self.remote_creds = remote_creds
1714 return self.remote_server
1716 def new_remote_lsa_connection(self):
1717 return lsa.lsarpc(self.remote_binding_string, self.local_lp, self.remote_creds)
1719 def new_remote_netlogon_connection(self):
1720 return netlogon.netlogon(self.remote_binding_string, self.local_lp, self.remote_creds)
1722 def get_lsa_info(self, conn, policy_access):
1723 objectAttr = lsa.ObjectAttribute()
1724 objectAttr.sec_qos = lsa.QosInfo()
1726 policy = conn.OpenPolicy2(''.decode('utf-8'),
1727 objectAttr, policy_access)
1729 info = conn.QueryInfoPolicy2(policy, lsa.LSA_POLICY_INFO_DNS)
1731 return (policy, info)
1733 def get_netlogon_dc_info(self, conn, server):
1734 info = conn.netr_DsRGetDCNameEx2(server,
1735 None, 0, None, None, None,
1736 netlogon.DS_RETURN_DNS_NAME)
1739 def netr_DomainTrust_to_name(self, t):
1740 if t.trust_type == lsa.LSA_TRUST_TYPE_DOWNLEVEL:
1741 return t.netbios_name
1745 def netr_DomainTrust_to_type(self, a, t):
1747 primary_parent = None
1749 if _t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
1751 if not _t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1752 primary_parent = a[_t.parent_index]
1755 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1756 if t is primary_parent:
1759 if t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1762 parent = a[t.parent_index]
1763 if parent is primary:
1768 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1773 def netr_DomainTrust_to_transitive(self, t):
1774 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1777 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE:
1780 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1785 def netr_DomainTrust_to_direction(self, t):
1786 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND and \
1787 t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1790 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND:
1793 if t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1798 def generic_enum_to_string(self, e_dict, v, names_only=False):
1802 v32 = self._uint32(v)
1803 w = "__unknown__%08X__" % v32
1805 r = "0x%x (%s)" % (v, w)
1808 def generic_bitmap_to_string(self, b_dict, v, names_only=False):
1813 for b in sorted(b_dict.keys()):
1820 c32 = self._uint32(c)
1821 s += ["__unknown_%08X__" % c32]
1826 r = "0x%x (%s)" % (v, w)
1829 def trustType_string(self, v):
1831 lsa.LSA_TRUST_TYPE_DOWNLEVEL : "DOWNLEVEL",
1832 lsa.LSA_TRUST_TYPE_UPLEVEL : "UPLEVEL",
1833 lsa.LSA_TRUST_TYPE_MIT : "MIT",
1834 lsa.LSA_TRUST_TYPE_DCE : "DCE",
1836 return self.generic_enum_to_string(types, v)
1838 def trustDirection_string(self, v):
1840 lsa.LSA_TRUST_DIRECTION_INBOUND |
1841 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "BOTH",
1842 lsa.LSA_TRUST_DIRECTION_INBOUND : "INBOUND",
1843 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "OUTBOUND",
1845 return self.generic_enum_to_string(directions, v)
1847 def trustAttributes_string(self, v):
1849 lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE : "NON_TRANSITIVE",
1850 lsa.LSA_TRUST_ATTRIBUTE_UPLEVEL_ONLY : "UPLEVEL_ONLY",
1851 lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN : "QUARANTINED_DOMAIN",
1852 lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE : "FOREST_TRANSITIVE",
1853 lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION : "CROSS_ORGANIZATION",
1854 lsa.LSA_TRUST_ATTRIBUTE_WITHIN_FOREST : "WITHIN_FOREST",
1855 lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL : "TREAT_AS_EXTERNAL",
1856 lsa.LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION : "USES_RC4_ENCRYPTION",
1858 return self.generic_bitmap_to_string(attributes, v)
1860 def kerb_EncTypes_string(self, v):
1862 security.KERB_ENCTYPE_DES_CBC_CRC : "DES_CBC_CRC",
1863 security.KERB_ENCTYPE_DES_CBC_MD5 : "DES_CBC_MD5",
1864 security.KERB_ENCTYPE_RC4_HMAC_MD5 : "RC4_HMAC_MD5",
1865 security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96 : "AES128_CTS_HMAC_SHA1_96",
1866 security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96 : "AES256_CTS_HMAC_SHA1_96",
1867 security.KERB_ENCTYPE_FAST_SUPPORTED : "FAST_SUPPORTED",
1868 security.KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED : "COMPOUND_IDENTITY_SUPPORTED",
1869 security.KERB_ENCTYPE_CLAIMS_SUPPORTED : "CLAIMS_SUPPORTED",
1870 security.KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED : "RESOURCE_SID_COMPRESSION_DISABLED",
1872 return self.generic_bitmap_to_string(enctypes, v)
1874 def entry_tln_status(self, e_flags, ):
1876 return "Status[Enabled]"
1879 lsa.LSA_TLN_DISABLED_NEW : "Disabled-New",
1880 lsa.LSA_TLN_DISABLED_ADMIN : "Disabled",
1881 lsa.LSA_TLN_DISABLED_CONFLICT : "Disabled-Conflicting",
1883 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
1885 def entry_dom_status(self, e_flags):
1887 return "Status[Enabled]"
1890 lsa.LSA_SID_DISABLED_ADMIN : "Disabled-SID",
1891 lsa.LSA_SID_DISABLED_CONFLICT : "Disabled-SID-Conflicting",
1892 lsa.LSA_NB_DISABLED_ADMIN : "Disabled-NB",
1893 lsa.LSA_NB_DISABLED_CONFLICT : "Disabled-NB-Conflicting",
1895 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
1897 def write_forest_trust_info(self, fti, tln=None, collisions=None):
1899 tln_string = " TDO[%s]" % tln
1903 self.outf.write("Namespaces[%d]%s:\n" % (
1904 len(fti.entries), tln_string))
1906 for i in xrange(0, len(fti.entries)):
1910 collision_string = ""
1912 if collisions is not None:
1913 for c in collisions.entries:
1917 collision_string = " Collision[%s]" % (c.name.string)
1919 d = e.forest_trust_data
1920 if e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
1921 self.outf.write("TLN: %-32s DNS[*.%s]%s\n" % (
1922 self.entry_tln_status(flags),
1923 d.string, collision_string))
1924 elif e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
1925 self.outf.write("TLN_EX: %-29s DNS[*.%s]\n" % (
1927 elif e.type == lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
1928 self.outf.write("DOM: %-32s DNS[%s] Netbios[%s] SID[%s]%s\n" % (
1929 self.entry_dom_status(flags),
1930 d.dns_domain_name.string,
1931 d.netbios_domain_name.string,
1932 d.domain_sid, collision_string))
1935 class cmd_domain_trust_list(DomainTrustCommand):
1936 """List domain trusts."""
1938 synopsis = "%prog [options]"
1940 takes_optiongroups = {
1941 "sambaopts": options.SambaOptions,
1942 "versionopts": options.VersionOptions,
1943 "localdcopts": LocalDCCredentialsOptions,
1949 def run(self, sambaopts=None, versionopts=None, localdcopts=None):
1951 local_server = self.setup_local_server(sambaopts, localdcopts)
1953 local_netlogon = self.new_local_netlogon_connection()
1954 except RuntimeError as error:
1955 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
1958 local_netlogon_trusts = local_netlogon.netr_DsrEnumerateDomainTrusts(local_server,
1959 netlogon.NETR_TRUST_FLAG_IN_FOREST |
1960 netlogon.NETR_TRUST_FLAG_OUTBOUND |
1961 netlogon.NETR_TRUST_FLAG_INBOUND)
1962 except RuntimeError as error:
1963 if self.check_runtime_error(error, self.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
1964 # TODO: we could implement a fallback to lsa.EnumTrustDom()
1965 raise CommandError("LOCAL_DC[%s]: netr_DsrEnumerateDomainTrusts not supported." % (
1967 raise self.LocalRuntimeError(self, error, "netr_DsrEnumerateDomainTrusts failed")
1969 a = local_netlogon_trusts.array
1971 if t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
1973 self.outf.write("%-14s %-15s %-19s %s\n" % (
1974 "Type[%s]" % self.netr_DomainTrust_to_type(a, t),
1975 "Transitive[%s]" % self.netr_DomainTrust_to_transitive(t),
1976 "Direction[%s]" % self.netr_DomainTrust_to_direction(t),
1977 "Name[%s]" % self.netr_DomainTrust_to_name(t)))
1980 class cmd_domain_trust_show(DomainTrustCommand):
1981 """Show trusted domain details."""
1983 synopsis = "%prog NAME [options]"
1985 takes_optiongroups = {
1986 "sambaopts": options.SambaOptions,
1987 "versionopts": options.VersionOptions,
1988 "localdcopts": LocalDCCredentialsOptions,
1994 takes_args = ["domain"]
1996 def run(self, domain, sambaopts=None, versionopts=None, localdcopts=None):
1998 local_server = self.setup_local_server(sambaopts, localdcopts)
2000 local_lsa = self.new_local_lsa_connection()
2001 except RuntimeError as error:
2002 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2005 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2006 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2007 except RuntimeError as error:
2008 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2010 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2011 local_lsa_info.name.string,
2012 local_lsa_info.dns_domain.string,
2013 local_lsa_info.sid))
2015 lsaString = lsa.String()
2016 lsaString.string = domain
2018 local_tdo_full = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2019 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2020 local_tdo_info = local_tdo_full.info_ex
2021 local_tdo_posix = local_tdo_full.posix_offset
2022 except RuntimeError as error:
2023 if self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2024 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2026 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(FULL_INFO) failed")
2029 local_tdo_enctypes = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2030 lsaString, lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES)
2031 except RuntimeError as error:
2032 if self.check_runtime_error(error, self.NT_STATUS_INVALID_PARAMETER):
2034 if self.check_runtime_error(error, self.NT_STATUS_INVALID_INFO_CLASS):
2037 if error is not None:
2038 raise self.LocalRuntimeError(self, error,
2039 "QueryTrustedDomainInfoByName(SUPPORTED_ENCRYPTION_TYPES) failed")
2041 local_tdo_enctypes = lsa.TrustDomainInfoSupportedEncTypes()
2042 local_tdo_enctypes.enc_types = 0
2045 local_tdo_forest = None
2046 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2047 local_tdo_forest = local_lsa.lsaRQueryForestTrustInformation(local_policy,
2048 lsaString, lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
2049 except RuntimeError as error:
2050 if self.check_runtime_error(error, self.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
2052 if self.check_runtime_error(error, self.NT_STATUS_NOT_FOUND):
2054 if error is not None:
2055 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation failed")
2057 local_tdo_forest = lsa.ForestTrustInformation()
2058 local_tdo_forest.count = 0
2059 local_tdo_forest.entries = []
2061 self.outf.write("TrusteDomain:\n\n");
2062 self.outf.write("NetbiosName: %s\n" % local_tdo_info.netbios_name.string)
2063 if local_tdo_info.netbios_name.string != local_tdo_info.domain_name.string:
2064 self.outf.write("DnsName: %s\n" % local_tdo_info.domain_name.string)
2065 self.outf.write("SID: %s\n" % local_tdo_info.sid)
2066 self.outf.write("Type: %s\n" % self.trustType_string(local_tdo_info.trust_type))
2067 self.outf.write("Direction: %s\n" % self.trustDirection_string(local_tdo_info.trust_direction))
2068 self.outf.write("Attributes: %s\n" % self.trustAttributes_string(local_tdo_info.trust_attributes))
2069 posix_offset_u32 = ctypes.c_uint32(local_tdo_posix.posix_offset).value
2070 posix_offset_i32 = ctypes.c_int32(local_tdo_posix.posix_offset).value
2071 self.outf.write("PosixOffset: 0x%08X (%d)\n" % (posix_offset_u32, posix_offset_i32))
2072 self.outf.write("kerb_EncTypes: %s\n" % self.kerb_EncTypes_string(local_tdo_enctypes.enc_types))
2074 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2075 self.write_forest_trust_info(local_tdo_forest,
2076 tln=local_tdo_info.domain_name.string)
2080 class cmd_domain_trust_create(DomainTrustCommand):
2081 """Create a domain or forest trust."""
2083 synopsis = "%prog DOMAIN [options]"
2085 takes_optiongroups = {
2086 "sambaopts": options.SambaOptions,
2087 "versionopts": options.VersionOptions,
2088 "credopts": options.CredentialsOptions,
2089 "localdcopts": LocalDCCredentialsOptions,
2093 Option("--type", type="choice", metavar="TYPE",
2094 choices=["external", "forest"],
2095 help="The type of the trust: 'external' or 'forest'.",
2097 default="external"),
2098 Option("--direction", type="choice", metavar="DIRECTION",
2099 choices=["incoming", "outgoing", "both"],
2100 help="The trust direction: 'incoming', 'outgoing' or 'both'.",
2101 dest='trust_direction',
2103 Option("--create-location", type="choice", metavar="LOCATION",
2104 choices=["local", "both"],
2105 help="Where to create the trusted domain object: 'local' or 'both'.",
2106 dest='create_location',
2108 Option("--cross-organisation", action="store_true",
2109 help="The related domains does not belong to the same organisation.",
2110 dest='cross_organisation',
2112 Option("--quarantined", type="choice", metavar="yes|no",
2113 choices=["yes", "no", None],
2114 help="Special SID filtering rules are applied to the trust. "
2115 "With --type=external the default is yes. "
2116 "With --type=forest the default is no.",
2117 dest='quarantined_arg',
2119 Option("--not-transitive", action="store_true",
2120 help="The forest trust is not transitive.",
2121 dest='not_transitive',
2123 Option("--treat-as-external", action="store_true",
2124 help="The treat the forest trust as external.",
2125 dest='treat_as_external',
2127 Option("--no-aes-keys", action="store_false",
2128 help="The trust uses aes kerberos keys.",
2129 dest='use_aes_keys',
2131 Option("--skip-validation", action="store_false",
2132 help="Skip validation of the trust.",
2137 takes_args = ["domain"]
2139 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2140 trust_type=None, trust_direction=None, create_location=None,
2141 cross_organisation=False, quarantined_arg=None,
2142 not_transitive=False, treat_as_external=False,
2143 use_aes_keys=False, validate=True):
2145 lsaString = lsa.String()
2148 if quarantined_arg is None:
2149 if trust_type == 'external':
2151 elif quarantined_arg == 'yes':
2154 if trust_type != 'forest':
2156 raise CommandError("--not-transitive requires --type=forest")
2157 if treat_as_external:
2158 raise CommandError("--treat-as-external requires --type=forest")
2162 enc_types = lsa.TrustDomainInfoSupportedEncTypes()
2163 enc_types.enc_types = security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96
2164 enc_types.enc_types |= security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96
2166 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2167 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2168 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2170 local_trust_info = lsa.TrustDomainInfoInfoEx()
2171 local_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2172 local_trust_info.trust_direction = 0
2173 if trust_direction == "both":
2174 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2175 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2176 elif trust_direction == "incoming":
2177 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2178 elif trust_direction == "outgoing":
2179 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2180 local_trust_info.trust_attributes = 0
2181 if cross_organisation:
2182 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2184 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2185 if trust_type == "forest":
2186 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2188 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2189 if treat_as_external:
2190 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2192 def get_password(name):
2195 if password is not None and password is not '':
2197 password = getpass("New %s Password: " % name)
2198 passwordverify = getpass("Retype %s Password: " % name)
2199 if not password == passwordverify:
2201 self.outf.write("Sorry, passwords do not match.\n")
2203 def string_to_array(string):
2204 blob = [0] * len(string)
2206 for i in range(len(string)):
2207 blob[i] = ord(string[i])
2211 incoming_secret = None
2212 outgoing_secret = None
2213 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2214 if create_location == "local":
2215 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2216 incoming_password = get_password("Incoming Trust")
2217 incoming_secret = string_to_array(incoming_password.encode('utf-16-le'))
2218 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2219 outgoing_password = get_password("Outgoing Trust")
2220 outgoing_secret = string_to_array(outgoing_password.encode('utf-16-le'))
2222 remote_trust_info = None
2224 # We use 240 random bytes.
2225 # Windows uses 28 or 240 random bytes. I guess it's
2226 # based on the trust type external vs. forest.
2228 # The initial trust password can be up to 512 bytes
2229 # while the versioned passwords used for periodic updates
2230 # can only be up to 498 bytes, as netr_ServerPasswordSet2()
2231 # needs to pass the NL_PASSWORD_VERSION structure within the
2232 # 512 bytes and a 2 bytes confounder is required.
2234 def random_trust_secret(length, use_aes_keys=True):
2235 secret = [0] * length
2237 pw1 = samba.generate_random_password(length/2, length/2)
2238 if not use_aes_keys:
2239 # With arcfour-hmac-md5 we have to use valid utf16
2240 # in order to generate the correct pre-auth key
2241 # based on a utf8 password.
2243 # We can remove this once our client libraries
2244 # support using the correct NTHASH.
2245 return string_to_array(pw1.encode('utf-16-le'))
2247 # We mix characters from generate_random_password
2248 # with random numbers from random.randint()
2249 for i in range(len(secret)):
2251 secret[i] = ord(pw1[i])
2253 secret[i] = random.randint(0, 255)
2257 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2258 incoming_secret = random_trust_secret(240, use_aes_keys=use_aes_keys)
2259 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2260 outgoing_secret = random_trust_secret(240, use_aes_keys=use_aes_keys)
2262 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2263 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2265 remote_trust_info = lsa.TrustDomainInfoInfoEx()
2266 remote_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2267 remote_trust_info.trust_direction = 0
2268 if trust_direction == "both":
2269 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2270 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2271 elif trust_direction == "incoming":
2272 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2273 elif trust_direction == "outgoing":
2274 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2275 remote_trust_info.trust_attributes = 0
2276 if cross_organisation:
2277 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2279 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2280 if trust_type == "forest":
2281 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2283 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2284 if treat_as_external:
2285 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2287 local_server = self.setup_local_server(sambaopts, localdcopts)
2289 local_lsa = self.new_local_lsa_connection()
2290 except RuntimeError as error:
2291 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2294 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2295 except RuntimeError as error:
2296 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2298 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2299 local_lsa_info.name.string,
2300 local_lsa_info.dns_domain.string,
2301 local_lsa_info.sid))
2304 remote_server = self.setup_remote_server(credopts, domain)
2305 except RuntimeError as error:
2306 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2309 remote_lsa = self.new_remote_lsa_connection()
2310 except RuntimeError as error:
2311 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2314 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2315 except RuntimeError as error:
2316 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2318 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2319 remote_lsa_info.name.string,
2320 remote_lsa_info.dns_domain.string,
2321 remote_lsa_info.sid))
2323 local_trust_info.domain_name.string = remote_lsa_info.dns_domain.string
2324 local_trust_info.netbios_name.string = remote_lsa_info.name.string
2325 local_trust_info.sid = remote_lsa_info.sid
2327 if remote_trust_info:
2328 remote_trust_info.domain_name.string = local_lsa_info.dns_domain.string
2329 remote_trust_info.netbios_name.string = local_lsa_info.name.string
2330 remote_trust_info.sid = local_lsa_info.sid
2333 lsaString.string = local_trust_info.domain_name.string
2334 local_old_netbios = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2335 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2336 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2337 except RuntimeError as error:
2338 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2339 raise self.LocalRuntimeError(self, error,
2340 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2344 lsaString.string = local_trust_info.netbios_name.string
2345 local_old_dns = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2346 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2347 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2348 except RuntimeError as error:
2349 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2350 raise self.LocalRuntimeError(self, error,
2351 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2354 if remote_trust_info:
2356 lsaString.string = remote_trust_info.domain_name.string
2357 remote_old_netbios = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2358 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2359 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2360 except RuntimeError as error:
2361 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2362 raise self.RemoteRuntimeError(self, error,
2363 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2367 lsaString.string = remote_trust_info.netbios_name.string
2368 remote_old_dns = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2369 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2370 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2371 except RuntimeError as error:
2372 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2373 raise self.RemoteRuntimeError(self, error,
2374 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2378 local_netlogon = self.new_local_netlogon_connection()
2379 except RuntimeError as error:
2380 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2383 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
2384 except RuntimeError as error:
2385 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
2387 if remote_trust_info:
2389 remote_netlogon = self.new_remote_netlogon_connection()
2390 except RuntimeError as error:
2391 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2394 remote_netlogon_info = self.get_netlogon_dc_info(remote_netlogon, remote_server)
2395 except RuntimeError as error:
2396 raise self.RemoteRuntimeError(self, error, "failed to get netlogon dc info")
2398 def arcfour_encrypt(key, data):
2399 from Crypto.Cipher import ARC4
2401 return c.encrypt(data)
2403 def generate_AuthInOutBlob(secret, update_time):
2405 blob = drsblobs.trustAuthInOutBlob()
2410 clear = drsblobs.AuthInfoClear()
2411 clear.size = len(secret)
2412 clear.password = secret
2414 info = drsblobs.AuthenticationInformation()
2415 info.LastUpdateTime = samba.unix2nttime(update_time)
2416 info.AuthType = lsa.TRUST_AUTH_TYPE_CLEAR
2417 info.AuthInfo = clear
2419 array = drsblobs.AuthenticationInformationArray()
2421 array.array = [info]
2423 blob = drsblobs.trustAuthInOutBlob()
2425 blob.current = array
2429 def generate_AuthInfoInternal(session_key, incoming=None, outgoing=None):
2430 confounder = [0] * 512
2431 for i in range(len(confounder)):
2432 confounder[i] = random.randint(0, 255)
2434 trustpass = drsblobs.trustDomainPasswords()
2436 trustpass.confounder = confounder
2437 trustpass.outgoing = outgoing
2438 trustpass.incoming = incoming
2440 trustpass_blob = ndr_pack(trustpass)
2442 encrypted_trustpass = arcfour_encrypt(session_key, trustpass_blob)
2444 auth_blob = lsa.DATA_BUF2()
2445 auth_blob.size = len(encrypted_trustpass)
2446 auth_blob.data = string_to_array(encrypted_trustpass)
2448 auth_info = lsa.TrustDomainInfoAuthInfoInternal()
2449 auth_info.auth_blob = auth_blob
2453 update_time = samba.current_unix_time()
2454 incoming_blob = generate_AuthInOutBlob(incoming_secret, update_time)
2455 outgoing_blob = generate_AuthInOutBlob(outgoing_secret, update_time)
2457 local_tdo_handle = None
2458 remote_tdo_handle = None
2460 local_auth_info = generate_AuthInfoInternal(local_lsa.session_key,
2461 incoming=incoming_blob,
2462 outgoing=outgoing_blob)
2463 if remote_trust_info:
2464 remote_auth_info = generate_AuthInfoInternal(remote_lsa.session_key,
2465 incoming=outgoing_blob,
2466 outgoing=incoming_blob)
2469 if remote_trust_info:
2470 self.outf.write("Creating remote TDO.\n")
2471 current_request = { "location": "remote", "name": "CreateTrustedDomainEx2"}
2472 remote_tdo_handle = remote_lsa.CreateTrustedDomainEx2(remote_policy,
2475 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2476 self.outf.write("Remote TDO created.\n")
2478 self.outf.write("Setting supported encryption types on remote TDO.\n")
2479 current_request = { "location": "remote", "name": "SetInformationTrustedDomain"}
2480 remote_lsa.SetInformationTrustedDomain(remote_tdo_handle,
2481 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2484 self.outf.write("Creating local TDO.\n")
2485 current_request = { "location": "local", "name": "CreateTrustedDomainEx2"}
2486 local_tdo_handle = local_lsa.CreateTrustedDomainEx2(local_policy,
2489 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2490 self.outf.write("Local TDO created\n")
2492 self.outf.write("Setting supported encryption types on local TDO.\n")
2493 current_request = { "location": "local", "name": "SetInformationTrustedDomain"}
2494 local_lsa.SetInformationTrustedDomain(local_tdo_handle,
2495 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2497 except RuntimeError as error:
2498 self.outf.write("Error: %s failed %sly - cleaning up\n" % (
2499 current_request['name'], current_request['location']))
2500 if remote_tdo_handle:
2501 self.outf.write("Deleting remote TDO.\n")
2502 remote_lsa.DeleteObject(remote_tdo_handle)
2503 remote_tdo_handle = None
2504 if local_tdo_handle:
2505 self.outf.write("Deleting local TDO.\n")
2506 local_lsa.DeleteObject(local_tdo_handle)
2507 local_tdo_handle = None
2508 if current_request['location'] is "remote":
2509 raise self.RemoteRuntimeError(self, error, "%s" % (
2510 current_request['name']))
2511 raise self.LocalRuntimeError(self, error, "%s" % (
2512 current_request['name']))
2515 if local_trust_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2516 self.outf.write("Setup local forest trust information...\n")
2518 # get all information about the remote trust
2519 # this triggers netr_GetForestTrustInformation to the remote domain
2520 # and lsaRSetForestTrustInformation() locally, but new top level
2521 # names are disabled by default.
2522 local_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
2523 remote_lsa_info.dns_domain.string,
2524 netlogon.DS_GFTI_UPDATE_TDO)
2525 except RuntimeError as error:
2526 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2529 # here we try to enable all top level names
2530 local_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
2531 remote_lsa_info.dns_domain,
2532 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2535 except RuntimeError as error:
2536 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2538 self.write_forest_trust_info(local_forest_info,
2539 tln=remote_lsa_info.dns_domain.string,
2540 collisions=local_forest_collision)
2542 if remote_trust_info:
2543 self.outf.write("Setup remote forest trust information...\n")
2545 # get all information about the local trust (from the perspective of the remote domain)
2546 # this triggers netr_GetForestTrustInformation to our domain.
2547 # and lsaRSetForestTrustInformation() remotely, but new top level
2548 # names are disabled by default.
2549 remote_forest_info = remote_netlogon.netr_DsRGetForestTrustInformation(remote_netlogon_info.dc_unc,
2550 local_lsa_info.dns_domain.string,
2551 netlogon.DS_GFTI_UPDATE_TDO)
2552 except RuntimeError as error:
2553 raise self.RemoteRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2556 # here we try to enable all top level names
2557 remote_forest_collision = remote_lsa.lsaRSetForestTrustInformation(remote_policy,
2558 local_lsa_info.dns_domain,
2559 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2562 except RuntimeError as error:
2563 raise self.RemoteRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2565 self.write_forest_trust_info(remote_forest_info,
2566 tln=local_lsa_info.dns_domain.string,
2567 collisions=remote_forest_collision)
2569 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2570 self.outf.write("Validating outgoing trust...\n")
2572 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_netlogon_info.dc_unc,
2573 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2575 remote_lsa_info.dns_domain.string)
2576 except RuntimeError as error:
2577 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2579 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2580 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2582 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2583 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2584 local_trust_verify.trusted_dc_name,
2585 local_trust_verify.tc_connection_status[1],
2586 local_trust_verify.pdc_connection_status[1])
2588 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2589 local_trust_verify.trusted_dc_name,
2590 local_trust_verify.tc_connection_status[1],
2591 local_trust_verify.pdc_connection_status[1])
2593 if local_trust_status != self.WERR_OK or local_conn_status != self.WERR_OK:
2594 raise CommandError(local_validation)
2596 self.outf.write("OK: %s\n" % local_validation)
2598 if remote_trust_info:
2599 if remote_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2600 self.outf.write("Validating incoming trust...\n")
2602 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_netlogon_info.dc_unc,
2603 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2605 local_lsa_info.dns_domain.string)
2606 except RuntimeError as error:
2607 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2609 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2610 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2612 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2613 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2614 remote_trust_verify.trusted_dc_name,
2615 remote_trust_verify.tc_connection_status[1],
2616 remote_trust_verify.pdc_connection_status[1])
2618 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2619 remote_trust_verify.trusted_dc_name,
2620 remote_trust_verify.tc_connection_status[1],
2621 remote_trust_verify.pdc_connection_status[1])
2623 if remote_trust_status != self.WERR_OK or remote_conn_status != self.WERR_OK:
2624 raise CommandError(remote_validation)
2626 self.outf.write("OK: %s\n" % remote_validation)
2628 if remote_tdo_handle is not None:
2630 remote_lsa.Close(remote_tdo_handle)
2631 except RuntimeError as error:
2633 remote_tdo_handle = None
2634 if local_tdo_handle is not None:
2636 local_lsa.Close(local_tdo_handle)
2637 except RuntimeError as error:
2639 local_tdo_handle = None
2641 self.outf.write("Success.\n")
2644 class cmd_domain_trust_delete(DomainTrustCommand):
2645 """Delete a domain trust."""
2647 synopsis = "%prog DOMAIN [options]"
2649 takes_optiongroups = {
2650 "sambaopts": options.SambaOptions,
2651 "versionopts": options.VersionOptions,
2652 "credopts": options.CredentialsOptions,
2653 "localdcopts": LocalDCCredentialsOptions,
2657 Option("--delete-location", type="choice", metavar="LOCATION",
2658 choices=["local", "both"],
2659 help="Where to delete the trusted domain object: 'local' or 'both'.",
2660 dest='delete_location',
2664 takes_args = ["domain"]
2666 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2667 delete_location=None):
2669 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2670 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2671 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2673 if delete_location == "local":
2674 remote_policy_access = None
2676 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2677 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2678 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2680 local_server = self.setup_local_server(sambaopts, localdcopts)
2682 local_lsa = self.new_local_lsa_connection()
2683 except RuntimeError as error:
2684 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2687 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2688 except RuntimeError as error:
2689 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2691 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2692 local_lsa_info.name.string,
2693 local_lsa_info.dns_domain.string,
2694 local_lsa_info.sid))
2696 local_tdo_info = None
2697 local_tdo_handle = None
2698 remote_tdo_info = None
2699 remote_tdo_handle = None
2701 lsaString = lsa.String()
2703 lsaString.string = domain
2704 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2705 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2706 except RuntimeError as error:
2707 if self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2708 raise CommandError("Failed to find trust for domain '%s'" % domain)
2709 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2712 if remote_policy_access is not None:
2714 remote_server = self.setup_remote_server(credopts, domain)
2715 except RuntimeError as error:
2716 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2719 remote_lsa = self.new_remote_lsa_connection()
2720 except RuntimeError as error:
2721 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2724 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2725 except RuntimeError as error:
2726 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2728 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2729 remote_lsa_info.name.string,
2730 remote_lsa_info.dns_domain.string,
2731 remote_lsa_info.sid))
2733 if remote_lsa_info.sid != local_tdo_info.sid or \
2734 remote_lsa_info.name.string != local_tdo_info.netbios_name.string or \
2735 remote_lsa_info.dns_domain.string != local_tdo_info.domain_name.string:
2736 raise CommandError("LocalTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2737 local_tdo_info.netbios_name.string,
2738 local_tdo_info.domain_name.string,
2739 local_tdo_info.sid))
2742 lsaString.string = local_lsa_info.dns_domain.string
2743 remote_tdo_info = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2744 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2745 except RuntimeError as error:
2746 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2747 raise self.RemoteRuntimeError(self, error, "QueryTrustedDomainInfoByName(%s)" % (
2751 if remote_tdo_info is not None:
2752 if local_lsa_info.sid != remote_tdo_info.sid or \
2753 local_lsa_info.name.string != remote_tdo_info.netbios_name.string or \
2754 local_lsa_info.dns_domain.string != remote_tdo_info.domain_name.string:
2755 raise CommandError("RemoteTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2756 remote_tdo_info.netbios_name.string,
2757 remote_tdo_info.domain_name.string,
2758 remote_tdo_info.sid))
2760 if local_tdo_info is not None:
2762 lsaString.string = local_tdo_info.domain_name.string
2763 local_tdo_handle = local_lsa.OpenTrustedDomainByName(local_policy,
2765 security.SEC_STD_DELETE)
2766 except RuntimeError as error:
2767 raise self.LocalRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2770 local_lsa.DeleteObject(local_tdo_handle)
2771 local_tdo_handle = None
2773 if remote_tdo_info is not None:
2775 lsaString.string = remote_tdo_info.domain_name.string
2776 remote_tdo_handle = remote_lsa.OpenTrustedDomainByName(remote_policy,
2778 security.SEC_STD_DELETE)
2779 except RuntimeError as error:
2780 raise self.RemoteRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2783 if remote_tdo_handle is not None:
2785 remote_lsa.DeleteObject(remote_tdo_handle)
2786 remote_tdo_handle = None
2787 self.outf.write("RemoteTDO deleted.\n")
2788 except RuntimeError as error:
2789 self.outf.write("%s\n" % self.RemoteRuntimeError(self, error, "DeleteObject() failed"))
2791 if local_tdo_handle is not None:
2793 local_lsa.DeleteObject(local_tdo_handle)
2794 local_tdo_handle = None
2795 self.outf.write("LocalTDO deleted.\n")
2796 except RuntimeError as error:
2797 self.outf.write("%s\n" % self.LocalRuntimeError(self, error, "DeleteObject() failed"))
2801 class cmd_domain_trust_validate(DomainTrustCommand):
2802 """Validate a domain trust."""
2804 synopsis = "%prog DOMAIN [options]"
2806 takes_optiongroups = {
2807 "sambaopts": options.SambaOptions,
2808 "versionopts": options.VersionOptions,
2809 "credopts": options.CredentialsOptions,
2810 "localdcopts": LocalDCCredentialsOptions,
2814 Option("--validate-location", type="choice", metavar="LOCATION",
2815 choices=["local", "both"],
2816 help="Where to validate the trusted domain object: 'local' or 'both'.",
2817 dest='validate_location',
2821 takes_args = ["domain"]
2823 def run(self, domain, sambaopts=None, versionopts=None, credopts=None, localdcopts=None,
2824 validate_location=None):
2826 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2828 local_server = self.setup_local_server(sambaopts, localdcopts)
2830 local_lsa = self.new_local_lsa_connection()
2831 except RuntimeError as error:
2832 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2835 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2836 except RuntimeError as error:
2837 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2839 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2840 local_lsa_info.name.string,
2841 local_lsa_info.dns_domain.string,
2842 local_lsa_info.sid))
2845 lsaString = lsa.String()
2846 lsaString.string = domain
2847 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2848 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2849 except RuntimeError as error:
2850 if self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2851 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2853 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
2855 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
2856 local_tdo_info.netbios_name.string,
2857 local_tdo_info.domain_name.string,
2858 local_tdo_info.sid))
2861 local_netlogon = self.new_local_netlogon_connection()
2862 except RuntimeError as error:
2863 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2866 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_server,
2867 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2869 local_tdo_info.domain_name.string)
2870 except RuntimeError as error:
2871 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2873 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2874 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2876 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2877 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2878 local_trust_verify.trusted_dc_name,
2879 local_trust_verify.tc_connection_status[1],
2880 local_trust_verify.pdc_connection_status[1])
2882 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2883 local_trust_verify.trusted_dc_name,
2884 local_trust_verify.tc_connection_status[1],
2885 local_trust_verify.pdc_connection_status[1])
2887 if local_trust_status != self.WERR_OK or local_conn_status != self.WERR_OK:
2888 raise CommandError(local_validation)
2890 self.outf.write("OK: %s\n" % local_validation)
2893 server = local_trust_verify.trusted_dc_name.replace('\\', '')
2894 domain_and_server = "%s\\%s" % (local_tdo_info.domain_name.string, server)
2895 local_trust_rediscover = local_netlogon.netr_LogonControl2Ex(local_server,
2896 netlogon.NETLOGON_CONTROL_REDISCOVER,
2899 except RuntimeError as error:
2900 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
2902 local_conn_status = self._uint32(local_trust_rediscover.tc_connection_status[0])
2903 local_rediscover = "LocalRediscover: DC[%s] CONNECTION[%s]" % (
2904 local_trust_rediscover.trusted_dc_name,
2905 local_trust_rediscover.tc_connection_status[1])
2907 if local_conn_status != self.WERR_OK:
2908 raise CommandError(local_rediscover)
2910 self.outf.write("OK: %s\n" % local_rediscover)
2912 if validate_location != "local":
2914 remote_server = self.setup_remote_server(credopts, domain, require_pdc=False)
2915 except RuntimeError as error:
2916 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2919 remote_netlogon = self.new_remote_netlogon_connection()
2920 except RuntimeError as error:
2921 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2924 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_server,
2925 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2927 local_lsa_info.dns_domain.string)
2928 except RuntimeError as error:
2929 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2931 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2932 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2934 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2935 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2936 remote_trust_verify.trusted_dc_name,
2937 remote_trust_verify.tc_connection_status[1],
2938 remote_trust_verify.pdc_connection_status[1])
2940 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2941 remote_trust_verify.trusted_dc_name,
2942 remote_trust_verify.tc_connection_status[1],
2943 remote_trust_verify.pdc_connection_status[1])
2945 if remote_trust_status != self.WERR_OK or remote_conn_status != self.WERR_OK:
2946 raise CommandError(remote_validation)
2948 self.outf.write("OK: %s\n" % remote_validation)
2951 server = remote_trust_verify.trusted_dc_name.replace('\\', '')
2952 domain_and_server = "%s\\%s" % (local_lsa_info.dns_domain.string, server)
2953 remote_trust_rediscover = remote_netlogon.netr_LogonControl2Ex(remote_server,
2954 netlogon.NETLOGON_CONTROL_REDISCOVER,
2957 except RuntimeError as error:
2958 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
2960 remote_conn_status = self._uint32(remote_trust_rediscover.tc_connection_status[0])
2962 remote_rediscover = "RemoteRediscover: DC[%s] CONNECTION[%s]" % (
2963 remote_trust_rediscover.trusted_dc_name,
2964 remote_trust_rediscover.tc_connection_status[1])
2966 if remote_conn_status != self.WERR_OK:
2967 raise CommandError(remote_rediscover)
2969 self.outf.write("OK: %s\n" % remote_rediscover)
2973 class cmd_domain_trust_namespaces(DomainTrustCommand):
2974 """Manage forest trust namespaces."""
2976 synopsis = "%prog [DOMAIN] [options]"
2978 takes_optiongroups = {
2979 "sambaopts": options.SambaOptions,
2980 "versionopts": options.VersionOptions,
2981 "localdcopts": LocalDCCredentialsOptions,
2985 Option("--refresh", type="choice", metavar="check|store",
2986 choices=["check", "store", None],
2987 help="List and maybe store refreshed forest trust information: 'check' or 'store'.",
2990 Option("--enable-all", action="store_true",
2991 help="Try to update disabled entries, not allowed with --refresh=check.",
2994 Option("--enable-tln", action="append", metavar='DNSDOMAIN',
2995 help="Enable a top level name entry. Can be specified multiple times.",
2998 Option("--disable-tln", action="append", metavar='DNSDOMAIN',
2999 help="Disable a top level name entry. Can be specified multiple times.",
3002 Option("--add-tln-ex", action="append", metavar='DNSDOMAIN',
3003 help="Add a top level exclusion entry. Can be specified multiple times.",
3006 Option("--delete-tln-ex", action="append", metavar='DNSDOMAIN',
3007 help="Delete a top level exclusion entry. Can be specified multiple times.",
3008 dest='delete_tln_ex',
3010 Option("--enable-nb", action="append", metavar='NETBIOSDOMAIN',
3011 help="Enable a netbios name in a domain entry. Can be specified multiple times.",
3014 Option("--disable-nb", action="append", metavar='NETBIOSDOMAIN',
3015 help="Disable a netbios name in a domain entry. Can be specified multiple times.",
3018 Option("--enable-sid", action="append", metavar='DOMAINSID',
3019 help="Enable a SID in a domain entry. Can be specified multiple times.",
3020 dest='enable_sid_str',
3022 Option("--disable-sid", action="append", metavar='DOMAINSID',
3023 help="Disable a SID in a domain entry. Can be specified multiple times.",
3024 dest='disable_sid_str',
3026 Option("--add-upn-suffix", action="append", metavar='DNSDOMAIN',
3027 help="Add a new uPNSuffixes attribute for the local forest. Can be specified multiple times.",
3030 Option("--delete-upn-suffix", action="append", metavar='DNSDOMAIN',
3031 help="Delete an existing uPNSuffixes attribute of the local forest. Can be specified multiple times.",
3034 Option("--add-spn-suffix", action="append", metavar='DNSDOMAIN',
3035 help="Add a new msDS-SPNSuffixes attribute for the local forest. Can be specified multiple times.",
3038 Option("--delete-spn-suffix", action="append", metavar='DNSDOMAIN',
3039 help="Delete an existing msDS-SPNSuffixes attribute of the local forest. Can be specified multiple times.",
3044 takes_args = ["domain?"]
3046 def run(self, domain=None, sambaopts=None, localdcopts=None, versionopts=None,
3047 refresh=None, enable_all=False,
3048 enable_tln=[], disable_tln=[], add_tln_ex=[], delete_tln_ex=[],
3049 enable_sid_str=[], disable_sid_str=[], enable_nb=[], disable_nb=[],
3050 add_upn=[], delete_upn=[], add_spn=[], delete_spn=[]):
3052 require_update = False
3055 if refresh == "store":
3056 raise CommandError("--refresh=%s not allowed without DOMAIN" % refresh)
3059 raise CommandError("--enable-all not allowed without DOMAIN")
3061 if len(enable_tln) > 0:
3062 raise CommandError("--enable-tln not allowed without DOMAIN")
3063 if len(disable_tln) > 0:
3064 raise CommandError("--disable-tln not allowed without DOMAIN")
3066 if len(add_tln_ex) > 0:
3067 raise CommandError("--add-tln-ex not allowed without DOMAIN")
3068 if len(delete_tln_ex) > 0:
3069 raise CommandError("--delete-tln-ex not allowed without DOMAIN")
3071 if len(enable_nb) > 0:
3072 raise CommandError("--enable-nb not allowed without DOMAIN")
3073 if len(disable_nb) > 0:
3074 raise CommandError("--disable-nb not allowed without DOMAIN")
3076 if len(enable_sid_str) > 0:
3077 raise CommandError("--enable-sid not allowed without DOMAIN")
3078 if len(disable_sid_str) > 0:
3079 raise CommandError("--disable-sid not allowed without DOMAIN")
3081 if len(add_upn) > 0:
3083 if not n.startswith("*."):
3085 raise CommandError("value[%s] specified for --add-upn-suffix should not include with '*.'" % n)
3086 require_update = True
3087 if len(delete_upn) > 0:
3088 for n in delete_upn:
3089 if not n.startswith("*."):
3091 raise CommandError("value[%s] specified for --delete-upn-suffix should not include with '*.'" % n)
3092 require_update = True
3094 for d in delete_upn:
3095 if a.lower() != d.lower():
3097 raise CommandError("value[%s] specified for --add-upn-suffix and --delete-upn-suffix" % a)
3099 if len(add_spn) > 0:
3101 if not n.startswith("*."):
3103 raise CommandError("value[%s] specified for --add-spn-suffix should not include with '*.'" % n)
3104 require_update = True
3105 if len(delete_spn) > 0:
3106 for n in delete_spn:
3107 if not n.startswith("*."):
3109 raise CommandError("value[%s] specified for --delete-spn-suffix should not include with '*.'" % n)
3110 require_update = True
3112 for d in delete_spn:
3113 if a.lower() != d.lower():
3115 raise CommandError("value[%s] specified for --add-spn-suffix and --delete-spn-suffix" % a)
3117 if len(add_upn) > 0:
3118 raise CommandError("--add-upn-suffix not allowed together with DOMAIN")
3119 if len(delete_upn) > 0:
3120 raise CommandError("--delete-upn-suffix not allowed together with DOMAIN")
3121 if len(add_spn) > 0:
3122 raise CommandError("--add-spn-suffix not allowed together with DOMAIN")
3123 if len(delete_spn) > 0:
3124 raise CommandError("--delete-spn-suffix not allowed together with DOMAIN")
3126 if refresh is not None:
3127 if refresh == "store":
3128 require_update = True
3130 if enable_all and refresh != "store":
3131 raise CommandError("--enable-all not allowed together with --refresh=%s" % refresh)
3133 if len(enable_tln) > 0:
3134 raise CommandError("--enable-tln not allowed together with --refresh")
3135 if len(disable_tln) > 0:
3136 raise CommandError("--disable-tln not allowed together with --refresh")
3138 if len(add_tln_ex) > 0:
3139 raise CommandError("--add-tln-ex not allowed together with --refresh")
3140 if len(delete_tln_ex) > 0:
3141 raise CommandError("--delete-tln-ex not allowed together with --refresh")
3143 if len(enable_nb) > 0:
3144 raise CommandError("--enable-nb not allowed together with --refresh")
3145 if len(disable_nb) > 0:
3146 raise CommandError("--disable-nb not allowed together with --refresh")
3148 if len(enable_sid_str) > 0:
3149 raise CommandError("--enable-sid not allowed together with --refresh")
3150 if len(disable_sid_str) > 0:
3151 raise CommandError("--disable-sid not allowed together with --refresh")
3154 require_update = True
3156 if len(enable_tln) > 0:
3157 raise CommandError("--enable-tln not allowed together with --enable-all")
3159 if len(enable_nb) > 0:
3160 raise CommandError("--enable-nb not allowed together with --enable-all")
3162 if len(enable_sid) > 0:
3163 raise CommandError("--enable-sid not allowed together with --enable-all")
3165 if len(enable_tln) > 0:
3166 require_update = True
3167 if len(disable_tln) > 0:
3168 require_update = True
3169 for e in enable_tln:
3170 for d in disable_tln:
3171 if e.lower() != d.lower():
3173 raise CommandError("value[%s] specified for --enable-tln and --disable-tln" % e)
3175 if len(add_tln_ex) > 0:
3176 for n in add_tln_ex:
3177 if not n.startswith("*."):
3179 raise CommandError("value[%s] specified for --add-tln-ex should not include with '*.'" % n)
3180 require_update = True
3181 if len(delete_tln_ex) > 0:
3182 for n in delete_tln_ex:
3183 if not n.startswith("*."):
3185 raise CommandError("value[%s] specified for --delete-tln-ex should not include with '*.'" % n)
3186 require_update = True
3187 for a in add_tln_ex:
3188 for d in delete_tln_ex:
3189 if a.lower() != d.lower():
3191 raise CommandError("value[%s] specified for --add-tln-ex and --delete-tln-ex" % a)
3193 if len(enable_nb) > 0:
3194 require_update = True
3195 if len(disable_nb) > 0:
3196 require_update = True
3198 for d in disable_nb:
3199 if e.upper() != d.upper():
3201 raise CommandError("value[%s] specified for --enable-nb and --disable-nb" % e)
3204 for s in enable_sid_str:
3206 sid = security.dom_sid(s)
3207 except TypeError as error:
3208 raise CommandError("value[%s] specified for --enable-sid is not a valid SID" % s)
3209 enable_sid.append(sid)
3211 for s in disable_sid_str:
3213 sid = security.dom_sid(s)
3214 except TypeError as error:
3215 raise CommandError("value[%s] specified for --disable-sid is not a valid SID" % s)
3216 disable_sid.append(sid)
3217 if len(enable_sid) > 0:
3218 require_update = True
3219 if len(disable_sid) > 0:
3220 require_update = True
3221 for e in enable_sid:
3222 for d in disable_sid:
3225 raise CommandError("value[%s] specified for --enable-sid and --disable-sid" % e)
3227 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
3229 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
3231 local_server = self.setup_local_server(sambaopts, localdcopts)
3233 local_lsa = self.new_local_lsa_connection()
3234 except RuntimeError as error:
3235 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
3238 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
3239 except RuntimeError as error:
3240 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
3242 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
3243 local_lsa_info.name.string,
3244 local_lsa_info.dns_domain.string,
3245 local_lsa_info.sid))
3249 local_netlogon = self.new_local_netlogon_connection()
3250 except RuntimeError as error:
3251 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3254 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3255 except RuntimeError as error:
3256 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3258 if local_netlogon_info.domain_name != local_netlogon_info.forest_name:
3259 raise CommandError("The local domain [%s] is not the forest root [%s]" % (
3260 local_netlogon_info.domain_name,
3261 local_netlogon_info.forest_name))
3264 # get all information about our own forest
3265 own_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3267 except RuntimeError as error:
3268 if self.check_runtime_error(error, self.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
3269 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3272 if self.check_runtime_error(error, self.WERR_INVALID_FUNCTION):
3273 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3276 if self.check_runtime_error(error, self.WERR_NERR_ACFNOTLOADED):
3277 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3280 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3282 self.outf.write("Own forest trust information...\n")
3283 self.write_forest_trust_info(own_forest_info,
3284 tln=local_lsa_info.dns_domain.string)
3287 local_samdb = self.new_local_ldap_connection()
3288 except RuntimeError as error:
3289 raise self.LocalRuntimeError(self, error, "failed to connect to SamDB")
3291 local_partitions_dn = "CN=Partitions,%s" % str(local_samdb.get_config_basedn())
3292 attrs = ['uPNSuffixes', 'msDS-SPNSuffixes']
3294 msgs = local_samdb.search(base=local_partitions_dn,
3295 scope=ldb.SCOPE_BASE,
3296 expression="(objectClass=crossRefContainer)",
3298 stored_msg = msgs[0]
3299 except ldb.LdbError as error:
3300 raise self.LocalLdbError(self, error, "failed to search partition dn")
3302 stored_upn_vals = []
3303 if 'uPNSuffixes' in stored_msg:
3304 stored_upn_vals.extend(stored_msg['uPNSuffixes'])
3306 stored_spn_vals = []
3307 if 'msDS-SPNSuffixes' in stored_msg:
3308 stored_spn_vals.extend(stored_msg['msDS-SPNSuffixes'])
3310 self.outf.write("Stored uPNSuffixes attributes[%d]:\n" % len(stored_upn_vals))
3311 for v in stored_upn_vals:
3312 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3313 self.outf.write("Stored msDS-SPNSuffixes attributes[%d]:\n" % len(stored_spn_vals))
3314 for v in stored_spn_vals:
3315 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3317 if not require_update:
3321 update_upn_vals = []
3322 update_upn_vals.extend(stored_upn_vals)
3325 update_spn_vals = []
3326 update_spn_vals.extend(stored_spn_vals)
3330 for i in xrange(0, len(update_upn_vals)):
3331 v = update_upn_vals[i]
3332 if v.lower() != upn.lower():
3337 raise CommandError("Entry already present for value[%s] specified for --add-upn-suffix" % upn)
3338 update_upn_vals.append(upn)
3341 for upn in delete_upn:
3343 for i in xrange(0, len(update_upn_vals)):
3344 v = update_upn_vals[i]
3345 if v.lower() != upn.lower():
3350 raise CommandError("Entry not found for value[%s] specified for --delete-upn-suffix" % upn)
3352 update_upn_vals.pop(idx)
3357 for i in xrange(0, len(update_spn_vals)):
3358 v = update_spn_vals[i]
3359 if v.lower() != spn.lower():
3364 raise CommandError("Entry already present for value[%s] specified for --add-spn-suffix" % spn)
3365 update_spn_vals.append(spn)
3368 for spn in delete_spn:
3370 for i in xrange(0, len(update_spn_vals)):
3371 v = update_spn_vals[i]
3372 if v.lower() != spn.lower():
3377 raise CommandError("Entry not found for value[%s] specified for --delete-spn-suffix" % spn)
3379 update_spn_vals.pop(idx)
3382 self.outf.write("Update uPNSuffixes attributes[%d]:\n" % len(update_upn_vals))
3383 for v in update_upn_vals:
3384 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3385 self.outf.write("Update msDS-SPNSuffixes attributes[%d]:\n" % len(update_spn_vals))
3386 for v in update_spn_vals:
3387 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3389 update_msg = ldb.Message()
3390 update_msg.dn = stored_msg.dn
3393 update_msg['uPNSuffixes'] = ldb.MessageElement(update_upn_vals,
3394 ldb.FLAG_MOD_REPLACE,
3397 update_msg['msDS-SPNSuffixes'] = ldb.MessageElement(update_spn_vals,
3398 ldb.FLAG_MOD_REPLACE,
3401 local_samdb.modify(update_msg)
3402 except ldb.LdbError as error:
3403 raise self.LocalLdbError(self, error, "failed to update partition dn")
3406 stored_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3408 except RuntimeError as error:
3409 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3411 self.outf.write("Stored forest trust information...\n")
3412 self.write_forest_trust_info(stored_forest_info,
3413 tln=local_lsa_info.dns_domain.string)
3417 lsaString = lsa.String()
3418 lsaString.string = domain
3419 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
3420 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
3421 except RuntimeError as error:
3422 if self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
3423 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
3425 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
3427 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
3428 local_tdo_info.netbios_name.string,
3429 local_tdo_info.domain_name.string,
3430 local_tdo_info.sid))
3432 if not local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
3433 raise CommandError("trusted domain object for domain [%s] is not marked as FOREST_TRANSITIVE." % domain)
3435 if refresh is not None:
3437 local_netlogon = self.new_local_netlogon_connection()
3438 except RuntimeError as error:
3439 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3442 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3443 except RuntimeError as error:
3444 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3446 lsa_update_check = 1
3447 if refresh == "store":
3448 netlogon_update_tdo = netlogon.DS_GFTI_UPDATE_TDO
3450 lsa_update_check = 0
3452 netlogon_update_tdo = 0
3455 # get all information about the remote trust
3456 # this triggers netr_GetForestTrustInformation to the remote domain
3457 # and lsaRSetForestTrustInformation() locally, but new top level
3458 # names are disabled by default.
3459 fresh_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3460 local_tdo_info.domain_name.string,
3461 netlogon_update_tdo)
3462 except RuntimeError as error:
3463 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3466 fresh_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3467 local_tdo_info.domain_name,
3468 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3471 except RuntimeError as error:
3472 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3474 self.outf.write("Fresh forest trust information...\n")
3475 self.write_forest_trust_info(fresh_forest_info,
3476 tln=local_tdo_info.domain_name.string,
3477 collisions=fresh_forest_collision)
3479 if refresh == "store":
3481 lsaString = lsa.String()
3482 lsaString.string = local_tdo_info.domain_name.string
3483 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3485 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3486 except RuntimeError as error:
3487 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3489 self.outf.write("Stored forest trust information...\n")
3490 self.write_forest_trust_info(stored_forest_info,
3491 tln=local_tdo_info.domain_name.string)
3496 # The none --refresh path
3500 lsaString = lsa.String()
3501 lsaString.string = local_tdo_info.domain_name.string
3502 local_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3504 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3505 except RuntimeError as error:
3506 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3508 self.outf.write("Local forest trust information...\n")
3509 self.write_forest_trust_info(local_forest_info,
3510 tln=local_tdo_info.domain_name.string)
3512 if not require_update:
3516 entries.extend(local_forest_info.entries)
3517 update_forest_info = lsa.ForestTrustInformation()
3518 update_forest_info.count = len(entries)
3519 update_forest_info.entries = entries
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_TOP_LEVEL_NAME:
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_TLN_DISABLED_MASK
3530 for i in xrange(0, len(update_forest_info.entries)):
3531 r = update_forest_info.entries[i]
3532 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3534 if update_forest_info.entries[i].flags == 0:
3536 update_forest_info.entries[i].time = 0
3537 update_forest_info.entries[i].flags &= ~lsa.LSA_NB_DISABLED_MASK
3538 update_forest_info.entries[i].flags &= ~lsa.LSA_SID_DISABLED_MASK
3540 for tln in enable_tln:
3542 for i in xrange(0, len(update_forest_info.entries)):
3543 r = update_forest_info.entries[i]
3544 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3546 if r.forest_trust_data.string.lower() != tln.lower():
3551 raise CommandError("Entry not found for value[%s] specified for --enable-tln" % tln)
3552 if not update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_MASK:
3553 raise CommandError("Entry found for value[%s] specified for --enable-tln is already enabled" % tln)
3554 update_forest_info.entries[idx].time = 0
3555 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3557 for tln in disable_tln:
3559 for i in xrange(0, len(update_forest_info.entries)):
3560 r = update_forest_info.entries[i]
3561 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3563 if r.forest_trust_data.string.lower() != tln.lower():
3568 raise CommandError("Entry not found for value[%s] specified for --disable-tln" % tln)
3569 if update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_ADMIN:
3570 raise CommandError("Entry found for value[%s] specified for --disable-tln is already disabled" % tln)
3571 update_forest_info.entries[idx].time = 0
3572 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3573 update_forest_info.entries[idx].flags |= lsa.LSA_TLN_DISABLED_ADMIN
3575 for tln_ex in add_tln_ex:
3577 for i in xrange(0, len(update_forest_info.entries)):
3578 r = update_forest_info.entries[i]
3579 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3581 if r.forest_trust_data.string.lower() != tln_ex.lower():
3586 raise CommandError("Entry already present for value[%s] specified for --add-tln-ex" % tln_ex)
3588 tln_dot = ".%s" % tln_ex.lower()
3590 for i in xrange(0, len(update_forest_info.entries)):
3591 r = update_forest_info.entries[i]
3592 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3594 r_dot = ".%s" % r.forest_trust_data.string.lower()
3595 if tln_dot == r_dot:
3596 raise CommandError("TLN entry present for value[%s] specified for --add-tln-ex" % tln_ex)
3597 if not tln_dot.endswith(r_dot):
3603 raise CommandError("No TLN parent present for value[%s] specified for --add-tln-ex" % tln_ex)
3605 r = lsa.ForestTrustRecord()
3606 r.type = lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX
3609 r.forest_trust_data.string = tln_ex
3612 entries.extend(update_forest_info.entries)
3613 entries.insert(idx + 1, r)
3614 update_forest_info.count = len(entries)
3615 update_forest_info.entries = entries
3617 for tln_ex in delete_tln_ex:
3619 for i in xrange(0, len(update_forest_info.entries)):
3620 r = update_forest_info.entries[i]
3621 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3623 if r.forest_trust_data.string.lower() != tln_ex.lower():
3628 raise CommandError("Entry not found for value[%s] specified for --delete-tln-ex" % tln_ex)
3631 entries.extend(update_forest_info.entries)
3633 update_forest_info.count = len(entries)
3634 update_forest_info.entries = entries
3636 for nb in enable_nb:
3638 for i in xrange(0, len(update_forest_info.entries)):
3639 r = update_forest_info.entries[i]
3640 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3642 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3647 raise CommandError("Entry not found for value[%s] specified for --enable-nb" % nb)
3648 if not update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_MASK:
3649 raise CommandError("Entry found for value[%s] specified for --enable-nb is already enabled" % nb)
3650 update_forest_info.entries[idx].time = 0
3651 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3653 for nb in disable_nb:
3655 for i in xrange(0, len(update_forest_info.entries)):
3656 r = update_forest_info.entries[i]
3657 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3659 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3664 raise CommandError("Entry not found for value[%s] specified for --delete-nb" % nb)
3665 if update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_ADMIN:
3666 raise CommandError("Entry found for value[%s] specified for --disable-nb is already disabled" % nb)
3667 update_forest_info.entries[idx].time = 0
3668 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3669 update_forest_info.entries[idx].flags |= lsa.LSA_NB_DISABLED_ADMIN
3671 for sid in enable_sid:
3673 for i in xrange(0, len(update_forest_info.entries)):
3674 r = update_forest_info.entries[i]
3675 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3677 if r.forest_trust_data.domain_sid != sid:
3682 raise CommandError("Entry not found for value[%s] specified for --enable-sid" % sid)
3683 if not update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_MASK:
3684 raise CommandError("Entry found for value[%s] specified for --enable-sid is already enabled" % nb)
3685 update_forest_info.entries[idx].time = 0
3686 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3688 for sid in disable_sid:
3690 for i in xrange(0, len(update_forest_info.entries)):
3691 r = update_forest_info.entries[i]
3692 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3694 if r.forest_trust_data.domain_sid != sid:
3699 raise CommandError("Entry not found for value[%s] specified for --delete-sid" % sid)
3700 if update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_ADMIN:
3701 raise CommandError("Entry found for value[%s] specified for --disable-sid is already disabled" % nb)
3702 update_forest_info.entries[idx].time = 0
3703 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3704 update_forest_info.entries[idx].flags |= lsa.LSA_SID_DISABLED_ADMIN
3707 update_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3708 local_tdo_info.domain_name,
3709 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3710 update_forest_info, 0)
3711 except RuntimeError as error:
3712 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3714 self.outf.write("Updated forest trust information...\n")
3715 self.write_forest_trust_info(update_forest_info,
3716 tln=local_tdo_info.domain_name.string,
3717 collisions=update_forest_collision)
3720 lsaString = lsa.String()
3721 lsaString.string = local_tdo_info.domain_name.string
3722 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3724 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3725 except RuntimeError as error:
3726 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3728 self.outf.write("Stored forest trust information...\n")
3729 self.write_forest_trust_info(stored_forest_info,
3730 tln=local_tdo_info.domain_name.string)
3733 class cmd_domain_trust(SuperCommand):
3734 """Domain and forest trust management."""
3737 subcommands["list"] = cmd_domain_trust_list()
3738 subcommands["show"] = cmd_domain_trust_show()
3739 subcommands["create"] = cmd_domain_trust_create()
3740 subcommands["delete"] = cmd_domain_trust_delete()
3741 subcommands["validate"] = cmd_domain_trust_validate()
3742 subcommands["namespaces"] = cmd_domain_trust_namespaces()
3744 class cmd_domain(SuperCommand):
3745 """Domain management."""
3748 subcommands["demote"] = cmd_domain_demote()
3749 if cmd_domain_export_keytab is not None:
3750 subcommands["exportkeytab"] = cmd_domain_export_keytab()
3751 subcommands["info"] = cmd_domain_info()
3752 subcommands["provision"] = cmd_domain_provision()
3753 subcommands["join"] = cmd_domain_join()
3754 subcommands["dcpromo"] = cmd_domain_dcpromo()
3755 subcommands["level"] = cmd_domain_level()
3756 subcommands["passwordsettings"] = cmd_domain_passwordsettings()
3757 subcommands["classicupgrade"] = cmd_domain_classicupgrade()
3758 subcommands["samba3upgrade"] = cmd_domain_samba3upgrade()
3759 subcommands["trust"] = cmd_domain_trust()