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)
698 remove_dc.remove_dc(samdb, remove_other_dead_server)
699 except remove_dc.DemoteException as err:
700 raise CommandError("Demote failed: %s" % err)
703 netbios_name = lp.get("netbios name")
704 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
706 res = samdb.search(expression='(&(objectClass=computer)(serverReferenceBL=*))', attrs=["dnsHostName", "name"])
708 raise CommandError("Unable to search for servers")
711 raise CommandError("You are the latest server in the domain")
715 if str(e["name"]).lower() != netbios_name.lower():
716 server = e["dnsHostName"]
719 ntds_guid = samdb.get_ntds_GUID()
720 msg = samdb.search(base=str(samdb.get_config_basedn()),
721 scope=ldb.SCOPE_SUBTREE, expression="(objectGUID=%s)" % ntds_guid,
723 if len(msg) == 0 or "options" not in msg[0]:
724 raise CommandError("Failed to find options on %s" % ntds_guid)
727 dsa_options = int(str(msg[0]['options']))
729 res = samdb.search(expression="(fSMORoleOwner=%s)" % str(ntds_dn),
730 controls=["search_options:1:2"])
733 raise CommandError("Current DC is still the owner of %d role(s), use the role command to transfer roles to another DC" % len(res))
735 self.errf.write("Using %s as partner server for the demotion\n" %
737 (drsuapiBind, drsuapi_handle, supportedExtensions) = drsuapi_connect(server, lp, creds)
739 self.errf.write("Deactivating inbound replication\n")
741 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
745 dsa_options |= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
746 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
750 self.errf.write("Asking partner server %s to synchronize from us\n"
752 for part in (samdb.get_schema_basedn(),
753 samdb.get_config_basedn(),
754 samdb.get_root_basedn()):
755 nc = drsuapi.DsReplicaObjectIdentifier()
758 req1 = drsuapi.DsReplicaSyncRequest1()
759 req1.naming_context = nc;
760 req1.options = drsuapi.DRSUAPI_DRS_WRIT_REP
761 req1.source_dsa_guid = misc.GUID(ntds_guid)
764 drsuapiBind.DsReplicaSync(drsuapi_handle, 1, req1)
765 except RuntimeError as (werr, string):
766 if werr == 8452: #WERR_DS_DRA_NO_REPLICA
770 "Error while demoting, "
771 "re-enabling inbound replication\n")
772 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
773 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
775 raise CommandError("Error while sending a DsReplicaSync for partion %s" % str(part), e)
777 remote_samdb = SamDB(url="ldap://%s" % server,
778 session_info=system_session(),
779 credentials=creds, lp=lp)
781 self.errf.write("Changing userControl and container\n")
782 res = remote_samdb.search(base=str(remote_samdb.domain_dn()),
783 expression="(&(objectClass=user)(sAMAccountName=%s$))" %
784 netbios_name.upper(),
785 attrs=["userAccountControl"])
787 uac = int(str(res[0]["userAccountControl"]))
791 "Error while demoting, re-enabling inbound replication\n")
792 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
793 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
795 raise CommandError("Error while changing account control", e)
799 "Error while demoting, re-enabling inbound replication")
800 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
801 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
803 raise CommandError("Unable to find object with samaccountName = %s$"
804 " in the remote dc" % netbios_name.upper())
808 uac &= ~(UF_SERVER_TRUST_ACCOUNT|UF_TRUSTED_FOR_DELEGATION|UF_PARTIAL_SECRETS_ACCOUNT)
809 uac |= UF_WORKSTATION_TRUST_ACCOUNT
814 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
815 ldb.FLAG_MOD_REPLACE,
816 "userAccountControl")
818 remote_samdb.modify(msg)
821 "Error while demoting, re-enabling inbound replication")
822 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
823 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
826 raise CommandError("Error while changing account control", e)
828 parent = msg.dn.parent()
829 rdn = "%s=%s" % (res[0].dn.get_rdn_name(), res[0].dn.get_rdn_value())
830 # Let's move to the Computer container
834 computer_dn = ldb.Dn(remote_samdb, "CN=Computers,%s" % str(remote_samdb.domain_dn()))
835 res = remote_samdb.search(base=computer_dn, expression=rdn, scope=ldb.SCOPE_ONELEVEL)
838 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
839 scope=ldb.SCOPE_ONELEVEL)
840 while(len(res) != 0 and i < 100):
842 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
843 scope=ldb.SCOPE_ONELEVEL)
847 "Error while demoting, re-enabling inbound replication\n")
848 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
849 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
855 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
856 ldb.FLAG_MOD_REPLACE,
857 "userAccountControl")
859 remote_samdb.modify(msg)
861 raise CommandError("Unable to find a slot for renaming %s,"
862 " all names from %s-1 to %s-%d seemed used" %
863 (str(dc_dn), rdn, rdn, i - 9))
865 newrdn = "%s-%d" % (rdn, i)
868 newdn = ldb.Dn(remote_samdb, "%s,%s" % (newrdn, str(computer_dn)))
869 remote_samdb.rename(dc_dn, newdn)
872 "Error while demoting, re-enabling inbound replication\n")
873 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
874 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
880 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
881 ldb.FLAG_MOD_REPLACE,
882 "userAccountControl")
884 remote_samdb.modify(msg)
885 raise CommandError("Error while renaming %s to %s" % (str(dc_dn), str(newdn)), e)
888 server_dsa_dn = samdb.get_serverName()
889 domain = remote_samdb.get_root_basedn()
892 req1 = drsuapi.DsRemoveDSServerRequest1()
893 req1.server_dn = str(server_dsa_dn)
894 req1.domain_dn = str(domain)
897 drsuapiBind.DsRemoveDSServer(drsuapi_handle, 1, req1)
898 except RuntimeError as (werr, string):
899 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
901 "Error while demoting, re-enabling inbound replication\n")
902 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
903 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
909 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
910 ldb.FLAG_MOD_REPLACE,
911 "userAccountControl")
912 remote_samdb.modify(msg)
913 remote_samdb.rename(newdn, dc_dn)
914 if werr == 8452: #WERR_DS_DRA_NO_REPLICA
915 raise CommandError("The DC %s is not present on (already removed from) the remote server: " % server_dsa_dn, e)
917 raise CommandError("Error while sending a removeDsServer of %s: " % server_dsa_dn, e)
919 remove_dc.remove_sysvol_references(remote_samdb, rdn)
921 # These are objects under the computer account that should be deleted
922 for s in ("CN=Enterprise,CN=NTFRS Subscriptions",
923 "CN=%s, CN=NTFRS Subscriptions" % lp.get("realm"),
924 "CN=Domain system Volumes (SYSVOL Share), CN=NTFRS Subscriptions",
925 "CN=NTFRS Subscriptions"):
927 remote_samdb.delete(ldb.Dn(remote_samdb,
928 "%s,%s" % (s, str(newdn))))
929 except ldb.LdbError, l:
932 self.errf.write("Demote successful\n")
935 class cmd_domain_level(Command):
936 """Raise domain and forest function levels."""
938 synopsis = "%prog (show|raise <options>) [options]"
940 takes_optiongroups = {
941 "sambaopts": options.SambaOptions,
942 "credopts": options.CredentialsOptions,
943 "versionopts": options.VersionOptions,
947 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
948 metavar="URL", dest="H"),
949 Option("--quiet", help="Be quiet", action="store_true"),
950 Option("--forest-level", type="choice", choices=["2003", "2008", "2008_R2"],
951 help="The forest function level (2003 | 2008 | 2008_R2)"),
952 Option("--domain-level", type="choice", choices=["2003", "2008", "2008_R2"],
953 help="The domain function level (2003 | 2008 | 2008_R2)")
956 takes_args = ["subcommand"]
958 def run(self, subcommand, H=None, forest_level=None, domain_level=None,
959 quiet=False, credopts=None, sambaopts=None, versionopts=None):
960 lp = sambaopts.get_loadparm()
961 creds = credopts.get_credentials(lp, fallback_machine=True)
963 samdb = SamDB(url=H, session_info=system_session(),
964 credentials=creds, lp=lp)
966 domain_dn = samdb.domain_dn()
968 res_forest = samdb.search("CN=Partitions,%s" % samdb.get_config_basedn(),
969 scope=ldb.SCOPE_BASE, attrs=["msDS-Behavior-Version"])
970 assert len(res_forest) == 1
972 res_domain = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
973 attrs=["msDS-Behavior-Version", "nTMixedDomain"])
974 assert len(res_domain) == 1
976 res_dc_s = samdb.search("CN=Sites,%s" % samdb.get_config_basedn(),
977 scope=ldb.SCOPE_SUBTREE, expression="(objectClass=nTDSDSA)",
978 attrs=["msDS-Behavior-Version"])
979 assert len(res_dc_s) >= 1
982 level_forest = int(res_forest[0]["msDS-Behavior-Version"][0])
983 level_domain = int(res_domain[0]["msDS-Behavior-Version"][0])
984 level_domain_mixed = int(res_domain[0]["nTMixedDomain"][0])
986 min_level_dc = int(res_dc_s[0]["msDS-Behavior-Version"][0]) # Init value
988 if int(msg["msDS-Behavior-Version"][0]) < min_level_dc:
989 min_level_dc = int(msg["msDS-Behavior-Version"][0])
991 if level_forest < 0 or level_domain < 0:
992 raise CommandError("Domain and/or forest function level(s) is/are invalid. Correct them or reprovision!")
994 raise CommandError("Lowest function level of a DC is invalid. Correct this or reprovision!")
995 if level_forest > level_domain:
996 raise CommandError("Forest function level is higher than the domain level(s). Correct this or reprovision!")
997 if level_domain > min_level_dc:
998 raise CommandError("Domain function level is higher than the lowest function level of a DC. Correct this or reprovision!")
1001 raise CommandError("Could not retrieve the actual domain, forest level and/or lowest DC function level!")
1003 if subcommand == "show":
1004 self.message("Domain and forest function level for domain '%s'" % domain_dn)
1005 if level_forest == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1006 self.message("\nATTENTION: You run SAMBA 4 on a forest function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1007 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1008 self.message("\nATTENTION: You run SAMBA 4 on a domain function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1009 if min_level_dc == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1010 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)!")
1014 if level_forest == DS_DOMAIN_FUNCTION_2000:
1016 elif level_forest == DS_DOMAIN_FUNCTION_2003_MIXED:
1017 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1018 elif level_forest == DS_DOMAIN_FUNCTION_2003:
1020 elif level_forest == DS_DOMAIN_FUNCTION_2008:
1022 elif level_forest == DS_DOMAIN_FUNCTION_2008_R2:
1025 outstr = "higher than 2008 R2"
1026 self.message("Forest function level: (Windows) " + outstr)
1028 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1029 outstr = "2000 mixed (NT4 DC support)"
1030 elif level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed == 0:
1032 elif level_domain == DS_DOMAIN_FUNCTION_2003_MIXED:
1033 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1034 elif level_domain == DS_DOMAIN_FUNCTION_2003:
1036 elif level_domain == DS_DOMAIN_FUNCTION_2008:
1038 elif level_domain == DS_DOMAIN_FUNCTION_2008_R2:
1041 outstr = "higher than 2008 R2"
1042 self.message("Domain function level: (Windows) " + outstr)
1044 if min_level_dc == DS_DOMAIN_FUNCTION_2000:
1046 elif min_level_dc == DS_DOMAIN_FUNCTION_2003:
1048 elif min_level_dc == DS_DOMAIN_FUNCTION_2008:
1050 elif min_level_dc == DS_DOMAIN_FUNCTION_2008_R2:
1053 outstr = "higher than 2008 R2"
1054 self.message("Lowest function level of a DC: (Windows) " + outstr)
1056 elif subcommand == "raise":
1059 if domain_level is not None:
1060 if domain_level == "2003":
1061 new_level_domain = DS_DOMAIN_FUNCTION_2003
1062 elif domain_level == "2008":
1063 new_level_domain = DS_DOMAIN_FUNCTION_2008
1064 elif domain_level == "2008_R2":
1065 new_level_domain = DS_DOMAIN_FUNCTION_2008_R2
1067 if new_level_domain <= level_domain and level_domain_mixed == 0:
1068 raise CommandError("Domain function level can't be smaller than or equal to the actual one!")
1070 if new_level_domain > min_level_dc:
1071 raise CommandError("Domain function level can't be higher than the lowest function level of a DC!")
1073 # Deactivate mixed/interim domain support
1074 if level_domain_mixed != 0:
1075 # Directly on the base DN
1077 m.dn = ldb.Dn(samdb, domain_dn)
1078 m["nTMixedDomain"] = ldb.MessageElement("0",
1079 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1083 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup") + ",CN=Partitions,%s" % samdb.get_config_basedn())
1084 m["nTMixedDomain"] = ldb.MessageElement("0",
1085 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1088 except ldb.LdbError, (enum, emsg):
1089 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1092 # Directly on the base DN
1094 m.dn = ldb.Dn(samdb, domain_dn)
1095 m["msDS-Behavior-Version"]= ldb.MessageElement(
1096 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1097 "msDS-Behavior-Version")
1101 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup")
1102 + ",CN=Partitions,%s" % samdb.get_config_basedn())
1103 m["msDS-Behavior-Version"]= ldb.MessageElement(
1104 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1105 "msDS-Behavior-Version")
1108 except ldb.LdbError, (enum, emsg):
1109 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1112 level_domain = new_level_domain
1113 msgs.append("Domain function level changed!")
1115 if forest_level is not None:
1116 if forest_level == "2003":
1117 new_level_forest = DS_DOMAIN_FUNCTION_2003
1118 elif forest_level == "2008":
1119 new_level_forest = DS_DOMAIN_FUNCTION_2008
1120 elif forest_level == "2008_R2":
1121 new_level_forest = DS_DOMAIN_FUNCTION_2008_R2
1122 if new_level_forest <= level_forest:
1123 raise CommandError("Forest function level can't be smaller than or equal to the actual one!")
1124 if new_level_forest > level_domain:
1125 raise CommandError("Forest function level can't be higher than the domain function level(s). Please raise it/them first!")
1127 m.dn = ldb.Dn(samdb, "CN=Partitions,%s" % samdb.get_config_basedn())
1128 m["msDS-Behavior-Version"]= ldb.MessageElement(
1129 str(new_level_forest), ldb.FLAG_MOD_REPLACE,
1130 "msDS-Behavior-Version")
1132 msgs.append("Forest function level changed!")
1133 msgs.append("All changes applied successfully!")
1134 self.message("\n".join(msgs))
1136 raise CommandError("invalid argument: '%s' (choose from 'show', 'raise')" % subcommand)
1139 class cmd_domain_passwordsettings(Command):
1140 """Set password settings.
1142 Password complexity, password lockout policy, history length,
1143 minimum password length, the minimum and maximum password age) on
1144 a Samba AD DC server.
1146 Use against a Windows DC is possible, but group policy will override it.
1149 synopsis = "%prog (show|set <options>) [options]"
1151 takes_optiongroups = {
1152 "sambaopts": options.SambaOptions,
1153 "versionopts": options.VersionOptions,
1154 "credopts": options.CredentialsOptions,
1158 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1159 metavar="URL", dest="H"),
1160 Option("--quiet", help="Be quiet", action="store_true"),
1161 Option("--complexity", type="choice", choices=["on","off","default"],
1162 help="The password complexity (on | off | default). Default is 'on'"),
1163 Option("--store-plaintext", type="choice", choices=["on","off","default"],
1164 help="Store plaintext passwords where account have 'store passwords with reversible encryption' set (on | off | default). Default is 'off'"),
1165 Option("--history-length",
1166 help="The password history length (<integer> | default). Default is 24.", type=str),
1167 Option("--min-pwd-length",
1168 help="The minimum password length (<integer> | default). Default is 7.", type=str),
1169 Option("--min-pwd-age",
1170 help="The minimum password age (<integer in days> | default). Default is 1.", type=str),
1171 Option("--max-pwd-age",
1172 help="The maximum password age (<integer in days> | default). Default is 43.", type=str),
1173 Option("--account-lockout-duration",
1174 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),
1175 Option("--account-lockout-threshold",
1176 help="The number of bad password attempts allowed before locking out the account (<integer> | default). Default is 0 (never lock out).", type=str),
1177 Option("--reset-account-lockout-after",
1178 help="After this time is elapsed, the recorded number of attempts restarts from zero (<integer> | default). Default is 30.", type=str),
1181 takes_args = ["subcommand"]
1183 def run(self, subcommand, H=None, min_pwd_age=None, max_pwd_age=None,
1184 quiet=False, complexity=None, store_plaintext=None, history_length=None,
1185 min_pwd_length=None, account_lockout_duration=None, account_lockout_threshold=None,
1186 reset_account_lockout_after=None, credopts=None, sambaopts=None,
1188 lp = sambaopts.get_loadparm()
1189 creds = credopts.get_credentials(lp)
1191 samdb = SamDB(url=H, session_info=system_session(),
1192 credentials=creds, lp=lp)
1194 domain_dn = samdb.domain_dn()
1195 res = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1196 attrs=["pwdProperties", "pwdHistoryLength", "minPwdLength",
1197 "minPwdAge", "maxPwdAge", "lockoutDuration", "lockoutThreshold",
1198 "lockOutObservationWindow"])
1199 assert(len(res) == 1)
1201 pwd_props = int(res[0]["pwdProperties"][0])
1202 pwd_hist_len = int(res[0]["pwdHistoryLength"][0])
1203 cur_min_pwd_len = int(res[0]["minPwdLength"][0])
1205 cur_min_pwd_age = int(abs(int(res[0]["minPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1206 if int(res[0]["maxPwdAge"][0]) == -0x8000000000000000:
1209 cur_max_pwd_age = int(abs(int(res[0]["maxPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1210 cur_account_lockout_threshold = int(res[0]["lockoutThreshold"][0])
1212 if int(res[0]["lockoutDuration"][0]) == -0x8000000000000000:
1213 cur_account_lockout_duration = 0
1215 cur_account_lockout_duration = abs(int(res[0]["lockoutDuration"][0])) / (1e7 * 60)
1216 cur_reset_account_lockout_after = abs(int(res[0]["lockOutObservationWindow"][0])) / (1e7 * 60)
1217 except Exception, e:
1218 raise CommandError("Could not retrieve password properties!", e)
1220 if subcommand == "show":
1221 self.message("Password informations for domain '%s'" % domain_dn)
1223 if pwd_props & DOMAIN_PASSWORD_COMPLEX != 0:
1224 self.message("Password complexity: on")
1226 self.message("Password complexity: off")
1227 if pwd_props & DOMAIN_PASSWORD_STORE_CLEARTEXT != 0:
1228 self.message("Store plaintext passwords: on")
1230 self.message("Store plaintext passwords: off")
1231 self.message("Password history length: %d" % pwd_hist_len)
1232 self.message("Minimum password length: %d" % cur_min_pwd_len)
1233 self.message("Minimum password age (days): %d" % cur_min_pwd_age)
1234 self.message("Maximum password age (days): %d" % cur_max_pwd_age)
1235 self.message("Account lockout duration (mins): %d" % cur_account_lockout_duration)
1236 self.message("Account lockout threshold (attempts): %d" % cur_account_lockout_threshold)
1237 self.message("Reset account lockout after (mins): %d" % cur_reset_account_lockout_after)
1238 elif subcommand == "set":
1241 m.dn = ldb.Dn(samdb, domain_dn)
1243 if complexity is not None:
1244 if complexity == "on" or complexity == "default":
1245 pwd_props = pwd_props | DOMAIN_PASSWORD_COMPLEX
1246 msgs.append("Password complexity activated!")
1247 elif complexity == "off":
1248 pwd_props = pwd_props & (~DOMAIN_PASSWORD_COMPLEX)
1249 msgs.append("Password complexity deactivated!")
1251 if store_plaintext is not None:
1252 if store_plaintext == "on" or store_plaintext == "default":
1253 pwd_props = pwd_props | DOMAIN_PASSWORD_STORE_CLEARTEXT
1254 msgs.append("Plaintext password storage for changed passwords activated!")
1255 elif store_plaintext == "off":
1256 pwd_props = pwd_props & (~DOMAIN_PASSWORD_STORE_CLEARTEXT)
1257 msgs.append("Plaintext password storage for changed passwords deactivated!")
1259 if complexity is not None or store_plaintext is not None:
1260 m["pwdProperties"] = ldb.MessageElement(str(pwd_props),
1261 ldb.FLAG_MOD_REPLACE, "pwdProperties")
1263 if history_length is not None:
1264 if history_length == "default":
1267 pwd_hist_len = int(history_length)
1269 if pwd_hist_len < 0 or pwd_hist_len > 24:
1270 raise CommandError("Password history length must be in the range of 0 to 24!")
1272 m["pwdHistoryLength"] = ldb.MessageElement(str(pwd_hist_len),
1273 ldb.FLAG_MOD_REPLACE, "pwdHistoryLength")
1274 msgs.append("Password history length changed!")
1276 if min_pwd_length is not None:
1277 if min_pwd_length == "default":
1280 min_pwd_len = int(min_pwd_length)
1282 if min_pwd_len < 0 or min_pwd_len > 14:
1283 raise CommandError("Minimum password length must be in the range of 0 to 14!")
1285 m["minPwdLength"] = ldb.MessageElement(str(min_pwd_len),
1286 ldb.FLAG_MOD_REPLACE, "minPwdLength")
1287 msgs.append("Minimum password length changed!")
1289 if min_pwd_age is not None:
1290 if min_pwd_age == "default":
1293 min_pwd_age = int(min_pwd_age)
1295 if min_pwd_age < 0 or min_pwd_age > 998:
1296 raise CommandError("Minimum password age must be in the range of 0 to 998!")
1299 min_pwd_age_ticks = -int(min_pwd_age * (24 * 60 * 60 * 1e7))
1301 m["minPwdAge"] = ldb.MessageElement(str(min_pwd_age_ticks),
1302 ldb.FLAG_MOD_REPLACE, "minPwdAge")
1303 msgs.append("Minimum password age changed!")
1305 if max_pwd_age is not None:
1306 if max_pwd_age == "default":
1309 max_pwd_age = int(max_pwd_age)
1311 if max_pwd_age < 0 or max_pwd_age > 999:
1312 raise CommandError("Maximum password age must be in the range of 0 to 999!")
1315 if max_pwd_age == 0:
1316 max_pwd_age_ticks = -0x8000000000000000
1318 max_pwd_age_ticks = -int(max_pwd_age * (24 * 60 * 60 * 1e7))
1320 m["maxPwdAge"] = ldb.MessageElement(str(max_pwd_age_ticks),
1321 ldb.FLAG_MOD_REPLACE, "maxPwdAge")
1322 msgs.append("Maximum password age changed!")
1324 if account_lockout_duration is not None:
1325 if account_lockout_duration == "default":
1326 account_lockout_duration = 30
1328 account_lockout_duration = int(account_lockout_duration)
1330 if account_lockout_duration < 0 or account_lockout_duration > 99999:
1331 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1334 if account_lockout_duration == 0:
1335 account_lockout_duration_ticks = -0x8000000000000000
1337 account_lockout_duration_ticks = -int(account_lockout_duration * (60 * 1e7))
1339 m["lockoutDuration"] = ldb.MessageElement(str(account_lockout_duration_ticks),
1340 ldb.FLAG_MOD_REPLACE, "lockoutDuration")
1341 msgs.append("Account lockout duration changed!")
1343 if account_lockout_threshold is not None:
1344 if account_lockout_threshold == "default":
1345 account_lockout_threshold = 0
1347 account_lockout_threshold = int(account_lockout_threshold)
1349 m["lockoutThreshold"] = ldb.MessageElement(str(account_lockout_threshold),
1350 ldb.FLAG_MOD_REPLACE, "lockoutThreshold")
1351 msgs.append("Account lockout threshold changed!")
1353 if reset_account_lockout_after is not None:
1354 if reset_account_lockout_after == "default":
1355 reset_account_lockout_after = 30
1357 reset_account_lockout_after = int(reset_account_lockout_after)
1359 if reset_account_lockout_after < 0 or reset_account_lockout_after > 99999:
1360 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1363 if reset_account_lockout_after == 0:
1364 reset_account_lockout_after_ticks = -0x8000000000000000
1366 reset_account_lockout_after_ticks = -int(reset_account_lockout_after * (60 * 1e7))
1368 m["lockOutObservationWindow"] = ldb.MessageElement(str(reset_account_lockout_after_ticks),
1369 ldb.FLAG_MOD_REPLACE, "lockOutObservationWindow")
1370 msgs.append("Duration to reset account lockout after changed!")
1372 if max_pwd_age > 0 and min_pwd_age >= max_pwd_age:
1373 raise CommandError("Maximum password age (%d) must be greater than minimum password age (%d)!" % (max_pwd_age, min_pwd_age))
1376 raise CommandError("You must specify at least one option to set. Try --help")
1378 msgs.append("All changes applied successfully!")
1379 self.message("\n".join(msgs))
1381 raise CommandError("Wrong argument '%s'!" % subcommand)
1384 class cmd_domain_classicupgrade(Command):
1385 """Upgrade from Samba classic (NT4-like) database to Samba AD DC database.
1387 Specify either a directory with all Samba classic DC databases and state files (with --dbdir) or
1388 the testparm utility from your classic installation (with --testparm).
1391 synopsis = "%prog [options] <classic_smb_conf>"
1393 takes_optiongroups = {
1394 "sambaopts": options.SambaOptions,
1395 "versionopts": options.VersionOptions
1399 Option("--dbdir", type="string", metavar="DIR",
1400 help="Path to samba classic DC database directory"),
1401 Option("--testparm", type="string", metavar="PATH",
1402 help="Path to samba classic DC testparm utility from the previous installation. This allows the default paths of the previous installation to be followed"),
1403 Option("--targetdir", type="string", metavar="DIR",
1404 help="Path prefix where the new Samba 4.0 AD domain should be initialised"),
1405 Option("--quiet", help="Be quiet", action="store_true"),
1406 Option("--verbose", help="Be verbose", action="store_true"),
1407 Option("--use-xattrs", type="choice", choices=["yes","no","auto"], metavar="[yes|no|auto]",
1408 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"),
1409 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
1410 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
1411 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
1412 "BIND9_FLATFILE uses bind9 text database to store zone information, "
1413 "BIND9_DLZ uses samba4 AD to store zone information, "
1414 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
1415 default="SAMBA_INTERNAL")
1419 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
1420 action="store_true")
1422 if samba.is_ntvfs_fileserver_built():
1423 takes_options.extend(ntvfs_options)
1425 takes_args = ["smbconf"]
1427 def run(self, smbconf=None, targetdir=None, dbdir=None, testparm=None,
1428 quiet=False, verbose=False, use_xattrs=None, sambaopts=None, versionopts=None,
1429 dns_backend=None, use_ntvfs=False):
1431 if not os.path.exists(smbconf):
1432 raise CommandError("File %s does not exist" % smbconf)
1434 if testparm and not os.path.exists(testparm):
1435 raise CommandError("Testparm utility %s does not exist" % testparm)
1437 if dbdir and not os.path.exists(dbdir):
1438 raise CommandError("Directory %s does not exist" % dbdir)
1440 if not dbdir and not testparm:
1441 raise CommandError("Please specify either dbdir or testparm")
1443 logger = self.get_logger()
1445 logger.setLevel(logging.DEBUG)
1447 logger.setLevel(logging.WARNING)
1449 logger.setLevel(logging.INFO)
1451 if dbdir and testparm:
1452 logger.warning("both dbdir and testparm specified, ignoring dbdir.")
1455 lp = sambaopts.get_loadparm()
1457 s3conf = s3param.get_context()
1460 s3conf.set("realm", sambaopts.realm)
1462 if targetdir is not None:
1463 if not os.path.isdir(targetdir):
1467 if use_xattrs == "yes":
1469 elif use_xattrs == "auto" and not s3conf.get("posix:eadb"):
1471 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
1473 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
1476 samba.ntacls.setntacl(lp, tmpfile.name,
1477 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
1480 # FIXME: Don't catch all exceptions here
1481 logger.info("You are not root or your system do not support xattr, using tdb backend for attributes. "
1482 "If you intend to use this provision in production, rerun the script as root on a system supporting xattrs.")
1486 # Set correct default values from dbdir or testparm
1489 paths["state directory"] = dbdir
1490 paths["private dir"] = dbdir
1491 paths["lock directory"] = dbdir
1492 paths["smb passwd file"] = dbdir + "/smbpasswd"
1494 paths["state directory"] = get_testparm_var(testparm, smbconf, "state directory")
1495 paths["private dir"] = get_testparm_var(testparm, smbconf, "private dir")
1496 paths["smb passwd file"] = get_testparm_var(testparm, smbconf, "smb passwd file")
1497 paths["lock directory"] = get_testparm_var(testparm, smbconf, "lock directory")
1498 # "testparm" from Samba 3 < 3.4.x is not aware of the parameter
1499 # "state directory", instead make use of "lock directory"
1500 if len(paths["state directory"]) == 0:
1501 paths["state directory"] = paths["lock directory"]
1504 s3conf.set(p, paths[p])
1506 # load smb.conf parameters
1507 logger.info("Reading smb.conf")
1508 s3conf.load(smbconf)
1509 samba3 = Samba3(smbconf, s3conf)
1511 logger.info("Provisioning")
1512 upgrade_from_samba3(samba3, logger, targetdir, session_info=system_session(),
1513 useeadb=eadb, dns_backend=dns_backend, use_ntvfs=use_ntvfs)
1516 class cmd_domain_samba3upgrade(cmd_domain_classicupgrade):
1517 __doc__ = cmd_domain_classicupgrade.__doc__
1519 # This command is present for backwards compatibility only,
1520 # and should not be shown.
1524 class LocalDCCredentialsOptions(options.CredentialsOptions):
1525 def __init__(self, parser):
1526 options.CredentialsOptions.__init__(self, parser, special_name="local-dc")
1528 class DomainTrustCommand(Command):
1529 """List domain trusts."""
1532 Command.__init__(self)
1533 self.local_lp = None
1535 self.local_server = None
1536 self.local_binding_string = None
1537 self.local_creds = None
1539 self.remote_server = None
1540 self.remote_binding_string = None
1541 self.remote_creds = None
1543 WERR_OK = 0x00000000
1544 WERR_INVALID_FUNCTION = 0x00000001
1545 WERR_NERR_ACFNOTLOADED = 0x000008B3
1547 NT_STATUS_NOT_FOUND = 0xC0000225
1548 NT_STATUS_OBJECT_NAME_NOT_FOUND = 0xC0000034
1549 NT_STATUS_INVALID_PARAMETER = 0xC000000D
1550 NT_STATUS_INVALID_INFO_CLASS = 0xC0000003
1551 NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE = 0xC002002E
1553 def _uint32(self, v):
1554 return ctypes.c_uint32(v).value
1556 def check_runtime_error(self, runtime, val):
1560 err32 = self._uint32(runtime[0])
1566 class LocalRuntimeError(CommandError):
1567 def __init__(exception_self, self, runtime, message):
1568 err32 = self._uint32(runtime[0])
1570 msg = "LOCAL_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1571 self.local_server, message, err32, errstr)
1572 CommandError.__init__(exception_self, msg)
1574 class RemoteRuntimeError(CommandError):
1575 def __init__(exception_self, self, runtime, message):
1576 err32 = self._uint32(runtime[0])
1578 msg = "REMOTE_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1579 self.remote_server, message, err32, errstr)
1580 CommandError.__init__(exception_self, msg)
1582 class LocalLdbError(CommandError):
1583 def __init__(exception_self, self, ldb_error, message):
1584 errval = ldb_error[0]
1585 errstr = ldb_error[1]
1586 msg = "LOCAL_DC[%s]: %s - ERROR(%d) - %s" % (
1587 self.local_server, message, errval, errstr)
1588 CommandError.__init__(exception_self, msg)
1590 def setup_local_server(self, sambaopts, localdcopts):
1591 if self.local_server is not None:
1592 return self.local_server
1594 lp = sambaopts.get_loadparm()
1596 local_server = localdcopts.ipaddress
1597 if local_server is None:
1598 server_role = lp.server_role()
1599 if server_role != "ROLE_ACTIVE_DIRECTORY_DC":
1600 raise CommandError("Invalid server_role %s" % (server_role))
1601 local_server = lp.get('netbios name')
1602 local_transport = "ncalrpc"
1603 local_binding_options = ""
1604 local_binding_options += ",auth_type=ncalrpc_as_system"
1605 local_ldap_url = None
1608 local_transport = "ncacn_np"
1609 local_binding_options = ""
1610 local_ldap_url = "ldap://%s" % local_server
1611 local_creds = localdcopts.get_credentials(lp)
1615 self.local_server = local_server
1616 self.local_binding_string = "%s:%s[%s]" % (local_transport, local_server, local_binding_options)
1617 self.local_ldap_url = local_ldap_url
1618 self.local_creds = local_creds
1619 return self.local_server
1621 def new_local_lsa_connection(self):
1622 return lsa.lsarpc(self.local_binding_string, self.local_lp, self.local_creds)
1624 def new_local_netlogon_connection(self):
1625 return netlogon.netlogon(self.local_binding_string, self.local_lp, self.local_creds)
1627 def new_local_ldap_connection(self):
1628 return SamDB(url=self.local_ldap_url,
1629 session_info=system_session(),
1630 credentials=self.local_creds,
1633 def setup_remote_server(self, credopts, domain,
1635 require_writable=True):
1638 assert require_writable
1640 if self.remote_server is not None:
1641 return self.remote_server
1643 self.remote_server = "__unknown__remote_server__.%s" % domain
1644 assert self.local_server is not None
1646 remote_creds = credopts.get_credentials(self.local_lp)
1647 remote_server = credopts.ipaddress
1648 remote_binding_options = ""
1650 # TODO: we should also support NT4 domains
1651 # we could use local_netlogon.netr_DsRGetDCNameEx2() with the remote domain name
1652 # and delegate NBT or CLDAP to the local netlogon server
1654 remote_net = Net(remote_creds, self.local_lp, server=remote_server)
1655 remote_flags = nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS
1656 if require_writable:
1657 remote_flags |= nbt.NBT_SERVER_WRITABLE
1659 remote_flags |= nbt.NBT_SERVER_PDC
1660 remote_info = remote_net.finddc(flags=remote_flags, domain=domain, address=remote_server)
1662 raise CommandError("Failed to find a writeable DC for domain '%s'" % domain)
1664 nbt.NBT_SERVER_PDC: "PDC",
1665 nbt.NBT_SERVER_GC: "GC",
1666 nbt.NBT_SERVER_LDAP: "LDAP",
1667 nbt.NBT_SERVER_DS: "DS",
1668 nbt.NBT_SERVER_KDC: "KDC",
1669 nbt.NBT_SERVER_TIMESERV: "TIMESERV",
1670 nbt.NBT_SERVER_CLOSEST: "CLOSEST",
1671 nbt.NBT_SERVER_WRITABLE: "WRITABLE",
1672 nbt.NBT_SERVER_GOOD_TIMESERV: "GOOD_TIMESERV",
1673 nbt.NBT_SERVER_NDNC: "NDNC",
1674 nbt.NBT_SERVER_SELECT_SECRET_DOMAIN_6: "SELECT_SECRET_DOMAIN_6",
1675 nbt.NBT_SERVER_FULL_SECRET_DOMAIN_6: "FULL_SECRET_DOMAIN_6",
1676 nbt.NBT_SERVER_ADS_WEB_SERVICE: "ADS_WEB_SERVICE",
1677 nbt.NBT_SERVER_DS_8: "DS_8",
1678 nbt.NBT_SERVER_HAS_DNS_NAME: "HAS_DNS_NAME",
1679 nbt.NBT_SERVER_IS_DEFAULT_NC: "IS_DEFAULT_NC",
1680 nbt.NBT_SERVER_FOREST_ROOT: "FOREST_ROOT",
1682 server_type_string = self.generic_bitmap_to_string(flag_map,
1683 remote_info.server_type, names_only=True)
1684 self.outf.write("RemoteDC Netbios[%s] DNS[%s] ServerType[%s]\n" % (
1685 remote_info.pdc_name,
1686 remote_info.pdc_dns_name,
1687 server_type_string))
1689 self.remote_server = remote_info.pdc_dns_name
1690 self.remote_binding_string="ncacn_np:%s[%s]" % (self.remote_server, remote_binding_options)
1691 self.remote_creds = remote_creds
1692 return self.remote_server
1694 def new_remote_lsa_connection(self):
1695 return lsa.lsarpc(self.remote_binding_string, self.local_lp, self.remote_creds)
1697 def new_remote_netlogon_connection(self):
1698 return netlogon.netlogon(self.remote_binding_string, self.local_lp, self.remote_creds)
1700 def get_lsa_info(self, conn, policy_access):
1701 objectAttr = lsa.ObjectAttribute()
1702 objectAttr.sec_qos = lsa.QosInfo()
1704 policy = conn.OpenPolicy2(''.decode('utf-8'),
1705 objectAttr, policy_access)
1707 info = conn.QueryInfoPolicy2(policy, lsa.LSA_POLICY_INFO_DNS)
1709 return (policy, info)
1711 def get_netlogon_dc_info(self, conn, server):
1712 info = conn.netr_DsRGetDCNameEx2(server,
1713 None, 0, None, None, None,
1714 netlogon.DS_RETURN_DNS_NAME)
1717 def netr_DomainTrust_to_name(self, t):
1718 if t.trust_type == lsa.LSA_TRUST_TYPE_DOWNLEVEL:
1719 return t.netbios_name
1723 def netr_DomainTrust_to_type(self, a, t):
1725 primary_parent = None
1727 if _t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
1729 if not _t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1730 primary_parent = a[_t.parent_index]
1733 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1734 if t is primary_parent:
1737 if t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1740 parent = a[t.parent_index]
1741 if parent is primary:
1746 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1751 def netr_DomainTrust_to_transitive(self, t):
1752 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1755 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE:
1758 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1763 def netr_DomainTrust_to_direction(self, t):
1764 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND and \
1765 t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1768 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND:
1771 if t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1776 def generic_enum_to_string(self, e_dict, v, names_only=False):
1780 v32 = self._uint32(v)
1781 w = "__unknown__%08X__" % v32
1783 r = "0x%x (%s)" % (v, w)
1786 def generic_bitmap_to_string(self, b_dict, v, names_only=False):
1791 for b in sorted(b_dict.keys()):
1798 c32 = self._uint32(c)
1799 s += ["__unknown_%08X__" % c32]
1804 r = "0x%x (%s)" % (v, w)
1807 def trustType_string(self, v):
1809 lsa.LSA_TRUST_TYPE_DOWNLEVEL : "DOWNLEVEL",
1810 lsa.LSA_TRUST_TYPE_UPLEVEL : "UPLEVEL",
1811 lsa.LSA_TRUST_TYPE_MIT : "MIT",
1812 lsa.LSA_TRUST_TYPE_DCE : "DCE",
1814 return self.generic_enum_to_string(types, v)
1816 def trustDirection_string(self, v):
1818 lsa.LSA_TRUST_DIRECTION_INBOUND |
1819 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "BOTH",
1820 lsa.LSA_TRUST_DIRECTION_INBOUND : "INBOUND",
1821 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "OUTBOUND",
1823 return self.generic_enum_to_string(directions, v)
1825 def trustAttributes_string(self, v):
1827 lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE : "NON_TRANSITIVE",
1828 lsa.LSA_TRUST_ATTRIBUTE_UPLEVEL_ONLY : "UPLEVEL_ONLY",
1829 lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN : "QUARANTINED_DOMAIN",
1830 lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE : "FOREST_TRANSITIVE",
1831 lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION : "CROSS_ORGANIZATION",
1832 lsa.LSA_TRUST_ATTRIBUTE_WITHIN_FOREST : "WITHIN_FOREST",
1833 lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL : "TREAT_AS_EXTERNAL",
1834 lsa.LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION : "USES_RC4_ENCRYPTION",
1836 return self.generic_bitmap_to_string(attributes, v)
1838 def kerb_EncTypes_string(self, v):
1840 security.KERB_ENCTYPE_DES_CBC_CRC : "DES_CBC_CRC",
1841 security.KERB_ENCTYPE_DES_CBC_MD5 : "DES_CBC_MD5",
1842 security.KERB_ENCTYPE_RC4_HMAC_MD5 : "RC4_HMAC_MD5",
1843 security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96 : "AES128_CTS_HMAC_SHA1_96",
1844 security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96 : "AES256_CTS_HMAC_SHA1_96",
1845 security.KERB_ENCTYPE_FAST_SUPPORTED : "FAST_SUPPORTED",
1846 security.KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED : "COMPOUND_IDENTITY_SUPPORTED",
1847 security.KERB_ENCTYPE_CLAIMS_SUPPORTED : "CLAIMS_SUPPORTED",
1848 security.KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED : "RESOURCE_SID_COMPRESSION_DISABLED",
1850 return self.generic_bitmap_to_string(enctypes, v)
1852 def entry_tln_status(self, e_flags, ):
1854 return "Status[Enabled]"
1857 lsa.LSA_TLN_DISABLED_NEW : "Disabled-New",
1858 lsa.LSA_TLN_DISABLED_ADMIN : "Disabled",
1859 lsa.LSA_TLN_DISABLED_CONFLICT : "Disabled-Conflicting",
1861 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
1863 def entry_dom_status(self, e_flags):
1865 return "Status[Enabled]"
1868 lsa.LSA_SID_DISABLED_ADMIN : "Disabled-SID",
1869 lsa.LSA_SID_DISABLED_CONFLICT : "Disabled-SID-Conflicting",
1870 lsa.LSA_NB_DISABLED_ADMIN : "Disabled-NB",
1871 lsa.LSA_NB_DISABLED_CONFLICT : "Disabled-NB-Conflicting",
1873 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
1875 def write_forest_trust_info(self, fti, tln=None, collisions=None):
1877 tln_string = " TDO[%s]" % tln
1881 self.outf.write("Namespaces[%d]%s:\n" % (
1882 len(fti.entries), tln_string))
1884 for i in xrange(0, len(fti.entries)):
1888 collision_string = ""
1890 if collisions is not None:
1891 for c in collisions.entries:
1895 collision_string = " Collision[%s]" % (c.name.string)
1897 d = e.forest_trust_data
1898 if e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
1899 self.outf.write("TLN: %-32s DNS[*.%s]%s\n" % (
1900 self.entry_tln_status(flags),
1901 d.string, collision_string))
1902 elif e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
1903 self.outf.write("TLN_EX: %-29s DNS[*.%s]\n" % (
1905 elif e.type == lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
1906 self.outf.write("DOM: %-32s DNS[%s] Netbios[%s] SID[%s]%s\n" % (
1907 self.entry_dom_status(flags),
1908 d.dns_domain_name.string,
1909 d.netbios_domain_name.string,
1910 d.domain_sid, collision_string))
1913 class cmd_domain_trust_list(DomainTrustCommand):
1914 """List domain trusts."""
1916 synopsis = "%prog [options]"
1918 takes_optiongroups = {
1919 "sambaopts": options.SambaOptions,
1920 "versionopts": options.VersionOptions,
1921 "localdcopts": LocalDCCredentialsOptions,
1927 def run(self, sambaopts=None, versionopts=None, localdcopts=None):
1929 local_server = self.setup_local_server(sambaopts, localdcopts)
1931 local_netlogon = self.new_local_netlogon_connection()
1932 except RuntimeError as error:
1933 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
1936 local_netlogon_trusts = local_netlogon.netr_DsrEnumerateDomainTrusts(local_server,
1937 netlogon.NETR_TRUST_FLAG_IN_FOREST |
1938 netlogon.NETR_TRUST_FLAG_OUTBOUND |
1939 netlogon.NETR_TRUST_FLAG_INBOUND)
1940 except RuntimeError as error:
1941 if self.check_runtime_error(error, self.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
1942 # TODO: we could implement a fallback to lsa.EnumTrustDom()
1943 raise CommandError("LOCAL_DC[%s]: netr_DsrEnumerateDomainTrusts not supported." % (
1945 raise self.LocalRuntimeError(self, error, "netr_DsrEnumerateDomainTrusts failed")
1947 a = local_netlogon_trusts.array
1949 if t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
1951 self.outf.write("%-14s %-15s %-19s %s\n" % (
1952 "Type[%s]" % self.netr_DomainTrust_to_type(a, t),
1953 "Transitive[%s]" % self.netr_DomainTrust_to_transitive(t),
1954 "Direction[%s]" % self.netr_DomainTrust_to_direction(t),
1955 "Name[%s]" % self.netr_DomainTrust_to_name(t)))
1958 class cmd_domain_trust_show(DomainTrustCommand):
1959 """Show trusted domain details."""
1961 synopsis = "%prog NAME [options]"
1963 takes_optiongroups = {
1964 "sambaopts": options.SambaOptions,
1965 "versionopts": options.VersionOptions,
1966 "localdcopts": LocalDCCredentialsOptions,
1972 takes_args = ["domain"]
1974 def run(self, domain, sambaopts=None, versionopts=None, localdcopts=None):
1976 local_server = self.setup_local_server(sambaopts, localdcopts)
1978 local_lsa = self.new_local_lsa_connection()
1979 except RuntimeError as error:
1980 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
1983 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
1984 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
1985 except RuntimeError as error:
1986 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
1988 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
1989 local_lsa_info.name.string,
1990 local_lsa_info.dns_domain.string,
1991 local_lsa_info.sid))
1993 lsaString = lsa.String()
1994 lsaString.string = domain
1996 local_tdo_full = local_lsa.QueryTrustedDomainInfoByName(local_policy,
1997 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
1998 local_tdo_info = local_tdo_full.info_ex
1999 local_tdo_posix = local_tdo_full.posix_offset
2000 except RuntimeError as error:
2001 if self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2002 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2004 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(FULL_INFO) failed")
2007 local_tdo_enctypes = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2008 lsaString, lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES)
2009 except RuntimeError as error:
2010 if self.check_runtime_error(error, self.NT_STATUS_INVALID_PARAMETER):
2012 if self.check_runtime_error(error, self.NT_STATUS_INVALID_INFO_CLASS):
2015 if error is not None:
2016 raise self.LocalRuntimeError(self, error,
2017 "QueryTrustedDomainInfoByName(SUPPORTED_ENCRYPTION_TYPES) failed")
2019 local_tdo_enctypes = lsa.TrustDomainInfoSupportedEncTypes()
2020 local_tdo_enctypes.enc_types = 0
2023 local_tdo_forest = None
2024 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2025 local_tdo_forest = local_lsa.lsaRQueryForestTrustInformation(local_policy,
2026 lsaString, lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
2027 except RuntimeError as error:
2028 if self.check_runtime_error(error, self.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
2030 if self.check_runtime_error(error, self.NT_STATUS_NOT_FOUND):
2032 if error is not None:
2033 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation failed")
2035 local_tdo_forest = lsa.ForestTrustInformation()
2036 local_tdo_forest.count = 0
2037 local_tdo_forest.entries = []
2039 self.outf.write("TrusteDomain:\n\n");
2040 self.outf.write("NetbiosName: %s\n" % local_tdo_info.netbios_name.string)
2041 if local_tdo_info.netbios_name.string != local_tdo_info.domain_name.string:
2042 self.outf.write("DnsName: %s\n" % local_tdo_info.domain_name.string)
2043 self.outf.write("SID: %s\n" % local_tdo_info.sid)
2044 self.outf.write("Type: %s\n" % self.trustType_string(local_tdo_info.trust_type))
2045 self.outf.write("Direction: %s\n" % self.trustDirection_string(local_tdo_info.trust_direction))
2046 self.outf.write("Attributes: %s\n" % self.trustAttributes_string(local_tdo_info.trust_attributes))
2047 posix_offset_u32 = ctypes.c_uint32(local_tdo_posix.posix_offset).value
2048 posix_offset_i32 = ctypes.c_int32(local_tdo_posix.posix_offset).value
2049 self.outf.write("PosixOffset: 0x%08X (%d)\n" % (posix_offset_u32, posix_offset_i32))
2050 self.outf.write("kerb_EncTypes: %s\n" % self.kerb_EncTypes_string(local_tdo_enctypes.enc_types))
2052 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2053 self.write_forest_trust_info(local_tdo_forest,
2054 tln=local_tdo_info.domain_name.string)
2058 class cmd_domain_trust_create(DomainTrustCommand):
2059 """Create a domain or forest trust."""
2061 synopsis = "%prog DOMAIN [options]"
2063 takes_optiongroups = {
2064 "sambaopts": options.SambaOptions,
2065 "versionopts": options.VersionOptions,
2066 "credopts": options.CredentialsOptions,
2067 "localdcopts": LocalDCCredentialsOptions,
2071 Option("--type", type="choice", metavar="TYPE",
2072 choices=["external", "forest"],
2073 help="The type of the trust: 'external' or 'forest'.",
2075 default="external"),
2076 Option("--direction", type="choice", metavar="DIRECTION",
2077 choices=["incoming", "outgoing", "both"],
2078 help="The trust direction: 'incoming', 'outgoing' or 'both'.",
2079 dest='trust_direction',
2081 Option("--create-location", type="choice", metavar="LOCATION",
2082 choices=["local", "both"],
2083 help="Where to create the trusted domain object: 'local' or 'both'.",
2084 dest='create_location',
2086 Option("--cross-organisation", action="store_true",
2087 help="The related domains does not belong to the same organisation.",
2088 dest='cross_organisation',
2090 Option("--quarantined", type="choice", metavar="yes|no",
2091 choices=["yes", "no", None],
2092 help="Special SID filtering rules are applied to the trust. "
2093 "With --type=external the default is yes. "
2094 "With --type=forest the default is no.",
2095 dest='quarantined_arg',
2097 Option("--not-transitive", action="store_true",
2098 help="The forest trust is not transitive.",
2099 dest='not_transitive',
2101 Option("--treat-as-external", action="store_true",
2102 help="The treat the forest trust as external.",
2103 dest='treat_as_external',
2105 Option("--no-aes-keys", action="store_false",
2106 help="The trust uses aes kerberos keys.",
2107 dest='use_aes_keys',
2109 Option("--skip-validation", action="store_false",
2110 help="Skip validation of the trust.",
2115 takes_args = ["domain"]
2117 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2118 trust_type=None, trust_direction=None, create_location=None,
2119 cross_organisation=False, quarantined_arg=None,
2120 not_transitive=False, treat_as_external=False,
2121 use_aes_keys=False, validate=True):
2123 lsaString = lsa.String()
2126 if quarantined_arg is None:
2127 if trust_type == 'external':
2129 elif quarantined_arg == 'yes':
2132 if trust_type != 'forest':
2134 raise CommandError("--not-transitive requires --type=forest")
2135 if treat_as_external:
2136 raise CommandError("--treat-as-external requires --type=forest")
2140 enc_types = lsa.TrustDomainInfoSupportedEncTypes()
2141 enc_types.enc_types = security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96
2142 enc_types.enc_types |= security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96
2144 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2145 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2146 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2148 local_trust_info = lsa.TrustDomainInfoInfoEx()
2149 local_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2150 local_trust_info.trust_direction = 0
2151 if trust_direction == "both":
2152 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2153 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2154 elif trust_direction == "incoming":
2155 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2156 elif trust_direction == "outgoing":
2157 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2158 local_trust_info.trust_attributes = 0
2159 if cross_organisation:
2160 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2162 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2163 if trust_type == "forest":
2164 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2166 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2167 if treat_as_external:
2168 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2170 def get_password(name):
2173 if password is not None and password is not '':
2175 password = getpass("New %s Password: " % name)
2176 passwordverify = getpass("Retype %s Password: " % name)
2177 if not password == passwordverify:
2179 self.outf.write("Sorry, passwords do not match.\n")
2181 def string_to_array(string):
2182 blob = [0] * len(string)
2184 for i in range(len(string)):
2185 blob[i] = ord(string[i])
2189 incoming_secret = None
2190 outgoing_secret = None
2191 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2192 if create_location == "local":
2193 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2194 incoming_password = get_password("Incoming Trust")
2195 incoming_secret = string_to_array(incoming_password.encode('utf-16-le'))
2196 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2197 outgoing_password = get_password("Outgoing Trust")
2198 outgoing_secret = string_to_array(outgoing_password.encode('utf-16-le'))
2200 remote_trust_info = None
2202 # We use 240 random bytes.
2203 # Windows uses 28 or 240 random bytes. I guess it's
2204 # based on the trust type external vs. forest.
2206 # The initial trust password can be up to 512 bytes
2207 # while the versioned passwords used for periodic updates
2208 # can only be up to 498 bytes, as netr_ServerPasswordSet2()
2209 # needs to pass the NL_PASSWORD_VERSION structure within the
2210 # 512 bytes and a 2 bytes confounder is required.
2212 def random_trust_secret(length, use_aes_keys=True):
2213 secret = [0] * length
2215 pw1 = samba.generate_random_password(length/2, length/2)
2216 if not use_aes_keys:
2217 # With arcfour-hmac-md5 we have to use valid utf16
2218 # in order to generate the correct pre-auth key
2219 # based on a utf8 password.
2221 # We can remove this once our client libraries
2222 # support using the correct NTHASH.
2223 return string_to_array(pw1.encode('utf-16-le'))
2225 # We mix characters from generate_random_password
2226 # with random numbers from random.randint()
2227 for i in range(len(secret)):
2229 secret[i] = ord(pw1[i])
2231 secret[i] = random.randint(0, 255)
2235 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2236 incoming_secret = random_trust_secret(240, use_aes_keys=use_aes_keys)
2237 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2238 outgoing_secret = random_trust_secret(240, use_aes_keys=use_aes_keys)
2240 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2241 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2243 remote_trust_info = lsa.TrustDomainInfoInfoEx()
2244 remote_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2245 remote_trust_info.trust_direction = 0
2246 if trust_direction == "both":
2247 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2248 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2249 elif trust_direction == "incoming":
2250 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2251 elif trust_direction == "outgoing":
2252 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2253 remote_trust_info.trust_attributes = 0
2254 if cross_organisation:
2255 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2257 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2258 if trust_type == "forest":
2259 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2261 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2262 if treat_as_external:
2263 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2265 local_server = self.setup_local_server(sambaopts, localdcopts)
2267 local_lsa = self.new_local_lsa_connection()
2268 except RuntimeError as error:
2269 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2272 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2273 except RuntimeError as error:
2274 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2276 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2277 local_lsa_info.name.string,
2278 local_lsa_info.dns_domain.string,
2279 local_lsa_info.sid))
2282 remote_server = self.setup_remote_server(credopts, domain)
2283 except RuntimeError as error:
2284 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2287 remote_lsa = self.new_remote_lsa_connection()
2288 except RuntimeError as error:
2289 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2292 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2293 except RuntimeError as error:
2294 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2296 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2297 remote_lsa_info.name.string,
2298 remote_lsa_info.dns_domain.string,
2299 remote_lsa_info.sid))
2301 local_trust_info.domain_name.string = remote_lsa_info.dns_domain.string
2302 local_trust_info.netbios_name.string = remote_lsa_info.name.string
2303 local_trust_info.sid = remote_lsa_info.sid
2305 if remote_trust_info:
2306 remote_trust_info.domain_name.string = local_lsa_info.dns_domain.string
2307 remote_trust_info.netbios_name.string = local_lsa_info.name.string
2308 remote_trust_info.sid = local_lsa_info.sid
2311 lsaString.string = local_trust_info.domain_name.string
2312 local_old_netbios = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2313 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2314 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2315 except RuntimeError as error:
2316 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2317 raise self.LocalRuntimeError(self, error,
2318 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2322 lsaString.string = local_trust_info.netbios_name.string
2323 local_old_dns = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2324 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2325 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2326 except RuntimeError as error:
2327 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2328 raise self.LocalRuntimeError(self, error,
2329 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2332 if remote_trust_info:
2334 lsaString.string = remote_trust_info.domain_name.string
2335 remote_old_netbios = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2336 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2337 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2338 except RuntimeError as error:
2339 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2340 raise self.RemoteRuntimeError(self, error,
2341 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2345 lsaString.string = remote_trust_info.netbios_name.string
2346 remote_old_dns = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2347 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2348 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2349 except RuntimeError as error:
2350 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2351 raise self.RemoteRuntimeError(self, error,
2352 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2356 local_netlogon = self.new_local_netlogon_connection()
2357 except RuntimeError as error:
2358 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2361 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
2362 except RuntimeError as error:
2363 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
2365 if remote_trust_info:
2367 remote_netlogon = self.new_remote_netlogon_connection()
2368 except RuntimeError as error:
2369 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2372 remote_netlogon_info = self.get_netlogon_dc_info(remote_netlogon, remote_server)
2373 except RuntimeError as error:
2374 raise self.RemoteRuntimeError(self, error, "failed to get netlogon dc info")
2376 def arcfour_encrypt(key, data):
2377 from Crypto.Cipher import ARC4
2379 return c.encrypt(data)
2381 def generate_AuthInOutBlob(secret, update_time):
2383 blob = drsblobs.trustAuthInOutBlob()
2388 clear = drsblobs.AuthInfoClear()
2389 clear.size = len(secret)
2390 clear.password = secret
2392 info = drsblobs.AuthenticationInformation()
2393 info.LastUpdateTime = samba.unix2nttime(update_time)
2394 info.AuthType = lsa.TRUST_AUTH_TYPE_CLEAR
2395 info.AuthInfo = clear
2397 array = drsblobs.AuthenticationInformationArray()
2399 array.array = [info]
2401 blob = drsblobs.trustAuthInOutBlob()
2403 blob.current = array
2407 def generate_AuthInfoInternal(session_key, incoming=None, outgoing=None):
2408 confounder = [0] * 512
2409 for i in range(len(confounder)):
2410 confounder[i] = random.randint(0, 255)
2412 trustpass = drsblobs.trustDomainPasswords()
2414 trustpass.confounder = confounder
2415 trustpass.outgoing = outgoing
2416 trustpass.incoming = incoming
2418 trustpass_blob = ndr_pack(trustpass)
2420 encrypted_trustpass = arcfour_encrypt(session_key, trustpass_blob)
2422 auth_blob = lsa.DATA_BUF2()
2423 auth_blob.size = len(encrypted_trustpass)
2424 auth_blob.data = string_to_array(encrypted_trustpass)
2426 auth_info = lsa.TrustDomainInfoAuthInfoInternal()
2427 auth_info.auth_blob = auth_blob
2431 update_time = samba.current_unix_time()
2432 incoming_blob = generate_AuthInOutBlob(incoming_secret, update_time)
2433 outgoing_blob = generate_AuthInOutBlob(outgoing_secret, update_time)
2435 local_tdo_handle = None
2436 remote_tdo_handle = None
2438 local_auth_info = generate_AuthInfoInternal(local_lsa.session_key,
2439 incoming=incoming_blob,
2440 outgoing=outgoing_blob)
2441 if remote_trust_info:
2442 remote_auth_info = generate_AuthInfoInternal(remote_lsa.session_key,
2443 incoming=outgoing_blob,
2444 outgoing=incoming_blob)
2447 if remote_trust_info:
2448 self.outf.write("Creating remote TDO.\n")
2449 current_request = { "location": "remote", "name": "CreateTrustedDomainEx2"}
2450 remote_tdo_handle = remote_lsa.CreateTrustedDomainEx2(remote_policy,
2453 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2454 self.outf.write("Remote TDO created.\n")
2456 self.outf.write("Setting supported encryption types on remote TDO.\n")
2457 current_request = { "location": "remote", "name": "SetInformationTrustedDomain"}
2458 remote_lsa.SetInformationTrustedDomain(remote_tdo_handle,
2459 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2462 self.outf.write("Creating local TDO.\n")
2463 current_request = { "location": "local", "name": "CreateTrustedDomainEx2"}
2464 local_tdo_handle = local_lsa.CreateTrustedDomainEx2(local_policy,
2467 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2468 self.outf.write("Local TDO created\n")
2470 self.outf.write("Setting supported encryption types on local TDO.\n")
2471 current_request = { "location": "local", "name": "SetInformationTrustedDomain"}
2472 local_lsa.SetInformationTrustedDomain(local_tdo_handle,
2473 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2475 except RuntimeError as error:
2476 self.outf.write("Error: %s failed %sly - cleaning up\n" % (
2477 current_request['name'], current_request['location']))
2478 if remote_tdo_handle:
2479 self.outf.write("Deleting remote TDO.\n")
2480 remote_lsa.DeleteObject(remote_tdo_handle)
2481 remote_tdo_handle = None
2482 if local_tdo_handle:
2483 self.outf.write("Deleting local TDO.\n")
2484 local_lsa.DeleteObject(local_tdo_handle)
2485 local_tdo_handle = None
2486 if current_request['location'] is "remote":
2487 raise self.RemoteRuntimeError(self, error, "%s" % (
2488 current_request['name']))
2489 raise self.LocalRuntimeError(self, error, "%s" % (
2490 current_request['name']))
2493 if local_trust_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2494 self.outf.write("Setup local forest trust information...\n")
2496 # get all information about the remote trust
2497 # this triggers netr_GetForestTrustInformation to the remote domain
2498 # and lsaRSetForestTrustInformation() locally, but new top level
2499 # names are disabled by default.
2500 local_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
2501 remote_lsa_info.dns_domain.string,
2502 netlogon.DS_GFTI_UPDATE_TDO)
2503 except RuntimeError as error:
2504 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2507 # here we try to enable all top level names
2508 local_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
2509 remote_lsa_info.dns_domain,
2510 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2513 except RuntimeError as error:
2514 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2516 self.write_forest_trust_info(local_forest_info,
2517 tln=remote_lsa_info.dns_domain.string,
2518 collisions=local_forest_collision)
2520 if remote_trust_info:
2521 self.outf.write("Setup remote forest trust information...\n")
2523 # get all information about the local trust (from the perspective of the remote domain)
2524 # this triggers netr_GetForestTrustInformation to our domain.
2525 # and lsaRSetForestTrustInformation() remotely, but new top level
2526 # names are disabled by default.
2527 remote_forest_info = remote_netlogon.netr_DsRGetForestTrustInformation(remote_netlogon_info.dc_unc,
2528 local_lsa_info.dns_domain.string,
2529 netlogon.DS_GFTI_UPDATE_TDO)
2530 except RuntimeError as error:
2531 raise self.RemoteRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2534 # here we try to enable all top level names
2535 remote_forest_collision = remote_lsa.lsaRSetForestTrustInformation(remote_policy,
2536 local_lsa_info.dns_domain,
2537 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2540 except RuntimeError as error:
2541 raise self.RemoteRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2543 self.write_forest_trust_info(remote_forest_info,
2544 tln=local_lsa_info.dns_domain.string,
2545 collisions=remote_forest_collision)
2547 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2548 self.outf.write("Validating outgoing trust...\n")
2550 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_netlogon_info.dc_unc,
2551 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2553 remote_lsa_info.dns_domain.string)
2554 except RuntimeError as error:
2555 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2557 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2558 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2560 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2561 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2562 local_trust_verify.trusted_dc_name,
2563 local_trust_verify.tc_connection_status[1],
2564 local_trust_verify.pdc_connection_status[1])
2566 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2567 local_trust_verify.trusted_dc_name,
2568 local_trust_verify.tc_connection_status[1],
2569 local_trust_verify.pdc_connection_status[1])
2571 if local_trust_status != self.WERR_OK or local_conn_status != self.WERR_OK:
2572 raise CommandError(local_validation)
2574 self.outf.write("OK: %s\n" % local_validation)
2576 if remote_trust_info:
2577 if remote_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2578 self.outf.write("Validating incoming trust...\n")
2580 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_netlogon_info.dc_unc,
2581 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2583 local_lsa_info.dns_domain.string)
2584 except RuntimeError as error:
2585 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2587 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2588 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2590 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2591 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2592 remote_trust_verify.trusted_dc_name,
2593 remote_trust_verify.tc_connection_status[1],
2594 remote_trust_verify.pdc_connection_status[1])
2596 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2597 remote_trust_verify.trusted_dc_name,
2598 remote_trust_verify.tc_connection_status[1],
2599 remote_trust_verify.pdc_connection_status[1])
2601 if remote_trust_status != self.WERR_OK or remote_conn_status != self.WERR_OK:
2602 raise CommandError(remote_validation)
2604 self.outf.write("OK: %s\n" % remote_validation)
2606 if remote_tdo_handle is not None:
2608 remote_lsa.Close(remote_tdo_handle)
2609 except RuntimeError as error:
2611 remote_tdo_handle = None
2612 if local_tdo_handle is not None:
2614 local_lsa.Close(local_tdo_handle)
2615 except RuntimeError as error:
2617 local_tdo_handle = None
2619 self.outf.write("Success.\n")
2622 class cmd_domain_trust_delete(DomainTrustCommand):
2623 """Delete a domain trust."""
2625 synopsis = "%prog DOMAIN [options]"
2627 takes_optiongroups = {
2628 "sambaopts": options.SambaOptions,
2629 "versionopts": options.VersionOptions,
2630 "credopts": options.CredentialsOptions,
2631 "localdcopts": LocalDCCredentialsOptions,
2635 Option("--delete-location", type="choice", metavar="LOCATION",
2636 choices=["local", "both"],
2637 help="Where to delete the trusted domain object: 'local' or 'both'.",
2638 dest='delete_location',
2642 takes_args = ["domain"]
2644 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2645 delete_location=None):
2647 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2648 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2649 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2651 if delete_location == "local":
2652 remote_policy_access = None
2654 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2655 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2656 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2658 local_server = self.setup_local_server(sambaopts, localdcopts)
2660 local_lsa = self.new_local_lsa_connection()
2661 except RuntimeError as error:
2662 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2665 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2666 except RuntimeError as error:
2667 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2669 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2670 local_lsa_info.name.string,
2671 local_lsa_info.dns_domain.string,
2672 local_lsa_info.sid))
2674 local_tdo_info = None
2675 local_tdo_handle = None
2676 remote_tdo_info = None
2677 remote_tdo_handle = None
2679 lsaString = lsa.String()
2681 lsaString.string = domain
2682 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2683 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2684 except RuntimeError as error:
2685 if self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2686 raise CommandError("Failed to find trust for domain '%s'" % domain)
2687 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2690 if remote_policy_access is not None:
2692 remote_server = self.setup_remote_server(credopts, domain)
2693 except RuntimeError as error:
2694 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2697 remote_lsa = self.new_remote_lsa_connection()
2698 except RuntimeError as error:
2699 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2702 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2703 except RuntimeError as error:
2704 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2706 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2707 remote_lsa_info.name.string,
2708 remote_lsa_info.dns_domain.string,
2709 remote_lsa_info.sid))
2711 if remote_lsa_info.sid != local_tdo_info.sid or \
2712 remote_lsa_info.name.string != local_tdo_info.netbios_name.string or \
2713 remote_lsa_info.dns_domain.string != local_tdo_info.domain_name.string:
2714 raise CommandError("LocalTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2715 local_tdo_info.netbios_name.string,
2716 local_tdo_info.domain_name.string,
2717 local_tdo_info.sid))
2720 lsaString.string = local_lsa_info.dns_domain.string
2721 remote_tdo_info = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2722 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2723 except RuntimeError as error:
2724 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2725 raise self.RemoteRuntimeError(self, error, "QueryTrustedDomainInfoByName(%s)" % (
2729 if remote_tdo_info is not None:
2730 if local_lsa_info.sid != remote_tdo_info.sid or \
2731 local_lsa_info.name.string != remote_tdo_info.netbios_name.string or \
2732 local_lsa_info.dns_domain.string != remote_tdo_info.domain_name.string:
2733 raise CommandError("RemoteTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2734 remote_tdo_info.netbios_name.string,
2735 remote_tdo_info.domain_name.string,
2736 remote_tdo_info.sid))
2738 if local_tdo_info is not None:
2740 lsaString.string = local_tdo_info.domain_name.string
2741 local_tdo_handle = local_lsa.OpenTrustedDomainByName(local_policy,
2743 security.SEC_STD_DELETE)
2744 except RuntimeError as error:
2745 raise self.LocalRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2748 local_lsa.DeleteObject(local_tdo_handle)
2749 local_tdo_handle = None
2751 if remote_tdo_info is not None:
2753 lsaString.string = remote_tdo_info.domain_name.string
2754 remote_tdo_handle = remote_lsa.OpenTrustedDomainByName(remote_policy,
2756 security.SEC_STD_DELETE)
2757 except RuntimeError as error:
2758 raise self.RemoteRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2761 if remote_tdo_handle is not None:
2763 remote_lsa.DeleteObject(remote_tdo_handle)
2764 remote_tdo_handle = None
2765 self.outf.write("RemoteTDO deleted.\n")
2766 except RuntimeError as error:
2767 self.outf.write("%s\n" % self.RemoteRuntimeError(self, error, "DeleteObject() failed"))
2769 if local_tdo_handle is not None:
2771 local_lsa.DeleteObject(local_tdo_handle)
2772 local_tdo_handle = None
2773 self.outf.write("LocalTDO deleted.\n")
2774 except RuntimeError as error:
2775 self.outf.write("%s\n" % self.LocalRuntimeError(self, error, "DeleteObject() failed"))
2779 class cmd_domain_trust_validate(DomainTrustCommand):
2780 """Validate a domain trust."""
2782 synopsis = "%prog DOMAIN [options]"
2784 takes_optiongroups = {
2785 "sambaopts": options.SambaOptions,
2786 "versionopts": options.VersionOptions,
2787 "credopts": options.CredentialsOptions,
2788 "localdcopts": LocalDCCredentialsOptions,
2792 Option("--validate-location", type="choice", metavar="LOCATION",
2793 choices=["local", "both"],
2794 help="Where to validate the trusted domain object: 'local' or 'both'.",
2795 dest='validate_location',
2799 takes_args = ["domain"]
2801 def run(self, domain, sambaopts=None, versionopts=None, credopts=None, localdcopts=None,
2802 validate_location=None):
2804 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2806 local_server = self.setup_local_server(sambaopts, localdcopts)
2808 local_lsa = self.new_local_lsa_connection()
2809 except RuntimeError as error:
2810 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2813 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2814 except RuntimeError as error:
2815 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2817 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2818 local_lsa_info.name.string,
2819 local_lsa_info.dns_domain.string,
2820 local_lsa_info.sid))
2823 lsaString = lsa.String()
2824 lsaString.string = domain
2825 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2826 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2827 except RuntimeError as error:
2828 if self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2829 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2831 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
2833 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
2834 local_tdo_info.netbios_name.string,
2835 local_tdo_info.domain_name.string,
2836 local_tdo_info.sid))
2839 local_netlogon = self.new_local_netlogon_connection()
2840 except RuntimeError as error:
2841 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2844 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_server,
2845 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2847 local_tdo_info.domain_name.string)
2848 except RuntimeError as error:
2849 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2851 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2852 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2854 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2855 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2856 local_trust_verify.trusted_dc_name,
2857 local_trust_verify.tc_connection_status[1],
2858 local_trust_verify.pdc_connection_status[1])
2860 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2861 local_trust_verify.trusted_dc_name,
2862 local_trust_verify.tc_connection_status[1],
2863 local_trust_verify.pdc_connection_status[1])
2865 if local_trust_status != self.WERR_OK or local_conn_status != self.WERR_OK:
2866 raise CommandError(local_validation)
2868 self.outf.write("OK: %s\n" % local_validation)
2871 server = local_trust_verify.trusted_dc_name.replace('\\', '')
2872 domain_and_server = "%s\\%s" % (local_tdo_info.domain_name.string, server)
2873 local_trust_rediscover = local_netlogon.netr_LogonControl2Ex(local_server,
2874 netlogon.NETLOGON_CONTROL_REDISCOVER,
2877 except RuntimeError as error:
2878 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
2880 local_conn_status = self._uint32(local_trust_rediscover.tc_connection_status[0])
2881 local_rediscover = "LocalRediscover: DC[%s] CONNECTION[%s]" % (
2882 local_trust_rediscover.trusted_dc_name,
2883 local_trust_rediscover.tc_connection_status[1])
2885 if local_conn_status != self.WERR_OK:
2886 raise CommandError(local_rediscover)
2888 self.outf.write("OK: %s\n" % local_rediscover)
2890 if validate_location != "local":
2892 remote_server = self.setup_remote_server(credopts, domain, require_pdc=False)
2893 except RuntimeError as error:
2894 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2897 remote_netlogon = self.new_remote_netlogon_connection()
2898 except RuntimeError as error:
2899 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2902 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_server,
2903 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2905 local_lsa_info.dns_domain.string)
2906 except RuntimeError as error:
2907 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2909 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2910 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2912 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2913 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2914 remote_trust_verify.trusted_dc_name,
2915 remote_trust_verify.tc_connection_status[1],
2916 remote_trust_verify.pdc_connection_status[1])
2918 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2919 remote_trust_verify.trusted_dc_name,
2920 remote_trust_verify.tc_connection_status[1],
2921 remote_trust_verify.pdc_connection_status[1])
2923 if remote_trust_status != self.WERR_OK or remote_conn_status != self.WERR_OK:
2924 raise CommandError(remote_validation)
2926 self.outf.write("OK: %s\n" % remote_validation)
2929 server = remote_trust_verify.trusted_dc_name.replace('\\', '')
2930 domain_and_server = "%s\\%s" % (local_lsa_info.dns_domain.string, server)
2931 remote_trust_rediscover = remote_netlogon.netr_LogonControl2Ex(remote_server,
2932 netlogon.NETLOGON_CONTROL_REDISCOVER,
2935 except RuntimeError as error:
2936 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
2938 remote_conn_status = self._uint32(remote_trust_rediscover.tc_connection_status[0])
2940 remote_rediscover = "RemoteRediscover: DC[%s] CONNECTION[%s]" % (
2941 remote_trust_rediscover.trusted_dc_name,
2942 remote_trust_rediscover.tc_connection_status[1])
2944 if remote_conn_status != self.WERR_OK:
2945 raise CommandError(remote_rediscover)
2947 self.outf.write("OK: %s\n" % remote_rediscover)
2951 class cmd_domain_trust_namespaces(DomainTrustCommand):
2952 """Manage forest trust namespaces."""
2954 synopsis = "%prog [DOMAIN] [options]"
2956 takes_optiongroups = {
2957 "sambaopts": options.SambaOptions,
2958 "versionopts": options.VersionOptions,
2959 "localdcopts": LocalDCCredentialsOptions,
2963 Option("--refresh", type="choice", metavar="check|store",
2964 choices=["check", "store", None],
2965 help="List and maybe store refreshed forest trust information: 'check' or 'store'.",
2968 Option("--enable-all", action="store_true",
2969 help="Try to update disabled entries, not allowed with --refresh=check.",
2972 Option("--enable-tln", action="append", metavar='DNSDOMAIN',
2973 help="Enable a top level name entry. Can be specified multiple times.",
2976 Option("--disable-tln", action="append", metavar='DNSDOMAIN',
2977 help="Disable a top level name entry. Can be specified multiple times.",
2980 Option("--add-tln-ex", action="append", metavar='DNSDOMAIN',
2981 help="Add a top level exclusion entry. Can be specified multiple times.",
2984 Option("--delete-tln-ex", action="append", metavar='DNSDOMAIN',
2985 help="Delete a top level exclusion entry. Can be specified multiple times.",
2986 dest='delete_tln_ex',
2988 Option("--enable-nb", action="append", metavar='NETBIOSDOMAIN',
2989 help="Enable a netbios name in a domain entry. Can be specified multiple times.",
2992 Option("--disable-nb", action="append", metavar='NETBIOSDOMAIN',
2993 help="Disable a netbios name in a domain entry. Can be specified multiple times.",
2996 Option("--enable-sid", action="append", metavar='DOMAINSID',
2997 help="Enable a SID in a domain entry. Can be specified multiple times.",
2998 dest='enable_sid_str',
3000 Option("--disable-sid", action="append", metavar='DOMAINSID',
3001 help="Disable a SID in a domain entry. Can be specified multiple times.",
3002 dest='disable_sid_str',
3004 Option("--add-upn-suffix", action="append", metavar='DNSDOMAIN',
3005 help="Add a new uPNSuffixes attribute for the local forest. Can be specified multiple times.",
3008 Option("--delete-upn-suffix", action="append", metavar='DNSDOMAIN',
3009 help="Delete an existing uPNSuffixes attribute of the local forest. Can be specified multiple times.",
3012 Option("--add-spn-suffix", action="append", metavar='DNSDOMAIN',
3013 help="Add a new msDS-SPNSuffixes attribute for the local forest. Can be specified multiple times.",
3016 Option("--delete-spn-suffix", action="append", metavar='DNSDOMAIN',
3017 help="Delete an existing msDS-SPNSuffixes attribute of the local forest. Can be specified multiple times.",
3022 takes_args = ["domain?"]
3024 def run(self, domain=None, sambaopts=None, localdcopts=None, versionopts=None,
3025 refresh=None, enable_all=False,
3026 enable_tln=[], disable_tln=[], add_tln_ex=[], delete_tln_ex=[],
3027 enable_sid_str=[], disable_sid_str=[], enable_nb=[], disable_nb=[],
3028 add_upn=[], delete_upn=[], add_spn=[], delete_spn=[]):
3030 require_update = False
3033 if refresh == "store":
3034 raise CommandError("--refresh=%s not allowed without DOMAIN" % refresh)
3037 raise CommandError("--enable-all not allowed without DOMAIN")
3039 if len(enable_tln) > 0:
3040 raise CommandError("--enable-tln not allowed without DOMAIN")
3041 if len(disable_tln) > 0:
3042 raise CommandError("--disable-tln not allowed without DOMAIN")
3044 if len(add_tln_ex) > 0:
3045 raise CommandError("--add-tln-ex not allowed without DOMAIN")
3046 if len(delete_tln_ex) > 0:
3047 raise CommandError("--delete-tln-ex not allowed without DOMAIN")
3049 if len(enable_nb) > 0:
3050 raise CommandError("--enable-nb not allowed without DOMAIN")
3051 if len(disable_nb) > 0:
3052 raise CommandError("--disable-nb not allowed without DOMAIN")
3054 if len(enable_sid_str) > 0:
3055 raise CommandError("--enable-sid not allowed without DOMAIN")
3056 if len(disable_sid_str) > 0:
3057 raise CommandError("--disable-sid not allowed without DOMAIN")
3059 if len(add_upn) > 0:
3061 if not n.startswith("*."):
3063 raise CommandError("value[%s] specified for --add-upn-suffix should not include with '*.'" % n)
3064 require_update = True
3065 if len(delete_upn) > 0:
3066 for n in delete_upn:
3067 if not n.startswith("*."):
3069 raise CommandError("value[%s] specified for --delete-upn-suffix should not include with '*.'" % n)
3070 require_update = True
3072 for d in delete_upn:
3073 if a.lower() != d.lower():
3075 raise CommandError("value[%s] specified for --add-upn-suffix and --delete-upn-suffix" % a)
3077 if len(add_spn) > 0:
3079 if not n.startswith("*."):
3081 raise CommandError("value[%s] specified for --add-spn-suffix should not include with '*.'" % n)
3082 require_update = True
3083 if len(delete_spn) > 0:
3084 for n in delete_spn:
3085 if not n.startswith("*."):
3087 raise CommandError("value[%s] specified for --delete-spn-suffix should not include with '*.'" % n)
3088 require_update = True
3090 for d in delete_spn:
3091 if a.lower() != d.lower():
3093 raise CommandError("value[%s] specified for --add-spn-suffix and --delete-spn-suffix" % a)
3095 if len(add_upn) > 0:
3096 raise CommandError("--add-upn-suffix not allowed together with DOMAIN")
3097 if len(delete_upn) > 0:
3098 raise CommandError("--delete-upn-suffix not allowed together with DOMAIN")
3099 if len(add_spn) > 0:
3100 raise CommandError("--add-spn-suffix not allowed together with DOMAIN")
3101 if len(delete_spn) > 0:
3102 raise CommandError("--delete-spn-suffix not allowed together with DOMAIN")
3104 if refresh is not None:
3105 if refresh == "store":
3106 require_update = True
3108 if enable_all and refresh != "store":
3109 raise CommandError("--enable-all not allowed together with --refresh=%s" % refresh)
3111 if len(enable_tln) > 0:
3112 raise CommandError("--enable-tln not allowed together with --refresh")
3113 if len(disable_tln) > 0:
3114 raise CommandError("--disable-tln not allowed together with --refresh")
3116 if len(add_tln_ex) > 0:
3117 raise CommandError("--add-tln-ex not allowed together with --refresh")
3118 if len(delete_tln_ex) > 0:
3119 raise CommandError("--delete-tln-ex not allowed together with --refresh")
3121 if len(enable_nb) > 0:
3122 raise CommandError("--enable-nb not allowed together with --refresh")
3123 if len(disable_nb) > 0:
3124 raise CommandError("--disable-nb not allowed together with --refresh")
3126 if len(enable_sid_str) > 0:
3127 raise CommandError("--enable-sid not allowed together with --refresh")
3128 if len(disable_sid_str) > 0:
3129 raise CommandError("--disable-sid not allowed together with --refresh")
3132 require_update = True
3134 if len(enable_tln) > 0:
3135 raise CommandError("--enable-tln not allowed together with --enable-all")
3137 if len(enable_nb) > 0:
3138 raise CommandError("--enable-nb not allowed together with --enable-all")
3140 if len(enable_sid) > 0:
3141 raise CommandError("--enable-sid not allowed together with --enable-all")
3143 if len(enable_tln) > 0:
3144 require_update = True
3145 if len(disable_tln) > 0:
3146 require_update = True
3147 for e in enable_tln:
3148 for d in disable_tln:
3149 if e.lower() != d.lower():
3151 raise CommandError("value[%s] specified for --enable-tln and --disable-tln" % e)
3153 if len(add_tln_ex) > 0:
3154 for n in add_tln_ex:
3155 if not n.startswith("*."):
3157 raise CommandError("value[%s] specified for --add-tln-ex should not include with '*.'" % n)
3158 require_update = True
3159 if len(delete_tln_ex) > 0:
3160 for n in delete_tln_ex:
3161 if not n.startswith("*."):
3163 raise CommandError("value[%s] specified for --delete-tln-ex should not include with '*.'" % n)
3164 require_update = True
3165 for a in add_tln_ex:
3166 for d in delete_tln_ex:
3167 if a.lower() != d.lower():
3169 raise CommandError("value[%s] specified for --add-tln-ex and --delete-tln-ex" % a)
3171 if len(enable_nb) > 0:
3172 require_update = True
3173 if len(disable_nb) > 0:
3174 require_update = True
3176 for d in disable_nb:
3177 if e.upper() != d.upper():
3179 raise CommandError("value[%s] specified for --enable-nb and --disable-nb" % e)
3182 for s in enable_sid_str:
3184 sid = security.dom_sid(s)
3185 except TypeError as error:
3186 raise CommandError("value[%s] specified for --enable-sid is not a valid SID" % s)
3187 enable_sid.append(sid)
3189 for s in disable_sid_str:
3191 sid = security.dom_sid(s)
3192 except TypeError as error:
3193 raise CommandError("value[%s] specified for --disable-sid is not a valid SID" % s)
3194 disable_sid.append(sid)
3195 if len(enable_sid) > 0:
3196 require_update = True
3197 if len(disable_sid) > 0:
3198 require_update = True
3199 for e in enable_sid:
3200 for d in disable_sid:
3203 raise CommandError("value[%s] specified for --enable-sid and --disable-sid" % e)
3205 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
3207 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
3209 local_server = self.setup_local_server(sambaopts, localdcopts)
3211 local_lsa = self.new_local_lsa_connection()
3212 except RuntimeError as error:
3213 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
3216 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
3217 except RuntimeError as error:
3218 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
3220 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
3221 local_lsa_info.name.string,
3222 local_lsa_info.dns_domain.string,
3223 local_lsa_info.sid))
3227 local_netlogon = self.new_local_netlogon_connection()
3228 except RuntimeError as error:
3229 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3232 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3233 except RuntimeError as error:
3234 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3236 if local_netlogon_info.domain_name != local_netlogon_info.forest_name:
3237 raise CommandError("The local domain [%s] is not the forest root [%s]" % (
3238 local_netlogon_info.domain_name,
3239 local_netlogon_info.forest_name))
3242 # get all information about our own forest
3243 own_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3245 except RuntimeError as error:
3246 if self.check_runtime_error(error, self.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
3247 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3250 if self.check_runtime_error(error, self.WERR_INVALID_FUNCTION):
3251 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3254 if self.check_runtime_error(error, self.WERR_NERR_ACFNOTLOADED):
3255 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3258 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3260 self.outf.write("Own forest trust information...\n")
3261 self.write_forest_trust_info(own_forest_info,
3262 tln=local_lsa_info.dns_domain.string)
3265 local_samdb = self.new_local_ldap_connection()
3266 except RuntimeError as error:
3267 raise self.LocalRuntimeError(self, error, "failed to connect to SamDB")
3269 local_partitions_dn = "CN=Partitions,%s" % str(local_samdb.get_config_basedn())
3270 attrs = ['uPNSuffixes', 'msDS-SPNSuffixes']
3272 msgs = local_samdb.search(base=local_partitions_dn,
3273 scope=ldb.SCOPE_BASE,
3274 expression="(objectClass=crossRefContainer)",
3276 stored_msg = msgs[0]
3277 except ldb.LdbError as error:
3278 raise self.LocalLdbError(self, error, "failed to search partition dn")
3280 stored_upn_vals = []
3281 if 'uPNSuffixes' in stored_msg:
3282 stored_upn_vals.extend(stored_msg['uPNSuffixes'])
3284 stored_spn_vals = []
3285 if 'msDS-SPNSuffixes' in stored_msg:
3286 stored_spn_vals.extend(stored_msg['msDS-SPNSuffixes'])
3288 self.outf.write("Stored uPNSuffixes attributes[%d]:\n" % len(stored_upn_vals))
3289 for v in stored_upn_vals:
3290 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3291 self.outf.write("Stored msDS-SPNSuffixes attributes[%d]:\n" % len(stored_spn_vals))
3292 for v in stored_spn_vals:
3293 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3295 if not require_update:
3299 update_upn_vals = []
3300 update_upn_vals.extend(stored_upn_vals)
3303 update_spn_vals = []
3304 update_spn_vals.extend(stored_spn_vals)
3308 for i in xrange(0, len(update_upn_vals)):
3309 v = update_upn_vals[i]
3310 if v.lower() != upn.lower():
3315 raise CommandError("Entry already present for value[%s] specified for --add-upn-suffix" % upn)
3316 update_upn_vals.append(upn)
3319 for upn in delete_upn:
3321 for i in xrange(0, len(update_upn_vals)):
3322 v = update_upn_vals[i]
3323 if v.lower() != upn.lower():
3328 raise CommandError("Entry not found for value[%s] specified for --delete-upn-suffix" % upn)
3330 update_upn_vals.pop(idx)
3335 for i in xrange(0, len(update_spn_vals)):
3336 v = update_spn_vals[i]
3337 if v.lower() != spn.lower():
3342 raise CommandError("Entry already present for value[%s] specified for --add-spn-suffix" % spn)
3343 update_spn_vals.append(spn)
3346 for spn in delete_spn:
3348 for i in xrange(0, len(update_spn_vals)):
3349 v = update_spn_vals[i]
3350 if v.lower() != spn.lower():
3355 raise CommandError("Entry not found for value[%s] specified for --delete-spn-suffix" % spn)
3357 update_spn_vals.pop(idx)
3360 self.outf.write("Update uPNSuffixes attributes[%d]:\n" % len(update_upn_vals))
3361 for v in update_upn_vals:
3362 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3363 self.outf.write("Update msDS-SPNSuffixes attributes[%d]:\n" % len(update_spn_vals))
3364 for v in update_spn_vals:
3365 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3367 update_msg = ldb.Message()
3368 update_msg.dn = stored_msg.dn
3371 update_msg['uPNSuffixes'] = ldb.MessageElement(update_upn_vals,
3372 ldb.FLAG_MOD_REPLACE,
3375 update_msg['msDS-SPNSuffixes'] = ldb.MessageElement(update_spn_vals,
3376 ldb.FLAG_MOD_REPLACE,
3379 local_samdb.modify(update_msg)
3380 except ldb.LdbError as error:
3381 raise self.LocalLdbError(self, error, "failed to update partition dn")
3384 stored_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3386 except RuntimeError as error:
3387 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3389 self.outf.write("Stored forest trust information...\n")
3390 self.write_forest_trust_info(stored_forest_info,
3391 tln=local_lsa_info.dns_domain.string)
3395 lsaString = lsa.String()
3396 lsaString.string = domain
3397 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
3398 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
3399 except RuntimeError as error:
3400 if self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
3401 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
3403 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
3405 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
3406 local_tdo_info.netbios_name.string,
3407 local_tdo_info.domain_name.string,
3408 local_tdo_info.sid))
3410 if not local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
3411 raise CommandError("trusted domain object for domain [%s] is not marked as FOREST_TRANSITIVE." % domain)
3413 if refresh is not None:
3415 local_netlogon = self.new_local_netlogon_connection()
3416 except RuntimeError as error:
3417 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3420 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3421 except RuntimeError as error:
3422 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3424 lsa_update_check = 1
3425 if refresh == "store":
3426 netlogon_update_tdo = netlogon.DS_GFTI_UPDATE_TDO
3428 lsa_update_check = 0
3430 netlogon_update_tdo = 0
3433 # get all information about the remote trust
3434 # this triggers netr_GetForestTrustInformation to the remote domain
3435 # and lsaRSetForestTrustInformation() locally, but new top level
3436 # names are disabled by default.
3437 fresh_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3438 local_tdo_info.domain_name.string,
3439 netlogon_update_tdo)
3440 except RuntimeError as error:
3441 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3444 fresh_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3445 local_tdo_info.domain_name,
3446 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3449 except RuntimeError as error:
3450 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3452 self.outf.write("Fresh forest trust information...\n")
3453 self.write_forest_trust_info(fresh_forest_info,
3454 tln=local_tdo_info.domain_name.string,
3455 collisions=fresh_forest_collision)
3457 if refresh == "store":
3459 lsaString = lsa.String()
3460 lsaString.string = local_tdo_info.domain_name.string
3461 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3463 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3464 except RuntimeError as error:
3465 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3467 self.outf.write("Stored forest trust information...\n")
3468 self.write_forest_trust_info(stored_forest_info,
3469 tln=local_tdo_info.domain_name.string)
3474 # The none --refresh path
3478 lsaString = lsa.String()
3479 lsaString.string = local_tdo_info.domain_name.string
3480 local_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3482 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3483 except RuntimeError as error:
3484 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3486 self.outf.write("Local forest trust information...\n")
3487 self.write_forest_trust_info(local_forest_info,
3488 tln=local_tdo_info.domain_name.string)
3490 if not require_update:
3494 entries.extend(local_forest_info.entries)
3495 update_forest_info = lsa.ForestTrustInformation()
3496 update_forest_info.count = len(entries)
3497 update_forest_info.entries = entries
3500 for i in xrange(0, len(update_forest_info.entries)):
3501 r = update_forest_info.entries[i]
3502 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3504 if update_forest_info.entries[i].flags == 0:
3506 update_forest_info.entries[i].time = 0
3507 update_forest_info.entries[i].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3508 for i in xrange(0, len(update_forest_info.entries)):
3509 r = update_forest_info.entries[i]
3510 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3512 if update_forest_info.entries[i].flags == 0:
3514 update_forest_info.entries[i].time = 0
3515 update_forest_info.entries[i].flags &= ~lsa.LSA_NB_DISABLED_MASK
3516 update_forest_info.entries[i].flags &= ~lsa.LSA_SID_DISABLED_MASK
3518 for tln in enable_tln:
3520 for i in xrange(0, len(update_forest_info.entries)):
3521 r = update_forest_info.entries[i]
3522 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3524 if r.forest_trust_data.string.lower() != tln.lower():
3529 raise CommandError("Entry not found for value[%s] specified for --enable-tln" % tln)
3530 if not update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_MASK:
3531 raise CommandError("Entry found for value[%s] specified for --enable-tln is already enabled" % tln)
3532 update_forest_info.entries[idx].time = 0
3533 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3535 for tln in disable_tln:
3537 for i in xrange(0, len(update_forest_info.entries)):
3538 r = update_forest_info.entries[i]
3539 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3541 if r.forest_trust_data.string.lower() != tln.lower():
3546 raise CommandError("Entry not found for value[%s] specified for --disable-tln" % tln)
3547 if update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_ADMIN:
3548 raise CommandError("Entry found for value[%s] specified for --disable-tln is already disabled" % tln)
3549 update_forest_info.entries[idx].time = 0
3550 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3551 update_forest_info.entries[idx].flags |= lsa.LSA_TLN_DISABLED_ADMIN
3553 for tln_ex in add_tln_ex:
3555 for i in xrange(0, len(update_forest_info.entries)):
3556 r = update_forest_info.entries[i]
3557 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3559 if r.forest_trust_data.string.lower() != tln_ex.lower():
3564 raise CommandError("Entry already present for value[%s] specified for --add-tln-ex" % tln_ex)
3566 tln_dot = ".%s" % tln_ex.lower()
3568 for i in xrange(0, len(update_forest_info.entries)):
3569 r = update_forest_info.entries[i]
3570 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3572 r_dot = ".%s" % r.forest_trust_data.string.lower()
3573 if tln_dot == r_dot:
3574 raise CommandError("TLN entry present for value[%s] specified for --add-tln-ex" % tln_ex)
3575 if not tln_dot.endswith(r_dot):
3581 raise CommandError("No TLN parent present for value[%s] specified for --add-tln-ex" % tln_ex)
3583 r = lsa.ForestTrustRecord()
3584 r.type = lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX
3587 r.forest_trust_data.string = tln_ex
3590 entries.extend(update_forest_info.entries)
3591 entries.insert(idx + 1, r)
3592 update_forest_info.count = len(entries)
3593 update_forest_info.entries = entries
3595 for tln_ex in delete_tln_ex:
3597 for i in xrange(0, len(update_forest_info.entries)):
3598 r = update_forest_info.entries[i]
3599 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3601 if r.forest_trust_data.string.lower() != tln_ex.lower():
3606 raise CommandError("Entry not found for value[%s] specified for --delete-tln-ex" % tln_ex)
3609 entries.extend(update_forest_info.entries)
3611 update_forest_info.count = len(entries)
3612 update_forest_info.entries = entries
3614 for nb in enable_nb:
3616 for i in xrange(0, len(update_forest_info.entries)):
3617 r = update_forest_info.entries[i]
3618 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3620 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3625 raise CommandError("Entry not found for value[%s] specified for --enable-nb" % nb)
3626 if not update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_MASK:
3627 raise CommandError("Entry found for value[%s] specified for --enable-nb is already enabled" % nb)
3628 update_forest_info.entries[idx].time = 0
3629 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3631 for nb in disable_nb:
3633 for i in xrange(0, len(update_forest_info.entries)):
3634 r = update_forest_info.entries[i]
3635 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3637 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3642 raise CommandError("Entry not found for value[%s] specified for --delete-nb" % nb)
3643 if update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_ADMIN:
3644 raise CommandError("Entry found for value[%s] specified for --disable-nb is already disabled" % nb)
3645 update_forest_info.entries[idx].time = 0
3646 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3647 update_forest_info.entries[idx].flags |= lsa.LSA_NB_DISABLED_ADMIN
3649 for sid in enable_sid:
3651 for i in xrange(0, len(update_forest_info.entries)):
3652 r = update_forest_info.entries[i]
3653 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3655 if r.forest_trust_data.domain_sid != sid:
3660 raise CommandError("Entry not found for value[%s] specified for --enable-sid" % sid)
3661 if not update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_MASK:
3662 raise CommandError("Entry found for value[%s] specified for --enable-sid is already enabled" % nb)
3663 update_forest_info.entries[idx].time = 0
3664 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3666 for sid in disable_sid:
3668 for i in xrange(0, len(update_forest_info.entries)):
3669 r = update_forest_info.entries[i]
3670 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3672 if r.forest_trust_data.domain_sid != sid:
3677 raise CommandError("Entry not found for value[%s] specified for --delete-sid" % sid)
3678 if update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_ADMIN:
3679 raise CommandError("Entry found for value[%s] specified for --disable-sid is already disabled" % nb)
3680 update_forest_info.entries[idx].time = 0
3681 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3682 update_forest_info.entries[idx].flags |= lsa.LSA_SID_DISABLED_ADMIN
3685 update_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3686 local_tdo_info.domain_name,
3687 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3688 update_forest_info, 0)
3689 except RuntimeError as error:
3690 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3692 self.outf.write("Updated forest trust information...\n")
3693 self.write_forest_trust_info(update_forest_info,
3694 tln=local_tdo_info.domain_name.string,
3695 collisions=update_forest_collision)
3698 lsaString = lsa.String()
3699 lsaString.string = local_tdo_info.domain_name.string
3700 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3702 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3703 except RuntimeError as error:
3704 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3706 self.outf.write("Stored forest trust information...\n")
3707 self.write_forest_trust_info(stored_forest_info,
3708 tln=local_tdo_info.domain_name.string)
3711 class cmd_domain_trust(SuperCommand):
3712 """Domain and forest trust management."""
3715 subcommands["list"] = cmd_domain_trust_list()
3716 subcommands["show"] = cmd_domain_trust_show()
3717 subcommands["create"] = cmd_domain_trust_create()
3718 subcommands["delete"] = cmd_domain_trust_delete()
3719 subcommands["validate"] = cmd_domain_trust_validate()
3720 subcommands["namespaces"] = cmd_domain_trust_namespaces()
3722 class cmd_domain(SuperCommand):
3723 """Domain management."""
3726 subcommands["demote"] = cmd_domain_demote()
3727 if cmd_domain_export_keytab is not None:
3728 subcommands["exportkeytab"] = cmd_domain_export_keytab()
3729 subcommands["info"] = cmd_domain_info()
3730 subcommands["provision"] = cmd_domain_provision()
3731 subcommands["join"] = cmd_domain_join()
3732 subcommands["dcpromo"] = cmd_domain_dcpromo()
3733 subcommands["level"] = cmd_domain_level()
3734 subcommands["passwordsettings"] = cmd_domain_passwordsettings()
3735 subcommands["classicupgrade"] = cmd_domain_classicupgrade()
3736 subcommands["samba3upgrade"] = cmd_domain_samba3upgrade()
3737 subcommands["trust"] = cmd_domain_trust()