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
1617 WERR_RPC_S_PROCNUM_OUT_OF_RANGE = 0x000006D1
1619 NT_STATUS_NOT_FOUND = 0xC0000225
1620 NT_STATUS_OBJECT_NAME_NOT_FOUND = 0xC0000034
1621 NT_STATUS_INVALID_PARAMETER = 0xC000000D
1622 NT_STATUS_INVALID_INFO_CLASS = 0xC0000003
1623 NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE = 0xC002002E
1625 def _uint32(self, v):
1626 return ctypes.c_uint32(v).value
1628 def check_runtime_error(self, runtime, val):
1632 err32 = self._uint32(runtime[0])
1638 class LocalRuntimeError(CommandError):
1639 def __init__(exception_self, self, runtime, message):
1640 err32 = self._uint32(runtime[0])
1642 msg = "LOCAL_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1643 self.local_server, message, err32, errstr)
1644 CommandError.__init__(exception_self, msg)
1646 class RemoteRuntimeError(CommandError):
1647 def __init__(exception_self, self, runtime, message):
1648 err32 = self._uint32(runtime[0])
1650 msg = "REMOTE_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1651 self.remote_server, message, err32, errstr)
1652 CommandError.__init__(exception_self, msg)
1654 class LocalLdbError(CommandError):
1655 def __init__(exception_self, self, ldb_error, message):
1656 errval = ldb_error[0]
1657 errstr = ldb_error[1]
1658 msg = "LOCAL_DC[%s]: %s - ERROR(%d) - %s" % (
1659 self.local_server, message, errval, errstr)
1660 CommandError.__init__(exception_self, msg)
1662 def setup_local_server(self, sambaopts, localdcopts):
1663 if self.local_server is not None:
1664 return self.local_server
1666 lp = sambaopts.get_loadparm()
1668 local_server = localdcopts.ipaddress
1669 if local_server is None:
1670 server_role = lp.server_role()
1671 if server_role != "ROLE_ACTIVE_DIRECTORY_DC":
1672 raise CommandError("Invalid server_role %s" % (server_role))
1673 local_server = lp.get('netbios name')
1674 local_transport = "ncalrpc"
1675 local_binding_options = ""
1676 local_binding_options += ",auth_type=ncalrpc_as_system"
1677 local_ldap_url = None
1680 local_transport = "ncacn_np"
1681 local_binding_options = ""
1682 local_ldap_url = "ldap://%s" % local_server
1683 local_creds = localdcopts.get_credentials(lp)
1687 self.local_server = local_server
1688 self.local_binding_string = "%s:%s[%s]" % (local_transport, local_server, local_binding_options)
1689 self.local_ldap_url = local_ldap_url
1690 self.local_creds = local_creds
1691 return self.local_server
1693 def new_local_lsa_connection(self):
1694 return lsa.lsarpc(self.local_binding_string, self.local_lp, self.local_creds)
1696 def new_local_netlogon_connection(self):
1697 return netlogon.netlogon(self.local_binding_string, self.local_lp, self.local_creds)
1699 def new_local_ldap_connection(self):
1700 return SamDB(url=self.local_ldap_url,
1701 session_info=system_session(),
1702 credentials=self.local_creds,
1705 def setup_remote_server(self, credopts, domain,
1707 require_writable=True):
1710 assert require_writable
1712 if self.remote_server is not None:
1713 return self.remote_server
1715 self.remote_server = "__unknown__remote_server__.%s" % domain
1716 assert self.local_server is not None
1718 remote_creds = credopts.get_credentials(self.local_lp)
1719 remote_server = credopts.ipaddress
1720 remote_binding_options = ""
1722 # TODO: we should also support NT4 domains
1723 # we could use local_netlogon.netr_DsRGetDCNameEx2() with the remote domain name
1724 # and delegate NBT or CLDAP to the local netlogon server
1726 remote_net = Net(remote_creds, self.local_lp, server=remote_server)
1727 remote_flags = nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS
1728 if require_writable:
1729 remote_flags |= nbt.NBT_SERVER_WRITABLE
1731 remote_flags |= nbt.NBT_SERVER_PDC
1732 remote_info = remote_net.finddc(flags=remote_flags, domain=domain, address=remote_server)
1734 raise CommandError("Failed to find a writeable DC for domain '%s'" % domain)
1736 nbt.NBT_SERVER_PDC: "PDC",
1737 nbt.NBT_SERVER_GC: "GC",
1738 nbt.NBT_SERVER_LDAP: "LDAP",
1739 nbt.NBT_SERVER_DS: "DS",
1740 nbt.NBT_SERVER_KDC: "KDC",
1741 nbt.NBT_SERVER_TIMESERV: "TIMESERV",
1742 nbt.NBT_SERVER_CLOSEST: "CLOSEST",
1743 nbt.NBT_SERVER_WRITABLE: "WRITABLE",
1744 nbt.NBT_SERVER_GOOD_TIMESERV: "GOOD_TIMESERV",
1745 nbt.NBT_SERVER_NDNC: "NDNC",
1746 nbt.NBT_SERVER_SELECT_SECRET_DOMAIN_6: "SELECT_SECRET_DOMAIN_6",
1747 nbt.NBT_SERVER_FULL_SECRET_DOMAIN_6: "FULL_SECRET_DOMAIN_6",
1748 nbt.NBT_SERVER_ADS_WEB_SERVICE: "ADS_WEB_SERVICE",
1749 nbt.NBT_SERVER_DS_8: "DS_8",
1750 nbt.NBT_SERVER_HAS_DNS_NAME: "HAS_DNS_NAME",
1751 nbt.NBT_SERVER_IS_DEFAULT_NC: "IS_DEFAULT_NC",
1752 nbt.NBT_SERVER_FOREST_ROOT: "FOREST_ROOT",
1754 server_type_string = self.generic_bitmap_to_string(flag_map,
1755 remote_info.server_type, names_only=True)
1756 self.outf.write("RemoteDC Netbios[%s] DNS[%s] ServerType[%s]\n" % (
1757 remote_info.pdc_name,
1758 remote_info.pdc_dns_name,
1759 server_type_string))
1761 self.remote_server = remote_info.pdc_dns_name
1762 self.remote_binding_string="ncacn_np:%s[%s]" % (self.remote_server, remote_binding_options)
1763 self.remote_creds = remote_creds
1764 return self.remote_server
1766 def new_remote_lsa_connection(self):
1767 return lsa.lsarpc(self.remote_binding_string, self.local_lp, self.remote_creds)
1769 def new_remote_netlogon_connection(self):
1770 return netlogon.netlogon(self.remote_binding_string, self.local_lp, self.remote_creds)
1772 def get_lsa_info(self, conn, policy_access):
1773 objectAttr = lsa.ObjectAttribute()
1774 objectAttr.sec_qos = lsa.QosInfo()
1776 policy = conn.OpenPolicy2(''.decode('utf-8'),
1777 objectAttr, policy_access)
1779 info = conn.QueryInfoPolicy2(policy, lsa.LSA_POLICY_INFO_DNS)
1781 return (policy, info)
1783 def get_netlogon_dc_info(self, conn, server):
1784 info = conn.netr_DsRGetDCNameEx2(server,
1785 None, 0, None, None, None,
1786 netlogon.DS_RETURN_DNS_NAME)
1789 def netr_DomainTrust_to_name(self, t):
1790 if t.trust_type == lsa.LSA_TRUST_TYPE_DOWNLEVEL:
1791 return t.netbios_name
1795 def netr_DomainTrust_to_type(self, a, t):
1797 primary_parent = None
1799 if _t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
1801 if not _t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1802 primary_parent = a[_t.parent_index]
1805 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1806 if t is primary_parent:
1809 if t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1812 parent = a[t.parent_index]
1813 if parent is primary:
1818 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1823 def netr_DomainTrust_to_transitive(self, t):
1824 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1827 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE:
1830 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1835 def netr_DomainTrust_to_direction(self, t):
1836 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND and \
1837 t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1840 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND:
1843 if t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1848 def generic_enum_to_string(self, e_dict, v, names_only=False):
1852 v32 = self._uint32(v)
1853 w = "__unknown__%08X__" % v32
1855 r = "0x%x (%s)" % (v, w)
1858 def generic_bitmap_to_string(self, b_dict, v, names_only=False):
1863 for b in sorted(b_dict.keys()):
1870 c32 = self._uint32(c)
1871 s += ["__unknown_%08X__" % c32]
1876 r = "0x%x (%s)" % (v, w)
1879 def trustType_string(self, v):
1881 lsa.LSA_TRUST_TYPE_DOWNLEVEL : "DOWNLEVEL",
1882 lsa.LSA_TRUST_TYPE_UPLEVEL : "UPLEVEL",
1883 lsa.LSA_TRUST_TYPE_MIT : "MIT",
1884 lsa.LSA_TRUST_TYPE_DCE : "DCE",
1886 return self.generic_enum_to_string(types, v)
1888 def trustDirection_string(self, v):
1890 lsa.LSA_TRUST_DIRECTION_INBOUND |
1891 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "BOTH",
1892 lsa.LSA_TRUST_DIRECTION_INBOUND : "INBOUND",
1893 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "OUTBOUND",
1895 return self.generic_enum_to_string(directions, v)
1897 def trustAttributes_string(self, v):
1899 lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE : "NON_TRANSITIVE",
1900 lsa.LSA_TRUST_ATTRIBUTE_UPLEVEL_ONLY : "UPLEVEL_ONLY",
1901 lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN : "QUARANTINED_DOMAIN",
1902 lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE : "FOREST_TRANSITIVE",
1903 lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION : "CROSS_ORGANIZATION",
1904 lsa.LSA_TRUST_ATTRIBUTE_WITHIN_FOREST : "WITHIN_FOREST",
1905 lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL : "TREAT_AS_EXTERNAL",
1906 lsa.LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION : "USES_RC4_ENCRYPTION",
1908 return self.generic_bitmap_to_string(attributes, v)
1910 def kerb_EncTypes_string(self, v):
1912 security.KERB_ENCTYPE_DES_CBC_CRC : "DES_CBC_CRC",
1913 security.KERB_ENCTYPE_DES_CBC_MD5 : "DES_CBC_MD5",
1914 security.KERB_ENCTYPE_RC4_HMAC_MD5 : "RC4_HMAC_MD5",
1915 security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96 : "AES128_CTS_HMAC_SHA1_96",
1916 security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96 : "AES256_CTS_HMAC_SHA1_96",
1917 security.KERB_ENCTYPE_FAST_SUPPORTED : "FAST_SUPPORTED",
1918 security.KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED : "COMPOUND_IDENTITY_SUPPORTED",
1919 security.KERB_ENCTYPE_CLAIMS_SUPPORTED : "CLAIMS_SUPPORTED",
1920 security.KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED : "RESOURCE_SID_COMPRESSION_DISABLED",
1922 return self.generic_bitmap_to_string(enctypes, v)
1924 def entry_tln_status(self, e_flags, ):
1926 return "Status[Enabled]"
1929 lsa.LSA_TLN_DISABLED_NEW : "Disabled-New",
1930 lsa.LSA_TLN_DISABLED_ADMIN : "Disabled",
1931 lsa.LSA_TLN_DISABLED_CONFLICT : "Disabled-Conflicting",
1933 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
1935 def entry_dom_status(self, e_flags):
1937 return "Status[Enabled]"
1940 lsa.LSA_SID_DISABLED_ADMIN : "Disabled-SID",
1941 lsa.LSA_SID_DISABLED_CONFLICT : "Disabled-SID-Conflicting",
1942 lsa.LSA_NB_DISABLED_ADMIN : "Disabled-NB",
1943 lsa.LSA_NB_DISABLED_CONFLICT : "Disabled-NB-Conflicting",
1945 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
1947 def write_forest_trust_info(self, fti, tln=None, collisions=None):
1949 tln_string = " TDO[%s]" % tln
1953 self.outf.write("Namespaces[%d]%s:\n" % (
1954 len(fti.entries), tln_string))
1956 for i in xrange(0, len(fti.entries)):
1960 collision_string = ""
1962 if collisions is not None:
1963 for c in collisions.entries:
1967 collision_string = " Collision[%s]" % (c.name.string)
1969 d = e.forest_trust_data
1970 if e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
1971 self.outf.write("TLN: %-32s DNS[*.%s]%s\n" % (
1972 self.entry_tln_status(flags),
1973 d.string, collision_string))
1974 elif e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
1975 self.outf.write("TLN_EX: %-29s DNS[*.%s]\n" % (
1977 elif e.type == lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
1978 self.outf.write("DOM: %-32s DNS[%s] Netbios[%s] SID[%s]%s\n" % (
1979 self.entry_dom_status(flags),
1980 d.dns_domain_name.string,
1981 d.netbios_domain_name.string,
1982 d.domain_sid, collision_string))
1985 class cmd_domain_trust_list(DomainTrustCommand):
1986 """List domain trusts."""
1988 synopsis = "%prog [options]"
1990 takes_optiongroups = {
1991 "sambaopts": options.SambaOptions,
1992 "versionopts": options.VersionOptions,
1993 "localdcopts": LocalDCCredentialsOptions,
1999 def run(self, sambaopts=None, versionopts=None, localdcopts=None):
2001 local_server = self.setup_local_server(sambaopts, localdcopts)
2003 local_netlogon = self.new_local_netlogon_connection()
2004 except RuntimeError as error:
2005 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2008 local_netlogon_trusts = local_netlogon.netr_DsrEnumerateDomainTrusts(local_server,
2009 netlogon.NETR_TRUST_FLAG_IN_FOREST |
2010 netlogon.NETR_TRUST_FLAG_OUTBOUND |
2011 netlogon.NETR_TRUST_FLAG_INBOUND)
2012 except RuntimeError as error:
2013 if self.check_runtime_error(error, self.WERR_RPC_S_PROCNUM_OUT_OF_RANGE):
2014 # TODO: we could implement a fallback to lsa.EnumTrustDom()
2015 raise CommandError("LOCAL_DC[%s]: netr_DsrEnumerateDomainTrusts not supported." % (
2017 raise self.LocalRuntimeError(self, error, "netr_DsrEnumerateDomainTrusts failed")
2019 a = local_netlogon_trusts.array
2021 if t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
2023 self.outf.write("%-14s %-15s %-19s %s\n" % (
2024 "Type[%s]" % self.netr_DomainTrust_to_type(a, t),
2025 "Transitive[%s]" % self.netr_DomainTrust_to_transitive(t),
2026 "Direction[%s]" % self.netr_DomainTrust_to_direction(t),
2027 "Name[%s]" % self.netr_DomainTrust_to_name(t)))
2030 class cmd_domain_trust_show(DomainTrustCommand):
2031 """Show trusted domain details."""
2033 synopsis = "%prog NAME [options]"
2035 takes_optiongroups = {
2036 "sambaopts": options.SambaOptions,
2037 "versionopts": options.VersionOptions,
2038 "localdcopts": LocalDCCredentialsOptions,
2044 takes_args = ["domain"]
2046 def run(self, domain, sambaopts=None, versionopts=None, localdcopts=None):
2048 local_server = self.setup_local_server(sambaopts, localdcopts)
2050 local_lsa = self.new_local_lsa_connection()
2051 except RuntimeError as error:
2052 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2055 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2056 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2057 except RuntimeError as error:
2058 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2060 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2061 local_lsa_info.name.string,
2062 local_lsa_info.dns_domain.string,
2063 local_lsa_info.sid))
2065 lsaString = lsa.String()
2066 lsaString.string = domain
2068 local_tdo_full = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2069 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2070 local_tdo_info = local_tdo_full.info_ex
2071 local_tdo_posix = local_tdo_full.posix_offset
2072 except RuntimeError as error:
2073 if self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2074 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2076 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(FULL_INFO) failed")
2079 local_tdo_enctypes = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2080 lsaString, lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES)
2081 except RuntimeError as error:
2082 if self.check_runtime_error(error, self.NT_STATUS_INVALID_PARAMETER):
2084 if self.check_runtime_error(error, self.NT_STATUS_INVALID_INFO_CLASS):
2087 if error is not None:
2088 raise self.LocalRuntimeError(self, error,
2089 "QueryTrustedDomainInfoByName(SUPPORTED_ENCRYPTION_TYPES) failed")
2091 local_tdo_enctypes = lsa.TrustDomainInfoSupportedEncTypes()
2092 local_tdo_enctypes.enc_types = 0
2095 local_tdo_forest = None
2096 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2097 local_tdo_forest = local_lsa.lsaRQueryForestTrustInformation(local_policy,
2098 lsaString, lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
2099 except RuntimeError as error:
2100 if self.check_runtime_error(error, self.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
2102 if self.check_runtime_error(error, self.NT_STATUS_NOT_FOUND):
2104 if error is not None:
2105 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation failed")
2107 local_tdo_forest = lsa.ForestTrustInformation()
2108 local_tdo_forest.count = 0
2109 local_tdo_forest.entries = []
2111 self.outf.write("TrusteDomain:\n\n");
2112 self.outf.write("NetbiosName: %s\n" % local_tdo_info.netbios_name.string)
2113 if local_tdo_info.netbios_name.string != local_tdo_info.domain_name.string:
2114 self.outf.write("DnsName: %s\n" % local_tdo_info.domain_name.string)
2115 self.outf.write("SID: %s\n" % local_tdo_info.sid)
2116 self.outf.write("Type: %s\n" % self.trustType_string(local_tdo_info.trust_type))
2117 self.outf.write("Direction: %s\n" % self.trustDirection_string(local_tdo_info.trust_direction))
2118 self.outf.write("Attributes: %s\n" % self.trustAttributes_string(local_tdo_info.trust_attributes))
2119 posix_offset_u32 = ctypes.c_uint32(local_tdo_posix.posix_offset).value
2120 posix_offset_i32 = ctypes.c_int32(local_tdo_posix.posix_offset).value
2121 self.outf.write("PosixOffset: 0x%08X (%d)\n" % (posix_offset_u32, posix_offset_i32))
2122 self.outf.write("kerb_EncTypes: %s\n" % self.kerb_EncTypes_string(local_tdo_enctypes.enc_types))
2124 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2125 self.write_forest_trust_info(local_tdo_forest,
2126 tln=local_tdo_info.domain_name.string)
2130 class cmd_domain_trust_create(DomainTrustCommand):
2131 """Create a domain or forest trust."""
2133 synopsis = "%prog DOMAIN [options]"
2135 takes_optiongroups = {
2136 "sambaopts": options.SambaOptions,
2137 "versionopts": options.VersionOptions,
2138 "credopts": options.CredentialsOptions,
2139 "localdcopts": LocalDCCredentialsOptions,
2143 Option("--type", type="choice", metavar="TYPE",
2144 choices=["external", "forest"],
2145 help="The type of the trust: 'external' or 'forest'.",
2147 default="external"),
2148 Option("--direction", type="choice", metavar="DIRECTION",
2149 choices=["incoming", "outgoing", "both"],
2150 help="The trust direction: 'incoming', 'outgoing' or 'both'.",
2151 dest='trust_direction',
2153 Option("--create-location", type="choice", metavar="LOCATION",
2154 choices=["local", "both"],
2155 help="Where to create the trusted domain object: 'local' or 'both'.",
2156 dest='create_location',
2158 Option("--cross-organisation", action="store_true",
2159 help="The related domains does not belong to the same organisation.",
2160 dest='cross_organisation',
2162 Option("--quarantined", type="choice", metavar="yes|no",
2163 choices=["yes", "no", None],
2164 help="Special SID filtering rules are applied to the trust. "
2165 "With --type=external the default is yes. "
2166 "With --type=forest the default is no.",
2167 dest='quarantined_arg',
2169 Option("--not-transitive", action="store_true",
2170 help="The forest trust is not transitive.",
2171 dest='not_transitive',
2173 Option("--treat-as-external", action="store_true",
2174 help="The treat the forest trust as external.",
2175 dest='treat_as_external',
2177 Option("--no-aes-keys", action="store_false",
2178 help="The trust uses aes kerberos keys.",
2179 dest='use_aes_keys',
2181 Option("--skip-validation", action="store_false",
2182 help="Skip validation of the trust.",
2187 takes_args = ["domain"]
2189 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2190 trust_type=None, trust_direction=None, create_location=None,
2191 cross_organisation=False, quarantined_arg=None,
2192 not_transitive=False, treat_as_external=False,
2193 use_aes_keys=False, validate=True):
2195 lsaString = lsa.String()
2198 if quarantined_arg is None:
2199 if trust_type == 'external':
2201 elif quarantined_arg == 'yes':
2204 if trust_type != 'forest':
2206 raise CommandError("--not-transitive requires --type=forest")
2207 if treat_as_external:
2208 raise CommandError("--treat-as-external requires --type=forest")
2212 enc_types = lsa.TrustDomainInfoSupportedEncTypes()
2213 enc_types.enc_types = security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96
2214 enc_types.enc_types |= security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96
2216 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2217 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2218 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2220 local_trust_info = lsa.TrustDomainInfoInfoEx()
2221 local_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2222 local_trust_info.trust_direction = 0
2223 if trust_direction == "both":
2224 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2225 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2226 elif trust_direction == "incoming":
2227 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2228 elif trust_direction == "outgoing":
2229 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2230 local_trust_info.trust_attributes = 0
2231 if cross_organisation:
2232 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2234 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2235 if trust_type == "forest":
2236 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2238 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2239 if treat_as_external:
2240 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2242 def get_password(name):
2245 if password is not None and password is not '':
2247 password = getpass("New %s Password: " % name)
2248 passwordverify = getpass("Retype %s Password: " % name)
2249 if not password == passwordverify:
2251 self.outf.write("Sorry, passwords do not match.\n")
2253 incoming_secret = None
2254 outgoing_secret = None
2255 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2256 if create_location == "local":
2257 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2258 incoming_password = get_password("Incoming Trust")
2259 incoming_secret = string_to_byte_array(incoming_password.encode('utf-16-le'))
2260 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2261 outgoing_password = get_password("Outgoing Trust")
2262 outgoing_secret = string_to_byte_array(outgoing_password.encode('utf-16-le'))
2264 remote_trust_info = None
2266 # We use 240 random bytes.
2267 # Windows uses 28 or 240 random bytes. I guess it's
2268 # based on the trust type external vs. forest.
2270 # The initial trust password can be up to 512 bytes
2271 # while the versioned passwords used for periodic updates
2272 # can only be up to 498 bytes, as netr_ServerPasswordSet2()
2273 # needs to pass the NL_PASSWORD_VERSION structure within the
2274 # 512 bytes and a 2 bytes confounder is required.
2276 def random_trust_secret(length, use_aes_keys=True):
2277 secret = [0] * length
2279 pw1 = samba.generate_random_password(length/2, length/2)
2280 if not use_aes_keys:
2281 # With arcfour-hmac-md5 we have to use valid utf16
2282 # in order to generate the correct pre-auth key
2283 # based on a utf8 password.
2285 # We can remove this once our client libraries
2286 # support using the correct NTHASH.
2287 return string_to_byte_array(pw1.encode('utf-16-le'))
2289 # We mix characters from generate_random_password
2290 # with random numbers from random.randint()
2291 for i in range(len(secret)):
2293 secret[i] = ord(pw1[i])
2295 secret[i] = random.randint(0, 255)
2299 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2300 incoming_secret = random_trust_secret(240, use_aes_keys=use_aes_keys)
2301 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2302 outgoing_secret = random_trust_secret(240, use_aes_keys=use_aes_keys)
2304 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2305 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2307 remote_trust_info = lsa.TrustDomainInfoInfoEx()
2308 remote_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2309 remote_trust_info.trust_direction = 0
2310 if trust_direction == "both":
2311 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2312 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2313 elif trust_direction == "incoming":
2314 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2315 elif trust_direction == "outgoing":
2316 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2317 remote_trust_info.trust_attributes = 0
2318 if cross_organisation:
2319 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2321 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2322 if trust_type == "forest":
2323 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2325 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2326 if treat_as_external:
2327 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2329 local_server = self.setup_local_server(sambaopts, localdcopts)
2331 local_lsa = self.new_local_lsa_connection()
2332 except RuntimeError as error:
2333 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2336 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2337 except RuntimeError as error:
2338 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2340 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2341 local_lsa_info.name.string,
2342 local_lsa_info.dns_domain.string,
2343 local_lsa_info.sid))
2346 remote_server = self.setup_remote_server(credopts, domain)
2347 except RuntimeError as error:
2348 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2351 remote_lsa = self.new_remote_lsa_connection()
2352 except RuntimeError as error:
2353 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2356 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2357 except RuntimeError as error:
2358 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2360 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2361 remote_lsa_info.name.string,
2362 remote_lsa_info.dns_domain.string,
2363 remote_lsa_info.sid))
2365 local_trust_info.domain_name.string = remote_lsa_info.dns_domain.string
2366 local_trust_info.netbios_name.string = remote_lsa_info.name.string
2367 local_trust_info.sid = remote_lsa_info.sid
2369 if remote_trust_info:
2370 remote_trust_info.domain_name.string = local_lsa_info.dns_domain.string
2371 remote_trust_info.netbios_name.string = local_lsa_info.name.string
2372 remote_trust_info.sid = local_lsa_info.sid
2375 lsaString.string = local_trust_info.domain_name.string
2376 local_old_netbios = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2377 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2378 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2379 except RuntimeError as error:
2380 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2381 raise self.LocalRuntimeError(self, error,
2382 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2386 lsaString.string = local_trust_info.netbios_name.string
2387 local_old_dns = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2388 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2389 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2390 except RuntimeError as error:
2391 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2392 raise self.LocalRuntimeError(self, error,
2393 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2396 if remote_trust_info:
2398 lsaString.string = remote_trust_info.domain_name.string
2399 remote_old_netbios = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2400 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2401 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2402 except RuntimeError as error:
2403 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2404 raise self.RemoteRuntimeError(self, error,
2405 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2409 lsaString.string = remote_trust_info.netbios_name.string
2410 remote_old_dns = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2411 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2412 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2413 except RuntimeError as error:
2414 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2415 raise self.RemoteRuntimeError(self, error,
2416 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2420 local_netlogon = self.new_local_netlogon_connection()
2421 except RuntimeError as error:
2422 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2425 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
2426 except RuntimeError as error:
2427 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
2429 if remote_trust_info:
2431 remote_netlogon = self.new_remote_netlogon_connection()
2432 except RuntimeError as error:
2433 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2436 remote_netlogon_info = self.get_netlogon_dc_info(remote_netlogon, remote_server)
2437 except RuntimeError as error:
2438 raise self.RemoteRuntimeError(self, error, "failed to get netlogon dc info")
2440 def generate_AuthInOutBlob(secret, update_time):
2442 blob = drsblobs.trustAuthInOutBlob()
2447 clear = drsblobs.AuthInfoClear()
2448 clear.size = len(secret)
2449 clear.password = secret
2451 info = drsblobs.AuthenticationInformation()
2452 info.LastUpdateTime = samba.unix2nttime(update_time)
2453 info.AuthType = lsa.TRUST_AUTH_TYPE_CLEAR
2454 info.AuthInfo = clear
2456 array = drsblobs.AuthenticationInformationArray()
2458 array.array = [info]
2460 blob = drsblobs.trustAuthInOutBlob()
2462 blob.current = array
2466 def generate_AuthInfoInternal(session_key, incoming=None, outgoing=None):
2467 confounder = [0] * 512
2468 for i in range(len(confounder)):
2469 confounder[i] = random.randint(0, 255)
2471 trustpass = drsblobs.trustDomainPasswords()
2473 trustpass.confounder = confounder
2474 trustpass.outgoing = outgoing
2475 trustpass.incoming = incoming
2477 trustpass_blob = ndr_pack(trustpass)
2479 encrypted_trustpass = arcfour_encrypt(session_key, trustpass_blob)
2481 auth_blob = lsa.DATA_BUF2()
2482 auth_blob.size = len(encrypted_trustpass)
2483 auth_blob.data = string_to_byte_array(encrypted_trustpass)
2485 auth_info = lsa.TrustDomainInfoAuthInfoInternal()
2486 auth_info.auth_blob = auth_blob
2490 update_time = samba.current_unix_time()
2491 incoming_blob = generate_AuthInOutBlob(incoming_secret, update_time)
2492 outgoing_blob = generate_AuthInOutBlob(outgoing_secret, update_time)
2494 local_tdo_handle = None
2495 remote_tdo_handle = None
2497 local_auth_info = generate_AuthInfoInternal(local_lsa.session_key,
2498 incoming=incoming_blob,
2499 outgoing=outgoing_blob)
2500 if remote_trust_info:
2501 remote_auth_info = generate_AuthInfoInternal(remote_lsa.session_key,
2502 incoming=outgoing_blob,
2503 outgoing=incoming_blob)
2506 if remote_trust_info:
2507 self.outf.write("Creating remote TDO.\n")
2508 current_request = { "location": "remote", "name": "CreateTrustedDomainEx2"}
2509 remote_tdo_handle = remote_lsa.CreateTrustedDomainEx2(remote_policy,
2512 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2513 self.outf.write("Remote TDO created.\n")
2515 self.outf.write("Setting supported encryption types on remote TDO.\n")
2516 current_request = { "location": "remote", "name": "SetInformationTrustedDomain"}
2517 remote_lsa.SetInformationTrustedDomain(remote_tdo_handle,
2518 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2521 self.outf.write("Creating local TDO.\n")
2522 current_request = { "location": "local", "name": "CreateTrustedDomainEx2"}
2523 local_tdo_handle = local_lsa.CreateTrustedDomainEx2(local_policy,
2526 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2527 self.outf.write("Local TDO created\n")
2529 self.outf.write("Setting supported encryption types on local TDO.\n")
2530 current_request = { "location": "local", "name": "SetInformationTrustedDomain"}
2531 local_lsa.SetInformationTrustedDomain(local_tdo_handle,
2532 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2534 except RuntimeError as error:
2535 self.outf.write("Error: %s failed %sly - cleaning up\n" % (
2536 current_request['name'], current_request['location']))
2537 if remote_tdo_handle:
2538 self.outf.write("Deleting remote TDO.\n")
2539 remote_lsa.DeleteObject(remote_tdo_handle)
2540 remote_tdo_handle = None
2541 if local_tdo_handle:
2542 self.outf.write("Deleting local TDO.\n")
2543 local_lsa.DeleteObject(local_tdo_handle)
2544 local_tdo_handle = None
2545 if current_request['location'] is "remote":
2546 raise self.RemoteRuntimeError(self, error, "%s" % (
2547 current_request['name']))
2548 raise self.LocalRuntimeError(self, error, "%s" % (
2549 current_request['name']))
2552 if local_trust_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2553 self.outf.write("Setup local forest trust information...\n")
2555 # get all information about the remote trust
2556 # this triggers netr_GetForestTrustInformation to the remote domain
2557 # and lsaRSetForestTrustInformation() locally, but new top level
2558 # names are disabled by default.
2559 local_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
2560 remote_lsa_info.dns_domain.string,
2561 netlogon.DS_GFTI_UPDATE_TDO)
2562 except RuntimeError as error:
2563 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2566 # here we try to enable all top level names
2567 local_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
2568 remote_lsa_info.dns_domain,
2569 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2572 except RuntimeError as error:
2573 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2575 self.write_forest_trust_info(local_forest_info,
2576 tln=remote_lsa_info.dns_domain.string,
2577 collisions=local_forest_collision)
2579 if remote_trust_info:
2580 self.outf.write("Setup remote forest trust information...\n")
2582 # get all information about the local trust (from the perspective of the remote domain)
2583 # this triggers netr_GetForestTrustInformation to our domain.
2584 # and lsaRSetForestTrustInformation() remotely, but new top level
2585 # names are disabled by default.
2586 remote_forest_info = remote_netlogon.netr_DsRGetForestTrustInformation(remote_netlogon_info.dc_unc,
2587 local_lsa_info.dns_domain.string,
2588 netlogon.DS_GFTI_UPDATE_TDO)
2589 except RuntimeError as error:
2590 raise self.RemoteRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2593 # here we try to enable all top level names
2594 remote_forest_collision = remote_lsa.lsaRSetForestTrustInformation(remote_policy,
2595 local_lsa_info.dns_domain,
2596 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2599 except RuntimeError as error:
2600 raise self.RemoteRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2602 self.write_forest_trust_info(remote_forest_info,
2603 tln=local_lsa_info.dns_domain.string,
2604 collisions=remote_forest_collision)
2606 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2607 self.outf.write("Validating outgoing trust...\n")
2609 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_netlogon_info.dc_unc,
2610 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2612 remote_lsa_info.dns_domain.string)
2613 except RuntimeError as error:
2614 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2616 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2617 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2619 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2620 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2621 local_trust_verify.trusted_dc_name,
2622 local_trust_verify.tc_connection_status[1],
2623 local_trust_verify.pdc_connection_status[1])
2625 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2626 local_trust_verify.trusted_dc_name,
2627 local_trust_verify.tc_connection_status[1],
2628 local_trust_verify.pdc_connection_status[1])
2630 if local_trust_status != self.WERR_OK or local_conn_status != self.WERR_OK:
2631 raise CommandError(local_validation)
2633 self.outf.write("OK: %s\n" % local_validation)
2635 if remote_trust_info:
2636 if remote_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2637 self.outf.write("Validating incoming trust...\n")
2639 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_netlogon_info.dc_unc,
2640 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2642 local_lsa_info.dns_domain.string)
2643 except RuntimeError as error:
2644 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2646 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2647 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2649 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2650 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2651 remote_trust_verify.trusted_dc_name,
2652 remote_trust_verify.tc_connection_status[1],
2653 remote_trust_verify.pdc_connection_status[1])
2655 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2656 remote_trust_verify.trusted_dc_name,
2657 remote_trust_verify.tc_connection_status[1],
2658 remote_trust_verify.pdc_connection_status[1])
2660 if remote_trust_status != self.WERR_OK or remote_conn_status != self.WERR_OK:
2661 raise CommandError(remote_validation)
2663 self.outf.write("OK: %s\n" % remote_validation)
2665 if remote_tdo_handle is not None:
2667 remote_lsa.Close(remote_tdo_handle)
2668 except RuntimeError as error:
2670 remote_tdo_handle = None
2671 if local_tdo_handle is not None:
2673 local_lsa.Close(local_tdo_handle)
2674 except RuntimeError as error:
2676 local_tdo_handle = None
2678 self.outf.write("Success.\n")
2681 class cmd_domain_trust_delete(DomainTrustCommand):
2682 """Delete a domain trust."""
2684 synopsis = "%prog DOMAIN [options]"
2686 takes_optiongroups = {
2687 "sambaopts": options.SambaOptions,
2688 "versionopts": options.VersionOptions,
2689 "credopts": options.CredentialsOptions,
2690 "localdcopts": LocalDCCredentialsOptions,
2694 Option("--delete-location", type="choice", metavar="LOCATION",
2695 choices=["local", "both"],
2696 help="Where to delete the trusted domain object: 'local' or 'both'.",
2697 dest='delete_location',
2701 takes_args = ["domain"]
2703 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2704 delete_location=None):
2706 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2707 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2708 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2710 if delete_location == "local":
2711 remote_policy_access = None
2713 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2714 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2715 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2717 local_server = self.setup_local_server(sambaopts, localdcopts)
2719 local_lsa = self.new_local_lsa_connection()
2720 except RuntimeError as error:
2721 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2724 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2725 except RuntimeError as error:
2726 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2728 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2729 local_lsa_info.name.string,
2730 local_lsa_info.dns_domain.string,
2731 local_lsa_info.sid))
2733 local_tdo_info = None
2734 local_tdo_handle = None
2735 remote_tdo_info = None
2736 remote_tdo_handle = None
2738 lsaString = lsa.String()
2740 lsaString.string = domain
2741 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2742 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2743 except RuntimeError as error:
2744 if self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2745 raise CommandError("Failed to find trust for domain '%s'" % domain)
2746 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2749 if remote_policy_access is not None:
2751 remote_server = self.setup_remote_server(credopts, domain)
2752 except RuntimeError as error:
2753 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2756 remote_lsa = self.new_remote_lsa_connection()
2757 except RuntimeError as error:
2758 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2761 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2762 except RuntimeError as error:
2763 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2765 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2766 remote_lsa_info.name.string,
2767 remote_lsa_info.dns_domain.string,
2768 remote_lsa_info.sid))
2770 if remote_lsa_info.sid != local_tdo_info.sid or \
2771 remote_lsa_info.name.string != local_tdo_info.netbios_name.string or \
2772 remote_lsa_info.dns_domain.string != local_tdo_info.domain_name.string:
2773 raise CommandError("LocalTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2774 local_tdo_info.netbios_name.string,
2775 local_tdo_info.domain_name.string,
2776 local_tdo_info.sid))
2779 lsaString.string = local_lsa_info.dns_domain.string
2780 remote_tdo_info = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2781 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2782 except RuntimeError as error:
2783 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2784 raise self.RemoteRuntimeError(self, error, "QueryTrustedDomainInfoByName(%s)" % (
2788 if remote_tdo_info is not None:
2789 if local_lsa_info.sid != remote_tdo_info.sid or \
2790 local_lsa_info.name.string != remote_tdo_info.netbios_name.string or \
2791 local_lsa_info.dns_domain.string != remote_tdo_info.domain_name.string:
2792 raise CommandError("RemoteTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2793 remote_tdo_info.netbios_name.string,
2794 remote_tdo_info.domain_name.string,
2795 remote_tdo_info.sid))
2797 if local_tdo_info is not None:
2799 lsaString.string = local_tdo_info.domain_name.string
2800 local_tdo_handle = local_lsa.OpenTrustedDomainByName(local_policy,
2802 security.SEC_STD_DELETE)
2803 except RuntimeError as error:
2804 raise self.LocalRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2807 local_lsa.DeleteObject(local_tdo_handle)
2808 local_tdo_handle = None
2810 if remote_tdo_info is not None:
2812 lsaString.string = remote_tdo_info.domain_name.string
2813 remote_tdo_handle = remote_lsa.OpenTrustedDomainByName(remote_policy,
2815 security.SEC_STD_DELETE)
2816 except RuntimeError as error:
2817 raise self.RemoteRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2820 if remote_tdo_handle is not None:
2822 remote_lsa.DeleteObject(remote_tdo_handle)
2823 remote_tdo_handle = None
2824 self.outf.write("RemoteTDO deleted.\n")
2825 except RuntimeError as error:
2826 self.outf.write("%s\n" % self.RemoteRuntimeError(self, error, "DeleteObject() failed"))
2828 if local_tdo_handle is not None:
2830 local_lsa.DeleteObject(local_tdo_handle)
2831 local_tdo_handle = None
2832 self.outf.write("LocalTDO deleted.\n")
2833 except RuntimeError as error:
2834 self.outf.write("%s\n" % self.LocalRuntimeError(self, error, "DeleteObject() failed"))
2838 class cmd_domain_trust_validate(DomainTrustCommand):
2839 """Validate a domain trust."""
2841 synopsis = "%prog DOMAIN [options]"
2843 takes_optiongroups = {
2844 "sambaopts": options.SambaOptions,
2845 "versionopts": options.VersionOptions,
2846 "credopts": options.CredentialsOptions,
2847 "localdcopts": LocalDCCredentialsOptions,
2851 Option("--validate-location", type="choice", metavar="LOCATION",
2852 choices=["local", "both"],
2853 help="Where to validate the trusted domain object: 'local' or 'both'.",
2854 dest='validate_location',
2858 takes_args = ["domain"]
2860 def run(self, domain, sambaopts=None, versionopts=None, credopts=None, localdcopts=None,
2861 validate_location=None):
2863 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2865 local_server = self.setup_local_server(sambaopts, localdcopts)
2867 local_lsa = self.new_local_lsa_connection()
2868 except RuntimeError as error:
2869 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2872 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2873 except RuntimeError as error:
2874 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2876 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2877 local_lsa_info.name.string,
2878 local_lsa_info.dns_domain.string,
2879 local_lsa_info.sid))
2882 lsaString = lsa.String()
2883 lsaString.string = domain
2884 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2885 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2886 except RuntimeError as error:
2887 if self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2888 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2890 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
2892 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
2893 local_tdo_info.netbios_name.string,
2894 local_tdo_info.domain_name.string,
2895 local_tdo_info.sid))
2898 local_netlogon = self.new_local_netlogon_connection()
2899 except RuntimeError as error:
2900 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2903 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_server,
2904 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2906 local_tdo_info.domain_name.string)
2907 except RuntimeError as error:
2908 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2910 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2911 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2913 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2914 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2915 local_trust_verify.trusted_dc_name,
2916 local_trust_verify.tc_connection_status[1],
2917 local_trust_verify.pdc_connection_status[1])
2919 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2920 local_trust_verify.trusted_dc_name,
2921 local_trust_verify.tc_connection_status[1],
2922 local_trust_verify.pdc_connection_status[1])
2924 if local_trust_status != self.WERR_OK or local_conn_status != self.WERR_OK:
2925 raise CommandError(local_validation)
2927 self.outf.write("OK: %s\n" % local_validation)
2930 server = local_trust_verify.trusted_dc_name.replace('\\', '')
2931 domain_and_server = "%s\\%s" % (local_tdo_info.domain_name.string, server)
2932 local_trust_rediscover = local_netlogon.netr_LogonControl2Ex(local_server,
2933 netlogon.NETLOGON_CONTROL_REDISCOVER,
2936 except RuntimeError as error:
2937 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
2939 local_conn_status = self._uint32(local_trust_rediscover.tc_connection_status[0])
2940 local_rediscover = "LocalRediscover: DC[%s] CONNECTION[%s]" % (
2941 local_trust_rediscover.trusted_dc_name,
2942 local_trust_rediscover.tc_connection_status[1])
2944 if local_conn_status != self.WERR_OK:
2945 raise CommandError(local_rediscover)
2947 self.outf.write("OK: %s\n" % local_rediscover)
2949 if validate_location != "local":
2951 remote_server = self.setup_remote_server(credopts, domain, require_pdc=False)
2952 except RuntimeError as error:
2953 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2956 remote_netlogon = self.new_remote_netlogon_connection()
2957 except RuntimeError as error:
2958 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2961 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_server,
2962 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2964 local_lsa_info.dns_domain.string)
2965 except RuntimeError as error:
2966 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2968 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2969 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2971 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2972 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2973 remote_trust_verify.trusted_dc_name,
2974 remote_trust_verify.tc_connection_status[1],
2975 remote_trust_verify.pdc_connection_status[1])
2977 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2978 remote_trust_verify.trusted_dc_name,
2979 remote_trust_verify.tc_connection_status[1],
2980 remote_trust_verify.pdc_connection_status[1])
2982 if remote_trust_status != self.WERR_OK or remote_conn_status != self.WERR_OK:
2983 raise CommandError(remote_validation)
2985 self.outf.write("OK: %s\n" % remote_validation)
2988 server = remote_trust_verify.trusted_dc_name.replace('\\', '')
2989 domain_and_server = "%s\\%s" % (local_lsa_info.dns_domain.string, server)
2990 remote_trust_rediscover = remote_netlogon.netr_LogonControl2Ex(remote_server,
2991 netlogon.NETLOGON_CONTROL_REDISCOVER,
2994 except RuntimeError as error:
2995 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
2997 remote_conn_status = self._uint32(remote_trust_rediscover.tc_connection_status[0])
2999 remote_rediscover = "RemoteRediscover: DC[%s] CONNECTION[%s]" % (
3000 remote_trust_rediscover.trusted_dc_name,
3001 remote_trust_rediscover.tc_connection_status[1])
3003 if remote_conn_status != self.WERR_OK:
3004 raise CommandError(remote_rediscover)
3006 self.outf.write("OK: %s\n" % remote_rediscover)
3010 class cmd_domain_trust_namespaces(DomainTrustCommand):
3011 """Manage forest trust namespaces."""
3013 synopsis = "%prog [DOMAIN] [options]"
3015 takes_optiongroups = {
3016 "sambaopts": options.SambaOptions,
3017 "versionopts": options.VersionOptions,
3018 "localdcopts": LocalDCCredentialsOptions,
3022 Option("--refresh", type="choice", metavar="check|store",
3023 choices=["check", "store", None],
3024 help="List and maybe store refreshed forest trust information: 'check' or 'store'.",
3027 Option("--enable-all", action="store_true",
3028 help="Try to update disabled entries, not allowed with --refresh=check.",
3031 Option("--enable-tln", action="append", metavar='DNSDOMAIN',
3032 help="Enable a top level name entry. Can be specified multiple times.",
3035 Option("--disable-tln", action="append", metavar='DNSDOMAIN',
3036 help="Disable a top level name entry. Can be specified multiple times.",
3039 Option("--add-tln-ex", action="append", metavar='DNSDOMAIN',
3040 help="Add a top level exclusion entry. Can be specified multiple times.",
3043 Option("--delete-tln-ex", action="append", metavar='DNSDOMAIN',
3044 help="Delete a top level exclusion entry. Can be specified multiple times.",
3045 dest='delete_tln_ex',
3047 Option("--enable-nb", action="append", metavar='NETBIOSDOMAIN',
3048 help="Enable a netbios name in a domain entry. Can be specified multiple times.",
3051 Option("--disable-nb", action="append", metavar='NETBIOSDOMAIN',
3052 help="Disable a netbios name in a domain entry. Can be specified multiple times.",
3055 Option("--enable-sid", action="append", metavar='DOMAINSID',
3056 help="Enable a SID in a domain entry. Can be specified multiple times.",
3057 dest='enable_sid_str',
3059 Option("--disable-sid", action="append", metavar='DOMAINSID',
3060 help="Disable a SID in a domain entry. Can be specified multiple times.",
3061 dest='disable_sid_str',
3063 Option("--add-upn-suffix", action="append", metavar='DNSDOMAIN',
3064 help="Add a new uPNSuffixes attribute for the local forest. Can be specified multiple times.",
3067 Option("--delete-upn-suffix", action="append", metavar='DNSDOMAIN',
3068 help="Delete an existing uPNSuffixes attribute of the local forest. Can be specified multiple times.",
3071 Option("--add-spn-suffix", action="append", metavar='DNSDOMAIN',
3072 help="Add a new msDS-SPNSuffixes attribute for the local forest. Can be specified multiple times.",
3075 Option("--delete-spn-suffix", action="append", metavar='DNSDOMAIN',
3076 help="Delete an existing msDS-SPNSuffixes attribute of the local forest. Can be specified multiple times.",
3081 takes_args = ["domain?"]
3083 def run(self, domain=None, sambaopts=None, localdcopts=None, versionopts=None,
3084 refresh=None, enable_all=False,
3085 enable_tln=[], disable_tln=[], add_tln_ex=[], delete_tln_ex=[],
3086 enable_sid_str=[], disable_sid_str=[], enable_nb=[], disable_nb=[],
3087 add_upn=[], delete_upn=[], add_spn=[], delete_spn=[]):
3089 require_update = False
3092 if refresh == "store":
3093 raise CommandError("--refresh=%s not allowed without DOMAIN" % refresh)
3096 raise CommandError("--enable-all not allowed without DOMAIN")
3098 if len(enable_tln) > 0:
3099 raise CommandError("--enable-tln not allowed without DOMAIN")
3100 if len(disable_tln) > 0:
3101 raise CommandError("--disable-tln not allowed without DOMAIN")
3103 if len(add_tln_ex) > 0:
3104 raise CommandError("--add-tln-ex not allowed without DOMAIN")
3105 if len(delete_tln_ex) > 0:
3106 raise CommandError("--delete-tln-ex not allowed without DOMAIN")
3108 if len(enable_nb) > 0:
3109 raise CommandError("--enable-nb not allowed without DOMAIN")
3110 if len(disable_nb) > 0:
3111 raise CommandError("--disable-nb not allowed without DOMAIN")
3113 if len(enable_sid_str) > 0:
3114 raise CommandError("--enable-sid not allowed without DOMAIN")
3115 if len(disable_sid_str) > 0:
3116 raise CommandError("--disable-sid not allowed without DOMAIN")
3118 if len(add_upn) > 0:
3120 if not n.startswith("*."):
3122 raise CommandError("value[%s] specified for --add-upn-suffix should not include with '*.'" % n)
3123 require_update = True
3124 if len(delete_upn) > 0:
3125 for n in delete_upn:
3126 if not n.startswith("*."):
3128 raise CommandError("value[%s] specified for --delete-upn-suffix should not include with '*.'" % n)
3129 require_update = True
3131 for d in delete_upn:
3132 if a.lower() != d.lower():
3134 raise CommandError("value[%s] specified for --add-upn-suffix and --delete-upn-suffix" % a)
3136 if len(add_spn) > 0:
3138 if not n.startswith("*."):
3140 raise CommandError("value[%s] specified for --add-spn-suffix should not include with '*.'" % n)
3141 require_update = True
3142 if len(delete_spn) > 0:
3143 for n in delete_spn:
3144 if not n.startswith("*."):
3146 raise CommandError("value[%s] specified for --delete-spn-suffix should not include with '*.'" % n)
3147 require_update = True
3149 for d in delete_spn:
3150 if a.lower() != d.lower():
3152 raise CommandError("value[%s] specified for --add-spn-suffix and --delete-spn-suffix" % a)
3154 if len(add_upn) > 0:
3155 raise CommandError("--add-upn-suffix not allowed together with DOMAIN")
3156 if len(delete_upn) > 0:
3157 raise CommandError("--delete-upn-suffix not allowed together with DOMAIN")
3158 if len(add_spn) > 0:
3159 raise CommandError("--add-spn-suffix not allowed together with DOMAIN")
3160 if len(delete_spn) > 0:
3161 raise CommandError("--delete-spn-suffix not allowed together with DOMAIN")
3163 if refresh is not None:
3164 if refresh == "store":
3165 require_update = True
3167 if enable_all and refresh != "store":
3168 raise CommandError("--enable-all not allowed together with --refresh=%s" % refresh)
3170 if len(enable_tln) > 0:
3171 raise CommandError("--enable-tln not allowed together with --refresh")
3172 if len(disable_tln) > 0:
3173 raise CommandError("--disable-tln not allowed together with --refresh")
3175 if len(add_tln_ex) > 0:
3176 raise CommandError("--add-tln-ex not allowed together with --refresh")
3177 if len(delete_tln_ex) > 0:
3178 raise CommandError("--delete-tln-ex not allowed together with --refresh")
3180 if len(enable_nb) > 0:
3181 raise CommandError("--enable-nb not allowed together with --refresh")
3182 if len(disable_nb) > 0:
3183 raise CommandError("--disable-nb not allowed together with --refresh")
3185 if len(enable_sid_str) > 0:
3186 raise CommandError("--enable-sid not allowed together with --refresh")
3187 if len(disable_sid_str) > 0:
3188 raise CommandError("--disable-sid not allowed together with --refresh")
3191 require_update = True
3193 if len(enable_tln) > 0:
3194 raise CommandError("--enable-tln not allowed together with --enable-all")
3196 if len(enable_nb) > 0:
3197 raise CommandError("--enable-nb not allowed together with --enable-all")
3199 if len(enable_sid) > 0:
3200 raise CommandError("--enable-sid not allowed together with --enable-all")
3202 if len(enable_tln) > 0:
3203 require_update = True
3204 if len(disable_tln) > 0:
3205 require_update = True
3206 for e in enable_tln:
3207 for d in disable_tln:
3208 if e.lower() != d.lower():
3210 raise CommandError("value[%s] specified for --enable-tln and --disable-tln" % e)
3212 if len(add_tln_ex) > 0:
3213 for n in add_tln_ex:
3214 if not n.startswith("*."):
3216 raise CommandError("value[%s] specified for --add-tln-ex should not include with '*.'" % n)
3217 require_update = True
3218 if len(delete_tln_ex) > 0:
3219 for n in delete_tln_ex:
3220 if not n.startswith("*."):
3222 raise CommandError("value[%s] specified for --delete-tln-ex should not include with '*.'" % n)
3223 require_update = True
3224 for a in add_tln_ex:
3225 for d in delete_tln_ex:
3226 if a.lower() != d.lower():
3228 raise CommandError("value[%s] specified for --add-tln-ex and --delete-tln-ex" % a)
3230 if len(enable_nb) > 0:
3231 require_update = True
3232 if len(disable_nb) > 0:
3233 require_update = True
3235 for d in disable_nb:
3236 if e.upper() != d.upper():
3238 raise CommandError("value[%s] specified for --enable-nb and --disable-nb" % e)
3241 for s in enable_sid_str:
3243 sid = security.dom_sid(s)
3244 except TypeError as error:
3245 raise CommandError("value[%s] specified for --enable-sid is not a valid SID" % s)
3246 enable_sid.append(sid)
3248 for s in disable_sid_str:
3250 sid = security.dom_sid(s)
3251 except TypeError as error:
3252 raise CommandError("value[%s] specified for --disable-sid is not a valid SID" % s)
3253 disable_sid.append(sid)
3254 if len(enable_sid) > 0:
3255 require_update = True
3256 if len(disable_sid) > 0:
3257 require_update = True
3258 for e in enable_sid:
3259 for d in disable_sid:
3262 raise CommandError("value[%s] specified for --enable-sid and --disable-sid" % e)
3264 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
3266 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
3268 local_server = self.setup_local_server(sambaopts, localdcopts)
3270 local_lsa = self.new_local_lsa_connection()
3271 except RuntimeError as error:
3272 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
3275 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
3276 except RuntimeError as error:
3277 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
3279 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
3280 local_lsa_info.name.string,
3281 local_lsa_info.dns_domain.string,
3282 local_lsa_info.sid))
3286 local_netlogon = self.new_local_netlogon_connection()
3287 except RuntimeError as error:
3288 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3291 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3292 except RuntimeError as error:
3293 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3295 if local_netlogon_info.domain_name != local_netlogon_info.forest_name:
3296 raise CommandError("The local domain [%s] is not the forest root [%s]" % (
3297 local_netlogon_info.domain_name,
3298 local_netlogon_info.forest_name))
3301 # get all information about our own forest
3302 own_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3304 except RuntimeError as error:
3305 if self.check_runtime_error(error, self.WERR_RPC_S_PROCNUM_OUT_OF_RANGE):
3306 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3309 if self.check_runtime_error(error, self.WERR_INVALID_FUNCTION):
3310 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3313 if self.check_runtime_error(error, self.WERR_NERR_ACFNOTLOADED):
3314 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3317 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3319 self.outf.write("Own forest trust information...\n")
3320 self.write_forest_trust_info(own_forest_info,
3321 tln=local_lsa_info.dns_domain.string)
3324 local_samdb = self.new_local_ldap_connection()
3325 except RuntimeError as error:
3326 raise self.LocalRuntimeError(self, error, "failed to connect to SamDB")
3328 local_partitions_dn = "CN=Partitions,%s" % str(local_samdb.get_config_basedn())
3329 attrs = ['uPNSuffixes', 'msDS-SPNSuffixes']
3331 msgs = local_samdb.search(base=local_partitions_dn,
3332 scope=ldb.SCOPE_BASE,
3333 expression="(objectClass=crossRefContainer)",
3335 stored_msg = msgs[0]
3336 except ldb.LdbError as error:
3337 raise self.LocalLdbError(self, error, "failed to search partition dn")
3339 stored_upn_vals = []
3340 if 'uPNSuffixes' in stored_msg:
3341 stored_upn_vals.extend(stored_msg['uPNSuffixes'])
3343 stored_spn_vals = []
3344 if 'msDS-SPNSuffixes' in stored_msg:
3345 stored_spn_vals.extend(stored_msg['msDS-SPNSuffixes'])
3347 self.outf.write("Stored uPNSuffixes attributes[%d]:\n" % len(stored_upn_vals))
3348 for v in stored_upn_vals:
3349 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3350 self.outf.write("Stored msDS-SPNSuffixes attributes[%d]:\n" % len(stored_spn_vals))
3351 for v in stored_spn_vals:
3352 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3354 if not require_update:
3358 update_upn_vals = []
3359 update_upn_vals.extend(stored_upn_vals)
3362 update_spn_vals = []
3363 update_spn_vals.extend(stored_spn_vals)
3367 for i in xrange(0, len(update_upn_vals)):
3368 v = update_upn_vals[i]
3369 if v.lower() != upn.lower():
3374 raise CommandError("Entry already present for value[%s] specified for --add-upn-suffix" % upn)
3375 update_upn_vals.append(upn)
3378 for upn in delete_upn:
3380 for i in xrange(0, len(update_upn_vals)):
3381 v = update_upn_vals[i]
3382 if v.lower() != upn.lower():
3387 raise CommandError("Entry not found for value[%s] specified for --delete-upn-suffix" % upn)
3389 update_upn_vals.pop(idx)
3394 for i in xrange(0, len(update_spn_vals)):
3395 v = update_spn_vals[i]
3396 if v.lower() != spn.lower():
3401 raise CommandError("Entry already present for value[%s] specified for --add-spn-suffix" % spn)
3402 update_spn_vals.append(spn)
3405 for spn in delete_spn:
3407 for i in xrange(0, len(update_spn_vals)):
3408 v = update_spn_vals[i]
3409 if v.lower() != spn.lower():
3414 raise CommandError("Entry not found for value[%s] specified for --delete-spn-suffix" % spn)
3416 update_spn_vals.pop(idx)
3419 self.outf.write("Update uPNSuffixes attributes[%d]:\n" % len(update_upn_vals))
3420 for v in update_upn_vals:
3421 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3422 self.outf.write("Update msDS-SPNSuffixes attributes[%d]:\n" % len(update_spn_vals))
3423 for v in update_spn_vals:
3424 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3426 update_msg = ldb.Message()
3427 update_msg.dn = stored_msg.dn
3430 update_msg['uPNSuffixes'] = ldb.MessageElement(update_upn_vals,
3431 ldb.FLAG_MOD_REPLACE,
3434 update_msg['msDS-SPNSuffixes'] = ldb.MessageElement(update_spn_vals,
3435 ldb.FLAG_MOD_REPLACE,
3438 local_samdb.modify(update_msg)
3439 except ldb.LdbError as error:
3440 raise self.LocalLdbError(self, error, "failed to update partition dn")
3443 stored_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3445 except RuntimeError as error:
3446 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3448 self.outf.write("Stored forest trust information...\n")
3449 self.write_forest_trust_info(stored_forest_info,
3450 tln=local_lsa_info.dns_domain.string)
3454 lsaString = lsa.String()
3455 lsaString.string = domain
3456 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
3457 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
3458 except RuntimeError as error:
3459 if self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
3460 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
3462 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
3464 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
3465 local_tdo_info.netbios_name.string,
3466 local_tdo_info.domain_name.string,
3467 local_tdo_info.sid))
3469 if not local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
3470 raise CommandError("trusted domain object for domain [%s] is not marked as FOREST_TRANSITIVE." % domain)
3472 if refresh is not None:
3474 local_netlogon = self.new_local_netlogon_connection()
3475 except RuntimeError as error:
3476 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3479 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3480 except RuntimeError as error:
3481 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3483 lsa_update_check = 1
3484 if refresh == "store":
3485 netlogon_update_tdo = netlogon.DS_GFTI_UPDATE_TDO
3487 lsa_update_check = 0
3489 netlogon_update_tdo = 0
3492 # get all information about the remote trust
3493 # this triggers netr_GetForestTrustInformation to the remote domain
3494 # and lsaRSetForestTrustInformation() locally, but new top level
3495 # names are disabled by default.
3496 fresh_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3497 local_tdo_info.domain_name.string,
3498 netlogon_update_tdo)
3499 except RuntimeError as error:
3500 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3503 fresh_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3504 local_tdo_info.domain_name,
3505 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3508 except RuntimeError as error:
3509 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3511 self.outf.write("Fresh forest trust information...\n")
3512 self.write_forest_trust_info(fresh_forest_info,
3513 tln=local_tdo_info.domain_name.string,
3514 collisions=fresh_forest_collision)
3516 if refresh == "store":
3518 lsaString = lsa.String()
3519 lsaString.string = local_tdo_info.domain_name.string
3520 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3522 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3523 except RuntimeError as error:
3524 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3526 self.outf.write("Stored forest trust information...\n")
3527 self.write_forest_trust_info(stored_forest_info,
3528 tln=local_tdo_info.domain_name.string)
3533 # The none --refresh path
3537 lsaString = lsa.String()
3538 lsaString.string = local_tdo_info.domain_name.string
3539 local_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3541 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3542 except RuntimeError as error:
3543 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3545 self.outf.write("Local forest trust information...\n")
3546 self.write_forest_trust_info(local_forest_info,
3547 tln=local_tdo_info.domain_name.string)
3549 if not require_update:
3553 entries.extend(local_forest_info.entries)
3554 update_forest_info = lsa.ForestTrustInformation()
3555 update_forest_info.count = len(entries)
3556 update_forest_info.entries = entries
3559 for i in xrange(0, len(update_forest_info.entries)):
3560 r = update_forest_info.entries[i]
3561 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3563 if update_forest_info.entries[i].flags == 0:
3565 update_forest_info.entries[i].time = 0
3566 update_forest_info.entries[i].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3567 for i in xrange(0, len(update_forest_info.entries)):
3568 r = update_forest_info.entries[i]
3569 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3571 if update_forest_info.entries[i].flags == 0:
3573 update_forest_info.entries[i].time = 0
3574 update_forest_info.entries[i].flags &= ~lsa.LSA_NB_DISABLED_MASK
3575 update_forest_info.entries[i].flags &= ~lsa.LSA_SID_DISABLED_MASK
3577 for tln in enable_tln:
3579 for i in xrange(0, len(update_forest_info.entries)):
3580 r = update_forest_info.entries[i]
3581 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3583 if r.forest_trust_data.string.lower() != tln.lower():
3588 raise CommandError("Entry not found for value[%s] specified for --enable-tln" % tln)
3589 if not update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_MASK:
3590 raise CommandError("Entry found for value[%s] specified for --enable-tln is already enabled" % tln)
3591 update_forest_info.entries[idx].time = 0
3592 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3594 for tln in disable_tln:
3596 for i in xrange(0, len(update_forest_info.entries)):
3597 r = update_forest_info.entries[i]
3598 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3600 if r.forest_trust_data.string.lower() != tln.lower():
3605 raise CommandError("Entry not found for value[%s] specified for --disable-tln" % tln)
3606 if update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_ADMIN:
3607 raise CommandError("Entry found for value[%s] specified for --disable-tln is already disabled" % tln)
3608 update_forest_info.entries[idx].time = 0
3609 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3610 update_forest_info.entries[idx].flags |= lsa.LSA_TLN_DISABLED_ADMIN
3612 for tln_ex in add_tln_ex:
3614 for i in xrange(0, len(update_forest_info.entries)):
3615 r = update_forest_info.entries[i]
3616 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3618 if r.forest_trust_data.string.lower() != tln_ex.lower():
3623 raise CommandError("Entry already present for value[%s] specified for --add-tln-ex" % tln_ex)
3625 tln_dot = ".%s" % tln_ex.lower()
3627 for i in xrange(0, len(update_forest_info.entries)):
3628 r = update_forest_info.entries[i]
3629 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3631 r_dot = ".%s" % r.forest_trust_data.string.lower()
3632 if tln_dot == r_dot:
3633 raise CommandError("TLN entry present for value[%s] specified for --add-tln-ex" % tln_ex)
3634 if not tln_dot.endswith(r_dot):
3640 raise CommandError("No TLN parent present for value[%s] specified for --add-tln-ex" % tln_ex)
3642 r = lsa.ForestTrustRecord()
3643 r.type = lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX
3646 r.forest_trust_data.string = tln_ex
3649 entries.extend(update_forest_info.entries)
3650 entries.insert(idx + 1, r)
3651 update_forest_info.count = len(entries)
3652 update_forest_info.entries = entries
3654 for tln_ex in delete_tln_ex:
3656 for i in xrange(0, len(update_forest_info.entries)):
3657 r = update_forest_info.entries[i]
3658 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3660 if r.forest_trust_data.string.lower() != tln_ex.lower():
3665 raise CommandError("Entry not found for value[%s] specified for --delete-tln-ex" % tln_ex)
3668 entries.extend(update_forest_info.entries)
3670 update_forest_info.count = len(entries)
3671 update_forest_info.entries = entries
3673 for nb in enable_nb:
3675 for i in xrange(0, len(update_forest_info.entries)):
3676 r = update_forest_info.entries[i]
3677 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3679 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3684 raise CommandError("Entry not found for value[%s] specified for --enable-nb" % nb)
3685 if not update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_MASK:
3686 raise CommandError("Entry found for value[%s] specified for --enable-nb is already enabled" % nb)
3687 update_forest_info.entries[idx].time = 0
3688 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3690 for nb in disable_nb:
3692 for i in xrange(0, len(update_forest_info.entries)):
3693 r = update_forest_info.entries[i]
3694 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3696 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3701 raise CommandError("Entry not found for value[%s] specified for --delete-nb" % nb)
3702 if update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_ADMIN:
3703 raise CommandError("Entry found for value[%s] specified for --disable-nb is already disabled" % nb)
3704 update_forest_info.entries[idx].time = 0
3705 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3706 update_forest_info.entries[idx].flags |= lsa.LSA_NB_DISABLED_ADMIN
3708 for sid in enable_sid:
3710 for i in xrange(0, len(update_forest_info.entries)):
3711 r = update_forest_info.entries[i]
3712 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3714 if r.forest_trust_data.domain_sid != sid:
3719 raise CommandError("Entry not found for value[%s] specified for --enable-sid" % sid)
3720 if not update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_MASK:
3721 raise CommandError("Entry found for value[%s] specified for --enable-sid is already enabled" % nb)
3722 update_forest_info.entries[idx].time = 0
3723 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3725 for sid in disable_sid:
3727 for i in xrange(0, len(update_forest_info.entries)):
3728 r = update_forest_info.entries[i]
3729 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3731 if r.forest_trust_data.domain_sid != sid:
3736 raise CommandError("Entry not found for value[%s] specified for --delete-sid" % sid)
3737 if update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_ADMIN:
3738 raise CommandError("Entry found for value[%s] specified for --disable-sid is already disabled" % nb)
3739 update_forest_info.entries[idx].time = 0
3740 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3741 update_forest_info.entries[idx].flags |= lsa.LSA_SID_DISABLED_ADMIN
3744 update_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3745 local_tdo_info.domain_name,
3746 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3747 update_forest_info, 0)
3748 except RuntimeError as error:
3749 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3751 self.outf.write("Updated forest trust information...\n")
3752 self.write_forest_trust_info(update_forest_info,
3753 tln=local_tdo_info.domain_name.string,
3754 collisions=update_forest_collision)
3757 lsaString = lsa.String()
3758 lsaString.string = local_tdo_info.domain_name.string
3759 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3761 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3762 except RuntimeError as error:
3763 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3765 self.outf.write("Stored forest trust information...\n")
3766 self.write_forest_trust_info(stored_forest_info,
3767 tln=local_tdo_info.domain_name.string)
3770 class cmd_domain_tombstones_expunge(Command):
3771 """Expunge tombstones from the database.
3773 This command expunges tombstones from the database."""
3774 synopsis = "%prog NC [NC [...]] [options]"
3777 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
3778 metavar="URL", dest="H"),
3779 Option("--current-time",
3780 help="The current time to evaluate the tombstone lifetime from, expressed as YYYY-MM-DD",
3782 Option("--tombstone-lifetime", help="Number of days a tombstone should be preserved for", type=int),
3785 takes_args = ["nc*"]
3787 takes_optiongroups = {
3788 "sambaopts": options.SambaOptions,
3789 "credopts": options.CredentialsOptions,
3790 "versionopts": options.VersionOptions,
3793 def run(self, *ncs, **kwargs):
3794 sambaopts = kwargs.get("sambaopts")
3795 credopts = kwargs.get("credopts")
3796 versionpts = kwargs.get("versionopts")
3798 current_time_string = kwargs.get("current_time")
3799 tombstone_lifetime = kwargs.get("tombstone_lifetime")
3800 lp = sambaopts.get_loadparm()
3801 creds = credopts.get_credentials(lp)
3802 samdb = SamDB(url=H, session_info=system_session(),
3803 credentials=creds, lp=lp)
3805 if current_time_string is not None:
3806 current_time_obj = time.strptime(current_time_string, "%Y-%m-%d")
3807 current_time = long(time.mktime(current_time_obj))
3810 current_time = long(time.time())
3813 res = samdb.search(expression="", base="", scope=ldb.SCOPE_BASE,
3814 attrs=["namingContexts"])
3817 for nc in res[0]["namingContexts"]:
3822 started_transaction = False
3824 samdb.transaction_start()
3825 started_transaction = True
3827 removed_links) = samdb.garbage_collect_tombstones(ncs,
3828 current_time=current_time,
3829 tombstone_lifetime=tombstone_lifetime)
3831 except Exception, err:
3832 if started_transaction:
3833 samdb.transaction_cancel()
3834 raise CommandError("Failed to expunge / garbage collect tombstones", err)
3836 samdb.transaction_commit()
3838 self.outf.write("Removed %d objects and %d links successfully\n"
3839 % (removed_objects, removed_links))
3843 class cmd_domain_trust(SuperCommand):
3844 """Domain and forest trust management."""
3847 subcommands["list"] = cmd_domain_trust_list()
3848 subcommands["show"] = cmd_domain_trust_show()
3849 subcommands["create"] = cmd_domain_trust_create()
3850 subcommands["delete"] = cmd_domain_trust_delete()
3851 subcommands["validate"] = cmd_domain_trust_validate()
3852 subcommands["namespaces"] = cmd_domain_trust_namespaces()
3854 class cmd_domain_tombstones(SuperCommand):
3855 """Domain tombstone and recycled object management."""
3858 subcommands["expunge"] = cmd_domain_tombstones_expunge()
3860 class cmd_domain(SuperCommand):
3861 """Domain management."""
3864 subcommands["demote"] = cmd_domain_demote()
3865 if cmd_domain_export_keytab is not None:
3866 subcommands["exportkeytab"] = cmd_domain_export_keytab()
3867 subcommands["info"] = cmd_domain_info()
3868 subcommands["provision"] = cmd_domain_provision()
3869 subcommands["join"] = cmd_domain_join()
3870 subcommands["dcpromo"] = cmd_domain_dcpromo()
3871 subcommands["level"] = cmd_domain_level()
3872 subcommands["passwordsettings"] = cmd_domain_passwordsettings()
3873 subcommands["classicupgrade"] = cmd_domain_classicupgrade()
3874 subcommands["samba3upgrade"] = cmd_domain_samba3upgrade()
3875 subcommands["trust"] = cmd_domain_trust()
3876 subcommands["tombstones"] = cmd_domain_tombstones()