3 # Copyright Matthias Dieter Wallnoefer 2009
4 # Copyright Andrew Kroeger 2009
5 # Copyright Jelmer Vernooij 2007-2012
6 # Copyright Giampaolo Lauria 2011
7 # Copyright Matthieu Patou <mat@matws.net> 2011
8 # Copyright Andrew Bartlett 2008-2015
9 # Copyright Stefan Metzmacher 2012
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 3 of the License, or
14 # (at your option) any later version.
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 import samba.getopt as options
34 from getpass import getpass
35 from samba.net import Net, LIBNET_JOIN_AUTOMATIC
37 from samba.join import join_RODC, join_DC, join_subdomain
38 from samba.auth import system_session
39 from samba.samdb import SamDB
40 from samba.ndr import ndr_unpack, ndr_pack, ndr_print
41 from samba.dcerpc import drsuapi
42 from samba.dcerpc import drsblobs
43 from samba.dcerpc import lsa
44 from samba.dcerpc import netlogon
45 from samba.dcerpc import security
46 from samba.dcerpc import nbt
47 from samba.dcerpc import misc
48 from samba.dcerpc.samr import DOMAIN_PASSWORD_COMPLEX, DOMAIN_PASSWORD_STORE_CLEARTEXT
49 from samba.netcmd import (
55 from samba.netcmd.common import netcmd_get_domain_infos_via_cldap
56 from samba.samba3 import Samba3
57 from samba.samba3 import param as s3param
58 from samba.upgrade import upgrade_from_samba3
59 from samba.drs_utils import (
60 sendDsReplicaSync, drsuapi_connect, drsException,
62 from samba import remove_dc
64 from samba.dsdb import (
65 DS_DOMAIN_FUNCTION_2000,
66 DS_DOMAIN_FUNCTION_2003,
67 DS_DOMAIN_FUNCTION_2003_MIXED,
68 DS_DOMAIN_FUNCTION_2008,
69 DS_DOMAIN_FUNCTION_2008_R2,
70 DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL,
71 DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL,
72 UF_WORKSTATION_TRUST_ACCOUNT,
73 UF_SERVER_TRUST_ACCOUNT,
74 UF_TRUSTED_FOR_DELEGATION,
75 UF_PARTIAL_SECRETS_ACCOUNT
78 from samba.provision import (
83 from samba.provision.common import (
89 def get_testparm_var(testparm, smbconf, varname):
90 cmd = "%s -s -l --parameter-name='%s' %s 2>/dev/null" % (testparm, varname, smbconf)
91 output = os.popen(cmd, 'r').readline()
97 cmd_domain_export_keytab = None
99 class cmd_domain_export_keytab(Command):
100 """Dump Kerberos keys of the domain into a keytab."""
102 synopsis = "%prog <keytab> [options]"
104 takes_optiongroups = {
105 "sambaopts": options.SambaOptions,
106 "credopts": options.CredentialsOptions,
107 "versionopts": options.VersionOptions,
111 Option("--principal", help="extract only this principal", type=str),
114 takes_args = ["keytab"]
116 def run(self, keytab, credopts=None, sambaopts=None, versionopts=None, principal=None):
117 lp = sambaopts.get_loadparm()
119 net.export_keytab(keytab=keytab, principal=principal)
122 class cmd_domain_info(Command):
123 """Print basic info about a domain and the DC passed as parameter."""
125 synopsis = "%prog <ip_address> [options]"
130 takes_optiongroups = {
131 "sambaopts": options.SambaOptions,
132 "credopts": options.CredentialsOptions,
133 "versionopts": options.VersionOptions,
136 takes_args = ["address"]
138 def run(self, address, credopts=None, sambaopts=None, versionopts=None):
139 lp = sambaopts.get_loadparm()
141 res = netcmd_get_domain_infos_via_cldap(lp, None, address)
143 raise CommandError("Invalid IP address '" + address + "'!")
144 self.outf.write("Forest : %s\n" % res.forest)
145 self.outf.write("Domain : %s\n" % res.dns_domain)
146 self.outf.write("Netbios domain : %s\n" % res.domain_name)
147 self.outf.write("DC name : %s\n" % res.pdc_dns_name)
148 self.outf.write("DC netbios name : %s\n" % res.pdc_name)
149 self.outf.write("Server site : %s\n" % res.server_site)
150 self.outf.write("Client site : %s\n" % res.client_site)
153 class cmd_domain_provision(Command):
154 """Provision a domain."""
156 synopsis = "%prog [options]"
158 takes_optiongroups = {
159 "sambaopts": options.SambaOptions,
160 "versionopts": options.VersionOptions,
164 Option("--interactive", help="Ask for names", action="store_true"),
165 Option("--domain", type="string", metavar="DOMAIN",
167 Option("--domain-guid", type="string", metavar="GUID",
168 help="set domainguid (otherwise random)"),
169 Option("--domain-sid", type="string", metavar="SID",
170 help="set domainsid (otherwise random)"),
171 Option("--ntds-guid", type="string", metavar="GUID",
172 help="set NTDS object GUID (otherwise random)"),
173 Option("--invocationid", type="string", metavar="GUID",
174 help="set invocationid (otherwise random)"),
175 Option("--host-name", type="string", metavar="HOSTNAME",
176 help="set hostname"),
177 Option("--host-ip", type="string", metavar="IPADDRESS",
178 help="set IPv4 ipaddress"),
179 Option("--host-ip6", type="string", metavar="IP6ADDRESS",
180 help="set IPv6 ipaddress"),
181 Option("--site", type="string", metavar="SITENAME",
182 help="set site name"),
183 Option("--adminpass", type="string", metavar="PASSWORD",
184 help="choose admin password (otherwise random)"),
185 Option("--krbtgtpass", type="string", metavar="PASSWORD",
186 help="choose krbtgt password (otherwise random)"),
187 Option("--machinepass", type="string", metavar="PASSWORD",
188 help="choose machine password (otherwise random)"),
189 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
190 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
191 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
192 "BIND9_FLATFILE uses bind9 text database to store zone information, "
193 "BIND9_DLZ uses samba4 AD to store zone information, "
194 "NONE skips the DNS setup entirely (not recommended)",
195 default="SAMBA_INTERNAL"),
196 Option("--dnspass", type="string", metavar="PASSWORD",
197 help="choose dns password (otherwise random)"),
198 Option("--ldapadminpass", type="string", metavar="PASSWORD",
199 help="choose password to set between Samba and its LDAP backend (otherwise random)"),
200 Option("--root", type="string", metavar="USERNAME",
201 help="choose 'root' unix username"),
202 Option("--nobody", type="string", metavar="USERNAME",
203 help="choose 'nobody' user"),
204 Option("--users", type="string", metavar="GROUPNAME",
205 help="choose 'users' group"),
206 Option("--quiet", help="Be quiet", action="store_true"),
207 Option("--blank", action="store_true",
208 help="do not add users or groups, just the structure"),
209 Option("--ldap-backend-type", type="choice", metavar="LDAP-BACKEND-TYPE",
210 help="Test initialisation support for unsupported LDAP backend type (fedora-ds or openldap) DO NOT USE",
211 choices=["fedora-ds", "openldap"]),
212 Option("--server-role", type="choice", metavar="ROLE",
213 choices=["domain controller", "dc", "member server", "member", "standalone"],
214 help="The server role (domain controller | dc | member server | member | standalone). Default is dc.",
215 default="domain controller"),
216 Option("--function-level", type="choice", metavar="FOR-FUN-LEVEL",
217 choices=["2000", "2003", "2008", "2008_R2"],
218 help="The domain and forest function level (2000 | 2003 | 2008 | 2008_R2 - always native). Default is (Windows) 2008_R2 Native.",
220 Option("--next-rid", type="int", metavar="NEXTRID", default=1000,
221 help="The initial nextRid value (only needed for upgrades). Default is 1000."),
222 Option("--partitions-only",
223 help="Configure Samba's partitions, but do not modify them (ie, join a BDC)", action="store_true"),
224 Option("--targetdir", type="string", metavar="DIR",
225 help="Set target directory"),
226 Option("--ol-mmr-urls", type="string", metavar="LDAPSERVER",
227 help="List of LDAP-URLS [ ldap://<FQHN>:<PORT>/ (where <PORT> has to be different than 389!) ] separated with comma (\",\") for use with OpenLDAP-MMR (Multi-Master-Replication), e.g.: \"ldap://s4dc1:9000,ldap://s4dc2:9000\""),
228 Option("--use-xattrs", type="choice", choices=["yes", "no", "auto"], help="Define if we should use the native fs capabilities or a tdb file for storing attributes likes ntacl, auto tries to make an inteligent guess based on the user rights and system capabilities", default="auto"),
230 Option("--use-rfc2307", action="store_true", help="Use AD to store posix attributes (default = no)"),
234 Option("--ldap-dryrun-mode", help="Configure LDAP backend, but do not run any binaries and exit early. Used only for the test environment. DO NOT USE",
235 action="store_true"),
236 Option("--slapd-path", type="string", metavar="SLAPD-PATH",
237 help="Path to slapd for LDAP backend [e.g.:'/usr/local/libexec/slapd']. Required for Setup with LDAP-Backend. OpenLDAP Version >= 2.4.17 should be used."),
238 Option("--ldap-backend-extra-port", type="int", metavar="LDAP-BACKEND-EXTRA-PORT", help="Additional TCP port for LDAP backend server (to use for replication)"),
239 Option("--ldap-backend-forced-uri", type="string", metavar="LDAP-BACKEND-FORCED-URI",
240 help="Force the LDAP backend connection to be to a particular URI. Use this ONLY for 'existing' backends, or when debugging the interaction with the LDAP backend and you need to intercept the LDA"),
241 Option("--ldap-backend-nosync", help="Configure LDAP backend not to call fsync() (for performance in test environments)", action="store_true"),
245 Option("--use-ntvfs", action="store_true", help="Use NTVFS for the fileserver (default = no)"),
248 if os.getenv('TEST_LDAP', "no") == "yes":
249 takes_options.extend(openldap_options)
251 if samba.is_ntvfs_fileserver_built():
252 takes_options.extend(ntvfs_options)
256 def run(self, sambaopts=None, versionopts=None,
279 ldap_backend_type=None,
283 partitions_only=None,
290 ldap_backend_nosync=None,
291 ldap_backend_extra_port=None,
292 ldap_backend_forced_uri=None,
293 ldap_dryrun_mode=None):
295 self.logger = self.get_logger("provision")
297 self.logger.setLevel(logging.WARNING)
299 self.logger.setLevel(logging.INFO)
301 lp = sambaopts.get_loadparm()
302 smbconf = lp.configfile
304 if dns_forwarder is not None:
305 suggested_forwarder = dns_forwarder
307 suggested_forwarder = self._get_nameserver_ip()
308 if suggested_forwarder is None:
309 suggested_forwarder = "none"
311 if len(self.raw_argv) == 1:
315 from getpass import getpass
318 def ask(prompt, default=None):
319 if default is not None:
320 print "%s [%s]: " % (prompt, default),
322 print "%s: " % (prompt,),
323 return sys.stdin.readline().rstrip("\n") or default
326 default = socket.getfqdn().split(".", 1)[1].upper()
329 realm = ask("Realm", default)
330 if realm in (None, ""):
331 raise CommandError("No realm set!")
334 default = realm.split(".")[0]
337 domain = ask("Domain", default)
339 raise CommandError("No domain set!")
341 server_role = ask("Server Role (dc, member, standalone)", "dc")
343 dns_backend = ask("DNS backend (SAMBA_INTERNAL, BIND9_FLATFILE, BIND9_DLZ, NONE)", "SAMBA_INTERNAL")
344 if dns_backend in (None, ''):
345 raise CommandError("No DNS backend set!")
347 if dns_backend == "SAMBA_INTERNAL":
348 dns_forwarder = ask("DNS forwarder IP address (write 'none' to disable forwarding)", suggested_forwarder)
349 if dns_forwarder.lower() in (None, 'none'):
350 suggested_forwarder = None
354 adminpassplain = getpass("Administrator password: ")
355 if not adminpassplain:
356 self.errf.write("Invalid administrator password.\n")
358 adminpassverify = getpass("Retype password: ")
359 if not adminpassplain == adminpassverify:
360 self.errf.write("Sorry, passwords do not match.\n")
362 adminpass = adminpassplain
366 realm = sambaopts._lp.get('realm')
368 raise CommandError("No realm set!")
370 raise CommandError("No domain set!")
373 self.logger.info("Administrator password will be set randomly!")
375 if function_level == "2000":
376 dom_for_fun_level = DS_DOMAIN_FUNCTION_2000
377 elif function_level == "2003":
378 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
379 elif function_level == "2008":
380 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008
381 elif function_level == "2008_R2":
382 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
384 if dns_backend == "SAMBA_INTERNAL" and dns_forwarder is None:
385 dns_forwarder = suggested_forwarder
387 samdb_fill = FILL_FULL
389 samdb_fill = FILL_NT4SYNC
390 elif partitions_only:
391 samdb_fill = FILL_DRS
393 if targetdir is not None:
394 if not os.path.isdir(targetdir):
399 if use_xattrs == "yes":
401 elif use_xattrs == "auto" and not lp.get("posix:eadb"):
403 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
405 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
408 samba.ntacls.setntacl(lp, file.name,
409 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
412 self.logger.info("You are not root or your system do not support xattr, using tdb backend for attributes. ")
417 self.logger.info("not using extended attributes to store ACLs and other metadata. If you intend to use this provision in production, rerun the script as root on a system supporting xattrs.")
418 if ldap_backend_type == "existing":
419 if ldap_backend_forced_uri is not None:
420 self.logger.warn("You have specified to use an existing LDAP server as the backend, please make sure an LDAP server is running at %s" % ldap_backend_forced_uri)
422 self.logger.info("You have specified to use an existing LDAP server as the backend, please make sure an LDAP server is running at the default location")
424 if ldap_backend_forced_uri is not None:
425 self.logger.warn("You have specified to use an fixed URI %s for connecting to your LDAP server backend. This is NOT RECOMMENDED, as our default communiation over ldapi:// is more secure and much less")
427 if domain_sid is not None:
428 domain_sid = security.dom_sid(domain_sid)
430 session = system_session()
432 result = provision(self.logger,
433 session, smbconf=smbconf, targetdir=targetdir,
434 samdb_fill=samdb_fill, realm=realm, domain=domain,
435 domainguid=domain_guid, domainsid=domain_sid,
437 hostip=host_ip, hostip6=host_ip6,
438 sitename=site, ntdsguid=ntds_guid,
439 invocationid=invocationid, adminpass=adminpass,
440 krbtgtpass=krbtgtpass, machinepass=machinepass,
441 dns_backend=dns_backend, dns_forwarder=dns_forwarder,
442 dnspass=dnspass, root=root, nobody=nobody,
444 serverrole=server_role, dom_for_fun_level=dom_for_fun_level,
445 backend_type=ldap_backend_type,
446 ldapadminpass=ldapadminpass, ol_mmr_urls=ol_mmr_urls, slapd_path=slapd_path,
447 useeadb=eadb, next_rid=next_rid, lp=lp, use_ntvfs=use_ntvfs,
448 use_rfc2307=use_rfc2307, skip_sysvolacl=False,
449 ldap_backend_extra_port=ldap_backend_extra_port,
450 ldap_backend_forced_uri=ldap_backend_forced_uri,
451 nosync=ldap_backend_nosync, ldap_dryrun_mode=ldap_dryrun_mode)
453 except ProvisioningError, e:
454 raise CommandError("Provision failed", e)
456 result.report_logger(self.logger)
458 def _get_nameserver_ip(self):
459 """Grab the nameserver IP address from /etc/resolv.conf."""
461 RESOLV_CONF="/etc/resolv.conf"
463 if not path.isfile(RESOLV_CONF):
464 self.logger.warning("Failed to locate %s" % RESOLV_CONF)
469 handle = open(RESOLV_CONF, 'r')
471 if not line.startswith('nameserver'):
473 # we want the last non-space continuous string of the line
474 return line.strip().split()[-1]
476 if handle is not None:
479 self.logger.warning("No nameserver found in %s" % RESOLV_CONF)
482 class cmd_domain_dcpromo(Command):
483 """Promote an existing domain member or NT4 PDC to an AD DC."""
485 synopsis = "%prog <dnsdomain> [DC|RODC] [options]"
487 takes_optiongroups = {
488 "sambaopts": options.SambaOptions,
489 "versionopts": options.VersionOptions,
490 "credopts": options.CredentialsOptions,
494 Option("--server", help="DC to join", type=str),
495 Option("--site", help="site to join", type=str),
496 Option("--targetdir", help="where to store provision", type=str),
497 Option("--domain-critical-only",
498 help="only replicate critical domain objects",
499 action="store_true"),
500 Option("--machinepass", type=str, metavar="PASSWORD",
501 help="choose machine password (otherwise random)"),
502 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
503 choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
504 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
505 "BIND9_DLZ uses samba4 AD to store zone information, "
506 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
507 default="SAMBA_INTERNAL"),
508 Option("--quiet", help="Be quiet", action="store_true"),
509 Option("--verbose", help="Be verbose", action="store_true")
513 Option("--use-ntvfs", action="store_true", help="Use NTVFS for the fileserver (default = no)"),
516 if samba.is_ntvfs_fileserver_built():
517 takes_options.extend(ntvfs_options)
520 takes_args = ["domain", "role?"]
522 def run(self, domain, role=None, sambaopts=None, credopts=None,
523 versionopts=None, server=None, site=None, targetdir=None,
524 domain_critical_only=False, parent_domain=None, machinepass=None,
525 use_ntvfs=False, dns_backend=None,
526 quiet=False, verbose=False):
527 lp = sambaopts.get_loadparm()
528 creds = credopts.get_credentials(lp)
529 net = Net(creds, lp, server=credopts.ipaddress)
532 site = "Default-First-Site-Name"
534 logger = self.get_logger()
536 logger.setLevel(logging.DEBUG)
538 logger.setLevel(logging.WARNING)
540 logger.setLevel(logging.INFO)
542 netbios_name = lp.get("netbios name")
548 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
549 site=site, netbios_name=netbios_name, targetdir=targetdir,
550 domain_critical_only=domain_critical_only,
551 machinepass=machinepass, use_ntvfs=use_ntvfs,
552 dns_backend=dns_backend,
553 promote_existing=True)
555 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
556 site=site, netbios_name=netbios_name, targetdir=targetdir,
557 domain_critical_only=domain_critical_only,
558 machinepass=machinepass, use_ntvfs=use_ntvfs, dns_backend=dns_backend,
559 promote_existing=True)
561 raise CommandError("Invalid role '%s' (possible values: DC, RODC)" % role)
564 class cmd_domain_join(Command):
565 """Join domain as either member or backup domain controller."""
567 synopsis = "%prog <dnsdomain> [DC|RODC|MEMBER|SUBDOMAIN] [options]"
569 takes_optiongroups = {
570 "sambaopts": options.SambaOptions,
571 "versionopts": options.VersionOptions,
572 "credopts": options.CredentialsOptions,
576 Option("--server", help="DC to join", type=str),
577 Option("--site", help="site to join", type=str),
578 Option("--targetdir", help="where to store provision", type=str),
579 Option("--parent-domain", help="parent domain to create subdomain under", type=str),
580 Option("--domain-critical-only",
581 help="only replicate critical domain objects",
582 action="store_true"),
583 Option("--machinepass", type=str, metavar="PASSWORD",
584 help="choose machine password (otherwise random)"),
585 Option("--adminpass", type="string", metavar="PASSWORD",
586 help="choose adminstrator password when joining as a subdomain (otherwise random)"),
587 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
588 choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
589 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
590 "BIND9_DLZ uses samba4 AD to store zone information, "
591 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
592 default="SAMBA_INTERNAL"),
593 Option("--quiet", help="Be quiet", action="store_true"),
594 Option("--verbose", help="Be verbose", action="store_true")
598 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
601 if samba.is_ntvfs_fileserver_built():
602 takes_options.extend(ntvfs_options)
604 takes_args = ["domain", "role?"]
606 def run(self, domain, role=None, sambaopts=None, credopts=None,
607 versionopts=None, server=None, site=None, targetdir=None,
608 domain_critical_only=False, parent_domain=None, machinepass=None,
609 use_ntvfs=False, dns_backend=None, adminpass=None,
610 quiet=False, verbose=False):
611 lp = sambaopts.get_loadparm()
612 creds = credopts.get_credentials(lp)
613 net = Net(creds, lp, server=credopts.ipaddress)
616 site = "Default-First-Site-Name"
618 logger = self.get_logger()
620 logger.setLevel(logging.DEBUG)
622 logger.setLevel(logging.WARNING)
624 logger.setLevel(logging.INFO)
626 netbios_name = lp.get("netbios name")
631 if role is None or role == "MEMBER":
632 (join_password, sid, domain_name) = net.join_member(
633 domain, netbios_name, LIBNET_JOIN_AUTOMATIC,
634 machinepass=machinepass)
636 self.errf.write("Joined domain %s (%s)\n" % (domain_name, sid))
638 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
639 site=site, netbios_name=netbios_name, targetdir=targetdir,
640 domain_critical_only=domain_critical_only,
641 machinepass=machinepass, use_ntvfs=use_ntvfs, dns_backend=dns_backend)
643 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
644 site=site, netbios_name=netbios_name, targetdir=targetdir,
645 domain_critical_only=domain_critical_only,
646 machinepass=machinepass, use_ntvfs=use_ntvfs,
647 dns_backend=dns_backend)
648 elif role == "SUBDOMAIN":
650 logger.info("Administrator password will be set randomly!")
652 netbios_domain = lp.get("workgroup")
653 if parent_domain is None:
654 parent_domain = ".".join(domain.split(".")[1:])
655 join_subdomain(logger=logger, server=server, creds=creds, lp=lp, dnsdomain=domain,
656 parent_domain=parent_domain, site=site,
657 netbios_name=netbios_name, netbios_domain=netbios_domain,
658 targetdir=targetdir, machinepass=machinepass,
659 use_ntvfs=use_ntvfs, dns_backend=dns_backend,
662 raise CommandError("Invalid role '%s' (possible values: MEMBER, DC, RODC, SUBDOMAIN)" % role)
665 class cmd_domain_demote(Command):
666 """Demote ourselves from the role of Domain Controller."""
668 synopsis = "%prog [options]"
671 Option("--server", help="writable DC to write demotion changes on", type=str),
672 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
673 metavar="URL", dest="H"),
674 Option("--remove-other-dead-server", help="Dead DC to remove ALL references to (rather than this DC)", type=str),
677 takes_optiongroups = {
678 "sambaopts": options.SambaOptions,
679 "credopts": options.CredentialsOptions,
680 "versionopts": options.VersionOptions,
683 def run(self, sambaopts=None, credopts=None,
684 versionopts=None, server=None,
685 remove_other_dead_server=None, H=None):
686 lp = sambaopts.get_loadparm()
687 creds = credopts.get_credentials(lp)
688 net = Net(creds, lp, server=credopts.ipaddress)
690 if remove_other_dead_server is not None:
691 if server is not None:
692 samdb = SamDB(url="ldap://%s" % server,
693 session_info=system_session(),
694 credentials=creds, lp=lp)
696 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
697 remove_dc.remove_dc(samdb, remove_other_dead_server)
700 netbios_name = lp.get("netbios name")
701 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
703 res = samdb.search(expression='(&(objectClass=computer)(serverReferenceBL=*))', attrs=["dnsHostName", "name"])
705 raise CommandError("Unable to search for servers")
708 raise CommandError("You are the latest server in the domain")
712 if str(e["name"]).lower() != netbios_name.lower():
713 server = e["dnsHostName"]
716 ntds_guid = samdb.get_ntds_GUID()
717 msg = samdb.search(base=str(samdb.get_config_basedn()),
718 scope=ldb.SCOPE_SUBTREE, expression="(objectGUID=%s)" % ntds_guid,
720 if len(msg) == 0 or "options" not in msg[0]:
721 raise CommandError("Failed to find options on %s" % ntds_guid)
724 dsa_options = int(str(msg[0]['options']))
726 res = samdb.search(expression="(fSMORoleOwner=%s)" % str(ntds_dn),
727 controls=["search_options:1:2"])
730 raise CommandError("Current DC is still the owner of %d role(s), use the role command to transfer roles to another DC" % len(res))
732 self.errf.write("Using %s as partner server for the demotion\n" %
734 (drsuapiBind, drsuapi_handle, supportedExtensions) = drsuapi_connect(server, lp, creds)
736 self.errf.write("Deactivating inbound replication\n")
738 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
742 dsa_options |= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
743 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
747 self.errf.write("Asking partner server %s to synchronize from us\n"
749 for part in (samdb.get_schema_basedn(),
750 samdb.get_config_basedn(),
751 samdb.get_root_basedn()):
752 nc = drsuapi.DsReplicaObjectIdentifier()
755 req1 = drsuapi.DsReplicaSyncRequest1()
756 req1.naming_context = nc;
757 req1.options = drsuapi.DRSUAPI_DRS_WRIT_REP
758 req1.source_dsa_guid = misc.GUID(ntds_guid)
761 drsuapiBind.DsReplicaSync(drsuapi_handle, 1, req1)
762 except RuntimeError as (werr, string):
763 if werr == 8452: #WERR_DS_DRA_NO_REPLICA
767 "Error while demoting, "
768 "re-enabling inbound replication\n")
769 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
770 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
772 raise CommandError("Error while sending a DsReplicaSync for partion %s" % str(part), e)
774 remote_samdb = SamDB(url="ldap://%s" % server,
775 session_info=system_session(),
776 credentials=creds, lp=lp)
778 self.errf.write("Changing userControl and container\n")
779 res = remote_samdb.search(base=str(remote_samdb.domain_dn()),
780 expression="(&(objectClass=user)(sAMAccountName=%s$))" %
781 netbios_name.upper(),
782 attrs=["userAccountControl"])
784 uac = int(str(res[0]["userAccountControl"]))
788 "Error while demoting, re-enabling inbound replication\n")
789 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
790 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
792 raise CommandError("Error while changing account control", e)
796 "Error while demoting, re-enabling inbound replication")
797 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
798 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
800 raise CommandError("Unable to find object with samaccountName = %s$"
801 " in the remote dc" % netbios_name.upper())
805 uac &= ~(UF_SERVER_TRUST_ACCOUNT|UF_TRUSTED_FOR_DELEGATION|UF_PARTIAL_SECRETS_ACCOUNT)
806 uac |= UF_WORKSTATION_TRUST_ACCOUNT
811 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
812 ldb.FLAG_MOD_REPLACE,
813 "userAccountControl")
815 remote_samdb.modify(msg)
818 "Error while demoting, re-enabling inbound replication")
819 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
820 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
823 raise CommandError("Error while changing account control", e)
825 parent = msg.dn.parent()
826 rdn = "%s=%s" % (res[0].dn.get_rdn_name(), res[0].dn.get_rdn_value())
827 # Let's move to the Computer container
831 computer_dn = ldb.Dn(remote_samdb, "CN=Computers,%s" % str(remote_samdb.domain_dn()))
832 res = remote_samdb.search(base=computer_dn, expression=rdn, scope=ldb.SCOPE_ONELEVEL)
835 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
836 scope=ldb.SCOPE_ONELEVEL)
837 while(len(res) != 0 and i < 100):
839 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
840 scope=ldb.SCOPE_ONELEVEL)
844 "Error while demoting, re-enabling inbound replication\n")
845 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
846 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
852 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
853 ldb.FLAG_MOD_REPLACE,
854 "userAccountControl")
856 remote_samdb.modify(msg)
858 raise CommandError("Unable to find a slot for renaming %s,"
859 " all names from %s-1 to %s-%d seemed used" %
860 (str(dc_dn), rdn, rdn, i - 9))
862 newrdn = "%s-%d" % (rdn, i)
865 newdn = ldb.Dn(remote_samdb, "%s,%s" % (newrdn, str(computer_dn)))
866 remote_samdb.rename(dc_dn, newdn)
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)
882 raise CommandError("Error while renaming %s to %s" % (str(dc_dn), str(newdn)), e)
885 server_dsa_dn = samdb.get_serverName()
886 domain = remote_samdb.get_root_basedn()
889 req1 = drsuapi.DsRemoveDSServerRequest1()
890 req1.server_dn = str(server_dsa_dn)
891 req1.domain_dn = str(domain)
894 drsuapiBind.DsRemoveDSServer(drsuapi_handle, 1, req1)
895 except RuntimeError as (werr, string):
896 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
898 "Error while demoting, re-enabling inbound replication\n")
899 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
900 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
906 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
907 ldb.FLAG_MOD_REPLACE,
908 "userAccountControl")
909 remote_samdb.modify(msg)
910 remote_samdb.rename(newdn, dc_dn)
911 if werr == 8452: #WERR_DS_DRA_NO_REPLICA
912 raise CommandError("The DC %s is not present on (already removed from) the remote server: " % server_dsa_dn, e)
914 raise CommandError("Error while sending a removeDsServer of %s: " % server_dsa_dn, e)
916 remove_dc.remove_sysvol_references(remote_samdb, rdn)
918 # These are objects under the computer account that should be deleted
919 for s in ("CN=Enterprise,CN=NTFRS Subscriptions",
920 "CN=%s, CN=NTFRS Subscriptions" % lp.get("realm"),
921 "CN=Domain system Volumes (SYSVOL Share), CN=NTFRS Subscriptions",
922 "CN=NTFRS Subscriptions"):
924 remote_samdb.delete(ldb.Dn(remote_samdb,
925 "%s,%s" % (s, str(newdn))))
926 except ldb.LdbError, l:
929 self.errf.write("Demote successful\n")
932 class cmd_domain_level(Command):
933 """Raise domain and forest function levels."""
935 synopsis = "%prog (show|raise <options>) [options]"
937 takes_optiongroups = {
938 "sambaopts": options.SambaOptions,
939 "credopts": options.CredentialsOptions,
940 "versionopts": options.VersionOptions,
944 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
945 metavar="URL", dest="H"),
946 Option("--quiet", help="Be quiet", action="store_true"),
947 Option("--forest-level", type="choice", choices=["2003", "2008", "2008_R2"],
948 help="The forest function level (2003 | 2008 | 2008_R2)"),
949 Option("--domain-level", type="choice", choices=["2003", "2008", "2008_R2"],
950 help="The domain function level (2003 | 2008 | 2008_R2)")
953 takes_args = ["subcommand"]
955 def run(self, subcommand, H=None, forest_level=None, domain_level=None,
956 quiet=False, credopts=None, sambaopts=None, versionopts=None):
957 lp = sambaopts.get_loadparm()
958 creds = credopts.get_credentials(lp, fallback_machine=True)
960 samdb = SamDB(url=H, session_info=system_session(),
961 credentials=creds, lp=lp)
963 domain_dn = samdb.domain_dn()
965 res_forest = samdb.search("CN=Partitions,%s" % samdb.get_config_basedn(),
966 scope=ldb.SCOPE_BASE, attrs=["msDS-Behavior-Version"])
967 assert len(res_forest) == 1
969 res_domain = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
970 attrs=["msDS-Behavior-Version", "nTMixedDomain"])
971 assert len(res_domain) == 1
973 res_dc_s = samdb.search("CN=Sites,%s" % samdb.get_config_basedn(),
974 scope=ldb.SCOPE_SUBTREE, expression="(objectClass=nTDSDSA)",
975 attrs=["msDS-Behavior-Version"])
976 assert len(res_dc_s) >= 1
979 level_forest = int(res_forest[0]["msDS-Behavior-Version"][0])
980 level_domain = int(res_domain[0]["msDS-Behavior-Version"][0])
981 level_domain_mixed = int(res_domain[0]["nTMixedDomain"][0])
983 min_level_dc = int(res_dc_s[0]["msDS-Behavior-Version"][0]) # Init value
985 if int(msg["msDS-Behavior-Version"][0]) < min_level_dc:
986 min_level_dc = int(msg["msDS-Behavior-Version"][0])
988 if level_forest < 0 or level_domain < 0:
989 raise CommandError("Domain and/or forest function level(s) is/are invalid. Correct them or reprovision!")
991 raise CommandError("Lowest function level of a DC is invalid. Correct this or reprovision!")
992 if level_forest > level_domain:
993 raise CommandError("Forest function level is higher than the domain level(s). Correct this or reprovision!")
994 if level_domain > min_level_dc:
995 raise CommandError("Domain function level is higher than the lowest function level of a DC. Correct this or reprovision!")
998 raise CommandError("Could not retrieve the actual domain, forest level and/or lowest DC function level!")
1000 if subcommand == "show":
1001 self.message("Domain and forest function level for domain '%s'" % domain_dn)
1002 if level_forest == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1003 self.message("\nATTENTION: You run SAMBA 4 on a forest function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1004 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1005 self.message("\nATTENTION: You run SAMBA 4 on a domain function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1006 if min_level_dc == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1007 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)!")
1011 if level_forest == DS_DOMAIN_FUNCTION_2000:
1013 elif level_forest == DS_DOMAIN_FUNCTION_2003_MIXED:
1014 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1015 elif level_forest == DS_DOMAIN_FUNCTION_2003:
1017 elif level_forest == DS_DOMAIN_FUNCTION_2008:
1019 elif level_forest == DS_DOMAIN_FUNCTION_2008_R2:
1022 outstr = "higher than 2008 R2"
1023 self.message("Forest function level: (Windows) " + outstr)
1025 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1026 outstr = "2000 mixed (NT4 DC support)"
1027 elif level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed == 0:
1029 elif level_domain == DS_DOMAIN_FUNCTION_2003_MIXED:
1030 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1031 elif level_domain == DS_DOMAIN_FUNCTION_2003:
1033 elif level_domain == DS_DOMAIN_FUNCTION_2008:
1035 elif level_domain == DS_DOMAIN_FUNCTION_2008_R2:
1038 outstr = "higher than 2008 R2"
1039 self.message("Domain function level: (Windows) " + outstr)
1041 if min_level_dc == DS_DOMAIN_FUNCTION_2000:
1043 elif min_level_dc == DS_DOMAIN_FUNCTION_2003:
1045 elif min_level_dc == DS_DOMAIN_FUNCTION_2008:
1047 elif min_level_dc == DS_DOMAIN_FUNCTION_2008_R2:
1050 outstr = "higher than 2008 R2"
1051 self.message("Lowest function level of a DC: (Windows) " + outstr)
1053 elif subcommand == "raise":
1056 if domain_level is not None:
1057 if domain_level == "2003":
1058 new_level_domain = DS_DOMAIN_FUNCTION_2003
1059 elif domain_level == "2008":
1060 new_level_domain = DS_DOMAIN_FUNCTION_2008
1061 elif domain_level == "2008_R2":
1062 new_level_domain = DS_DOMAIN_FUNCTION_2008_R2
1064 if new_level_domain <= level_domain and level_domain_mixed == 0:
1065 raise CommandError("Domain function level can't be smaller than or equal to the actual one!")
1067 if new_level_domain > min_level_dc:
1068 raise CommandError("Domain function level can't be higher than the lowest function level of a DC!")
1070 # Deactivate mixed/interim domain support
1071 if level_domain_mixed != 0:
1072 # Directly on the base DN
1074 m.dn = ldb.Dn(samdb, domain_dn)
1075 m["nTMixedDomain"] = ldb.MessageElement("0",
1076 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1080 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup") + ",CN=Partitions,%s" % samdb.get_config_basedn())
1081 m["nTMixedDomain"] = ldb.MessageElement("0",
1082 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1085 except ldb.LdbError, (enum, emsg):
1086 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1089 # Directly on the base DN
1091 m.dn = ldb.Dn(samdb, domain_dn)
1092 m["msDS-Behavior-Version"]= ldb.MessageElement(
1093 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1094 "msDS-Behavior-Version")
1098 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup")
1099 + ",CN=Partitions,%s" % samdb.get_config_basedn())
1100 m["msDS-Behavior-Version"]= ldb.MessageElement(
1101 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1102 "msDS-Behavior-Version")
1105 except ldb.LdbError, (enum, emsg):
1106 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1109 level_domain = new_level_domain
1110 msgs.append("Domain function level changed!")
1112 if forest_level is not None:
1113 if forest_level == "2003":
1114 new_level_forest = DS_DOMAIN_FUNCTION_2003
1115 elif forest_level == "2008":
1116 new_level_forest = DS_DOMAIN_FUNCTION_2008
1117 elif forest_level == "2008_R2":
1118 new_level_forest = DS_DOMAIN_FUNCTION_2008_R2
1119 if new_level_forest <= level_forest:
1120 raise CommandError("Forest function level can't be smaller than or equal to the actual one!")
1121 if new_level_forest > level_domain:
1122 raise CommandError("Forest function level can't be higher than the domain function level(s). Please raise it/them first!")
1124 m.dn = ldb.Dn(samdb, "CN=Partitions,%s" % samdb.get_config_basedn())
1125 m["msDS-Behavior-Version"]= ldb.MessageElement(
1126 str(new_level_forest), ldb.FLAG_MOD_REPLACE,
1127 "msDS-Behavior-Version")
1129 msgs.append("Forest function level changed!")
1130 msgs.append("All changes applied successfully!")
1131 self.message("\n".join(msgs))
1133 raise CommandError("invalid argument: '%s' (choose from 'show', 'raise')" % subcommand)
1136 class cmd_domain_passwordsettings(Command):
1137 """Set password settings.
1139 Password complexity, password lockout policy, history length,
1140 minimum password length, the minimum and maximum password age) on
1141 a Samba AD DC server.
1143 Use against a Windows DC is possible, but group policy will override it.
1146 synopsis = "%prog (show|set <options>) [options]"
1148 takes_optiongroups = {
1149 "sambaopts": options.SambaOptions,
1150 "versionopts": options.VersionOptions,
1151 "credopts": options.CredentialsOptions,
1155 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1156 metavar="URL", dest="H"),
1157 Option("--quiet", help="Be quiet", action="store_true"),
1158 Option("--complexity", type="choice", choices=["on","off","default"],
1159 help="The password complexity (on | off | default). Default is 'on'"),
1160 Option("--store-plaintext", type="choice", choices=["on","off","default"],
1161 help="Store plaintext passwords where account have 'store passwords with reversible encryption' set (on | off | default). Default is 'off'"),
1162 Option("--history-length",
1163 help="The password history length (<integer> | default). Default is 24.", type=str),
1164 Option("--min-pwd-length",
1165 help="The minimum password length (<integer> | default). Default is 7.", type=str),
1166 Option("--min-pwd-age",
1167 help="The minimum password age (<integer in days> | default). Default is 1.", type=str),
1168 Option("--max-pwd-age",
1169 help="The maximum password age (<integer in days> | default). Default is 43.", type=str),
1170 Option("--account-lockout-duration",
1171 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),
1172 Option("--account-lockout-threshold",
1173 help="The number of bad password attempts allowed before locking out the account (<integer> | default). Default is 0 (never lock out).", type=str),
1174 Option("--reset-account-lockout-after",
1175 help="After this time is elapsed, the recorded number of attempts restarts from zero (<integer> | default). Default is 30.", type=str),
1178 takes_args = ["subcommand"]
1180 def run(self, subcommand, H=None, min_pwd_age=None, max_pwd_age=None,
1181 quiet=False, complexity=None, store_plaintext=None, history_length=None,
1182 min_pwd_length=None, account_lockout_duration=None, account_lockout_threshold=None,
1183 reset_account_lockout_after=None, credopts=None, sambaopts=None,
1185 lp = sambaopts.get_loadparm()
1186 creds = credopts.get_credentials(lp)
1188 samdb = SamDB(url=H, session_info=system_session(),
1189 credentials=creds, lp=lp)
1191 domain_dn = samdb.domain_dn()
1192 res = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1193 attrs=["pwdProperties", "pwdHistoryLength", "minPwdLength",
1194 "minPwdAge", "maxPwdAge", "lockoutDuration", "lockoutThreshold",
1195 "lockOutObservationWindow"])
1196 assert(len(res) == 1)
1198 pwd_props = int(res[0]["pwdProperties"][0])
1199 pwd_hist_len = int(res[0]["pwdHistoryLength"][0])
1200 cur_min_pwd_len = int(res[0]["minPwdLength"][0])
1202 cur_min_pwd_age = int(abs(int(res[0]["minPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1203 if int(res[0]["maxPwdAge"][0]) == -0x8000000000000000:
1206 cur_max_pwd_age = int(abs(int(res[0]["maxPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1207 cur_account_lockout_threshold = int(res[0]["lockoutThreshold"][0])
1209 if int(res[0]["lockoutDuration"][0]) == -0x8000000000000000:
1210 cur_account_lockout_duration = 0
1212 cur_account_lockout_duration = abs(int(res[0]["lockoutDuration"][0])) / (1e7 * 60)
1213 cur_reset_account_lockout_after = abs(int(res[0]["lockOutObservationWindow"][0])) / (1e7 * 60)
1214 except Exception, e:
1215 raise CommandError("Could not retrieve password properties!", e)
1217 if subcommand == "show":
1218 self.message("Password informations for domain '%s'" % domain_dn)
1220 if pwd_props & DOMAIN_PASSWORD_COMPLEX != 0:
1221 self.message("Password complexity: on")
1223 self.message("Password complexity: off")
1224 if pwd_props & DOMAIN_PASSWORD_STORE_CLEARTEXT != 0:
1225 self.message("Store plaintext passwords: on")
1227 self.message("Store plaintext passwords: off")
1228 self.message("Password history length: %d" % pwd_hist_len)
1229 self.message("Minimum password length: %d" % cur_min_pwd_len)
1230 self.message("Minimum password age (days): %d" % cur_min_pwd_age)
1231 self.message("Maximum password age (days): %d" % cur_max_pwd_age)
1232 self.message("Account lockout duration (mins): %d" % cur_account_lockout_duration)
1233 self.message("Account lockout threshold (attempts): %d" % cur_account_lockout_threshold)
1234 self.message("Reset account lockout after (mins): %d" % cur_reset_account_lockout_after)
1235 elif subcommand == "set":
1238 m.dn = ldb.Dn(samdb, domain_dn)
1240 if complexity is not None:
1241 if complexity == "on" or complexity == "default":
1242 pwd_props = pwd_props | DOMAIN_PASSWORD_COMPLEX
1243 msgs.append("Password complexity activated!")
1244 elif complexity == "off":
1245 pwd_props = pwd_props & (~DOMAIN_PASSWORD_COMPLEX)
1246 msgs.append("Password complexity deactivated!")
1248 if store_plaintext is not None:
1249 if store_plaintext == "on" or store_plaintext == "default":
1250 pwd_props = pwd_props | DOMAIN_PASSWORD_STORE_CLEARTEXT
1251 msgs.append("Plaintext password storage for changed passwords activated!")
1252 elif store_plaintext == "off":
1253 pwd_props = pwd_props & (~DOMAIN_PASSWORD_STORE_CLEARTEXT)
1254 msgs.append("Plaintext password storage for changed passwords deactivated!")
1256 if complexity is not None or store_plaintext is not None:
1257 m["pwdProperties"] = ldb.MessageElement(str(pwd_props),
1258 ldb.FLAG_MOD_REPLACE, "pwdProperties")
1260 if history_length is not None:
1261 if history_length == "default":
1264 pwd_hist_len = int(history_length)
1266 if pwd_hist_len < 0 or pwd_hist_len > 24:
1267 raise CommandError("Password history length must be in the range of 0 to 24!")
1269 m["pwdHistoryLength"] = ldb.MessageElement(str(pwd_hist_len),
1270 ldb.FLAG_MOD_REPLACE, "pwdHistoryLength")
1271 msgs.append("Password history length changed!")
1273 if min_pwd_length is not None:
1274 if min_pwd_length == "default":
1277 min_pwd_len = int(min_pwd_length)
1279 if min_pwd_len < 0 or min_pwd_len > 14:
1280 raise CommandError("Minimum password length must be in the range of 0 to 14!")
1282 m["minPwdLength"] = ldb.MessageElement(str(min_pwd_len),
1283 ldb.FLAG_MOD_REPLACE, "minPwdLength")
1284 msgs.append("Minimum password length changed!")
1286 if min_pwd_age is not None:
1287 if min_pwd_age == "default":
1290 min_pwd_age = int(min_pwd_age)
1292 if min_pwd_age < 0 or min_pwd_age > 998:
1293 raise CommandError("Minimum password age must be in the range of 0 to 998!")
1296 min_pwd_age_ticks = -int(min_pwd_age * (24 * 60 * 60 * 1e7))
1298 m["minPwdAge"] = ldb.MessageElement(str(min_pwd_age_ticks),
1299 ldb.FLAG_MOD_REPLACE, "minPwdAge")
1300 msgs.append("Minimum password age changed!")
1302 if max_pwd_age is not None:
1303 if max_pwd_age == "default":
1306 max_pwd_age = int(max_pwd_age)
1308 if max_pwd_age < 0 or max_pwd_age > 999:
1309 raise CommandError("Maximum password age must be in the range of 0 to 999!")
1312 if max_pwd_age == 0:
1313 max_pwd_age_ticks = -0x8000000000000000
1315 max_pwd_age_ticks = -int(max_pwd_age * (24 * 60 * 60 * 1e7))
1317 m["maxPwdAge"] = ldb.MessageElement(str(max_pwd_age_ticks),
1318 ldb.FLAG_MOD_REPLACE, "maxPwdAge")
1319 msgs.append("Maximum password age changed!")
1321 if account_lockout_duration is not None:
1322 if account_lockout_duration == "default":
1323 account_lockout_duration = 30
1325 account_lockout_duration = int(account_lockout_duration)
1327 if account_lockout_duration < 0 or account_lockout_duration > 99999:
1328 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1331 if account_lockout_duration == 0:
1332 account_lockout_duration_ticks = -0x8000000000000000
1334 account_lockout_duration_ticks = -int(account_lockout_duration * (60 * 1e7))
1336 m["lockoutDuration"] = ldb.MessageElement(str(account_lockout_duration_ticks),
1337 ldb.FLAG_MOD_REPLACE, "lockoutDuration")
1338 msgs.append("Account lockout duration changed!")
1340 if account_lockout_threshold is not None:
1341 if account_lockout_threshold == "default":
1342 account_lockout_threshold = 0
1344 account_lockout_threshold = int(account_lockout_threshold)
1346 m["lockoutThreshold"] = ldb.MessageElement(str(account_lockout_threshold),
1347 ldb.FLAG_MOD_REPLACE, "lockoutThreshold")
1348 msgs.append("Account lockout threshold changed!")
1350 if reset_account_lockout_after is not None:
1351 if reset_account_lockout_after == "default":
1352 reset_account_lockout_after = 30
1354 reset_account_lockout_after = int(reset_account_lockout_after)
1356 if reset_account_lockout_after < 0 or reset_account_lockout_after > 99999:
1357 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1360 if reset_account_lockout_after == 0:
1361 reset_account_lockout_after_ticks = -0x8000000000000000
1363 reset_account_lockout_after_ticks = -int(reset_account_lockout_after * (60 * 1e7))
1365 m["lockOutObservationWindow"] = ldb.MessageElement(str(reset_account_lockout_after_ticks),
1366 ldb.FLAG_MOD_REPLACE, "lockOutObservationWindow")
1367 msgs.append("Duration to reset account lockout after changed!")
1369 if max_pwd_age > 0 and min_pwd_age >= max_pwd_age:
1370 raise CommandError("Maximum password age (%d) must be greater than minimum password age (%d)!" % (max_pwd_age, min_pwd_age))
1373 raise CommandError("You must specify at least one option to set. Try --help")
1375 msgs.append("All changes applied successfully!")
1376 self.message("\n".join(msgs))
1378 raise CommandError("Wrong argument '%s'!" % subcommand)
1381 class cmd_domain_classicupgrade(Command):
1382 """Upgrade from Samba classic (NT4-like) database to Samba AD DC database.
1384 Specify either a directory with all Samba classic DC databases and state files (with --dbdir) or
1385 the testparm utility from your classic installation (with --testparm).
1388 synopsis = "%prog [options] <classic_smb_conf>"
1390 takes_optiongroups = {
1391 "sambaopts": options.SambaOptions,
1392 "versionopts": options.VersionOptions
1396 Option("--dbdir", type="string", metavar="DIR",
1397 help="Path to samba classic DC database directory"),
1398 Option("--testparm", type="string", metavar="PATH",
1399 help="Path to samba classic DC testparm utility from the previous installation. This allows the default paths of the previous installation to be followed"),
1400 Option("--targetdir", type="string", metavar="DIR",
1401 help="Path prefix where the new Samba 4.0 AD domain should be initialised"),
1402 Option("--quiet", help="Be quiet", action="store_true"),
1403 Option("--verbose", help="Be verbose", action="store_true"),
1404 Option("--use-xattrs", type="choice", choices=["yes","no","auto"], metavar="[yes|no|auto]",
1405 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"),
1406 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
1407 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
1408 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
1409 "BIND9_FLATFILE uses bind9 text database to store zone information, "
1410 "BIND9_DLZ uses samba4 AD to store zone information, "
1411 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
1412 default="SAMBA_INTERNAL")
1416 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
1417 action="store_true")
1419 if samba.is_ntvfs_fileserver_built():
1420 takes_options.extend(ntvfs_options)
1422 takes_args = ["smbconf"]
1424 def run(self, smbconf=None, targetdir=None, dbdir=None, testparm=None,
1425 quiet=False, verbose=False, use_xattrs=None, sambaopts=None, versionopts=None,
1426 dns_backend=None, use_ntvfs=False):
1428 if not os.path.exists(smbconf):
1429 raise CommandError("File %s does not exist" % smbconf)
1431 if testparm and not os.path.exists(testparm):
1432 raise CommandError("Testparm utility %s does not exist" % testparm)
1434 if dbdir and not os.path.exists(dbdir):
1435 raise CommandError("Directory %s does not exist" % dbdir)
1437 if not dbdir and not testparm:
1438 raise CommandError("Please specify either dbdir or testparm")
1440 logger = self.get_logger()
1442 logger.setLevel(logging.DEBUG)
1444 logger.setLevel(logging.WARNING)
1446 logger.setLevel(logging.INFO)
1448 if dbdir and testparm:
1449 logger.warning("both dbdir and testparm specified, ignoring dbdir.")
1452 lp = sambaopts.get_loadparm()
1454 s3conf = s3param.get_context()
1457 s3conf.set("realm", sambaopts.realm)
1459 if targetdir is not None:
1460 if not os.path.isdir(targetdir):
1464 if use_xattrs == "yes":
1466 elif use_xattrs == "auto" and not s3conf.get("posix:eadb"):
1468 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
1470 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
1473 samba.ntacls.setntacl(lp, tmpfile.name,
1474 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
1477 # FIXME: Don't catch all exceptions here
1478 logger.info("You are not root or your system do not support xattr, using tdb backend for attributes. "
1479 "If you intend to use this provision in production, rerun the script as root on a system supporting xattrs.")
1483 # Set correct default values from dbdir or testparm
1486 paths["state directory"] = dbdir
1487 paths["private dir"] = dbdir
1488 paths["lock directory"] = dbdir
1489 paths["smb passwd file"] = dbdir + "/smbpasswd"
1491 paths["state directory"] = get_testparm_var(testparm, smbconf, "state directory")
1492 paths["private dir"] = get_testparm_var(testparm, smbconf, "private dir")
1493 paths["smb passwd file"] = get_testparm_var(testparm, smbconf, "smb passwd file")
1494 paths["lock directory"] = get_testparm_var(testparm, smbconf, "lock directory")
1495 # "testparm" from Samba 3 < 3.4.x is not aware of the parameter
1496 # "state directory", instead make use of "lock directory"
1497 if len(paths["state directory"]) == 0:
1498 paths["state directory"] = paths["lock directory"]
1501 s3conf.set(p, paths[p])
1503 # load smb.conf parameters
1504 logger.info("Reading smb.conf")
1505 s3conf.load(smbconf)
1506 samba3 = Samba3(smbconf, s3conf)
1508 logger.info("Provisioning")
1509 upgrade_from_samba3(samba3, logger, targetdir, session_info=system_session(),
1510 useeadb=eadb, dns_backend=dns_backend, use_ntvfs=use_ntvfs)
1513 class cmd_domain_samba3upgrade(cmd_domain_classicupgrade):
1514 __doc__ = cmd_domain_classicupgrade.__doc__
1516 # This command is present for backwards compatibility only,
1517 # and should not be shown.
1521 class LocalDCCredentialsOptions(options.CredentialsOptions):
1522 def __init__(self, parser):
1523 options.CredentialsOptions.__init__(self, parser, special_name="local-dc")
1525 class DomainTrustCommand(Command):
1526 """List domain trusts."""
1529 Command.__init__(self)
1530 self.local_lp = None
1532 self.local_server = None
1533 self.local_binding_string = None
1534 self.local_creds = None
1536 self.remote_server = None
1537 self.remote_binding_string = None
1538 self.remote_creds = None
1540 WERR_OK = 0x00000000
1541 WERR_INVALID_FUNCTION = 0x00000001
1542 WERR_NERR_ACFNOTLOADED = 0x000008B3
1544 NT_STATUS_NOT_FOUND = 0xC0000225
1545 NT_STATUS_OBJECT_NAME_NOT_FOUND = 0xC0000034
1546 NT_STATUS_INVALID_PARAMETER = 0xC000000D
1547 NT_STATUS_INVALID_INFO_CLASS = 0xC0000003
1548 NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE = 0xC002002E
1550 def _uint32(self, v):
1551 return ctypes.c_uint32(v).value
1553 def check_runtime_error(self, runtime, val):
1557 err32 = self._uint32(runtime[0])
1563 class LocalRuntimeError(CommandError):
1564 def __init__(exception_self, self, runtime, message):
1565 err32 = self._uint32(runtime[0])
1567 msg = "LOCAL_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1568 self.local_server, message, err32, errstr)
1569 CommandError.__init__(exception_self, msg)
1571 class RemoteRuntimeError(CommandError):
1572 def __init__(exception_self, self, runtime, message):
1573 err32 = self._uint32(runtime[0])
1575 msg = "REMOTE_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1576 self.remote_server, message, err32, errstr)
1577 CommandError.__init__(exception_self, msg)
1579 class LocalLdbError(CommandError):
1580 def __init__(exception_self, self, ldb_error, message):
1581 errval = ldb_error[0]
1582 errstr = ldb_error[1]
1583 msg = "LOCAL_DC[%s]: %s - ERROR(%d) - %s" % (
1584 self.local_server, message, errval, errstr)
1585 CommandError.__init__(exception_self, msg)
1587 def setup_local_server(self, sambaopts, localdcopts):
1588 if self.local_server is not None:
1589 return self.local_server
1591 lp = sambaopts.get_loadparm()
1593 local_server = localdcopts.ipaddress
1594 if local_server is None:
1595 server_role = lp.server_role()
1596 if server_role != "ROLE_ACTIVE_DIRECTORY_DC":
1597 raise CommandError("Invalid server_role %s" % (server_role))
1598 local_server = lp.get('netbios name')
1599 local_transport = "ncalrpc"
1600 local_binding_options = ""
1601 local_binding_options += ",auth_type=ncalrpc_as_system"
1602 local_ldap_url = None
1605 local_transport = "ncacn_np"
1606 local_binding_options = ""
1607 local_ldap_url = "ldap://%s" % local_server
1608 local_creds = localdcopts.get_credentials(lp)
1612 self.local_server = local_server
1613 self.local_binding_string = "%s:%s[%s]" % (local_transport, local_server, local_binding_options)
1614 self.local_ldap_url = local_ldap_url
1615 self.local_creds = local_creds
1616 return self.local_server
1618 def new_local_lsa_connection(self):
1619 return lsa.lsarpc(self.local_binding_string, self.local_lp, self.local_creds)
1621 def new_local_netlogon_connection(self):
1622 return netlogon.netlogon(self.local_binding_string, self.local_lp, self.local_creds)
1624 def new_local_ldap_connection(self):
1625 return SamDB(url=self.local_ldap_url,
1626 session_info=system_session(),
1627 credentials=self.local_creds,
1630 def setup_remote_server(self, credopts, domain,
1632 require_writable=True):
1635 assert require_writable
1637 if self.remote_server is not None:
1638 return self.remote_server
1640 self.remote_server = "__unknown__remote_server__.%s" % domain
1641 assert self.local_server is not None
1643 remote_creds = credopts.get_credentials(self.local_lp)
1644 remote_server = credopts.ipaddress
1645 remote_binding_options = ""
1647 # TODO: we should also support NT4 domains
1648 # we could use local_netlogon.netr_DsRGetDCNameEx2() with the remote domain name
1649 # and delegate NBT or CLDAP to the local netlogon server
1651 remote_net = Net(remote_creds, self.local_lp, server=remote_server)
1652 remote_flags = nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS
1653 if require_writable:
1654 remote_flags |= nbt.NBT_SERVER_WRITABLE
1656 remote_flags |= nbt.NBT_SERVER_PDC
1657 remote_info = remote_net.finddc(flags=remote_flags, domain=domain, address=remote_server)
1659 raise CommandError("Failed to find a writeable DC for domain '%s'" % domain)
1661 nbt.NBT_SERVER_PDC: "PDC",
1662 nbt.NBT_SERVER_GC: "GC",
1663 nbt.NBT_SERVER_LDAP: "LDAP",
1664 nbt.NBT_SERVER_DS: "DS",
1665 nbt.NBT_SERVER_KDC: "KDC",
1666 nbt.NBT_SERVER_TIMESERV: "TIMESERV",
1667 nbt.NBT_SERVER_CLOSEST: "CLOSEST",
1668 nbt.NBT_SERVER_WRITABLE: "WRITABLE",
1669 nbt.NBT_SERVER_GOOD_TIMESERV: "GOOD_TIMESERV",
1670 nbt.NBT_SERVER_NDNC: "NDNC",
1671 nbt.NBT_SERVER_SELECT_SECRET_DOMAIN_6: "SELECT_SECRET_DOMAIN_6",
1672 nbt.NBT_SERVER_FULL_SECRET_DOMAIN_6: "FULL_SECRET_DOMAIN_6",
1673 nbt.NBT_SERVER_ADS_WEB_SERVICE: "ADS_WEB_SERVICE",
1674 nbt.NBT_SERVER_DS_8: "DS_8",
1675 nbt.NBT_SERVER_HAS_DNS_NAME: "HAS_DNS_NAME",
1676 nbt.NBT_SERVER_IS_DEFAULT_NC: "IS_DEFAULT_NC",
1677 nbt.NBT_SERVER_FOREST_ROOT: "FOREST_ROOT",
1679 server_type_string = self.generic_bitmap_to_string(flag_map,
1680 remote_info.server_type, names_only=True)
1681 self.outf.write("RemoteDC Netbios[%s] DNS[%s] ServerType[%s]\n" % (
1682 remote_info.pdc_name,
1683 remote_info.pdc_dns_name,
1684 server_type_string))
1686 self.remote_server = remote_info.pdc_dns_name
1687 self.remote_binding_string="ncacn_np:%s[%s]" % (self.remote_server, remote_binding_options)
1688 self.remote_creds = remote_creds
1689 return self.remote_server
1691 def new_remote_lsa_connection(self):
1692 return lsa.lsarpc(self.remote_binding_string, self.local_lp, self.remote_creds)
1694 def new_remote_netlogon_connection(self):
1695 return netlogon.netlogon(self.remote_binding_string, self.local_lp, self.remote_creds)
1697 def get_lsa_info(self, conn, policy_access):
1698 objectAttr = lsa.ObjectAttribute()
1699 objectAttr.sec_qos = lsa.QosInfo()
1701 policy = conn.OpenPolicy2(''.decode('utf-8'),
1702 objectAttr, policy_access)
1704 info = conn.QueryInfoPolicy2(policy, lsa.LSA_POLICY_INFO_DNS)
1706 return (policy, info)
1708 def get_netlogon_dc_info(self, conn, server):
1709 info = conn.netr_DsRGetDCNameEx2(server,
1710 None, 0, None, None, None,
1711 netlogon.DS_RETURN_DNS_NAME)
1714 def netr_DomainTrust_to_name(self, t):
1715 if t.trust_type == lsa.LSA_TRUST_TYPE_DOWNLEVEL:
1716 return t.netbios_name
1720 def netr_DomainTrust_to_type(self, a, t):
1722 primary_parent = None
1724 if _t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
1726 if not _t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1727 primary_parent = a[_t.parent_index]
1730 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1731 if t is primary_parent:
1734 if t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1737 parent = a[t.parent_index]
1738 if parent is primary:
1743 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1748 def netr_DomainTrust_to_transitive(self, t):
1749 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1752 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE:
1755 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1760 def netr_DomainTrust_to_direction(self, t):
1761 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND and \
1762 t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1765 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND:
1768 if t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1773 def generic_enum_to_string(self, e_dict, v, names_only=False):
1777 v32 = self._uint32(v)
1778 w = "__unknown__%08X__" % v32
1780 r = "0x%x (%s)" % (v, w)
1783 def generic_bitmap_to_string(self, b_dict, v, names_only=False):
1788 for b in sorted(b_dict.keys()):
1795 c32 = self._uint32(c)
1796 s += ["__unknown_%08X__" % c32]
1801 r = "0x%x (%s)" % (v, w)
1804 def trustType_string(self, v):
1806 lsa.LSA_TRUST_TYPE_DOWNLEVEL : "DOWNLEVEL",
1807 lsa.LSA_TRUST_TYPE_UPLEVEL : "UPLEVEL",
1808 lsa.LSA_TRUST_TYPE_MIT : "MIT",
1809 lsa.LSA_TRUST_TYPE_DCE : "DCE",
1811 return self.generic_enum_to_string(types, v)
1813 def trustDirection_string(self, v):
1815 lsa.LSA_TRUST_DIRECTION_INBOUND |
1816 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "BOTH",
1817 lsa.LSA_TRUST_DIRECTION_INBOUND : "INBOUND",
1818 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "OUTBOUND",
1820 return self.generic_enum_to_string(directions, v)
1822 def trustAttributes_string(self, v):
1824 lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE : "NON_TRANSITIVE",
1825 lsa.LSA_TRUST_ATTRIBUTE_UPLEVEL_ONLY : "UPLEVEL_ONLY",
1826 lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN : "QUARANTINED_DOMAIN",
1827 lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE : "FOREST_TRANSITIVE",
1828 lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION : "CROSS_ORGANIZATION",
1829 lsa.LSA_TRUST_ATTRIBUTE_WITHIN_FOREST : "WITHIN_FOREST",
1830 lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL : "TREAT_AS_EXTERNAL",
1831 lsa.LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION : "USES_RC4_ENCRYPTION",
1833 return self.generic_bitmap_to_string(attributes, v)
1835 def kerb_EncTypes_string(self, v):
1837 security.KERB_ENCTYPE_DES_CBC_CRC : "DES_CBC_CRC",
1838 security.KERB_ENCTYPE_DES_CBC_MD5 : "DES_CBC_MD5",
1839 security.KERB_ENCTYPE_RC4_HMAC_MD5 : "RC4_HMAC_MD5",
1840 security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96 : "AES128_CTS_HMAC_SHA1_96",
1841 security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96 : "AES256_CTS_HMAC_SHA1_96",
1842 security.KERB_ENCTYPE_FAST_SUPPORTED : "FAST_SUPPORTED",
1843 security.KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED : "COMPOUND_IDENTITY_SUPPORTED",
1844 security.KERB_ENCTYPE_CLAIMS_SUPPORTED : "CLAIMS_SUPPORTED",
1845 security.KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED : "RESOURCE_SID_COMPRESSION_DISABLED",
1847 return self.generic_bitmap_to_string(enctypes, v)
1849 def entry_tln_status(self, e_flags, ):
1851 return "Status[Enabled]"
1854 lsa.LSA_TLN_DISABLED_NEW : "Disabled-New",
1855 lsa.LSA_TLN_DISABLED_ADMIN : "Disabled",
1856 lsa.LSA_TLN_DISABLED_CONFLICT : "Disabled-Conflicting",
1858 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
1860 def entry_dom_status(self, e_flags):
1862 return "Status[Enabled]"
1865 lsa.LSA_SID_DISABLED_ADMIN : "Disabled-SID",
1866 lsa.LSA_SID_DISABLED_CONFLICT : "Disabled-SID-Conflicting",
1867 lsa.LSA_NB_DISABLED_ADMIN : "Disabled-NB",
1868 lsa.LSA_NB_DISABLED_CONFLICT : "Disabled-NB-Conflicting",
1870 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
1872 def write_forest_trust_info(self, fti, tln=None, collisions=None):
1874 tln_string = " TDO[%s]" % tln
1878 self.outf.write("Namespaces[%d]%s:\n" % (
1879 len(fti.entries), tln_string))
1881 for i in xrange(0, len(fti.entries)):
1885 collision_string = ""
1887 if collisions is not None:
1888 for c in collisions.entries:
1892 collision_string = " Collision[%s]" % (c.name.string)
1894 d = e.forest_trust_data
1895 if e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
1896 self.outf.write("TLN: %-32s DNS[*.%s]%s\n" % (
1897 self.entry_tln_status(flags),
1898 d.string, collision_string))
1899 elif e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
1900 self.outf.write("TLN_EX: %-29s DNS[*.%s]\n" % (
1902 elif e.type == lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
1903 self.outf.write("DOM: %-32s DNS[%s] Netbios[%s] SID[%s]%s\n" % (
1904 self.entry_dom_status(flags),
1905 d.dns_domain_name.string,
1906 d.netbios_domain_name.string,
1907 d.domain_sid, collision_string))
1910 class cmd_domain_trust_list(DomainTrustCommand):
1911 """List domain trusts."""
1913 synopsis = "%prog [options]"
1915 takes_optiongroups = {
1916 "sambaopts": options.SambaOptions,
1917 "versionopts": options.VersionOptions,
1918 "localdcopts": LocalDCCredentialsOptions,
1924 def run(self, sambaopts=None, versionopts=None, localdcopts=None):
1926 local_server = self.setup_local_server(sambaopts, localdcopts)
1928 local_netlogon = self.new_local_netlogon_connection()
1929 except RuntimeError as error:
1930 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
1933 local_netlogon_trusts = local_netlogon.netr_DsrEnumerateDomainTrusts(local_server,
1934 netlogon.NETR_TRUST_FLAG_IN_FOREST |
1935 netlogon.NETR_TRUST_FLAG_OUTBOUND |
1936 netlogon.NETR_TRUST_FLAG_INBOUND)
1937 except RuntimeError as error:
1938 if self.check_runtime_error(error, self.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
1939 # TODO: we could implement a fallback to lsa.EnumTrustDom()
1940 raise CommandError("LOCAL_DC[%s]: netr_DsrEnumerateDomainTrusts not supported." % (
1942 raise self.LocalRuntimeError(self, error, "netr_DsrEnumerateDomainTrusts failed")
1944 a = local_netlogon_trusts.array
1946 if t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
1948 self.outf.write("%-14s %-15s %-19s %s\n" % (
1949 "Type[%s]" % self.netr_DomainTrust_to_type(a, t),
1950 "Transitive[%s]" % self.netr_DomainTrust_to_transitive(t),
1951 "Direction[%s]" % self.netr_DomainTrust_to_direction(t),
1952 "Name[%s]" % self.netr_DomainTrust_to_name(t)))
1955 class cmd_domain_trust_show(DomainTrustCommand):
1956 """Show trusted domain details."""
1958 synopsis = "%prog NAME [options]"
1960 takes_optiongroups = {
1961 "sambaopts": options.SambaOptions,
1962 "versionopts": options.VersionOptions,
1963 "localdcopts": LocalDCCredentialsOptions,
1969 takes_args = ["domain"]
1971 def run(self, domain, sambaopts=None, versionopts=None, localdcopts=None):
1973 local_server = self.setup_local_server(sambaopts, localdcopts)
1975 local_lsa = self.new_local_lsa_connection()
1976 except RuntimeError as error:
1977 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
1980 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
1981 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
1982 except RuntimeError as error:
1983 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
1985 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
1986 local_lsa_info.name.string,
1987 local_lsa_info.dns_domain.string,
1988 local_lsa_info.sid))
1990 lsaString = lsa.String()
1991 lsaString.string = domain
1993 local_tdo_full = local_lsa.QueryTrustedDomainInfoByName(local_policy,
1994 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
1995 local_tdo_info = local_tdo_full.info_ex
1996 local_tdo_posix = local_tdo_full.posix_offset
1997 except RuntimeError as error:
1998 if self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
1999 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2001 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(FULL_INFO) failed")
2004 local_tdo_enctypes = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2005 lsaString, lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES)
2006 except RuntimeError as error:
2007 if self.check_runtime_error(error, self.NT_STATUS_INVALID_PARAMETER):
2009 if self.check_runtime_error(error, self.NT_STATUS_INVALID_INFO_CLASS):
2012 if error is not None:
2013 raise self.LocalRuntimeError(self, error,
2014 "QueryTrustedDomainInfoByName(SUPPORTED_ENCRYPTION_TYPES) failed")
2016 local_tdo_enctypes = lsa.TrustDomainInfoSupportedEncTypes()
2017 local_tdo_enctypes.enc_types = 0
2020 local_tdo_forest = None
2021 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2022 local_tdo_forest = local_lsa.lsaRQueryForestTrustInformation(local_policy,
2023 lsaString, lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
2024 except RuntimeError as error:
2025 if self.check_runtime_error(error, self.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
2027 if self.check_runtime_error(error, self.NT_STATUS_NOT_FOUND):
2029 if error is not None:
2030 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation failed")
2032 local_tdo_forest = lsa.ForestTrustInformation()
2033 local_tdo_forest.count = 0
2034 local_tdo_forest.entries = []
2036 self.outf.write("TrusteDomain:\n\n");
2037 self.outf.write("NetbiosName: %s\n" % local_tdo_info.netbios_name.string)
2038 if local_tdo_info.netbios_name.string != local_tdo_info.domain_name.string:
2039 self.outf.write("DnsName: %s\n" % local_tdo_info.domain_name.string)
2040 self.outf.write("SID: %s\n" % local_tdo_info.sid)
2041 self.outf.write("Type: %s\n" % self.trustType_string(local_tdo_info.trust_type))
2042 self.outf.write("Direction: %s\n" % self.trustDirection_string(local_tdo_info.trust_direction))
2043 self.outf.write("Attributes: %s\n" % self.trustAttributes_string(local_tdo_info.trust_attributes))
2044 posix_offset_u32 = ctypes.c_uint32(local_tdo_posix.posix_offset).value
2045 posix_offset_i32 = ctypes.c_int32(local_tdo_posix.posix_offset).value
2046 self.outf.write("PosixOffset: 0x%08X (%d)\n" % (posix_offset_u32, posix_offset_i32))
2047 self.outf.write("kerb_EncTypes: %s\n" % self.kerb_EncTypes_string(local_tdo_enctypes.enc_types))
2049 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2050 self.write_forest_trust_info(local_tdo_forest,
2051 tln=local_tdo_info.domain_name.string)
2055 class cmd_domain_trust_create(DomainTrustCommand):
2056 """Create a domain or forest trust."""
2058 synopsis = "%prog DOMAIN [options]"
2060 takes_optiongroups = {
2061 "sambaopts": options.SambaOptions,
2062 "versionopts": options.VersionOptions,
2063 "credopts": options.CredentialsOptions,
2064 "localdcopts": LocalDCCredentialsOptions,
2068 Option("--type", type="choice", metavar="TYPE",
2069 choices=["external", "forest"],
2070 help="The type of the trust: 'external' or 'forest'.",
2072 default="external"),
2073 Option("--direction", type="choice", metavar="DIRECTION",
2074 choices=["incoming", "outgoing", "both"],
2075 help="The trust direction: 'incoming', 'outgoing' or 'both'.",
2076 dest='trust_direction',
2078 Option("--create-location", type="choice", metavar="LOCATION",
2079 choices=["local", "both"],
2080 help="Where to create the trusted domain object: 'local' or 'both'.",
2081 dest='create_location',
2083 Option("--cross-organisation", action="store_true",
2084 help="The related domains does not belong to the same organisation.",
2085 dest='cross_organisation',
2087 Option("--quarantined", type="choice", metavar="yes|no",
2088 choices=["yes", "no", None],
2089 help="Special SID filtering rules are applied to the trust. "
2090 "With --type=external the default is yes. "
2091 "With --type=forest the default is no.",
2092 dest='quarantined_arg',
2094 Option("--not-transitive", action="store_true",
2095 help="The forest trust is not transitive.",
2096 dest='not_transitive',
2098 Option("--treat-as-external", action="store_true",
2099 help="The treat the forest trust as external.",
2100 dest='treat_as_external',
2102 Option("--no-aes-keys", action="store_false",
2103 help="The trust uses aes kerberos keys.",
2104 dest='use_aes_keys',
2106 Option("--skip-validation", action="store_false",
2107 help="Skip validation of the trust.",
2112 takes_args = ["domain"]
2114 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2115 trust_type=None, trust_direction=None, create_location=None,
2116 cross_organisation=False, quarantined_arg=None,
2117 not_transitive=False, treat_as_external=False,
2118 use_aes_keys=False, validate=True):
2120 lsaString = lsa.String()
2123 if quarantined_arg is None:
2124 if trust_type == 'external':
2126 elif quarantined_arg == 'yes':
2129 if trust_type != 'forest':
2131 raise CommandError("--not-transitive requires --type=forest")
2132 if treat_as_external:
2133 raise CommandError("--treat-as-external requires --type=forest")
2137 enc_types = lsa.TrustDomainInfoSupportedEncTypes()
2138 enc_types.enc_types = security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96
2139 enc_types.enc_types |= security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96
2141 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2142 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2143 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2145 local_trust_info = lsa.TrustDomainInfoInfoEx()
2146 local_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2147 local_trust_info.trust_direction = 0
2148 if trust_direction == "both":
2149 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2150 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2151 elif trust_direction == "incoming":
2152 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2153 elif trust_direction == "outgoing":
2154 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2155 local_trust_info.trust_attributes = 0
2156 if cross_organisation:
2157 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2159 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2160 if trust_type == "forest":
2161 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2163 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2164 if treat_as_external:
2165 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2167 def get_password(name):
2170 if password is not None and password is not '':
2172 password = getpass("New %s Password: " % name)
2173 passwordverify = getpass("Retype %s Password: " % name)
2174 if not password == passwordverify:
2176 self.outf.write("Sorry, passwords do not match.\n")
2178 def string_to_array(string):
2179 blob = [0] * len(string)
2181 for i in range(len(string)):
2182 blob[i] = ord(string[i])
2186 incoming_secret = None
2187 outgoing_secret = None
2188 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2189 if create_location == "local":
2190 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2191 incoming_password = get_password("Incoming Trust")
2192 incoming_secret = string_to_array(incoming_password.encode('utf-16-le'))
2193 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2194 outgoing_password = get_password("Outgoing Trust")
2195 outgoing_secret = string_to_array(outgoing_password.encode('utf-16-le'))
2197 remote_trust_info = None
2199 # We use 240 random bytes.
2200 # Windows uses 28 or 240 random bytes. I guess it's
2201 # based on the trust type external vs. forest.
2203 # The initial trust password can be up to 512 bytes
2204 # while the versioned passwords used for periodic updates
2205 # can only be up to 498 bytes, as netr_ServerPasswordSet2()
2206 # needs to pass the NL_PASSWORD_VERSION structure within the
2207 # 512 bytes and a 2 bytes confounder is required.
2209 def random_trust_secret(length, use_aes_keys=True):
2210 secret = [0] * length
2212 pw1 = samba.generate_random_password(length/2, length/2)
2213 if not use_aes_keys:
2214 # With arcfour-hmac-md5 we have to use valid utf16
2215 # in order to generate the correct pre-auth key
2216 # based on a utf8 password.
2218 # We can remove this once our client libraries
2219 # support using the correct NTHASH.
2220 return string_to_array(pw1.encode('utf-16-le'))
2222 # We mix characters from generate_random_password
2223 # with random numbers from random.randint()
2224 for i in range(len(secret)):
2226 secret[i] = ord(pw1[i])
2228 secret[i] = random.randint(0, 255)
2232 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2233 incoming_secret = random_trust_secret(240, use_aes_keys=use_aes_keys)
2234 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2235 outgoing_secret = random_trust_secret(240, use_aes_keys=use_aes_keys)
2237 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2238 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2240 remote_trust_info = lsa.TrustDomainInfoInfoEx()
2241 remote_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2242 remote_trust_info.trust_direction = 0
2243 if trust_direction == "both":
2244 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2245 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2246 elif trust_direction == "incoming":
2247 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2248 elif trust_direction == "outgoing":
2249 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2250 remote_trust_info.trust_attributes = 0
2251 if cross_organisation:
2252 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2254 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2255 if trust_type == "forest":
2256 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2258 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2259 if treat_as_external:
2260 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2262 local_server = self.setup_local_server(sambaopts, localdcopts)
2264 local_lsa = self.new_local_lsa_connection()
2265 except RuntimeError as error:
2266 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2269 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2270 except RuntimeError as error:
2271 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2273 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2274 local_lsa_info.name.string,
2275 local_lsa_info.dns_domain.string,
2276 local_lsa_info.sid))
2279 remote_server = self.setup_remote_server(credopts, domain)
2280 except RuntimeError as error:
2281 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2284 remote_lsa = self.new_remote_lsa_connection()
2285 except RuntimeError as error:
2286 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2289 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2290 except RuntimeError as error:
2291 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2293 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2294 remote_lsa_info.name.string,
2295 remote_lsa_info.dns_domain.string,
2296 remote_lsa_info.sid))
2298 local_trust_info.domain_name.string = remote_lsa_info.dns_domain.string
2299 local_trust_info.netbios_name.string = remote_lsa_info.name.string
2300 local_trust_info.sid = remote_lsa_info.sid
2302 if remote_trust_info:
2303 remote_trust_info.domain_name.string = local_lsa_info.dns_domain.string
2304 remote_trust_info.netbios_name.string = local_lsa_info.name.string
2305 remote_trust_info.sid = local_lsa_info.sid
2308 lsaString.string = local_trust_info.domain_name.string
2309 local_old_netbios = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2310 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2311 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2312 except RuntimeError as error:
2313 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2314 raise self.LocalRuntimeError(self, error,
2315 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2319 lsaString.string = local_trust_info.netbios_name.string
2320 local_old_dns = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2321 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2322 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2323 except RuntimeError as error:
2324 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2325 raise self.LocalRuntimeError(self, error,
2326 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2329 if remote_trust_info:
2331 lsaString.string = remote_trust_info.domain_name.string
2332 remote_old_netbios = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2333 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2334 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2335 except RuntimeError as error:
2336 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2337 raise self.RemoteRuntimeError(self, error,
2338 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2342 lsaString.string = remote_trust_info.netbios_name.string
2343 remote_old_dns = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2344 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2345 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2346 except RuntimeError as error:
2347 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2348 raise self.RemoteRuntimeError(self, error,
2349 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2353 local_netlogon = self.new_local_netlogon_connection()
2354 except RuntimeError as error:
2355 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2358 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
2359 except RuntimeError as error:
2360 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
2362 if remote_trust_info:
2364 remote_netlogon = self.new_remote_netlogon_connection()
2365 except RuntimeError as error:
2366 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2369 remote_netlogon_info = self.get_netlogon_dc_info(remote_netlogon, remote_server)
2370 except RuntimeError as error:
2371 raise self.RemoteRuntimeError(self, error, "failed to get netlogon dc info")
2373 def arcfour_encrypt(key, data):
2374 from Crypto.Cipher import ARC4
2376 return c.encrypt(data)
2378 def generate_AuthInOutBlob(secret, update_time):
2380 blob = drsblobs.trustAuthInOutBlob()
2385 clear = drsblobs.AuthInfoClear()
2386 clear.size = len(secret)
2387 clear.password = secret
2389 info = drsblobs.AuthenticationInformation()
2390 info.LastUpdateTime = samba.unix2nttime(update_time)
2391 info.AuthType = lsa.TRUST_AUTH_TYPE_CLEAR
2392 info.AuthInfo = clear
2394 array = drsblobs.AuthenticationInformationArray()
2396 array.array = [info]
2398 blob = drsblobs.trustAuthInOutBlob()
2400 blob.current = array
2404 def generate_AuthInfoInternal(session_key, incoming=None, outgoing=None):
2405 confounder = [0] * 512
2406 for i in range(len(confounder)):
2407 confounder[i] = random.randint(0, 255)
2409 trustpass = drsblobs.trustDomainPasswords()
2411 trustpass.confounder = confounder
2412 trustpass.outgoing = outgoing
2413 trustpass.incoming = incoming
2415 trustpass_blob = ndr_pack(trustpass)
2417 encrypted_trustpass = arcfour_encrypt(session_key, trustpass_blob)
2419 auth_blob = lsa.DATA_BUF2()
2420 auth_blob.size = len(encrypted_trustpass)
2421 auth_blob.data = string_to_array(encrypted_trustpass)
2423 auth_info = lsa.TrustDomainInfoAuthInfoInternal()
2424 auth_info.auth_blob = auth_blob
2428 update_time = samba.current_unix_time()
2429 incoming_blob = generate_AuthInOutBlob(incoming_secret, update_time)
2430 outgoing_blob = generate_AuthInOutBlob(outgoing_secret, update_time)
2432 local_tdo_handle = None
2433 remote_tdo_handle = None
2435 local_auth_info = generate_AuthInfoInternal(local_lsa.session_key,
2436 incoming=incoming_blob,
2437 outgoing=outgoing_blob)
2438 if remote_trust_info:
2439 remote_auth_info = generate_AuthInfoInternal(remote_lsa.session_key,
2440 incoming=outgoing_blob,
2441 outgoing=incoming_blob)
2444 if remote_trust_info:
2445 self.outf.write("Creating remote TDO.\n")
2446 current_request = { "location": "remote", "name": "CreateTrustedDomainEx2"}
2447 remote_tdo_handle = remote_lsa.CreateTrustedDomainEx2(remote_policy,
2450 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2451 self.outf.write("Remote TDO created.\n")
2453 self.outf.write("Setting supported encryption types on remote TDO.\n")
2454 current_request = { "location": "remote", "name": "SetInformationTrustedDomain"}
2455 remote_lsa.SetInformationTrustedDomain(remote_tdo_handle,
2456 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2459 self.outf.write("Creating local TDO.\n")
2460 current_request = { "location": "local", "name": "CreateTrustedDomainEx2"}
2461 local_tdo_handle = local_lsa.CreateTrustedDomainEx2(local_policy,
2464 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2465 self.outf.write("Local TDO created\n")
2467 self.outf.write("Setting supported encryption types on local TDO.\n")
2468 current_request = { "location": "local", "name": "SetInformationTrustedDomain"}
2469 local_lsa.SetInformationTrustedDomain(local_tdo_handle,
2470 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2472 except RuntimeError as error:
2473 self.outf.write("Error: %s failed %sly - cleaning up\n" % (
2474 current_request['name'], current_request['location']))
2475 if remote_tdo_handle:
2476 self.outf.write("Deleting remote TDO.\n")
2477 remote_lsa.DeleteObject(remote_tdo_handle)
2478 remote_tdo_handle = None
2479 if local_tdo_handle:
2480 self.outf.write("Deleting local TDO.\n")
2481 local_lsa.DeleteObject(local_tdo_handle)
2482 local_tdo_handle = None
2483 if current_request['location'] is "remote":
2484 raise self.RemoteRuntimeError(self, error, "%s" % (
2485 current_request['name']))
2486 raise self.LocalRuntimeError(self, error, "%s" % (
2487 current_request['name']))
2490 if local_trust_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2491 self.outf.write("Setup local forest trust information...\n")
2493 # get all information about the remote trust
2494 # this triggers netr_GetForestTrustInformation to the remote domain
2495 # and lsaRSetForestTrustInformation() locally, but new top level
2496 # names are disabled by default.
2497 local_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
2498 remote_lsa_info.dns_domain.string,
2499 netlogon.DS_GFTI_UPDATE_TDO)
2500 except RuntimeError as error:
2501 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2504 # here we try to enable all top level names
2505 local_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
2506 remote_lsa_info.dns_domain,
2507 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2510 except RuntimeError as error:
2511 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2513 self.write_forest_trust_info(local_forest_info,
2514 tln=remote_lsa_info.dns_domain.string,
2515 collisions=local_forest_collision)
2517 if remote_trust_info:
2518 self.outf.write("Setup remote forest trust information...\n")
2520 # get all information about the local trust (from the perspective of the remote domain)
2521 # this triggers netr_GetForestTrustInformation to our domain.
2522 # and lsaRSetForestTrustInformation() remotely, but new top level
2523 # names are disabled by default.
2524 remote_forest_info = remote_netlogon.netr_DsRGetForestTrustInformation(remote_netlogon_info.dc_unc,
2525 local_lsa_info.dns_domain.string,
2526 netlogon.DS_GFTI_UPDATE_TDO)
2527 except RuntimeError as error:
2528 raise self.RemoteRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2531 # here we try to enable all top level names
2532 remote_forest_collision = remote_lsa.lsaRSetForestTrustInformation(remote_policy,
2533 local_lsa_info.dns_domain,
2534 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2537 except RuntimeError as error:
2538 raise self.RemoteRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2540 self.write_forest_trust_info(remote_forest_info,
2541 tln=local_lsa_info.dns_domain.string,
2542 collisions=remote_forest_collision)
2544 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2545 self.outf.write("Validating outgoing trust...\n")
2547 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_netlogon_info.dc_unc,
2548 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2550 remote_lsa_info.dns_domain.string)
2551 except RuntimeError as error:
2552 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2554 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2555 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2557 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2558 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2559 local_trust_verify.trusted_dc_name,
2560 local_trust_verify.tc_connection_status[1],
2561 local_trust_verify.pdc_connection_status[1])
2563 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2564 local_trust_verify.trusted_dc_name,
2565 local_trust_verify.tc_connection_status[1],
2566 local_trust_verify.pdc_connection_status[1])
2568 if local_trust_status != self.WERR_OK or local_conn_status != self.WERR_OK:
2569 raise CommandError(local_validation)
2571 self.outf.write("OK: %s\n" % local_validation)
2573 if remote_trust_info:
2574 if remote_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2575 self.outf.write("Validating incoming trust...\n")
2577 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_netlogon_info.dc_unc,
2578 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2580 local_lsa_info.dns_domain.string)
2581 except RuntimeError as error:
2582 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2584 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2585 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2587 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2588 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2589 remote_trust_verify.trusted_dc_name,
2590 remote_trust_verify.tc_connection_status[1],
2591 remote_trust_verify.pdc_connection_status[1])
2593 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2594 remote_trust_verify.trusted_dc_name,
2595 remote_trust_verify.tc_connection_status[1],
2596 remote_trust_verify.pdc_connection_status[1])
2598 if remote_trust_status != self.WERR_OK or remote_conn_status != self.WERR_OK:
2599 raise CommandError(remote_validation)
2601 self.outf.write("OK: %s\n" % remote_validation)
2603 if remote_tdo_handle is not None:
2605 remote_lsa.Close(remote_tdo_handle)
2606 except RuntimeError as error:
2608 remote_tdo_handle = None
2609 if local_tdo_handle is not None:
2611 local_lsa.Close(local_tdo_handle)
2612 except RuntimeError as error:
2614 local_tdo_handle = None
2616 self.outf.write("Success.\n")
2619 class cmd_domain_trust_delete(DomainTrustCommand):
2620 """Delete a domain trust."""
2622 synopsis = "%prog DOMAIN [options]"
2624 takes_optiongroups = {
2625 "sambaopts": options.SambaOptions,
2626 "versionopts": options.VersionOptions,
2627 "credopts": options.CredentialsOptions,
2628 "localdcopts": LocalDCCredentialsOptions,
2632 Option("--delete-location", type="choice", metavar="LOCATION",
2633 choices=["local", "both"],
2634 help="Where to delete the trusted domain object: 'local' or 'both'.",
2635 dest='delete_location',
2639 takes_args = ["domain"]
2641 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2642 delete_location=None):
2644 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2645 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2646 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2648 if delete_location == "local":
2649 remote_policy_access = None
2651 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2652 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2653 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2655 local_server = self.setup_local_server(sambaopts, localdcopts)
2657 local_lsa = self.new_local_lsa_connection()
2658 except RuntimeError as error:
2659 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2662 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2663 except RuntimeError as error:
2664 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2666 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2667 local_lsa_info.name.string,
2668 local_lsa_info.dns_domain.string,
2669 local_lsa_info.sid))
2671 local_tdo_info = None
2672 local_tdo_handle = None
2673 remote_tdo_info = None
2674 remote_tdo_handle = None
2676 lsaString = lsa.String()
2678 lsaString.string = domain
2679 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2680 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2681 except RuntimeError as error:
2682 if self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2683 raise CommandError("Failed to find trust for domain '%s'" % domain)
2684 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2687 if remote_policy_access is not None:
2689 remote_server = self.setup_remote_server(credopts, domain)
2690 except RuntimeError as error:
2691 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2694 remote_lsa = self.new_remote_lsa_connection()
2695 except RuntimeError as error:
2696 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2699 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2700 except RuntimeError as error:
2701 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2703 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2704 remote_lsa_info.name.string,
2705 remote_lsa_info.dns_domain.string,
2706 remote_lsa_info.sid))
2708 if remote_lsa_info.sid != local_tdo_info.sid or \
2709 remote_lsa_info.name.string != local_tdo_info.netbios_name.string or \
2710 remote_lsa_info.dns_domain.string != local_tdo_info.domain_name.string:
2711 raise CommandError("LocalTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2712 local_tdo_info.netbios_name.string,
2713 local_tdo_info.domain_name.string,
2714 local_tdo_info.sid))
2717 lsaString.string = local_lsa_info.dns_domain.string
2718 remote_tdo_info = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2719 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2720 except RuntimeError as error:
2721 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2722 raise self.RemoteRuntimeError(self, error, "QueryTrustedDomainInfoByName(%s)" % (
2726 if remote_tdo_info is not None:
2727 if local_lsa_info.sid != remote_tdo_info.sid or \
2728 local_lsa_info.name.string != remote_tdo_info.netbios_name.string or \
2729 local_lsa_info.dns_domain.string != remote_tdo_info.domain_name.string:
2730 raise CommandError("RemoteTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2731 remote_tdo_info.netbios_name.string,
2732 remote_tdo_info.domain_name.string,
2733 remote_tdo_info.sid))
2735 if local_tdo_info is not None:
2737 lsaString.string = local_tdo_info.domain_name.string
2738 local_tdo_handle = local_lsa.OpenTrustedDomainByName(local_policy,
2740 security.SEC_STD_DELETE)
2741 except RuntimeError as error:
2742 raise self.LocalRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2745 local_lsa.DeleteObject(local_tdo_handle)
2746 local_tdo_handle = None
2748 if remote_tdo_info is not None:
2750 lsaString.string = remote_tdo_info.domain_name.string
2751 remote_tdo_handle = remote_lsa.OpenTrustedDomainByName(remote_policy,
2753 security.SEC_STD_DELETE)
2754 except RuntimeError as error:
2755 raise self.RemoteRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2758 if remote_tdo_handle is not None:
2760 remote_lsa.DeleteObject(remote_tdo_handle)
2761 remote_tdo_handle = None
2762 self.outf.write("RemoteTDO deleted.\n")
2763 except RuntimeError as error:
2764 self.outf.write("%s\n" % self.RemoteRuntimeError(self, error, "DeleteObject() failed"))
2766 if local_tdo_handle is not None:
2768 local_lsa.DeleteObject(local_tdo_handle)
2769 local_tdo_handle = None
2770 self.outf.write("LocalTDO deleted.\n")
2771 except RuntimeError as error:
2772 self.outf.write("%s\n" % self.LocalRuntimeError(self, error, "DeleteObject() failed"))
2776 class cmd_domain_trust_validate(DomainTrustCommand):
2777 """Validate a domain trust."""
2779 synopsis = "%prog DOMAIN [options]"
2781 takes_optiongroups = {
2782 "sambaopts": options.SambaOptions,
2783 "versionopts": options.VersionOptions,
2784 "credopts": options.CredentialsOptions,
2785 "localdcopts": LocalDCCredentialsOptions,
2789 Option("--validate-location", type="choice", metavar="LOCATION",
2790 choices=["local", "both"],
2791 help="Where to validate the trusted domain object: 'local' or 'both'.",
2792 dest='validate_location',
2796 takes_args = ["domain"]
2798 def run(self, domain, sambaopts=None, versionopts=None, credopts=None, localdcopts=None,
2799 validate_location=None):
2801 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2803 local_server = self.setup_local_server(sambaopts, localdcopts)
2805 local_lsa = self.new_local_lsa_connection()
2806 except RuntimeError as error:
2807 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2810 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2811 except RuntimeError as error:
2812 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2814 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2815 local_lsa_info.name.string,
2816 local_lsa_info.dns_domain.string,
2817 local_lsa_info.sid))
2820 lsaString = lsa.String()
2821 lsaString.string = domain
2822 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2823 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2824 except RuntimeError as error:
2825 if self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2826 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2828 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
2830 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
2831 local_tdo_info.netbios_name.string,
2832 local_tdo_info.domain_name.string,
2833 local_tdo_info.sid))
2836 local_netlogon = self.new_local_netlogon_connection()
2837 except RuntimeError as error:
2838 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2841 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_server,
2842 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2844 local_tdo_info.domain_name.string)
2845 except RuntimeError as error:
2846 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2848 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2849 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2851 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2852 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2853 local_trust_verify.trusted_dc_name,
2854 local_trust_verify.tc_connection_status[1],
2855 local_trust_verify.pdc_connection_status[1])
2857 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2858 local_trust_verify.trusted_dc_name,
2859 local_trust_verify.tc_connection_status[1],
2860 local_trust_verify.pdc_connection_status[1])
2862 if local_trust_status != self.WERR_OK or local_conn_status != self.WERR_OK:
2863 raise CommandError(local_validation)
2865 self.outf.write("OK: %s\n" % local_validation)
2868 server = local_trust_verify.trusted_dc_name.replace('\\', '')
2869 domain_and_server = "%s\\%s" % (local_tdo_info.domain_name.string, server)
2870 local_trust_rediscover = local_netlogon.netr_LogonControl2Ex(local_server,
2871 netlogon.NETLOGON_CONTROL_REDISCOVER,
2874 except RuntimeError as error:
2875 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
2877 local_conn_status = self._uint32(local_trust_rediscover.tc_connection_status[0])
2878 local_rediscover = "LocalRediscover: DC[%s] CONNECTION[%s]" % (
2879 local_trust_rediscover.trusted_dc_name,
2880 local_trust_rediscover.tc_connection_status[1])
2882 if local_conn_status != self.WERR_OK:
2883 raise CommandError(local_rediscover)
2885 self.outf.write("OK: %s\n" % local_rediscover)
2887 if validate_location != "local":
2889 remote_server = self.setup_remote_server(credopts, domain, require_pdc=False)
2890 except RuntimeError as error:
2891 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2894 remote_netlogon = self.new_remote_netlogon_connection()
2895 except RuntimeError as error:
2896 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2899 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_server,
2900 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2902 local_lsa_info.dns_domain.string)
2903 except RuntimeError as error:
2904 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2906 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2907 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2909 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2910 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2911 remote_trust_verify.trusted_dc_name,
2912 remote_trust_verify.tc_connection_status[1],
2913 remote_trust_verify.pdc_connection_status[1])
2915 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2916 remote_trust_verify.trusted_dc_name,
2917 remote_trust_verify.tc_connection_status[1],
2918 remote_trust_verify.pdc_connection_status[1])
2920 if remote_trust_status != self.WERR_OK or remote_conn_status != self.WERR_OK:
2921 raise CommandError(remote_validation)
2923 self.outf.write("OK: %s\n" % remote_validation)
2926 server = remote_trust_verify.trusted_dc_name.replace('\\', '')
2927 domain_and_server = "%s\\%s" % (local_lsa_info.dns_domain.string, server)
2928 remote_trust_rediscover = remote_netlogon.netr_LogonControl2Ex(remote_server,
2929 netlogon.NETLOGON_CONTROL_REDISCOVER,
2932 except RuntimeError as error:
2933 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
2935 remote_conn_status = self._uint32(remote_trust_rediscover.tc_connection_status[0])
2937 remote_rediscover = "RemoteRediscover: DC[%s] CONNECTION[%s]" % (
2938 remote_trust_rediscover.trusted_dc_name,
2939 remote_trust_rediscover.tc_connection_status[1])
2941 if remote_conn_status != self.WERR_OK:
2942 raise CommandError(remote_rediscover)
2944 self.outf.write("OK: %s\n" % remote_rediscover)
2948 class cmd_domain_trust_namespaces(DomainTrustCommand):
2949 """Manage forest trust namespaces."""
2951 synopsis = "%prog [DOMAIN] [options]"
2953 takes_optiongroups = {
2954 "sambaopts": options.SambaOptions,
2955 "versionopts": options.VersionOptions,
2956 "localdcopts": LocalDCCredentialsOptions,
2960 Option("--refresh", type="choice", metavar="check|store",
2961 choices=["check", "store", None],
2962 help="List and maybe store refreshed forest trust information: 'check' or 'store'.",
2965 Option("--enable-all", action="store_true",
2966 help="Try to update disabled entries, not allowed with --refresh=check.",
2969 Option("--enable-tln", action="append", metavar='DNSDOMAIN',
2970 help="Enable a top level name entry. Can be specified multiple times.",
2973 Option("--disable-tln", action="append", metavar='DNSDOMAIN',
2974 help="Disable a top level name entry. Can be specified multiple times.",
2977 Option("--add-tln-ex", action="append", metavar='DNSDOMAIN',
2978 help="Add a top level exclusion entry. Can be specified multiple times.",
2981 Option("--delete-tln-ex", action="append", metavar='DNSDOMAIN',
2982 help="Delete a top level exclusion entry. Can be specified multiple times.",
2983 dest='delete_tln_ex',
2985 Option("--enable-nb", action="append", metavar='NETBIOSDOMAIN',
2986 help="Enable a netbios name in a domain entry. Can be specified multiple times.",
2989 Option("--disable-nb", action="append", metavar='NETBIOSDOMAIN',
2990 help="Disable a netbios name in a domain entry. Can be specified multiple times.",
2993 Option("--enable-sid", action="append", metavar='DOMAINSID',
2994 help="Enable a SID in a domain entry. Can be specified multiple times.",
2995 dest='enable_sid_str',
2997 Option("--disable-sid", action="append", metavar='DOMAINSID',
2998 help="Disable a SID in a domain entry. Can be specified multiple times.",
2999 dest='disable_sid_str',
3001 Option("--add-upn-suffix", action="append", metavar='DNSDOMAIN',
3002 help="Add a new uPNSuffixes attribute for the local forest. Can be specified multiple times.",
3005 Option("--delete-upn-suffix", action="append", metavar='DNSDOMAIN',
3006 help="Delete an existing uPNSuffixes attribute of the local forest. Can be specified multiple times.",
3009 Option("--add-spn-suffix", action="append", metavar='DNSDOMAIN',
3010 help="Add a new msDS-SPNSuffixes attribute for the local forest. Can be specified multiple times.",
3013 Option("--delete-spn-suffix", action="append", metavar='DNSDOMAIN',
3014 help="Delete an existing msDS-SPNSuffixes attribute of the local forest. Can be specified multiple times.",
3019 takes_args = ["domain?"]
3021 def run(self, domain=None, sambaopts=None, localdcopts=None, versionopts=None,
3022 refresh=None, enable_all=False,
3023 enable_tln=[], disable_tln=[], add_tln_ex=[], delete_tln_ex=[],
3024 enable_sid_str=[], disable_sid_str=[], enable_nb=[], disable_nb=[],
3025 add_upn=[], delete_upn=[], add_spn=[], delete_spn=[]):
3027 require_update = False
3030 if refresh == "store":
3031 raise CommandError("--refresh=%s not allowed without DOMAIN" % refresh)
3034 raise CommandError("--enable-all not allowed without DOMAIN")
3036 if len(enable_tln) > 0:
3037 raise CommandError("--enable-tln not allowed without DOMAIN")
3038 if len(disable_tln) > 0:
3039 raise CommandError("--disable-tln not allowed without DOMAIN")
3041 if len(add_tln_ex) > 0:
3042 raise CommandError("--add-tln-ex not allowed without DOMAIN")
3043 if len(delete_tln_ex) > 0:
3044 raise CommandError("--delete-tln-ex not allowed without DOMAIN")
3046 if len(enable_nb) > 0:
3047 raise CommandError("--enable-nb not allowed without DOMAIN")
3048 if len(disable_nb) > 0:
3049 raise CommandError("--disable-nb not allowed without DOMAIN")
3051 if len(enable_sid_str) > 0:
3052 raise CommandError("--enable-sid not allowed without DOMAIN")
3053 if len(disable_sid_str) > 0:
3054 raise CommandError("--disable-sid not allowed without DOMAIN")
3056 if len(add_upn) > 0:
3058 if not n.startswith("*."):
3060 raise CommandError("value[%s] specified for --add-upn-suffix should not include with '*.'" % n)
3061 require_update = True
3062 if len(delete_upn) > 0:
3063 for n in delete_upn:
3064 if not n.startswith("*."):
3066 raise CommandError("value[%s] specified for --delete-upn-suffix should not include with '*.'" % n)
3067 require_update = True
3069 for d in delete_upn:
3070 if a.lower() != d.lower():
3072 raise CommandError("value[%s] specified for --add-upn-suffix and --delete-upn-suffix" % a)
3074 if len(add_spn) > 0:
3076 if not n.startswith("*."):
3078 raise CommandError("value[%s] specified for --add-spn-suffix should not include with '*.'" % n)
3079 require_update = True
3080 if len(delete_spn) > 0:
3081 for n in delete_spn:
3082 if not n.startswith("*."):
3084 raise CommandError("value[%s] specified for --delete-spn-suffix should not include with '*.'" % n)
3085 require_update = True
3087 for d in delete_spn:
3088 if a.lower() != d.lower():
3090 raise CommandError("value[%s] specified for --add-spn-suffix and --delete-spn-suffix" % a)
3092 if len(add_upn) > 0:
3093 raise CommandError("--add-upn-suffix not allowed together with DOMAIN")
3094 if len(delete_upn) > 0:
3095 raise CommandError("--delete-upn-suffix not allowed together with DOMAIN")
3096 if len(add_spn) > 0:
3097 raise CommandError("--add-spn-suffix not allowed together with DOMAIN")
3098 if len(delete_spn) > 0:
3099 raise CommandError("--delete-spn-suffix not allowed together with DOMAIN")
3101 if refresh is not None:
3102 if refresh == "store":
3103 require_update = True
3105 if enable_all and refresh != "store":
3106 raise CommandError("--enable-all not allowed together with --refresh=%s" % refresh)
3108 if len(enable_tln) > 0:
3109 raise CommandError("--enable-tln not allowed together with --refresh")
3110 if len(disable_tln) > 0:
3111 raise CommandError("--disable-tln not allowed together with --refresh")
3113 if len(add_tln_ex) > 0:
3114 raise CommandError("--add-tln-ex not allowed together with --refresh")
3115 if len(delete_tln_ex) > 0:
3116 raise CommandError("--delete-tln-ex not allowed together with --refresh")
3118 if len(enable_nb) > 0:
3119 raise CommandError("--enable-nb not allowed together with --refresh")
3120 if len(disable_nb) > 0:
3121 raise CommandError("--disable-nb not allowed together with --refresh")
3123 if len(enable_sid_str) > 0:
3124 raise CommandError("--enable-sid not allowed together with --refresh")
3125 if len(disable_sid_str) > 0:
3126 raise CommandError("--disable-sid not allowed together with --refresh")
3129 require_update = True
3131 if len(enable_tln) > 0:
3132 raise CommandError("--enable-tln not allowed together with --enable-all")
3134 if len(enable_nb) > 0:
3135 raise CommandError("--enable-nb not allowed together with --enable-all")
3137 if len(enable_sid) > 0:
3138 raise CommandError("--enable-sid not allowed together with --enable-all")
3140 if len(enable_tln) > 0:
3141 require_update = True
3142 if len(disable_tln) > 0:
3143 require_update = True
3144 for e in enable_tln:
3145 for d in disable_tln:
3146 if e.lower() != d.lower():
3148 raise CommandError("value[%s] specified for --enable-tln and --disable-tln" % e)
3150 if len(add_tln_ex) > 0:
3151 for n in add_tln_ex:
3152 if not n.startswith("*."):
3154 raise CommandError("value[%s] specified for --add-tln-ex should not include with '*.'" % n)
3155 require_update = True
3156 if len(delete_tln_ex) > 0:
3157 for n in delete_tln_ex:
3158 if not n.startswith("*."):
3160 raise CommandError("value[%s] specified for --delete-tln-ex should not include with '*.'" % n)
3161 require_update = True
3162 for a in add_tln_ex:
3163 for d in delete_tln_ex:
3164 if a.lower() != d.lower():
3166 raise CommandError("value[%s] specified for --add-tln-ex and --delete-tln-ex" % a)
3168 if len(enable_nb) > 0:
3169 require_update = True
3170 if len(disable_nb) > 0:
3171 require_update = True
3173 for d in disable_nb:
3174 if e.upper() != d.upper():
3176 raise CommandError("value[%s] specified for --enable-nb and --disable-nb" % e)
3179 for s in enable_sid_str:
3181 sid = security.dom_sid(s)
3182 except TypeError as error:
3183 raise CommandError("value[%s] specified for --enable-sid is not a valid SID" % s)
3184 enable_sid.append(sid)
3186 for s in disable_sid_str:
3188 sid = security.dom_sid(s)
3189 except TypeError as error:
3190 raise CommandError("value[%s] specified for --disable-sid is not a valid SID" % s)
3191 disable_sid.append(sid)
3192 if len(enable_sid) > 0:
3193 require_update = True
3194 if len(disable_sid) > 0:
3195 require_update = True
3196 for e in enable_sid:
3197 for d in disable_sid:
3200 raise CommandError("value[%s] specified for --enable-sid and --disable-sid" % e)
3202 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
3204 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
3206 local_server = self.setup_local_server(sambaopts, localdcopts)
3208 local_lsa = self.new_local_lsa_connection()
3209 except RuntimeError as error:
3210 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
3213 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
3214 except RuntimeError as error:
3215 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
3217 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
3218 local_lsa_info.name.string,
3219 local_lsa_info.dns_domain.string,
3220 local_lsa_info.sid))
3224 local_netlogon = self.new_local_netlogon_connection()
3225 except RuntimeError as error:
3226 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3229 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3230 except RuntimeError as error:
3231 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3233 if local_netlogon_info.domain_name != local_netlogon_info.forest_name:
3234 raise CommandError("The local domain [%s] is not the forest root [%s]" % (
3235 local_netlogon_info.domain_name,
3236 local_netlogon_info.forest_name))
3239 # get all information about our own forest
3240 own_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3242 except RuntimeError as error:
3243 if self.check_runtime_error(error, self.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
3244 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3247 if self.check_runtime_error(error, self.WERR_INVALID_FUNCTION):
3248 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3251 if self.check_runtime_error(error, self.WERR_NERR_ACFNOTLOADED):
3252 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3255 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3257 self.outf.write("Own forest trust information...\n")
3258 self.write_forest_trust_info(own_forest_info,
3259 tln=local_lsa_info.dns_domain.string)
3262 local_samdb = self.new_local_ldap_connection()
3263 except RuntimeError as error:
3264 raise self.LocalRuntimeError(self, error, "failed to connect to SamDB")
3266 local_partitions_dn = "CN=Partitions,%s" % str(local_samdb.get_config_basedn())
3267 attrs = ['uPNSuffixes', 'msDS-SPNSuffixes']
3269 msgs = local_samdb.search(base=local_partitions_dn,
3270 scope=ldb.SCOPE_BASE,
3271 expression="(objectClass=crossRefContainer)",
3273 stored_msg = msgs[0]
3274 except ldb.LdbError as error:
3275 raise self.LocalLdbError(self, error, "failed to search partition dn")
3277 stored_upn_vals = []
3278 if 'uPNSuffixes' in stored_msg:
3279 stored_upn_vals.extend(stored_msg['uPNSuffixes'])
3281 stored_spn_vals = []
3282 if 'msDS-SPNSuffixes' in stored_msg:
3283 stored_spn_vals.extend(stored_msg['msDS-SPNSuffixes'])
3285 self.outf.write("Stored uPNSuffixes attributes[%d]:\n" % len(stored_upn_vals))
3286 for v in stored_upn_vals:
3287 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3288 self.outf.write("Stored msDS-SPNSuffixes attributes[%d]:\n" % len(stored_spn_vals))
3289 for v in stored_spn_vals:
3290 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3292 if not require_update:
3296 update_upn_vals = []
3297 update_upn_vals.extend(stored_upn_vals)
3300 update_spn_vals = []
3301 update_spn_vals.extend(stored_spn_vals)
3305 for i in xrange(0, len(update_upn_vals)):
3306 v = update_upn_vals[i]
3307 if v.lower() != upn.lower():
3312 raise CommandError("Entry already present for value[%s] specified for --add-upn-suffix" % upn)
3313 update_upn_vals.append(upn)
3316 for upn in delete_upn:
3318 for i in xrange(0, len(update_upn_vals)):
3319 v = update_upn_vals[i]
3320 if v.lower() != upn.lower():
3325 raise CommandError("Entry not found for value[%s] specified for --delete-upn-suffix" % upn)
3327 update_upn_vals.pop(idx)
3332 for i in xrange(0, len(update_spn_vals)):
3333 v = update_spn_vals[i]
3334 if v.lower() != spn.lower():
3339 raise CommandError("Entry already present for value[%s] specified for --add-spn-suffix" % spn)
3340 update_spn_vals.append(spn)
3343 for spn in delete_spn:
3345 for i in xrange(0, len(update_spn_vals)):
3346 v = update_spn_vals[i]
3347 if v.lower() != spn.lower():
3352 raise CommandError("Entry not found for value[%s] specified for --delete-spn-suffix" % spn)
3354 update_spn_vals.pop(idx)
3357 self.outf.write("Update uPNSuffixes attributes[%d]:\n" % len(update_upn_vals))
3358 for v in update_upn_vals:
3359 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3360 self.outf.write("Update msDS-SPNSuffixes attributes[%d]:\n" % len(update_spn_vals))
3361 for v in update_spn_vals:
3362 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3364 update_msg = ldb.Message()
3365 update_msg.dn = stored_msg.dn
3368 update_msg['uPNSuffixes'] = ldb.MessageElement(update_upn_vals,
3369 ldb.FLAG_MOD_REPLACE,
3372 update_msg['msDS-SPNSuffixes'] = ldb.MessageElement(update_spn_vals,
3373 ldb.FLAG_MOD_REPLACE,
3376 local_samdb.modify(update_msg)
3377 except ldb.LdbError as error:
3378 raise self.LocalLdbError(self, error, "failed to update partition dn")
3381 stored_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3383 except RuntimeError as error:
3384 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3386 self.outf.write("Stored forest trust information...\n")
3387 self.write_forest_trust_info(stored_forest_info,
3388 tln=local_lsa_info.dns_domain.string)
3392 lsaString = lsa.String()
3393 lsaString.string = domain
3394 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
3395 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
3396 except RuntimeError as error:
3397 if self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
3398 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
3400 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
3402 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
3403 local_tdo_info.netbios_name.string,
3404 local_tdo_info.domain_name.string,
3405 local_tdo_info.sid))
3407 if not local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
3408 raise CommandError("trusted domain object for domain [%s] is not marked as FOREST_TRANSITIVE." % domain)
3410 if refresh is not None:
3412 local_netlogon = self.new_local_netlogon_connection()
3413 except RuntimeError as error:
3414 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3417 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3418 except RuntimeError as error:
3419 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3421 lsa_update_check = 1
3422 if refresh == "store":
3423 netlogon_update_tdo = netlogon.DS_GFTI_UPDATE_TDO
3425 lsa_update_check = 0
3427 netlogon_update_tdo = 0
3430 # get all information about the remote trust
3431 # this triggers netr_GetForestTrustInformation to the remote domain
3432 # and lsaRSetForestTrustInformation() locally, but new top level
3433 # names are disabled by default.
3434 fresh_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3435 local_tdo_info.domain_name.string,
3436 netlogon_update_tdo)
3437 except RuntimeError as error:
3438 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3441 fresh_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3442 local_tdo_info.domain_name,
3443 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3446 except RuntimeError as error:
3447 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3449 self.outf.write("Fresh forest trust information...\n")
3450 self.write_forest_trust_info(fresh_forest_info,
3451 tln=local_tdo_info.domain_name.string,
3452 collisions=fresh_forest_collision)
3454 if refresh == "store":
3456 lsaString = lsa.String()
3457 lsaString.string = local_tdo_info.domain_name.string
3458 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3460 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3461 except RuntimeError as error:
3462 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3464 self.outf.write("Stored forest trust information...\n")
3465 self.write_forest_trust_info(stored_forest_info,
3466 tln=local_tdo_info.domain_name.string)
3471 # The none --refresh path
3475 lsaString = lsa.String()
3476 lsaString.string = local_tdo_info.domain_name.string
3477 local_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3479 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3480 except RuntimeError as error:
3481 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3483 self.outf.write("Local forest trust information...\n")
3484 self.write_forest_trust_info(local_forest_info,
3485 tln=local_tdo_info.domain_name.string)
3487 if not require_update:
3491 entries.extend(local_forest_info.entries)
3492 update_forest_info = lsa.ForestTrustInformation()
3493 update_forest_info.count = len(entries)
3494 update_forest_info.entries = entries
3497 for i in xrange(0, len(update_forest_info.entries)):
3498 r = update_forest_info.entries[i]
3499 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3501 if update_forest_info.entries[i].flags == 0:
3503 update_forest_info.entries[i].time = 0
3504 update_forest_info.entries[i].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3505 for i in xrange(0, len(update_forest_info.entries)):
3506 r = update_forest_info.entries[i]
3507 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3509 if update_forest_info.entries[i].flags == 0:
3511 update_forest_info.entries[i].time = 0
3512 update_forest_info.entries[i].flags &= ~lsa.LSA_NB_DISABLED_MASK
3513 update_forest_info.entries[i].flags &= ~lsa.LSA_SID_DISABLED_MASK
3515 for tln in enable_tln:
3517 for i in xrange(0, len(update_forest_info.entries)):
3518 r = update_forest_info.entries[i]
3519 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3521 if r.forest_trust_data.string.lower() != tln.lower():
3526 raise CommandError("Entry not found for value[%s] specified for --enable-tln" % tln)
3527 if not update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_MASK:
3528 raise CommandError("Entry found for value[%s] specified for --enable-tln is already enabled" % tln)
3529 update_forest_info.entries[idx].time = 0
3530 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3532 for tln in disable_tln:
3534 for i in xrange(0, len(update_forest_info.entries)):
3535 r = update_forest_info.entries[i]
3536 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3538 if r.forest_trust_data.string.lower() != tln.lower():
3543 raise CommandError("Entry not found for value[%s] specified for --disable-tln" % tln)
3544 if update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_ADMIN:
3545 raise CommandError("Entry found for value[%s] specified for --disable-tln is already disabled" % tln)
3546 update_forest_info.entries[idx].time = 0
3547 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3548 update_forest_info.entries[idx].flags |= lsa.LSA_TLN_DISABLED_ADMIN
3550 for tln_ex in add_tln_ex:
3552 for i in xrange(0, len(update_forest_info.entries)):
3553 r = update_forest_info.entries[i]
3554 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3556 if r.forest_trust_data.string.lower() != tln_ex.lower():
3561 raise CommandError("Entry already present for value[%s] specified for --add-tln-ex" % tln_ex)
3563 tln_dot = ".%s" % tln_ex.lower()
3565 for i in xrange(0, len(update_forest_info.entries)):
3566 r = update_forest_info.entries[i]
3567 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3569 r_dot = ".%s" % r.forest_trust_data.string.lower()
3570 if tln_dot == r_dot:
3571 raise CommandError("TLN entry present for value[%s] specified for --add-tln-ex" % tln_ex)
3572 if not tln_dot.endswith(r_dot):
3578 raise CommandError("No TLN parent present for value[%s] specified for --add-tln-ex" % tln_ex)
3580 r = lsa.ForestTrustRecord()
3581 r.type = lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX
3584 r.forest_trust_data.string = tln_ex
3587 entries.extend(update_forest_info.entries)
3588 entries.insert(idx + 1, r)
3589 update_forest_info.count = len(entries)
3590 update_forest_info.entries = entries
3592 for tln_ex in delete_tln_ex:
3594 for i in xrange(0, len(update_forest_info.entries)):
3595 r = update_forest_info.entries[i]
3596 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3598 if r.forest_trust_data.string.lower() != tln_ex.lower():
3603 raise CommandError("Entry not found for value[%s] specified for --delete-tln-ex" % tln_ex)
3606 entries.extend(update_forest_info.entries)
3608 update_forest_info.count = len(entries)
3609 update_forest_info.entries = entries
3611 for nb in enable_nb:
3613 for i in xrange(0, len(update_forest_info.entries)):
3614 r = update_forest_info.entries[i]
3615 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3617 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3622 raise CommandError("Entry not found for value[%s] specified for --enable-nb" % nb)
3623 if not update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_MASK:
3624 raise CommandError("Entry found for value[%s] specified for --enable-nb is already enabled" % nb)
3625 update_forest_info.entries[idx].time = 0
3626 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3628 for nb in disable_nb:
3630 for i in xrange(0, len(update_forest_info.entries)):
3631 r = update_forest_info.entries[i]
3632 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3634 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3639 raise CommandError("Entry not found for value[%s] specified for --delete-nb" % nb)
3640 if update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_ADMIN:
3641 raise CommandError("Entry found for value[%s] specified for --disable-nb is already disabled" % nb)
3642 update_forest_info.entries[idx].time = 0
3643 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3644 update_forest_info.entries[idx].flags |= lsa.LSA_NB_DISABLED_ADMIN
3646 for sid in enable_sid:
3648 for i in xrange(0, len(update_forest_info.entries)):
3649 r = update_forest_info.entries[i]
3650 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3652 if r.forest_trust_data.domain_sid != sid:
3657 raise CommandError("Entry not found for value[%s] specified for --enable-sid" % sid)
3658 if not update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_MASK:
3659 raise CommandError("Entry found for value[%s] specified for --enable-sid is already enabled" % nb)
3660 update_forest_info.entries[idx].time = 0
3661 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3663 for sid in disable_sid:
3665 for i in xrange(0, len(update_forest_info.entries)):
3666 r = update_forest_info.entries[i]
3667 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3669 if r.forest_trust_data.domain_sid != sid:
3674 raise CommandError("Entry not found for value[%s] specified for --delete-sid" % sid)
3675 if update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_ADMIN:
3676 raise CommandError("Entry found for value[%s] specified for --disable-sid is already disabled" % nb)
3677 update_forest_info.entries[idx].time = 0
3678 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3679 update_forest_info.entries[idx].flags |= lsa.LSA_SID_DISABLED_ADMIN
3682 update_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3683 local_tdo_info.domain_name,
3684 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3685 update_forest_info, 0)
3686 except RuntimeError as error:
3687 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3689 self.outf.write("Updated forest trust information...\n")
3690 self.write_forest_trust_info(update_forest_info,
3691 tln=local_tdo_info.domain_name.string,
3692 collisions=update_forest_collision)
3695 lsaString = lsa.String()
3696 lsaString.string = local_tdo_info.domain_name.string
3697 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3699 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3700 except RuntimeError as error:
3701 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3703 self.outf.write("Stored forest trust information...\n")
3704 self.write_forest_trust_info(stored_forest_info,
3705 tln=local_tdo_info.domain_name.string)
3708 class cmd_domain_trust(SuperCommand):
3709 """Domain and forest trust management."""
3712 subcommands["list"] = cmd_domain_trust_list()
3713 subcommands["show"] = cmd_domain_trust_show()
3714 subcommands["create"] = cmd_domain_trust_create()
3715 subcommands["delete"] = cmd_domain_trust_delete()
3716 subcommands["validate"] = cmd_domain_trust_validate()
3717 subcommands["namespaces"] = cmd_domain_trust_namespaces()
3719 class cmd_domain(SuperCommand):
3720 """Domain management."""
3723 subcommands["demote"] = cmd_domain_demote()
3724 if cmd_domain_export_keytab is not None:
3725 subcommands["exportkeytab"] = cmd_domain_export_keytab()
3726 subcommands["info"] = cmd_domain_info()
3727 subcommands["provision"] = cmd_domain_provision()
3728 subcommands["join"] = cmd_domain_join()
3729 subcommands["dcpromo"] = cmd_domain_dcpromo()
3730 subcommands["level"] = cmd_domain_level()
3731 subcommands["passwordsettings"] = cmd_domain_passwordsettings()
3732 subcommands["classicupgrade"] = cmd_domain_classicupgrade()
3733 subcommands["samba3upgrade"] = cmd_domain_samba3upgrade()
3734 subcommands["trust"] = cmd_domain_trust()