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 dc_name = res[0].dn.get_rdn_value()
830 rdn = "CN=%s" % dc_name
832 # Let's move to the Computer container
836 computer_dn = ldb.Dn(remote_samdb, "CN=Computers,%s" % str(remote_samdb.domain_dn()))
837 res = remote_samdb.search(base=computer_dn, expression=rdn, scope=ldb.SCOPE_ONELEVEL)
840 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
841 scope=ldb.SCOPE_ONELEVEL)
842 while(len(res) != 0 and i < 100):
844 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
845 scope=ldb.SCOPE_ONELEVEL)
849 "Error while demoting, re-enabling inbound replication\n")
850 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
851 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
857 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
858 ldb.FLAG_MOD_REPLACE,
859 "userAccountControl")
861 remote_samdb.modify(msg)
863 raise CommandError("Unable to find a slot for renaming %s,"
864 " all names from %s-1 to %s-%d seemed used" %
865 (str(dc_dn), rdn, rdn, i - 9))
867 newrdn = "%s-%d" % (rdn, i)
870 newdn = ldb.Dn(remote_samdb, "%s,%s" % (newrdn, str(computer_dn)))
871 remote_samdb.rename(dc_dn, newdn)
874 "Error while demoting, re-enabling inbound replication\n")
875 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
876 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
882 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
883 ldb.FLAG_MOD_REPLACE,
884 "userAccountControl")
886 remote_samdb.modify(msg)
887 raise CommandError("Error while renaming %s to %s" % (str(dc_dn), str(newdn)), e)
890 server_dsa_dn = samdb.get_serverName()
891 domain = remote_samdb.get_root_basedn()
894 req1 = drsuapi.DsRemoveDSServerRequest1()
895 req1.server_dn = str(server_dsa_dn)
896 req1.domain_dn = str(domain)
899 drsuapiBind.DsRemoveDSServer(drsuapi_handle, 1, req1)
900 except RuntimeError as (werr, string):
901 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
903 "Error while demoting, re-enabling inbound replication\n")
904 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
905 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
911 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
912 ldb.FLAG_MOD_REPLACE,
913 "userAccountControl")
914 remote_samdb.modify(msg)
915 remote_samdb.rename(newdn, dc_dn)
916 if werr == 8452: #WERR_DS_DRA_NO_REPLICA
917 raise CommandError("The DC %s is not present on (already removed from) the remote server: " % server_dsa_dn, e)
919 raise CommandError("Error while sending a removeDsServer of %s: " % server_dsa_dn, e)
921 remove_dc.remove_sysvol_references(remote_samdb, dc_name)
923 # These are objects under the computer account that should be deleted
924 for s in ("CN=Enterprise,CN=NTFRS Subscriptions",
925 "CN=%s, CN=NTFRS Subscriptions" % lp.get("realm"),
926 "CN=Domain system Volumes (SYSVOL Share), CN=NTFRS Subscriptions",
927 "CN=NTFRS Subscriptions"):
929 remote_samdb.delete(ldb.Dn(remote_samdb,
930 "%s,%s" % (s, str(newdn))))
931 except ldb.LdbError, l:
934 self.errf.write("Demote successful\n")
937 class cmd_domain_level(Command):
938 """Raise domain and forest function levels."""
940 synopsis = "%prog (show|raise <options>) [options]"
942 takes_optiongroups = {
943 "sambaopts": options.SambaOptions,
944 "credopts": options.CredentialsOptions,
945 "versionopts": options.VersionOptions,
949 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
950 metavar="URL", dest="H"),
951 Option("--quiet", help="Be quiet", action="store_true"),
952 Option("--forest-level", type="choice", choices=["2003", "2008", "2008_R2"],
953 help="The forest function level (2003 | 2008 | 2008_R2)"),
954 Option("--domain-level", type="choice", choices=["2003", "2008", "2008_R2"],
955 help="The domain function level (2003 | 2008 | 2008_R2)")
958 takes_args = ["subcommand"]
960 def run(self, subcommand, H=None, forest_level=None, domain_level=None,
961 quiet=False, credopts=None, sambaopts=None, versionopts=None):
962 lp = sambaopts.get_loadparm()
963 creds = credopts.get_credentials(lp, fallback_machine=True)
965 samdb = SamDB(url=H, session_info=system_session(),
966 credentials=creds, lp=lp)
968 domain_dn = samdb.domain_dn()
970 res_forest = samdb.search("CN=Partitions,%s" % samdb.get_config_basedn(),
971 scope=ldb.SCOPE_BASE, attrs=["msDS-Behavior-Version"])
972 assert len(res_forest) == 1
974 res_domain = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
975 attrs=["msDS-Behavior-Version", "nTMixedDomain"])
976 assert len(res_domain) == 1
978 res_dc_s = samdb.search("CN=Sites,%s" % samdb.get_config_basedn(),
979 scope=ldb.SCOPE_SUBTREE, expression="(objectClass=nTDSDSA)",
980 attrs=["msDS-Behavior-Version"])
981 assert len(res_dc_s) >= 1
984 level_forest = int(res_forest[0]["msDS-Behavior-Version"][0])
985 level_domain = int(res_domain[0]["msDS-Behavior-Version"][0])
986 level_domain_mixed = int(res_domain[0]["nTMixedDomain"][0])
988 min_level_dc = int(res_dc_s[0]["msDS-Behavior-Version"][0]) # Init value
990 if int(msg["msDS-Behavior-Version"][0]) < min_level_dc:
991 min_level_dc = int(msg["msDS-Behavior-Version"][0])
993 if level_forest < 0 or level_domain < 0:
994 raise CommandError("Domain and/or forest function level(s) is/are invalid. Correct them or reprovision!")
996 raise CommandError("Lowest function level of a DC is invalid. Correct this or reprovision!")
997 if level_forest > level_domain:
998 raise CommandError("Forest function level is higher than the domain level(s). Correct this or reprovision!")
999 if level_domain > min_level_dc:
1000 raise CommandError("Domain function level is higher than the lowest function level of a DC. Correct this or reprovision!")
1003 raise CommandError("Could not retrieve the actual domain, forest level and/or lowest DC function level!")
1005 if subcommand == "show":
1006 self.message("Domain and forest function level for domain '%s'" % domain_dn)
1007 if level_forest == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1008 self.message("\nATTENTION: You run SAMBA 4 on a forest function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1009 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1010 self.message("\nATTENTION: You run SAMBA 4 on a domain function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1011 if min_level_dc == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1012 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)!")
1016 if level_forest == DS_DOMAIN_FUNCTION_2000:
1018 elif level_forest == DS_DOMAIN_FUNCTION_2003_MIXED:
1019 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1020 elif level_forest == DS_DOMAIN_FUNCTION_2003:
1022 elif level_forest == DS_DOMAIN_FUNCTION_2008:
1024 elif level_forest == DS_DOMAIN_FUNCTION_2008_R2:
1027 outstr = "higher than 2008 R2"
1028 self.message("Forest function level: (Windows) " + outstr)
1030 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1031 outstr = "2000 mixed (NT4 DC support)"
1032 elif level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed == 0:
1034 elif level_domain == DS_DOMAIN_FUNCTION_2003_MIXED:
1035 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1036 elif level_domain == DS_DOMAIN_FUNCTION_2003:
1038 elif level_domain == DS_DOMAIN_FUNCTION_2008:
1040 elif level_domain == DS_DOMAIN_FUNCTION_2008_R2:
1043 outstr = "higher than 2008 R2"
1044 self.message("Domain function level: (Windows) " + outstr)
1046 if min_level_dc == DS_DOMAIN_FUNCTION_2000:
1048 elif min_level_dc == DS_DOMAIN_FUNCTION_2003:
1050 elif min_level_dc == DS_DOMAIN_FUNCTION_2008:
1052 elif min_level_dc == DS_DOMAIN_FUNCTION_2008_R2:
1055 outstr = "higher than 2008 R2"
1056 self.message("Lowest function level of a DC: (Windows) " + outstr)
1058 elif subcommand == "raise":
1061 if domain_level is not None:
1062 if domain_level == "2003":
1063 new_level_domain = DS_DOMAIN_FUNCTION_2003
1064 elif domain_level == "2008":
1065 new_level_domain = DS_DOMAIN_FUNCTION_2008
1066 elif domain_level == "2008_R2":
1067 new_level_domain = DS_DOMAIN_FUNCTION_2008_R2
1069 if new_level_domain <= level_domain and level_domain_mixed == 0:
1070 raise CommandError("Domain function level can't be smaller than or equal to the actual one!")
1072 if new_level_domain > min_level_dc:
1073 raise CommandError("Domain function level can't be higher than the lowest function level of a DC!")
1075 # Deactivate mixed/interim domain support
1076 if level_domain_mixed != 0:
1077 # Directly on the base DN
1079 m.dn = ldb.Dn(samdb, domain_dn)
1080 m["nTMixedDomain"] = ldb.MessageElement("0",
1081 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1085 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup") + ",CN=Partitions,%s" % samdb.get_config_basedn())
1086 m["nTMixedDomain"] = ldb.MessageElement("0",
1087 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1090 except ldb.LdbError, (enum, emsg):
1091 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1094 # Directly on the base DN
1096 m.dn = ldb.Dn(samdb, domain_dn)
1097 m["msDS-Behavior-Version"]= ldb.MessageElement(
1098 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1099 "msDS-Behavior-Version")
1103 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup")
1104 + ",CN=Partitions,%s" % samdb.get_config_basedn())
1105 m["msDS-Behavior-Version"]= ldb.MessageElement(
1106 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1107 "msDS-Behavior-Version")
1110 except ldb.LdbError, (enum, emsg):
1111 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1114 level_domain = new_level_domain
1115 msgs.append("Domain function level changed!")
1117 if forest_level is not None:
1118 if forest_level == "2003":
1119 new_level_forest = DS_DOMAIN_FUNCTION_2003
1120 elif forest_level == "2008":
1121 new_level_forest = DS_DOMAIN_FUNCTION_2008
1122 elif forest_level == "2008_R2":
1123 new_level_forest = DS_DOMAIN_FUNCTION_2008_R2
1124 if new_level_forest <= level_forest:
1125 raise CommandError("Forest function level can't be smaller than or equal to the actual one!")
1126 if new_level_forest > level_domain:
1127 raise CommandError("Forest function level can't be higher than the domain function level(s). Please raise it/them first!")
1129 m.dn = ldb.Dn(samdb, "CN=Partitions,%s" % samdb.get_config_basedn())
1130 m["msDS-Behavior-Version"]= ldb.MessageElement(
1131 str(new_level_forest), ldb.FLAG_MOD_REPLACE,
1132 "msDS-Behavior-Version")
1134 msgs.append("Forest function level changed!")
1135 msgs.append("All changes applied successfully!")
1136 self.message("\n".join(msgs))
1138 raise CommandError("invalid argument: '%s' (choose from 'show', 'raise')" % subcommand)
1141 class cmd_domain_passwordsettings(Command):
1142 """Set password settings.
1144 Password complexity, password lockout policy, history length,
1145 minimum password length, the minimum and maximum password age) on
1146 a Samba AD DC server.
1148 Use against a Windows DC is possible, but group policy will override it.
1151 synopsis = "%prog (show|set <options>) [options]"
1153 takes_optiongroups = {
1154 "sambaopts": options.SambaOptions,
1155 "versionopts": options.VersionOptions,
1156 "credopts": options.CredentialsOptions,
1160 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1161 metavar="URL", dest="H"),
1162 Option("--quiet", help="Be quiet", action="store_true"),
1163 Option("--complexity", type="choice", choices=["on","off","default"],
1164 help="The password complexity (on | off | default). Default is 'on'"),
1165 Option("--store-plaintext", type="choice", choices=["on","off","default"],
1166 help="Store plaintext passwords where account have 'store passwords with reversible encryption' set (on | off | default). Default is 'off'"),
1167 Option("--history-length",
1168 help="The password history length (<integer> | default). Default is 24.", type=str),
1169 Option("--min-pwd-length",
1170 help="The minimum password length (<integer> | default). Default is 7.", type=str),
1171 Option("--min-pwd-age",
1172 help="The minimum password age (<integer in days> | default). Default is 1.", type=str),
1173 Option("--max-pwd-age",
1174 help="The maximum password age (<integer in days> | default). Default is 43.", type=str),
1175 Option("--account-lockout-duration",
1176 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),
1177 Option("--account-lockout-threshold",
1178 help="The number of bad password attempts allowed before locking out the account (<integer> | default). Default is 0 (never lock out).", type=str),
1179 Option("--reset-account-lockout-after",
1180 help="After this time is elapsed, the recorded number of attempts restarts from zero (<integer> | default). Default is 30.", type=str),
1183 takes_args = ["subcommand"]
1185 def run(self, subcommand, H=None, min_pwd_age=None, max_pwd_age=None,
1186 quiet=False, complexity=None, store_plaintext=None, history_length=None,
1187 min_pwd_length=None, account_lockout_duration=None, account_lockout_threshold=None,
1188 reset_account_lockout_after=None, credopts=None, sambaopts=None,
1190 lp = sambaopts.get_loadparm()
1191 creds = credopts.get_credentials(lp)
1193 samdb = SamDB(url=H, session_info=system_session(),
1194 credentials=creds, lp=lp)
1196 domain_dn = samdb.domain_dn()
1197 res = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1198 attrs=["pwdProperties", "pwdHistoryLength", "minPwdLength",
1199 "minPwdAge", "maxPwdAge", "lockoutDuration", "lockoutThreshold",
1200 "lockOutObservationWindow"])
1201 assert(len(res) == 1)
1203 pwd_props = int(res[0]["pwdProperties"][0])
1204 pwd_hist_len = int(res[0]["pwdHistoryLength"][0])
1205 cur_min_pwd_len = int(res[0]["minPwdLength"][0])
1207 cur_min_pwd_age = int(abs(int(res[0]["minPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1208 if int(res[0]["maxPwdAge"][0]) == -0x8000000000000000:
1211 cur_max_pwd_age = int(abs(int(res[0]["maxPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1212 cur_account_lockout_threshold = int(res[0]["lockoutThreshold"][0])
1214 if int(res[0]["lockoutDuration"][0]) == -0x8000000000000000:
1215 cur_account_lockout_duration = 0
1217 cur_account_lockout_duration = abs(int(res[0]["lockoutDuration"][0])) / (1e7 * 60)
1218 cur_reset_account_lockout_after = abs(int(res[0]["lockOutObservationWindow"][0])) / (1e7 * 60)
1219 except Exception, e:
1220 raise CommandError("Could not retrieve password properties!", e)
1222 if subcommand == "show":
1223 self.message("Password informations for domain '%s'" % domain_dn)
1225 if pwd_props & DOMAIN_PASSWORD_COMPLEX != 0:
1226 self.message("Password complexity: on")
1228 self.message("Password complexity: off")
1229 if pwd_props & DOMAIN_PASSWORD_STORE_CLEARTEXT != 0:
1230 self.message("Store plaintext passwords: on")
1232 self.message("Store plaintext passwords: off")
1233 self.message("Password history length: %d" % pwd_hist_len)
1234 self.message("Minimum password length: %d" % cur_min_pwd_len)
1235 self.message("Minimum password age (days): %d" % cur_min_pwd_age)
1236 self.message("Maximum password age (days): %d" % cur_max_pwd_age)
1237 self.message("Account lockout duration (mins): %d" % cur_account_lockout_duration)
1238 self.message("Account lockout threshold (attempts): %d" % cur_account_lockout_threshold)
1239 self.message("Reset account lockout after (mins): %d" % cur_reset_account_lockout_after)
1240 elif subcommand == "set":
1243 m.dn = ldb.Dn(samdb, domain_dn)
1245 if complexity is not None:
1246 if complexity == "on" or complexity == "default":
1247 pwd_props = pwd_props | DOMAIN_PASSWORD_COMPLEX
1248 msgs.append("Password complexity activated!")
1249 elif complexity == "off":
1250 pwd_props = pwd_props & (~DOMAIN_PASSWORD_COMPLEX)
1251 msgs.append("Password complexity deactivated!")
1253 if store_plaintext is not None:
1254 if store_plaintext == "on" or store_plaintext == "default":
1255 pwd_props = pwd_props | DOMAIN_PASSWORD_STORE_CLEARTEXT
1256 msgs.append("Plaintext password storage for changed passwords activated!")
1257 elif store_plaintext == "off":
1258 pwd_props = pwd_props & (~DOMAIN_PASSWORD_STORE_CLEARTEXT)
1259 msgs.append("Plaintext password storage for changed passwords deactivated!")
1261 if complexity is not None or store_plaintext is not None:
1262 m["pwdProperties"] = ldb.MessageElement(str(pwd_props),
1263 ldb.FLAG_MOD_REPLACE, "pwdProperties")
1265 if history_length is not None:
1266 if history_length == "default":
1269 pwd_hist_len = int(history_length)
1271 if pwd_hist_len < 0 or pwd_hist_len > 24:
1272 raise CommandError("Password history length must be in the range of 0 to 24!")
1274 m["pwdHistoryLength"] = ldb.MessageElement(str(pwd_hist_len),
1275 ldb.FLAG_MOD_REPLACE, "pwdHistoryLength")
1276 msgs.append("Password history length changed!")
1278 if min_pwd_length is not None:
1279 if min_pwd_length == "default":
1282 min_pwd_len = int(min_pwd_length)
1284 if min_pwd_len < 0 or min_pwd_len > 14:
1285 raise CommandError("Minimum password length must be in the range of 0 to 14!")
1287 m["minPwdLength"] = ldb.MessageElement(str(min_pwd_len),
1288 ldb.FLAG_MOD_REPLACE, "minPwdLength")
1289 msgs.append("Minimum password length changed!")
1291 if min_pwd_age is not None:
1292 if min_pwd_age == "default":
1295 min_pwd_age = int(min_pwd_age)
1297 if min_pwd_age < 0 or min_pwd_age > 998:
1298 raise CommandError("Minimum password age must be in the range of 0 to 998!")
1301 min_pwd_age_ticks = -int(min_pwd_age * (24 * 60 * 60 * 1e7))
1303 m["minPwdAge"] = ldb.MessageElement(str(min_pwd_age_ticks),
1304 ldb.FLAG_MOD_REPLACE, "minPwdAge")
1305 msgs.append("Minimum password age changed!")
1307 if max_pwd_age is not None:
1308 if max_pwd_age == "default":
1311 max_pwd_age = int(max_pwd_age)
1313 if max_pwd_age < 0 or max_pwd_age > 999:
1314 raise CommandError("Maximum password age must be in the range of 0 to 999!")
1317 if max_pwd_age == 0:
1318 max_pwd_age_ticks = -0x8000000000000000
1320 max_pwd_age_ticks = -int(max_pwd_age * (24 * 60 * 60 * 1e7))
1322 m["maxPwdAge"] = ldb.MessageElement(str(max_pwd_age_ticks),
1323 ldb.FLAG_MOD_REPLACE, "maxPwdAge")
1324 msgs.append("Maximum password age changed!")
1326 if account_lockout_duration is not None:
1327 if account_lockout_duration == "default":
1328 account_lockout_duration = 30
1330 account_lockout_duration = int(account_lockout_duration)
1332 if account_lockout_duration < 0 or account_lockout_duration > 99999:
1333 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1336 if account_lockout_duration == 0:
1337 account_lockout_duration_ticks = -0x8000000000000000
1339 account_lockout_duration_ticks = -int(account_lockout_duration * (60 * 1e7))
1341 m["lockoutDuration"] = ldb.MessageElement(str(account_lockout_duration_ticks),
1342 ldb.FLAG_MOD_REPLACE, "lockoutDuration")
1343 msgs.append("Account lockout duration changed!")
1345 if account_lockout_threshold is not None:
1346 if account_lockout_threshold == "default":
1347 account_lockout_threshold = 0
1349 account_lockout_threshold = int(account_lockout_threshold)
1351 m["lockoutThreshold"] = ldb.MessageElement(str(account_lockout_threshold),
1352 ldb.FLAG_MOD_REPLACE, "lockoutThreshold")
1353 msgs.append("Account lockout threshold changed!")
1355 if reset_account_lockout_after is not None:
1356 if reset_account_lockout_after == "default":
1357 reset_account_lockout_after = 30
1359 reset_account_lockout_after = int(reset_account_lockout_after)
1361 if reset_account_lockout_after < 0 or reset_account_lockout_after > 99999:
1362 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1365 if reset_account_lockout_after == 0:
1366 reset_account_lockout_after_ticks = -0x8000000000000000
1368 reset_account_lockout_after_ticks = -int(reset_account_lockout_after * (60 * 1e7))
1370 m["lockOutObservationWindow"] = ldb.MessageElement(str(reset_account_lockout_after_ticks),
1371 ldb.FLAG_MOD_REPLACE, "lockOutObservationWindow")
1372 msgs.append("Duration to reset account lockout after changed!")
1374 if max_pwd_age > 0 and min_pwd_age >= max_pwd_age:
1375 raise CommandError("Maximum password age (%d) must be greater than minimum password age (%d)!" % (max_pwd_age, min_pwd_age))
1378 raise CommandError("You must specify at least one option to set. Try --help")
1380 msgs.append("All changes applied successfully!")
1381 self.message("\n".join(msgs))
1383 raise CommandError("Wrong argument '%s'!" % subcommand)
1386 class cmd_domain_classicupgrade(Command):
1387 """Upgrade from Samba classic (NT4-like) database to Samba AD DC database.
1389 Specify either a directory with all Samba classic DC databases and state files (with --dbdir) or
1390 the testparm utility from your classic installation (with --testparm).
1393 synopsis = "%prog [options] <classic_smb_conf>"
1395 takes_optiongroups = {
1396 "sambaopts": options.SambaOptions,
1397 "versionopts": options.VersionOptions
1401 Option("--dbdir", type="string", metavar="DIR",
1402 help="Path to samba classic DC database directory"),
1403 Option("--testparm", type="string", metavar="PATH",
1404 help="Path to samba classic DC testparm utility from the previous installation. This allows the default paths of the previous installation to be followed"),
1405 Option("--targetdir", type="string", metavar="DIR",
1406 help="Path prefix where the new Samba 4.0 AD domain should be initialised"),
1407 Option("--quiet", help="Be quiet", action="store_true"),
1408 Option("--verbose", help="Be verbose", action="store_true"),
1409 Option("--use-xattrs", type="choice", choices=["yes","no","auto"], metavar="[yes|no|auto]",
1410 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"),
1411 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
1412 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
1413 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
1414 "BIND9_FLATFILE uses bind9 text database to store zone information, "
1415 "BIND9_DLZ uses samba4 AD to store zone information, "
1416 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
1417 default="SAMBA_INTERNAL")
1421 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
1422 action="store_true")
1424 if samba.is_ntvfs_fileserver_built():
1425 takes_options.extend(ntvfs_options)
1427 takes_args = ["smbconf"]
1429 def run(self, smbconf=None, targetdir=None, dbdir=None, testparm=None,
1430 quiet=False, verbose=False, use_xattrs=None, sambaopts=None, versionopts=None,
1431 dns_backend=None, use_ntvfs=False):
1433 if not os.path.exists(smbconf):
1434 raise CommandError("File %s does not exist" % smbconf)
1436 if testparm and not os.path.exists(testparm):
1437 raise CommandError("Testparm utility %s does not exist" % testparm)
1439 if dbdir and not os.path.exists(dbdir):
1440 raise CommandError("Directory %s does not exist" % dbdir)
1442 if not dbdir and not testparm:
1443 raise CommandError("Please specify either dbdir or testparm")
1445 logger = self.get_logger()
1447 logger.setLevel(logging.DEBUG)
1449 logger.setLevel(logging.WARNING)
1451 logger.setLevel(logging.INFO)
1453 if dbdir and testparm:
1454 logger.warning("both dbdir and testparm specified, ignoring dbdir.")
1457 lp = sambaopts.get_loadparm()
1459 s3conf = s3param.get_context()
1462 s3conf.set("realm", sambaopts.realm)
1464 if targetdir is not None:
1465 if not os.path.isdir(targetdir):
1469 if use_xattrs == "yes":
1471 elif use_xattrs == "auto" and not s3conf.get("posix:eadb"):
1473 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
1475 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
1478 samba.ntacls.setntacl(lp, tmpfile.name,
1479 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
1482 # FIXME: Don't catch all exceptions here
1483 logger.info("You are not root or your system do not support xattr, using tdb backend for attributes. "
1484 "If you intend to use this provision in production, rerun the script as root on a system supporting xattrs.")
1488 # Set correct default values from dbdir or testparm
1491 paths["state directory"] = dbdir
1492 paths["private dir"] = dbdir
1493 paths["lock directory"] = dbdir
1494 paths["smb passwd file"] = dbdir + "/smbpasswd"
1496 paths["state directory"] = get_testparm_var(testparm, smbconf, "state directory")
1497 paths["private dir"] = get_testparm_var(testparm, smbconf, "private dir")
1498 paths["smb passwd file"] = get_testparm_var(testparm, smbconf, "smb passwd file")
1499 paths["lock directory"] = get_testparm_var(testparm, smbconf, "lock directory")
1500 # "testparm" from Samba 3 < 3.4.x is not aware of the parameter
1501 # "state directory", instead make use of "lock directory"
1502 if len(paths["state directory"]) == 0:
1503 paths["state directory"] = paths["lock directory"]
1506 s3conf.set(p, paths[p])
1508 # load smb.conf parameters
1509 logger.info("Reading smb.conf")
1510 s3conf.load(smbconf)
1511 samba3 = Samba3(smbconf, s3conf)
1513 logger.info("Provisioning")
1514 upgrade_from_samba3(samba3, logger, targetdir, session_info=system_session(),
1515 useeadb=eadb, dns_backend=dns_backend, use_ntvfs=use_ntvfs)
1518 class cmd_domain_samba3upgrade(cmd_domain_classicupgrade):
1519 __doc__ = cmd_domain_classicupgrade.__doc__
1521 # This command is present for backwards compatibility only,
1522 # and should not be shown.
1526 class LocalDCCredentialsOptions(options.CredentialsOptions):
1527 def __init__(self, parser):
1528 options.CredentialsOptions.__init__(self, parser, special_name="local-dc")
1530 class DomainTrustCommand(Command):
1531 """List domain trusts."""
1534 Command.__init__(self)
1535 self.local_lp = None
1537 self.local_server = None
1538 self.local_binding_string = None
1539 self.local_creds = None
1541 self.remote_server = None
1542 self.remote_binding_string = None
1543 self.remote_creds = None
1545 WERR_OK = 0x00000000
1546 WERR_INVALID_FUNCTION = 0x00000001
1547 WERR_NERR_ACFNOTLOADED = 0x000008B3
1549 NT_STATUS_NOT_FOUND = 0xC0000225
1550 NT_STATUS_OBJECT_NAME_NOT_FOUND = 0xC0000034
1551 NT_STATUS_INVALID_PARAMETER = 0xC000000D
1552 NT_STATUS_INVALID_INFO_CLASS = 0xC0000003
1553 NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE = 0xC002002E
1555 def _uint32(self, v):
1556 return ctypes.c_uint32(v).value
1558 def check_runtime_error(self, runtime, val):
1562 err32 = self._uint32(runtime[0])
1568 class LocalRuntimeError(CommandError):
1569 def __init__(exception_self, self, runtime, message):
1570 err32 = self._uint32(runtime[0])
1572 msg = "LOCAL_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1573 self.local_server, message, err32, errstr)
1574 CommandError.__init__(exception_self, msg)
1576 class RemoteRuntimeError(CommandError):
1577 def __init__(exception_self, self, runtime, message):
1578 err32 = self._uint32(runtime[0])
1580 msg = "REMOTE_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1581 self.remote_server, message, err32, errstr)
1582 CommandError.__init__(exception_self, msg)
1584 class LocalLdbError(CommandError):
1585 def __init__(exception_self, self, ldb_error, message):
1586 errval = ldb_error[0]
1587 errstr = ldb_error[1]
1588 msg = "LOCAL_DC[%s]: %s - ERROR(%d) - %s" % (
1589 self.local_server, message, errval, errstr)
1590 CommandError.__init__(exception_self, msg)
1592 def setup_local_server(self, sambaopts, localdcopts):
1593 if self.local_server is not None:
1594 return self.local_server
1596 lp = sambaopts.get_loadparm()
1598 local_server = localdcopts.ipaddress
1599 if local_server is None:
1600 server_role = lp.server_role()
1601 if server_role != "ROLE_ACTIVE_DIRECTORY_DC":
1602 raise CommandError("Invalid server_role %s" % (server_role))
1603 local_server = lp.get('netbios name')
1604 local_transport = "ncalrpc"
1605 local_binding_options = ""
1606 local_binding_options += ",auth_type=ncalrpc_as_system"
1607 local_ldap_url = None
1610 local_transport = "ncacn_np"
1611 local_binding_options = ""
1612 local_ldap_url = "ldap://%s" % local_server
1613 local_creds = localdcopts.get_credentials(lp)
1617 self.local_server = local_server
1618 self.local_binding_string = "%s:%s[%s]" % (local_transport, local_server, local_binding_options)
1619 self.local_ldap_url = local_ldap_url
1620 self.local_creds = local_creds
1621 return self.local_server
1623 def new_local_lsa_connection(self):
1624 return lsa.lsarpc(self.local_binding_string, self.local_lp, self.local_creds)
1626 def new_local_netlogon_connection(self):
1627 return netlogon.netlogon(self.local_binding_string, self.local_lp, self.local_creds)
1629 def new_local_ldap_connection(self):
1630 return SamDB(url=self.local_ldap_url,
1631 session_info=system_session(),
1632 credentials=self.local_creds,
1635 def setup_remote_server(self, credopts, domain,
1637 require_writable=True):
1640 assert require_writable
1642 if self.remote_server is not None:
1643 return self.remote_server
1645 self.remote_server = "__unknown__remote_server__.%s" % domain
1646 assert self.local_server is not None
1648 remote_creds = credopts.get_credentials(self.local_lp)
1649 remote_server = credopts.ipaddress
1650 remote_binding_options = ""
1652 # TODO: we should also support NT4 domains
1653 # we could use local_netlogon.netr_DsRGetDCNameEx2() with the remote domain name
1654 # and delegate NBT or CLDAP to the local netlogon server
1656 remote_net = Net(remote_creds, self.local_lp, server=remote_server)
1657 remote_flags = nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS
1658 if require_writable:
1659 remote_flags |= nbt.NBT_SERVER_WRITABLE
1661 remote_flags |= nbt.NBT_SERVER_PDC
1662 remote_info = remote_net.finddc(flags=remote_flags, domain=domain, address=remote_server)
1664 raise CommandError("Failed to find a writeable DC for domain '%s'" % domain)
1666 nbt.NBT_SERVER_PDC: "PDC",
1667 nbt.NBT_SERVER_GC: "GC",
1668 nbt.NBT_SERVER_LDAP: "LDAP",
1669 nbt.NBT_SERVER_DS: "DS",
1670 nbt.NBT_SERVER_KDC: "KDC",
1671 nbt.NBT_SERVER_TIMESERV: "TIMESERV",
1672 nbt.NBT_SERVER_CLOSEST: "CLOSEST",
1673 nbt.NBT_SERVER_WRITABLE: "WRITABLE",
1674 nbt.NBT_SERVER_GOOD_TIMESERV: "GOOD_TIMESERV",
1675 nbt.NBT_SERVER_NDNC: "NDNC",
1676 nbt.NBT_SERVER_SELECT_SECRET_DOMAIN_6: "SELECT_SECRET_DOMAIN_6",
1677 nbt.NBT_SERVER_FULL_SECRET_DOMAIN_6: "FULL_SECRET_DOMAIN_6",
1678 nbt.NBT_SERVER_ADS_WEB_SERVICE: "ADS_WEB_SERVICE",
1679 nbt.NBT_SERVER_DS_8: "DS_8",
1680 nbt.NBT_SERVER_HAS_DNS_NAME: "HAS_DNS_NAME",
1681 nbt.NBT_SERVER_IS_DEFAULT_NC: "IS_DEFAULT_NC",
1682 nbt.NBT_SERVER_FOREST_ROOT: "FOREST_ROOT",
1684 server_type_string = self.generic_bitmap_to_string(flag_map,
1685 remote_info.server_type, names_only=True)
1686 self.outf.write("RemoteDC Netbios[%s] DNS[%s] ServerType[%s]\n" % (
1687 remote_info.pdc_name,
1688 remote_info.pdc_dns_name,
1689 server_type_string))
1691 self.remote_server = remote_info.pdc_dns_name
1692 self.remote_binding_string="ncacn_np:%s[%s]" % (self.remote_server, remote_binding_options)
1693 self.remote_creds = remote_creds
1694 return self.remote_server
1696 def new_remote_lsa_connection(self):
1697 return lsa.lsarpc(self.remote_binding_string, self.local_lp, self.remote_creds)
1699 def new_remote_netlogon_connection(self):
1700 return netlogon.netlogon(self.remote_binding_string, self.local_lp, self.remote_creds)
1702 def get_lsa_info(self, conn, policy_access):
1703 objectAttr = lsa.ObjectAttribute()
1704 objectAttr.sec_qos = lsa.QosInfo()
1706 policy = conn.OpenPolicy2(''.decode('utf-8'),
1707 objectAttr, policy_access)
1709 info = conn.QueryInfoPolicy2(policy, lsa.LSA_POLICY_INFO_DNS)
1711 return (policy, info)
1713 def get_netlogon_dc_info(self, conn, server):
1714 info = conn.netr_DsRGetDCNameEx2(server,
1715 None, 0, None, None, None,
1716 netlogon.DS_RETURN_DNS_NAME)
1719 def netr_DomainTrust_to_name(self, t):
1720 if t.trust_type == lsa.LSA_TRUST_TYPE_DOWNLEVEL:
1721 return t.netbios_name
1725 def netr_DomainTrust_to_type(self, a, t):
1727 primary_parent = None
1729 if _t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
1731 if not _t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1732 primary_parent = a[_t.parent_index]
1735 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1736 if t is primary_parent:
1739 if t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1742 parent = a[t.parent_index]
1743 if parent is primary:
1748 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1753 def netr_DomainTrust_to_transitive(self, t):
1754 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1757 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE:
1760 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1765 def netr_DomainTrust_to_direction(self, t):
1766 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND and \
1767 t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1770 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND:
1773 if t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1778 def generic_enum_to_string(self, e_dict, v, names_only=False):
1782 v32 = self._uint32(v)
1783 w = "__unknown__%08X__" % v32
1785 r = "0x%x (%s)" % (v, w)
1788 def generic_bitmap_to_string(self, b_dict, v, names_only=False):
1793 for b in sorted(b_dict.keys()):
1800 c32 = self._uint32(c)
1801 s += ["__unknown_%08X__" % c32]
1806 r = "0x%x (%s)" % (v, w)
1809 def trustType_string(self, v):
1811 lsa.LSA_TRUST_TYPE_DOWNLEVEL : "DOWNLEVEL",
1812 lsa.LSA_TRUST_TYPE_UPLEVEL : "UPLEVEL",
1813 lsa.LSA_TRUST_TYPE_MIT : "MIT",
1814 lsa.LSA_TRUST_TYPE_DCE : "DCE",
1816 return self.generic_enum_to_string(types, v)
1818 def trustDirection_string(self, v):
1820 lsa.LSA_TRUST_DIRECTION_INBOUND |
1821 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "BOTH",
1822 lsa.LSA_TRUST_DIRECTION_INBOUND : "INBOUND",
1823 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "OUTBOUND",
1825 return self.generic_enum_to_string(directions, v)
1827 def trustAttributes_string(self, v):
1829 lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE : "NON_TRANSITIVE",
1830 lsa.LSA_TRUST_ATTRIBUTE_UPLEVEL_ONLY : "UPLEVEL_ONLY",
1831 lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN : "QUARANTINED_DOMAIN",
1832 lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE : "FOREST_TRANSITIVE",
1833 lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION : "CROSS_ORGANIZATION",
1834 lsa.LSA_TRUST_ATTRIBUTE_WITHIN_FOREST : "WITHIN_FOREST",
1835 lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL : "TREAT_AS_EXTERNAL",
1836 lsa.LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION : "USES_RC4_ENCRYPTION",
1838 return self.generic_bitmap_to_string(attributes, v)
1840 def kerb_EncTypes_string(self, v):
1842 security.KERB_ENCTYPE_DES_CBC_CRC : "DES_CBC_CRC",
1843 security.KERB_ENCTYPE_DES_CBC_MD5 : "DES_CBC_MD5",
1844 security.KERB_ENCTYPE_RC4_HMAC_MD5 : "RC4_HMAC_MD5",
1845 security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96 : "AES128_CTS_HMAC_SHA1_96",
1846 security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96 : "AES256_CTS_HMAC_SHA1_96",
1847 security.KERB_ENCTYPE_FAST_SUPPORTED : "FAST_SUPPORTED",
1848 security.KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED : "COMPOUND_IDENTITY_SUPPORTED",
1849 security.KERB_ENCTYPE_CLAIMS_SUPPORTED : "CLAIMS_SUPPORTED",
1850 security.KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED : "RESOURCE_SID_COMPRESSION_DISABLED",
1852 return self.generic_bitmap_to_string(enctypes, v)
1854 def entry_tln_status(self, e_flags, ):
1856 return "Status[Enabled]"
1859 lsa.LSA_TLN_DISABLED_NEW : "Disabled-New",
1860 lsa.LSA_TLN_DISABLED_ADMIN : "Disabled",
1861 lsa.LSA_TLN_DISABLED_CONFLICT : "Disabled-Conflicting",
1863 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
1865 def entry_dom_status(self, e_flags):
1867 return "Status[Enabled]"
1870 lsa.LSA_SID_DISABLED_ADMIN : "Disabled-SID",
1871 lsa.LSA_SID_DISABLED_CONFLICT : "Disabled-SID-Conflicting",
1872 lsa.LSA_NB_DISABLED_ADMIN : "Disabled-NB",
1873 lsa.LSA_NB_DISABLED_CONFLICT : "Disabled-NB-Conflicting",
1875 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
1877 def write_forest_trust_info(self, fti, tln=None, collisions=None):
1879 tln_string = " TDO[%s]" % tln
1883 self.outf.write("Namespaces[%d]%s:\n" % (
1884 len(fti.entries), tln_string))
1886 for i in xrange(0, len(fti.entries)):
1890 collision_string = ""
1892 if collisions is not None:
1893 for c in collisions.entries:
1897 collision_string = " Collision[%s]" % (c.name.string)
1899 d = e.forest_trust_data
1900 if e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
1901 self.outf.write("TLN: %-32s DNS[*.%s]%s\n" % (
1902 self.entry_tln_status(flags),
1903 d.string, collision_string))
1904 elif e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
1905 self.outf.write("TLN_EX: %-29s DNS[*.%s]\n" % (
1907 elif e.type == lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
1908 self.outf.write("DOM: %-32s DNS[%s] Netbios[%s] SID[%s]%s\n" % (
1909 self.entry_dom_status(flags),
1910 d.dns_domain_name.string,
1911 d.netbios_domain_name.string,
1912 d.domain_sid, collision_string))
1915 class cmd_domain_trust_list(DomainTrustCommand):
1916 """List domain trusts."""
1918 synopsis = "%prog [options]"
1920 takes_optiongroups = {
1921 "sambaopts": options.SambaOptions,
1922 "versionopts": options.VersionOptions,
1923 "localdcopts": LocalDCCredentialsOptions,
1929 def run(self, sambaopts=None, versionopts=None, localdcopts=None):
1931 local_server = self.setup_local_server(sambaopts, localdcopts)
1933 local_netlogon = self.new_local_netlogon_connection()
1934 except RuntimeError as error:
1935 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
1938 local_netlogon_trusts = local_netlogon.netr_DsrEnumerateDomainTrusts(local_server,
1939 netlogon.NETR_TRUST_FLAG_IN_FOREST |
1940 netlogon.NETR_TRUST_FLAG_OUTBOUND |
1941 netlogon.NETR_TRUST_FLAG_INBOUND)
1942 except RuntimeError as error:
1943 if self.check_runtime_error(error, self.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
1944 # TODO: we could implement a fallback to lsa.EnumTrustDom()
1945 raise CommandError("LOCAL_DC[%s]: netr_DsrEnumerateDomainTrusts not supported." % (
1947 raise self.LocalRuntimeError(self, error, "netr_DsrEnumerateDomainTrusts failed")
1949 a = local_netlogon_trusts.array
1951 if t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
1953 self.outf.write("%-14s %-15s %-19s %s\n" % (
1954 "Type[%s]" % self.netr_DomainTrust_to_type(a, t),
1955 "Transitive[%s]" % self.netr_DomainTrust_to_transitive(t),
1956 "Direction[%s]" % self.netr_DomainTrust_to_direction(t),
1957 "Name[%s]" % self.netr_DomainTrust_to_name(t)))
1960 class cmd_domain_trust_show(DomainTrustCommand):
1961 """Show trusted domain details."""
1963 synopsis = "%prog NAME [options]"
1965 takes_optiongroups = {
1966 "sambaopts": options.SambaOptions,
1967 "versionopts": options.VersionOptions,
1968 "localdcopts": LocalDCCredentialsOptions,
1974 takes_args = ["domain"]
1976 def run(self, domain, sambaopts=None, versionopts=None, localdcopts=None):
1978 local_server = self.setup_local_server(sambaopts, localdcopts)
1980 local_lsa = self.new_local_lsa_connection()
1981 except RuntimeError as error:
1982 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
1985 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
1986 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
1987 except RuntimeError as error:
1988 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
1990 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
1991 local_lsa_info.name.string,
1992 local_lsa_info.dns_domain.string,
1993 local_lsa_info.sid))
1995 lsaString = lsa.String()
1996 lsaString.string = domain
1998 local_tdo_full = local_lsa.QueryTrustedDomainInfoByName(local_policy,
1999 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2000 local_tdo_info = local_tdo_full.info_ex
2001 local_tdo_posix = local_tdo_full.posix_offset
2002 except RuntimeError as error:
2003 if self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2004 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2006 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(FULL_INFO) failed")
2009 local_tdo_enctypes = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2010 lsaString, lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES)
2011 except RuntimeError as error:
2012 if self.check_runtime_error(error, self.NT_STATUS_INVALID_PARAMETER):
2014 if self.check_runtime_error(error, self.NT_STATUS_INVALID_INFO_CLASS):
2017 if error is not None:
2018 raise self.LocalRuntimeError(self, error,
2019 "QueryTrustedDomainInfoByName(SUPPORTED_ENCRYPTION_TYPES) failed")
2021 local_tdo_enctypes = lsa.TrustDomainInfoSupportedEncTypes()
2022 local_tdo_enctypes.enc_types = 0
2025 local_tdo_forest = None
2026 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2027 local_tdo_forest = local_lsa.lsaRQueryForestTrustInformation(local_policy,
2028 lsaString, lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
2029 except RuntimeError as error:
2030 if self.check_runtime_error(error, self.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
2032 if self.check_runtime_error(error, self.NT_STATUS_NOT_FOUND):
2034 if error is not None:
2035 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation failed")
2037 local_tdo_forest = lsa.ForestTrustInformation()
2038 local_tdo_forest.count = 0
2039 local_tdo_forest.entries = []
2041 self.outf.write("TrusteDomain:\n\n");
2042 self.outf.write("NetbiosName: %s\n" % local_tdo_info.netbios_name.string)
2043 if local_tdo_info.netbios_name.string != local_tdo_info.domain_name.string:
2044 self.outf.write("DnsName: %s\n" % local_tdo_info.domain_name.string)
2045 self.outf.write("SID: %s\n" % local_tdo_info.sid)
2046 self.outf.write("Type: %s\n" % self.trustType_string(local_tdo_info.trust_type))
2047 self.outf.write("Direction: %s\n" % self.trustDirection_string(local_tdo_info.trust_direction))
2048 self.outf.write("Attributes: %s\n" % self.trustAttributes_string(local_tdo_info.trust_attributes))
2049 posix_offset_u32 = ctypes.c_uint32(local_tdo_posix.posix_offset).value
2050 posix_offset_i32 = ctypes.c_int32(local_tdo_posix.posix_offset).value
2051 self.outf.write("PosixOffset: 0x%08X (%d)\n" % (posix_offset_u32, posix_offset_i32))
2052 self.outf.write("kerb_EncTypes: %s\n" % self.kerb_EncTypes_string(local_tdo_enctypes.enc_types))
2054 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2055 self.write_forest_trust_info(local_tdo_forest,
2056 tln=local_tdo_info.domain_name.string)
2060 class cmd_domain_trust_create(DomainTrustCommand):
2061 """Create a domain or forest trust."""
2063 synopsis = "%prog DOMAIN [options]"
2065 takes_optiongroups = {
2066 "sambaopts": options.SambaOptions,
2067 "versionopts": options.VersionOptions,
2068 "credopts": options.CredentialsOptions,
2069 "localdcopts": LocalDCCredentialsOptions,
2073 Option("--type", type="choice", metavar="TYPE",
2074 choices=["external", "forest"],
2075 help="The type of the trust: 'external' or 'forest'.",
2077 default="external"),
2078 Option("--direction", type="choice", metavar="DIRECTION",
2079 choices=["incoming", "outgoing", "both"],
2080 help="The trust direction: 'incoming', 'outgoing' or 'both'.",
2081 dest='trust_direction',
2083 Option("--create-location", type="choice", metavar="LOCATION",
2084 choices=["local", "both"],
2085 help="Where to create the trusted domain object: 'local' or 'both'.",
2086 dest='create_location',
2088 Option("--cross-organisation", action="store_true",
2089 help="The related domains does not belong to the same organisation.",
2090 dest='cross_organisation',
2092 Option("--quarantined", type="choice", metavar="yes|no",
2093 choices=["yes", "no", None],
2094 help="Special SID filtering rules are applied to the trust. "
2095 "With --type=external the default is yes. "
2096 "With --type=forest the default is no.",
2097 dest='quarantined_arg',
2099 Option("--not-transitive", action="store_true",
2100 help="The forest trust is not transitive.",
2101 dest='not_transitive',
2103 Option("--treat-as-external", action="store_true",
2104 help="The treat the forest trust as external.",
2105 dest='treat_as_external',
2107 Option("--no-aes-keys", action="store_false",
2108 help="The trust uses aes kerberos keys.",
2109 dest='use_aes_keys',
2111 Option("--skip-validation", action="store_false",
2112 help="Skip validation of the trust.",
2117 takes_args = ["domain"]
2119 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2120 trust_type=None, trust_direction=None, create_location=None,
2121 cross_organisation=False, quarantined_arg=None,
2122 not_transitive=False, treat_as_external=False,
2123 use_aes_keys=False, validate=True):
2125 lsaString = lsa.String()
2128 if quarantined_arg is None:
2129 if trust_type == 'external':
2131 elif quarantined_arg == 'yes':
2134 if trust_type != 'forest':
2136 raise CommandError("--not-transitive requires --type=forest")
2137 if treat_as_external:
2138 raise CommandError("--treat-as-external requires --type=forest")
2142 enc_types = lsa.TrustDomainInfoSupportedEncTypes()
2143 enc_types.enc_types = security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96
2144 enc_types.enc_types |= security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96
2146 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2147 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2148 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2150 local_trust_info = lsa.TrustDomainInfoInfoEx()
2151 local_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2152 local_trust_info.trust_direction = 0
2153 if trust_direction == "both":
2154 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2155 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2156 elif trust_direction == "incoming":
2157 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2158 elif trust_direction == "outgoing":
2159 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2160 local_trust_info.trust_attributes = 0
2161 if cross_organisation:
2162 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2164 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2165 if trust_type == "forest":
2166 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2168 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2169 if treat_as_external:
2170 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2172 def get_password(name):
2175 if password is not None and password is not '':
2177 password = getpass("New %s Password: " % name)
2178 passwordverify = getpass("Retype %s Password: " % name)
2179 if not password == passwordverify:
2181 self.outf.write("Sorry, passwords do not match.\n")
2183 def string_to_array(string):
2184 blob = [0] * len(string)
2186 for i in range(len(string)):
2187 blob[i] = ord(string[i])
2191 incoming_secret = None
2192 outgoing_secret = None
2193 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2194 if create_location == "local":
2195 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2196 incoming_password = get_password("Incoming Trust")
2197 incoming_secret = string_to_array(incoming_password.encode('utf-16-le'))
2198 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2199 outgoing_password = get_password("Outgoing Trust")
2200 outgoing_secret = string_to_array(outgoing_password.encode('utf-16-le'))
2202 remote_trust_info = None
2204 # We use 240 random bytes.
2205 # Windows uses 28 or 240 random bytes. I guess it's
2206 # based on the trust type external vs. forest.
2208 # The initial trust password can be up to 512 bytes
2209 # while the versioned passwords used for periodic updates
2210 # can only be up to 498 bytes, as netr_ServerPasswordSet2()
2211 # needs to pass the NL_PASSWORD_VERSION structure within the
2212 # 512 bytes and a 2 bytes confounder is required.
2214 def random_trust_secret(length, use_aes_keys=True):
2215 secret = [0] * length
2217 pw1 = samba.generate_random_password(length/2, length/2)
2218 if not use_aes_keys:
2219 # With arcfour-hmac-md5 we have to use valid utf16
2220 # in order to generate the correct pre-auth key
2221 # based on a utf8 password.
2223 # We can remove this once our client libraries
2224 # support using the correct NTHASH.
2225 return string_to_array(pw1.encode('utf-16-le'))
2227 # We mix characters from generate_random_password
2228 # with random numbers from random.randint()
2229 for i in range(len(secret)):
2231 secret[i] = ord(pw1[i])
2233 secret[i] = random.randint(0, 255)
2237 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2238 incoming_secret = random_trust_secret(240, use_aes_keys=use_aes_keys)
2239 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2240 outgoing_secret = random_trust_secret(240, use_aes_keys=use_aes_keys)
2242 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2243 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2245 remote_trust_info = lsa.TrustDomainInfoInfoEx()
2246 remote_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2247 remote_trust_info.trust_direction = 0
2248 if trust_direction == "both":
2249 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2250 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2251 elif trust_direction == "incoming":
2252 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2253 elif trust_direction == "outgoing":
2254 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2255 remote_trust_info.trust_attributes = 0
2256 if cross_organisation:
2257 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2259 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2260 if trust_type == "forest":
2261 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2263 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2264 if treat_as_external:
2265 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2267 local_server = self.setup_local_server(sambaopts, localdcopts)
2269 local_lsa = self.new_local_lsa_connection()
2270 except RuntimeError as error:
2271 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2274 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2275 except RuntimeError as error:
2276 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2278 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2279 local_lsa_info.name.string,
2280 local_lsa_info.dns_domain.string,
2281 local_lsa_info.sid))
2284 remote_server = self.setup_remote_server(credopts, domain)
2285 except RuntimeError as error:
2286 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2289 remote_lsa = self.new_remote_lsa_connection()
2290 except RuntimeError as error:
2291 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2294 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2295 except RuntimeError as error:
2296 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2298 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2299 remote_lsa_info.name.string,
2300 remote_lsa_info.dns_domain.string,
2301 remote_lsa_info.sid))
2303 local_trust_info.domain_name.string = remote_lsa_info.dns_domain.string
2304 local_trust_info.netbios_name.string = remote_lsa_info.name.string
2305 local_trust_info.sid = remote_lsa_info.sid
2307 if remote_trust_info:
2308 remote_trust_info.domain_name.string = local_lsa_info.dns_domain.string
2309 remote_trust_info.netbios_name.string = local_lsa_info.name.string
2310 remote_trust_info.sid = local_lsa_info.sid
2313 lsaString.string = local_trust_info.domain_name.string
2314 local_old_netbios = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2315 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2316 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2317 except RuntimeError as error:
2318 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2319 raise self.LocalRuntimeError(self, error,
2320 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2324 lsaString.string = local_trust_info.netbios_name.string
2325 local_old_dns = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2326 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2327 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2328 except RuntimeError as error:
2329 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2330 raise self.LocalRuntimeError(self, error,
2331 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2334 if remote_trust_info:
2336 lsaString.string = remote_trust_info.domain_name.string
2337 remote_old_netbios = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2338 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2339 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2340 except RuntimeError as error:
2341 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2342 raise self.RemoteRuntimeError(self, error,
2343 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2347 lsaString.string = remote_trust_info.netbios_name.string
2348 remote_old_dns = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2349 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2350 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2351 except RuntimeError as error:
2352 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2353 raise self.RemoteRuntimeError(self, error,
2354 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2358 local_netlogon = self.new_local_netlogon_connection()
2359 except RuntimeError as error:
2360 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2363 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
2364 except RuntimeError as error:
2365 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
2367 if remote_trust_info:
2369 remote_netlogon = self.new_remote_netlogon_connection()
2370 except RuntimeError as error:
2371 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2374 remote_netlogon_info = self.get_netlogon_dc_info(remote_netlogon, remote_server)
2375 except RuntimeError as error:
2376 raise self.RemoteRuntimeError(self, error, "failed to get netlogon dc info")
2378 def arcfour_encrypt(key, data):
2379 from Crypto.Cipher import ARC4
2381 return c.encrypt(data)
2383 def generate_AuthInOutBlob(secret, update_time):
2385 blob = drsblobs.trustAuthInOutBlob()
2390 clear = drsblobs.AuthInfoClear()
2391 clear.size = len(secret)
2392 clear.password = secret
2394 info = drsblobs.AuthenticationInformation()
2395 info.LastUpdateTime = samba.unix2nttime(update_time)
2396 info.AuthType = lsa.TRUST_AUTH_TYPE_CLEAR
2397 info.AuthInfo = clear
2399 array = drsblobs.AuthenticationInformationArray()
2401 array.array = [info]
2403 blob = drsblobs.trustAuthInOutBlob()
2405 blob.current = array
2409 def generate_AuthInfoInternal(session_key, incoming=None, outgoing=None):
2410 confounder = [0] * 512
2411 for i in range(len(confounder)):
2412 confounder[i] = random.randint(0, 255)
2414 trustpass = drsblobs.trustDomainPasswords()
2416 trustpass.confounder = confounder
2417 trustpass.outgoing = outgoing
2418 trustpass.incoming = incoming
2420 trustpass_blob = ndr_pack(trustpass)
2422 encrypted_trustpass = arcfour_encrypt(session_key, trustpass_blob)
2424 auth_blob = lsa.DATA_BUF2()
2425 auth_blob.size = len(encrypted_trustpass)
2426 auth_blob.data = string_to_array(encrypted_trustpass)
2428 auth_info = lsa.TrustDomainInfoAuthInfoInternal()
2429 auth_info.auth_blob = auth_blob
2433 update_time = samba.current_unix_time()
2434 incoming_blob = generate_AuthInOutBlob(incoming_secret, update_time)
2435 outgoing_blob = generate_AuthInOutBlob(outgoing_secret, update_time)
2437 local_tdo_handle = None
2438 remote_tdo_handle = None
2440 local_auth_info = generate_AuthInfoInternal(local_lsa.session_key,
2441 incoming=incoming_blob,
2442 outgoing=outgoing_blob)
2443 if remote_trust_info:
2444 remote_auth_info = generate_AuthInfoInternal(remote_lsa.session_key,
2445 incoming=outgoing_blob,
2446 outgoing=incoming_blob)
2449 if remote_trust_info:
2450 self.outf.write("Creating remote TDO.\n")
2451 current_request = { "location": "remote", "name": "CreateTrustedDomainEx2"}
2452 remote_tdo_handle = remote_lsa.CreateTrustedDomainEx2(remote_policy,
2455 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2456 self.outf.write("Remote TDO created.\n")
2458 self.outf.write("Setting supported encryption types on remote TDO.\n")
2459 current_request = { "location": "remote", "name": "SetInformationTrustedDomain"}
2460 remote_lsa.SetInformationTrustedDomain(remote_tdo_handle,
2461 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2464 self.outf.write("Creating local TDO.\n")
2465 current_request = { "location": "local", "name": "CreateTrustedDomainEx2"}
2466 local_tdo_handle = local_lsa.CreateTrustedDomainEx2(local_policy,
2469 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2470 self.outf.write("Local TDO created\n")
2472 self.outf.write("Setting supported encryption types on local TDO.\n")
2473 current_request = { "location": "local", "name": "SetInformationTrustedDomain"}
2474 local_lsa.SetInformationTrustedDomain(local_tdo_handle,
2475 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2477 except RuntimeError as error:
2478 self.outf.write("Error: %s failed %sly - cleaning up\n" % (
2479 current_request['name'], current_request['location']))
2480 if remote_tdo_handle:
2481 self.outf.write("Deleting remote TDO.\n")
2482 remote_lsa.DeleteObject(remote_tdo_handle)
2483 remote_tdo_handle = None
2484 if local_tdo_handle:
2485 self.outf.write("Deleting local TDO.\n")
2486 local_lsa.DeleteObject(local_tdo_handle)
2487 local_tdo_handle = None
2488 if current_request['location'] is "remote":
2489 raise self.RemoteRuntimeError(self, error, "%s" % (
2490 current_request['name']))
2491 raise self.LocalRuntimeError(self, error, "%s" % (
2492 current_request['name']))
2495 if local_trust_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2496 self.outf.write("Setup local forest trust information...\n")
2498 # get all information about the remote trust
2499 # this triggers netr_GetForestTrustInformation to the remote domain
2500 # and lsaRSetForestTrustInformation() locally, but new top level
2501 # names are disabled by default.
2502 local_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
2503 remote_lsa_info.dns_domain.string,
2504 netlogon.DS_GFTI_UPDATE_TDO)
2505 except RuntimeError as error:
2506 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2509 # here we try to enable all top level names
2510 local_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
2511 remote_lsa_info.dns_domain,
2512 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2515 except RuntimeError as error:
2516 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2518 self.write_forest_trust_info(local_forest_info,
2519 tln=remote_lsa_info.dns_domain.string,
2520 collisions=local_forest_collision)
2522 if remote_trust_info:
2523 self.outf.write("Setup remote forest trust information...\n")
2525 # get all information about the local trust (from the perspective of the remote domain)
2526 # this triggers netr_GetForestTrustInformation to our domain.
2527 # and lsaRSetForestTrustInformation() remotely, but new top level
2528 # names are disabled by default.
2529 remote_forest_info = remote_netlogon.netr_DsRGetForestTrustInformation(remote_netlogon_info.dc_unc,
2530 local_lsa_info.dns_domain.string,
2531 netlogon.DS_GFTI_UPDATE_TDO)
2532 except RuntimeError as error:
2533 raise self.RemoteRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2536 # here we try to enable all top level names
2537 remote_forest_collision = remote_lsa.lsaRSetForestTrustInformation(remote_policy,
2538 local_lsa_info.dns_domain,
2539 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2542 except RuntimeError as error:
2543 raise self.RemoteRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2545 self.write_forest_trust_info(remote_forest_info,
2546 tln=local_lsa_info.dns_domain.string,
2547 collisions=remote_forest_collision)
2549 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2550 self.outf.write("Validating outgoing trust...\n")
2552 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_netlogon_info.dc_unc,
2553 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2555 remote_lsa_info.dns_domain.string)
2556 except RuntimeError as error:
2557 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2559 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2560 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2562 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2563 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2564 local_trust_verify.trusted_dc_name,
2565 local_trust_verify.tc_connection_status[1],
2566 local_trust_verify.pdc_connection_status[1])
2568 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2569 local_trust_verify.trusted_dc_name,
2570 local_trust_verify.tc_connection_status[1],
2571 local_trust_verify.pdc_connection_status[1])
2573 if local_trust_status != self.WERR_OK or local_conn_status != self.WERR_OK:
2574 raise CommandError(local_validation)
2576 self.outf.write("OK: %s\n" % local_validation)
2578 if remote_trust_info:
2579 if remote_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2580 self.outf.write("Validating incoming trust...\n")
2582 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_netlogon_info.dc_unc,
2583 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2585 local_lsa_info.dns_domain.string)
2586 except RuntimeError as error:
2587 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2589 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2590 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2592 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2593 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2594 remote_trust_verify.trusted_dc_name,
2595 remote_trust_verify.tc_connection_status[1],
2596 remote_trust_verify.pdc_connection_status[1])
2598 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2599 remote_trust_verify.trusted_dc_name,
2600 remote_trust_verify.tc_connection_status[1],
2601 remote_trust_verify.pdc_connection_status[1])
2603 if remote_trust_status != self.WERR_OK or remote_conn_status != self.WERR_OK:
2604 raise CommandError(remote_validation)
2606 self.outf.write("OK: %s\n" % remote_validation)
2608 if remote_tdo_handle is not None:
2610 remote_lsa.Close(remote_tdo_handle)
2611 except RuntimeError as error:
2613 remote_tdo_handle = None
2614 if local_tdo_handle is not None:
2616 local_lsa.Close(local_tdo_handle)
2617 except RuntimeError as error:
2619 local_tdo_handle = None
2621 self.outf.write("Success.\n")
2624 class cmd_domain_trust_delete(DomainTrustCommand):
2625 """Delete a domain trust."""
2627 synopsis = "%prog DOMAIN [options]"
2629 takes_optiongroups = {
2630 "sambaopts": options.SambaOptions,
2631 "versionopts": options.VersionOptions,
2632 "credopts": options.CredentialsOptions,
2633 "localdcopts": LocalDCCredentialsOptions,
2637 Option("--delete-location", type="choice", metavar="LOCATION",
2638 choices=["local", "both"],
2639 help="Where to delete the trusted domain object: 'local' or 'both'.",
2640 dest='delete_location',
2644 takes_args = ["domain"]
2646 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2647 delete_location=None):
2649 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2650 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2651 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2653 if delete_location == "local":
2654 remote_policy_access = None
2656 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2657 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2658 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2660 local_server = self.setup_local_server(sambaopts, localdcopts)
2662 local_lsa = self.new_local_lsa_connection()
2663 except RuntimeError as error:
2664 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2667 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2668 except RuntimeError as error:
2669 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2671 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2672 local_lsa_info.name.string,
2673 local_lsa_info.dns_domain.string,
2674 local_lsa_info.sid))
2676 local_tdo_info = None
2677 local_tdo_handle = None
2678 remote_tdo_info = None
2679 remote_tdo_handle = None
2681 lsaString = lsa.String()
2683 lsaString.string = domain
2684 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2685 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2686 except RuntimeError as error:
2687 if self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2688 raise CommandError("Failed to find trust for domain '%s'" % domain)
2689 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2692 if remote_policy_access is not None:
2694 remote_server = self.setup_remote_server(credopts, domain)
2695 except RuntimeError as error:
2696 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2699 remote_lsa = self.new_remote_lsa_connection()
2700 except RuntimeError as error:
2701 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2704 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2705 except RuntimeError as error:
2706 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2708 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2709 remote_lsa_info.name.string,
2710 remote_lsa_info.dns_domain.string,
2711 remote_lsa_info.sid))
2713 if remote_lsa_info.sid != local_tdo_info.sid or \
2714 remote_lsa_info.name.string != local_tdo_info.netbios_name.string or \
2715 remote_lsa_info.dns_domain.string != local_tdo_info.domain_name.string:
2716 raise CommandError("LocalTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2717 local_tdo_info.netbios_name.string,
2718 local_tdo_info.domain_name.string,
2719 local_tdo_info.sid))
2722 lsaString.string = local_lsa_info.dns_domain.string
2723 remote_tdo_info = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2724 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2725 except RuntimeError as error:
2726 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2727 raise self.RemoteRuntimeError(self, error, "QueryTrustedDomainInfoByName(%s)" % (
2731 if remote_tdo_info is not None:
2732 if local_lsa_info.sid != remote_tdo_info.sid or \
2733 local_lsa_info.name.string != remote_tdo_info.netbios_name.string or \
2734 local_lsa_info.dns_domain.string != remote_tdo_info.domain_name.string:
2735 raise CommandError("RemoteTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2736 remote_tdo_info.netbios_name.string,
2737 remote_tdo_info.domain_name.string,
2738 remote_tdo_info.sid))
2740 if local_tdo_info is not None:
2742 lsaString.string = local_tdo_info.domain_name.string
2743 local_tdo_handle = local_lsa.OpenTrustedDomainByName(local_policy,
2745 security.SEC_STD_DELETE)
2746 except RuntimeError as error:
2747 raise self.LocalRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2750 local_lsa.DeleteObject(local_tdo_handle)
2751 local_tdo_handle = None
2753 if remote_tdo_info is not None:
2755 lsaString.string = remote_tdo_info.domain_name.string
2756 remote_tdo_handle = remote_lsa.OpenTrustedDomainByName(remote_policy,
2758 security.SEC_STD_DELETE)
2759 except RuntimeError as error:
2760 raise self.RemoteRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2763 if remote_tdo_handle is not None:
2765 remote_lsa.DeleteObject(remote_tdo_handle)
2766 remote_tdo_handle = None
2767 self.outf.write("RemoteTDO deleted.\n")
2768 except RuntimeError as error:
2769 self.outf.write("%s\n" % self.RemoteRuntimeError(self, error, "DeleteObject() failed"))
2771 if local_tdo_handle is not None:
2773 local_lsa.DeleteObject(local_tdo_handle)
2774 local_tdo_handle = None
2775 self.outf.write("LocalTDO deleted.\n")
2776 except RuntimeError as error:
2777 self.outf.write("%s\n" % self.LocalRuntimeError(self, error, "DeleteObject() failed"))
2781 class cmd_domain_trust_validate(DomainTrustCommand):
2782 """Validate a domain trust."""
2784 synopsis = "%prog DOMAIN [options]"
2786 takes_optiongroups = {
2787 "sambaopts": options.SambaOptions,
2788 "versionopts": options.VersionOptions,
2789 "credopts": options.CredentialsOptions,
2790 "localdcopts": LocalDCCredentialsOptions,
2794 Option("--validate-location", type="choice", metavar="LOCATION",
2795 choices=["local", "both"],
2796 help="Where to validate the trusted domain object: 'local' or 'both'.",
2797 dest='validate_location',
2801 takes_args = ["domain"]
2803 def run(self, domain, sambaopts=None, versionopts=None, credopts=None, localdcopts=None,
2804 validate_location=None):
2806 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2808 local_server = self.setup_local_server(sambaopts, localdcopts)
2810 local_lsa = self.new_local_lsa_connection()
2811 except RuntimeError as error:
2812 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2815 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2816 except RuntimeError as error:
2817 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2819 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2820 local_lsa_info.name.string,
2821 local_lsa_info.dns_domain.string,
2822 local_lsa_info.sid))
2825 lsaString = lsa.String()
2826 lsaString.string = domain
2827 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2828 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2829 except RuntimeError as error:
2830 if self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2831 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2833 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
2835 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
2836 local_tdo_info.netbios_name.string,
2837 local_tdo_info.domain_name.string,
2838 local_tdo_info.sid))
2841 local_netlogon = self.new_local_netlogon_connection()
2842 except RuntimeError as error:
2843 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2846 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_server,
2847 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2849 local_tdo_info.domain_name.string)
2850 except RuntimeError as error:
2851 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2853 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2854 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2856 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2857 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2858 local_trust_verify.trusted_dc_name,
2859 local_trust_verify.tc_connection_status[1],
2860 local_trust_verify.pdc_connection_status[1])
2862 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2863 local_trust_verify.trusted_dc_name,
2864 local_trust_verify.tc_connection_status[1],
2865 local_trust_verify.pdc_connection_status[1])
2867 if local_trust_status != self.WERR_OK or local_conn_status != self.WERR_OK:
2868 raise CommandError(local_validation)
2870 self.outf.write("OK: %s\n" % local_validation)
2873 server = local_trust_verify.trusted_dc_name.replace('\\', '')
2874 domain_and_server = "%s\\%s" % (local_tdo_info.domain_name.string, server)
2875 local_trust_rediscover = local_netlogon.netr_LogonControl2Ex(local_server,
2876 netlogon.NETLOGON_CONTROL_REDISCOVER,
2879 except RuntimeError as error:
2880 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
2882 local_conn_status = self._uint32(local_trust_rediscover.tc_connection_status[0])
2883 local_rediscover = "LocalRediscover: DC[%s] CONNECTION[%s]" % (
2884 local_trust_rediscover.trusted_dc_name,
2885 local_trust_rediscover.tc_connection_status[1])
2887 if local_conn_status != self.WERR_OK:
2888 raise CommandError(local_rediscover)
2890 self.outf.write("OK: %s\n" % local_rediscover)
2892 if validate_location != "local":
2894 remote_server = self.setup_remote_server(credopts, domain, require_pdc=False)
2895 except RuntimeError as error:
2896 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2899 remote_netlogon = self.new_remote_netlogon_connection()
2900 except RuntimeError as error:
2901 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2904 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_server,
2905 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2907 local_lsa_info.dns_domain.string)
2908 except RuntimeError as error:
2909 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2911 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2912 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2914 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2915 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2916 remote_trust_verify.trusted_dc_name,
2917 remote_trust_verify.tc_connection_status[1],
2918 remote_trust_verify.pdc_connection_status[1])
2920 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2921 remote_trust_verify.trusted_dc_name,
2922 remote_trust_verify.tc_connection_status[1],
2923 remote_trust_verify.pdc_connection_status[1])
2925 if remote_trust_status != self.WERR_OK or remote_conn_status != self.WERR_OK:
2926 raise CommandError(remote_validation)
2928 self.outf.write("OK: %s\n" % remote_validation)
2931 server = remote_trust_verify.trusted_dc_name.replace('\\', '')
2932 domain_and_server = "%s\\%s" % (local_lsa_info.dns_domain.string, server)
2933 remote_trust_rediscover = remote_netlogon.netr_LogonControl2Ex(remote_server,
2934 netlogon.NETLOGON_CONTROL_REDISCOVER,
2937 except RuntimeError as error:
2938 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
2940 remote_conn_status = self._uint32(remote_trust_rediscover.tc_connection_status[0])
2942 remote_rediscover = "RemoteRediscover: DC[%s] CONNECTION[%s]" % (
2943 remote_trust_rediscover.trusted_dc_name,
2944 remote_trust_rediscover.tc_connection_status[1])
2946 if remote_conn_status != self.WERR_OK:
2947 raise CommandError(remote_rediscover)
2949 self.outf.write("OK: %s\n" % remote_rediscover)
2953 class cmd_domain_trust_namespaces(DomainTrustCommand):
2954 """Manage forest trust namespaces."""
2956 synopsis = "%prog [DOMAIN] [options]"
2958 takes_optiongroups = {
2959 "sambaopts": options.SambaOptions,
2960 "versionopts": options.VersionOptions,
2961 "localdcopts": LocalDCCredentialsOptions,
2965 Option("--refresh", type="choice", metavar="check|store",
2966 choices=["check", "store", None],
2967 help="List and maybe store refreshed forest trust information: 'check' or 'store'.",
2970 Option("--enable-all", action="store_true",
2971 help="Try to update disabled entries, not allowed with --refresh=check.",
2974 Option("--enable-tln", action="append", metavar='DNSDOMAIN',
2975 help="Enable a top level name entry. Can be specified multiple times.",
2978 Option("--disable-tln", action="append", metavar='DNSDOMAIN',
2979 help="Disable a top level name entry. Can be specified multiple times.",
2982 Option("--add-tln-ex", action="append", metavar='DNSDOMAIN',
2983 help="Add a top level exclusion entry. Can be specified multiple times.",
2986 Option("--delete-tln-ex", action="append", metavar='DNSDOMAIN',
2987 help="Delete a top level exclusion entry. Can be specified multiple times.",
2988 dest='delete_tln_ex',
2990 Option("--enable-nb", action="append", metavar='NETBIOSDOMAIN',
2991 help="Enable a netbios name in a domain entry. Can be specified multiple times.",
2994 Option("--disable-nb", action="append", metavar='NETBIOSDOMAIN',
2995 help="Disable a netbios name in a domain entry. Can be specified multiple times.",
2998 Option("--enable-sid", action="append", metavar='DOMAINSID',
2999 help="Enable a SID in a domain entry. Can be specified multiple times.",
3000 dest='enable_sid_str',
3002 Option("--disable-sid", action="append", metavar='DOMAINSID',
3003 help="Disable a SID in a domain entry. Can be specified multiple times.",
3004 dest='disable_sid_str',
3006 Option("--add-upn-suffix", action="append", metavar='DNSDOMAIN',
3007 help="Add a new uPNSuffixes attribute for the local forest. Can be specified multiple times.",
3010 Option("--delete-upn-suffix", action="append", metavar='DNSDOMAIN',
3011 help="Delete an existing uPNSuffixes attribute of the local forest. Can be specified multiple times.",
3014 Option("--add-spn-suffix", action="append", metavar='DNSDOMAIN',
3015 help="Add a new msDS-SPNSuffixes attribute for the local forest. Can be specified multiple times.",
3018 Option("--delete-spn-suffix", action="append", metavar='DNSDOMAIN',
3019 help="Delete an existing msDS-SPNSuffixes attribute of the local forest. Can be specified multiple times.",
3024 takes_args = ["domain?"]
3026 def run(self, domain=None, sambaopts=None, localdcopts=None, versionopts=None,
3027 refresh=None, enable_all=False,
3028 enable_tln=[], disable_tln=[], add_tln_ex=[], delete_tln_ex=[],
3029 enable_sid_str=[], disable_sid_str=[], enable_nb=[], disable_nb=[],
3030 add_upn=[], delete_upn=[], add_spn=[], delete_spn=[]):
3032 require_update = False
3035 if refresh == "store":
3036 raise CommandError("--refresh=%s not allowed without DOMAIN" % refresh)
3039 raise CommandError("--enable-all not allowed without DOMAIN")
3041 if len(enable_tln) > 0:
3042 raise CommandError("--enable-tln not allowed without DOMAIN")
3043 if len(disable_tln) > 0:
3044 raise CommandError("--disable-tln not allowed without DOMAIN")
3046 if len(add_tln_ex) > 0:
3047 raise CommandError("--add-tln-ex not allowed without DOMAIN")
3048 if len(delete_tln_ex) > 0:
3049 raise CommandError("--delete-tln-ex not allowed without DOMAIN")
3051 if len(enable_nb) > 0:
3052 raise CommandError("--enable-nb not allowed without DOMAIN")
3053 if len(disable_nb) > 0:
3054 raise CommandError("--disable-nb not allowed without DOMAIN")
3056 if len(enable_sid_str) > 0:
3057 raise CommandError("--enable-sid not allowed without DOMAIN")
3058 if len(disable_sid_str) > 0:
3059 raise CommandError("--disable-sid not allowed without DOMAIN")
3061 if len(add_upn) > 0:
3063 if not n.startswith("*."):
3065 raise CommandError("value[%s] specified for --add-upn-suffix should not include with '*.'" % n)
3066 require_update = True
3067 if len(delete_upn) > 0:
3068 for n in delete_upn:
3069 if not n.startswith("*."):
3071 raise CommandError("value[%s] specified for --delete-upn-suffix should not include with '*.'" % n)
3072 require_update = True
3074 for d in delete_upn:
3075 if a.lower() != d.lower():
3077 raise CommandError("value[%s] specified for --add-upn-suffix and --delete-upn-suffix" % a)
3079 if len(add_spn) > 0:
3081 if not n.startswith("*."):
3083 raise CommandError("value[%s] specified for --add-spn-suffix should not include with '*.'" % n)
3084 require_update = True
3085 if len(delete_spn) > 0:
3086 for n in delete_spn:
3087 if not n.startswith("*."):
3089 raise CommandError("value[%s] specified for --delete-spn-suffix should not include with '*.'" % n)
3090 require_update = True
3092 for d in delete_spn:
3093 if a.lower() != d.lower():
3095 raise CommandError("value[%s] specified for --add-spn-suffix and --delete-spn-suffix" % a)
3097 if len(add_upn) > 0:
3098 raise CommandError("--add-upn-suffix not allowed together with DOMAIN")
3099 if len(delete_upn) > 0:
3100 raise CommandError("--delete-upn-suffix not allowed together with DOMAIN")
3101 if len(add_spn) > 0:
3102 raise CommandError("--add-spn-suffix not allowed together with DOMAIN")
3103 if len(delete_spn) > 0:
3104 raise CommandError("--delete-spn-suffix not allowed together with DOMAIN")
3106 if refresh is not None:
3107 if refresh == "store":
3108 require_update = True
3110 if enable_all and refresh != "store":
3111 raise CommandError("--enable-all not allowed together with --refresh=%s" % refresh)
3113 if len(enable_tln) > 0:
3114 raise CommandError("--enable-tln not allowed together with --refresh")
3115 if len(disable_tln) > 0:
3116 raise CommandError("--disable-tln not allowed together with --refresh")
3118 if len(add_tln_ex) > 0:
3119 raise CommandError("--add-tln-ex not allowed together with --refresh")
3120 if len(delete_tln_ex) > 0:
3121 raise CommandError("--delete-tln-ex not allowed together with --refresh")
3123 if len(enable_nb) > 0:
3124 raise CommandError("--enable-nb not allowed together with --refresh")
3125 if len(disable_nb) > 0:
3126 raise CommandError("--disable-nb not allowed together with --refresh")
3128 if len(enable_sid_str) > 0:
3129 raise CommandError("--enable-sid not allowed together with --refresh")
3130 if len(disable_sid_str) > 0:
3131 raise CommandError("--disable-sid not allowed together with --refresh")
3134 require_update = True
3136 if len(enable_tln) > 0:
3137 raise CommandError("--enable-tln not allowed together with --enable-all")
3139 if len(enable_nb) > 0:
3140 raise CommandError("--enable-nb not allowed together with --enable-all")
3142 if len(enable_sid) > 0:
3143 raise CommandError("--enable-sid not allowed together with --enable-all")
3145 if len(enable_tln) > 0:
3146 require_update = True
3147 if len(disable_tln) > 0:
3148 require_update = True
3149 for e in enable_tln:
3150 for d in disable_tln:
3151 if e.lower() != d.lower():
3153 raise CommandError("value[%s] specified for --enable-tln and --disable-tln" % e)
3155 if len(add_tln_ex) > 0:
3156 for n in add_tln_ex:
3157 if not n.startswith("*."):
3159 raise CommandError("value[%s] specified for --add-tln-ex should not include with '*.'" % n)
3160 require_update = True
3161 if len(delete_tln_ex) > 0:
3162 for n in delete_tln_ex:
3163 if not n.startswith("*."):
3165 raise CommandError("value[%s] specified for --delete-tln-ex should not include with '*.'" % n)
3166 require_update = True
3167 for a in add_tln_ex:
3168 for d in delete_tln_ex:
3169 if a.lower() != d.lower():
3171 raise CommandError("value[%s] specified for --add-tln-ex and --delete-tln-ex" % a)
3173 if len(enable_nb) > 0:
3174 require_update = True
3175 if len(disable_nb) > 0:
3176 require_update = True
3178 for d in disable_nb:
3179 if e.upper() != d.upper():
3181 raise CommandError("value[%s] specified for --enable-nb and --disable-nb" % e)
3184 for s in enable_sid_str:
3186 sid = security.dom_sid(s)
3187 except TypeError as error:
3188 raise CommandError("value[%s] specified for --enable-sid is not a valid SID" % s)
3189 enable_sid.append(sid)
3191 for s in disable_sid_str:
3193 sid = security.dom_sid(s)
3194 except TypeError as error:
3195 raise CommandError("value[%s] specified for --disable-sid is not a valid SID" % s)
3196 disable_sid.append(sid)
3197 if len(enable_sid) > 0:
3198 require_update = True
3199 if len(disable_sid) > 0:
3200 require_update = True
3201 for e in enable_sid:
3202 for d in disable_sid:
3205 raise CommandError("value[%s] specified for --enable-sid and --disable-sid" % e)
3207 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
3209 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
3211 local_server = self.setup_local_server(sambaopts, localdcopts)
3213 local_lsa = self.new_local_lsa_connection()
3214 except RuntimeError as error:
3215 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
3218 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
3219 except RuntimeError as error:
3220 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
3222 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
3223 local_lsa_info.name.string,
3224 local_lsa_info.dns_domain.string,
3225 local_lsa_info.sid))
3229 local_netlogon = self.new_local_netlogon_connection()
3230 except RuntimeError as error:
3231 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3234 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3235 except RuntimeError as error:
3236 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3238 if local_netlogon_info.domain_name != local_netlogon_info.forest_name:
3239 raise CommandError("The local domain [%s] is not the forest root [%s]" % (
3240 local_netlogon_info.domain_name,
3241 local_netlogon_info.forest_name))
3244 # get all information about our own forest
3245 own_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3247 except RuntimeError as error:
3248 if self.check_runtime_error(error, self.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
3249 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3252 if self.check_runtime_error(error, self.WERR_INVALID_FUNCTION):
3253 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3256 if self.check_runtime_error(error, self.WERR_NERR_ACFNOTLOADED):
3257 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3260 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3262 self.outf.write("Own forest trust information...\n")
3263 self.write_forest_trust_info(own_forest_info,
3264 tln=local_lsa_info.dns_domain.string)
3267 local_samdb = self.new_local_ldap_connection()
3268 except RuntimeError as error:
3269 raise self.LocalRuntimeError(self, error, "failed to connect to SamDB")
3271 local_partitions_dn = "CN=Partitions,%s" % str(local_samdb.get_config_basedn())
3272 attrs = ['uPNSuffixes', 'msDS-SPNSuffixes']
3274 msgs = local_samdb.search(base=local_partitions_dn,
3275 scope=ldb.SCOPE_BASE,
3276 expression="(objectClass=crossRefContainer)",
3278 stored_msg = msgs[0]
3279 except ldb.LdbError as error:
3280 raise self.LocalLdbError(self, error, "failed to search partition dn")
3282 stored_upn_vals = []
3283 if 'uPNSuffixes' in stored_msg:
3284 stored_upn_vals.extend(stored_msg['uPNSuffixes'])
3286 stored_spn_vals = []
3287 if 'msDS-SPNSuffixes' in stored_msg:
3288 stored_spn_vals.extend(stored_msg['msDS-SPNSuffixes'])
3290 self.outf.write("Stored uPNSuffixes attributes[%d]:\n" % len(stored_upn_vals))
3291 for v in stored_upn_vals:
3292 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3293 self.outf.write("Stored msDS-SPNSuffixes attributes[%d]:\n" % len(stored_spn_vals))
3294 for v in stored_spn_vals:
3295 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3297 if not require_update:
3301 update_upn_vals = []
3302 update_upn_vals.extend(stored_upn_vals)
3305 update_spn_vals = []
3306 update_spn_vals.extend(stored_spn_vals)
3310 for i in xrange(0, len(update_upn_vals)):
3311 v = update_upn_vals[i]
3312 if v.lower() != upn.lower():
3317 raise CommandError("Entry already present for value[%s] specified for --add-upn-suffix" % upn)
3318 update_upn_vals.append(upn)
3321 for upn in delete_upn:
3323 for i in xrange(0, len(update_upn_vals)):
3324 v = update_upn_vals[i]
3325 if v.lower() != upn.lower():
3330 raise CommandError("Entry not found for value[%s] specified for --delete-upn-suffix" % upn)
3332 update_upn_vals.pop(idx)
3337 for i in xrange(0, len(update_spn_vals)):
3338 v = update_spn_vals[i]
3339 if v.lower() != spn.lower():
3344 raise CommandError("Entry already present for value[%s] specified for --add-spn-suffix" % spn)
3345 update_spn_vals.append(spn)
3348 for spn in delete_spn:
3350 for i in xrange(0, len(update_spn_vals)):
3351 v = update_spn_vals[i]
3352 if v.lower() != spn.lower():
3357 raise CommandError("Entry not found for value[%s] specified for --delete-spn-suffix" % spn)
3359 update_spn_vals.pop(idx)
3362 self.outf.write("Update uPNSuffixes attributes[%d]:\n" % len(update_upn_vals))
3363 for v in update_upn_vals:
3364 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3365 self.outf.write("Update msDS-SPNSuffixes attributes[%d]:\n" % len(update_spn_vals))
3366 for v in update_spn_vals:
3367 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3369 update_msg = ldb.Message()
3370 update_msg.dn = stored_msg.dn
3373 update_msg['uPNSuffixes'] = ldb.MessageElement(update_upn_vals,
3374 ldb.FLAG_MOD_REPLACE,
3377 update_msg['msDS-SPNSuffixes'] = ldb.MessageElement(update_spn_vals,
3378 ldb.FLAG_MOD_REPLACE,
3381 local_samdb.modify(update_msg)
3382 except ldb.LdbError as error:
3383 raise self.LocalLdbError(self, error, "failed to update partition dn")
3386 stored_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3388 except RuntimeError as error:
3389 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3391 self.outf.write("Stored forest trust information...\n")
3392 self.write_forest_trust_info(stored_forest_info,
3393 tln=local_lsa_info.dns_domain.string)
3397 lsaString = lsa.String()
3398 lsaString.string = domain
3399 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
3400 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
3401 except RuntimeError as error:
3402 if self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
3403 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
3405 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
3407 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
3408 local_tdo_info.netbios_name.string,
3409 local_tdo_info.domain_name.string,
3410 local_tdo_info.sid))
3412 if not local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
3413 raise CommandError("trusted domain object for domain [%s] is not marked as FOREST_TRANSITIVE." % domain)
3415 if refresh is not None:
3417 local_netlogon = self.new_local_netlogon_connection()
3418 except RuntimeError as error:
3419 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3422 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3423 except RuntimeError as error:
3424 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3426 lsa_update_check = 1
3427 if refresh == "store":
3428 netlogon_update_tdo = netlogon.DS_GFTI_UPDATE_TDO
3430 lsa_update_check = 0
3432 netlogon_update_tdo = 0
3435 # get all information about the remote trust
3436 # this triggers netr_GetForestTrustInformation to the remote domain
3437 # and lsaRSetForestTrustInformation() locally, but new top level
3438 # names are disabled by default.
3439 fresh_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3440 local_tdo_info.domain_name.string,
3441 netlogon_update_tdo)
3442 except RuntimeError as error:
3443 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3446 fresh_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3447 local_tdo_info.domain_name,
3448 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3451 except RuntimeError as error:
3452 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3454 self.outf.write("Fresh forest trust information...\n")
3455 self.write_forest_trust_info(fresh_forest_info,
3456 tln=local_tdo_info.domain_name.string,
3457 collisions=fresh_forest_collision)
3459 if refresh == "store":
3461 lsaString = lsa.String()
3462 lsaString.string = local_tdo_info.domain_name.string
3463 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3465 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3466 except RuntimeError as error:
3467 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3469 self.outf.write("Stored forest trust information...\n")
3470 self.write_forest_trust_info(stored_forest_info,
3471 tln=local_tdo_info.domain_name.string)
3476 # The none --refresh path
3480 lsaString = lsa.String()
3481 lsaString.string = local_tdo_info.domain_name.string
3482 local_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3484 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3485 except RuntimeError as error:
3486 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3488 self.outf.write("Local forest trust information...\n")
3489 self.write_forest_trust_info(local_forest_info,
3490 tln=local_tdo_info.domain_name.string)
3492 if not require_update:
3496 entries.extend(local_forest_info.entries)
3497 update_forest_info = lsa.ForestTrustInformation()
3498 update_forest_info.count = len(entries)
3499 update_forest_info.entries = entries
3502 for i in xrange(0, len(update_forest_info.entries)):
3503 r = update_forest_info.entries[i]
3504 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3506 if update_forest_info.entries[i].flags == 0:
3508 update_forest_info.entries[i].time = 0
3509 update_forest_info.entries[i].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3510 for i in xrange(0, len(update_forest_info.entries)):
3511 r = update_forest_info.entries[i]
3512 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3514 if update_forest_info.entries[i].flags == 0:
3516 update_forest_info.entries[i].time = 0
3517 update_forest_info.entries[i].flags &= ~lsa.LSA_NB_DISABLED_MASK
3518 update_forest_info.entries[i].flags &= ~lsa.LSA_SID_DISABLED_MASK
3520 for tln in enable_tln:
3522 for i in xrange(0, len(update_forest_info.entries)):
3523 r = update_forest_info.entries[i]
3524 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3526 if r.forest_trust_data.string.lower() != tln.lower():
3531 raise CommandError("Entry not found for value[%s] specified for --enable-tln" % tln)
3532 if not update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_MASK:
3533 raise CommandError("Entry found for value[%s] specified for --enable-tln is already enabled" % tln)
3534 update_forest_info.entries[idx].time = 0
3535 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3537 for tln in disable_tln:
3539 for i in xrange(0, len(update_forest_info.entries)):
3540 r = update_forest_info.entries[i]
3541 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3543 if r.forest_trust_data.string.lower() != tln.lower():
3548 raise CommandError("Entry not found for value[%s] specified for --disable-tln" % tln)
3549 if update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_ADMIN:
3550 raise CommandError("Entry found for value[%s] specified for --disable-tln is already disabled" % tln)
3551 update_forest_info.entries[idx].time = 0
3552 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3553 update_forest_info.entries[idx].flags |= lsa.LSA_TLN_DISABLED_ADMIN
3555 for tln_ex in add_tln_ex:
3557 for i in xrange(0, len(update_forest_info.entries)):
3558 r = update_forest_info.entries[i]
3559 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3561 if r.forest_trust_data.string.lower() != tln_ex.lower():
3566 raise CommandError("Entry already present for value[%s] specified for --add-tln-ex" % tln_ex)
3568 tln_dot = ".%s" % tln_ex.lower()
3570 for i in xrange(0, len(update_forest_info.entries)):
3571 r = update_forest_info.entries[i]
3572 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3574 r_dot = ".%s" % r.forest_trust_data.string.lower()
3575 if tln_dot == r_dot:
3576 raise CommandError("TLN entry present for value[%s] specified for --add-tln-ex" % tln_ex)
3577 if not tln_dot.endswith(r_dot):
3583 raise CommandError("No TLN parent present for value[%s] specified for --add-tln-ex" % tln_ex)
3585 r = lsa.ForestTrustRecord()
3586 r.type = lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX
3589 r.forest_trust_data.string = tln_ex
3592 entries.extend(update_forest_info.entries)
3593 entries.insert(idx + 1, r)
3594 update_forest_info.count = len(entries)
3595 update_forest_info.entries = entries
3597 for tln_ex in delete_tln_ex:
3599 for i in xrange(0, len(update_forest_info.entries)):
3600 r = update_forest_info.entries[i]
3601 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3603 if r.forest_trust_data.string.lower() != tln_ex.lower():
3608 raise CommandError("Entry not found for value[%s] specified for --delete-tln-ex" % tln_ex)
3611 entries.extend(update_forest_info.entries)
3613 update_forest_info.count = len(entries)
3614 update_forest_info.entries = entries
3616 for nb in enable_nb:
3618 for i in xrange(0, len(update_forest_info.entries)):
3619 r = update_forest_info.entries[i]
3620 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3622 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3627 raise CommandError("Entry not found for value[%s] specified for --enable-nb" % nb)
3628 if not update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_MASK:
3629 raise CommandError("Entry found for value[%s] specified for --enable-nb is already enabled" % nb)
3630 update_forest_info.entries[idx].time = 0
3631 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3633 for nb in disable_nb:
3635 for i in xrange(0, len(update_forest_info.entries)):
3636 r = update_forest_info.entries[i]
3637 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3639 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3644 raise CommandError("Entry not found for value[%s] specified for --delete-nb" % nb)
3645 if update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_ADMIN:
3646 raise CommandError("Entry found for value[%s] specified for --disable-nb is already disabled" % nb)
3647 update_forest_info.entries[idx].time = 0
3648 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3649 update_forest_info.entries[idx].flags |= lsa.LSA_NB_DISABLED_ADMIN
3651 for sid in enable_sid:
3653 for i in xrange(0, len(update_forest_info.entries)):
3654 r = update_forest_info.entries[i]
3655 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3657 if r.forest_trust_data.domain_sid != sid:
3662 raise CommandError("Entry not found for value[%s] specified for --enable-sid" % sid)
3663 if not update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_MASK:
3664 raise CommandError("Entry found for value[%s] specified for --enable-sid is already enabled" % nb)
3665 update_forest_info.entries[idx].time = 0
3666 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3668 for sid in disable_sid:
3670 for i in xrange(0, len(update_forest_info.entries)):
3671 r = update_forest_info.entries[i]
3672 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3674 if r.forest_trust_data.domain_sid != sid:
3679 raise CommandError("Entry not found for value[%s] specified for --delete-sid" % sid)
3680 if update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_ADMIN:
3681 raise CommandError("Entry found for value[%s] specified for --disable-sid is already disabled" % nb)
3682 update_forest_info.entries[idx].time = 0
3683 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3684 update_forest_info.entries[idx].flags |= lsa.LSA_SID_DISABLED_ADMIN
3687 update_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3688 local_tdo_info.domain_name,
3689 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3690 update_forest_info, 0)
3691 except RuntimeError as error:
3692 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3694 self.outf.write("Updated forest trust information...\n")
3695 self.write_forest_trust_info(update_forest_info,
3696 tln=local_tdo_info.domain_name.string,
3697 collisions=update_forest_collision)
3700 lsaString = lsa.String()
3701 lsaString.string = local_tdo_info.domain_name.string
3702 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3704 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3705 except RuntimeError as error:
3706 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3708 self.outf.write("Stored forest trust information...\n")
3709 self.write_forest_trust_info(stored_forest_info,
3710 tln=local_tdo_info.domain_name.string)
3713 class cmd_domain_trust(SuperCommand):
3714 """Domain and forest trust management."""
3717 subcommands["list"] = cmd_domain_trust_list()
3718 subcommands["show"] = cmd_domain_trust_show()
3719 subcommands["create"] = cmd_domain_trust_create()
3720 subcommands["delete"] = cmd_domain_trust_delete()
3721 subcommands["validate"] = cmd_domain_trust_validate()
3722 subcommands["namespaces"] = cmd_domain_trust_namespaces()
3724 class cmd_domain(SuperCommand):
3725 """Domain management."""
3728 subcommands["demote"] = cmd_domain_demote()
3729 if cmd_domain_export_keytab is not None:
3730 subcommands["exportkeytab"] = cmd_domain_export_keytab()
3731 subcommands["info"] = cmd_domain_info()
3732 subcommands["provision"] = cmd_domain_provision()
3733 subcommands["join"] = cmd_domain_join()
3734 subcommands["dcpromo"] = cmd_domain_dcpromo()
3735 subcommands["level"] = cmd_domain_level()
3736 subcommands["passwordsettings"] = cmd_domain_passwordsettings()
3737 subcommands["classicupgrade"] = cmd_domain_classicupgrade()
3738 subcommands["samba3upgrade"] = cmd_domain_samba3upgrade()
3739 subcommands["trust"] = cmd_domain_trust()