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
77 from samba.provision import (
82 from samba.provision.common import (
88 def get_testparm_var(testparm, smbconf, varname):
89 cmd = "%s -s -l --parameter-name='%s' %s 2>/dev/null" % (testparm, varname, smbconf)
90 output = os.popen(cmd, 'r').readline()
96 cmd_domain_export_keytab = None
98 class cmd_domain_export_keytab(Command):
99 """Dump Kerberos keys of the domain into a keytab."""
101 synopsis = "%prog <keytab> [options]"
103 takes_optiongroups = {
104 "sambaopts": options.SambaOptions,
105 "credopts": options.CredentialsOptions,
106 "versionopts": options.VersionOptions,
110 Option("--principal", help="extract only this principal", type=str),
113 takes_args = ["keytab"]
115 def run(self, keytab, credopts=None, sambaopts=None, versionopts=None, principal=None):
116 lp = sambaopts.get_loadparm()
118 net.export_keytab(keytab=keytab, principal=principal)
121 class cmd_domain_info(Command):
122 """Print basic info about a domain and the DC passed as parameter."""
124 synopsis = "%prog <ip_address> [options]"
129 takes_optiongroups = {
130 "sambaopts": options.SambaOptions,
131 "credopts": options.CredentialsOptions,
132 "versionopts": options.VersionOptions,
135 takes_args = ["address"]
137 def run(self, address, credopts=None, sambaopts=None, versionopts=None):
138 lp = sambaopts.get_loadparm()
140 res = netcmd_get_domain_infos_via_cldap(lp, None, address)
142 raise CommandError("Invalid IP address '" + address + "'!")
143 self.outf.write("Forest : %s\n" % res.forest)
144 self.outf.write("Domain : %s\n" % res.dns_domain)
145 self.outf.write("Netbios domain : %s\n" % res.domain_name)
146 self.outf.write("DC name : %s\n" % res.pdc_dns_name)
147 self.outf.write("DC netbios name : %s\n" % res.pdc_name)
148 self.outf.write("Server site : %s\n" % res.server_site)
149 self.outf.write("Client site : %s\n" % res.client_site)
152 class cmd_domain_provision(Command):
153 """Provision a domain."""
155 synopsis = "%prog [options]"
157 takes_optiongroups = {
158 "sambaopts": options.SambaOptions,
159 "versionopts": options.VersionOptions,
163 Option("--interactive", help="Ask for names", action="store_true"),
164 Option("--domain", type="string", metavar="DOMAIN",
166 Option("--domain-guid", type="string", metavar="GUID",
167 help="set domainguid (otherwise random)"),
168 Option("--domain-sid", type="string", metavar="SID",
169 help="set domainsid (otherwise random)"),
170 Option("--ntds-guid", type="string", metavar="GUID",
171 help="set NTDS object GUID (otherwise random)"),
172 Option("--invocationid", type="string", metavar="GUID",
173 help="set invocationid (otherwise random)"),
174 Option("--host-name", type="string", metavar="HOSTNAME",
175 help="set hostname"),
176 Option("--host-ip", type="string", metavar="IPADDRESS",
177 help="set IPv4 ipaddress"),
178 Option("--host-ip6", type="string", metavar="IP6ADDRESS",
179 help="set IPv6 ipaddress"),
180 Option("--site", type="string", metavar="SITENAME",
181 help="set site name"),
182 Option("--adminpass", type="string", metavar="PASSWORD",
183 help="choose admin password (otherwise random)"),
184 Option("--krbtgtpass", type="string", metavar="PASSWORD",
185 help="choose krbtgt password (otherwise random)"),
186 Option("--machinepass", type="string", metavar="PASSWORD",
187 help="choose machine password (otherwise random)"),
188 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
189 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
190 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
191 "BIND9_FLATFILE uses bind9 text database to store zone information, "
192 "BIND9_DLZ uses samba4 AD to store zone information, "
193 "NONE skips the DNS setup entirely (not recommended)",
194 default="SAMBA_INTERNAL"),
195 Option("--dnspass", type="string", metavar="PASSWORD",
196 help="choose dns password (otherwise random)"),
197 Option("--ldapadminpass", type="string", metavar="PASSWORD",
198 help="choose password to set between Samba and its LDAP backend (otherwise random)"),
199 Option("--root", type="string", metavar="USERNAME",
200 help="choose 'root' unix username"),
201 Option("--nobody", type="string", metavar="USERNAME",
202 help="choose 'nobody' user"),
203 Option("--users", type="string", metavar="GROUPNAME",
204 help="choose 'users' group"),
205 Option("--quiet", help="Be quiet", action="store_true"),
206 Option("--blank", action="store_true",
207 help="do not add users or groups, just the structure"),
208 Option("--ldap-backend-type", type="choice", metavar="LDAP-BACKEND-TYPE",
209 help="Test initialisation support for unsupported LDAP backend type (fedora-ds or openldap) DO NOT USE",
210 choices=["fedora-ds", "openldap"]),
211 Option("--server-role", type="choice", metavar="ROLE",
212 choices=["domain controller", "dc", "member server", "member", "standalone"],
213 help="The server role (domain controller | dc | member server | member | standalone). Default is dc.",
214 default="domain controller"),
215 Option("--function-level", type="choice", metavar="FOR-FUN-LEVEL",
216 choices=["2000", "2003", "2008", "2008_R2"],
217 help="The domain and forest function level (2000 | 2003 | 2008 | 2008_R2 - always native). Default is (Windows) 2008_R2 Native.",
219 Option("--next-rid", type="int", metavar="NEXTRID", default=1000,
220 help="The initial nextRid value (only needed for upgrades). Default is 1000."),
221 Option("--partitions-only",
222 help="Configure Samba's partitions, but do not modify them (ie, join a BDC)", action="store_true"),
223 Option("--targetdir", type="string", metavar="DIR",
224 help="Set target directory"),
225 Option("--ol-mmr-urls", type="string", metavar="LDAPSERVER",
226 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\""),
227 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"),
229 Option("--use-rfc2307", action="store_true", help="Use AD to store posix attributes (default = no)"),
233 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",
234 action="store_true"),
235 Option("--slapd-path", type="string", metavar="SLAPD-PATH",
236 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."),
237 Option("--ldap-backend-extra-port", type="int", metavar="LDAP-BACKEND-EXTRA-PORT", help="Additional TCP port for LDAP backend server (to use for replication)"),
238 Option("--ldap-backend-forced-uri", type="string", metavar="LDAP-BACKEND-FORCED-URI",
239 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"),
240 Option("--ldap-backend-nosync", help="Configure LDAP backend not to call fsync() (for performance in test environments)", action="store_true"),
244 Option("--use-ntvfs", action="store_true", help="Use NTVFS for the fileserver (default = no)"),
247 if os.getenv('TEST_LDAP', "no") == "yes":
248 takes_options.extend(openldap_options)
250 if samba.is_ntvfs_fileserver_built():
251 takes_options.extend(ntvfs_options)
255 def run(self, sambaopts=None, versionopts=None,
278 ldap_backend_type=None,
282 partitions_only=None,
289 ldap_backend_nosync=None,
290 ldap_backend_extra_port=None,
291 ldap_backend_forced_uri=None,
292 ldap_dryrun_mode=None):
294 self.logger = self.get_logger("provision")
296 self.logger.setLevel(logging.WARNING)
298 self.logger.setLevel(logging.INFO)
300 lp = sambaopts.get_loadparm()
301 smbconf = lp.configfile
303 if dns_forwarder is not None:
304 suggested_forwarder = dns_forwarder
306 suggested_forwarder = self._get_nameserver_ip()
307 if suggested_forwarder is None:
308 suggested_forwarder = "none"
310 if len(self.raw_argv) == 1:
314 from getpass import getpass
317 def ask(prompt, default=None):
318 if default is not None:
319 print "%s [%s]: " % (prompt, default),
321 print "%s: " % (prompt,),
322 return sys.stdin.readline().rstrip("\n") or default
325 default = socket.getfqdn().split(".", 1)[1].upper()
328 realm = ask("Realm", default)
329 if realm in (None, ""):
330 raise CommandError("No realm set!")
333 default = realm.split(".")[0]
336 domain = ask("Domain", default)
338 raise CommandError("No domain set!")
340 server_role = ask("Server Role (dc, member, standalone)", "dc")
342 dns_backend = ask("DNS backend (SAMBA_INTERNAL, BIND9_FLATFILE, BIND9_DLZ, NONE)", "SAMBA_INTERNAL")
343 if dns_backend in (None, ''):
344 raise CommandError("No DNS backend set!")
346 if dns_backend == "SAMBA_INTERNAL":
347 dns_forwarder = ask("DNS forwarder IP address (write 'none' to disable forwarding)", suggested_forwarder)
348 if dns_forwarder.lower() in (None, 'none'):
349 suggested_forwarder = None
353 adminpassplain = getpass("Administrator password: ")
354 if not adminpassplain:
355 self.errf.write("Invalid administrator password.\n")
357 adminpassverify = getpass("Retype password: ")
358 if not adminpassplain == adminpassverify:
359 self.errf.write("Sorry, passwords do not match.\n")
361 adminpass = adminpassplain
365 realm = sambaopts._lp.get('realm')
367 raise CommandError("No realm set!")
369 raise CommandError("No domain set!")
372 self.logger.info("Administrator password will be set randomly!")
374 if function_level == "2000":
375 dom_for_fun_level = DS_DOMAIN_FUNCTION_2000
376 elif function_level == "2003":
377 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
378 elif function_level == "2008":
379 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008
380 elif function_level == "2008_R2":
381 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
383 if dns_backend == "SAMBA_INTERNAL" and dns_forwarder is None:
384 dns_forwarder = suggested_forwarder
386 samdb_fill = FILL_FULL
388 samdb_fill = FILL_NT4SYNC
389 elif partitions_only:
390 samdb_fill = FILL_DRS
392 if targetdir is not None:
393 if not os.path.isdir(targetdir):
398 if use_xattrs == "yes":
400 elif use_xattrs == "auto" and not lp.get("posix:eadb"):
402 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
404 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
407 samba.ntacls.setntacl(lp, file.name,
408 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
411 self.logger.info("You are not root or your system do not support xattr, using tdb backend for attributes. ")
416 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.")
417 if ldap_backend_type == "existing":
418 if ldap_backend_forced_uri is not None:
419 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)
421 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")
423 if ldap_backend_forced_uri is not None:
424 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")
426 if domain_sid is not None:
427 domain_sid = security.dom_sid(domain_sid)
429 session = system_session()
431 result = provision(self.logger,
432 session, smbconf=smbconf, targetdir=targetdir,
433 samdb_fill=samdb_fill, realm=realm, domain=domain,
434 domainguid=domain_guid, domainsid=domain_sid,
436 hostip=host_ip, hostip6=host_ip6,
437 sitename=site, ntdsguid=ntds_guid,
438 invocationid=invocationid, adminpass=adminpass,
439 krbtgtpass=krbtgtpass, machinepass=machinepass,
440 dns_backend=dns_backend, dns_forwarder=dns_forwarder,
441 dnspass=dnspass, root=root, nobody=nobody,
443 serverrole=server_role, dom_for_fun_level=dom_for_fun_level,
444 backend_type=ldap_backend_type,
445 ldapadminpass=ldapadminpass, ol_mmr_urls=ol_mmr_urls, slapd_path=slapd_path,
446 useeadb=eadb, next_rid=next_rid, lp=lp, use_ntvfs=use_ntvfs,
447 use_rfc2307=use_rfc2307, skip_sysvolacl=False,
448 ldap_backend_extra_port=ldap_backend_extra_port,
449 ldap_backend_forced_uri=ldap_backend_forced_uri,
450 nosync=ldap_backend_nosync, ldap_dryrun_mode=ldap_dryrun_mode)
452 except ProvisioningError, e:
453 raise CommandError("Provision failed", e)
455 result.report_logger(self.logger)
457 def _get_nameserver_ip(self):
458 """Grab the nameserver IP address from /etc/resolv.conf."""
460 RESOLV_CONF="/etc/resolv.conf"
462 if not path.isfile(RESOLV_CONF):
463 self.logger.warning("Failed to locate %s" % RESOLV_CONF)
468 handle = open(RESOLV_CONF, 'r')
470 if not line.startswith('nameserver'):
472 # we want the last non-space continuous string of the line
473 return line.strip().split()[-1]
475 if handle is not None:
478 self.logger.warning("No nameserver found in %s" % RESOLV_CONF)
481 class cmd_domain_dcpromo(Command):
482 """Promote an existing domain member or NT4 PDC to an AD DC."""
484 synopsis = "%prog <dnsdomain> [DC|RODC] [options]"
486 takes_optiongroups = {
487 "sambaopts": options.SambaOptions,
488 "versionopts": options.VersionOptions,
489 "credopts": options.CredentialsOptions,
493 Option("--server", help="DC to join", type=str),
494 Option("--site", help="site to join", type=str),
495 Option("--targetdir", help="where to store provision", type=str),
496 Option("--domain-critical-only",
497 help="only replicate critical domain objects",
498 action="store_true"),
499 Option("--machinepass", type=str, metavar="PASSWORD",
500 help="choose machine password (otherwise random)"),
501 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
502 choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
503 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
504 "BIND9_DLZ uses samba4 AD to store zone information, "
505 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
506 default="SAMBA_INTERNAL"),
507 Option("--quiet", help="Be quiet", action="store_true"),
508 Option("--verbose", help="Be verbose", action="store_true")
512 Option("--use-ntvfs", action="store_true", help="Use NTVFS for the fileserver (default = no)"),
515 if samba.is_ntvfs_fileserver_built():
516 takes_options.extend(ntvfs_options)
519 takes_args = ["domain", "role?"]
521 def run(self, domain, role=None, sambaopts=None, credopts=None,
522 versionopts=None, server=None, site=None, targetdir=None,
523 domain_critical_only=False, parent_domain=None, machinepass=None,
524 use_ntvfs=False, dns_backend=None,
525 quiet=False, verbose=False):
526 lp = sambaopts.get_loadparm()
527 creds = credopts.get_credentials(lp)
528 net = Net(creds, lp, server=credopts.ipaddress)
531 site = "Default-First-Site-Name"
533 logger = self.get_logger()
535 logger.setLevel(logging.DEBUG)
537 logger.setLevel(logging.WARNING)
539 logger.setLevel(logging.INFO)
541 netbios_name = lp.get("netbios name")
547 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
548 site=site, netbios_name=netbios_name, targetdir=targetdir,
549 domain_critical_only=domain_critical_only,
550 machinepass=machinepass, use_ntvfs=use_ntvfs,
551 dns_backend=dns_backend,
552 promote_existing=True)
554 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
555 site=site, netbios_name=netbios_name, targetdir=targetdir,
556 domain_critical_only=domain_critical_only,
557 machinepass=machinepass, use_ntvfs=use_ntvfs, dns_backend=dns_backend,
558 promote_existing=True)
560 raise CommandError("Invalid role '%s' (possible values: DC, RODC)" % role)
563 class cmd_domain_join(Command):
564 """Join domain as either member or backup domain controller."""
566 synopsis = "%prog <dnsdomain> [DC|RODC|MEMBER|SUBDOMAIN] [options]"
568 takes_optiongroups = {
569 "sambaopts": options.SambaOptions,
570 "versionopts": options.VersionOptions,
571 "credopts": options.CredentialsOptions,
575 Option("--server", help="DC to join", type=str),
576 Option("--site", help="site to join", type=str),
577 Option("--targetdir", help="where to store provision", type=str),
578 Option("--parent-domain", help="parent domain to create subdomain under", type=str),
579 Option("--domain-critical-only",
580 help="only replicate critical domain objects",
581 action="store_true"),
582 Option("--machinepass", type=str, metavar="PASSWORD",
583 help="choose machine password (otherwise random)"),
584 Option("--adminpass", type="string", metavar="PASSWORD",
585 help="choose adminstrator password when joining as a subdomain (otherwise random)"),
586 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
587 choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
588 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
589 "BIND9_DLZ uses samba4 AD to store zone information, "
590 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
591 default="SAMBA_INTERNAL"),
592 Option("--quiet", help="Be quiet", action="store_true"),
593 Option("--verbose", help="Be verbose", action="store_true")
597 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
600 if samba.is_ntvfs_fileserver_built():
601 takes_options.extend(ntvfs_options)
603 takes_args = ["domain", "role?"]
605 def run(self, domain, role=None, sambaopts=None, credopts=None,
606 versionopts=None, server=None, site=None, targetdir=None,
607 domain_critical_only=False, parent_domain=None, machinepass=None,
608 use_ntvfs=False, dns_backend=None, adminpass=None,
609 quiet=False, verbose=False):
610 lp = sambaopts.get_loadparm()
611 creds = credopts.get_credentials(lp)
612 net = Net(creds, lp, server=credopts.ipaddress)
615 site = "Default-First-Site-Name"
617 logger = self.get_logger()
619 logger.setLevel(logging.DEBUG)
621 logger.setLevel(logging.WARNING)
623 logger.setLevel(logging.INFO)
625 netbios_name = lp.get("netbios name")
630 if role is None or role == "MEMBER":
631 (join_password, sid, domain_name) = net.join_member(
632 domain, netbios_name, LIBNET_JOIN_AUTOMATIC,
633 machinepass=machinepass)
635 self.errf.write("Joined domain %s (%s)\n" % (domain_name, sid))
637 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
638 site=site, netbios_name=netbios_name, targetdir=targetdir,
639 domain_critical_only=domain_critical_only,
640 machinepass=machinepass, use_ntvfs=use_ntvfs, dns_backend=dns_backend)
642 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
643 site=site, netbios_name=netbios_name, targetdir=targetdir,
644 domain_critical_only=domain_critical_only,
645 machinepass=machinepass, use_ntvfs=use_ntvfs,
646 dns_backend=dns_backend)
647 elif role == "SUBDOMAIN":
649 logger.info("Administrator password will be set randomly!")
651 netbios_domain = lp.get("workgroup")
652 if parent_domain is None:
653 parent_domain = ".".join(domain.split(".")[1:])
654 join_subdomain(logger=logger, server=server, creds=creds, lp=lp, dnsdomain=domain,
655 parent_domain=parent_domain, site=site,
656 netbios_name=netbios_name, netbios_domain=netbios_domain,
657 targetdir=targetdir, machinepass=machinepass,
658 use_ntvfs=use_ntvfs, dns_backend=dns_backend,
661 raise CommandError("Invalid role '%s' (possible values: MEMBER, DC, RODC, SUBDOMAIN)" % role)
664 class cmd_domain_demote(Command):
665 """Demote ourselves from the role of Domain Controller."""
667 synopsis = "%prog [options]"
670 Option("--server", help="writable DC to write demotion changes on", type=str),
671 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
672 metavar="URL", dest="H"),
673 Option("--remove-other-dead-server", help="Dead DC to remove ALL references to (rather than this DC)", type=str),
676 takes_optiongroups = {
677 "sambaopts": options.SambaOptions,
678 "credopts": options.CredentialsOptions,
679 "versionopts": options.VersionOptions,
682 def run(self, sambaopts=None, credopts=None,
683 versionopts=None, server=None,
684 remove_other_dead_server=None, H=None):
685 lp = sambaopts.get_loadparm()
686 creds = credopts.get_credentials(lp)
687 net = Net(creds, lp, server=credopts.ipaddress)
689 if remove_other_dead_server is not None:
690 if server is not None:
691 samdb = SamDB(url="ldap://%s" % server,
692 session_info=system_session(),
693 credentials=creds, lp=lp)
695 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
696 remove_dc.remove_dc(samdb, remove_other_dead_server)
699 netbios_name = lp.get("netbios name")
700 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
702 res = samdb.search(expression='(&(objectClass=computer)(serverReferenceBL=*))', attrs=["dnsHostName", "name"])
704 raise CommandError("Unable to search for servers")
707 raise CommandError("You are the latest server in the domain")
711 if str(e["name"]).lower() != netbios_name.lower():
712 server = e["dnsHostName"]
715 ntds_guid = samdb.get_ntds_GUID()
716 msg = samdb.search(base=str(samdb.get_config_basedn()),
717 scope=ldb.SCOPE_SUBTREE, expression="(objectGUID=%s)" % ntds_guid,
719 if len(msg) == 0 or "options" not in msg[0]:
720 raise CommandError("Failed to find options on %s" % ntds_guid)
723 dsa_options = int(str(msg[0]['options']))
725 res = samdb.search(expression="(fSMORoleOwner=%s)" % str(ntds_dn),
726 controls=["search_options:1:2"])
729 raise CommandError("Current DC is still the owner of %d role(s), use the role command to transfer roles to another DC" % len(res))
731 self.errf.write("Using %s as partner server for the demotion\n" %
733 (drsuapiBind, drsuapi_handle, supportedExtensions) = drsuapi_connect(server, lp, creds)
735 self.errf.write("Deactivating inbound replication\n")
740 dsa_options |= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
741 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
744 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
746 self.errf.write("Asking partner server %s to synchronize from us\n"
748 for part in (samdb.get_schema_basedn(),
749 samdb.get_config_basedn(),
750 samdb.get_root_basedn()):
752 sendDsReplicaSync(drsuapiBind, drsuapi_handle, ntds_guid, str(part), drsuapi.DRSUAPI_DRS_WRIT_REP)
753 except drsException, e:
755 "Error while demoting, "
756 "re-enabling inbound replication\n")
757 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
758 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
760 raise CommandError("Error while sending a DsReplicaSync for partion %s" % str(part), e)
762 remote_samdb = SamDB(url="ldap://%s" % server,
763 session_info=system_session(),
764 credentials=creds, lp=lp)
766 self.errf.write("Changing userControl and container\n")
767 res = remote_samdb.search(base=str(remote_samdb.get_root_basedn()),
768 expression="(&(objectClass=user)(sAMAccountName=%s$))" %
769 netbios_name.upper(),
770 attrs=["userAccountControl"])
772 uac = int(str(res[0]["userAccountControl"]))
776 "Error while demoting, re-enabling inbound replication\n")
777 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
778 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
780 raise CommandError("Error while changing account control", e)
784 "Error while demoting, re-enabling inbound replication")
785 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
786 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
788 raise CommandError("Unable to find object with samaccountName = %s$"
789 " in the remote dc" % netbios_name.upper())
793 uac ^= (UF_SERVER_TRUST_ACCOUNT|UF_TRUSTED_FOR_DELEGATION)
794 uac |= UF_WORKSTATION_TRUST_ACCOUNT
799 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
800 ldb.FLAG_MOD_REPLACE,
801 "userAccountControl")
803 remote_samdb.modify(msg)
806 "Error while demoting, re-enabling inbound replication")
807 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
808 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
811 raise CommandError("Error while changing account control", e)
813 parent = msg.dn.parent()
815 rdn = string.replace(rdn, ",%s" % str(parent), "")
816 # Let's move to the Computer container
820 computer_dn = ldb.Dn(remote_samdb, "CN=Computers,%s" % str(remote_samdb.get_root_basedn()))
821 res = remote_samdb.search(base=computer_dn, expression=rdn, scope=ldb.SCOPE_ONELEVEL)
824 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
825 scope=ldb.SCOPE_ONELEVEL)
826 while(len(res) != 0 and i < 100):
828 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
829 scope=ldb.SCOPE_ONELEVEL)
833 "Error while demoting, re-enabling inbound replication\n")
834 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
835 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
841 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
842 ldb.FLAG_MOD_REPLACE,
843 "userAccountControl")
845 remote_samdb.modify(msg)
847 raise CommandError("Unable to find a slot for renaming %s,"
848 " all names from %s-1 to %s-%d seemed used" %
849 (str(dc_dn), rdn, rdn, i - 9))
851 newrdn = "%s-%d" % (rdn, i)
854 newdn = ldb.Dn(remote_samdb, "%s,%s" % (newrdn, str(computer_dn)))
855 remote_samdb.rename(dc_dn, newdn)
858 "Error while demoting, re-enabling inbound replication\n")
859 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
860 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
866 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
867 ldb.FLAG_MOD_REPLACE,
868 "userAccountControl")
870 remote_samdb.modify(msg)
871 raise CommandError("Error while renaming %s to %s" % (str(dc_dn), str(newdn)), e)
874 server_dsa_dn = samdb.get_serverName()
875 domain = remote_samdb.get_root_basedn()
878 sendRemoveDsServer(drsuapiBind, drsuapi_handle, server_dsa_dn, domain)
879 except drsException, e:
881 "Error while demoting, re-enabling inbound replication\n")
882 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
883 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
889 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
890 ldb.FLAG_MOD_REPLACE,
891 "userAccountControl")
893 remote_samdb.modify(msg)
894 remote_samdb.rename(newdn, dc_dn)
895 raise CommandError("Error while sending a removeDsServer", e)
897 for s in ("CN=Enterprise,CN=Microsoft System Volumes,CN=System,CN=Configuration",
898 "CN=%s,CN=Microsoft System Volumes,CN=System,CN=Configuration" % lp.get("realm"),
899 "CN=Domain System Volumes (SYSVOL share),CN=File Replication Service,CN=System"):
901 remote_samdb.delete(ldb.Dn(remote_samdb,
902 "%s,%s,%s" % (str(rdn), s, str(remote_samdb.get_root_basedn()))))
903 except ldb.LdbError, l:
906 for s in ("CN=Enterprise,CN=NTFRS Subscriptions",
907 "CN=%s, CN=NTFRS Subscriptions" % lp.get("realm"),
908 "CN=Domain system Volumes (SYSVOL Share), CN=NTFRS Subscriptions",
909 "CN=NTFRS Subscriptions"):
911 remote_samdb.delete(ldb.Dn(remote_samdb,
912 "%s,%s" % (s, str(newdn))))
913 except ldb.LdbError, l:
916 self.errf.write("Demote successful\n")
919 class cmd_domain_level(Command):
920 """Raise domain and forest function levels."""
922 synopsis = "%prog (show|raise <options>) [options]"
924 takes_optiongroups = {
925 "sambaopts": options.SambaOptions,
926 "credopts": options.CredentialsOptions,
927 "versionopts": options.VersionOptions,
931 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
932 metavar="URL", dest="H"),
933 Option("--quiet", help="Be quiet", action="store_true"),
934 Option("--forest-level", type="choice", choices=["2003", "2008", "2008_R2"],
935 help="The forest function level (2003 | 2008 | 2008_R2)"),
936 Option("--domain-level", type="choice", choices=["2003", "2008", "2008_R2"],
937 help="The domain function level (2003 | 2008 | 2008_R2)")
940 takes_args = ["subcommand"]
942 def run(self, subcommand, H=None, forest_level=None, domain_level=None,
943 quiet=False, credopts=None, sambaopts=None, versionopts=None):
944 lp = sambaopts.get_loadparm()
945 creds = credopts.get_credentials(lp, fallback_machine=True)
947 samdb = SamDB(url=H, session_info=system_session(),
948 credentials=creds, lp=lp)
950 domain_dn = samdb.domain_dn()
952 res_forest = samdb.search("CN=Partitions,%s" % samdb.get_config_basedn(),
953 scope=ldb.SCOPE_BASE, attrs=["msDS-Behavior-Version"])
954 assert len(res_forest) == 1
956 res_domain = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
957 attrs=["msDS-Behavior-Version", "nTMixedDomain"])
958 assert len(res_domain) == 1
960 res_dc_s = samdb.search("CN=Sites,%s" % samdb.get_config_basedn(),
961 scope=ldb.SCOPE_SUBTREE, expression="(objectClass=nTDSDSA)",
962 attrs=["msDS-Behavior-Version"])
963 assert len(res_dc_s) >= 1
966 level_forest = int(res_forest[0]["msDS-Behavior-Version"][0])
967 level_domain = int(res_domain[0]["msDS-Behavior-Version"][0])
968 level_domain_mixed = int(res_domain[0]["nTMixedDomain"][0])
970 min_level_dc = int(res_dc_s[0]["msDS-Behavior-Version"][0]) # Init value
972 if int(msg["msDS-Behavior-Version"][0]) < min_level_dc:
973 min_level_dc = int(msg["msDS-Behavior-Version"][0])
975 if level_forest < 0 or level_domain < 0:
976 raise CommandError("Domain and/or forest function level(s) is/are invalid. Correct them or reprovision!")
978 raise CommandError("Lowest function level of a DC is invalid. Correct this or reprovision!")
979 if level_forest > level_domain:
980 raise CommandError("Forest function level is higher than the domain level(s). Correct this or reprovision!")
981 if level_domain > min_level_dc:
982 raise CommandError("Domain function level is higher than the lowest function level of a DC. Correct this or reprovision!")
985 raise CommandError("Could not retrieve the actual domain, forest level and/or lowest DC function level!")
987 if subcommand == "show":
988 self.message("Domain and forest function level for domain '%s'" % domain_dn)
989 if level_forest == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
990 self.message("\nATTENTION: You run SAMBA 4 on a forest function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
991 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
992 self.message("\nATTENTION: You run SAMBA 4 on a domain function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
993 if min_level_dc == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
994 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)!")
998 if level_forest == DS_DOMAIN_FUNCTION_2000:
1000 elif level_forest == DS_DOMAIN_FUNCTION_2003_MIXED:
1001 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1002 elif level_forest == DS_DOMAIN_FUNCTION_2003:
1004 elif level_forest == DS_DOMAIN_FUNCTION_2008:
1006 elif level_forest == DS_DOMAIN_FUNCTION_2008_R2:
1009 outstr = "higher than 2008 R2"
1010 self.message("Forest function level: (Windows) " + outstr)
1012 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1013 outstr = "2000 mixed (NT4 DC support)"
1014 elif level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed == 0:
1016 elif level_domain == DS_DOMAIN_FUNCTION_2003_MIXED:
1017 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1018 elif level_domain == DS_DOMAIN_FUNCTION_2003:
1020 elif level_domain == DS_DOMAIN_FUNCTION_2008:
1022 elif level_domain == DS_DOMAIN_FUNCTION_2008_R2:
1025 outstr = "higher than 2008 R2"
1026 self.message("Domain function level: (Windows) " + outstr)
1028 if min_level_dc == DS_DOMAIN_FUNCTION_2000:
1030 elif min_level_dc == DS_DOMAIN_FUNCTION_2003:
1032 elif min_level_dc == DS_DOMAIN_FUNCTION_2008:
1034 elif min_level_dc == DS_DOMAIN_FUNCTION_2008_R2:
1037 outstr = "higher than 2008 R2"
1038 self.message("Lowest function level of a DC: (Windows) " + outstr)
1040 elif subcommand == "raise":
1043 if domain_level is not None:
1044 if domain_level == "2003":
1045 new_level_domain = DS_DOMAIN_FUNCTION_2003
1046 elif domain_level == "2008":
1047 new_level_domain = DS_DOMAIN_FUNCTION_2008
1048 elif domain_level == "2008_R2":
1049 new_level_domain = DS_DOMAIN_FUNCTION_2008_R2
1051 if new_level_domain <= level_domain and level_domain_mixed == 0:
1052 raise CommandError("Domain function level can't be smaller than or equal to the actual one!")
1054 if new_level_domain > min_level_dc:
1055 raise CommandError("Domain function level can't be higher than the lowest function level of a DC!")
1057 # Deactivate mixed/interim domain support
1058 if level_domain_mixed != 0:
1059 # Directly on the base DN
1061 m.dn = ldb.Dn(samdb, domain_dn)
1062 m["nTMixedDomain"] = ldb.MessageElement("0",
1063 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1067 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup") + ",CN=Partitions,%s" % samdb.get_config_basedn())
1068 m["nTMixedDomain"] = ldb.MessageElement("0",
1069 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1072 except ldb.LdbError, (enum, emsg):
1073 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1076 # Directly on the base DN
1078 m.dn = ldb.Dn(samdb, domain_dn)
1079 m["msDS-Behavior-Version"]= ldb.MessageElement(
1080 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1081 "msDS-Behavior-Version")
1085 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup")
1086 + ",CN=Partitions,%s" % samdb.get_config_basedn())
1087 m["msDS-Behavior-Version"]= ldb.MessageElement(
1088 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1089 "msDS-Behavior-Version")
1092 except ldb.LdbError, (enum, emsg):
1093 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1096 level_domain = new_level_domain
1097 msgs.append("Domain function level changed!")
1099 if forest_level is not None:
1100 if forest_level == "2003":
1101 new_level_forest = DS_DOMAIN_FUNCTION_2003
1102 elif forest_level == "2008":
1103 new_level_forest = DS_DOMAIN_FUNCTION_2008
1104 elif forest_level == "2008_R2":
1105 new_level_forest = DS_DOMAIN_FUNCTION_2008_R2
1106 if new_level_forest <= level_forest:
1107 raise CommandError("Forest function level can't be smaller than or equal to the actual one!")
1108 if new_level_forest > level_domain:
1109 raise CommandError("Forest function level can't be higher than the domain function level(s). Please raise it/them first!")
1111 m.dn = ldb.Dn(samdb, "CN=Partitions,%s" % samdb.get_config_basedn())
1112 m["msDS-Behavior-Version"]= ldb.MessageElement(
1113 str(new_level_forest), ldb.FLAG_MOD_REPLACE,
1114 "msDS-Behavior-Version")
1116 msgs.append("Forest function level changed!")
1117 msgs.append("All changes applied successfully!")
1118 self.message("\n".join(msgs))
1120 raise CommandError("invalid argument: '%s' (choose from 'show', 'raise')" % subcommand)
1123 class cmd_domain_passwordsettings(Command):
1124 """Set password settings.
1126 Password complexity, password lockout policy, history length,
1127 minimum password length, the minimum and maximum password age) on
1128 a Samba AD DC server.
1130 Use against a Windows DC is possible, but group policy will override it.
1133 synopsis = "%prog (show|set <options>) [options]"
1135 takes_optiongroups = {
1136 "sambaopts": options.SambaOptions,
1137 "versionopts": options.VersionOptions,
1138 "credopts": options.CredentialsOptions,
1142 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1143 metavar="URL", dest="H"),
1144 Option("--quiet", help="Be quiet", action="store_true"),
1145 Option("--complexity", type="choice", choices=["on","off","default"],
1146 help="The password complexity (on | off | default). Default is 'on'"),
1147 Option("--store-plaintext", type="choice", choices=["on","off","default"],
1148 help="Store plaintext passwords where account have 'store passwords with reversible encryption' set (on | off | default). Default is 'off'"),
1149 Option("--history-length",
1150 help="The password history length (<integer> | default). Default is 24.", type=str),
1151 Option("--min-pwd-length",
1152 help="The minimum password length (<integer> | default). Default is 7.", type=str),
1153 Option("--min-pwd-age",
1154 help="The minimum password age (<integer in days> | default). Default is 1.", type=str),
1155 Option("--max-pwd-age",
1156 help="The maximum password age (<integer in days> | default). Default is 43.", type=str),
1157 Option("--account-lockout-duration",
1158 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),
1159 Option("--account-lockout-threshold",
1160 help="The number of bad password attempts allowed before locking out the account (<integer> | default). Default is 0 (never lock out).", type=str),
1161 Option("--reset-account-lockout-after",
1162 help="After this time is elapsed, the recorded number of attempts restarts from zero (<integer> | default). Default is 30.", type=str),
1165 takes_args = ["subcommand"]
1167 def run(self, subcommand, H=None, min_pwd_age=None, max_pwd_age=None,
1168 quiet=False, complexity=None, store_plaintext=None, history_length=None,
1169 min_pwd_length=None, account_lockout_duration=None, account_lockout_threshold=None,
1170 reset_account_lockout_after=None, credopts=None, sambaopts=None,
1172 lp = sambaopts.get_loadparm()
1173 creds = credopts.get_credentials(lp)
1175 samdb = SamDB(url=H, session_info=system_session(),
1176 credentials=creds, lp=lp)
1178 domain_dn = samdb.domain_dn()
1179 res = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1180 attrs=["pwdProperties", "pwdHistoryLength", "minPwdLength",
1181 "minPwdAge", "maxPwdAge", "lockoutDuration", "lockoutThreshold",
1182 "lockOutObservationWindow"])
1183 assert(len(res) == 1)
1185 pwd_props = int(res[0]["pwdProperties"][0])
1186 pwd_hist_len = int(res[0]["pwdHistoryLength"][0])
1187 cur_min_pwd_len = int(res[0]["minPwdLength"][0])
1189 cur_min_pwd_age = int(abs(int(res[0]["minPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1190 if int(res[0]["maxPwdAge"][0]) == -0x8000000000000000:
1193 cur_max_pwd_age = int(abs(int(res[0]["maxPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1194 cur_account_lockout_threshold = int(res[0]["lockoutThreshold"][0])
1196 if int(res[0]["lockoutDuration"][0]) == -0x8000000000000000:
1197 cur_account_lockout_duration = 0
1199 cur_account_lockout_duration = abs(int(res[0]["lockoutDuration"][0])) / (1e7 * 60)
1200 cur_reset_account_lockout_after = abs(int(res[0]["lockOutObservationWindow"][0])) / (1e7 * 60)
1201 except Exception, e:
1202 raise CommandError("Could not retrieve password properties!", e)
1204 if subcommand == "show":
1205 self.message("Password informations for domain '%s'" % domain_dn)
1207 if pwd_props & DOMAIN_PASSWORD_COMPLEX != 0:
1208 self.message("Password complexity: on")
1210 self.message("Password complexity: off")
1211 if pwd_props & DOMAIN_PASSWORD_STORE_CLEARTEXT != 0:
1212 self.message("Store plaintext passwords: on")
1214 self.message("Store plaintext passwords: off")
1215 self.message("Password history length: %d" % pwd_hist_len)
1216 self.message("Minimum password length: %d" % cur_min_pwd_len)
1217 self.message("Minimum password age (days): %d" % cur_min_pwd_age)
1218 self.message("Maximum password age (days): %d" % cur_max_pwd_age)
1219 self.message("Account lockout duration (mins): %d" % cur_account_lockout_duration)
1220 self.message("Account lockout threshold (attempts): %d" % cur_account_lockout_threshold)
1221 self.message("Reset account lockout after (mins): %d" % cur_reset_account_lockout_after)
1222 elif subcommand == "set":
1225 m.dn = ldb.Dn(samdb, domain_dn)
1227 if complexity is not None:
1228 if complexity == "on" or complexity == "default":
1229 pwd_props = pwd_props | DOMAIN_PASSWORD_COMPLEX
1230 msgs.append("Password complexity activated!")
1231 elif complexity == "off":
1232 pwd_props = pwd_props & (~DOMAIN_PASSWORD_COMPLEX)
1233 msgs.append("Password complexity deactivated!")
1235 if store_plaintext is not None:
1236 if store_plaintext == "on" or store_plaintext == "default":
1237 pwd_props = pwd_props | DOMAIN_PASSWORD_STORE_CLEARTEXT
1238 msgs.append("Plaintext password storage for changed passwords activated!")
1239 elif store_plaintext == "off":
1240 pwd_props = pwd_props & (~DOMAIN_PASSWORD_STORE_CLEARTEXT)
1241 msgs.append("Plaintext password storage for changed passwords deactivated!")
1243 if complexity is not None or store_plaintext is not None:
1244 m["pwdProperties"] = ldb.MessageElement(str(pwd_props),
1245 ldb.FLAG_MOD_REPLACE, "pwdProperties")
1247 if history_length is not None:
1248 if history_length == "default":
1251 pwd_hist_len = int(history_length)
1253 if pwd_hist_len < 0 or pwd_hist_len > 24:
1254 raise CommandError("Password history length must be in the range of 0 to 24!")
1256 m["pwdHistoryLength"] = ldb.MessageElement(str(pwd_hist_len),
1257 ldb.FLAG_MOD_REPLACE, "pwdHistoryLength")
1258 msgs.append("Password history length changed!")
1260 if min_pwd_length is not None:
1261 if min_pwd_length == "default":
1264 min_pwd_len = int(min_pwd_length)
1266 if min_pwd_len < 0 or min_pwd_len > 14:
1267 raise CommandError("Minimum password length must be in the range of 0 to 14!")
1269 m["minPwdLength"] = ldb.MessageElement(str(min_pwd_len),
1270 ldb.FLAG_MOD_REPLACE, "minPwdLength")
1271 msgs.append("Minimum password length changed!")
1273 if min_pwd_age is not None:
1274 if min_pwd_age == "default":
1277 min_pwd_age = int(min_pwd_age)
1279 if min_pwd_age < 0 or min_pwd_age > 998:
1280 raise CommandError("Minimum password age must be in the range of 0 to 998!")
1283 min_pwd_age_ticks = -int(min_pwd_age * (24 * 60 * 60 * 1e7))
1285 m["minPwdAge"] = ldb.MessageElement(str(min_pwd_age_ticks),
1286 ldb.FLAG_MOD_REPLACE, "minPwdAge")
1287 msgs.append("Minimum password age changed!")
1289 if max_pwd_age is not None:
1290 if max_pwd_age == "default":
1293 max_pwd_age = int(max_pwd_age)
1295 if max_pwd_age < 0 or max_pwd_age > 999:
1296 raise CommandError("Maximum password age must be in the range of 0 to 999!")
1299 if max_pwd_age == 0:
1300 max_pwd_age_ticks = -0x8000000000000000
1302 max_pwd_age_ticks = -int(max_pwd_age * (24 * 60 * 60 * 1e7))
1304 m["maxPwdAge"] = ldb.MessageElement(str(max_pwd_age_ticks),
1305 ldb.FLAG_MOD_REPLACE, "maxPwdAge")
1306 msgs.append("Maximum password age changed!")
1308 if account_lockout_duration is not None:
1309 if account_lockout_duration == "default":
1310 account_lockout_duration = 30
1312 account_lockout_duration = int(account_lockout_duration)
1314 if account_lockout_duration < 0 or account_lockout_duration > 99999:
1315 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1318 if account_lockout_duration == 0:
1319 account_lockout_duration_ticks = -0x8000000000000000
1321 account_lockout_duration_ticks = -int(account_lockout_duration * (60 * 1e7))
1323 m["lockoutDuration"] = ldb.MessageElement(str(account_lockout_duration_ticks),
1324 ldb.FLAG_MOD_REPLACE, "lockoutDuration")
1325 msgs.append("Account lockout duration changed!")
1327 if account_lockout_threshold is not None:
1328 if account_lockout_threshold == "default":
1329 account_lockout_threshold = 0
1331 account_lockout_threshold = int(account_lockout_threshold)
1333 m["lockoutThreshold"] = ldb.MessageElement(str(account_lockout_threshold),
1334 ldb.FLAG_MOD_REPLACE, "lockoutThreshold")
1335 msgs.append("Account lockout threshold changed!")
1337 if reset_account_lockout_after is not None:
1338 if reset_account_lockout_after == "default":
1339 reset_account_lockout_after = 30
1341 reset_account_lockout_after = int(reset_account_lockout_after)
1343 if reset_account_lockout_after < 0 or reset_account_lockout_after > 99999:
1344 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1347 if reset_account_lockout_after == 0:
1348 reset_account_lockout_after_ticks = -0x8000000000000000
1350 reset_account_lockout_after_ticks = -int(reset_account_lockout_after * (60 * 1e7))
1352 m["lockOutObservationWindow"] = ldb.MessageElement(str(reset_account_lockout_after_ticks),
1353 ldb.FLAG_MOD_REPLACE, "lockOutObservationWindow")
1354 msgs.append("Duration to reset account lockout after changed!")
1356 if max_pwd_age > 0 and min_pwd_age >= max_pwd_age:
1357 raise CommandError("Maximum password age (%d) must be greater than minimum password age (%d)!" % (max_pwd_age, min_pwd_age))
1360 raise CommandError("You must specify at least one option to set. Try --help")
1362 msgs.append("All changes applied successfully!")
1363 self.message("\n".join(msgs))
1365 raise CommandError("Wrong argument '%s'!" % subcommand)
1368 class cmd_domain_classicupgrade(Command):
1369 """Upgrade from Samba classic (NT4-like) database to Samba AD DC database.
1371 Specify either a directory with all Samba classic DC databases and state files (with --dbdir) or
1372 the testparm utility from your classic installation (with --testparm).
1375 synopsis = "%prog [options] <classic_smb_conf>"
1377 takes_optiongroups = {
1378 "sambaopts": options.SambaOptions,
1379 "versionopts": options.VersionOptions
1383 Option("--dbdir", type="string", metavar="DIR",
1384 help="Path to samba classic DC database directory"),
1385 Option("--testparm", type="string", metavar="PATH",
1386 help="Path to samba classic DC testparm utility from the previous installation. This allows the default paths of the previous installation to be followed"),
1387 Option("--targetdir", type="string", metavar="DIR",
1388 help="Path prefix where the new Samba 4.0 AD domain should be initialised"),
1389 Option("--quiet", help="Be quiet", action="store_true"),
1390 Option("--verbose", help="Be verbose", action="store_true"),
1391 Option("--use-xattrs", type="choice", choices=["yes","no","auto"], metavar="[yes|no|auto]",
1392 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"),
1393 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
1394 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
1395 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
1396 "BIND9_FLATFILE uses bind9 text database to store zone information, "
1397 "BIND9_DLZ uses samba4 AD to store zone information, "
1398 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
1399 default="SAMBA_INTERNAL")
1403 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
1404 action="store_true")
1406 if samba.is_ntvfs_fileserver_built():
1407 takes_options.extend(ntvfs_options)
1409 takes_args = ["smbconf"]
1411 def run(self, smbconf=None, targetdir=None, dbdir=None, testparm=None,
1412 quiet=False, verbose=False, use_xattrs=None, sambaopts=None, versionopts=None,
1413 dns_backend=None, use_ntvfs=False):
1415 if not os.path.exists(smbconf):
1416 raise CommandError("File %s does not exist" % smbconf)
1418 if testparm and not os.path.exists(testparm):
1419 raise CommandError("Testparm utility %s does not exist" % testparm)
1421 if dbdir and not os.path.exists(dbdir):
1422 raise CommandError("Directory %s does not exist" % dbdir)
1424 if not dbdir and not testparm:
1425 raise CommandError("Please specify either dbdir or testparm")
1427 logger = self.get_logger()
1429 logger.setLevel(logging.DEBUG)
1431 logger.setLevel(logging.WARNING)
1433 logger.setLevel(logging.INFO)
1435 if dbdir and testparm:
1436 logger.warning("both dbdir and testparm specified, ignoring dbdir.")
1439 lp = sambaopts.get_loadparm()
1441 s3conf = s3param.get_context()
1444 s3conf.set("realm", sambaopts.realm)
1446 if targetdir is not None:
1447 if not os.path.isdir(targetdir):
1451 if use_xattrs == "yes":
1453 elif use_xattrs == "auto" and not s3conf.get("posix:eadb"):
1455 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
1457 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
1460 samba.ntacls.setntacl(lp, tmpfile.name,
1461 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
1464 # FIXME: Don't catch all exceptions here
1465 logger.info("You are not root or your system do not support xattr, using tdb backend for attributes. "
1466 "If you intend to use this provision in production, rerun the script as root on a system supporting xattrs.")
1470 # Set correct default values from dbdir or testparm
1473 paths["state directory"] = dbdir
1474 paths["private dir"] = dbdir
1475 paths["lock directory"] = dbdir
1476 paths["smb passwd file"] = dbdir + "/smbpasswd"
1478 paths["state directory"] = get_testparm_var(testparm, smbconf, "state directory")
1479 paths["private dir"] = get_testparm_var(testparm, smbconf, "private dir")
1480 paths["smb passwd file"] = get_testparm_var(testparm, smbconf, "smb passwd file")
1481 paths["lock directory"] = get_testparm_var(testparm, smbconf, "lock directory")
1482 # "testparm" from Samba 3 < 3.4.x is not aware of the parameter
1483 # "state directory", instead make use of "lock directory"
1484 if len(paths["state directory"]) == 0:
1485 paths["state directory"] = paths["lock directory"]
1488 s3conf.set(p, paths[p])
1490 # load smb.conf parameters
1491 logger.info("Reading smb.conf")
1492 s3conf.load(smbconf)
1493 samba3 = Samba3(smbconf, s3conf)
1495 logger.info("Provisioning")
1496 upgrade_from_samba3(samba3, logger, targetdir, session_info=system_session(),
1497 useeadb=eadb, dns_backend=dns_backend, use_ntvfs=use_ntvfs)
1500 class cmd_domain_samba3upgrade(cmd_domain_classicupgrade):
1501 __doc__ = cmd_domain_classicupgrade.__doc__
1503 # This command is present for backwards compatibility only,
1504 # and should not be shown.
1508 class LocalDCCredentialsOptions(options.CredentialsOptions):
1509 def __init__(self, parser):
1510 options.CredentialsOptions.__init__(self, parser, special_name="local-dc")
1512 class DomainTrustCommand(Command):
1513 """List domain trusts."""
1516 Command.__init__(self)
1517 self.local_lp = None
1519 self.local_server = None
1520 self.local_binding_string = None
1521 self.local_creds = None
1523 self.remote_server = None
1524 self.remote_binding_string = None
1525 self.remote_creds = None
1527 WERR_OK = 0x00000000
1528 WERR_INVALID_FUNCTION = 0x00000001
1529 WERR_NERR_ACFNOTLOADED = 0x000008B3
1531 NT_STATUS_NOT_FOUND = 0xC0000225
1532 NT_STATUS_OBJECT_NAME_NOT_FOUND = 0xC0000034
1533 NT_STATUS_INVALID_PARAMETER = 0xC000000D
1534 NT_STATUS_INVALID_INFO_CLASS = 0xC0000003
1535 NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE = 0xC002002E
1537 def _uint32(self, v):
1538 return ctypes.c_uint32(v).value
1540 def check_runtime_error(self, runtime, val):
1544 err32 = self._uint32(runtime[0])
1550 class LocalRuntimeError(CommandError):
1551 def __init__(exception_self, self, runtime, message):
1552 err32 = self._uint32(runtime[0])
1554 msg = "LOCAL_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1555 self.local_server, message, err32, errstr)
1556 CommandError.__init__(exception_self, msg)
1558 class RemoteRuntimeError(CommandError):
1559 def __init__(exception_self, self, runtime, message):
1560 err32 = self._uint32(runtime[0])
1562 msg = "REMOTE_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1563 self.remote_server, message, err32, errstr)
1564 CommandError.__init__(exception_self, msg)
1566 class LocalLdbError(CommandError):
1567 def __init__(exception_self, self, ldb_error, message):
1568 errval = ldb_error[0]
1569 errstr = ldb_error[1]
1570 msg = "LOCAL_DC[%s]: %s - ERROR(%d) - %s" % (
1571 self.local_server, message, errval, errstr)
1572 CommandError.__init__(exception_self, msg)
1574 def setup_local_server(self, sambaopts, localdcopts):
1575 if self.local_server is not None:
1576 return self.local_server
1578 lp = sambaopts.get_loadparm()
1580 local_server = localdcopts.ipaddress
1581 if local_server is None:
1582 server_role = lp.server_role()
1583 if server_role != "ROLE_ACTIVE_DIRECTORY_DC":
1584 raise CommandError("Invalid server_role %s" % (server_role))
1585 local_server = lp.get('netbios name')
1586 local_transport = "ncalrpc"
1587 local_binding_options = ""
1588 local_binding_options += ",auth_type=ncalrpc_as_system"
1589 local_ldap_url = None
1592 local_transport = "ncacn_np"
1593 local_binding_options = ""
1594 local_ldap_url = "ldap://%s" % local_server
1595 local_creds = localdcopts.get_credentials(lp)
1599 self.local_server = local_server
1600 self.local_binding_string = "%s:%s[%s]" % (local_transport, local_server, local_binding_options)
1601 self.local_ldap_url = local_ldap_url
1602 self.local_creds = local_creds
1603 return self.local_server
1605 def new_local_lsa_connection(self):
1606 return lsa.lsarpc(self.local_binding_string, self.local_lp, self.local_creds)
1608 def new_local_netlogon_connection(self):
1609 return netlogon.netlogon(self.local_binding_string, self.local_lp, self.local_creds)
1611 def new_local_ldap_connection(self):
1612 return SamDB(url=self.local_ldap_url,
1613 session_info=system_session(),
1614 credentials=self.local_creds,
1617 def setup_remote_server(self, credopts, domain,
1619 require_writable=True):
1622 assert require_writable
1624 if self.remote_server is not None:
1625 return self.remote_server
1627 self.remote_server = "__unknown__remote_server__.%s" % domain
1628 assert self.local_server is not None
1630 remote_creds = credopts.get_credentials(self.local_lp)
1631 remote_server = credopts.ipaddress
1632 remote_binding_options = ""
1634 # TODO: we should also support NT4 domains
1635 # we could use local_netlogon.netr_DsRGetDCNameEx2() with the remote domain name
1636 # and delegate NBT or CLDAP to the local netlogon server
1638 remote_net = Net(remote_creds, self.local_lp, server=remote_server)
1639 remote_flags = nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS
1640 if require_writable:
1641 remote_flags |= nbt.NBT_SERVER_WRITABLE
1643 remote_flags |= nbt.NBT_SERVER_PDC
1644 remote_info = remote_net.finddc(flags=remote_flags, domain=domain, address=remote_server)
1646 raise CommandError("Failed to find a writeable DC for domain '%s'" % domain)
1648 nbt.NBT_SERVER_PDC: "PDC",
1649 nbt.NBT_SERVER_GC: "GC",
1650 nbt.NBT_SERVER_LDAP: "LDAP",
1651 nbt.NBT_SERVER_DS: "DS",
1652 nbt.NBT_SERVER_KDC: "KDC",
1653 nbt.NBT_SERVER_TIMESERV: "TIMESERV",
1654 nbt.NBT_SERVER_CLOSEST: "CLOSEST",
1655 nbt.NBT_SERVER_WRITABLE: "WRITABLE",
1656 nbt.NBT_SERVER_GOOD_TIMESERV: "GOOD_TIMESERV",
1657 nbt.NBT_SERVER_NDNC: "NDNC",
1658 nbt.NBT_SERVER_SELECT_SECRET_DOMAIN_6: "SELECT_SECRET_DOMAIN_6",
1659 nbt.NBT_SERVER_FULL_SECRET_DOMAIN_6: "FULL_SECRET_DOMAIN_6",
1660 nbt.NBT_SERVER_ADS_WEB_SERVICE: "ADS_WEB_SERVICE",
1661 nbt.NBT_SERVER_DS_8: "DS_8",
1662 nbt.NBT_SERVER_HAS_DNS_NAME: "HAS_DNS_NAME",
1663 nbt.NBT_SERVER_IS_DEFAULT_NC: "IS_DEFAULT_NC",
1664 nbt.NBT_SERVER_FOREST_ROOT: "FOREST_ROOT",
1666 server_type_string = self.generic_bitmap_to_string(flag_map,
1667 remote_info.server_type, names_only=True)
1668 self.outf.write("RemoteDC Netbios[%s] DNS[%s] ServerType[%s]\n" % (
1669 remote_info.pdc_name,
1670 remote_info.pdc_dns_name,
1671 server_type_string))
1673 self.remote_server = remote_info.pdc_dns_name
1674 self.remote_binding_string="ncacn_np:%s[%s]" % (self.remote_server, remote_binding_options)
1675 self.remote_creds = remote_creds
1676 return self.remote_server
1678 def new_remote_lsa_connection(self):
1679 return lsa.lsarpc(self.remote_binding_string, self.local_lp, self.remote_creds)
1681 def new_remote_netlogon_connection(self):
1682 return netlogon.netlogon(self.remote_binding_string, self.local_lp, self.remote_creds)
1684 def get_lsa_info(self, conn, policy_access):
1685 objectAttr = lsa.ObjectAttribute()
1686 objectAttr.sec_qos = lsa.QosInfo()
1688 policy = conn.OpenPolicy2(''.decode('utf-8'),
1689 objectAttr, policy_access)
1691 info = conn.QueryInfoPolicy2(policy, lsa.LSA_POLICY_INFO_DNS)
1693 return (policy, info)
1695 def get_netlogon_dc_info(self, conn, server):
1696 info = conn.netr_DsRGetDCNameEx2(server,
1697 None, 0, None, None, None,
1698 netlogon.DS_RETURN_DNS_NAME)
1701 def netr_DomainTrust_to_name(self, t):
1702 if t.trust_type == lsa.LSA_TRUST_TYPE_DOWNLEVEL:
1703 return t.netbios_name
1707 def netr_DomainTrust_to_type(self, a, t):
1709 primary_parent = None
1711 if _t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
1713 if not _t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1714 primary_parent = a[_t.parent_index]
1717 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1718 if t is primary_parent:
1721 if t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1724 parent = a[t.parent_index]
1725 if parent is primary:
1730 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1735 def netr_DomainTrust_to_transitive(self, t):
1736 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1739 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE:
1742 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1747 def netr_DomainTrust_to_direction(self, t):
1748 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND and \
1749 t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1752 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND:
1755 if t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1760 def generic_enum_to_string(self, e_dict, v, names_only=False):
1764 v32 = self._uint32(v)
1765 w = "__unknown__%08X__" % v32
1767 r = "0x%x (%s)" % (v, w)
1770 def generic_bitmap_to_string(self, b_dict, v, names_only=False):
1775 for b in sorted(b_dict.keys()):
1782 c32 = self._uint32(c)
1783 s += ["__unknown_%08X__" % c32]
1788 r = "0x%x (%s)" % (v, w)
1791 def trustType_string(self, v):
1793 lsa.LSA_TRUST_TYPE_DOWNLEVEL : "DOWNLEVEL",
1794 lsa.LSA_TRUST_TYPE_UPLEVEL : "UPLEVEL",
1795 lsa.LSA_TRUST_TYPE_MIT : "MIT",
1796 lsa.LSA_TRUST_TYPE_DCE : "DCE",
1798 return self.generic_enum_to_string(types, v)
1800 def trustDirection_string(self, v):
1802 lsa.LSA_TRUST_DIRECTION_INBOUND |
1803 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "BOTH",
1804 lsa.LSA_TRUST_DIRECTION_INBOUND : "INBOUND",
1805 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "OUTBOUND",
1807 return self.generic_enum_to_string(directions, v)
1809 def trustAttributes_string(self, v):
1811 lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE : "NON_TRANSITIVE",
1812 lsa.LSA_TRUST_ATTRIBUTE_UPLEVEL_ONLY : "UPLEVEL_ONLY",
1813 lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN : "QUARANTINED_DOMAIN",
1814 lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE : "FOREST_TRANSITIVE",
1815 lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION : "CROSS_ORGANIZATION",
1816 lsa.LSA_TRUST_ATTRIBUTE_WITHIN_FOREST : "WITHIN_FOREST",
1817 lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL : "TREAT_AS_EXTERNAL",
1818 lsa.LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION : "USES_RC4_ENCRYPTION",
1820 return self.generic_bitmap_to_string(attributes, v)
1822 def kerb_EncTypes_string(self, v):
1824 security.KERB_ENCTYPE_DES_CBC_CRC : "DES_CBC_CRC",
1825 security.KERB_ENCTYPE_DES_CBC_MD5 : "DES_CBC_MD5",
1826 security.KERB_ENCTYPE_RC4_HMAC_MD5 : "RC4_HMAC_MD5",
1827 security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96 : "AES128_CTS_HMAC_SHA1_96",
1828 security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96 : "AES256_CTS_HMAC_SHA1_96",
1829 security.KERB_ENCTYPE_FAST_SUPPORTED : "FAST_SUPPORTED",
1830 security.KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED : "COMPOUND_IDENTITY_SUPPORTED",
1831 security.KERB_ENCTYPE_CLAIMS_SUPPORTED : "CLAIMS_SUPPORTED",
1832 security.KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED : "RESOURCE_SID_COMPRESSION_DISABLED",
1834 return self.generic_bitmap_to_string(enctypes, v)
1836 def entry_tln_status(self, e_flags, ):
1838 return "Status[Enabled]"
1841 lsa.LSA_TLN_DISABLED_NEW : "Disabled-New",
1842 lsa.LSA_TLN_DISABLED_ADMIN : "Disabled",
1843 lsa.LSA_TLN_DISABLED_CONFLICT : "Disabled-Conflicting",
1845 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
1847 def entry_dom_status(self, e_flags):
1849 return "Status[Enabled]"
1852 lsa.LSA_SID_DISABLED_ADMIN : "Disabled-SID",
1853 lsa.LSA_SID_DISABLED_CONFLICT : "Disabled-SID-Conflicting",
1854 lsa.LSA_NB_DISABLED_ADMIN : "Disabled-NB",
1855 lsa.LSA_NB_DISABLED_CONFLICT : "Disabled-NB-Conflicting",
1857 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
1859 def write_forest_trust_info(self, fti, tln=None, collisions=None):
1861 tln_string = " TDO[%s]" % tln
1865 self.outf.write("Namespaces[%d]%s:\n" % (
1866 len(fti.entries), tln_string))
1868 for i in xrange(0, len(fti.entries)):
1872 collision_string = ""
1874 if collisions is not None:
1875 for c in collisions.entries:
1879 collision_string = " Collision[%s]" % (c.name.string)
1881 d = e.forest_trust_data
1882 if e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
1883 self.outf.write("TLN: %-32s DNS[*.%s]%s\n" % (
1884 self.entry_tln_status(flags),
1885 d.string, collision_string))
1886 elif e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
1887 self.outf.write("TLN_EX: %-29s DNS[*.%s]\n" % (
1889 elif e.type == lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
1890 self.outf.write("DOM: %-32s DNS[%s] Netbios[%s] SID[%s]%s\n" % (
1891 self.entry_dom_status(flags),
1892 d.dns_domain_name.string,
1893 d.netbios_domain_name.string,
1894 d.domain_sid, collision_string))
1897 class cmd_domain_trust_list(DomainTrustCommand):
1898 """List domain trusts."""
1900 synopsis = "%prog [options]"
1902 takes_optiongroups = {
1903 "sambaopts": options.SambaOptions,
1904 "versionopts": options.VersionOptions,
1905 "localdcopts": LocalDCCredentialsOptions,
1911 def run(self, sambaopts=None, versionopts=None, localdcopts=None):
1913 local_server = self.setup_local_server(sambaopts, localdcopts)
1915 local_netlogon = self.new_local_netlogon_connection()
1916 except RuntimeError as error:
1917 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
1920 local_netlogon_trusts = local_netlogon.netr_DsrEnumerateDomainTrusts(local_server,
1921 netlogon.NETR_TRUST_FLAG_IN_FOREST |
1922 netlogon.NETR_TRUST_FLAG_OUTBOUND |
1923 netlogon.NETR_TRUST_FLAG_INBOUND)
1924 except RuntimeError as error:
1925 if self.check_runtime_error(error, self.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
1926 # TODO: we could implement a fallback to lsa.EnumTrustDom()
1927 raise CommandError("LOCAL_DC[%s]: netr_DsrEnumerateDomainTrusts not supported." % (
1929 raise self.LocalRuntimeError(self, error, "netr_DsrEnumerateDomainTrusts failed")
1931 a = local_netlogon_trusts.array
1933 if t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
1935 self.outf.write("%-14s %-15s %-19s %s\n" % (
1936 "Type[%s]" % self.netr_DomainTrust_to_type(a, t),
1937 "Transitive[%s]" % self.netr_DomainTrust_to_transitive(t),
1938 "Direction[%s]" % self.netr_DomainTrust_to_direction(t),
1939 "Name[%s]" % self.netr_DomainTrust_to_name(t)))
1942 class cmd_domain_trust_show(DomainTrustCommand):
1943 """Show trusted domain details."""
1945 synopsis = "%prog NAME [options]"
1947 takes_optiongroups = {
1948 "sambaopts": options.SambaOptions,
1949 "versionopts": options.VersionOptions,
1950 "localdcopts": LocalDCCredentialsOptions,
1956 takes_args = ["domain"]
1958 def run(self, domain, sambaopts=None, versionopts=None, localdcopts=None):
1960 local_server = self.setup_local_server(sambaopts, localdcopts)
1962 local_lsa = self.new_local_lsa_connection()
1963 except RuntimeError as error:
1964 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
1967 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
1968 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
1969 except RuntimeError as error:
1970 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
1972 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
1973 local_lsa_info.name.string,
1974 local_lsa_info.dns_domain.string,
1975 local_lsa_info.sid))
1977 lsaString = lsa.String()
1978 lsaString.string = domain
1980 local_tdo_full = local_lsa.QueryTrustedDomainInfoByName(local_policy,
1981 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
1982 local_tdo_info = local_tdo_full.info_ex
1983 local_tdo_posix = local_tdo_full.posix_offset
1984 except RuntimeError as error:
1985 if self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
1986 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
1988 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(FULL_INFO) failed")
1991 local_tdo_enctypes = local_lsa.QueryTrustedDomainInfoByName(local_policy,
1992 lsaString, lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES)
1993 except RuntimeError as error:
1994 if self.check_runtime_error(error, self.NT_STATUS_INVALID_PARAMETER):
1996 if self.check_runtime_error(error, self.NT_STATUS_INVALID_INFO_CLASS):
1999 if error is not None:
2000 raise self.LocalRuntimeError(self, error,
2001 "QueryTrustedDomainInfoByName(SUPPORTED_ENCRYPTION_TYPES) failed")
2003 local_tdo_enctypes = lsa.TrustDomainInfoSupportedEncTypes()
2004 local_tdo_enctypes.enc_types = 0
2007 local_tdo_forest = None
2008 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2009 local_tdo_forest = local_lsa.lsaRQueryForestTrustInformation(local_policy,
2010 lsaString, lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
2011 except RuntimeError as error:
2012 if self.check_runtime_error(error, self.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
2014 if self.check_runtime_error(error, self.NT_STATUS_NOT_FOUND):
2016 if error is not None:
2017 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation failed")
2019 local_tdo_forest = lsa.ForestTrustInformation()
2020 local_tdo_forest.count = 0
2021 local_tdo_forest.entries = []
2023 self.outf.write("TrusteDomain:\n\n");
2024 self.outf.write("NetbiosName: %s\n" % local_tdo_info.netbios_name.string)
2025 if local_tdo_info.netbios_name.string != local_tdo_info.domain_name.string:
2026 self.outf.write("DnsName: %s\n" % local_tdo_info.domain_name.string)
2027 self.outf.write("SID: %s\n" % local_tdo_info.sid)
2028 self.outf.write("Type: %s\n" % self.trustType_string(local_tdo_info.trust_type))
2029 self.outf.write("Direction: %s\n" % self.trustDirection_string(local_tdo_info.trust_direction))
2030 self.outf.write("Attributes: %s\n" % self.trustAttributes_string(local_tdo_info.trust_attributes))
2031 posix_offset_u32 = ctypes.c_uint32(local_tdo_posix.posix_offset).value
2032 posix_offset_i32 = ctypes.c_int32(local_tdo_posix.posix_offset).value
2033 self.outf.write("PosixOffset: 0x%08X (%d)\n" % (posix_offset_u32, posix_offset_i32))
2034 self.outf.write("kerb_EncTypes: %s\n" % self.kerb_EncTypes_string(local_tdo_enctypes.enc_types))
2036 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2037 self.write_forest_trust_info(local_tdo_forest,
2038 tln=local_tdo_info.domain_name.string)
2042 class cmd_domain_trust_create(DomainTrustCommand):
2043 """Create a domain or forest trust."""
2045 synopsis = "%prog DOMAIN [options]"
2047 takes_optiongroups = {
2048 "sambaopts": options.SambaOptions,
2049 "versionopts": options.VersionOptions,
2050 "credopts": options.CredentialsOptions,
2051 "localdcopts": LocalDCCredentialsOptions,
2055 Option("--type", type="choice", metavar="TYPE",
2056 choices=["external", "forest"],
2057 help="The type of the trust: 'external' or 'forest'.",
2059 default="external"),
2060 Option("--direction", type="choice", metavar="DIRECTION",
2061 choices=["incoming", "outgoing", "both"],
2062 help="The trust direction: 'incoming', 'outgoing' or 'both'.",
2063 dest='trust_direction',
2065 Option("--create-location", type="choice", metavar="LOCATION",
2066 choices=["local", "both"],
2067 help="Where to create the trusted domain object: 'local' or 'both'.",
2068 dest='create_location',
2070 Option("--cross-organisation", action="store_true",
2071 help="The related domains does not belong to the same organisation.",
2072 dest='cross_organisation',
2074 Option("--quarantined", type="choice", metavar="yes|no",
2075 choices=["yes", "no", None],
2076 help="Special SID filtering rules are applied to the trust. "
2077 "With --type=external the default is yes. "
2078 "With --type=forest the default is no.",
2079 dest='quarantined_arg',
2081 Option("--not-transitive", action="store_true",
2082 help="The forest trust is not transitive.",
2083 dest='not_transitive',
2085 Option("--treat-as-external", action="store_true",
2086 help="The treat the forest trust as external.",
2087 dest='treat_as_external',
2089 Option("--no-aes-keys", action="store_false",
2090 help="The trust uses aes kerberos keys.",
2091 dest='use_aes_keys',
2093 Option("--skip-validation", action="store_false",
2094 help="Skip validation of the trust.",
2099 takes_args = ["domain"]
2101 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2102 trust_type=None, trust_direction=None, create_location=None,
2103 cross_organisation=False, quarantined_arg=None,
2104 not_transitive=False, treat_as_external=False,
2105 use_aes_keys=False, validate=True):
2107 lsaString = lsa.String()
2110 if quarantined_arg is None:
2111 if trust_type == 'external':
2113 elif quarantined_arg == 'yes':
2116 if trust_type != 'forest':
2118 raise CommandError("--not-transitive requires --type=forest")
2119 if treat_as_external:
2120 raise CommandError("--treat-as-external requires --type=forest")
2124 enc_types = lsa.TrustDomainInfoSupportedEncTypes()
2125 enc_types.enc_types = security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96
2126 enc_types.enc_types |= security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96
2128 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2129 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2130 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2132 local_trust_info = lsa.TrustDomainInfoInfoEx()
2133 local_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2134 local_trust_info.trust_direction = 0
2135 if trust_direction == "both":
2136 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2137 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2138 elif trust_direction == "incoming":
2139 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2140 elif trust_direction == "outgoing":
2141 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2142 local_trust_info.trust_attributes = 0
2143 if cross_organisation:
2144 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2146 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2147 if trust_type == "forest":
2148 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2150 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2151 if treat_as_external:
2152 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2154 def get_password(name):
2157 if password is not None and password is not '':
2159 password = getpass("New %s Password: " % name)
2160 passwordverify = getpass("Retype %s Password: " % name)
2161 if not password == passwordverify:
2163 self.outf.write("Sorry, passwords do not match.\n")
2165 def string_to_array(string):
2166 blob = [0] * len(string)
2168 for i in range(len(string)):
2169 blob[i] = ord(string[i])
2173 incoming_secret = None
2174 outgoing_secret = None
2175 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2176 if create_location == "local":
2177 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2178 incoming_password = get_password("Incoming Trust")
2179 incoming_secret = string_to_array(incoming_password.encode('utf-16-le'))
2180 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2181 outgoing_password = get_password("Outgoing Trust")
2182 outgoing_secret = string_to_array(outgoing_password.encode('utf-16-le'))
2184 remote_trust_info = None
2186 # We use 240 random bytes.
2187 # Windows uses 28 or 240 random bytes. I guess it's
2188 # based on the trust type external vs. forest.
2190 # The initial trust password can be up to 512 bytes
2191 # while the versioned passwords used for periodic updates
2192 # can only be up to 498 bytes, as netr_ServerPasswordSet2()
2193 # needs to pass the NL_PASSWORD_VERSION structure within the
2194 # 512 bytes and a 2 bytes confounder is required.
2196 def random_trust_secret(length, use_aes_keys=True):
2197 secret = [0] * length
2199 pw1 = samba.generate_random_password(length/2, length/2)
2200 if not use_aes_keys:
2201 # With arcfour-hmac-md5 we have to use valid utf16
2202 # in order to generate the correct pre-auth key
2203 # based on a utf8 password.
2205 # We can remove this once our client libraries
2206 # support using the correct NTHASH.
2207 return string_to_array(pw1.encode('utf-16-le'))
2209 # We mix characters from generate_random_password
2210 # with random numbers from random.randint()
2211 for i in range(len(secret)):
2213 secret[i] = ord(pw1[i])
2215 secret[i] = random.randint(0, 255)
2219 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2220 incoming_secret = random_trust_secret(240, use_aes_keys=use_aes_keys)
2221 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2222 outgoing_secret = random_trust_secret(240, use_aes_keys=use_aes_keys)
2224 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2225 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2227 remote_trust_info = lsa.TrustDomainInfoInfoEx()
2228 remote_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2229 remote_trust_info.trust_direction = 0
2230 if trust_direction == "both":
2231 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2232 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2233 elif trust_direction == "incoming":
2234 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2235 elif trust_direction == "outgoing":
2236 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2237 remote_trust_info.trust_attributes = 0
2238 if cross_organisation:
2239 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2241 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2242 if trust_type == "forest":
2243 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2245 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2246 if treat_as_external:
2247 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2249 local_server = self.setup_local_server(sambaopts, localdcopts)
2251 local_lsa = self.new_local_lsa_connection()
2252 except RuntimeError as error:
2253 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2256 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2257 except RuntimeError as error:
2258 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2260 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2261 local_lsa_info.name.string,
2262 local_lsa_info.dns_domain.string,
2263 local_lsa_info.sid))
2266 remote_server = self.setup_remote_server(credopts, domain)
2267 except RuntimeError as error:
2268 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2271 remote_lsa = self.new_remote_lsa_connection()
2272 except RuntimeError as error:
2273 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2276 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2277 except RuntimeError as error:
2278 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2280 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2281 remote_lsa_info.name.string,
2282 remote_lsa_info.dns_domain.string,
2283 remote_lsa_info.sid))
2285 local_trust_info.domain_name.string = remote_lsa_info.dns_domain.string
2286 local_trust_info.netbios_name.string = remote_lsa_info.name.string
2287 local_trust_info.sid = remote_lsa_info.sid
2289 if remote_trust_info:
2290 remote_trust_info.domain_name.string = local_lsa_info.dns_domain.string
2291 remote_trust_info.netbios_name.string = local_lsa_info.name.string
2292 remote_trust_info.sid = local_lsa_info.sid
2295 lsaString.string = local_trust_info.domain_name.string
2296 local_old_netbios = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2297 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2298 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2299 except RuntimeError as error:
2300 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2301 raise self.LocalRuntimeError(self, error,
2302 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2306 lsaString.string = local_trust_info.netbios_name.string
2307 local_old_dns = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2308 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2309 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2310 except RuntimeError as error:
2311 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2312 raise self.LocalRuntimeError(self, error,
2313 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2316 if remote_trust_info:
2318 lsaString.string = remote_trust_info.domain_name.string
2319 remote_old_netbios = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2320 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2321 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2322 except RuntimeError as error:
2323 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2324 raise self.RemoteRuntimeError(self, error,
2325 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2329 lsaString.string = remote_trust_info.netbios_name.string
2330 remote_old_dns = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2331 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2332 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2333 except RuntimeError as error:
2334 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2335 raise self.RemoteRuntimeError(self, error,
2336 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2340 local_netlogon = self.new_local_netlogon_connection()
2341 except RuntimeError as error:
2342 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2345 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
2346 except RuntimeError as error:
2347 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
2349 if remote_trust_info:
2351 remote_netlogon = self.new_remote_netlogon_connection()
2352 except RuntimeError as error:
2353 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2356 remote_netlogon_info = self.get_netlogon_dc_info(remote_netlogon, remote_server)
2357 except RuntimeError as error:
2358 raise self.RemoteRuntimeError(self, error, "failed to get netlogon dc info")
2360 def arcfour_encrypt(key, data):
2361 from Crypto.Cipher import ARC4
2363 return c.encrypt(data)
2365 def generate_AuthInOutBlob(secret, update_time):
2367 blob = drsblobs.trustAuthInOutBlob()
2372 clear = drsblobs.AuthInfoClear()
2373 clear.size = len(secret)
2374 clear.password = secret
2376 info = drsblobs.AuthenticationInformation()
2377 info.LastUpdateTime = samba.unix2nttime(update_time)
2378 info.AuthType = lsa.TRUST_AUTH_TYPE_CLEAR
2379 info.AuthInfo = clear
2381 array = drsblobs.AuthenticationInformationArray()
2383 array.array = [info]
2385 blob = drsblobs.trustAuthInOutBlob()
2387 blob.current = array
2391 def generate_AuthInfoInternal(session_key, incoming=None, outgoing=None):
2392 confounder = [0] * 512
2393 for i in range(len(confounder)):
2394 confounder[i] = random.randint(0, 255)
2396 trustpass = drsblobs.trustDomainPasswords()
2398 trustpass.confounder = confounder
2399 trustpass.outgoing = outgoing
2400 trustpass.incoming = incoming
2402 trustpass_blob = ndr_pack(trustpass)
2404 encrypted_trustpass = arcfour_encrypt(session_key, trustpass_blob)
2406 auth_blob = lsa.DATA_BUF2()
2407 auth_blob.size = len(encrypted_trustpass)
2408 auth_blob.data = string_to_array(encrypted_trustpass)
2410 auth_info = lsa.TrustDomainInfoAuthInfoInternal()
2411 auth_info.auth_blob = auth_blob
2415 update_time = samba.current_unix_time()
2416 incoming_blob = generate_AuthInOutBlob(incoming_secret, update_time)
2417 outgoing_blob = generate_AuthInOutBlob(outgoing_secret, update_time)
2419 local_tdo_handle = None
2420 remote_tdo_handle = None
2422 local_auth_info = generate_AuthInfoInternal(local_lsa.session_key,
2423 incoming=incoming_blob,
2424 outgoing=outgoing_blob)
2425 if remote_trust_info:
2426 remote_auth_info = generate_AuthInfoInternal(remote_lsa.session_key,
2427 incoming=outgoing_blob,
2428 outgoing=incoming_blob)
2431 if remote_trust_info:
2432 self.outf.write("Creating remote TDO.\n")
2433 current_request = { "location": "remote", "name": "CreateTrustedDomainEx2"}
2434 remote_tdo_handle = remote_lsa.CreateTrustedDomainEx2(remote_policy,
2437 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2438 self.outf.write("Remote TDO created.\n")
2440 self.outf.write("Setting supported encryption types on remote TDO.\n")
2441 current_request = { "location": "remote", "name": "SetInformationTrustedDomain"}
2442 remote_lsa.SetInformationTrustedDomain(remote_tdo_handle,
2443 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2446 self.outf.write("Creating local TDO.\n")
2447 current_request = { "location": "local", "name": "CreateTrustedDomainEx2"}
2448 local_tdo_handle = local_lsa.CreateTrustedDomainEx2(local_policy,
2451 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2452 self.outf.write("Local TDO created\n")
2454 self.outf.write("Setting supported encryption types on local TDO.\n")
2455 current_request = { "location": "local", "name": "SetInformationTrustedDomain"}
2456 local_lsa.SetInformationTrustedDomain(local_tdo_handle,
2457 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2459 except RuntimeError as error:
2460 self.outf.write("Error: %s failed %sly - cleaning up\n" % (
2461 current_request['name'], current_request['location']))
2462 if remote_tdo_handle:
2463 self.outf.write("Deleting remote TDO.\n")
2464 remote_lsa.DeleteObject(remote_tdo_handle)
2465 remote_tdo_handle = None
2466 if local_tdo_handle:
2467 self.outf.write("Deleting local TDO.\n")
2468 local_lsa.DeleteObject(local_tdo_handle)
2469 local_tdo_handle = None
2470 if current_request['location'] is "remote":
2471 raise self.RemoteRuntimeError(self, error, "%s" % (
2472 current_request['name']))
2473 raise self.LocalRuntimeError(self, error, "%s" % (
2474 current_request['name']))
2477 if local_trust_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2478 self.outf.write("Setup local forest trust information...\n")
2480 # get all information about the remote trust
2481 # this triggers netr_GetForestTrustInformation to the remote domain
2482 # and lsaRSetForestTrustInformation() locally, but new top level
2483 # names are disabled by default.
2484 local_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
2485 remote_lsa_info.dns_domain.string,
2486 netlogon.DS_GFTI_UPDATE_TDO)
2487 except RuntimeError as error:
2488 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2491 # here we try to enable all top level names
2492 local_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
2493 remote_lsa_info.dns_domain,
2494 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2497 except RuntimeError as error:
2498 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2500 self.write_forest_trust_info(local_forest_info,
2501 tln=remote_lsa_info.dns_domain.string,
2502 collisions=local_forest_collision)
2504 if remote_trust_info:
2505 self.outf.write("Setup remote forest trust information...\n")
2507 # get all information about the local trust (from the perspective of the remote domain)
2508 # this triggers netr_GetForestTrustInformation to our domain.
2509 # and lsaRSetForestTrustInformation() remotely, but new top level
2510 # names are disabled by default.
2511 remote_forest_info = remote_netlogon.netr_DsRGetForestTrustInformation(remote_netlogon_info.dc_unc,
2512 local_lsa_info.dns_domain.string,
2513 netlogon.DS_GFTI_UPDATE_TDO)
2514 except RuntimeError as error:
2515 raise self.RemoteRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2518 # here we try to enable all top level names
2519 remote_forest_collision = remote_lsa.lsaRSetForestTrustInformation(remote_policy,
2520 local_lsa_info.dns_domain,
2521 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2524 except RuntimeError as error:
2525 raise self.RemoteRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2527 self.write_forest_trust_info(remote_forest_info,
2528 tln=local_lsa_info.dns_domain.string,
2529 collisions=remote_forest_collision)
2531 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2532 self.outf.write("Validating outgoing trust...\n")
2534 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_netlogon_info.dc_unc,
2535 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2537 remote_lsa_info.dns_domain.string)
2538 except RuntimeError as error:
2539 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2541 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2542 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2544 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2545 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2546 local_trust_verify.trusted_dc_name,
2547 local_trust_verify.tc_connection_status[1],
2548 local_trust_verify.pdc_connection_status[1])
2550 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2551 local_trust_verify.trusted_dc_name,
2552 local_trust_verify.tc_connection_status[1],
2553 local_trust_verify.pdc_connection_status[1])
2555 if local_trust_status != self.WERR_OK or local_conn_status != self.WERR_OK:
2556 raise CommandError(local_validation)
2558 self.outf.write("OK: %s\n" % local_validation)
2560 if remote_trust_info:
2561 if remote_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2562 self.outf.write("Validating incoming trust...\n")
2564 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_netlogon_info.dc_unc,
2565 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2567 local_lsa_info.dns_domain.string)
2568 except RuntimeError as error:
2569 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2571 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2572 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2574 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2575 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2576 remote_trust_verify.trusted_dc_name,
2577 remote_trust_verify.tc_connection_status[1],
2578 remote_trust_verify.pdc_connection_status[1])
2580 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2581 remote_trust_verify.trusted_dc_name,
2582 remote_trust_verify.tc_connection_status[1],
2583 remote_trust_verify.pdc_connection_status[1])
2585 if remote_trust_status != self.WERR_OK or remote_conn_status != self.WERR_OK:
2586 raise CommandError(remote_validation)
2588 self.outf.write("OK: %s\n" % remote_validation)
2590 if remote_tdo_handle is not None:
2592 remote_lsa.Close(remote_tdo_handle)
2593 except RuntimeError as error:
2595 remote_tdo_handle = None
2596 if local_tdo_handle is not None:
2598 local_lsa.Close(local_tdo_handle)
2599 except RuntimeError as error:
2601 local_tdo_handle = None
2603 self.outf.write("Success.\n")
2606 class cmd_domain_trust_delete(DomainTrustCommand):
2607 """Delete a domain trust."""
2609 synopsis = "%prog DOMAIN [options]"
2611 takes_optiongroups = {
2612 "sambaopts": options.SambaOptions,
2613 "versionopts": options.VersionOptions,
2614 "credopts": options.CredentialsOptions,
2615 "localdcopts": LocalDCCredentialsOptions,
2619 Option("--delete-location", type="choice", metavar="LOCATION",
2620 choices=["local", "both"],
2621 help="Where to delete the trusted domain object: 'local' or 'both'.",
2622 dest='delete_location',
2626 takes_args = ["domain"]
2628 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2629 delete_location=None):
2631 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2632 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2633 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2635 if delete_location == "local":
2636 remote_policy_access = None
2638 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2639 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2640 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2642 local_server = self.setup_local_server(sambaopts, localdcopts)
2644 local_lsa = self.new_local_lsa_connection()
2645 except RuntimeError as error:
2646 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2649 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2650 except RuntimeError as error:
2651 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2653 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2654 local_lsa_info.name.string,
2655 local_lsa_info.dns_domain.string,
2656 local_lsa_info.sid))
2658 local_tdo_info = None
2659 local_tdo_handle = None
2660 remote_tdo_info = None
2661 remote_tdo_handle = None
2663 lsaString = lsa.String()
2665 lsaString.string = domain
2666 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2667 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2668 except RuntimeError as error:
2669 if self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2670 raise CommandError("Failed to find trust for domain '%s'" % domain)
2671 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2674 if remote_policy_access is not None:
2676 remote_server = self.setup_remote_server(credopts, domain)
2677 except RuntimeError as error:
2678 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2681 remote_lsa = self.new_remote_lsa_connection()
2682 except RuntimeError as error:
2683 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2686 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2687 except RuntimeError as error:
2688 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2690 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2691 remote_lsa_info.name.string,
2692 remote_lsa_info.dns_domain.string,
2693 remote_lsa_info.sid))
2695 if remote_lsa_info.sid != local_tdo_info.sid or \
2696 remote_lsa_info.name.string != local_tdo_info.netbios_name.string or \
2697 remote_lsa_info.dns_domain.string != local_tdo_info.domain_name.string:
2698 raise CommandError("LocalTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2699 local_tdo_info.netbios_name.string,
2700 local_tdo_info.domain_name.string,
2701 local_tdo_info.sid))
2704 lsaString.string = local_lsa_info.dns_domain.string
2705 remote_tdo_info = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2706 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2707 except RuntimeError as error:
2708 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2709 raise self.RemoteRuntimeError(self, error, "QueryTrustedDomainInfoByName(%s)" % (
2713 if remote_tdo_info is not None:
2714 if local_lsa_info.sid != remote_tdo_info.sid or \
2715 local_lsa_info.name.string != remote_tdo_info.netbios_name.string or \
2716 local_lsa_info.dns_domain.string != remote_tdo_info.domain_name.string:
2717 raise CommandError("RemoteTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2718 remote_tdo_info.netbios_name.string,
2719 remote_tdo_info.domain_name.string,
2720 remote_tdo_info.sid))
2722 if local_tdo_info is not None:
2724 lsaString.string = local_tdo_info.domain_name.string
2725 local_tdo_handle = local_lsa.OpenTrustedDomainByName(local_policy,
2727 security.SEC_STD_DELETE)
2728 except RuntimeError as error:
2729 raise self.LocalRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2732 local_lsa.DeleteObject(local_tdo_handle)
2733 local_tdo_handle = None
2735 if remote_tdo_info is not None:
2737 lsaString.string = remote_tdo_info.domain_name.string
2738 remote_tdo_handle = remote_lsa.OpenTrustedDomainByName(remote_policy,
2740 security.SEC_STD_DELETE)
2741 except RuntimeError as error:
2742 raise self.RemoteRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2745 if remote_tdo_handle is not None:
2747 remote_lsa.DeleteObject(remote_tdo_handle)
2748 remote_tdo_handle = None
2749 self.outf.write("RemoteTDO deleted.\n")
2750 except RuntimeError as error:
2751 self.outf.write("%s\n" % self.RemoteRuntimeError(self, error, "DeleteObject() failed"))
2753 if local_tdo_handle is not None:
2755 local_lsa.DeleteObject(local_tdo_handle)
2756 local_tdo_handle = None
2757 self.outf.write("LocalTDO deleted.\n")
2758 except RuntimeError as error:
2759 self.outf.write("%s\n" % self.LocalRuntimeError(self, error, "DeleteObject() failed"))
2763 class cmd_domain_trust_validate(DomainTrustCommand):
2764 """Validate a domain trust."""
2766 synopsis = "%prog DOMAIN [options]"
2768 takes_optiongroups = {
2769 "sambaopts": options.SambaOptions,
2770 "versionopts": options.VersionOptions,
2771 "credopts": options.CredentialsOptions,
2772 "localdcopts": LocalDCCredentialsOptions,
2776 Option("--validate-location", type="choice", metavar="LOCATION",
2777 choices=["local", "both"],
2778 help="Where to validate the trusted domain object: 'local' or 'both'.",
2779 dest='validate_location',
2783 takes_args = ["domain"]
2785 def run(self, domain, sambaopts=None, versionopts=None, credopts=None, localdcopts=None,
2786 validate_location=None):
2788 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2790 local_server = self.setup_local_server(sambaopts, localdcopts)
2792 local_lsa = self.new_local_lsa_connection()
2793 except RuntimeError as error:
2794 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2797 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2798 except RuntimeError as error:
2799 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2801 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2802 local_lsa_info.name.string,
2803 local_lsa_info.dns_domain.string,
2804 local_lsa_info.sid))
2807 lsaString = lsa.String()
2808 lsaString.string = domain
2809 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2810 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2811 except RuntimeError as error:
2812 if self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2813 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2815 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
2817 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
2818 local_tdo_info.netbios_name.string,
2819 local_tdo_info.domain_name.string,
2820 local_tdo_info.sid))
2823 local_netlogon = self.new_local_netlogon_connection()
2824 except RuntimeError as error:
2825 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2828 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_server,
2829 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2831 local_tdo_info.domain_name.string)
2832 except RuntimeError as error:
2833 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2835 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2836 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2838 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2839 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2840 local_trust_verify.trusted_dc_name,
2841 local_trust_verify.tc_connection_status[1],
2842 local_trust_verify.pdc_connection_status[1])
2844 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2845 local_trust_verify.trusted_dc_name,
2846 local_trust_verify.tc_connection_status[1],
2847 local_trust_verify.pdc_connection_status[1])
2849 if local_trust_status != self.WERR_OK or local_conn_status != self.WERR_OK:
2850 raise CommandError(local_validation)
2852 self.outf.write("OK: %s\n" % local_validation)
2855 server = local_trust_verify.trusted_dc_name.replace('\\', '')
2856 domain_and_server = "%s\\%s" % (local_tdo_info.domain_name.string, server)
2857 local_trust_rediscover = local_netlogon.netr_LogonControl2Ex(local_server,
2858 netlogon.NETLOGON_CONTROL_REDISCOVER,
2861 except RuntimeError as error:
2862 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
2864 local_conn_status = self._uint32(local_trust_rediscover.tc_connection_status[0])
2865 local_rediscover = "LocalRediscover: DC[%s] CONNECTION[%s]" % (
2866 local_trust_rediscover.trusted_dc_name,
2867 local_trust_rediscover.tc_connection_status[1])
2869 if local_conn_status != self.WERR_OK:
2870 raise CommandError(local_rediscover)
2872 self.outf.write("OK: %s\n" % local_rediscover)
2874 if validate_location != "local":
2876 remote_server = self.setup_remote_server(credopts, domain, require_pdc=False)
2877 except RuntimeError as error:
2878 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2881 remote_netlogon = self.new_remote_netlogon_connection()
2882 except RuntimeError as error:
2883 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2886 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_server,
2887 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2889 local_lsa_info.dns_domain.string)
2890 except RuntimeError as error:
2891 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2893 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2894 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2896 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2897 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2898 remote_trust_verify.trusted_dc_name,
2899 remote_trust_verify.tc_connection_status[1],
2900 remote_trust_verify.pdc_connection_status[1])
2902 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2903 remote_trust_verify.trusted_dc_name,
2904 remote_trust_verify.tc_connection_status[1],
2905 remote_trust_verify.pdc_connection_status[1])
2907 if remote_trust_status != self.WERR_OK or remote_conn_status != self.WERR_OK:
2908 raise CommandError(remote_validation)
2910 self.outf.write("OK: %s\n" % remote_validation)
2913 server = remote_trust_verify.trusted_dc_name.replace('\\', '')
2914 domain_and_server = "%s\\%s" % (local_lsa_info.dns_domain.string, server)
2915 remote_trust_rediscover = remote_netlogon.netr_LogonControl2Ex(remote_server,
2916 netlogon.NETLOGON_CONTROL_REDISCOVER,
2919 except RuntimeError as error:
2920 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
2922 remote_conn_status = self._uint32(remote_trust_rediscover.tc_connection_status[0])
2924 remote_rediscover = "RemoteRediscover: DC[%s] CONNECTION[%s]" % (
2925 remote_trust_rediscover.trusted_dc_name,
2926 remote_trust_rediscover.tc_connection_status[1])
2928 if remote_conn_status != self.WERR_OK:
2929 raise CommandError(remote_rediscover)
2931 self.outf.write("OK: %s\n" % remote_rediscover)
2935 class cmd_domain_trust_namespaces(DomainTrustCommand):
2936 """Manage forest trust namespaces."""
2938 synopsis = "%prog [DOMAIN] [options]"
2940 takes_optiongroups = {
2941 "sambaopts": options.SambaOptions,
2942 "versionopts": options.VersionOptions,
2943 "localdcopts": LocalDCCredentialsOptions,
2947 Option("--refresh", type="choice", metavar="check|store",
2948 choices=["check", "store", None],
2949 help="List and maybe store refreshed forest trust information: 'check' or 'store'.",
2952 Option("--enable-all", action="store_true",
2953 help="Try to update disabled entries, not allowed with --refresh=check.",
2956 Option("--enable-tln", action="append", metavar='DNSDOMAIN',
2957 help="Enable a top level name entry. Can be specified multiple times.",
2960 Option("--disable-tln", action="append", metavar='DNSDOMAIN',
2961 help="Disable a top level name entry. Can be specified multiple times.",
2964 Option("--add-tln-ex", action="append", metavar='DNSDOMAIN',
2965 help="Add a top level exclusion entry. Can be specified multiple times.",
2968 Option("--delete-tln-ex", action="append", metavar='DNSDOMAIN',
2969 help="Delete a top level exclusion entry. Can be specified multiple times.",
2970 dest='delete_tln_ex',
2972 Option("--enable-nb", action="append", metavar='NETBIOSDOMAIN',
2973 help="Enable a netbios name in a domain entry. Can be specified multiple times.",
2976 Option("--disable-nb", action="append", metavar='NETBIOSDOMAIN',
2977 help="Disable a netbios name in a domain entry. Can be specified multiple times.",
2980 Option("--enable-sid", action="append", metavar='DOMAINSID',
2981 help="Enable a SID in a domain entry. Can be specified multiple times.",
2982 dest='enable_sid_str',
2984 Option("--disable-sid", action="append", metavar='DOMAINSID',
2985 help="Disable a SID in a domain entry. Can be specified multiple times.",
2986 dest='disable_sid_str',
2988 Option("--add-upn-suffix", action="append", metavar='DNSDOMAIN',
2989 help="Add a new uPNSuffixes attribute for the local forest. Can be specified multiple times.",
2992 Option("--delete-upn-suffix", action="append", metavar='DNSDOMAIN',
2993 help="Delete an existing uPNSuffixes attribute of the local forest. Can be specified multiple times.",
2996 Option("--add-spn-suffix", action="append", metavar='DNSDOMAIN',
2997 help="Add a new msDS-SPNSuffixes attribute for the local forest. Can be specified multiple times.",
3000 Option("--delete-spn-suffix", action="append", metavar='DNSDOMAIN',
3001 help="Delete an existing msDS-SPNSuffixes attribute of the local forest. Can be specified multiple times.",
3006 takes_args = ["domain?"]
3008 def run(self, domain=None, sambaopts=None, localdcopts=None, versionopts=None,
3009 refresh=None, enable_all=False,
3010 enable_tln=[], disable_tln=[], add_tln_ex=[], delete_tln_ex=[],
3011 enable_sid_str=[], disable_sid_str=[], enable_nb=[], disable_nb=[],
3012 add_upn=[], delete_upn=[], add_spn=[], delete_spn=[]):
3014 require_update = False
3017 if refresh == "store":
3018 raise CommandError("--refresh=%s not allowed without DOMAIN" % refresh)
3021 raise CommandError("--enable-all not allowed without DOMAIN")
3023 if len(enable_tln) > 0:
3024 raise CommandError("--enable-tln not allowed without DOMAIN")
3025 if len(disable_tln) > 0:
3026 raise CommandError("--disable-tln not allowed without DOMAIN")
3028 if len(add_tln_ex) > 0:
3029 raise CommandError("--add-tln-ex not allowed without DOMAIN")
3030 if len(delete_tln_ex) > 0:
3031 raise CommandError("--delete-tln-ex not allowed without DOMAIN")
3033 if len(enable_nb) > 0:
3034 raise CommandError("--enable-nb not allowed without DOMAIN")
3035 if len(disable_nb) > 0:
3036 raise CommandError("--disable-nb not allowed without DOMAIN")
3038 if len(enable_sid_str) > 0:
3039 raise CommandError("--enable-sid not allowed without DOMAIN")
3040 if len(disable_sid_str) > 0:
3041 raise CommandError("--disable-sid not allowed without DOMAIN")
3043 if len(add_upn) > 0:
3045 if not n.startswith("*."):
3047 raise CommandError("value[%s] specified for --add-upn-suffix should not include with '*.'" % n)
3048 require_update = True
3049 if len(delete_upn) > 0:
3050 for n in delete_upn:
3051 if not n.startswith("*."):
3053 raise CommandError("value[%s] specified for --delete-upn-suffix should not include with '*.'" % n)
3054 require_update = True
3056 for d in delete_upn:
3057 if a.lower() != d.lower():
3059 raise CommandError("value[%s] specified for --add-upn-suffix and --delete-upn-suffix" % a)
3061 if len(add_spn) > 0:
3063 if not n.startswith("*."):
3065 raise CommandError("value[%s] specified for --add-spn-suffix should not include with '*.'" % n)
3066 require_update = True
3067 if len(delete_spn) > 0:
3068 for n in delete_spn:
3069 if not n.startswith("*."):
3071 raise CommandError("value[%s] specified for --delete-spn-suffix should not include with '*.'" % n)
3072 require_update = True
3074 for d in delete_spn:
3075 if a.lower() != d.lower():
3077 raise CommandError("value[%s] specified for --add-spn-suffix and --delete-spn-suffix" % a)
3079 if len(add_upn) > 0:
3080 raise CommandError("--add-upn-suffix not allowed together with DOMAIN")
3081 if len(delete_upn) > 0:
3082 raise CommandError("--delete-upn-suffix not allowed together with DOMAIN")
3083 if len(add_spn) > 0:
3084 raise CommandError("--add-spn-suffix not allowed together with DOMAIN")
3085 if len(delete_spn) > 0:
3086 raise CommandError("--delete-spn-suffix not allowed together with DOMAIN")
3088 if refresh is not None:
3089 if refresh == "store":
3090 require_update = True
3092 if enable_all and refresh != "store":
3093 raise CommandError("--enable-all not allowed together with --refresh=%s" % refresh)
3095 if len(enable_tln) > 0:
3096 raise CommandError("--enable-tln not allowed together with --refresh")
3097 if len(disable_tln) > 0:
3098 raise CommandError("--disable-tln not allowed together with --refresh")
3100 if len(add_tln_ex) > 0:
3101 raise CommandError("--add-tln-ex not allowed together with --refresh")
3102 if len(delete_tln_ex) > 0:
3103 raise CommandError("--delete-tln-ex not allowed together with --refresh")
3105 if len(enable_nb) > 0:
3106 raise CommandError("--enable-nb not allowed together with --refresh")
3107 if len(disable_nb) > 0:
3108 raise CommandError("--disable-nb not allowed together with --refresh")
3110 if len(enable_sid_str) > 0:
3111 raise CommandError("--enable-sid not allowed together with --refresh")
3112 if len(disable_sid_str) > 0:
3113 raise CommandError("--disable-sid not allowed together with --refresh")
3116 require_update = True
3118 if len(enable_tln) > 0:
3119 raise CommandError("--enable-tln not allowed together with --enable-all")
3121 if len(enable_nb) > 0:
3122 raise CommandError("--enable-nb not allowed together with --enable-all")
3124 if len(enable_sid) > 0:
3125 raise CommandError("--enable-sid not allowed together with --enable-all")
3127 if len(enable_tln) > 0:
3128 require_update = True
3129 if len(disable_tln) > 0:
3130 require_update = True
3131 for e in enable_tln:
3132 for d in disable_tln:
3133 if e.lower() != d.lower():
3135 raise CommandError("value[%s] specified for --enable-tln and --disable-tln" % e)
3137 if len(add_tln_ex) > 0:
3138 for n in add_tln_ex:
3139 if not n.startswith("*."):
3141 raise CommandError("value[%s] specified for --add-tln-ex should not include with '*.'" % n)
3142 require_update = True
3143 if len(delete_tln_ex) > 0:
3144 for n in delete_tln_ex:
3145 if not n.startswith("*."):
3147 raise CommandError("value[%s] specified for --delete-tln-ex should not include with '*.'" % n)
3148 require_update = True
3149 for a in add_tln_ex:
3150 for d in delete_tln_ex:
3151 if a.lower() != d.lower():
3153 raise CommandError("value[%s] specified for --add-tln-ex and --delete-tln-ex" % a)
3155 if len(enable_nb) > 0:
3156 require_update = True
3157 if len(disable_nb) > 0:
3158 require_update = True
3160 for d in disable_nb:
3161 if e.upper() != d.upper():
3163 raise CommandError("value[%s] specified for --enable-nb and --disable-nb" % e)
3166 for s in enable_sid_str:
3168 sid = security.dom_sid(s)
3169 except TypeError as error:
3170 raise CommandError("value[%s] specified for --enable-sid is not a valid SID" % s)
3171 enable_sid.append(sid)
3173 for s in disable_sid_str:
3175 sid = security.dom_sid(s)
3176 except TypeError as error:
3177 raise CommandError("value[%s] specified for --disable-sid is not a valid SID" % s)
3178 disable_sid.append(sid)
3179 if len(enable_sid) > 0:
3180 require_update = True
3181 if len(disable_sid) > 0:
3182 require_update = True
3183 for e in enable_sid:
3184 for d in disable_sid:
3187 raise CommandError("value[%s] specified for --enable-sid and --disable-sid" % e)
3189 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
3191 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
3193 local_server = self.setup_local_server(sambaopts, localdcopts)
3195 local_lsa = self.new_local_lsa_connection()
3196 except RuntimeError as error:
3197 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
3200 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
3201 except RuntimeError as error:
3202 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
3204 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
3205 local_lsa_info.name.string,
3206 local_lsa_info.dns_domain.string,
3207 local_lsa_info.sid))
3211 local_netlogon = self.new_local_netlogon_connection()
3212 except RuntimeError as error:
3213 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3216 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3217 except RuntimeError as error:
3218 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3220 if local_netlogon_info.domain_name != local_netlogon_info.forest_name:
3221 raise CommandError("The local domain [%s] is not the forest root [%s]" % (
3222 local_netlogon_info.domain_name,
3223 local_netlogon_info.forest_name))
3226 # get all information about our own forest
3227 own_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3229 except RuntimeError as error:
3230 if self.check_runtime_error(error, self.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
3231 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3234 if self.check_runtime_error(error, self.WERR_INVALID_FUNCTION):
3235 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3238 if self.check_runtime_error(error, self.WERR_NERR_ACFNOTLOADED):
3239 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3242 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3244 self.outf.write("Own forest trust information...\n")
3245 self.write_forest_trust_info(own_forest_info,
3246 tln=local_lsa_info.dns_domain.string)
3249 local_samdb = self.new_local_ldap_connection()
3250 except RuntimeError as error:
3251 raise self.LocalRuntimeError(self, error, "failed to connect to SamDB")
3253 local_partitions_dn = "CN=Partitions,%s" % str(local_samdb.get_config_basedn())
3254 attrs = ['uPNSuffixes', 'msDS-SPNSuffixes']
3256 msgs = local_samdb.search(base=local_partitions_dn,
3257 scope=ldb.SCOPE_BASE,
3258 expression="(objectClass=crossRefContainer)",
3260 stored_msg = msgs[0]
3261 except ldb.LdbError as error:
3262 raise self.LocalLdbError(self, error, "failed to search partition dn")
3264 stored_upn_vals = []
3265 if 'uPNSuffixes' in stored_msg:
3266 stored_upn_vals.extend(stored_msg['uPNSuffixes'])
3268 stored_spn_vals = []
3269 if 'msDS-SPNSuffixes' in stored_msg:
3270 stored_spn_vals.extend(stored_msg['msDS-SPNSuffixes'])
3272 self.outf.write("Stored uPNSuffixes attributes[%d]:\n" % len(stored_upn_vals))
3273 for v in stored_upn_vals:
3274 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3275 self.outf.write("Stored msDS-SPNSuffixes attributes[%d]:\n" % len(stored_spn_vals))
3276 for v in stored_spn_vals:
3277 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3279 if not require_update:
3283 update_upn_vals = []
3284 update_upn_vals.extend(stored_upn_vals)
3287 update_spn_vals = []
3288 update_spn_vals.extend(stored_spn_vals)
3292 for i in xrange(0, len(update_upn_vals)):
3293 v = update_upn_vals[i]
3294 if v.lower() != upn.lower():
3299 raise CommandError("Entry already present for value[%s] specified for --add-upn-suffix" % upn)
3300 update_upn_vals.append(upn)
3303 for upn in delete_upn:
3305 for i in xrange(0, len(update_upn_vals)):
3306 v = update_upn_vals[i]
3307 if v.lower() != upn.lower():
3312 raise CommandError("Entry not found for value[%s] specified for --delete-upn-suffix" % upn)
3314 update_upn_vals.pop(idx)
3319 for i in xrange(0, len(update_spn_vals)):
3320 v = update_spn_vals[i]
3321 if v.lower() != spn.lower():
3326 raise CommandError("Entry already present for value[%s] specified for --add-spn-suffix" % spn)
3327 update_spn_vals.append(spn)
3330 for spn in delete_spn:
3332 for i in xrange(0, len(update_spn_vals)):
3333 v = update_spn_vals[i]
3334 if v.lower() != spn.lower():
3339 raise CommandError("Entry not found for value[%s] specified for --delete-spn-suffix" % spn)
3341 update_spn_vals.pop(idx)
3344 self.outf.write("Update uPNSuffixes attributes[%d]:\n" % len(update_upn_vals))
3345 for v in update_upn_vals:
3346 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3347 self.outf.write("Update msDS-SPNSuffixes attributes[%d]:\n" % len(update_spn_vals))
3348 for v in update_spn_vals:
3349 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3351 update_msg = ldb.Message()
3352 update_msg.dn = stored_msg.dn
3355 update_msg['uPNSuffixes'] = ldb.MessageElement(update_upn_vals,
3356 ldb.FLAG_MOD_REPLACE,
3359 update_msg['msDS-SPNSuffixes'] = ldb.MessageElement(update_spn_vals,
3360 ldb.FLAG_MOD_REPLACE,
3363 local_samdb.modify(update_msg)
3364 except ldb.LdbError as error:
3365 raise self.LocalLdbError(self, error, "failed to update partition dn")
3368 stored_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3370 except RuntimeError as error:
3371 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3373 self.outf.write("Stored forest trust information...\n")
3374 self.write_forest_trust_info(stored_forest_info,
3375 tln=local_lsa_info.dns_domain.string)
3379 lsaString = lsa.String()
3380 lsaString.string = domain
3381 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
3382 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
3383 except RuntimeError as error:
3384 if self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
3385 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
3387 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
3389 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
3390 local_tdo_info.netbios_name.string,
3391 local_tdo_info.domain_name.string,
3392 local_tdo_info.sid))
3394 if not local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
3395 raise CommandError("trusted domain object for domain [%s] is not marked as FOREST_TRANSITIVE." % domain)
3397 if refresh is not None:
3399 local_netlogon = self.new_local_netlogon_connection()
3400 except RuntimeError as error:
3401 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3404 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3405 except RuntimeError as error:
3406 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3408 lsa_update_check = 1
3409 if refresh == "store":
3410 netlogon_update_tdo = netlogon.DS_GFTI_UPDATE_TDO
3412 lsa_update_check = 0
3414 netlogon_update_tdo = 0
3417 # get all information about the remote trust
3418 # this triggers netr_GetForestTrustInformation to the remote domain
3419 # and lsaRSetForestTrustInformation() locally, but new top level
3420 # names are disabled by default.
3421 fresh_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3422 local_tdo_info.domain_name.string,
3423 netlogon_update_tdo)
3424 except RuntimeError as error:
3425 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3428 fresh_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3429 local_tdo_info.domain_name,
3430 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3433 except RuntimeError as error:
3434 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3436 self.outf.write("Fresh forest trust information...\n")
3437 self.write_forest_trust_info(fresh_forest_info,
3438 tln=local_tdo_info.domain_name.string,
3439 collisions=fresh_forest_collision)
3441 if refresh == "store":
3443 lsaString = lsa.String()
3444 lsaString.string = local_tdo_info.domain_name.string
3445 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3447 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3448 except RuntimeError as error:
3449 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3451 self.outf.write("Stored forest trust information...\n")
3452 self.write_forest_trust_info(stored_forest_info,
3453 tln=local_tdo_info.domain_name.string)
3458 # The none --refresh path
3462 lsaString = lsa.String()
3463 lsaString.string = local_tdo_info.domain_name.string
3464 local_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3466 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3467 except RuntimeError as error:
3468 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3470 self.outf.write("Local forest trust information...\n")
3471 self.write_forest_trust_info(local_forest_info,
3472 tln=local_tdo_info.domain_name.string)
3474 if not require_update:
3478 entries.extend(local_forest_info.entries)
3479 update_forest_info = lsa.ForestTrustInformation()
3480 update_forest_info.count = len(entries)
3481 update_forest_info.entries = entries
3484 for i in xrange(0, len(update_forest_info.entries)):
3485 r = update_forest_info.entries[i]
3486 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3488 if update_forest_info.entries[i].flags == 0:
3490 update_forest_info.entries[i].time = 0
3491 update_forest_info.entries[i].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3492 for i in xrange(0, len(update_forest_info.entries)):
3493 r = update_forest_info.entries[i]
3494 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3496 if update_forest_info.entries[i].flags == 0:
3498 update_forest_info.entries[i].time = 0
3499 update_forest_info.entries[i].flags &= ~lsa.LSA_NB_DISABLED_MASK
3500 update_forest_info.entries[i].flags &= ~lsa.LSA_SID_DISABLED_MASK
3502 for tln in enable_tln:
3504 for i in xrange(0, len(update_forest_info.entries)):
3505 r = update_forest_info.entries[i]
3506 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3508 if r.forest_trust_data.string.lower() != tln.lower():
3513 raise CommandError("Entry not found for value[%s] specified for --enable-tln" % tln)
3514 if not update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_MASK:
3515 raise CommandError("Entry found for value[%s] specified for --enable-tln is already enabled" % tln)
3516 update_forest_info.entries[idx].time = 0
3517 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3519 for tln in disable_tln:
3521 for i in xrange(0, len(update_forest_info.entries)):
3522 r = update_forest_info.entries[i]
3523 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3525 if r.forest_trust_data.string.lower() != tln.lower():
3530 raise CommandError("Entry not found for value[%s] specified for --disable-tln" % tln)
3531 if update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_ADMIN:
3532 raise CommandError("Entry found for value[%s] specified for --disable-tln is already disabled" % tln)
3533 update_forest_info.entries[idx].time = 0
3534 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3535 update_forest_info.entries[idx].flags |= lsa.LSA_TLN_DISABLED_ADMIN
3537 for tln_ex in add_tln_ex:
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_EX:
3543 if r.forest_trust_data.string.lower() != tln_ex.lower():
3548 raise CommandError("Entry already present for value[%s] specified for --add-tln-ex" % tln_ex)
3550 tln_dot = ".%s" % tln_ex.lower()
3552 for i in xrange(0, len(update_forest_info.entries)):
3553 r = update_forest_info.entries[i]
3554 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3556 r_dot = ".%s" % r.forest_trust_data.string.lower()
3557 if tln_dot == r_dot:
3558 raise CommandError("TLN entry present for value[%s] specified for --add-tln-ex" % tln_ex)
3559 if not tln_dot.endswith(r_dot):
3565 raise CommandError("No TLN parent present for value[%s] specified for --add-tln-ex" % tln_ex)
3567 r = lsa.ForestTrustRecord()
3568 r.type = lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX
3571 r.forest_trust_data.string = tln_ex
3574 entries.extend(update_forest_info.entries)
3575 entries.insert(idx + 1, r)
3576 update_forest_info.count = len(entries)
3577 update_forest_info.entries = entries
3579 for tln_ex in delete_tln_ex:
3581 for i in xrange(0, len(update_forest_info.entries)):
3582 r = update_forest_info.entries[i]
3583 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3585 if r.forest_trust_data.string.lower() != tln_ex.lower():
3590 raise CommandError("Entry not found for value[%s] specified for --delete-tln-ex" % tln_ex)
3593 entries.extend(update_forest_info.entries)
3595 update_forest_info.count = len(entries)
3596 update_forest_info.entries = entries
3598 for nb in enable_nb:
3600 for i in xrange(0, len(update_forest_info.entries)):
3601 r = update_forest_info.entries[i]
3602 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3604 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3609 raise CommandError("Entry not found for value[%s] specified for --enable-nb" % nb)
3610 if not update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_MASK:
3611 raise CommandError("Entry found for value[%s] specified for --enable-nb is already enabled" % nb)
3612 update_forest_info.entries[idx].time = 0
3613 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3615 for nb in disable_nb:
3617 for i in xrange(0, len(update_forest_info.entries)):
3618 r = update_forest_info.entries[i]
3619 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3621 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3626 raise CommandError("Entry not found for value[%s] specified for --delete-nb" % nb)
3627 if update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_ADMIN:
3628 raise CommandError("Entry found for value[%s] specified for --disable-nb is already disabled" % nb)
3629 update_forest_info.entries[idx].time = 0
3630 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3631 update_forest_info.entries[idx].flags |= lsa.LSA_NB_DISABLED_ADMIN
3633 for sid in enable_sid:
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.domain_sid != sid:
3644 raise CommandError("Entry not found for value[%s] specified for --enable-sid" % sid)
3645 if not update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_MASK:
3646 raise CommandError("Entry found for value[%s] specified for --enable-sid is already enabled" % nb)
3647 update_forest_info.entries[idx].time = 0
3648 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3650 for sid in disable_sid:
3652 for i in xrange(0, len(update_forest_info.entries)):
3653 r = update_forest_info.entries[i]
3654 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3656 if r.forest_trust_data.domain_sid != sid:
3661 raise CommandError("Entry not found for value[%s] specified for --delete-sid" % sid)
3662 if update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_ADMIN:
3663 raise CommandError("Entry found for value[%s] specified for --disable-sid is already disabled" % nb)
3664 update_forest_info.entries[idx].time = 0
3665 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3666 update_forest_info.entries[idx].flags |= lsa.LSA_SID_DISABLED_ADMIN
3669 update_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3670 local_tdo_info.domain_name,
3671 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3672 update_forest_info, 0)
3673 except RuntimeError as error:
3674 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3676 self.outf.write("Updated forest trust information...\n")
3677 self.write_forest_trust_info(update_forest_info,
3678 tln=local_tdo_info.domain_name.string,
3679 collisions=update_forest_collision)
3682 lsaString = lsa.String()
3683 lsaString.string = local_tdo_info.domain_name.string
3684 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3686 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3687 except RuntimeError as error:
3688 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3690 self.outf.write("Stored forest trust information...\n")
3691 self.write_forest_trust_info(stored_forest_info,
3692 tln=local_tdo_info.domain_name.string)
3695 class cmd_domain_trust(SuperCommand):
3696 """Domain and forest trust management."""
3699 subcommands["list"] = cmd_domain_trust_list()
3700 subcommands["show"] = cmd_domain_trust_show()
3701 subcommands["create"] = cmd_domain_trust_create()
3702 subcommands["delete"] = cmd_domain_trust_delete()
3703 subcommands["validate"] = cmd_domain_trust_validate()
3704 subcommands["namespaces"] = cmd_domain_trust_namespaces()
3706 class cmd_domain(SuperCommand):
3707 """Domain management."""
3710 subcommands["demote"] = cmd_domain_demote()
3711 if cmd_domain_export_keytab is not None:
3712 subcommands["exportkeytab"] = cmd_domain_export_keytab()
3713 subcommands["info"] = cmd_domain_info()
3714 subcommands["provision"] = cmd_domain_provision()
3715 subcommands["join"] = cmd_domain_join()
3716 subcommands["dcpromo"] = cmd_domain_dcpromo()
3717 subcommands["level"] = cmd_domain_level()
3718 subcommands["passwordsettings"] = cmd_domain_passwordsettings()
3719 subcommands["classicupgrade"] = cmd_domain_classicupgrade()
3720 subcommands["samba3upgrade"] = cmd_domain_samba3upgrade()
3721 subcommands["trust"] = cmd_domain_trust()