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
36 from getpass import getpass
37 from samba.net import Net, LIBNET_JOIN_AUTOMATIC
39 from samba.join import join_RODC, join_DC, join_subdomain
40 from samba.auth import system_session
41 from samba.samdb import SamDB
42 from samba.ndr import ndr_unpack, ndr_pack, ndr_print
43 from samba.dcerpc import drsuapi
44 from samba.dcerpc import drsblobs
45 from samba.dcerpc import lsa
46 from samba.dcerpc import netlogon
47 from samba.dcerpc import security
48 from samba.dcerpc import nbt
49 from samba.dcerpc import misc
50 from samba.dcerpc.samr import DOMAIN_PASSWORD_COMPLEX, DOMAIN_PASSWORD_STORE_CLEARTEXT
51 from samba.netcmd import (
57 from samba.netcmd.common import netcmd_get_domain_infos_via_cldap
58 from samba.samba3 import Samba3
59 from samba.samba3 import param as s3param
60 from samba.upgrade import upgrade_from_samba3
61 from samba.drs_utils import (
62 sendDsReplicaSync, drsuapi_connect, drsException,
64 from samba import remove_dc, arcfour_encrypt, string_to_byte_array
66 from samba.dsdb import (
67 DS_DOMAIN_FUNCTION_2000,
68 DS_DOMAIN_FUNCTION_2003,
69 DS_DOMAIN_FUNCTION_2003_MIXED,
70 DS_DOMAIN_FUNCTION_2008,
71 DS_DOMAIN_FUNCTION_2008_R2,
72 DS_DOMAIN_FUNCTION_2012,
73 DS_DOMAIN_FUNCTION_2012_R2,
74 DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL,
75 DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL,
76 UF_WORKSTATION_TRUST_ACCOUNT,
77 UF_SERVER_TRUST_ACCOUNT,
78 UF_TRUSTED_FOR_DELEGATION,
79 UF_PARTIAL_SECRETS_ACCOUNT
82 from samba.provision import (
87 from samba.provision.common import (
93 def get_testparm_var(testparm, smbconf, varname):
94 errfile = open(os.devnull, 'w')
95 p = subprocess.Popen([testparm, '-s', '-l',
96 '--parameter-name=%s' % varname, smbconf],
97 stdout=subprocess.PIPE, stderr=errfile)
98 (out,err) = p.communicate()
100 lines = out.split('\n')
102 return lines[0].strip()
106 import samba.dckeytab
108 cmd_domain_export_keytab = None
110 class cmd_domain_export_keytab(Command):
111 """Dump Kerberos keys of the domain into a keytab."""
113 synopsis = "%prog <keytab> [options]"
115 takes_optiongroups = {
116 "sambaopts": options.SambaOptions,
117 "credopts": options.CredentialsOptions,
118 "versionopts": options.VersionOptions,
122 Option("--principal", help="extract only this principal", type=str),
125 takes_args = ["keytab"]
127 def run(self, keytab, credopts=None, sambaopts=None, versionopts=None, principal=None):
128 lp = sambaopts.get_loadparm()
130 net.export_keytab(keytab=keytab, principal=principal)
133 class cmd_domain_info(Command):
134 """Print basic info about a domain and the DC passed as parameter."""
136 synopsis = "%prog <ip_address> [options]"
141 takes_optiongroups = {
142 "sambaopts": options.SambaOptions,
143 "credopts": options.CredentialsOptions,
144 "versionopts": options.VersionOptions,
147 takes_args = ["address"]
149 def run(self, address, credopts=None, sambaopts=None, versionopts=None):
150 lp = sambaopts.get_loadparm()
152 res = netcmd_get_domain_infos_via_cldap(lp, None, address)
154 raise CommandError("Invalid IP address '" + address + "'!")
155 self.outf.write("Forest : %s\n" % res.forest)
156 self.outf.write("Domain : %s\n" % res.dns_domain)
157 self.outf.write("Netbios domain : %s\n" % res.domain_name)
158 self.outf.write("DC name : %s\n" % res.pdc_dns_name)
159 self.outf.write("DC netbios name : %s\n" % res.pdc_name)
160 self.outf.write("Server site : %s\n" % res.server_site)
161 self.outf.write("Client site : %s\n" % res.client_site)
164 class cmd_domain_provision(Command):
165 """Provision a domain."""
167 synopsis = "%prog [options]"
169 takes_optiongroups = {
170 "sambaopts": options.SambaOptions,
171 "versionopts": options.VersionOptions,
175 Option("--interactive", help="Ask for names", action="store_true"),
176 Option("--domain", type="string", metavar="DOMAIN",
177 help="NetBIOS domain name to use"),
178 Option("--domain-guid", type="string", metavar="GUID",
179 help="set domainguid (otherwise random)"),
180 Option("--domain-sid", type="string", metavar="SID",
181 help="set domainsid (otherwise random)"),
182 Option("--ntds-guid", type="string", metavar="GUID",
183 help="set NTDS object GUID (otherwise random)"),
184 Option("--invocationid", type="string", metavar="GUID",
185 help="set invocationid (otherwise random)"),
186 Option("--host-name", type="string", metavar="HOSTNAME",
187 help="set hostname"),
188 Option("--host-ip", type="string", metavar="IPADDRESS",
189 help="set IPv4 ipaddress"),
190 Option("--host-ip6", type="string", metavar="IP6ADDRESS",
191 help="set IPv6 ipaddress"),
192 Option("--site", type="string", metavar="SITENAME",
193 help="set site name"),
194 Option("--adminpass", type="string", metavar="PASSWORD",
195 help="choose admin password (otherwise random)"),
196 Option("--krbtgtpass", type="string", metavar="PASSWORD",
197 help="choose krbtgt password (otherwise random)"),
198 Option("--machinepass", type="string", metavar="PASSWORD",
199 help="choose machine password (otherwise random)"),
200 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
201 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
202 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
203 "BIND9_FLATFILE uses bind9 text database to store zone information, "
204 "BIND9_DLZ uses samba4 AD to store zone information, "
205 "NONE skips the DNS setup entirely (not recommended)",
206 default="SAMBA_INTERNAL"),
207 Option("--dnspass", type="string", metavar="PASSWORD",
208 help="choose dns password (otherwise random)"),
209 Option("--ldapadminpass", type="string", metavar="PASSWORD",
210 help="choose password to set between Samba and its LDAP backend (otherwise random)"),
211 Option("--root", type="string", metavar="USERNAME",
212 help="choose 'root' unix username"),
213 Option("--nobody", type="string", metavar="USERNAME",
214 help="choose 'nobody' user"),
215 Option("--users", type="string", metavar="GROUPNAME",
216 help="choose 'users' group"),
217 Option("--quiet", help="Be quiet", action="store_true"),
218 Option("--blank", action="store_true",
219 help="do not add users or groups, just the structure"),
220 Option("--ldap-backend-type", type="choice", metavar="LDAP-BACKEND-TYPE",
221 help="Test initialisation support for unsupported LDAP backend type (fedora-ds or openldap) DO NOT USE",
222 choices=["fedora-ds", "openldap"]),
223 Option("--server-role", type="choice", metavar="ROLE",
224 choices=["domain controller", "dc", "member server", "member", "standalone"],
225 help="The server role (domain controller | dc | member server | member | standalone). Default is dc.",
226 default="domain controller"),
227 Option("--function-level", type="choice", metavar="FOR-FUN-LEVEL",
228 choices=["2000", "2003", "2008", "2008_R2"],
229 help="The domain and forest function level (2000 | 2003 | 2008 | 2008_R2 - always native). Default is (Windows) 2008_R2 Native.",
231 Option("--next-rid", type="int", metavar="NEXTRID", default=1000,
232 help="The initial nextRid value (only needed for upgrades). Default is 1000."),
233 Option("--partitions-only",
234 help="Configure Samba's partitions, but do not modify them (ie, join a BDC)", action="store_true"),
235 Option("--targetdir", type="string", metavar="DIR",
236 help="Set target directory"),
237 Option("--ol-mmr-urls", type="string", metavar="LDAPSERVER",
238 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\""),
239 Option("--use-rfc2307", action="store_true", help="Use AD to store posix attributes (default = no)"),
243 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",
244 action="store_true"),
245 Option("--slapd-path", type="string", metavar="SLAPD-PATH",
246 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."),
247 Option("--ldap-backend-extra-port", type="int", metavar="LDAP-BACKEND-EXTRA-PORT", help="Additional TCP port for LDAP backend server (to use for replication)"),
248 Option("--ldap-backend-forced-uri", type="string", metavar="LDAP-BACKEND-FORCED-URI",
249 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"),
250 Option("--ldap-backend-nosync", help="Configure LDAP backend not to call fsync() (for performance in test environments)", action="store_true"),
254 Option("--use-ntvfs", action="store_true", help="Use NTVFS for the fileserver (default = no)"),
255 Option("--use-xattrs", type="choice", choices=["yes","no","auto"],
256 metavar="[yes|no|auto]",
257 help="Define if we should use the native fs capabilities or a tdb file for "
258 "storing attributes likes ntacl when --use-ntvfs is set. "
259 "auto tries to make an inteligent guess based on the user rights and system capabilities",
263 if os.getenv('TEST_LDAP', "no") == "yes":
264 takes_options.extend(openldap_options)
266 if samba.is_ntvfs_fileserver_built():
267 takes_options.extend(ntvfs_options)
271 def run(self, sambaopts=None, versionopts=None,
294 ldap_backend_type=None,
298 partitions_only=None,
305 ldap_backend_nosync=None,
306 ldap_backend_extra_port=None,
307 ldap_backend_forced_uri=None,
308 ldap_dryrun_mode=None):
310 self.logger = self.get_logger("provision")
312 self.logger.setLevel(logging.WARNING)
314 self.logger.setLevel(logging.INFO)
316 lp = sambaopts.get_loadparm()
317 smbconf = lp.configfile
319 if dns_forwarder is not None:
320 suggested_forwarder = dns_forwarder
322 suggested_forwarder = self._get_nameserver_ip()
323 if suggested_forwarder is None:
324 suggested_forwarder = "none"
326 if len(self.raw_argv) == 1:
330 from getpass import getpass
333 def ask(prompt, default=None):
334 if default is not None:
335 print "%s [%s]: " % (prompt, default),
337 print "%s: " % (prompt,),
338 return sys.stdin.readline().rstrip("\n") or default
341 default = socket.getfqdn().split(".", 1)[1].upper()
344 realm = ask("Realm", default)
345 if realm in (None, ""):
346 raise CommandError("No realm set!")
349 default = realm.split(".")[0]
352 domain = ask("Domain", default)
354 raise CommandError("No domain set!")
356 server_role = ask("Server Role (dc, member, standalone)", "dc")
358 dns_backend = ask("DNS backend (SAMBA_INTERNAL, BIND9_FLATFILE, BIND9_DLZ, NONE)", "SAMBA_INTERNAL")
359 if dns_backend in (None, ''):
360 raise CommandError("No DNS backend set!")
362 if dns_backend == "SAMBA_INTERNAL":
363 dns_forwarder = ask("DNS forwarder IP address (write 'none' to disable forwarding)", suggested_forwarder)
364 if dns_forwarder.lower() in (None, 'none'):
365 suggested_forwarder = None
369 adminpassplain = getpass("Administrator password: ")
370 if not adminpassplain:
371 self.errf.write("Invalid administrator password.\n")
373 adminpassverify = getpass("Retype password: ")
374 if not adminpassplain == adminpassverify:
375 self.errf.write("Sorry, passwords do not match.\n")
377 adminpass = adminpassplain
381 realm = sambaopts._lp.get('realm')
383 raise CommandError("No realm set!")
385 raise CommandError("No domain set!")
388 self.logger.info("Administrator password will be set randomly!")
390 if function_level == "2000":
391 dom_for_fun_level = DS_DOMAIN_FUNCTION_2000
392 elif function_level == "2003":
393 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
394 elif function_level == "2008":
395 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008
396 elif function_level == "2008_R2":
397 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
399 if dns_backend == "SAMBA_INTERNAL" and dns_forwarder is None:
400 dns_forwarder = suggested_forwarder
402 samdb_fill = FILL_FULL
404 samdb_fill = FILL_NT4SYNC
405 elif partitions_only:
406 samdb_fill = FILL_DRS
408 if targetdir is not None:
409 if not os.path.isdir(targetdir):
414 if use_xattrs == "yes":
416 elif use_xattrs == "auto" and use_ntvfs == False:
418 elif use_ntvfs == False:
419 raise CommandError("--use-xattrs=no requires --use-ntvfs (not supported for production use). "
420 "Please re-run with --use-xattrs omitted.")
421 elif use_xattrs == "auto" and not lp.get("posix:eadb"):
423 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
425 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
428 samba.ntacls.setntacl(lp, file.name,
429 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
432 self.logger.info("You are not root or your system does not support xattr, using tdb backend for attributes. ")
437 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.")
438 if ldap_backend_type == "existing":
439 if ldap_backend_forced_uri is not None:
440 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)
442 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")
444 if ldap_backend_forced_uri is not None:
445 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")
447 if domain_sid is not None:
448 domain_sid = security.dom_sid(domain_sid)
450 session = system_session()
452 result = provision(self.logger,
453 session, smbconf=smbconf, targetdir=targetdir,
454 samdb_fill=samdb_fill, realm=realm, domain=domain,
455 domainguid=domain_guid, domainsid=domain_sid,
457 hostip=host_ip, hostip6=host_ip6,
458 sitename=site, ntdsguid=ntds_guid,
459 invocationid=invocationid, adminpass=adminpass,
460 krbtgtpass=krbtgtpass, machinepass=machinepass,
461 dns_backend=dns_backend, dns_forwarder=dns_forwarder,
462 dnspass=dnspass, root=root, nobody=nobody,
464 serverrole=server_role, dom_for_fun_level=dom_for_fun_level,
465 backend_type=ldap_backend_type,
466 ldapadminpass=ldapadminpass, ol_mmr_urls=ol_mmr_urls, slapd_path=slapd_path,
467 useeadb=eadb, next_rid=next_rid, lp=lp, use_ntvfs=use_ntvfs,
468 use_rfc2307=use_rfc2307, skip_sysvolacl=False,
469 ldap_backend_extra_port=ldap_backend_extra_port,
470 ldap_backend_forced_uri=ldap_backend_forced_uri,
471 nosync=ldap_backend_nosync, ldap_dryrun_mode=ldap_dryrun_mode)
473 except ProvisioningError, e:
474 raise CommandError("Provision failed", e)
476 result.report_logger(self.logger)
478 def _get_nameserver_ip(self):
479 """Grab the nameserver IP address from /etc/resolv.conf."""
481 RESOLV_CONF="/etc/resolv.conf"
483 if not path.isfile(RESOLV_CONF):
484 self.logger.warning("Failed to locate %s" % RESOLV_CONF)
489 handle = open(RESOLV_CONF, 'r')
491 if not line.startswith('nameserver'):
493 # we want the last non-space continuous string of the line
494 return line.strip().split()[-1]
496 if handle is not None:
499 self.logger.warning("No nameserver found in %s" % RESOLV_CONF)
502 class cmd_domain_dcpromo(Command):
503 """Promote an existing domain member or NT4 PDC to an AD DC."""
505 synopsis = "%prog <dnsdomain> [DC|RODC] [options]"
507 takes_optiongroups = {
508 "sambaopts": options.SambaOptions,
509 "versionopts": options.VersionOptions,
510 "credopts": options.CredentialsOptions,
514 Option("--server", help="DC to join", type=str),
515 Option("--site", help="site to join", type=str),
516 Option("--targetdir", help="where to store provision", type=str),
517 Option("--domain-critical-only",
518 help="only replicate critical domain objects",
519 action="store_true"),
520 Option("--machinepass", type=str, metavar="PASSWORD",
521 help="choose machine password (otherwise random)"),
522 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
523 choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
524 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
525 "BIND9_DLZ uses samba4 AD to store zone information, "
526 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
527 default="SAMBA_INTERNAL"),
528 Option("--quiet", help="Be quiet", action="store_true"),
529 Option("--verbose", help="Be verbose", action="store_true")
533 Option("--use-ntvfs", action="store_true", help="Use NTVFS for the fileserver (default = no)"),
536 if samba.is_ntvfs_fileserver_built():
537 takes_options.extend(ntvfs_options)
540 takes_args = ["domain", "role?"]
542 def run(self, domain, role=None, sambaopts=None, credopts=None,
543 versionopts=None, server=None, site=None, targetdir=None,
544 domain_critical_only=False, parent_domain=None, machinepass=None,
545 use_ntvfs=False, dns_backend=None,
546 quiet=False, verbose=False):
547 lp = sambaopts.get_loadparm()
548 creds = credopts.get_credentials(lp)
549 net = Net(creds, lp, server=credopts.ipaddress)
552 site = "Default-First-Site-Name"
554 logger = self.get_logger()
556 logger.setLevel(logging.DEBUG)
558 logger.setLevel(logging.WARNING)
560 logger.setLevel(logging.INFO)
562 netbios_name = lp.get("netbios name")
568 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
569 site=site, netbios_name=netbios_name, targetdir=targetdir,
570 domain_critical_only=domain_critical_only,
571 machinepass=machinepass, use_ntvfs=use_ntvfs,
572 dns_backend=dns_backend,
573 promote_existing=True)
575 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
576 site=site, netbios_name=netbios_name, targetdir=targetdir,
577 domain_critical_only=domain_critical_only,
578 machinepass=machinepass, use_ntvfs=use_ntvfs, dns_backend=dns_backend,
579 promote_existing=True)
581 raise CommandError("Invalid role '%s' (possible values: DC, RODC)" % role)
584 class cmd_domain_join(Command):
585 """Join domain as either member or backup domain controller."""
587 synopsis = "%prog <dnsdomain> [DC|RODC|MEMBER|SUBDOMAIN] [options]"
589 takes_optiongroups = {
590 "sambaopts": options.SambaOptions,
591 "versionopts": options.VersionOptions,
592 "credopts": options.CredentialsOptions,
596 Option("--server", help="DC to join", type=str),
597 Option("--site", help="site to join", type=str),
598 Option("--targetdir", help="where to store provision", type=str),
599 Option("--parent-domain", help="parent domain to create subdomain under", type=str),
600 Option("--domain-critical-only",
601 help="only replicate critical domain objects",
602 action="store_true"),
603 Option("--machinepass", type=str, metavar="PASSWORD",
604 help="choose machine password (otherwise random)"),
605 Option("--adminpass", type="string", metavar="PASSWORD",
606 help="choose adminstrator password when joining as a subdomain (otherwise random)"),
607 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
608 choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
609 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
610 "BIND9_DLZ uses samba4 AD to store zone information, "
611 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
612 default="SAMBA_INTERNAL"),
613 Option("--quiet", help="Be quiet", action="store_true"),
614 Option("--verbose", help="Be verbose", action="store_true")
618 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
621 if samba.is_ntvfs_fileserver_built():
622 takes_options.extend(ntvfs_options)
624 takes_args = ["domain", "role?"]
626 def run(self, domain, role=None, sambaopts=None, credopts=None,
627 versionopts=None, server=None, site=None, targetdir=None,
628 domain_critical_only=False, parent_domain=None, machinepass=None,
629 use_ntvfs=False, dns_backend=None, adminpass=None,
630 quiet=False, verbose=False):
631 lp = sambaopts.get_loadparm()
632 creds = credopts.get_credentials(lp)
633 net = Net(creds, lp, server=credopts.ipaddress)
636 site = "Default-First-Site-Name"
638 logger = self.get_logger()
640 logger.setLevel(logging.DEBUG)
642 logger.setLevel(logging.WARNING)
644 logger.setLevel(logging.INFO)
646 netbios_name = lp.get("netbios name")
651 if role is None or role == "MEMBER":
652 (join_password, sid, domain_name) = net.join_member(
653 domain, netbios_name, LIBNET_JOIN_AUTOMATIC,
654 machinepass=machinepass)
656 self.errf.write("Joined domain %s (%s)\n" % (domain_name, sid))
658 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
659 site=site, netbios_name=netbios_name, targetdir=targetdir,
660 domain_critical_only=domain_critical_only,
661 machinepass=machinepass, use_ntvfs=use_ntvfs, dns_backend=dns_backend)
663 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
664 site=site, netbios_name=netbios_name, targetdir=targetdir,
665 domain_critical_only=domain_critical_only,
666 machinepass=machinepass, use_ntvfs=use_ntvfs,
667 dns_backend=dns_backend)
668 elif role == "SUBDOMAIN":
670 logger.info("Administrator password will be set randomly!")
672 netbios_domain = lp.get("workgroup")
673 if parent_domain is None:
674 parent_domain = ".".join(domain.split(".")[1:])
675 join_subdomain(logger=logger, server=server, creds=creds, lp=lp, dnsdomain=domain,
676 parent_domain=parent_domain, site=site,
677 netbios_name=netbios_name, netbios_domain=netbios_domain,
678 targetdir=targetdir, machinepass=machinepass,
679 use_ntvfs=use_ntvfs, dns_backend=dns_backend,
682 raise CommandError("Invalid role '%s' (possible values: MEMBER, DC, RODC, SUBDOMAIN)" % role)
685 class cmd_domain_demote(Command):
686 """Demote ourselves from the role of Domain Controller."""
688 synopsis = "%prog [options]"
691 Option("--server", help="writable DC to write demotion changes on", type=str),
692 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
693 metavar="URL", dest="H"),
694 Option("--remove-other-dead-server", help="Dead DC (name or NTDS GUID) "
695 "to remove ALL references to (rather than this DC)", type=str),
696 Option("--quiet", help="Be quiet", action="store_true"),
697 Option("--verbose", help="Be verbose", action="store_true"),
700 takes_optiongroups = {
701 "sambaopts": options.SambaOptions,
702 "credopts": options.CredentialsOptions,
703 "versionopts": options.VersionOptions,
706 def run(self, sambaopts=None, credopts=None,
707 versionopts=None, server=None,
708 remove_other_dead_server=None, H=None,
709 verbose=False, quiet=False):
710 lp = sambaopts.get_loadparm()
711 creds = credopts.get_credentials(lp)
712 net = Net(creds, lp, server=credopts.ipaddress)
714 logger = self.get_logger()
716 logger.setLevel(logging.DEBUG)
718 logger.setLevel(logging.WARNING)
720 logger.setLevel(logging.INFO)
722 if remove_other_dead_server is not None:
723 if server is not None:
724 samdb = SamDB(url="ldap://%s" % server,
725 session_info=system_session(),
726 credentials=creds, lp=lp)
728 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
730 remove_dc.remove_dc(samdb, logger, remove_other_dead_server)
731 except remove_dc.DemoteException as err:
732 raise CommandError("Demote failed: %s" % err)
735 netbios_name = lp.get("netbios name")
736 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
738 res = samdb.search(expression='(&(objectClass=computer)(serverReferenceBL=*))', attrs=["dnsHostName", "name"])
740 raise CommandError("Unable to search for servers")
743 raise CommandError("You are the latest server in the domain")
747 if str(e["name"]).lower() != netbios_name.lower():
748 server = e["dnsHostName"]
751 ntds_guid = samdb.get_ntds_GUID()
752 msg = samdb.search(base=str(samdb.get_config_basedn()),
753 scope=ldb.SCOPE_SUBTREE, expression="(objectGUID=%s)" % ntds_guid,
755 if len(msg) == 0 or "options" not in msg[0]:
756 raise CommandError("Failed to find options on %s" % ntds_guid)
759 dsa_options = int(str(msg[0]['options']))
761 res = samdb.search(expression="(fSMORoleOwner=%s)" % str(ntds_dn),
762 controls=["search_options:1:2"])
765 raise CommandError("Current DC is still the owner of %d role(s), use the role command to transfer roles to another DC" % len(res))
767 self.errf.write("Using %s as partner server for the demotion\n" %
769 (drsuapiBind, drsuapi_handle, supportedExtensions) = drsuapi_connect(server, lp, creds)
771 self.errf.write("Deactivating inbound replication\n")
773 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
777 dsa_options |= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
778 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
782 self.errf.write("Asking partner server %s to synchronize from us\n"
784 for part in (samdb.get_schema_basedn(),
785 samdb.get_config_basedn(),
786 samdb.get_root_basedn()):
787 nc = drsuapi.DsReplicaObjectIdentifier()
790 req1 = drsuapi.DsReplicaSyncRequest1()
791 req1.naming_context = nc;
792 req1.options = drsuapi.DRSUAPI_DRS_WRIT_REP
793 req1.source_dsa_guid = misc.GUID(ntds_guid)
796 drsuapiBind.DsReplicaSync(drsuapi_handle, 1, req1)
797 except RuntimeError as (werr, string):
798 if werr == 8452: #WERR_DS_DRA_NO_REPLICA
802 "Error while replicating out last local changes from '%s' for demotion, "
803 "re-enabling inbound replication\n" % part)
804 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
805 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
807 raise CommandError("Error while sending a DsReplicaSync for partition '%s'" % str(part), string)
809 remote_samdb = SamDB(url="ldap://%s" % server,
810 session_info=system_session(),
811 credentials=creds, lp=lp)
813 self.errf.write("Changing userControl and container\n")
814 res = remote_samdb.search(base=str(remote_samdb.domain_dn()),
815 expression="(&(objectClass=user)(sAMAccountName=%s$))" %
816 netbios_name.upper(),
817 attrs=["userAccountControl"])
819 uac = int(str(res[0]["userAccountControl"]))
823 "Error while demoting, re-enabling inbound replication\n")
824 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
825 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
827 raise CommandError("Error while changing account control", e)
831 "Error while demoting, re-enabling inbound replication")
832 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
833 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
835 raise CommandError("Unable to find object with samaccountName = %s$"
836 " in the remote dc" % netbios_name.upper())
840 uac &= ~(UF_SERVER_TRUST_ACCOUNT|UF_TRUSTED_FOR_DELEGATION|UF_PARTIAL_SECRETS_ACCOUNT)
841 uac |= UF_WORKSTATION_TRUST_ACCOUNT
846 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
847 ldb.FLAG_MOD_REPLACE,
848 "userAccountControl")
850 remote_samdb.modify(msg)
853 "Error while demoting, re-enabling inbound replication")
854 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
855 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
858 raise CommandError("Error while changing account control", e)
860 parent = msg.dn.parent()
861 dc_name = res[0].dn.get_rdn_value()
862 rdn = "CN=%s" % dc_name
864 # Let's move to the Computer container
868 computer_dn = ldb.Dn(remote_samdb, "CN=Computers,%s" % str(remote_samdb.domain_dn()))
869 res = remote_samdb.search(base=computer_dn, expression=rdn, scope=ldb.SCOPE_ONELEVEL)
872 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
873 scope=ldb.SCOPE_ONELEVEL)
874 while(len(res) != 0 and i < 100):
876 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
877 scope=ldb.SCOPE_ONELEVEL)
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)
895 raise CommandError("Unable to find a slot for renaming %s,"
896 " all names from %s-1 to %s-%d seemed used" %
897 (str(dc_dn), rdn, rdn, i - 9))
899 newrdn = "%s-%d" % (rdn, i)
902 newdn = ldb.Dn(remote_samdb, "%s,%s" % (newrdn, str(computer_dn)))
903 remote_samdb.rename(dc_dn, newdn)
906 "Error while demoting, re-enabling inbound replication\n")
907 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
908 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
914 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
915 ldb.FLAG_MOD_REPLACE,
916 "userAccountControl")
918 remote_samdb.modify(msg)
919 raise CommandError("Error while renaming %s to %s" % (str(dc_dn), str(newdn)), e)
922 server_dsa_dn = samdb.get_serverName()
923 domain = remote_samdb.get_root_basedn()
926 req1 = drsuapi.DsRemoveDSServerRequest1()
927 req1.server_dn = str(server_dsa_dn)
928 req1.domain_dn = str(domain)
931 drsuapiBind.DsRemoveDSServer(drsuapi_handle, 1, req1)
932 except RuntimeError as (werr, string):
933 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
935 "Error while demoting, re-enabling inbound replication\n")
936 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
937 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
943 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
944 ldb.FLAG_MOD_REPLACE,
945 "userAccountControl")
946 remote_samdb.modify(msg)
947 remote_samdb.rename(newdn, dc_dn)
948 if werr == 8452: #WERR_DS_DRA_NO_REPLICA
949 raise CommandError("The DC %s is not present on (already removed from) the remote server: " % server_dsa_dn, e)
951 raise CommandError("Error while sending a removeDsServer of %s: " % server_dsa_dn, e)
953 remove_dc.remove_sysvol_references(remote_samdb, logger, dc_name)
955 # These are objects under the computer account that should be deleted
956 for s in ("CN=Enterprise,CN=NTFRS Subscriptions",
957 "CN=%s, CN=NTFRS Subscriptions" % lp.get("realm"),
958 "CN=Domain system Volumes (SYSVOL Share), CN=NTFRS Subscriptions",
959 "CN=NTFRS Subscriptions"):
961 remote_samdb.delete(ldb.Dn(remote_samdb,
962 "%s,%s" % (s, str(newdn))))
963 except ldb.LdbError, l:
966 self.errf.write("Demote successful\n")
969 class cmd_domain_level(Command):
970 """Raise domain and forest function levels."""
972 synopsis = "%prog (show|raise <options>) [options]"
974 takes_optiongroups = {
975 "sambaopts": options.SambaOptions,
976 "credopts": options.CredentialsOptions,
977 "versionopts": options.VersionOptions,
981 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
982 metavar="URL", dest="H"),
983 Option("--quiet", help="Be quiet", action="store_true"),
984 Option("--forest-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
985 help="The forest function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)"),
986 Option("--domain-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
987 help="The domain function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)")
990 takes_args = ["subcommand"]
992 def run(self, subcommand, H=None, forest_level=None, domain_level=None,
993 quiet=False, credopts=None, sambaopts=None, versionopts=None):
994 lp = sambaopts.get_loadparm()
995 creds = credopts.get_credentials(lp, fallback_machine=True)
997 samdb = SamDB(url=H, session_info=system_session(),
998 credentials=creds, lp=lp)
1000 domain_dn = samdb.domain_dn()
1002 res_forest = samdb.search("CN=Partitions,%s" % samdb.get_config_basedn(),
1003 scope=ldb.SCOPE_BASE, attrs=["msDS-Behavior-Version"])
1004 assert len(res_forest) == 1
1006 res_domain = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1007 attrs=["msDS-Behavior-Version", "nTMixedDomain"])
1008 assert len(res_domain) == 1
1010 res_dc_s = samdb.search("CN=Sites,%s" % samdb.get_config_basedn(),
1011 scope=ldb.SCOPE_SUBTREE, expression="(objectClass=nTDSDSA)",
1012 attrs=["msDS-Behavior-Version"])
1013 assert len(res_dc_s) >= 1
1015 # default values, since "msDS-Behavior-Version" does not exist on Windows 2000 AD
1016 level_forest = DS_DOMAIN_FUNCTION_2000
1017 level_domain = DS_DOMAIN_FUNCTION_2000
1019 if "msDS-Behavior-Version" in res_forest[0]:
1020 level_forest = int(res_forest[0]["msDS-Behavior-Version"][0])
1021 if "msDS-Behavior-Version" in res_domain[0]:
1022 level_domain = int(res_domain[0]["msDS-Behavior-Version"][0])
1023 level_domain_mixed = int(res_domain[0]["nTMixedDomain"][0])
1026 for msg in res_dc_s:
1027 if "msDS-Behavior-Version" in msg:
1028 if min_level_dc is None or int(msg["msDS-Behavior-Version"][0]) < min_level_dc:
1029 min_level_dc = int(msg["msDS-Behavior-Version"][0])
1031 min_level_dc = DS_DOMAIN_FUNCTION_2000
1032 # well, this is the least
1035 if level_forest < DS_DOMAIN_FUNCTION_2000 or level_domain < DS_DOMAIN_FUNCTION_2000:
1036 raise CommandError("Domain and/or forest function level(s) is/are invalid. Correct them or reprovision!")
1037 if min_level_dc < DS_DOMAIN_FUNCTION_2000:
1038 raise CommandError("Lowest function level of a DC is invalid. Correct this or reprovision!")
1039 if level_forest > level_domain:
1040 raise CommandError("Forest function level is higher than the domain level(s). Correct this or reprovision!")
1041 if level_domain > min_level_dc:
1042 raise CommandError("Domain function level is higher than the lowest function level of a DC. Correct this or reprovision!")
1044 if subcommand == "show":
1045 self.message("Domain and forest function level for domain '%s'" % domain_dn)
1046 if level_forest == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1047 self.message("\nATTENTION: You run SAMBA 4 on a forest function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1048 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1049 self.message("\nATTENTION: You run SAMBA 4 on a domain function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1050 if min_level_dc == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1051 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)!")
1055 if level_forest == DS_DOMAIN_FUNCTION_2000:
1057 elif level_forest == DS_DOMAIN_FUNCTION_2003_MIXED:
1058 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1059 elif level_forest == DS_DOMAIN_FUNCTION_2003:
1061 elif level_forest == DS_DOMAIN_FUNCTION_2008:
1063 elif level_forest == DS_DOMAIN_FUNCTION_2008_R2:
1065 elif level_forest == DS_DOMAIN_FUNCTION_2012:
1067 elif level_forest == DS_DOMAIN_FUNCTION_2012_R2:
1070 outstr = "higher than 2012 R2"
1071 self.message("Forest function level: (Windows) " + outstr)
1073 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1074 outstr = "2000 mixed (NT4 DC support)"
1075 elif level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed == 0:
1077 elif level_domain == DS_DOMAIN_FUNCTION_2003_MIXED:
1078 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1079 elif level_domain == DS_DOMAIN_FUNCTION_2003:
1081 elif level_domain == DS_DOMAIN_FUNCTION_2008:
1083 elif level_domain == DS_DOMAIN_FUNCTION_2008_R2:
1085 elif level_domain == DS_DOMAIN_FUNCTION_2012:
1087 elif level_domain == DS_DOMAIN_FUNCTION_2012_R2:
1090 outstr = "higher than 2012 R2"
1091 self.message("Domain function level: (Windows) " + outstr)
1093 if min_level_dc == DS_DOMAIN_FUNCTION_2000:
1095 elif min_level_dc == DS_DOMAIN_FUNCTION_2003:
1097 elif min_level_dc == DS_DOMAIN_FUNCTION_2008:
1099 elif min_level_dc == DS_DOMAIN_FUNCTION_2008_R2:
1101 elif min_level_dc == DS_DOMAIN_FUNCTION_2012:
1103 elif min_level_dc == DS_DOMAIN_FUNCTION_2012_R2:
1106 outstr = "higher than 2012 R2"
1107 self.message("Lowest function level of a DC: (Windows) " + outstr)
1109 elif subcommand == "raise":
1112 if domain_level is not None:
1113 if domain_level == "2003":
1114 new_level_domain = DS_DOMAIN_FUNCTION_2003
1115 elif domain_level == "2008":
1116 new_level_domain = DS_DOMAIN_FUNCTION_2008
1117 elif domain_level == "2008_R2":
1118 new_level_domain = DS_DOMAIN_FUNCTION_2008_R2
1119 elif domain_level == "2012":
1120 new_level_domain = DS_DOMAIN_FUNCTION_2012
1121 elif domain_level == "2012_R2":
1122 new_level_domain = DS_DOMAIN_FUNCTION_2012_R2
1124 if new_level_domain <= level_domain and level_domain_mixed == 0:
1125 raise CommandError("Domain function level can't be smaller than or equal to the actual one!")
1126 if new_level_domain > min_level_dc:
1127 raise CommandError("Domain function level can't be higher than the lowest function level of a DC!")
1129 # Deactivate mixed/interim domain support
1130 if level_domain_mixed != 0:
1131 # Directly on the base DN
1133 m.dn = ldb.Dn(samdb, domain_dn)
1134 m["nTMixedDomain"] = ldb.MessageElement("0",
1135 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1139 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup") + ",CN=Partitions,%s" % samdb.get_config_basedn())
1140 m["nTMixedDomain"] = ldb.MessageElement("0",
1141 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1144 except ldb.LdbError, (enum, emsg):
1145 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1148 # Directly on the base DN
1150 m.dn = ldb.Dn(samdb, domain_dn)
1151 m["msDS-Behavior-Version"]= ldb.MessageElement(
1152 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1153 "msDS-Behavior-Version")
1157 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup")
1158 + ",CN=Partitions,%s" % samdb.get_config_basedn())
1159 m["msDS-Behavior-Version"]= ldb.MessageElement(
1160 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1161 "msDS-Behavior-Version")
1164 except ldb.LdbError, (enum, emsg):
1165 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1168 level_domain = new_level_domain
1169 msgs.append("Domain function level changed!")
1171 if forest_level is not None:
1172 if forest_level == "2003":
1173 new_level_forest = DS_DOMAIN_FUNCTION_2003
1174 elif forest_level == "2008":
1175 new_level_forest = DS_DOMAIN_FUNCTION_2008
1176 elif forest_level == "2008_R2":
1177 new_level_forest = DS_DOMAIN_FUNCTION_2008_R2
1178 elif forest_level == "2012":
1179 new_level_forest = DS_DOMAIN_FUNCTION_2012
1180 elif forest_level == "2012_R2":
1181 new_level_forest = DS_DOMAIN_FUNCTION_2012_R2
1183 if new_level_forest <= level_forest:
1184 raise CommandError("Forest function level can't be smaller than or equal to the actual one!")
1185 if new_level_forest > level_domain:
1186 raise CommandError("Forest function level can't be higher than the domain function level(s). Please raise it/them first!")
1189 m.dn = ldb.Dn(samdb, "CN=Partitions,%s" % samdb.get_config_basedn())
1190 m["msDS-Behavior-Version"]= ldb.MessageElement(
1191 str(new_level_forest), ldb.FLAG_MOD_REPLACE,
1192 "msDS-Behavior-Version")
1194 msgs.append("Forest function level changed!")
1195 msgs.append("All changes applied successfully!")
1196 self.message("\n".join(msgs))
1198 raise CommandError("invalid argument: '%s' (choose from 'show', 'raise')" % subcommand)
1201 class cmd_domain_passwordsettings(Command):
1202 """Set password settings.
1204 Password complexity, password lockout policy, history length,
1205 minimum password length, the minimum and maximum password age) on
1206 a Samba AD DC server.
1208 Use against a Windows DC is possible, but group policy will override it.
1211 synopsis = "%prog (show|set <options>) [options]"
1213 takes_optiongroups = {
1214 "sambaopts": options.SambaOptions,
1215 "versionopts": options.VersionOptions,
1216 "credopts": options.CredentialsOptions,
1220 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1221 metavar="URL", dest="H"),
1222 Option("--quiet", help="Be quiet", action="store_true"),
1223 Option("--complexity", type="choice", choices=["on","off","default"],
1224 help="The password complexity (on | off | default). Default is 'on'"),
1225 Option("--store-plaintext", type="choice", choices=["on","off","default"],
1226 help="Store plaintext passwords where account have 'store passwords with reversible encryption' set (on | off | default). Default is 'off'"),
1227 Option("--history-length",
1228 help="The password history length (<integer> | default). Default is 24.", type=str),
1229 Option("--min-pwd-length",
1230 help="The minimum password length (<integer> | default). Default is 7.", type=str),
1231 Option("--min-pwd-age",
1232 help="The minimum password age (<integer in days> | default). Default is 1.", type=str),
1233 Option("--max-pwd-age",
1234 help="The maximum password age (<integer in days> | default). Default is 43.", type=str),
1235 Option("--account-lockout-duration",
1236 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),
1237 Option("--account-lockout-threshold",
1238 help="The number of bad password attempts allowed before locking out the account (<integer> | default). Default is 0 (never lock out).", type=str),
1239 Option("--reset-account-lockout-after",
1240 help="After this time is elapsed, the recorded number of attempts restarts from zero (<integer> | default). Default is 30.", type=str),
1243 takes_args = ["subcommand"]
1245 def run(self, subcommand, H=None, min_pwd_age=None, max_pwd_age=None,
1246 quiet=False, complexity=None, store_plaintext=None, history_length=None,
1247 min_pwd_length=None, account_lockout_duration=None, account_lockout_threshold=None,
1248 reset_account_lockout_after=None, credopts=None, sambaopts=None,
1250 lp = sambaopts.get_loadparm()
1251 creds = credopts.get_credentials(lp)
1253 samdb = SamDB(url=H, session_info=system_session(),
1254 credentials=creds, lp=lp)
1256 domain_dn = samdb.domain_dn()
1257 res = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1258 attrs=["pwdProperties", "pwdHistoryLength", "minPwdLength",
1259 "minPwdAge", "maxPwdAge", "lockoutDuration", "lockoutThreshold",
1260 "lockOutObservationWindow"])
1261 assert(len(res) == 1)
1263 pwd_props = int(res[0]["pwdProperties"][0])
1264 pwd_hist_len = int(res[0]["pwdHistoryLength"][0])
1265 cur_min_pwd_len = int(res[0]["minPwdLength"][0])
1267 cur_min_pwd_age = int(abs(int(res[0]["minPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1268 if int(res[0]["maxPwdAge"][0]) == -0x8000000000000000:
1271 cur_max_pwd_age = int(abs(int(res[0]["maxPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1272 cur_account_lockout_threshold = int(res[0]["lockoutThreshold"][0])
1274 if int(res[0]["lockoutDuration"][0]) == -0x8000000000000000:
1275 cur_account_lockout_duration = 0
1277 cur_account_lockout_duration = abs(int(res[0]["lockoutDuration"][0])) / (1e7 * 60)
1278 cur_reset_account_lockout_after = abs(int(res[0]["lockOutObservationWindow"][0])) / (1e7 * 60)
1279 except Exception, e:
1280 raise CommandError("Could not retrieve password properties!", e)
1282 if subcommand == "show":
1283 self.message("Password informations for domain '%s'" % domain_dn)
1285 if pwd_props & DOMAIN_PASSWORD_COMPLEX != 0:
1286 self.message("Password complexity: on")
1288 self.message("Password complexity: off")
1289 if pwd_props & DOMAIN_PASSWORD_STORE_CLEARTEXT != 0:
1290 self.message("Store plaintext passwords: on")
1292 self.message("Store plaintext passwords: off")
1293 self.message("Password history length: %d" % pwd_hist_len)
1294 self.message("Minimum password length: %d" % cur_min_pwd_len)
1295 self.message("Minimum password age (days): %d" % cur_min_pwd_age)
1296 self.message("Maximum password age (days): %d" % cur_max_pwd_age)
1297 self.message("Account lockout duration (mins): %d" % cur_account_lockout_duration)
1298 self.message("Account lockout threshold (attempts): %d" % cur_account_lockout_threshold)
1299 self.message("Reset account lockout after (mins): %d" % cur_reset_account_lockout_after)
1300 elif subcommand == "set":
1303 m.dn = ldb.Dn(samdb, domain_dn)
1305 if complexity is not None:
1306 if complexity == "on" or complexity == "default":
1307 pwd_props = pwd_props | DOMAIN_PASSWORD_COMPLEX
1308 msgs.append("Password complexity activated!")
1309 elif complexity == "off":
1310 pwd_props = pwd_props & (~DOMAIN_PASSWORD_COMPLEX)
1311 msgs.append("Password complexity deactivated!")
1313 if store_plaintext is not None:
1314 if store_plaintext == "on" or store_plaintext == "default":
1315 pwd_props = pwd_props | DOMAIN_PASSWORD_STORE_CLEARTEXT
1316 msgs.append("Plaintext password storage for changed passwords activated!")
1317 elif store_plaintext == "off":
1318 pwd_props = pwd_props & (~DOMAIN_PASSWORD_STORE_CLEARTEXT)
1319 msgs.append("Plaintext password storage for changed passwords deactivated!")
1321 if complexity is not None or store_plaintext is not None:
1322 m["pwdProperties"] = ldb.MessageElement(str(pwd_props),
1323 ldb.FLAG_MOD_REPLACE, "pwdProperties")
1325 if history_length is not None:
1326 if history_length == "default":
1329 pwd_hist_len = int(history_length)
1331 if pwd_hist_len < 0 or pwd_hist_len > 24:
1332 raise CommandError("Password history length must be in the range of 0 to 24!")
1334 m["pwdHistoryLength"] = ldb.MessageElement(str(pwd_hist_len),
1335 ldb.FLAG_MOD_REPLACE, "pwdHistoryLength")
1336 msgs.append("Password history length changed!")
1338 if min_pwd_length is not None:
1339 if min_pwd_length == "default":
1342 min_pwd_len = int(min_pwd_length)
1344 if min_pwd_len < 0 or min_pwd_len > 14:
1345 raise CommandError("Minimum password length must be in the range of 0 to 14!")
1347 m["minPwdLength"] = ldb.MessageElement(str(min_pwd_len),
1348 ldb.FLAG_MOD_REPLACE, "minPwdLength")
1349 msgs.append("Minimum password length changed!")
1351 if min_pwd_age is not None:
1352 if min_pwd_age == "default":
1355 min_pwd_age = int(min_pwd_age)
1357 if min_pwd_age < 0 or min_pwd_age > 998:
1358 raise CommandError("Minimum password age must be in the range of 0 to 998!")
1361 min_pwd_age_ticks = -int(min_pwd_age * (24 * 60 * 60 * 1e7))
1363 m["minPwdAge"] = ldb.MessageElement(str(min_pwd_age_ticks),
1364 ldb.FLAG_MOD_REPLACE, "minPwdAge")
1365 msgs.append("Minimum password age changed!")
1367 if max_pwd_age is not None:
1368 if max_pwd_age == "default":
1371 max_pwd_age = int(max_pwd_age)
1373 if max_pwd_age < 0 or max_pwd_age > 999:
1374 raise CommandError("Maximum password age must be in the range of 0 to 999!")
1377 if max_pwd_age == 0:
1378 max_pwd_age_ticks = -0x8000000000000000
1380 max_pwd_age_ticks = -int(max_pwd_age * (24 * 60 * 60 * 1e7))
1382 m["maxPwdAge"] = ldb.MessageElement(str(max_pwd_age_ticks),
1383 ldb.FLAG_MOD_REPLACE, "maxPwdAge")
1384 msgs.append("Maximum password age changed!")
1386 if account_lockout_duration is not None:
1387 if account_lockout_duration == "default":
1388 account_lockout_duration = 30
1390 account_lockout_duration = int(account_lockout_duration)
1392 if account_lockout_duration < 0 or account_lockout_duration > 99999:
1393 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1396 if account_lockout_duration == 0:
1397 account_lockout_duration_ticks = -0x8000000000000000
1399 account_lockout_duration_ticks = -int(account_lockout_duration * (60 * 1e7))
1401 m["lockoutDuration"] = ldb.MessageElement(str(account_lockout_duration_ticks),
1402 ldb.FLAG_MOD_REPLACE, "lockoutDuration")
1403 msgs.append("Account lockout duration changed!")
1405 if account_lockout_threshold is not None:
1406 if account_lockout_threshold == "default":
1407 account_lockout_threshold = 0
1409 account_lockout_threshold = int(account_lockout_threshold)
1411 m["lockoutThreshold"] = ldb.MessageElement(str(account_lockout_threshold),
1412 ldb.FLAG_MOD_REPLACE, "lockoutThreshold")
1413 msgs.append("Account lockout threshold changed!")
1415 if reset_account_lockout_after is not None:
1416 if reset_account_lockout_after == "default":
1417 reset_account_lockout_after = 30
1419 reset_account_lockout_after = int(reset_account_lockout_after)
1421 if reset_account_lockout_after < 0 or reset_account_lockout_after > 99999:
1422 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1425 if reset_account_lockout_after == 0:
1426 reset_account_lockout_after_ticks = -0x8000000000000000
1428 reset_account_lockout_after_ticks = -int(reset_account_lockout_after * (60 * 1e7))
1430 m["lockOutObservationWindow"] = ldb.MessageElement(str(reset_account_lockout_after_ticks),
1431 ldb.FLAG_MOD_REPLACE, "lockOutObservationWindow")
1432 msgs.append("Duration to reset account lockout after changed!")
1434 if max_pwd_age > 0 and min_pwd_age >= max_pwd_age:
1435 raise CommandError("Maximum password age (%d) must be greater than minimum password age (%d)!" % (max_pwd_age, min_pwd_age))
1438 raise CommandError("You must specify at least one option to set. Try --help")
1440 msgs.append("All changes applied successfully!")
1441 self.message("\n".join(msgs))
1443 raise CommandError("Wrong argument '%s'!" % subcommand)
1446 class cmd_domain_classicupgrade(Command):
1447 """Upgrade from Samba classic (NT4-like) database to Samba AD DC database.
1449 Specify either a directory with all Samba classic DC databases and state files (with --dbdir) or
1450 the testparm utility from your classic installation (with --testparm).
1453 synopsis = "%prog [options] <classic_smb_conf>"
1455 takes_optiongroups = {
1456 "sambaopts": options.SambaOptions,
1457 "versionopts": options.VersionOptions
1461 Option("--dbdir", type="string", metavar="DIR",
1462 help="Path to samba classic DC database directory"),
1463 Option("--testparm", type="string", metavar="PATH",
1464 help="Path to samba classic DC testparm utility from the previous installation. This allows the default paths of the previous installation to be followed"),
1465 Option("--targetdir", type="string", metavar="DIR",
1466 help="Path prefix where the new Samba 4.0 AD domain should be initialised"),
1467 Option("--quiet", help="Be quiet", action="store_true"),
1468 Option("--verbose", help="Be verbose", action="store_true"),
1469 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
1470 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
1471 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
1472 "BIND9_FLATFILE uses bind9 text database to store zone information, "
1473 "BIND9_DLZ uses samba4 AD to store zone information, "
1474 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
1475 default="SAMBA_INTERNAL")
1479 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
1480 action="store_true"),
1481 Option("--use-xattrs", type="choice", choices=["yes","no","auto"],
1482 metavar="[yes|no|auto]",
1483 help="Define if we should use the native fs capabilities or a tdb file for "
1484 "storing attributes likes ntacl when --use-ntvfs is set. "
1485 "auto tries to make an inteligent guess based on the user rights and system capabilities",
1488 if samba.is_ntvfs_fileserver_built():
1489 takes_options.extend(ntvfs_options)
1491 takes_args = ["smbconf"]
1493 def run(self, smbconf=None, targetdir=None, dbdir=None, testparm=None,
1494 quiet=False, verbose=False, use_xattrs="auto", sambaopts=None, versionopts=None,
1495 dns_backend=None, use_ntvfs=False):
1497 if not os.path.exists(smbconf):
1498 raise CommandError("File %s does not exist" % smbconf)
1500 if testparm and not os.path.exists(testparm):
1501 raise CommandError("Testparm utility %s does not exist" % testparm)
1503 if dbdir and not os.path.exists(dbdir):
1504 raise CommandError("Directory %s does not exist" % dbdir)
1506 if not dbdir and not testparm:
1507 raise CommandError("Please specify either dbdir or testparm")
1509 logger = self.get_logger()
1511 logger.setLevel(logging.DEBUG)
1513 logger.setLevel(logging.WARNING)
1515 logger.setLevel(logging.INFO)
1517 if dbdir and testparm:
1518 logger.warning("both dbdir and testparm specified, ignoring dbdir.")
1521 lp = sambaopts.get_loadparm()
1523 s3conf = s3param.get_context()
1526 s3conf.set("realm", sambaopts.realm)
1528 if targetdir is not None:
1529 if not os.path.isdir(targetdir):
1533 if use_xattrs == "yes":
1535 elif use_xattrs == "auto" and use_ntvfs == False:
1537 elif use_ntvfs == False:
1538 raise CommandError("--use-xattrs=no requires --use-ntvfs (not supported for production use). "
1539 "Please re-run with --use-xattrs omitted.")
1540 elif use_xattrs == "auto" and not s3conf.get("posix:eadb"):
1542 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
1544 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
1547 samba.ntacls.setntacl(lp, tmpfile.name,
1548 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
1551 # FIXME: Don't catch all exceptions here
1552 logger.info("You are not root or your system does not support xattr, using tdb backend for attributes. "
1553 "If you intend to use this provision in production, rerun the script as root on a system supporting xattrs.")
1557 # Set correct default values from dbdir or testparm
1560 paths["state directory"] = dbdir
1561 paths["private dir"] = dbdir
1562 paths["lock directory"] = dbdir
1563 paths["smb passwd file"] = dbdir + "/smbpasswd"
1565 paths["state directory"] = get_testparm_var(testparm, smbconf, "state directory")
1566 paths["private dir"] = get_testparm_var(testparm, smbconf, "private dir")
1567 paths["smb passwd file"] = get_testparm_var(testparm, smbconf, "smb passwd file")
1568 paths["lock directory"] = get_testparm_var(testparm, smbconf, "lock directory")
1569 # "testparm" from Samba 3 < 3.4.x is not aware of the parameter
1570 # "state directory", instead make use of "lock directory"
1571 if len(paths["state directory"]) == 0:
1572 paths["state directory"] = paths["lock directory"]
1575 s3conf.set(p, paths[p])
1577 # load smb.conf parameters
1578 logger.info("Reading smb.conf")
1579 s3conf.load(smbconf)
1580 samba3 = Samba3(smbconf, s3conf)
1582 logger.info("Provisioning")
1583 upgrade_from_samba3(samba3, logger, targetdir, session_info=system_session(),
1584 useeadb=eadb, dns_backend=dns_backend, use_ntvfs=use_ntvfs)
1587 class cmd_domain_samba3upgrade(cmd_domain_classicupgrade):
1588 __doc__ = cmd_domain_classicupgrade.__doc__
1590 # This command is present for backwards compatibility only,
1591 # and should not be shown.
1595 class LocalDCCredentialsOptions(options.CredentialsOptions):
1596 def __init__(self, parser):
1597 options.CredentialsOptions.__init__(self, parser, special_name="local-dc")
1599 class DomainTrustCommand(Command):
1600 """List domain trusts."""
1603 Command.__init__(self)
1604 self.local_lp = None
1606 self.local_server = None
1607 self.local_binding_string = None
1608 self.local_creds = None
1610 self.remote_server = None
1611 self.remote_binding_string = None
1612 self.remote_creds = None
1614 WERR_OK = 0x00000000
1615 WERR_INVALID_FUNCTION = 0x00000001
1616 WERR_NERR_ACFNOTLOADED = 0x000008B3
1618 NT_STATUS_NOT_FOUND = 0xC0000225
1619 NT_STATUS_OBJECT_NAME_NOT_FOUND = 0xC0000034
1620 NT_STATUS_INVALID_PARAMETER = 0xC000000D
1621 NT_STATUS_INVALID_INFO_CLASS = 0xC0000003
1622 NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE = 0xC002002E
1624 def _uint32(self, v):
1625 return ctypes.c_uint32(v).value
1627 def check_runtime_error(self, runtime, val):
1631 err32 = self._uint32(runtime[0])
1637 class LocalRuntimeError(CommandError):
1638 def __init__(exception_self, self, runtime, message):
1639 err32 = self._uint32(runtime[0])
1641 msg = "LOCAL_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1642 self.local_server, message, err32, errstr)
1643 CommandError.__init__(exception_self, msg)
1645 class RemoteRuntimeError(CommandError):
1646 def __init__(exception_self, self, runtime, message):
1647 err32 = self._uint32(runtime[0])
1649 msg = "REMOTE_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1650 self.remote_server, message, err32, errstr)
1651 CommandError.__init__(exception_self, msg)
1653 class LocalLdbError(CommandError):
1654 def __init__(exception_self, self, ldb_error, message):
1655 errval = ldb_error[0]
1656 errstr = ldb_error[1]
1657 msg = "LOCAL_DC[%s]: %s - ERROR(%d) - %s" % (
1658 self.local_server, message, errval, errstr)
1659 CommandError.__init__(exception_self, msg)
1661 def setup_local_server(self, sambaopts, localdcopts):
1662 if self.local_server is not None:
1663 return self.local_server
1665 lp = sambaopts.get_loadparm()
1667 local_server = localdcopts.ipaddress
1668 if local_server is None:
1669 server_role = lp.server_role()
1670 if server_role != "ROLE_ACTIVE_DIRECTORY_DC":
1671 raise CommandError("Invalid server_role %s" % (server_role))
1672 local_server = lp.get('netbios name')
1673 local_transport = "ncalrpc"
1674 local_binding_options = ""
1675 local_binding_options += ",auth_type=ncalrpc_as_system"
1676 local_ldap_url = None
1679 local_transport = "ncacn_np"
1680 local_binding_options = ""
1681 local_ldap_url = "ldap://%s" % local_server
1682 local_creds = localdcopts.get_credentials(lp)
1686 self.local_server = local_server
1687 self.local_binding_string = "%s:%s[%s]" % (local_transport, local_server, local_binding_options)
1688 self.local_ldap_url = local_ldap_url
1689 self.local_creds = local_creds
1690 return self.local_server
1692 def new_local_lsa_connection(self):
1693 return lsa.lsarpc(self.local_binding_string, self.local_lp, self.local_creds)
1695 def new_local_netlogon_connection(self):
1696 return netlogon.netlogon(self.local_binding_string, self.local_lp, self.local_creds)
1698 def new_local_ldap_connection(self):
1699 return SamDB(url=self.local_ldap_url,
1700 session_info=system_session(),
1701 credentials=self.local_creds,
1704 def setup_remote_server(self, credopts, domain,
1706 require_writable=True):
1709 assert require_writable
1711 if self.remote_server is not None:
1712 return self.remote_server
1714 self.remote_server = "__unknown__remote_server__.%s" % domain
1715 assert self.local_server is not None
1717 remote_creds = credopts.get_credentials(self.local_lp)
1718 remote_server = credopts.ipaddress
1719 remote_binding_options = ""
1721 # TODO: we should also support NT4 domains
1722 # we could use local_netlogon.netr_DsRGetDCNameEx2() with the remote domain name
1723 # and delegate NBT or CLDAP to the local netlogon server
1725 remote_net = Net(remote_creds, self.local_lp, server=remote_server)
1726 remote_flags = nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS
1727 if require_writable:
1728 remote_flags |= nbt.NBT_SERVER_WRITABLE
1730 remote_flags |= nbt.NBT_SERVER_PDC
1731 remote_info = remote_net.finddc(flags=remote_flags, domain=domain, address=remote_server)
1733 raise CommandError("Failed to find a writeable DC for domain '%s'" % domain)
1735 nbt.NBT_SERVER_PDC: "PDC",
1736 nbt.NBT_SERVER_GC: "GC",
1737 nbt.NBT_SERVER_LDAP: "LDAP",
1738 nbt.NBT_SERVER_DS: "DS",
1739 nbt.NBT_SERVER_KDC: "KDC",
1740 nbt.NBT_SERVER_TIMESERV: "TIMESERV",
1741 nbt.NBT_SERVER_CLOSEST: "CLOSEST",
1742 nbt.NBT_SERVER_WRITABLE: "WRITABLE",
1743 nbt.NBT_SERVER_GOOD_TIMESERV: "GOOD_TIMESERV",
1744 nbt.NBT_SERVER_NDNC: "NDNC",
1745 nbt.NBT_SERVER_SELECT_SECRET_DOMAIN_6: "SELECT_SECRET_DOMAIN_6",
1746 nbt.NBT_SERVER_FULL_SECRET_DOMAIN_6: "FULL_SECRET_DOMAIN_6",
1747 nbt.NBT_SERVER_ADS_WEB_SERVICE: "ADS_WEB_SERVICE",
1748 nbt.NBT_SERVER_DS_8: "DS_8",
1749 nbt.NBT_SERVER_HAS_DNS_NAME: "HAS_DNS_NAME",
1750 nbt.NBT_SERVER_IS_DEFAULT_NC: "IS_DEFAULT_NC",
1751 nbt.NBT_SERVER_FOREST_ROOT: "FOREST_ROOT",
1753 server_type_string = self.generic_bitmap_to_string(flag_map,
1754 remote_info.server_type, names_only=True)
1755 self.outf.write("RemoteDC Netbios[%s] DNS[%s] ServerType[%s]\n" % (
1756 remote_info.pdc_name,
1757 remote_info.pdc_dns_name,
1758 server_type_string))
1760 self.remote_server = remote_info.pdc_dns_name
1761 self.remote_binding_string="ncacn_np:%s[%s]" % (self.remote_server, remote_binding_options)
1762 self.remote_creds = remote_creds
1763 return self.remote_server
1765 def new_remote_lsa_connection(self):
1766 return lsa.lsarpc(self.remote_binding_string, self.local_lp, self.remote_creds)
1768 def new_remote_netlogon_connection(self):
1769 return netlogon.netlogon(self.remote_binding_string, self.local_lp, self.remote_creds)
1771 def get_lsa_info(self, conn, policy_access):
1772 objectAttr = lsa.ObjectAttribute()
1773 objectAttr.sec_qos = lsa.QosInfo()
1775 policy = conn.OpenPolicy2(''.decode('utf-8'),
1776 objectAttr, policy_access)
1778 info = conn.QueryInfoPolicy2(policy, lsa.LSA_POLICY_INFO_DNS)
1780 return (policy, info)
1782 def get_netlogon_dc_info(self, conn, server):
1783 info = conn.netr_DsRGetDCNameEx2(server,
1784 None, 0, None, None, None,
1785 netlogon.DS_RETURN_DNS_NAME)
1788 def netr_DomainTrust_to_name(self, t):
1789 if t.trust_type == lsa.LSA_TRUST_TYPE_DOWNLEVEL:
1790 return t.netbios_name
1794 def netr_DomainTrust_to_type(self, a, t):
1796 primary_parent = None
1798 if _t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
1800 if not _t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1801 primary_parent = a[_t.parent_index]
1804 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1805 if t is primary_parent:
1808 if t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1811 parent = a[t.parent_index]
1812 if parent is primary:
1817 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1822 def netr_DomainTrust_to_transitive(self, t):
1823 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1826 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE:
1829 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1834 def netr_DomainTrust_to_direction(self, t):
1835 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND and \
1836 t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1839 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND:
1842 if t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1847 def generic_enum_to_string(self, e_dict, v, names_only=False):
1851 v32 = self._uint32(v)
1852 w = "__unknown__%08X__" % v32
1854 r = "0x%x (%s)" % (v, w)
1857 def generic_bitmap_to_string(self, b_dict, v, names_only=False):
1862 for b in sorted(b_dict.keys()):
1869 c32 = self._uint32(c)
1870 s += ["__unknown_%08X__" % c32]
1875 r = "0x%x (%s)" % (v, w)
1878 def trustType_string(self, v):
1880 lsa.LSA_TRUST_TYPE_DOWNLEVEL : "DOWNLEVEL",
1881 lsa.LSA_TRUST_TYPE_UPLEVEL : "UPLEVEL",
1882 lsa.LSA_TRUST_TYPE_MIT : "MIT",
1883 lsa.LSA_TRUST_TYPE_DCE : "DCE",
1885 return self.generic_enum_to_string(types, v)
1887 def trustDirection_string(self, v):
1889 lsa.LSA_TRUST_DIRECTION_INBOUND |
1890 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "BOTH",
1891 lsa.LSA_TRUST_DIRECTION_INBOUND : "INBOUND",
1892 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "OUTBOUND",
1894 return self.generic_enum_to_string(directions, v)
1896 def trustAttributes_string(self, v):
1898 lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE : "NON_TRANSITIVE",
1899 lsa.LSA_TRUST_ATTRIBUTE_UPLEVEL_ONLY : "UPLEVEL_ONLY",
1900 lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN : "QUARANTINED_DOMAIN",
1901 lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE : "FOREST_TRANSITIVE",
1902 lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION : "CROSS_ORGANIZATION",
1903 lsa.LSA_TRUST_ATTRIBUTE_WITHIN_FOREST : "WITHIN_FOREST",
1904 lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL : "TREAT_AS_EXTERNAL",
1905 lsa.LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION : "USES_RC4_ENCRYPTION",
1907 return self.generic_bitmap_to_string(attributes, v)
1909 def kerb_EncTypes_string(self, v):
1911 security.KERB_ENCTYPE_DES_CBC_CRC : "DES_CBC_CRC",
1912 security.KERB_ENCTYPE_DES_CBC_MD5 : "DES_CBC_MD5",
1913 security.KERB_ENCTYPE_RC4_HMAC_MD5 : "RC4_HMAC_MD5",
1914 security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96 : "AES128_CTS_HMAC_SHA1_96",
1915 security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96 : "AES256_CTS_HMAC_SHA1_96",
1916 security.KERB_ENCTYPE_FAST_SUPPORTED : "FAST_SUPPORTED",
1917 security.KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED : "COMPOUND_IDENTITY_SUPPORTED",
1918 security.KERB_ENCTYPE_CLAIMS_SUPPORTED : "CLAIMS_SUPPORTED",
1919 security.KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED : "RESOURCE_SID_COMPRESSION_DISABLED",
1921 return self.generic_bitmap_to_string(enctypes, v)
1923 def entry_tln_status(self, e_flags, ):
1925 return "Status[Enabled]"
1928 lsa.LSA_TLN_DISABLED_NEW : "Disabled-New",
1929 lsa.LSA_TLN_DISABLED_ADMIN : "Disabled",
1930 lsa.LSA_TLN_DISABLED_CONFLICT : "Disabled-Conflicting",
1932 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
1934 def entry_dom_status(self, e_flags):
1936 return "Status[Enabled]"
1939 lsa.LSA_SID_DISABLED_ADMIN : "Disabled-SID",
1940 lsa.LSA_SID_DISABLED_CONFLICT : "Disabled-SID-Conflicting",
1941 lsa.LSA_NB_DISABLED_ADMIN : "Disabled-NB",
1942 lsa.LSA_NB_DISABLED_CONFLICT : "Disabled-NB-Conflicting",
1944 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
1946 def write_forest_trust_info(self, fti, tln=None, collisions=None):
1948 tln_string = " TDO[%s]" % tln
1952 self.outf.write("Namespaces[%d]%s:\n" % (
1953 len(fti.entries), tln_string))
1955 for i in xrange(0, len(fti.entries)):
1959 collision_string = ""
1961 if collisions is not None:
1962 for c in collisions.entries:
1966 collision_string = " Collision[%s]" % (c.name.string)
1968 d = e.forest_trust_data
1969 if e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
1970 self.outf.write("TLN: %-32s DNS[*.%s]%s\n" % (
1971 self.entry_tln_status(flags),
1972 d.string, collision_string))
1973 elif e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
1974 self.outf.write("TLN_EX: %-29s DNS[*.%s]\n" % (
1976 elif e.type == lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
1977 self.outf.write("DOM: %-32s DNS[%s] Netbios[%s] SID[%s]%s\n" % (
1978 self.entry_dom_status(flags),
1979 d.dns_domain_name.string,
1980 d.netbios_domain_name.string,
1981 d.domain_sid, collision_string))
1984 class cmd_domain_trust_list(DomainTrustCommand):
1985 """List domain trusts."""
1987 synopsis = "%prog [options]"
1989 takes_optiongroups = {
1990 "sambaopts": options.SambaOptions,
1991 "versionopts": options.VersionOptions,
1992 "localdcopts": LocalDCCredentialsOptions,
1998 def run(self, sambaopts=None, versionopts=None, localdcopts=None):
2000 local_server = self.setup_local_server(sambaopts, localdcopts)
2002 local_netlogon = self.new_local_netlogon_connection()
2003 except RuntimeError as error:
2004 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2007 local_netlogon_trusts = local_netlogon.netr_DsrEnumerateDomainTrusts(local_server,
2008 netlogon.NETR_TRUST_FLAG_IN_FOREST |
2009 netlogon.NETR_TRUST_FLAG_OUTBOUND |
2010 netlogon.NETR_TRUST_FLAG_INBOUND)
2011 except RuntimeError as error:
2012 if self.check_runtime_error(error, self.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
2013 # TODO: we could implement a fallback to lsa.EnumTrustDom()
2014 raise CommandError("LOCAL_DC[%s]: netr_DsrEnumerateDomainTrusts not supported." % (
2016 raise self.LocalRuntimeError(self, error, "netr_DsrEnumerateDomainTrusts failed")
2018 a = local_netlogon_trusts.array
2020 if t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
2022 self.outf.write("%-14s %-15s %-19s %s\n" % (
2023 "Type[%s]" % self.netr_DomainTrust_to_type(a, t),
2024 "Transitive[%s]" % self.netr_DomainTrust_to_transitive(t),
2025 "Direction[%s]" % self.netr_DomainTrust_to_direction(t),
2026 "Name[%s]" % self.netr_DomainTrust_to_name(t)))
2029 class cmd_domain_trust_show(DomainTrustCommand):
2030 """Show trusted domain details."""
2032 synopsis = "%prog NAME [options]"
2034 takes_optiongroups = {
2035 "sambaopts": options.SambaOptions,
2036 "versionopts": options.VersionOptions,
2037 "localdcopts": LocalDCCredentialsOptions,
2043 takes_args = ["domain"]
2045 def run(self, domain, sambaopts=None, versionopts=None, localdcopts=None):
2047 local_server = self.setup_local_server(sambaopts, localdcopts)
2049 local_lsa = self.new_local_lsa_connection()
2050 except RuntimeError as error:
2051 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2054 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2055 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2056 except RuntimeError as error:
2057 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2059 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2060 local_lsa_info.name.string,
2061 local_lsa_info.dns_domain.string,
2062 local_lsa_info.sid))
2064 lsaString = lsa.String()
2065 lsaString.string = domain
2067 local_tdo_full = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2068 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2069 local_tdo_info = local_tdo_full.info_ex
2070 local_tdo_posix = local_tdo_full.posix_offset
2071 except RuntimeError as error:
2072 if self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2073 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2075 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(FULL_INFO) failed")
2078 local_tdo_enctypes = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2079 lsaString, lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES)
2080 except RuntimeError as error:
2081 if self.check_runtime_error(error, self.NT_STATUS_INVALID_PARAMETER):
2083 if self.check_runtime_error(error, self.NT_STATUS_INVALID_INFO_CLASS):
2086 if error is not None:
2087 raise self.LocalRuntimeError(self, error,
2088 "QueryTrustedDomainInfoByName(SUPPORTED_ENCRYPTION_TYPES) failed")
2090 local_tdo_enctypes = lsa.TrustDomainInfoSupportedEncTypes()
2091 local_tdo_enctypes.enc_types = 0
2094 local_tdo_forest = None
2095 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2096 local_tdo_forest = local_lsa.lsaRQueryForestTrustInformation(local_policy,
2097 lsaString, lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
2098 except RuntimeError as error:
2099 if self.check_runtime_error(error, self.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
2101 if self.check_runtime_error(error, self.NT_STATUS_NOT_FOUND):
2103 if error is not None:
2104 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation failed")
2106 local_tdo_forest = lsa.ForestTrustInformation()
2107 local_tdo_forest.count = 0
2108 local_tdo_forest.entries = []
2110 self.outf.write("TrusteDomain:\n\n");
2111 self.outf.write("NetbiosName: %s\n" % local_tdo_info.netbios_name.string)
2112 if local_tdo_info.netbios_name.string != local_tdo_info.domain_name.string:
2113 self.outf.write("DnsName: %s\n" % local_tdo_info.domain_name.string)
2114 self.outf.write("SID: %s\n" % local_tdo_info.sid)
2115 self.outf.write("Type: %s\n" % self.trustType_string(local_tdo_info.trust_type))
2116 self.outf.write("Direction: %s\n" % self.trustDirection_string(local_tdo_info.trust_direction))
2117 self.outf.write("Attributes: %s\n" % self.trustAttributes_string(local_tdo_info.trust_attributes))
2118 posix_offset_u32 = ctypes.c_uint32(local_tdo_posix.posix_offset).value
2119 posix_offset_i32 = ctypes.c_int32(local_tdo_posix.posix_offset).value
2120 self.outf.write("PosixOffset: 0x%08X (%d)\n" % (posix_offset_u32, posix_offset_i32))
2121 self.outf.write("kerb_EncTypes: %s\n" % self.kerb_EncTypes_string(local_tdo_enctypes.enc_types))
2123 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2124 self.write_forest_trust_info(local_tdo_forest,
2125 tln=local_tdo_info.domain_name.string)
2129 class cmd_domain_trust_create(DomainTrustCommand):
2130 """Create a domain or forest trust."""
2132 synopsis = "%prog DOMAIN [options]"
2134 takes_optiongroups = {
2135 "sambaopts": options.SambaOptions,
2136 "versionopts": options.VersionOptions,
2137 "credopts": options.CredentialsOptions,
2138 "localdcopts": LocalDCCredentialsOptions,
2142 Option("--type", type="choice", metavar="TYPE",
2143 choices=["external", "forest"],
2144 help="The type of the trust: 'external' or 'forest'.",
2146 default="external"),
2147 Option("--direction", type="choice", metavar="DIRECTION",
2148 choices=["incoming", "outgoing", "both"],
2149 help="The trust direction: 'incoming', 'outgoing' or 'both'.",
2150 dest='trust_direction',
2152 Option("--create-location", type="choice", metavar="LOCATION",
2153 choices=["local", "both"],
2154 help="Where to create the trusted domain object: 'local' or 'both'.",
2155 dest='create_location',
2157 Option("--cross-organisation", action="store_true",
2158 help="The related domains does not belong to the same organisation.",
2159 dest='cross_organisation',
2161 Option("--quarantined", type="choice", metavar="yes|no",
2162 choices=["yes", "no", None],
2163 help="Special SID filtering rules are applied to the trust. "
2164 "With --type=external the default is yes. "
2165 "With --type=forest the default is no.",
2166 dest='quarantined_arg',
2168 Option("--not-transitive", action="store_true",
2169 help="The forest trust is not transitive.",
2170 dest='not_transitive',
2172 Option("--treat-as-external", action="store_true",
2173 help="The treat the forest trust as external.",
2174 dest='treat_as_external',
2176 Option("--no-aes-keys", action="store_false",
2177 help="The trust uses aes kerberos keys.",
2178 dest='use_aes_keys',
2180 Option("--skip-validation", action="store_false",
2181 help="Skip validation of the trust.",
2186 takes_args = ["domain"]
2188 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2189 trust_type=None, trust_direction=None, create_location=None,
2190 cross_organisation=False, quarantined_arg=None,
2191 not_transitive=False, treat_as_external=False,
2192 use_aes_keys=False, validate=True):
2194 lsaString = lsa.String()
2197 if quarantined_arg is None:
2198 if trust_type == 'external':
2200 elif quarantined_arg == 'yes':
2203 if trust_type != 'forest':
2205 raise CommandError("--not-transitive requires --type=forest")
2206 if treat_as_external:
2207 raise CommandError("--treat-as-external requires --type=forest")
2211 enc_types = lsa.TrustDomainInfoSupportedEncTypes()
2212 enc_types.enc_types = security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96
2213 enc_types.enc_types |= security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96
2215 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2216 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2217 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2219 local_trust_info = lsa.TrustDomainInfoInfoEx()
2220 local_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2221 local_trust_info.trust_direction = 0
2222 if trust_direction == "both":
2223 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2224 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2225 elif trust_direction == "incoming":
2226 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2227 elif trust_direction == "outgoing":
2228 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2229 local_trust_info.trust_attributes = 0
2230 if cross_organisation:
2231 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2233 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2234 if trust_type == "forest":
2235 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2237 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2238 if treat_as_external:
2239 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2241 def get_password(name):
2244 if password is not None and password is not '':
2246 password = getpass("New %s Password: " % name)
2247 passwordverify = getpass("Retype %s Password: " % name)
2248 if not password == passwordverify:
2250 self.outf.write("Sorry, passwords do not match.\n")
2252 incoming_secret = None
2253 outgoing_secret = None
2254 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2255 if create_location == "local":
2256 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2257 incoming_password = get_password("Incoming Trust")
2258 incoming_secret = string_to_byte_array(incoming_password.encode('utf-16-le'))
2259 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2260 outgoing_password = get_password("Outgoing Trust")
2261 outgoing_secret = string_to_byte_array(outgoing_password.encode('utf-16-le'))
2263 remote_trust_info = None
2265 # We use 240 random bytes.
2266 # Windows uses 28 or 240 random bytes. I guess it's
2267 # based on the trust type external vs. forest.
2269 # The initial trust password can be up to 512 bytes
2270 # while the versioned passwords used for periodic updates
2271 # can only be up to 498 bytes, as netr_ServerPasswordSet2()
2272 # needs to pass the NL_PASSWORD_VERSION structure within the
2273 # 512 bytes and a 2 bytes confounder is required.
2275 def random_trust_secret(length, use_aes_keys=True):
2276 secret = [0] * length
2278 pw1 = samba.generate_random_password(length/2, length/2)
2279 if not use_aes_keys:
2280 # With arcfour-hmac-md5 we have to use valid utf16
2281 # in order to generate the correct pre-auth key
2282 # based on a utf8 password.
2284 # We can remove this once our client libraries
2285 # support using the correct NTHASH.
2286 return string_to_byte_array(pw1.encode('utf-16-le'))
2288 # We mix characters from generate_random_password
2289 # with random numbers from random.randint()
2290 for i in range(len(secret)):
2292 secret[i] = ord(pw1[i])
2294 secret[i] = random.randint(0, 255)
2298 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2299 incoming_secret = random_trust_secret(240, use_aes_keys=use_aes_keys)
2300 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2301 outgoing_secret = random_trust_secret(240, use_aes_keys=use_aes_keys)
2303 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2304 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2306 remote_trust_info = lsa.TrustDomainInfoInfoEx()
2307 remote_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2308 remote_trust_info.trust_direction = 0
2309 if trust_direction == "both":
2310 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2311 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2312 elif trust_direction == "incoming":
2313 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2314 elif trust_direction == "outgoing":
2315 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2316 remote_trust_info.trust_attributes = 0
2317 if cross_organisation:
2318 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2320 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2321 if trust_type == "forest":
2322 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2324 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2325 if treat_as_external:
2326 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2328 local_server = self.setup_local_server(sambaopts, localdcopts)
2330 local_lsa = self.new_local_lsa_connection()
2331 except RuntimeError as error:
2332 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2335 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2336 except RuntimeError as error:
2337 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2339 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2340 local_lsa_info.name.string,
2341 local_lsa_info.dns_domain.string,
2342 local_lsa_info.sid))
2345 remote_server = self.setup_remote_server(credopts, domain)
2346 except RuntimeError as error:
2347 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2350 remote_lsa = self.new_remote_lsa_connection()
2351 except RuntimeError as error:
2352 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2355 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2356 except RuntimeError as error:
2357 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2359 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2360 remote_lsa_info.name.string,
2361 remote_lsa_info.dns_domain.string,
2362 remote_lsa_info.sid))
2364 local_trust_info.domain_name.string = remote_lsa_info.dns_domain.string
2365 local_trust_info.netbios_name.string = remote_lsa_info.name.string
2366 local_trust_info.sid = remote_lsa_info.sid
2368 if remote_trust_info:
2369 remote_trust_info.domain_name.string = local_lsa_info.dns_domain.string
2370 remote_trust_info.netbios_name.string = local_lsa_info.name.string
2371 remote_trust_info.sid = local_lsa_info.sid
2374 lsaString.string = local_trust_info.domain_name.string
2375 local_old_netbios = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2376 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2377 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2378 except RuntimeError as error:
2379 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2380 raise self.LocalRuntimeError(self, error,
2381 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2385 lsaString.string = local_trust_info.netbios_name.string
2386 local_old_dns = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2387 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2388 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2389 except RuntimeError as error:
2390 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2391 raise self.LocalRuntimeError(self, error,
2392 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2395 if remote_trust_info:
2397 lsaString.string = remote_trust_info.domain_name.string
2398 remote_old_netbios = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2399 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2400 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2401 except RuntimeError as error:
2402 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2403 raise self.RemoteRuntimeError(self, error,
2404 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2408 lsaString.string = remote_trust_info.netbios_name.string
2409 remote_old_dns = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2410 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2411 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2412 except RuntimeError as error:
2413 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2414 raise self.RemoteRuntimeError(self, error,
2415 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2419 local_netlogon = self.new_local_netlogon_connection()
2420 except RuntimeError as error:
2421 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2424 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
2425 except RuntimeError as error:
2426 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
2428 if remote_trust_info:
2430 remote_netlogon = self.new_remote_netlogon_connection()
2431 except RuntimeError as error:
2432 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2435 remote_netlogon_info = self.get_netlogon_dc_info(remote_netlogon, remote_server)
2436 except RuntimeError as error:
2437 raise self.RemoteRuntimeError(self, error, "failed to get netlogon dc info")
2439 def generate_AuthInOutBlob(secret, update_time):
2441 blob = drsblobs.trustAuthInOutBlob()
2446 clear = drsblobs.AuthInfoClear()
2447 clear.size = len(secret)
2448 clear.password = secret
2450 info = drsblobs.AuthenticationInformation()
2451 info.LastUpdateTime = samba.unix2nttime(update_time)
2452 info.AuthType = lsa.TRUST_AUTH_TYPE_CLEAR
2453 info.AuthInfo = clear
2455 array = drsblobs.AuthenticationInformationArray()
2457 array.array = [info]
2459 blob = drsblobs.trustAuthInOutBlob()
2461 blob.current = array
2465 def generate_AuthInfoInternal(session_key, incoming=None, outgoing=None):
2466 confounder = [0] * 512
2467 for i in range(len(confounder)):
2468 confounder[i] = random.randint(0, 255)
2470 trustpass = drsblobs.trustDomainPasswords()
2472 trustpass.confounder = confounder
2473 trustpass.outgoing = outgoing
2474 trustpass.incoming = incoming
2476 trustpass_blob = ndr_pack(trustpass)
2478 encrypted_trustpass = arcfour_encrypt(session_key, trustpass_blob)
2480 auth_blob = lsa.DATA_BUF2()
2481 auth_blob.size = len(encrypted_trustpass)
2482 auth_blob.data = string_to_byte_array(encrypted_trustpass)
2484 auth_info = lsa.TrustDomainInfoAuthInfoInternal()
2485 auth_info.auth_blob = auth_blob
2489 update_time = samba.current_unix_time()
2490 incoming_blob = generate_AuthInOutBlob(incoming_secret, update_time)
2491 outgoing_blob = generate_AuthInOutBlob(outgoing_secret, update_time)
2493 local_tdo_handle = None
2494 remote_tdo_handle = None
2496 local_auth_info = generate_AuthInfoInternal(local_lsa.session_key,
2497 incoming=incoming_blob,
2498 outgoing=outgoing_blob)
2499 if remote_trust_info:
2500 remote_auth_info = generate_AuthInfoInternal(remote_lsa.session_key,
2501 incoming=outgoing_blob,
2502 outgoing=incoming_blob)
2505 if remote_trust_info:
2506 self.outf.write("Creating remote TDO.\n")
2507 current_request = { "location": "remote", "name": "CreateTrustedDomainEx2"}
2508 remote_tdo_handle = remote_lsa.CreateTrustedDomainEx2(remote_policy,
2511 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2512 self.outf.write("Remote TDO created.\n")
2514 self.outf.write("Setting supported encryption types on remote TDO.\n")
2515 current_request = { "location": "remote", "name": "SetInformationTrustedDomain"}
2516 remote_lsa.SetInformationTrustedDomain(remote_tdo_handle,
2517 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2520 self.outf.write("Creating local TDO.\n")
2521 current_request = { "location": "local", "name": "CreateTrustedDomainEx2"}
2522 local_tdo_handle = local_lsa.CreateTrustedDomainEx2(local_policy,
2525 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2526 self.outf.write("Local TDO created\n")
2528 self.outf.write("Setting supported encryption types on local TDO.\n")
2529 current_request = { "location": "local", "name": "SetInformationTrustedDomain"}
2530 local_lsa.SetInformationTrustedDomain(local_tdo_handle,
2531 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2533 except RuntimeError as error:
2534 self.outf.write("Error: %s failed %sly - cleaning up\n" % (
2535 current_request['name'], current_request['location']))
2536 if remote_tdo_handle:
2537 self.outf.write("Deleting remote TDO.\n")
2538 remote_lsa.DeleteObject(remote_tdo_handle)
2539 remote_tdo_handle = None
2540 if local_tdo_handle:
2541 self.outf.write("Deleting local TDO.\n")
2542 local_lsa.DeleteObject(local_tdo_handle)
2543 local_tdo_handle = None
2544 if current_request['location'] is "remote":
2545 raise self.RemoteRuntimeError(self, error, "%s" % (
2546 current_request['name']))
2547 raise self.LocalRuntimeError(self, error, "%s" % (
2548 current_request['name']))
2551 if local_trust_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2552 self.outf.write("Setup local forest trust information...\n")
2554 # get all information about the remote trust
2555 # this triggers netr_GetForestTrustInformation to the remote domain
2556 # and lsaRSetForestTrustInformation() locally, but new top level
2557 # names are disabled by default.
2558 local_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
2559 remote_lsa_info.dns_domain.string,
2560 netlogon.DS_GFTI_UPDATE_TDO)
2561 except RuntimeError as error:
2562 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2565 # here we try to enable all top level names
2566 local_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
2567 remote_lsa_info.dns_domain,
2568 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2571 except RuntimeError as error:
2572 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2574 self.write_forest_trust_info(local_forest_info,
2575 tln=remote_lsa_info.dns_domain.string,
2576 collisions=local_forest_collision)
2578 if remote_trust_info:
2579 self.outf.write("Setup remote forest trust information...\n")
2581 # get all information about the local trust (from the perspective of the remote domain)
2582 # this triggers netr_GetForestTrustInformation to our domain.
2583 # and lsaRSetForestTrustInformation() remotely, but new top level
2584 # names are disabled by default.
2585 remote_forest_info = remote_netlogon.netr_DsRGetForestTrustInformation(remote_netlogon_info.dc_unc,
2586 local_lsa_info.dns_domain.string,
2587 netlogon.DS_GFTI_UPDATE_TDO)
2588 except RuntimeError as error:
2589 raise self.RemoteRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2592 # here we try to enable all top level names
2593 remote_forest_collision = remote_lsa.lsaRSetForestTrustInformation(remote_policy,
2594 local_lsa_info.dns_domain,
2595 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2598 except RuntimeError as error:
2599 raise self.RemoteRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2601 self.write_forest_trust_info(remote_forest_info,
2602 tln=local_lsa_info.dns_domain.string,
2603 collisions=remote_forest_collision)
2605 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2606 self.outf.write("Validating outgoing trust...\n")
2608 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_netlogon_info.dc_unc,
2609 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2611 remote_lsa_info.dns_domain.string)
2612 except RuntimeError as error:
2613 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2615 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2616 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2618 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2619 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2620 local_trust_verify.trusted_dc_name,
2621 local_trust_verify.tc_connection_status[1],
2622 local_trust_verify.pdc_connection_status[1])
2624 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2625 local_trust_verify.trusted_dc_name,
2626 local_trust_verify.tc_connection_status[1],
2627 local_trust_verify.pdc_connection_status[1])
2629 if local_trust_status != self.WERR_OK or local_conn_status != self.WERR_OK:
2630 raise CommandError(local_validation)
2632 self.outf.write("OK: %s\n" % local_validation)
2634 if remote_trust_info:
2635 if remote_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2636 self.outf.write("Validating incoming trust...\n")
2638 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_netlogon_info.dc_unc,
2639 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2641 local_lsa_info.dns_domain.string)
2642 except RuntimeError as error:
2643 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2645 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2646 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2648 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2649 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2650 remote_trust_verify.trusted_dc_name,
2651 remote_trust_verify.tc_connection_status[1],
2652 remote_trust_verify.pdc_connection_status[1])
2654 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2655 remote_trust_verify.trusted_dc_name,
2656 remote_trust_verify.tc_connection_status[1],
2657 remote_trust_verify.pdc_connection_status[1])
2659 if remote_trust_status != self.WERR_OK or remote_conn_status != self.WERR_OK:
2660 raise CommandError(remote_validation)
2662 self.outf.write("OK: %s\n" % remote_validation)
2664 if remote_tdo_handle is not None:
2666 remote_lsa.Close(remote_tdo_handle)
2667 except RuntimeError as error:
2669 remote_tdo_handle = None
2670 if local_tdo_handle is not None:
2672 local_lsa.Close(local_tdo_handle)
2673 except RuntimeError as error:
2675 local_tdo_handle = None
2677 self.outf.write("Success.\n")
2680 class cmd_domain_trust_delete(DomainTrustCommand):
2681 """Delete a domain trust."""
2683 synopsis = "%prog DOMAIN [options]"
2685 takes_optiongroups = {
2686 "sambaopts": options.SambaOptions,
2687 "versionopts": options.VersionOptions,
2688 "credopts": options.CredentialsOptions,
2689 "localdcopts": LocalDCCredentialsOptions,
2693 Option("--delete-location", type="choice", metavar="LOCATION",
2694 choices=["local", "both"],
2695 help="Where to delete the trusted domain object: 'local' or 'both'.",
2696 dest='delete_location',
2700 takes_args = ["domain"]
2702 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2703 delete_location=None):
2705 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2706 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2707 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2709 if delete_location == "local":
2710 remote_policy_access = None
2712 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2713 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2714 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2716 local_server = self.setup_local_server(sambaopts, localdcopts)
2718 local_lsa = self.new_local_lsa_connection()
2719 except RuntimeError as error:
2720 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2723 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2724 except RuntimeError as error:
2725 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2727 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2728 local_lsa_info.name.string,
2729 local_lsa_info.dns_domain.string,
2730 local_lsa_info.sid))
2732 local_tdo_info = None
2733 local_tdo_handle = None
2734 remote_tdo_info = None
2735 remote_tdo_handle = None
2737 lsaString = lsa.String()
2739 lsaString.string = domain
2740 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2741 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2742 except RuntimeError as error:
2743 if self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2744 raise CommandError("Failed to find trust for domain '%s'" % domain)
2745 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2748 if remote_policy_access is not None:
2750 remote_server = self.setup_remote_server(credopts, domain)
2751 except RuntimeError as error:
2752 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2755 remote_lsa = self.new_remote_lsa_connection()
2756 except RuntimeError as error:
2757 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2760 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2761 except RuntimeError as error:
2762 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2764 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2765 remote_lsa_info.name.string,
2766 remote_lsa_info.dns_domain.string,
2767 remote_lsa_info.sid))
2769 if remote_lsa_info.sid != local_tdo_info.sid or \
2770 remote_lsa_info.name.string != local_tdo_info.netbios_name.string or \
2771 remote_lsa_info.dns_domain.string != local_tdo_info.domain_name.string:
2772 raise CommandError("LocalTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2773 local_tdo_info.netbios_name.string,
2774 local_tdo_info.domain_name.string,
2775 local_tdo_info.sid))
2778 lsaString.string = local_lsa_info.dns_domain.string
2779 remote_tdo_info = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2780 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2781 except RuntimeError as error:
2782 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2783 raise self.RemoteRuntimeError(self, error, "QueryTrustedDomainInfoByName(%s)" % (
2787 if remote_tdo_info is not None:
2788 if local_lsa_info.sid != remote_tdo_info.sid or \
2789 local_lsa_info.name.string != remote_tdo_info.netbios_name.string or \
2790 local_lsa_info.dns_domain.string != remote_tdo_info.domain_name.string:
2791 raise CommandError("RemoteTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2792 remote_tdo_info.netbios_name.string,
2793 remote_tdo_info.domain_name.string,
2794 remote_tdo_info.sid))
2796 if local_tdo_info is not None:
2798 lsaString.string = local_tdo_info.domain_name.string
2799 local_tdo_handle = local_lsa.OpenTrustedDomainByName(local_policy,
2801 security.SEC_STD_DELETE)
2802 except RuntimeError as error:
2803 raise self.LocalRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2806 local_lsa.DeleteObject(local_tdo_handle)
2807 local_tdo_handle = None
2809 if remote_tdo_info is not None:
2811 lsaString.string = remote_tdo_info.domain_name.string
2812 remote_tdo_handle = remote_lsa.OpenTrustedDomainByName(remote_policy,
2814 security.SEC_STD_DELETE)
2815 except RuntimeError as error:
2816 raise self.RemoteRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2819 if remote_tdo_handle is not None:
2821 remote_lsa.DeleteObject(remote_tdo_handle)
2822 remote_tdo_handle = None
2823 self.outf.write("RemoteTDO deleted.\n")
2824 except RuntimeError as error:
2825 self.outf.write("%s\n" % self.RemoteRuntimeError(self, error, "DeleteObject() failed"))
2827 if local_tdo_handle is not None:
2829 local_lsa.DeleteObject(local_tdo_handle)
2830 local_tdo_handle = None
2831 self.outf.write("LocalTDO deleted.\n")
2832 except RuntimeError as error:
2833 self.outf.write("%s\n" % self.LocalRuntimeError(self, error, "DeleteObject() failed"))
2837 class cmd_domain_trust_validate(DomainTrustCommand):
2838 """Validate a domain trust."""
2840 synopsis = "%prog DOMAIN [options]"
2842 takes_optiongroups = {
2843 "sambaopts": options.SambaOptions,
2844 "versionopts": options.VersionOptions,
2845 "credopts": options.CredentialsOptions,
2846 "localdcopts": LocalDCCredentialsOptions,
2850 Option("--validate-location", type="choice", metavar="LOCATION",
2851 choices=["local", "both"],
2852 help="Where to validate the trusted domain object: 'local' or 'both'.",
2853 dest='validate_location',
2857 takes_args = ["domain"]
2859 def run(self, domain, sambaopts=None, versionopts=None, credopts=None, localdcopts=None,
2860 validate_location=None):
2862 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2864 local_server = self.setup_local_server(sambaopts, localdcopts)
2866 local_lsa = self.new_local_lsa_connection()
2867 except RuntimeError as error:
2868 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2871 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2872 except RuntimeError as error:
2873 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2875 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2876 local_lsa_info.name.string,
2877 local_lsa_info.dns_domain.string,
2878 local_lsa_info.sid))
2881 lsaString = lsa.String()
2882 lsaString.string = domain
2883 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2884 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2885 except RuntimeError as error:
2886 if self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2887 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2889 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
2891 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
2892 local_tdo_info.netbios_name.string,
2893 local_tdo_info.domain_name.string,
2894 local_tdo_info.sid))
2897 local_netlogon = self.new_local_netlogon_connection()
2898 except RuntimeError as error:
2899 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2902 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_server,
2903 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2905 local_tdo_info.domain_name.string)
2906 except RuntimeError as error:
2907 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2909 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2910 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2912 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2913 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2914 local_trust_verify.trusted_dc_name,
2915 local_trust_verify.tc_connection_status[1],
2916 local_trust_verify.pdc_connection_status[1])
2918 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2919 local_trust_verify.trusted_dc_name,
2920 local_trust_verify.tc_connection_status[1],
2921 local_trust_verify.pdc_connection_status[1])
2923 if local_trust_status != self.WERR_OK or local_conn_status != self.WERR_OK:
2924 raise CommandError(local_validation)
2926 self.outf.write("OK: %s\n" % local_validation)
2929 server = local_trust_verify.trusted_dc_name.replace('\\', '')
2930 domain_and_server = "%s\\%s" % (local_tdo_info.domain_name.string, server)
2931 local_trust_rediscover = local_netlogon.netr_LogonControl2Ex(local_server,
2932 netlogon.NETLOGON_CONTROL_REDISCOVER,
2935 except RuntimeError as error:
2936 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
2938 local_conn_status = self._uint32(local_trust_rediscover.tc_connection_status[0])
2939 local_rediscover = "LocalRediscover: DC[%s] CONNECTION[%s]" % (
2940 local_trust_rediscover.trusted_dc_name,
2941 local_trust_rediscover.tc_connection_status[1])
2943 if local_conn_status != self.WERR_OK:
2944 raise CommandError(local_rediscover)
2946 self.outf.write("OK: %s\n" % local_rediscover)
2948 if validate_location != "local":
2950 remote_server = self.setup_remote_server(credopts, domain, require_pdc=False)
2951 except RuntimeError as error:
2952 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2955 remote_netlogon = self.new_remote_netlogon_connection()
2956 except RuntimeError as error:
2957 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2960 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_server,
2961 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2963 local_lsa_info.dns_domain.string)
2964 except RuntimeError as error:
2965 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2967 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2968 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2970 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2971 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2972 remote_trust_verify.trusted_dc_name,
2973 remote_trust_verify.tc_connection_status[1],
2974 remote_trust_verify.pdc_connection_status[1])
2976 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2977 remote_trust_verify.trusted_dc_name,
2978 remote_trust_verify.tc_connection_status[1],
2979 remote_trust_verify.pdc_connection_status[1])
2981 if remote_trust_status != self.WERR_OK or remote_conn_status != self.WERR_OK:
2982 raise CommandError(remote_validation)
2984 self.outf.write("OK: %s\n" % remote_validation)
2987 server = remote_trust_verify.trusted_dc_name.replace('\\', '')
2988 domain_and_server = "%s\\%s" % (local_lsa_info.dns_domain.string, server)
2989 remote_trust_rediscover = remote_netlogon.netr_LogonControl2Ex(remote_server,
2990 netlogon.NETLOGON_CONTROL_REDISCOVER,
2993 except RuntimeError as error:
2994 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
2996 remote_conn_status = self._uint32(remote_trust_rediscover.tc_connection_status[0])
2998 remote_rediscover = "RemoteRediscover: DC[%s] CONNECTION[%s]" % (
2999 remote_trust_rediscover.trusted_dc_name,
3000 remote_trust_rediscover.tc_connection_status[1])
3002 if remote_conn_status != self.WERR_OK:
3003 raise CommandError(remote_rediscover)
3005 self.outf.write("OK: %s\n" % remote_rediscover)
3009 class cmd_domain_trust_namespaces(DomainTrustCommand):
3010 """Manage forest trust namespaces."""
3012 synopsis = "%prog [DOMAIN] [options]"
3014 takes_optiongroups = {
3015 "sambaopts": options.SambaOptions,
3016 "versionopts": options.VersionOptions,
3017 "localdcopts": LocalDCCredentialsOptions,
3021 Option("--refresh", type="choice", metavar="check|store",
3022 choices=["check", "store", None],
3023 help="List and maybe store refreshed forest trust information: 'check' or 'store'.",
3026 Option("--enable-all", action="store_true",
3027 help="Try to update disabled entries, not allowed with --refresh=check.",
3030 Option("--enable-tln", action="append", metavar='DNSDOMAIN',
3031 help="Enable a top level name entry. Can be specified multiple times.",
3034 Option("--disable-tln", action="append", metavar='DNSDOMAIN',
3035 help="Disable a top level name entry. Can be specified multiple times.",
3038 Option("--add-tln-ex", action="append", metavar='DNSDOMAIN',
3039 help="Add a top level exclusion entry. Can be specified multiple times.",
3042 Option("--delete-tln-ex", action="append", metavar='DNSDOMAIN',
3043 help="Delete a top level exclusion entry. Can be specified multiple times.",
3044 dest='delete_tln_ex',
3046 Option("--enable-nb", action="append", metavar='NETBIOSDOMAIN',
3047 help="Enable a netbios name in a domain entry. Can be specified multiple times.",
3050 Option("--disable-nb", action="append", metavar='NETBIOSDOMAIN',
3051 help="Disable a netbios name in a domain entry. Can be specified multiple times.",
3054 Option("--enable-sid", action="append", metavar='DOMAINSID',
3055 help="Enable a SID in a domain entry. Can be specified multiple times.",
3056 dest='enable_sid_str',
3058 Option("--disable-sid", action="append", metavar='DOMAINSID',
3059 help="Disable a SID in a domain entry. Can be specified multiple times.",
3060 dest='disable_sid_str',
3062 Option("--add-upn-suffix", action="append", metavar='DNSDOMAIN',
3063 help="Add a new uPNSuffixes attribute for the local forest. Can be specified multiple times.",
3066 Option("--delete-upn-suffix", action="append", metavar='DNSDOMAIN',
3067 help="Delete an existing uPNSuffixes attribute of the local forest. Can be specified multiple times.",
3070 Option("--add-spn-suffix", action="append", metavar='DNSDOMAIN',
3071 help="Add a new msDS-SPNSuffixes attribute for the local forest. Can be specified multiple times.",
3074 Option("--delete-spn-suffix", action="append", metavar='DNSDOMAIN',
3075 help="Delete an existing msDS-SPNSuffixes attribute of the local forest. Can be specified multiple times.",
3080 takes_args = ["domain?"]
3082 def run(self, domain=None, sambaopts=None, localdcopts=None, versionopts=None,
3083 refresh=None, enable_all=False,
3084 enable_tln=[], disable_tln=[], add_tln_ex=[], delete_tln_ex=[],
3085 enable_sid_str=[], disable_sid_str=[], enable_nb=[], disable_nb=[],
3086 add_upn=[], delete_upn=[], add_spn=[], delete_spn=[]):
3088 require_update = False
3091 if refresh == "store":
3092 raise CommandError("--refresh=%s not allowed without DOMAIN" % refresh)
3095 raise CommandError("--enable-all not allowed without DOMAIN")
3097 if len(enable_tln) > 0:
3098 raise CommandError("--enable-tln not allowed without DOMAIN")
3099 if len(disable_tln) > 0:
3100 raise CommandError("--disable-tln not allowed without DOMAIN")
3102 if len(add_tln_ex) > 0:
3103 raise CommandError("--add-tln-ex not allowed without DOMAIN")
3104 if len(delete_tln_ex) > 0:
3105 raise CommandError("--delete-tln-ex not allowed without DOMAIN")
3107 if len(enable_nb) > 0:
3108 raise CommandError("--enable-nb not allowed without DOMAIN")
3109 if len(disable_nb) > 0:
3110 raise CommandError("--disable-nb not allowed without DOMAIN")
3112 if len(enable_sid_str) > 0:
3113 raise CommandError("--enable-sid not allowed without DOMAIN")
3114 if len(disable_sid_str) > 0:
3115 raise CommandError("--disable-sid not allowed without DOMAIN")
3117 if len(add_upn) > 0:
3119 if not n.startswith("*."):
3121 raise CommandError("value[%s] specified for --add-upn-suffix should not include with '*.'" % n)
3122 require_update = True
3123 if len(delete_upn) > 0:
3124 for n in delete_upn:
3125 if not n.startswith("*."):
3127 raise CommandError("value[%s] specified for --delete-upn-suffix should not include with '*.'" % n)
3128 require_update = True
3130 for d in delete_upn:
3131 if a.lower() != d.lower():
3133 raise CommandError("value[%s] specified for --add-upn-suffix and --delete-upn-suffix" % a)
3135 if len(add_spn) > 0:
3137 if not n.startswith("*."):
3139 raise CommandError("value[%s] specified for --add-spn-suffix should not include with '*.'" % n)
3140 require_update = True
3141 if len(delete_spn) > 0:
3142 for n in delete_spn:
3143 if not n.startswith("*."):
3145 raise CommandError("value[%s] specified for --delete-spn-suffix should not include with '*.'" % n)
3146 require_update = True
3148 for d in delete_spn:
3149 if a.lower() != d.lower():
3151 raise CommandError("value[%s] specified for --add-spn-suffix and --delete-spn-suffix" % a)
3153 if len(add_upn) > 0:
3154 raise CommandError("--add-upn-suffix not allowed together with DOMAIN")
3155 if len(delete_upn) > 0:
3156 raise CommandError("--delete-upn-suffix not allowed together with DOMAIN")
3157 if len(add_spn) > 0:
3158 raise CommandError("--add-spn-suffix not allowed together with DOMAIN")
3159 if len(delete_spn) > 0:
3160 raise CommandError("--delete-spn-suffix not allowed together with DOMAIN")
3162 if refresh is not None:
3163 if refresh == "store":
3164 require_update = True
3166 if enable_all and refresh != "store":
3167 raise CommandError("--enable-all not allowed together with --refresh=%s" % refresh)
3169 if len(enable_tln) > 0:
3170 raise CommandError("--enable-tln not allowed together with --refresh")
3171 if len(disable_tln) > 0:
3172 raise CommandError("--disable-tln not allowed together with --refresh")
3174 if len(add_tln_ex) > 0:
3175 raise CommandError("--add-tln-ex not allowed together with --refresh")
3176 if len(delete_tln_ex) > 0:
3177 raise CommandError("--delete-tln-ex not allowed together with --refresh")
3179 if len(enable_nb) > 0:
3180 raise CommandError("--enable-nb not allowed together with --refresh")
3181 if len(disable_nb) > 0:
3182 raise CommandError("--disable-nb not allowed together with --refresh")
3184 if len(enable_sid_str) > 0:
3185 raise CommandError("--enable-sid not allowed together with --refresh")
3186 if len(disable_sid_str) > 0:
3187 raise CommandError("--disable-sid not allowed together with --refresh")
3190 require_update = True
3192 if len(enable_tln) > 0:
3193 raise CommandError("--enable-tln not allowed together with --enable-all")
3195 if len(enable_nb) > 0:
3196 raise CommandError("--enable-nb not allowed together with --enable-all")
3198 if len(enable_sid) > 0:
3199 raise CommandError("--enable-sid not allowed together with --enable-all")
3201 if len(enable_tln) > 0:
3202 require_update = True
3203 if len(disable_tln) > 0:
3204 require_update = True
3205 for e in enable_tln:
3206 for d in disable_tln:
3207 if e.lower() != d.lower():
3209 raise CommandError("value[%s] specified for --enable-tln and --disable-tln" % e)
3211 if len(add_tln_ex) > 0:
3212 for n in add_tln_ex:
3213 if not n.startswith("*."):
3215 raise CommandError("value[%s] specified for --add-tln-ex should not include with '*.'" % n)
3216 require_update = True
3217 if len(delete_tln_ex) > 0:
3218 for n in delete_tln_ex:
3219 if not n.startswith("*."):
3221 raise CommandError("value[%s] specified for --delete-tln-ex should not include with '*.'" % n)
3222 require_update = True
3223 for a in add_tln_ex:
3224 for d in delete_tln_ex:
3225 if a.lower() != d.lower():
3227 raise CommandError("value[%s] specified for --add-tln-ex and --delete-tln-ex" % a)
3229 if len(enable_nb) > 0:
3230 require_update = True
3231 if len(disable_nb) > 0:
3232 require_update = True
3234 for d in disable_nb:
3235 if e.upper() != d.upper():
3237 raise CommandError("value[%s] specified for --enable-nb and --disable-nb" % e)
3240 for s in enable_sid_str:
3242 sid = security.dom_sid(s)
3243 except TypeError as error:
3244 raise CommandError("value[%s] specified for --enable-sid is not a valid SID" % s)
3245 enable_sid.append(sid)
3247 for s in disable_sid_str:
3249 sid = security.dom_sid(s)
3250 except TypeError as error:
3251 raise CommandError("value[%s] specified for --disable-sid is not a valid SID" % s)
3252 disable_sid.append(sid)
3253 if len(enable_sid) > 0:
3254 require_update = True
3255 if len(disable_sid) > 0:
3256 require_update = True
3257 for e in enable_sid:
3258 for d in disable_sid:
3261 raise CommandError("value[%s] specified for --enable-sid and --disable-sid" % e)
3263 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
3265 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
3267 local_server = self.setup_local_server(sambaopts, localdcopts)
3269 local_lsa = self.new_local_lsa_connection()
3270 except RuntimeError as error:
3271 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
3274 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
3275 except RuntimeError as error:
3276 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
3278 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
3279 local_lsa_info.name.string,
3280 local_lsa_info.dns_domain.string,
3281 local_lsa_info.sid))
3285 local_netlogon = self.new_local_netlogon_connection()
3286 except RuntimeError as error:
3287 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3290 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3291 except RuntimeError as error:
3292 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3294 if local_netlogon_info.domain_name != local_netlogon_info.forest_name:
3295 raise CommandError("The local domain [%s] is not the forest root [%s]" % (
3296 local_netlogon_info.domain_name,
3297 local_netlogon_info.forest_name))
3300 # get all information about our own forest
3301 own_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3303 except RuntimeError as error:
3304 if self.check_runtime_error(error, self.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
3305 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3308 if self.check_runtime_error(error, self.WERR_INVALID_FUNCTION):
3309 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3312 if self.check_runtime_error(error, self.WERR_NERR_ACFNOTLOADED):
3313 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3316 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3318 self.outf.write("Own forest trust information...\n")
3319 self.write_forest_trust_info(own_forest_info,
3320 tln=local_lsa_info.dns_domain.string)
3323 local_samdb = self.new_local_ldap_connection()
3324 except RuntimeError as error:
3325 raise self.LocalRuntimeError(self, error, "failed to connect to SamDB")
3327 local_partitions_dn = "CN=Partitions,%s" % str(local_samdb.get_config_basedn())
3328 attrs = ['uPNSuffixes', 'msDS-SPNSuffixes']
3330 msgs = local_samdb.search(base=local_partitions_dn,
3331 scope=ldb.SCOPE_BASE,
3332 expression="(objectClass=crossRefContainer)",
3334 stored_msg = msgs[0]
3335 except ldb.LdbError as error:
3336 raise self.LocalLdbError(self, error, "failed to search partition dn")
3338 stored_upn_vals = []
3339 if 'uPNSuffixes' in stored_msg:
3340 stored_upn_vals.extend(stored_msg['uPNSuffixes'])
3342 stored_spn_vals = []
3343 if 'msDS-SPNSuffixes' in stored_msg:
3344 stored_spn_vals.extend(stored_msg['msDS-SPNSuffixes'])
3346 self.outf.write("Stored uPNSuffixes attributes[%d]:\n" % len(stored_upn_vals))
3347 for v in stored_upn_vals:
3348 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3349 self.outf.write("Stored msDS-SPNSuffixes attributes[%d]:\n" % len(stored_spn_vals))
3350 for v in stored_spn_vals:
3351 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3353 if not require_update:
3357 update_upn_vals = []
3358 update_upn_vals.extend(stored_upn_vals)
3361 update_spn_vals = []
3362 update_spn_vals.extend(stored_spn_vals)
3366 for i in xrange(0, len(update_upn_vals)):
3367 v = update_upn_vals[i]
3368 if v.lower() != upn.lower():
3373 raise CommandError("Entry already present for value[%s] specified for --add-upn-suffix" % upn)
3374 update_upn_vals.append(upn)
3377 for upn in delete_upn:
3379 for i in xrange(0, len(update_upn_vals)):
3380 v = update_upn_vals[i]
3381 if v.lower() != upn.lower():
3386 raise CommandError("Entry not found for value[%s] specified for --delete-upn-suffix" % upn)
3388 update_upn_vals.pop(idx)
3393 for i in xrange(0, len(update_spn_vals)):
3394 v = update_spn_vals[i]
3395 if v.lower() != spn.lower():
3400 raise CommandError("Entry already present for value[%s] specified for --add-spn-suffix" % spn)
3401 update_spn_vals.append(spn)
3404 for spn in delete_spn:
3406 for i in xrange(0, len(update_spn_vals)):
3407 v = update_spn_vals[i]
3408 if v.lower() != spn.lower():
3413 raise CommandError("Entry not found for value[%s] specified for --delete-spn-suffix" % spn)
3415 update_spn_vals.pop(idx)
3418 self.outf.write("Update uPNSuffixes attributes[%d]:\n" % len(update_upn_vals))
3419 for v in update_upn_vals:
3420 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3421 self.outf.write("Update msDS-SPNSuffixes attributes[%d]:\n" % len(update_spn_vals))
3422 for v in update_spn_vals:
3423 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3425 update_msg = ldb.Message()
3426 update_msg.dn = stored_msg.dn
3429 update_msg['uPNSuffixes'] = ldb.MessageElement(update_upn_vals,
3430 ldb.FLAG_MOD_REPLACE,
3433 update_msg['msDS-SPNSuffixes'] = ldb.MessageElement(update_spn_vals,
3434 ldb.FLAG_MOD_REPLACE,
3437 local_samdb.modify(update_msg)
3438 except ldb.LdbError as error:
3439 raise self.LocalLdbError(self, error, "failed to update partition dn")
3442 stored_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3444 except RuntimeError as error:
3445 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3447 self.outf.write("Stored forest trust information...\n")
3448 self.write_forest_trust_info(stored_forest_info,
3449 tln=local_lsa_info.dns_domain.string)
3453 lsaString = lsa.String()
3454 lsaString.string = domain
3455 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
3456 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
3457 except RuntimeError as error:
3458 if self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
3459 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
3461 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
3463 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
3464 local_tdo_info.netbios_name.string,
3465 local_tdo_info.domain_name.string,
3466 local_tdo_info.sid))
3468 if not local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
3469 raise CommandError("trusted domain object for domain [%s] is not marked as FOREST_TRANSITIVE." % domain)
3471 if refresh is not None:
3473 local_netlogon = self.new_local_netlogon_connection()
3474 except RuntimeError as error:
3475 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3478 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3479 except RuntimeError as error:
3480 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3482 lsa_update_check = 1
3483 if refresh == "store":
3484 netlogon_update_tdo = netlogon.DS_GFTI_UPDATE_TDO
3486 lsa_update_check = 0
3488 netlogon_update_tdo = 0
3491 # get all information about the remote trust
3492 # this triggers netr_GetForestTrustInformation to the remote domain
3493 # and lsaRSetForestTrustInformation() locally, but new top level
3494 # names are disabled by default.
3495 fresh_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3496 local_tdo_info.domain_name.string,
3497 netlogon_update_tdo)
3498 except RuntimeError as error:
3499 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3502 fresh_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3503 local_tdo_info.domain_name,
3504 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3507 except RuntimeError as error:
3508 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3510 self.outf.write("Fresh forest trust information...\n")
3511 self.write_forest_trust_info(fresh_forest_info,
3512 tln=local_tdo_info.domain_name.string,
3513 collisions=fresh_forest_collision)
3515 if refresh == "store":
3517 lsaString = lsa.String()
3518 lsaString.string = local_tdo_info.domain_name.string
3519 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3521 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3522 except RuntimeError as error:
3523 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3525 self.outf.write("Stored forest trust information...\n")
3526 self.write_forest_trust_info(stored_forest_info,
3527 tln=local_tdo_info.domain_name.string)
3532 # The none --refresh path
3536 lsaString = lsa.String()
3537 lsaString.string = local_tdo_info.domain_name.string
3538 local_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3540 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3541 except RuntimeError as error:
3542 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3544 self.outf.write("Local forest trust information...\n")
3545 self.write_forest_trust_info(local_forest_info,
3546 tln=local_tdo_info.domain_name.string)
3548 if not require_update:
3552 entries.extend(local_forest_info.entries)
3553 update_forest_info = lsa.ForestTrustInformation()
3554 update_forest_info.count = len(entries)
3555 update_forest_info.entries = entries
3558 for i in xrange(0, len(update_forest_info.entries)):
3559 r = update_forest_info.entries[i]
3560 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3562 if update_forest_info.entries[i].flags == 0:
3564 update_forest_info.entries[i].time = 0
3565 update_forest_info.entries[i].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3566 for i in xrange(0, len(update_forest_info.entries)):
3567 r = update_forest_info.entries[i]
3568 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3570 if update_forest_info.entries[i].flags == 0:
3572 update_forest_info.entries[i].time = 0
3573 update_forest_info.entries[i].flags &= ~lsa.LSA_NB_DISABLED_MASK
3574 update_forest_info.entries[i].flags &= ~lsa.LSA_SID_DISABLED_MASK
3576 for tln in enable_tln:
3578 for i in xrange(0, len(update_forest_info.entries)):
3579 r = update_forest_info.entries[i]
3580 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3582 if r.forest_trust_data.string.lower() != tln.lower():
3587 raise CommandError("Entry not found for value[%s] specified for --enable-tln" % tln)
3588 if not update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_MASK:
3589 raise CommandError("Entry found for value[%s] specified for --enable-tln is already enabled" % tln)
3590 update_forest_info.entries[idx].time = 0
3591 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3593 for tln in disable_tln:
3595 for i in xrange(0, len(update_forest_info.entries)):
3596 r = update_forest_info.entries[i]
3597 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3599 if r.forest_trust_data.string.lower() != tln.lower():
3604 raise CommandError("Entry not found for value[%s] specified for --disable-tln" % tln)
3605 if update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_ADMIN:
3606 raise CommandError("Entry found for value[%s] specified for --disable-tln is already disabled" % tln)
3607 update_forest_info.entries[idx].time = 0
3608 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3609 update_forest_info.entries[idx].flags |= lsa.LSA_TLN_DISABLED_ADMIN
3611 for tln_ex in add_tln_ex:
3613 for i in xrange(0, len(update_forest_info.entries)):
3614 r = update_forest_info.entries[i]
3615 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3617 if r.forest_trust_data.string.lower() != tln_ex.lower():
3622 raise CommandError("Entry already present for value[%s] specified for --add-tln-ex" % tln_ex)
3624 tln_dot = ".%s" % tln_ex.lower()
3626 for i in xrange(0, len(update_forest_info.entries)):
3627 r = update_forest_info.entries[i]
3628 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3630 r_dot = ".%s" % r.forest_trust_data.string.lower()
3631 if tln_dot == r_dot:
3632 raise CommandError("TLN entry present for value[%s] specified for --add-tln-ex" % tln_ex)
3633 if not tln_dot.endswith(r_dot):
3639 raise CommandError("No TLN parent present for value[%s] specified for --add-tln-ex" % tln_ex)
3641 r = lsa.ForestTrustRecord()
3642 r.type = lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX
3645 r.forest_trust_data.string = tln_ex
3648 entries.extend(update_forest_info.entries)
3649 entries.insert(idx + 1, r)
3650 update_forest_info.count = len(entries)
3651 update_forest_info.entries = entries
3653 for tln_ex in delete_tln_ex:
3655 for i in xrange(0, len(update_forest_info.entries)):
3656 r = update_forest_info.entries[i]
3657 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3659 if r.forest_trust_data.string.lower() != tln_ex.lower():
3664 raise CommandError("Entry not found for value[%s] specified for --delete-tln-ex" % tln_ex)
3667 entries.extend(update_forest_info.entries)
3669 update_forest_info.count = len(entries)
3670 update_forest_info.entries = entries
3672 for nb in enable_nb:
3674 for i in xrange(0, len(update_forest_info.entries)):
3675 r = update_forest_info.entries[i]
3676 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3678 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3683 raise CommandError("Entry not found for value[%s] specified for --enable-nb" % nb)
3684 if not update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_MASK:
3685 raise CommandError("Entry found for value[%s] specified for --enable-nb is already enabled" % nb)
3686 update_forest_info.entries[idx].time = 0
3687 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3689 for nb in disable_nb:
3691 for i in xrange(0, len(update_forest_info.entries)):
3692 r = update_forest_info.entries[i]
3693 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3695 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3700 raise CommandError("Entry not found for value[%s] specified for --delete-nb" % nb)
3701 if update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_ADMIN:
3702 raise CommandError("Entry found for value[%s] specified for --disable-nb is already disabled" % nb)
3703 update_forest_info.entries[idx].time = 0
3704 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3705 update_forest_info.entries[idx].flags |= lsa.LSA_NB_DISABLED_ADMIN
3707 for sid in enable_sid:
3709 for i in xrange(0, len(update_forest_info.entries)):
3710 r = update_forest_info.entries[i]
3711 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3713 if r.forest_trust_data.domain_sid != sid:
3718 raise CommandError("Entry not found for value[%s] specified for --enable-sid" % sid)
3719 if not update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_MASK:
3720 raise CommandError("Entry found for value[%s] specified for --enable-sid is already enabled" % nb)
3721 update_forest_info.entries[idx].time = 0
3722 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3724 for sid in disable_sid:
3726 for i in xrange(0, len(update_forest_info.entries)):
3727 r = update_forest_info.entries[i]
3728 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3730 if r.forest_trust_data.domain_sid != sid:
3735 raise CommandError("Entry not found for value[%s] specified for --delete-sid" % sid)
3736 if update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_ADMIN:
3737 raise CommandError("Entry found for value[%s] specified for --disable-sid is already disabled" % nb)
3738 update_forest_info.entries[idx].time = 0
3739 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3740 update_forest_info.entries[idx].flags |= lsa.LSA_SID_DISABLED_ADMIN
3743 update_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3744 local_tdo_info.domain_name,
3745 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3746 update_forest_info, 0)
3747 except RuntimeError as error:
3748 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3750 self.outf.write("Updated forest trust information...\n")
3751 self.write_forest_trust_info(update_forest_info,
3752 tln=local_tdo_info.domain_name.string,
3753 collisions=update_forest_collision)
3756 lsaString = lsa.String()
3757 lsaString.string = local_tdo_info.domain_name.string
3758 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3760 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3761 except RuntimeError as error:
3762 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3764 self.outf.write("Stored forest trust information...\n")
3765 self.write_forest_trust_info(stored_forest_info,
3766 tln=local_tdo_info.domain_name.string)
3769 class cmd_domain_tombstones_expunge(Command):
3770 """Expunge tombstones from the database.
3772 This command expunges tombstones from the database."""
3773 synopsis = "%prog NC [NC [...]] [options]"
3776 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
3777 metavar="URL", dest="H"),
3778 Option("--current-time",
3779 help="The current time to evaluate the tombstone lifetime from, expressed as YYYY-MM-DD",
3781 Option("--tombstone-lifetime", help="Number of days a tombstone should be preserved for", type=int),
3784 takes_args = ["nc*"]
3786 takes_optiongroups = {
3787 "sambaopts": options.SambaOptions,
3788 "credopts": options.CredentialsOptions,
3789 "versionopts": options.VersionOptions,
3792 def run(self, *ncs, **kwargs):
3793 sambaopts = kwargs.get("sambaopts")
3794 credopts = kwargs.get("credopts")
3795 versionpts = kwargs.get("versionopts")
3797 current_time_string = kwargs.get("current_time")
3798 tombstone_lifetime = kwargs.get("tombstone_lifetime")
3799 lp = sambaopts.get_loadparm()
3800 creds = credopts.get_credentials(lp)
3801 samdb = SamDB(url=H, session_info=system_session(),
3802 credentials=creds, lp=lp)
3804 if current_time_string is not None:
3805 current_time_obj = time.strptime(current_time_string, "%Y-%m-%d")
3806 current_time = long(time.mktime(current_time_obj))
3809 current_time = long(time.time())
3812 res = samdb.search(expression="", base="", scope=ldb.SCOPE_BASE,
3813 attrs=["namingContexts"])
3816 for nc in res[0]["namingContexts"]:
3821 started_transaction = False
3823 samdb.transaction_start()
3824 started_transaction = True
3826 removed_links) = samdb.garbage_collect_tombstones(ncs,
3827 current_time=current_time,
3828 tombstone_lifetime=tombstone_lifetime)
3830 except Exception, err:
3831 if started_transaction:
3832 samdb.transaction_cancel()
3833 raise CommandError("Failed to expunge / garbage collect tombstones", err)
3835 samdb.transaction_commit()
3837 self.outf.write("Removed %d objects and %d links successfully\n"
3838 % (removed_objects, removed_links))
3842 class cmd_domain_trust(SuperCommand):
3843 """Domain and forest trust management."""
3846 subcommands["list"] = cmd_domain_trust_list()
3847 subcommands["show"] = cmd_domain_trust_show()
3848 subcommands["create"] = cmd_domain_trust_create()
3849 subcommands["delete"] = cmd_domain_trust_delete()
3850 subcommands["validate"] = cmd_domain_trust_validate()
3851 subcommands["namespaces"] = cmd_domain_trust_namespaces()
3853 class cmd_domain_tombstones(SuperCommand):
3854 """Domain tombstone and recycled object management."""
3857 subcommands["expunge"] = cmd_domain_tombstones_expunge()
3859 class cmd_domain(SuperCommand):
3860 """Domain management."""
3863 subcommands["demote"] = cmd_domain_demote()
3864 if cmd_domain_export_keytab is not None:
3865 subcommands["exportkeytab"] = cmd_domain_export_keytab()
3866 subcommands["info"] = cmd_domain_info()
3867 subcommands["provision"] = cmd_domain_provision()
3868 subcommands["join"] = cmd_domain_join()
3869 subcommands["dcpromo"] = cmd_domain_dcpromo()
3870 subcommands["level"] = cmd_domain_level()
3871 subcommands["passwordsettings"] = cmd_domain_passwordsettings()
3872 subcommands["classicupgrade"] = cmd_domain_classicupgrade()
3873 subcommands["samba3upgrade"] = cmd_domain_samba3upgrade()
3874 subcommands["trust"] = cmd_domain_trust()
3875 subcommands["tombstones"] = cmd_domain_tombstones()