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 samba import ntstatus
37 from samba import NTSTATUSError
38 from samba import werror
39 from getpass import getpass
40 from samba.net import Net, LIBNET_JOIN_AUTOMATIC
42 from samba.join import join_RODC, join_DC, join_subdomain
43 from samba.auth import system_session
44 from samba.samdb import SamDB
45 from samba.ndr import ndr_unpack, ndr_pack, ndr_print
46 from samba.dcerpc import drsuapi
47 from samba.dcerpc import drsblobs
48 from samba.dcerpc import lsa
49 from samba.dcerpc import netlogon
50 from samba.dcerpc import security
51 from samba.dcerpc import nbt
52 from samba.dcerpc import misc
53 from samba.dcerpc.samr import DOMAIN_PASSWORD_COMPLEX, DOMAIN_PASSWORD_STORE_CLEARTEXT
54 from samba.netcmd import (
60 from samba.netcmd.common import netcmd_get_domain_infos_via_cldap
61 from samba.samba3 import Samba3
62 from samba.samba3 import param as s3param
63 from samba.upgrade import upgrade_from_samba3
64 from samba.drs_utils import (
65 sendDsReplicaSync, drsuapi_connect, drsException,
67 from samba import remove_dc, arcfour_encrypt, string_to_byte_array
69 from samba.dsdb import (
70 DS_DOMAIN_FUNCTION_2000,
71 DS_DOMAIN_FUNCTION_2003,
72 DS_DOMAIN_FUNCTION_2003_MIXED,
73 DS_DOMAIN_FUNCTION_2008,
74 DS_DOMAIN_FUNCTION_2008_R2,
75 DS_DOMAIN_FUNCTION_2012,
76 DS_DOMAIN_FUNCTION_2012_R2,
77 DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL,
78 DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL,
79 UF_WORKSTATION_TRUST_ACCOUNT,
80 UF_SERVER_TRUST_ACCOUNT,
81 UF_TRUSTED_FOR_DELEGATION,
82 UF_PARTIAL_SECRETS_ACCOUNT
85 from samba.provision import (
90 from samba.provision.common import (
96 def get_testparm_var(testparm, smbconf, varname):
97 errfile = open(os.devnull, 'w')
98 p = subprocess.Popen([testparm, '-s', '-l',
99 '--parameter-name=%s' % varname, smbconf],
100 stdout=subprocess.PIPE, stderr=errfile)
101 (out,err) = p.communicate()
103 lines = out.split('\n')
105 return lines[0].strip()
109 import samba.dckeytab
111 cmd_domain_export_keytab = None
113 class cmd_domain_export_keytab(Command):
114 """Dump Kerberos keys of the domain into a keytab."""
116 synopsis = "%prog <keytab> [options]"
118 takes_optiongroups = {
119 "sambaopts": options.SambaOptions,
120 "credopts": options.CredentialsOptions,
121 "versionopts": options.VersionOptions,
125 Option("--principal", help="extract only this principal", type=str),
128 takes_args = ["keytab"]
130 def run(self, keytab, credopts=None, sambaopts=None, versionopts=None, principal=None):
131 lp = sambaopts.get_loadparm()
133 net.export_keytab(keytab=keytab, principal=principal)
136 class cmd_domain_info(Command):
137 """Print basic info about a domain and the DC passed as parameter."""
139 synopsis = "%prog <ip_address> [options]"
144 takes_optiongroups = {
145 "sambaopts": options.SambaOptions,
146 "credopts": options.CredentialsOptions,
147 "versionopts": options.VersionOptions,
150 takes_args = ["address"]
152 def run(self, address, credopts=None, sambaopts=None, versionopts=None):
153 lp = sambaopts.get_loadparm()
155 res = netcmd_get_domain_infos_via_cldap(lp, None, address)
157 raise CommandError("Invalid IP address '" + address + "'!")
158 self.outf.write("Forest : %s\n" % res.forest)
159 self.outf.write("Domain : %s\n" % res.dns_domain)
160 self.outf.write("Netbios domain : %s\n" % res.domain_name)
161 self.outf.write("DC name : %s\n" % res.pdc_dns_name)
162 self.outf.write("DC netbios name : %s\n" % res.pdc_name)
163 self.outf.write("Server site : %s\n" % res.server_site)
164 self.outf.write("Client site : %s\n" % res.client_site)
167 class cmd_domain_provision(Command):
168 """Provision a domain."""
170 synopsis = "%prog [options]"
172 takes_optiongroups = {
173 "sambaopts": options.SambaOptions,
174 "versionopts": options.VersionOptions,
178 Option("--interactive", help="Ask for names", action="store_true"),
179 Option("--domain", type="string", metavar="DOMAIN",
180 help="NetBIOS domain name to use"),
181 Option("--domain-guid", type="string", metavar="GUID",
182 help="set domainguid (otherwise random)"),
183 Option("--domain-sid", type="string", metavar="SID",
184 help="set domainsid (otherwise random)"),
185 Option("--ntds-guid", type="string", metavar="GUID",
186 help="set NTDS object GUID (otherwise random)"),
187 Option("--invocationid", type="string", metavar="GUID",
188 help="set invocationid (otherwise random)"),
189 Option("--host-name", type="string", metavar="HOSTNAME",
190 help="set hostname"),
191 Option("--host-ip", type="string", metavar="IPADDRESS",
192 help="set IPv4 ipaddress"),
193 Option("--host-ip6", type="string", metavar="IP6ADDRESS",
194 help="set IPv6 ipaddress"),
195 Option("--site", type="string", metavar="SITENAME",
196 help="set site name"),
197 Option("--adminpass", type="string", metavar="PASSWORD",
198 help="choose admin password (otherwise random)"),
199 Option("--krbtgtpass", type="string", metavar="PASSWORD",
200 help="choose krbtgt password (otherwise random)"),
201 Option("--machinepass", type="string", metavar="PASSWORD",
202 help="choose machine password (otherwise random)"),
203 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
204 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
205 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
206 "BIND9_FLATFILE uses bind9 text database to store zone information, "
207 "BIND9_DLZ uses samba4 AD to store zone information, "
208 "NONE skips the DNS setup entirely (not recommended)",
209 default="SAMBA_INTERNAL"),
210 Option("--dnspass", type="string", metavar="PASSWORD",
211 help="choose dns password (otherwise random)"),
212 Option("--ldapadminpass", type="string", metavar="PASSWORD",
213 help="choose password to set between Samba and its LDAP backend (otherwise random)"),
214 Option("--root", type="string", metavar="USERNAME",
215 help="choose 'root' unix username"),
216 Option("--nobody", type="string", metavar="USERNAME",
217 help="choose 'nobody' user"),
218 Option("--users", type="string", metavar="GROUPNAME",
219 help="choose 'users' group"),
220 Option("--quiet", help="Be quiet", action="store_true"),
221 Option("--blank", action="store_true",
222 help="do not add users or groups, just the structure"),
223 Option("--ldap-backend-type", type="choice", metavar="LDAP-BACKEND-TYPE",
224 help="Test initialisation support for unsupported LDAP backend type (fedora-ds or openldap) DO NOT USE",
225 choices=["fedora-ds", "openldap"]),
226 Option("--server-role", type="choice", metavar="ROLE",
227 choices=["domain controller", "dc", "member server", "member", "standalone"],
228 help="The server role (domain controller | dc | member server | member | standalone). Default is dc.",
229 default="domain controller"),
230 Option("--function-level", type="choice", metavar="FOR-FUN-LEVEL",
231 choices=["2000", "2003", "2008", "2008_R2"],
232 help="The domain and forest function level (2000 | 2003 | 2008 | 2008_R2 - always native). Default is (Windows) 2008_R2 Native.",
234 Option("--next-rid", type="int", metavar="NEXTRID", default=1000,
235 help="The initial nextRid value (only needed for upgrades). Default is 1000."),
236 Option("--partitions-only",
237 help="Configure Samba's partitions, but do not modify them (ie, join a BDC)", action="store_true"),
238 Option("--targetdir", type="string", metavar="DIR",
239 help="Set target directory"),
240 Option("--ol-mmr-urls", type="string", metavar="LDAPSERVER",
241 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\""),
242 Option("--use-rfc2307", action="store_true", help="Use AD to store posix attributes (default = no)"),
246 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",
247 action="store_true"),
248 Option("--slapd-path", type="string", metavar="SLAPD-PATH",
249 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."),
250 Option("--ldap-backend-extra-port", type="int", metavar="LDAP-BACKEND-EXTRA-PORT", help="Additional TCP port for LDAP backend server (to use for replication)"),
251 Option("--ldap-backend-forced-uri", type="string", metavar="LDAP-BACKEND-FORCED-URI",
252 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"),
253 Option("--ldap-backend-nosync", help="Configure LDAP backend not to call fsync() (for performance in test environments)", action="store_true"),
257 Option("--use-ntvfs", action="store_true", help="Use NTVFS for the fileserver (default = no)"),
258 Option("--use-xattrs", type="choice", choices=["yes","no","auto"],
259 metavar="[yes|no|auto]",
260 help="Define if we should use the native fs capabilities or a tdb file for "
261 "storing attributes likes ntacl when --use-ntvfs is set. "
262 "auto tries to make an inteligent guess based on the user rights and system capabilities",
266 if os.getenv('TEST_LDAP', "no") == "yes":
267 takes_options.extend(openldap_options)
269 if samba.is_ntvfs_fileserver_built():
270 takes_options.extend(ntvfs_options)
274 def run(self, sambaopts=None, versionopts=None,
297 ldap_backend_type=None,
301 partitions_only=None,
308 ldap_backend_nosync=None,
309 ldap_backend_extra_port=None,
310 ldap_backend_forced_uri=None,
311 ldap_dryrun_mode=None):
313 self.logger = self.get_logger("provision")
315 self.logger.setLevel(logging.WARNING)
317 self.logger.setLevel(logging.INFO)
319 lp = sambaopts.get_loadparm()
320 smbconf = lp.configfile
322 if dns_forwarder is not None:
323 suggested_forwarder = dns_forwarder
325 suggested_forwarder = self._get_nameserver_ip()
326 if suggested_forwarder is None:
327 suggested_forwarder = "none"
329 if len(self.raw_argv) == 1:
333 from getpass import getpass
336 def ask(prompt, default=None):
337 if default is not None:
338 print "%s [%s]: " % (prompt, default),
340 print "%s: " % (prompt,),
341 return sys.stdin.readline().rstrip("\n") or default
344 default = socket.getfqdn().split(".", 1)[1].upper()
347 realm = ask("Realm", default)
348 if realm in (None, ""):
349 raise CommandError("No realm set!")
352 default = realm.split(".")[0]
355 domain = ask("Domain", default)
357 raise CommandError("No domain set!")
359 server_role = ask("Server Role (dc, member, standalone)", "dc")
361 dns_backend = ask("DNS backend (SAMBA_INTERNAL, BIND9_FLATFILE, BIND9_DLZ, NONE)", "SAMBA_INTERNAL")
362 if dns_backend in (None, ''):
363 raise CommandError("No DNS backend set!")
365 if dns_backend == "SAMBA_INTERNAL":
366 dns_forwarder = ask("DNS forwarder IP address (write 'none' to disable forwarding)", suggested_forwarder)
367 if dns_forwarder.lower() in (None, 'none'):
368 suggested_forwarder = None
372 adminpassplain = getpass("Administrator password: ")
373 if not adminpassplain:
374 self.errf.write("Invalid administrator password.\n")
376 adminpassverify = getpass("Retype password: ")
377 if not adminpassplain == adminpassverify:
378 self.errf.write("Sorry, passwords do not match.\n")
380 adminpass = adminpassplain
384 realm = sambaopts._lp.get('realm')
386 raise CommandError("No realm set!")
388 raise CommandError("No domain set!")
391 self.logger.info("Administrator password will be set randomly!")
393 if function_level == "2000":
394 dom_for_fun_level = DS_DOMAIN_FUNCTION_2000
395 elif function_level == "2003":
396 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
397 elif function_level == "2008":
398 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008
399 elif function_level == "2008_R2":
400 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
402 if dns_backend == "SAMBA_INTERNAL" and dns_forwarder is None:
403 dns_forwarder = suggested_forwarder
405 samdb_fill = FILL_FULL
407 samdb_fill = FILL_NT4SYNC
408 elif partitions_only:
409 samdb_fill = FILL_DRS
411 if targetdir is not None:
412 if not os.path.isdir(targetdir):
417 if use_xattrs == "yes":
419 elif use_xattrs == "auto" and use_ntvfs == False:
421 elif use_ntvfs == False:
422 raise CommandError("--use-xattrs=no requires --use-ntvfs (not supported for production use). "
423 "Please re-run with --use-xattrs omitted.")
424 elif use_xattrs == "auto" and not lp.get("posix:eadb"):
426 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
428 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
431 samba.ntacls.setntacl(lp, file.name,
432 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
435 self.logger.info("You are not root or your system does not support xattr, using tdb backend for attributes. ")
440 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.")
441 if ldap_backend_type == "existing":
442 if ldap_backend_forced_uri is not None:
443 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)
445 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")
447 if ldap_backend_forced_uri is not None:
448 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")
450 if domain_sid is not None:
451 domain_sid = security.dom_sid(domain_sid)
453 session = system_session()
455 result = provision(self.logger,
456 session, smbconf=smbconf, targetdir=targetdir,
457 samdb_fill=samdb_fill, realm=realm, domain=domain,
458 domainguid=domain_guid, domainsid=domain_sid,
460 hostip=host_ip, hostip6=host_ip6,
461 sitename=site, ntdsguid=ntds_guid,
462 invocationid=invocationid, adminpass=adminpass,
463 krbtgtpass=krbtgtpass, machinepass=machinepass,
464 dns_backend=dns_backend, dns_forwarder=dns_forwarder,
465 dnspass=dnspass, root=root, nobody=nobody,
467 serverrole=server_role, dom_for_fun_level=dom_for_fun_level,
468 backend_type=ldap_backend_type,
469 ldapadminpass=ldapadminpass, ol_mmr_urls=ol_mmr_urls, slapd_path=slapd_path,
470 useeadb=eadb, next_rid=next_rid, lp=lp, use_ntvfs=use_ntvfs,
471 use_rfc2307=use_rfc2307, skip_sysvolacl=False,
472 ldap_backend_extra_port=ldap_backend_extra_port,
473 ldap_backend_forced_uri=ldap_backend_forced_uri,
474 nosync=ldap_backend_nosync, ldap_dryrun_mode=ldap_dryrun_mode)
476 except ProvisioningError, e:
477 raise CommandError("Provision failed", e)
479 result.report_logger(self.logger)
481 def _get_nameserver_ip(self):
482 """Grab the nameserver IP address from /etc/resolv.conf."""
484 RESOLV_CONF="/etc/resolv.conf"
486 if not path.isfile(RESOLV_CONF):
487 self.logger.warning("Failed to locate %s" % RESOLV_CONF)
492 handle = open(RESOLV_CONF, 'r')
494 if not line.startswith('nameserver'):
496 # we want the last non-space continuous string of the line
497 return line.strip().split()[-1]
499 if handle is not None:
502 self.logger.warning("No nameserver found in %s" % RESOLV_CONF)
505 class cmd_domain_dcpromo(Command):
506 """Promote an existing domain member or NT4 PDC to an AD DC."""
508 synopsis = "%prog <dnsdomain> [DC|RODC] [options]"
510 takes_optiongroups = {
511 "sambaopts": options.SambaOptions,
512 "versionopts": options.VersionOptions,
513 "credopts": options.CredentialsOptions,
517 Option("--server", help="DC to join", type=str),
518 Option("--site", help="site to join", type=str),
519 Option("--targetdir", help="where to store provision", type=str),
520 Option("--domain-critical-only",
521 help="only replicate critical domain objects",
522 action="store_true"),
523 Option("--machinepass", type=str, metavar="PASSWORD",
524 help="choose machine password (otherwise random)"),
525 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
526 choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
527 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
528 "BIND9_DLZ uses samba4 AD to store zone information, "
529 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
530 default="SAMBA_INTERNAL"),
531 Option("--quiet", help="Be quiet", action="store_true"),
532 Option("--verbose", help="Be verbose", action="store_true")
536 Option("--use-ntvfs", action="store_true", help="Use NTVFS for the fileserver (default = no)"),
539 if samba.is_ntvfs_fileserver_built():
540 takes_options.extend(ntvfs_options)
543 takes_args = ["domain", "role?"]
545 def run(self, domain, role=None, sambaopts=None, credopts=None,
546 versionopts=None, server=None, site=None, targetdir=None,
547 domain_critical_only=False, parent_domain=None, machinepass=None,
548 use_ntvfs=False, dns_backend=None,
549 quiet=False, verbose=False):
550 lp = sambaopts.get_loadparm()
551 creds = credopts.get_credentials(lp)
552 net = Net(creds, lp, server=credopts.ipaddress)
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")
776 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 == werror.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"]))
822 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
824 "Error while demoting, re-enabling inbound replication\n")
825 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
826 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
828 raise CommandError("Error while changing account control", e)
831 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
833 "Error while demoting, re-enabling inbound replication")
834 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
835 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
837 raise CommandError("Unable to find object with samaccountName = %s$"
838 " in the remote dc" % netbios_name.upper())
842 uac &= ~(UF_SERVER_TRUST_ACCOUNT|UF_TRUSTED_FOR_DELEGATION|UF_PARTIAL_SECRETS_ACCOUNT)
843 uac |= UF_WORKSTATION_TRUST_ACCOUNT
848 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
849 ldb.FLAG_MOD_REPLACE,
850 "userAccountControl")
852 remote_samdb.modify(msg)
854 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
856 "Error while demoting, re-enabling inbound replication")
857 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
858 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
861 raise CommandError("Error while changing account control", e)
863 parent = msg.dn.parent()
864 dc_name = res[0].dn.get_rdn_value()
865 rdn = "CN=%s" % dc_name
867 # Let's move to the Computer container
871 computer_dn = ldb.Dn(remote_samdb, "CN=Computers,%s" % str(remote_samdb.domain_dn()))
872 res = remote_samdb.search(base=computer_dn, expression=rdn, scope=ldb.SCOPE_ONELEVEL)
875 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
876 scope=ldb.SCOPE_ONELEVEL)
877 while(len(res) != 0 and i < 100):
879 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
880 scope=ldb.SCOPE_ONELEVEL)
883 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
885 "Error while demoting, re-enabling inbound replication\n")
886 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
887 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
893 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
894 ldb.FLAG_MOD_REPLACE,
895 "userAccountControl")
897 remote_samdb.modify(msg)
899 raise CommandError("Unable to find a slot for renaming %s,"
900 " all names from %s-1 to %s-%d seemed used" %
901 (str(dc_dn), rdn, rdn, i - 9))
903 newrdn = "%s-%d" % (rdn, i)
906 newdn = ldb.Dn(remote_samdb, "%s,%s" % (newrdn, str(computer_dn)))
907 remote_samdb.rename(dc_dn, newdn)
909 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
911 "Error while demoting, re-enabling inbound replication\n")
912 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
913 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
919 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
920 ldb.FLAG_MOD_REPLACE,
921 "userAccountControl")
923 remote_samdb.modify(msg)
924 raise CommandError("Error while renaming %s to %s" % (str(dc_dn), str(newdn)), e)
927 server_dsa_dn = samdb.get_serverName()
928 domain = remote_samdb.get_root_basedn()
931 req1 = drsuapi.DsRemoveDSServerRequest1()
932 req1.server_dn = str(server_dsa_dn)
933 req1.domain_dn = str(domain)
936 drsuapiBind.DsRemoveDSServer(drsuapi_handle, 1, req1)
937 except RuntimeError as (werr, string):
938 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
940 "Error while demoting, re-enabling inbound replication\n")
941 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
942 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
948 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
949 ldb.FLAG_MOD_REPLACE,
950 "userAccountControl")
951 remote_samdb.modify(msg)
952 remote_samdb.rename(newdn, dc_dn)
953 if werr == werror.WERR_DS_DRA_NO_REPLICA:
954 raise CommandError("The DC %s is not present on (already removed from) the remote server: " % server_dsa_dn, e)
956 raise CommandError("Error while sending a removeDsServer of %s: " % server_dsa_dn, e)
958 remove_dc.remove_sysvol_references(remote_samdb, logger, dc_name)
960 # These are objects under the computer account that should be deleted
961 for s in ("CN=Enterprise,CN=NTFRS Subscriptions",
962 "CN=%s, CN=NTFRS Subscriptions" % lp.get("realm"),
963 "CN=Domain system Volumes (SYSVOL Share), CN=NTFRS Subscriptions",
964 "CN=NTFRS Subscriptions"):
966 remote_samdb.delete(ldb.Dn(remote_samdb,
967 "%s,%s" % (s, str(newdn))))
968 except ldb.LdbError, l:
971 self.errf.write("Demote successful\n")
974 class cmd_domain_level(Command):
975 """Raise domain and forest function levels."""
977 synopsis = "%prog (show|raise <options>) [options]"
979 takes_optiongroups = {
980 "sambaopts": options.SambaOptions,
981 "credopts": options.CredentialsOptions,
982 "versionopts": options.VersionOptions,
986 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
987 metavar="URL", dest="H"),
988 Option("--quiet", help="Be quiet", action="store_true"),
989 Option("--forest-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
990 help="The forest function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)"),
991 Option("--domain-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
992 help="The domain function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)")
995 takes_args = ["subcommand"]
997 def run(self, subcommand, H=None, forest_level=None, domain_level=None,
998 quiet=False, credopts=None, sambaopts=None, versionopts=None):
999 lp = sambaopts.get_loadparm()
1000 creds = credopts.get_credentials(lp, fallback_machine=True)
1002 samdb = SamDB(url=H, session_info=system_session(),
1003 credentials=creds, lp=lp)
1005 domain_dn = samdb.domain_dn()
1007 res_forest = samdb.search("CN=Partitions,%s" % samdb.get_config_basedn(),
1008 scope=ldb.SCOPE_BASE, attrs=["msDS-Behavior-Version"])
1009 assert len(res_forest) == 1
1011 res_domain = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1012 attrs=["msDS-Behavior-Version", "nTMixedDomain"])
1013 assert len(res_domain) == 1
1015 res_dc_s = samdb.search("CN=Sites,%s" % samdb.get_config_basedn(),
1016 scope=ldb.SCOPE_SUBTREE, expression="(objectClass=nTDSDSA)",
1017 attrs=["msDS-Behavior-Version"])
1018 assert len(res_dc_s) >= 1
1020 # default values, since "msDS-Behavior-Version" does not exist on Windows 2000 AD
1021 level_forest = DS_DOMAIN_FUNCTION_2000
1022 level_domain = DS_DOMAIN_FUNCTION_2000
1024 if "msDS-Behavior-Version" in res_forest[0]:
1025 level_forest = int(res_forest[0]["msDS-Behavior-Version"][0])
1026 if "msDS-Behavior-Version" in res_domain[0]:
1027 level_domain = int(res_domain[0]["msDS-Behavior-Version"][0])
1028 level_domain_mixed = int(res_domain[0]["nTMixedDomain"][0])
1031 for msg in res_dc_s:
1032 if "msDS-Behavior-Version" in msg:
1033 if min_level_dc is None or int(msg["msDS-Behavior-Version"][0]) < min_level_dc:
1034 min_level_dc = int(msg["msDS-Behavior-Version"][0])
1036 min_level_dc = DS_DOMAIN_FUNCTION_2000
1037 # well, this is the least
1040 if level_forest < DS_DOMAIN_FUNCTION_2000 or level_domain < DS_DOMAIN_FUNCTION_2000:
1041 raise CommandError("Domain and/or forest function level(s) is/are invalid. Correct them or reprovision!")
1042 if min_level_dc < DS_DOMAIN_FUNCTION_2000:
1043 raise CommandError("Lowest function level of a DC is invalid. Correct this or reprovision!")
1044 if level_forest > level_domain:
1045 raise CommandError("Forest function level is higher than the domain level(s). Correct this or reprovision!")
1046 if level_domain > min_level_dc:
1047 raise CommandError("Domain function level is higher than the lowest function level of a DC. Correct this or reprovision!")
1049 if subcommand == "show":
1050 self.message("Domain and forest function level for domain '%s'" % domain_dn)
1051 if level_forest == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1052 self.message("\nATTENTION: You run SAMBA 4 on a forest function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1053 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1054 self.message("\nATTENTION: You run SAMBA 4 on a domain function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1055 if min_level_dc == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1056 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)!")
1060 if level_forest == DS_DOMAIN_FUNCTION_2000:
1062 elif level_forest == DS_DOMAIN_FUNCTION_2003_MIXED:
1063 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1064 elif level_forest == DS_DOMAIN_FUNCTION_2003:
1066 elif level_forest == DS_DOMAIN_FUNCTION_2008:
1068 elif level_forest == DS_DOMAIN_FUNCTION_2008_R2:
1070 elif level_forest == DS_DOMAIN_FUNCTION_2012:
1072 elif level_forest == DS_DOMAIN_FUNCTION_2012_R2:
1075 outstr = "higher than 2012 R2"
1076 self.message("Forest function level: (Windows) " + outstr)
1078 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1079 outstr = "2000 mixed (NT4 DC support)"
1080 elif level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed == 0:
1082 elif level_domain == DS_DOMAIN_FUNCTION_2003_MIXED:
1083 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1084 elif level_domain == DS_DOMAIN_FUNCTION_2003:
1086 elif level_domain == DS_DOMAIN_FUNCTION_2008:
1088 elif level_domain == DS_DOMAIN_FUNCTION_2008_R2:
1090 elif level_domain == DS_DOMAIN_FUNCTION_2012:
1092 elif level_domain == DS_DOMAIN_FUNCTION_2012_R2:
1095 outstr = "higher than 2012 R2"
1096 self.message("Domain function level: (Windows) " + outstr)
1098 if min_level_dc == DS_DOMAIN_FUNCTION_2000:
1100 elif min_level_dc == DS_DOMAIN_FUNCTION_2003:
1102 elif min_level_dc == DS_DOMAIN_FUNCTION_2008:
1104 elif min_level_dc == DS_DOMAIN_FUNCTION_2008_R2:
1106 elif min_level_dc == DS_DOMAIN_FUNCTION_2012:
1108 elif min_level_dc == DS_DOMAIN_FUNCTION_2012_R2:
1111 outstr = "higher than 2012 R2"
1112 self.message("Lowest function level of a DC: (Windows) " + outstr)
1114 elif subcommand == "raise":
1117 if domain_level is not None:
1118 if domain_level == "2003":
1119 new_level_domain = DS_DOMAIN_FUNCTION_2003
1120 elif domain_level == "2008":
1121 new_level_domain = DS_DOMAIN_FUNCTION_2008
1122 elif domain_level == "2008_R2":
1123 new_level_domain = DS_DOMAIN_FUNCTION_2008_R2
1124 elif domain_level == "2012":
1125 new_level_domain = DS_DOMAIN_FUNCTION_2012
1126 elif domain_level == "2012_R2":
1127 new_level_domain = DS_DOMAIN_FUNCTION_2012_R2
1129 if new_level_domain <= level_domain and level_domain_mixed == 0:
1130 raise CommandError("Domain function level can't be smaller than or equal to the actual one!")
1131 if new_level_domain > min_level_dc:
1132 raise CommandError("Domain function level can't be higher than the lowest function level of a DC!")
1134 # Deactivate mixed/interim domain support
1135 if level_domain_mixed != 0:
1136 # Directly on the base DN
1138 m.dn = ldb.Dn(samdb, domain_dn)
1139 m["nTMixedDomain"] = ldb.MessageElement("0",
1140 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1144 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup") + ",CN=Partitions,%s" % samdb.get_config_basedn())
1145 m["nTMixedDomain"] = ldb.MessageElement("0",
1146 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1149 except ldb.LdbError, (enum, emsg):
1150 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1153 # Directly on the base DN
1155 m.dn = ldb.Dn(samdb, domain_dn)
1156 m["msDS-Behavior-Version"]= ldb.MessageElement(
1157 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1158 "msDS-Behavior-Version")
1162 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup")
1163 + ",CN=Partitions,%s" % samdb.get_config_basedn())
1164 m["msDS-Behavior-Version"]= ldb.MessageElement(
1165 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1166 "msDS-Behavior-Version")
1169 except ldb.LdbError, (enum, emsg):
1170 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1173 level_domain = new_level_domain
1174 msgs.append("Domain function level changed!")
1176 if forest_level is not None:
1177 if forest_level == "2003":
1178 new_level_forest = DS_DOMAIN_FUNCTION_2003
1179 elif forest_level == "2008":
1180 new_level_forest = DS_DOMAIN_FUNCTION_2008
1181 elif forest_level == "2008_R2":
1182 new_level_forest = DS_DOMAIN_FUNCTION_2008_R2
1183 elif forest_level == "2012":
1184 new_level_forest = DS_DOMAIN_FUNCTION_2012
1185 elif forest_level == "2012_R2":
1186 new_level_forest = DS_DOMAIN_FUNCTION_2012_R2
1188 if new_level_forest <= level_forest:
1189 raise CommandError("Forest function level can't be smaller than or equal to the actual one!")
1190 if new_level_forest > level_domain:
1191 raise CommandError("Forest function level can't be higher than the domain function level(s). Please raise it/them first!")
1194 m.dn = ldb.Dn(samdb, "CN=Partitions,%s" % samdb.get_config_basedn())
1195 m["msDS-Behavior-Version"]= ldb.MessageElement(
1196 str(new_level_forest), ldb.FLAG_MOD_REPLACE,
1197 "msDS-Behavior-Version")
1199 msgs.append("Forest function level changed!")
1200 msgs.append("All changes applied successfully!")
1201 self.message("\n".join(msgs))
1203 raise CommandError("invalid argument: '%s' (choose from 'show', 'raise')" % subcommand)
1206 class cmd_domain_passwordsettings(Command):
1207 """Set password settings.
1209 Password complexity, password lockout policy, history length,
1210 minimum password length, the minimum and maximum password age) on
1211 a Samba AD DC server.
1213 Use against a Windows DC is possible, but group policy will override it.
1216 synopsis = "%prog (show|set <options>) [options]"
1218 takes_optiongroups = {
1219 "sambaopts": options.SambaOptions,
1220 "versionopts": options.VersionOptions,
1221 "credopts": options.CredentialsOptions,
1225 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1226 metavar="URL", dest="H"),
1227 Option("--quiet", help="Be quiet", action="store_true"),
1228 Option("--complexity", type="choice", choices=["on","off","default"],
1229 help="The password complexity (on | off | default). Default is 'on'"),
1230 Option("--store-plaintext", type="choice", choices=["on","off","default"],
1231 help="Store plaintext passwords where account have 'store passwords with reversible encryption' set (on | off | default). Default is 'off'"),
1232 Option("--history-length",
1233 help="The password history length (<integer> | default). Default is 24.", type=str),
1234 Option("--min-pwd-length",
1235 help="The minimum password length (<integer> | default). Default is 7.", type=str),
1236 Option("--min-pwd-age",
1237 help="The minimum password age (<integer in days> | default). Default is 1.", type=str),
1238 Option("--max-pwd-age",
1239 help="The maximum password age (<integer in days> | default). Default is 43.", type=str),
1240 Option("--account-lockout-duration",
1241 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),
1242 Option("--account-lockout-threshold",
1243 help="The number of bad password attempts allowed before locking out the account (<integer> | default). Default is 0 (never lock out).", type=str),
1244 Option("--reset-account-lockout-after",
1245 help="After this time is elapsed, the recorded number of attempts restarts from zero (<integer> | default). Default is 30.", type=str),
1248 takes_args = ["subcommand"]
1250 def run(self, subcommand, H=None, min_pwd_age=None, max_pwd_age=None,
1251 quiet=False, complexity=None, store_plaintext=None, history_length=None,
1252 min_pwd_length=None, account_lockout_duration=None, account_lockout_threshold=None,
1253 reset_account_lockout_after=None, credopts=None, sambaopts=None,
1255 lp = sambaopts.get_loadparm()
1256 creds = credopts.get_credentials(lp)
1258 samdb = SamDB(url=H, session_info=system_session(),
1259 credentials=creds, lp=lp)
1261 domain_dn = samdb.domain_dn()
1262 res = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1263 attrs=["pwdProperties", "pwdHistoryLength", "minPwdLength",
1264 "minPwdAge", "maxPwdAge", "lockoutDuration", "lockoutThreshold",
1265 "lockOutObservationWindow"])
1266 assert(len(res) == 1)
1268 pwd_props = int(res[0]["pwdProperties"][0])
1269 pwd_hist_len = int(res[0]["pwdHistoryLength"][0])
1270 cur_min_pwd_len = int(res[0]["minPwdLength"][0])
1272 cur_min_pwd_age = int(abs(int(res[0]["minPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1273 if int(res[0]["maxPwdAge"][0]) == -0x8000000000000000:
1276 cur_max_pwd_age = int(abs(int(res[0]["maxPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1277 cur_account_lockout_threshold = int(res[0]["lockoutThreshold"][0])
1279 if int(res[0]["lockoutDuration"][0]) == -0x8000000000000000:
1280 cur_account_lockout_duration = 0
1282 cur_account_lockout_duration = abs(int(res[0]["lockoutDuration"][0])) / (1e7 * 60)
1283 cur_reset_account_lockout_after = abs(int(res[0]["lockOutObservationWindow"][0])) / (1e7 * 60)
1284 except Exception, e:
1285 raise CommandError("Could not retrieve password properties!", e)
1287 if subcommand == "show":
1288 self.message("Password informations for domain '%s'" % domain_dn)
1290 if pwd_props & DOMAIN_PASSWORD_COMPLEX != 0:
1291 self.message("Password complexity: on")
1293 self.message("Password complexity: off")
1294 if pwd_props & DOMAIN_PASSWORD_STORE_CLEARTEXT != 0:
1295 self.message("Store plaintext passwords: on")
1297 self.message("Store plaintext passwords: off")
1298 self.message("Password history length: %d" % pwd_hist_len)
1299 self.message("Minimum password length: %d" % cur_min_pwd_len)
1300 self.message("Minimum password age (days): %d" % cur_min_pwd_age)
1301 self.message("Maximum password age (days): %d" % cur_max_pwd_age)
1302 self.message("Account lockout duration (mins): %d" % cur_account_lockout_duration)
1303 self.message("Account lockout threshold (attempts): %d" % cur_account_lockout_threshold)
1304 self.message("Reset account lockout after (mins): %d" % cur_reset_account_lockout_after)
1305 elif subcommand == "set":
1308 m.dn = ldb.Dn(samdb, domain_dn)
1310 if complexity is not None:
1311 if complexity == "on" or complexity == "default":
1312 pwd_props = pwd_props | DOMAIN_PASSWORD_COMPLEX
1313 msgs.append("Password complexity activated!")
1314 elif complexity == "off":
1315 pwd_props = pwd_props & (~DOMAIN_PASSWORD_COMPLEX)
1316 msgs.append("Password complexity deactivated!")
1318 if store_plaintext is not None:
1319 if store_plaintext == "on" or store_plaintext == "default":
1320 pwd_props = pwd_props | DOMAIN_PASSWORD_STORE_CLEARTEXT
1321 msgs.append("Plaintext password storage for changed passwords activated!")
1322 elif store_plaintext == "off":
1323 pwd_props = pwd_props & (~DOMAIN_PASSWORD_STORE_CLEARTEXT)
1324 msgs.append("Plaintext password storage for changed passwords deactivated!")
1326 if complexity is not None or store_plaintext is not None:
1327 m["pwdProperties"] = ldb.MessageElement(str(pwd_props),
1328 ldb.FLAG_MOD_REPLACE, "pwdProperties")
1330 if history_length is not None:
1331 if history_length == "default":
1334 pwd_hist_len = int(history_length)
1336 if pwd_hist_len < 0 or pwd_hist_len > 24:
1337 raise CommandError("Password history length must be in the range of 0 to 24!")
1339 m["pwdHistoryLength"] = ldb.MessageElement(str(pwd_hist_len),
1340 ldb.FLAG_MOD_REPLACE, "pwdHistoryLength")
1341 msgs.append("Password history length changed!")
1343 if min_pwd_length is not None:
1344 if min_pwd_length == "default":
1347 min_pwd_len = int(min_pwd_length)
1349 if min_pwd_len < 0 or min_pwd_len > 14:
1350 raise CommandError("Minimum password length must be in the range of 0 to 14!")
1352 m["minPwdLength"] = ldb.MessageElement(str(min_pwd_len),
1353 ldb.FLAG_MOD_REPLACE, "minPwdLength")
1354 msgs.append("Minimum password length changed!")
1356 if min_pwd_age is not None:
1357 if min_pwd_age == "default":
1360 min_pwd_age = int(min_pwd_age)
1362 if min_pwd_age < 0 or min_pwd_age > 998:
1363 raise CommandError("Minimum password age must be in the range of 0 to 998!")
1366 min_pwd_age_ticks = -int(min_pwd_age * (24 * 60 * 60 * 1e7))
1368 m["minPwdAge"] = ldb.MessageElement(str(min_pwd_age_ticks),
1369 ldb.FLAG_MOD_REPLACE, "minPwdAge")
1370 msgs.append("Minimum password age changed!")
1372 if max_pwd_age is not None:
1373 if max_pwd_age == "default":
1376 max_pwd_age = int(max_pwd_age)
1378 if max_pwd_age < 0 or max_pwd_age > 999:
1379 raise CommandError("Maximum password age must be in the range of 0 to 999!")
1382 if max_pwd_age == 0:
1383 max_pwd_age_ticks = -0x8000000000000000
1385 max_pwd_age_ticks = -int(max_pwd_age * (24 * 60 * 60 * 1e7))
1387 m["maxPwdAge"] = ldb.MessageElement(str(max_pwd_age_ticks),
1388 ldb.FLAG_MOD_REPLACE, "maxPwdAge")
1389 msgs.append("Maximum password age changed!")
1391 if account_lockout_duration is not None:
1392 if account_lockout_duration == "default":
1393 account_lockout_duration = 30
1395 account_lockout_duration = int(account_lockout_duration)
1397 if account_lockout_duration < 0 or account_lockout_duration > 99999:
1398 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1401 if account_lockout_duration == 0:
1402 account_lockout_duration_ticks = -0x8000000000000000
1404 account_lockout_duration_ticks = -int(account_lockout_duration * (60 * 1e7))
1406 m["lockoutDuration"] = ldb.MessageElement(str(account_lockout_duration_ticks),
1407 ldb.FLAG_MOD_REPLACE, "lockoutDuration")
1408 msgs.append("Account lockout duration changed!")
1410 if account_lockout_threshold is not None:
1411 if account_lockout_threshold == "default":
1412 account_lockout_threshold = 0
1414 account_lockout_threshold = int(account_lockout_threshold)
1416 m["lockoutThreshold"] = ldb.MessageElement(str(account_lockout_threshold),
1417 ldb.FLAG_MOD_REPLACE, "lockoutThreshold")
1418 msgs.append("Account lockout threshold changed!")
1420 if reset_account_lockout_after is not None:
1421 if reset_account_lockout_after == "default":
1422 reset_account_lockout_after = 30
1424 reset_account_lockout_after = int(reset_account_lockout_after)
1426 if reset_account_lockout_after < 0 or reset_account_lockout_after > 99999:
1427 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1430 if reset_account_lockout_after == 0:
1431 reset_account_lockout_after_ticks = -0x8000000000000000
1433 reset_account_lockout_after_ticks = -int(reset_account_lockout_after * (60 * 1e7))
1435 m["lockOutObservationWindow"] = ldb.MessageElement(str(reset_account_lockout_after_ticks),
1436 ldb.FLAG_MOD_REPLACE, "lockOutObservationWindow")
1437 msgs.append("Duration to reset account lockout after changed!")
1439 if max_pwd_age > 0 and min_pwd_age >= max_pwd_age:
1440 raise CommandError("Maximum password age (%d) must be greater than minimum password age (%d)!" % (max_pwd_age, min_pwd_age))
1443 raise CommandError("You must specify at least one option to set. Try --help")
1445 msgs.append("All changes applied successfully!")
1446 self.message("\n".join(msgs))
1448 raise CommandError("Wrong argument '%s'!" % subcommand)
1451 class cmd_domain_classicupgrade(Command):
1452 """Upgrade from Samba classic (NT4-like) database to Samba AD DC database.
1454 Specify either a directory with all Samba classic DC databases and state files (with --dbdir) or
1455 the testparm utility from your classic installation (with --testparm).
1458 synopsis = "%prog [options] <classic_smb_conf>"
1460 takes_optiongroups = {
1461 "sambaopts": options.SambaOptions,
1462 "versionopts": options.VersionOptions
1466 Option("--dbdir", type="string", metavar="DIR",
1467 help="Path to samba classic DC database directory"),
1468 Option("--testparm", type="string", metavar="PATH",
1469 help="Path to samba classic DC testparm utility from the previous installation. This allows the default paths of the previous installation to be followed"),
1470 Option("--targetdir", type="string", metavar="DIR",
1471 help="Path prefix where the new Samba 4.0 AD domain should be initialised"),
1472 Option("--quiet", help="Be quiet", action="store_true"),
1473 Option("--verbose", help="Be verbose", action="store_true"),
1474 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
1475 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
1476 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
1477 "BIND9_FLATFILE uses bind9 text database to store zone information, "
1478 "BIND9_DLZ uses samba4 AD to store zone information, "
1479 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
1480 default="SAMBA_INTERNAL")
1484 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
1485 action="store_true"),
1486 Option("--use-xattrs", type="choice", choices=["yes","no","auto"],
1487 metavar="[yes|no|auto]",
1488 help="Define if we should use the native fs capabilities or a tdb file for "
1489 "storing attributes likes ntacl when --use-ntvfs is set. "
1490 "auto tries to make an inteligent guess based on the user rights and system capabilities",
1493 if samba.is_ntvfs_fileserver_built():
1494 takes_options.extend(ntvfs_options)
1496 takes_args = ["smbconf"]
1498 def run(self, smbconf=None, targetdir=None, dbdir=None, testparm=None,
1499 quiet=False, verbose=False, use_xattrs="auto", sambaopts=None, versionopts=None,
1500 dns_backend=None, use_ntvfs=False):
1502 if not os.path.exists(smbconf):
1503 raise CommandError("File %s does not exist" % smbconf)
1505 if testparm and not os.path.exists(testparm):
1506 raise CommandError("Testparm utility %s does not exist" % testparm)
1508 if dbdir and not os.path.exists(dbdir):
1509 raise CommandError("Directory %s does not exist" % dbdir)
1511 if not dbdir and not testparm:
1512 raise CommandError("Please specify either dbdir or testparm")
1514 logger = self.get_logger()
1516 logger.setLevel(logging.DEBUG)
1518 logger.setLevel(logging.WARNING)
1520 logger.setLevel(logging.INFO)
1522 if dbdir and testparm:
1523 logger.warning("both dbdir and testparm specified, ignoring dbdir.")
1526 lp = sambaopts.get_loadparm()
1528 s3conf = s3param.get_context()
1531 s3conf.set("realm", sambaopts.realm)
1533 if targetdir is not None:
1534 if not os.path.isdir(targetdir):
1538 if use_xattrs == "yes":
1540 elif use_xattrs == "auto" and use_ntvfs == False:
1542 elif use_ntvfs == False:
1543 raise CommandError("--use-xattrs=no requires --use-ntvfs (not supported for production use). "
1544 "Please re-run with --use-xattrs omitted.")
1545 elif use_xattrs == "auto" and not s3conf.get("posix:eadb"):
1547 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
1549 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
1552 samba.ntacls.setntacl(lp, tmpfile.name,
1553 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
1556 # FIXME: Don't catch all exceptions here
1557 logger.info("You are not root or your system does not support xattr, using tdb backend for attributes. "
1558 "If you intend to use this provision in production, rerun the script as root on a system supporting xattrs.")
1562 # Set correct default values from dbdir or testparm
1565 paths["state directory"] = dbdir
1566 paths["private dir"] = dbdir
1567 paths["lock directory"] = dbdir
1568 paths["smb passwd file"] = dbdir + "/smbpasswd"
1570 paths["state directory"] = get_testparm_var(testparm, smbconf, "state directory")
1571 paths["private dir"] = get_testparm_var(testparm, smbconf, "private dir")
1572 paths["smb passwd file"] = get_testparm_var(testparm, smbconf, "smb passwd file")
1573 paths["lock directory"] = get_testparm_var(testparm, smbconf, "lock directory")
1574 # "testparm" from Samba 3 < 3.4.x is not aware of the parameter
1575 # "state directory", instead make use of "lock directory"
1576 if len(paths["state directory"]) == 0:
1577 paths["state directory"] = paths["lock directory"]
1580 s3conf.set(p, paths[p])
1582 # load smb.conf parameters
1583 logger.info("Reading smb.conf")
1584 s3conf.load(smbconf)
1585 samba3 = Samba3(smbconf, s3conf)
1587 logger.info("Provisioning")
1588 upgrade_from_samba3(samba3, logger, targetdir, session_info=system_session(),
1589 useeadb=eadb, dns_backend=dns_backend, use_ntvfs=use_ntvfs)
1592 class cmd_domain_samba3upgrade(cmd_domain_classicupgrade):
1593 __doc__ = cmd_domain_classicupgrade.__doc__
1595 # This command is present for backwards compatibility only,
1596 # and should not be shown.
1600 class LocalDCCredentialsOptions(options.CredentialsOptions):
1601 def __init__(self, parser):
1602 options.CredentialsOptions.__init__(self, parser, special_name="local-dc")
1604 class DomainTrustCommand(Command):
1605 """List domain trusts."""
1608 Command.__init__(self)
1609 self.local_lp = None
1611 self.local_server = None
1612 self.local_binding_string = None
1613 self.local_creds = None
1615 self.remote_server = None
1616 self.remote_binding_string = None
1617 self.remote_creds = None
1619 def _uint32(self, v):
1620 return ctypes.c_uint32(v).value
1622 def check_runtime_error(self, runtime, val):
1626 err32 = self._uint32(runtime[0])
1632 class LocalRuntimeError(CommandError):
1633 def __init__(exception_self, self, runtime, message):
1634 err32 = self._uint32(runtime[0])
1636 msg = "LOCAL_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1637 self.local_server, message, err32, errstr)
1638 CommandError.__init__(exception_self, msg)
1640 class RemoteRuntimeError(CommandError):
1641 def __init__(exception_self, self, runtime, message):
1642 err32 = self._uint32(runtime[0])
1644 msg = "REMOTE_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1645 self.remote_server, message, err32, errstr)
1646 CommandError.__init__(exception_self, msg)
1648 class LocalLdbError(CommandError):
1649 def __init__(exception_self, self, ldb_error, message):
1650 errval = ldb_error[0]
1651 errstr = ldb_error[1]
1652 msg = "LOCAL_DC[%s]: %s - ERROR(%d) - %s" % (
1653 self.local_server, message, errval, errstr)
1654 CommandError.__init__(exception_self, msg)
1656 def setup_local_server(self, sambaopts, localdcopts):
1657 if self.local_server is not None:
1658 return self.local_server
1660 lp = sambaopts.get_loadparm()
1662 local_server = localdcopts.ipaddress
1663 if local_server is None:
1664 server_role = lp.server_role()
1665 if server_role != "ROLE_ACTIVE_DIRECTORY_DC":
1666 raise CommandError("Invalid server_role %s" % (server_role))
1667 local_server = lp.get('netbios name')
1668 local_transport = "ncalrpc"
1669 local_binding_options = ""
1670 local_binding_options += ",auth_type=ncalrpc_as_system"
1671 local_ldap_url = None
1674 local_transport = "ncacn_np"
1675 local_binding_options = ""
1676 local_ldap_url = "ldap://%s" % local_server
1677 local_creds = localdcopts.get_credentials(lp)
1681 self.local_server = local_server
1682 self.local_binding_string = "%s:%s[%s]" % (local_transport, local_server, local_binding_options)
1683 self.local_ldap_url = local_ldap_url
1684 self.local_creds = local_creds
1685 return self.local_server
1687 def new_local_lsa_connection(self):
1688 return lsa.lsarpc(self.local_binding_string, self.local_lp, self.local_creds)
1690 def new_local_netlogon_connection(self):
1691 return netlogon.netlogon(self.local_binding_string, self.local_lp, self.local_creds)
1693 def new_local_ldap_connection(self):
1694 return SamDB(url=self.local_ldap_url,
1695 session_info=system_session(),
1696 credentials=self.local_creds,
1699 def setup_remote_server(self, credopts, domain,
1701 require_writable=True):
1704 assert require_writable
1706 if self.remote_server is not None:
1707 return self.remote_server
1709 self.remote_server = "__unknown__remote_server__.%s" % domain
1710 assert self.local_server is not None
1712 remote_creds = credopts.get_credentials(self.local_lp)
1713 remote_server = credopts.ipaddress
1714 remote_binding_options = ""
1716 # TODO: we should also support NT4 domains
1717 # we could use local_netlogon.netr_DsRGetDCNameEx2() with the remote domain name
1718 # and delegate NBT or CLDAP to the local netlogon server
1720 remote_net = Net(remote_creds, self.local_lp, server=remote_server)
1721 remote_flags = nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS
1722 if require_writable:
1723 remote_flags |= nbt.NBT_SERVER_WRITABLE
1725 remote_flags |= nbt.NBT_SERVER_PDC
1726 remote_info = remote_net.finddc(flags=remote_flags, domain=domain, address=remote_server)
1728 raise CommandError("Failed to find a writeable DC for domain '%s'" % domain)
1730 nbt.NBT_SERVER_PDC: "PDC",
1731 nbt.NBT_SERVER_GC: "GC",
1732 nbt.NBT_SERVER_LDAP: "LDAP",
1733 nbt.NBT_SERVER_DS: "DS",
1734 nbt.NBT_SERVER_KDC: "KDC",
1735 nbt.NBT_SERVER_TIMESERV: "TIMESERV",
1736 nbt.NBT_SERVER_CLOSEST: "CLOSEST",
1737 nbt.NBT_SERVER_WRITABLE: "WRITABLE",
1738 nbt.NBT_SERVER_GOOD_TIMESERV: "GOOD_TIMESERV",
1739 nbt.NBT_SERVER_NDNC: "NDNC",
1740 nbt.NBT_SERVER_SELECT_SECRET_DOMAIN_6: "SELECT_SECRET_DOMAIN_6",
1741 nbt.NBT_SERVER_FULL_SECRET_DOMAIN_6: "FULL_SECRET_DOMAIN_6",
1742 nbt.NBT_SERVER_ADS_WEB_SERVICE: "ADS_WEB_SERVICE",
1743 nbt.NBT_SERVER_DS_8: "DS_8",
1744 nbt.NBT_SERVER_HAS_DNS_NAME: "HAS_DNS_NAME",
1745 nbt.NBT_SERVER_IS_DEFAULT_NC: "IS_DEFAULT_NC",
1746 nbt.NBT_SERVER_FOREST_ROOT: "FOREST_ROOT",
1748 server_type_string = self.generic_bitmap_to_string(flag_map,
1749 remote_info.server_type, names_only=True)
1750 self.outf.write("RemoteDC Netbios[%s] DNS[%s] ServerType[%s]\n" % (
1751 remote_info.pdc_name,
1752 remote_info.pdc_dns_name,
1753 server_type_string))
1755 self.remote_server = remote_info.pdc_dns_name
1756 self.remote_binding_string="ncacn_np:%s[%s]" % (self.remote_server, remote_binding_options)
1757 self.remote_creds = remote_creds
1758 return self.remote_server
1760 def new_remote_lsa_connection(self):
1761 return lsa.lsarpc(self.remote_binding_string, self.local_lp, self.remote_creds)
1763 def new_remote_netlogon_connection(self):
1764 return netlogon.netlogon(self.remote_binding_string, self.local_lp, self.remote_creds)
1766 def get_lsa_info(self, conn, policy_access):
1767 objectAttr = lsa.ObjectAttribute()
1768 objectAttr.sec_qos = lsa.QosInfo()
1770 policy = conn.OpenPolicy2(''.decode('utf-8'),
1771 objectAttr, policy_access)
1773 info = conn.QueryInfoPolicy2(policy, lsa.LSA_POLICY_INFO_DNS)
1775 return (policy, info)
1777 def get_netlogon_dc_info(self, conn, server):
1778 info = conn.netr_DsRGetDCNameEx2(server,
1779 None, 0, None, None, None,
1780 netlogon.DS_RETURN_DNS_NAME)
1783 def netr_DomainTrust_to_name(self, t):
1784 if t.trust_type == lsa.LSA_TRUST_TYPE_DOWNLEVEL:
1785 return t.netbios_name
1789 def netr_DomainTrust_to_type(self, a, t):
1791 primary_parent = None
1793 if _t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
1795 if not _t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1796 primary_parent = a[_t.parent_index]
1799 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1800 if t is primary_parent:
1803 if t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1806 parent = a[t.parent_index]
1807 if parent is primary:
1812 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1817 def netr_DomainTrust_to_transitive(self, t):
1818 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1821 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE:
1824 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1829 def netr_DomainTrust_to_direction(self, t):
1830 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND and \
1831 t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1834 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND:
1837 if t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1842 def generic_enum_to_string(self, e_dict, v, names_only=False):
1846 v32 = self._uint32(v)
1847 w = "__unknown__%08X__" % v32
1849 r = "0x%x (%s)" % (v, w)
1852 def generic_bitmap_to_string(self, b_dict, v, names_only=False):
1857 for b in sorted(b_dict.keys()):
1864 c32 = self._uint32(c)
1865 s += ["__unknown_%08X__" % c32]
1870 r = "0x%x (%s)" % (v, w)
1873 def trustType_string(self, v):
1875 lsa.LSA_TRUST_TYPE_DOWNLEVEL : "DOWNLEVEL",
1876 lsa.LSA_TRUST_TYPE_UPLEVEL : "UPLEVEL",
1877 lsa.LSA_TRUST_TYPE_MIT : "MIT",
1878 lsa.LSA_TRUST_TYPE_DCE : "DCE",
1880 return self.generic_enum_to_string(types, v)
1882 def trustDirection_string(self, v):
1884 lsa.LSA_TRUST_DIRECTION_INBOUND |
1885 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "BOTH",
1886 lsa.LSA_TRUST_DIRECTION_INBOUND : "INBOUND",
1887 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "OUTBOUND",
1889 return self.generic_enum_to_string(directions, v)
1891 def trustAttributes_string(self, v):
1893 lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE : "NON_TRANSITIVE",
1894 lsa.LSA_TRUST_ATTRIBUTE_UPLEVEL_ONLY : "UPLEVEL_ONLY",
1895 lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN : "QUARANTINED_DOMAIN",
1896 lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE : "FOREST_TRANSITIVE",
1897 lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION : "CROSS_ORGANIZATION",
1898 lsa.LSA_TRUST_ATTRIBUTE_WITHIN_FOREST : "WITHIN_FOREST",
1899 lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL : "TREAT_AS_EXTERNAL",
1900 lsa.LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION : "USES_RC4_ENCRYPTION",
1902 return self.generic_bitmap_to_string(attributes, v)
1904 def kerb_EncTypes_string(self, v):
1906 security.KERB_ENCTYPE_DES_CBC_CRC : "DES_CBC_CRC",
1907 security.KERB_ENCTYPE_DES_CBC_MD5 : "DES_CBC_MD5",
1908 security.KERB_ENCTYPE_RC4_HMAC_MD5 : "RC4_HMAC_MD5",
1909 security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96 : "AES128_CTS_HMAC_SHA1_96",
1910 security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96 : "AES256_CTS_HMAC_SHA1_96",
1911 security.KERB_ENCTYPE_FAST_SUPPORTED : "FAST_SUPPORTED",
1912 security.KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED : "COMPOUND_IDENTITY_SUPPORTED",
1913 security.KERB_ENCTYPE_CLAIMS_SUPPORTED : "CLAIMS_SUPPORTED",
1914 security.KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED : "RESOURCE_SID_COMPRESSION_DISABLED",
1916 return self.generic_bitmap_to_string(enctypes, v)
1918 def entry_tln_status(self, e_flags, ):
1920 return "Status[Enabled]"
1923 lsa.LSA_TLN_DISABLED_NEW : "Disabled-New",
1924 lsa.LSA_TLN_DISABLED_ADMIN : "Disabled",
1925 lsa.LSA_TLN_DISABLED_CONFLICT : "Disabled-Conflicting",
1927 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
1929 def entry_dom_status(self, e_flags):
1931 return "Status[Enabled]"
1934 lsa.LSA_SID_DISABLED_ADMIN : "Disabled-SID",
1935 lsa.LSA_SID_DISABLED_CONFLICT : "Disabled-SID-Conflicting",
1936 lsa.LSA_NB_DISABLED_ADMIN : "Disabled-NB",
1937 lsa.LSA_NB_DISABLED_CONFLICT : "Disabled-NB-Conflicting",
1939 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
1941 def write_forest_trust_info(self, fti, tln=None, collisions=None):
1943 tln_string = " TDO[%s]" % tln
1947 self.outf.write("Namespaces[%d]%s:\n" % (
1948 len(fti.entries), tln_string))
1950 for i in xrange(0, len(fti.entries)):
1954 collision_string = ""
1956 if collisions is not None:
1957 for c in collisions.entries:
1961 collision_string = " Collision[%s]" % (c.name.string)
1963 d = e.forest_trust_data
1964 if e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
1965 self.outf.write("TLN: %-32s DNS[*.%s]%s\n" % (
1966 self.entry_tln_status(flags),
1967 d.string, collision_string))
1968 elif e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
1969 self.outf.write("TLN_EX: %-29s DNS[*.%s]\n" % (
1971 elif e.type == lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
1972 self.outf.write("DOM: %-32s DNS[%s] Netbios[%s] SID[%s]%s\n" % (
1973 self.entry_dom_status(flags),
1974 d.dns_domain_name.string,
1975 d.netbios_domain_name.string,
1976 d.domain_sid, collision_string))
1979 class cmd_domain_trust_list(DomainTrustCommand):
1980 """List domain trusts."""
1982 synopsis = "%prog [options]"
1984 takes_optiongroups = {
1985 "sambaopts": options.SambaOptions,
1986 "versionopts": options.VersionOptions,
1987 "localdcopts": LocalDCCredentialsOptions,
1993 def run(self, sambaopts=None, versionopts=None, localdcopts=None):
1995 local_server = self.setup_local_server(sambaopts, localdcopts)
1997 local_netlogon = self.new_local_netlogon_connection()
1998 except RuntimeError as error:
1999 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2002 local_netlogon_trusts = local_netlogon.netr_DsrEnumerateDomainTrusts(local_server,
2003 netlogon.NETR_TRUST_FLAG_IN_FOREST |
2004 netlogon.NETR_TRUST_FLAG_OUTBOUND |
2005 netlogon.NETR_TRUST_FLAG_INBOUND)
2006 except RuntimeError as error:
2007 if self.check_runtime_error(error, werror.WERR_RPC_S_PROCNUM_OUT_OF_RANGE):
2008 # TODO: we could implement a fallback to lsa.EnumTrustDom()
2009 raise CommandError("LOCAL_DC[%s]: netr_DsrEnumerateDomainTrusts not supported." % (
2011 raise self.LocalRuntimeError(self, error, "netr_DsrEnumerateDomainTrusts failed")
2013 a = local_netlogon_trusts.array
2015 if t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
2017 self.outf.write("%-14s %-15s %-19s %s\n" % (
2018 "Type[%s]" % self.netr_DomainTrust_to_type(a, t),
2019 "Transitive[%s]" % self.netr_DomainTrust_to_transitive(t),
2020 "Direction[%s]" % self.netr_DomainTrust_to_direction(t),
2021 "Name[%s]" % self.netr_DomainTrust_to_name(t)))
2024 class cmd_domain_trust_show(DomainTrustCommand):
2025 """Show trusted domain details."""
2027 synopsis = "%prog NAME [options]"
2029 takes_optiongroups = {
2030 "sambaopts": options.SambaOptions,
2031 "versionopts": options.VersionOptions,
2032 "localdcopts": LocalDCCredentialsOptions,
2038 takes_args = ["domain"]
2040 def run(self, domain, sambaopts=None, versionopts=None, localdcopts=None):
2042 local_server = self.setup_local_server(sambaopts, localdcopts)
2044 local_lsa = self.new_local_lsa_connection()
2045 except RuntimeError as error:
2046 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2049 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2050 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2051 except RuntimeError as error:
2052 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2054 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2055 local_lsa_info.name.string,
2056 local_lsa_info.dns_domain.string,
2057 local_lsa_info.sid))
2059 lsaString = lsa.String()
2060 lsaString.string = domain
2062 local_tdo_full = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2063 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2064 local_tdo_info = local_tdo_full.info_ex
2065 local_tdo_posix = local_tdo_full.posix_offset
2066 except NTSTATUSError as error:
2067 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2068 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2070 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(FULL_INFO) failed")
2073 local_tdo_enctypes = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2074 lsaString, lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES)
2075 except NTSTATUSError as error:
2076 if self.check_runtime_error(error, ntstatus.NT_STATUS_INVALID_PARAMETER):
2078 if self.check_runtime_error(error, ntstatus.NT_STATUS_INVALID_INFO_CLASS):
2081 if error is not None:
2082 raise self.LocalRuntimeError(self, error,
2083 "QueryTrustedDomainInfoByName(SUPPORTED_ENCRYPTION_TYPES) failed")
2085 local_tdo_enctypes = lsa.TrustDomainInfoSupportedEncTypes()
2086 local_tdo_enctypes.enc_types = 0
2089 local_tdo_forest = None
2090 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2091 local_tdo_forest = local_lsa.lsaRQueryForestTrustInformation(local_policy,
2092 lsaString, lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
2093 except RuntimeError as error:
2094 if self.check_runtime_error(error, ntstatus.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
2096 if self.check_runtime_error(error, ntstatus.NT_STATUS_NOT_FOUND):
2098 if error is not None:
2099 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation failed")
2101 local_tdo_forest = lsa.ForestTrustInformation()
2102 local_tdo_forest.count = 0
2103 local_tdo_forest.entries = []
2105 self.outf.write("TrusteDomain:\n\n");
2106 self.outf.write("NetbiosName: %s\n" % local_tdo_info.netbios_name.string)
2107 if local_tdo_info.netbios_name.string != local_tdo_info.domain_name.string:
2108 self.outf.write("DnsName: %s\n" % local_tdo_info.domain_name.string)
2109 self.outf.write("SID: %s\n" % local_tdo_info.sid)
2110 self.outf.write("Type: %s\n" % self.trustType_string(local_tdo_info.trust_type))
2111 self.outf.write("Direction: %s\n" % self.trustDirection_string(local_tdo_info.trust_direction))
2112 self.outf.write("Attributes: %s\n" % self.trustAttributes_string(local_tdo_info.trust_attributes))
2113 posix_offset_u32 = ctypes.c_uint32(local_tdo_posix.posix_offset).value
2114 posix_offset_i32 = ctypes.c_int32(local_tdo_posix.posix_offset).value
2115 self.outf.write("PosixOffset: 0x%08X (%d)\n" % (posix_offset_u32, posix_offset_i32))
2116 self.outf.write("kerb_EncTypes: %s\n" % self.kerb_EncTypes_string(local_tdo_enctypes.enc_types))
2118 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2119 self.write_forest_trust_info(local_tdo_forest,
2120 tln=local_tdo_info.domain_name.string)
2124 class cmd_domain_trust_create(DomainTrustCommand):
2125 """Create a domain or forest trust."""
2127 synopsis = "%prog DOMAIN [options]"
2129 takes_optiongroups = {
2130 "sambaopts": options.SambaOptions,
2131 "versionopts": options.VersionOptions,
2132 "credopts": options.CredentialsOptions,
2133 "localdcopts": LocalDCCredentialsOptions,
2137 Option("--type", type="choice", metavar="TYPE",
2138 choices=["external", "forest"],
2139 help="The type of the trust: 'external' or 'forest'.",
2141 default="external"),
2142 Option("--direction", type="choice", metavar="DIRECTION",
2143 choices=["incoming", "outgoing", "both"],
2144 help="The trust direction: 'incoming', 'outgoing' or 'both'.",
2145 dest='trust_direction',
2147 Option("--create-location", type="choice", metavar="LOCATION",
2148 choices=["local", "both"],
2149 help="Where to create the trusted domain object: 'local' or 'both'.",
2150 dest='create_location',
2152 Option("--cross-organisation", action="store_true",
2153 help="The related domains does not belong to the same organisation.",
2154 dest='cross_organisation',
2156 Option("--quarantined", type="choice", metavar="yes|no",
2157 choices=["yes", "no", None],
2158 help="Special SID filtering rules are applied to the trust. "
2159 "With --type=external the default is yes. "
2160 "With --type=forest the default is no.",
2161 dest='quarantined_arg',
2163 Option("--not-transitive", action="store_true",
2164 help="The forest trust is not transitive.",
2165 dest='not_transitive',
2167 Option("--treat-as-external", action="store_true",
2168 help="The treat the forest trust as external.",
2169 dest='treat_as_external',
2171 Option("--no-aes-keys", action="store_false",
2172 help="The trust uses aes kerberos keys.",
2173 dest='use_aes_keys',
2175 Option("--skip-validation", action="store_false",
2176 help="Skip validation of the trust.",
2181 takes_args = ["domain"]
2183 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2184 trust_type=None, trust_direction=None, create_location=None,
2185 cross_organisation=False, quarantined_arg=None,
2186 not_transitive=False, treat_as_external=False,
2187 use_aes_keys=False, validate=True):
2189 lsaString = lsa.String()
2192 if quarantined_arg is None:
2193 if trust_type == 'external':
2195 elif quarantined_arg == 'yes':
2198 if trust_type != 'forest':
2200 raise CommandError("--not-transitive requires --type=forest")
2201 if treat_as_external:
2202 raise CommandError("--treat-as-external requires --type=forest")
2206 enc_types = lsa.TrustDomainInfoSupportedEncTypes()
2207 enc_types.enc_types = security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96
2208 enc_types.enc_types |= security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96
2210 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2211 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2212 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2214 local_trust_info = lsa.TrustDomainInfoInfoEx()
2215 local_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2216 local_trust_info.trust_direction = 0
2217 if trust_direction == "both":
2218 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2219 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2220 elif trust_direction == "incoming":
2221 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2222 elif trust_direction == "outgoing":
2223 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2224 local_trust_info.trust_attributes = 0
2225 if cross_organisation:
2226 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2228 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2229 if trust_type == "forest":
2230 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2232 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2233 if treat_as_external:
2234 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2236 def get_password(name):
2239 if password is not None and password is not '':
2241 password = getpass("New %s Password: " % name)
2242 passwordverify = getpass("Retype %s Password: " % name)
2243 if not password == passwordverify:
2245 self.outf.write("Sorry, passwords do not match.\n")
2247 incoming_secret = None
2248 outgoing_secret = None
2249 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2250 if create_location == "local":
2251 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2252 incoming_password = get_password("Incoming Trust")
2253 incoming_secret = string_to_byte_array(incoming_password.encode('utf-16-le'))
2254 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2255 outgoing_password = get_password("Outgoing Trust")
2256 outgoing_secret = string_to_byte_array(outgoing_password.encode('utf-16-le'))
2258 remote_trust_info = None
2260 # We use 240 random bytes.
2261 # Windows uses 28 or 240 random bytes. I guess it's
2262 # based on the trust type external vs. forest.
2264 # The initial trust password can be up to 512 bytes
2265 # while the versioned passwords used for periodic updates
2266 # can only be up to 498 bytes, as netr_ServerPasswordSet2()
2267 # needs to pass the NL_PASSWORD_VERSION structure within the
2268 # 512 bytes and a 2 bytes confounder is required.
2270 def random_trust_secret(length):
2271 pw = samba.generate_random_machine_password(length/2, length/2)
2272 return string_to_byte_array(pw.encode('utf-16-le'))
2274 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2275 incoming_secret = random_trust_secret(240)
2276 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2277 outgoing_secret = random_trust_secret(240)
2279 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2280 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2282 remote_trust_info = lsa.TrustDomainInfoInfoEx()
2283 remote_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2284 remote_trust_info.trust_direction = 0
2285 if trust_direction == "both":
2286 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2287 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2288 elif trust_direction == "incoming":
2289 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2290 elif trust_direction == "outgoing":
2291 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2292 remote_trust_info.trust_attributes = 0
2293 if cross_organisation:
2294 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2296 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2297 if trust_type == "forest":
2298 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2300 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2301 if treat_as_external:
2302 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2304 local_server = self.setup_local_server(sambaopts, localdcopts)
2306 local_lsa = self.new_local_lsa_connection()
2307 except RuntimeError as error:
2308 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2311 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2312 except RuntimeError as error:
2313 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2315 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2316 local_lsa_info.name.string,
2317 local_lsa_info.dns_domain.string,
2318 local_lsa_info.sid))
2321 remote_server = self.setup_remote_server(credopts, domain)
2322 except RuntimeError as error:
2323 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2326 remote_lsa = self.new_remote_lsa_connection()
2327 except RuntimeError as error:
2328 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2331 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2332 except RuntimeError as error:
2333 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2335 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2336 remote_lsa_info.name.string,
2337 remote_lsa_info.dns_domain.string,
2338 remote_lsa_info.sid))
2340 local_trust_info.domain_name.string = remote_lsa_info.dns_domain.string
2341 local_trust_info.netbios_name.string = remote_lsa_info.name.string
2342 local_trust_info.sid = remote_lsa_info.sid
2344 if remote_trust_info:
2345 remote_trust_info.domain_name.string = local_lsa_info.dns_domain.string
2346 remote_trust_info.netbios_name.string = local_lsa_info.name.string
2347 remote_trust_info.sid = local_lsa_info.sid
2350 lsaString.string = local_trust_info.domain_name.string
2351 local_old_netbios = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2352 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2353 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2354 except NTSTATUSError as error:
2355 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2356 raise self.LocalRuntimeError(self, error,
2357 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2361 lsaString.string = local_trust_info.netbios_name.string
2362 local_old_dns = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2363 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2364 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2365 except NTSTATUSError as error:
2366 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2367 raise self.LocalRuntimeError(self, error,
2368 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2371 if remote_trust_info:
2373 lsaString.string = remote_trust_info.domain_name.string
2374 remote_old_netbios = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2375 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2376 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2377 except NTSTATUSError as error:
2378 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2379 raise self.RemoteRuntimeError(self, error,
2380 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2384 lsaString.string = remote_trust_info.netbios_name.string
2385 remote_old_dns = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2386 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2387 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2388 except NTSTATUSError as error:
2389 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2390 raise self.RemoteRuntimeError(self, error,
2391 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2395 local_netlogon = self.new_local_netlogon_connection()
2396 except RuntimeError as error:
2397 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2400 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
2401 except RuntimeError as error:
2402 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
2404 if remote_trust_info:
2406 remote_netlogon = self.new_remote_netlogon_connection()
2407 except RuntimeError as error:
2408 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2411 remote_netlogon_info = self.get_netlogon_dc_info(remote_netlogon, remote_server)
2412 except RuntimeError as error:
2413 raise self.RemoteRuntimeError(self, error, "failed to get netlogon dc info")
2415 def generate_AuthInOutBlob(secret, update_time):
2417 blob = drsblobs.trustAuthInOutBlob()
2422 clear = drsblobs.AuthInfoClear()
2423 clear.size = len(secret)
2424 clear.password = secret
2426 info = drsblobs.AuthenticationInformation()
2427 info.LastUpdateTime = samba.unix2nttime(update_time)
2428 info.AuthType = lsa.TRUST_AUTH_TYPE_CLEAR
2429 info.AuthInfo = clear
2431 array = drsblobs.AuthenticationInformationArray()
2433 array.array = [info]
2435 blob = drsblobs.trustAuthInOutBlob()
2437 blob.current = array
2441 def generate_AuthInfoInternal(session_key, incoming=None, outgoing=None):
2442 confounder = [0] * 512
2443 for i in range(len(confounder)):
2444 confounder[i] = random.randint(0, 255)
2446 trustpass = drsblobs.trustDomainPasswords()
2448 trustpass.confounder = confounder
2449 trustpass.outgoing = outgoing
2450 trustpass.incoming = incoming
2452 trustpass_blob = ndr_pack(trustpass)
2454 encrypted_trustpass = arcfour_encrypt(session_key, trustpass_blob)
2456 auth_blob = lsa.DATA_BUF2()
2457 auth_blob.size = len(encrypted_trustpass)
2458 auth_blob.data = string_to_byte_array(encrypted_trustpass)
2460 auth_info = lsa.TrustDomainInfoAuthInfoInternal()
2461 auth_info.auth_blob = auth_blob
2465 update_time = samba.current_unix_time()
2466 incoming_blob = generate_AuthInOutBlob(incoming_secret, update_time)
2467 outgoing_blob = generate_AuthInOutBlob(outgoing_secret, update_time)
2469 local_tdo_handle = None
2470 remote_tdo_handle = None
2472 local_auth_info = generate_AuthInfoInternal(local_lsa.session_key,
2473 incoming=incoming_blob,
2474 outgoing=outgoing_blob)
2475 if remote_trust_info:
2476 remote_auth_info = generate_AuthInfoInternal(remote_lsa.session_key,
2477 incoming=outgoing_blob,
2478 outgoing=incoming_blob)
2481 if remote_trust_info:
2482 self.outf.write("Creating remote TDO.\n")
2483 current_request = { "location": "remote", "name": "CreateTrustedDomainEx2"}
2484 remote_tdo_handle = remote_lsa.CreateTrustedDomainEx2(remote_policy,
2487 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2488 self.outf.write("Remote TDO created.\n")
2490 self.outf.write("Setting supported encryption types on remote TDO.\n")
2491 current_request = { "location": "remote", "name": "SetInformationTrustedDomain"}
2492 remote_lsa.SetInformationTrustedDomain(remote_tdo_handle,
2493 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2496 self.outf.write("Creating local TDO.\n")
2497 current_request = { "location": "local", "name": "CreateTrustedDomainEx2"}
2498 local_tdo_handle = local_lsa.CreateTrustedDomainEx2(local_policy,
2501 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2502 self.outf.write("Local TDO created\n")
2504 self.outf.write("Setting supported encryption types on local TDO.\n")
2505 current_request = { "location": "local", "name": "SetInformationTrustedDomain"}
2506 local_lsa.SetInformationTrustedDomain(local_tdo_handle,
2507 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2509 except RuntimeError as error:
2510 self.outf.write("Error: %s failed %sly - cleaning up\n" % (
2511 current_request['name'], current_request['location']))
2512 if remote_tdo_handle:
2513 self.outf.write("Deleting remote TDO.\n")
2514 remote_lsa.DeleteObject(remote_tdo_handle)
2515 remote_tdo_handle = None
2516 if local_tdo_handle:
2517 self.outf.write("Deleting local TDO.\n")
2518 local_lsa.DeleteObject(local_tdo_handle)
2519 local_tdo_handle = None
2520 if current_request['location'] is "remote":
2521 raise self.RemoteRuntimeError(self, error, "%s" % (
2522 current_request['name']))
2523 raise self.LocalRuntimeError(self, error, "%s" % (
2524 current_request['name']))
2527 if local_trust_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2528 self.outf.write("Setup local forest trust information...\n")
2530 # get all information about the remote trust
2531 # this triggers netr_GetForestTrustInformation to the remote domain
2532 # and lsaRSetForestTrustInformation() locally, but new top level
2533 # names are disabled by default.
2534 local_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
2535 remote_lsa_info.dns_domain.string,
2536 netlogon.DS_GFTI_UPDATE_TDO)
2537 except RuntimeError as error:
2538 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2541 # here we try to enable all top level names
2542 local_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
2543 remote_lsa_info.dns_domain,
2544 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2547 except RuntimeError as error:
2548 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2550 self.write_forest_trust_info(local_forest_info,
2551 tln=remote_lsa_info.dns_domain.string,
2552 collisions=local_forest_collision)
2554 if remote_trust_info:
2555 self.outf.write("Setup remote forest trust information...\n")
2557 # get all information about the local trust (from the perspective of the remote domain)
2558 # this triggers netr_GetForestTrustInformation to our domain.
2559 # and lsaRSetForestTrustInformation() remotely, but new top level
2560 # names are disabled by default.
2561 remote_forest_info = remote_netlogon.netr_DsRGetForestTrustInformation(remote_netlogon_info.dc_unc,
2562 local_lsa_info.dns_domain.string,
2563 netlogon.DS_GFTI_UPDATE_TDO)
2564 except RuntimeError as error:
2565 raise self.RemoteRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2568 # here we try to enable all top level names
2569 remote_forest_collision = remote_lsa.lsaRSetForestTrustInformation(remote_policy,
2570 local_lsa_info.dns_domain,
2571 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2574 except RuntimeError as error:
2575 raise self.RemoteRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2577 self.write_forest_trust_info(remote_forest_info,
2578 tln=local_lsa_info.dns_domain.string,
2579 collisions=remote_forest_collision)
2581 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2582 self.outf.write("Validating outgoing trust...\n")
2584 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_netlogon_info.dc_unc,
2585 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2587 remote_lsa_info.dns_domain.string)
2588 except RuntimeError as error:
2589 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2591 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2592 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2594 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2595 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2596 local_trust_verify.trusted_dc_name,
2597 local_trust_verify.tc_connection_status[1],
2598 local_trust_verify.pdc_connection_status[1])
2600 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2601 local_trust_verify.trusted_dc_name,
2602 local_trust_verify.tc_connection_status[1],
2603 local_trust_verify.pdc_connection_status[1])
2605 if local_trust_status != werror.WERR_SUCCESS or local_conn_status != werror.WERR_SUCCESS:
2606 raise CommandError(local_validation)
2608 self.outf.write("OK: %s\n" % local_validation)
2610 if remote_trust_info:
2611 if remote_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2612 self.outf.write("Validating incoming trust...\n")
2614 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_netlogon_info.dc_unc,
2615 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2617 local_lsa_info.dns_domain.string)
2618 except RuntimeError as error:
2619 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2621 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2622 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2624 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2625 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2626 remote_trust_verify.trusted_dc_name,
2627 remote_trust_verify.tc_connection_status[1],
2628 remote_trust_verify.pdc_connection_status[1])
2630 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2631 remote_trust_verify.trusted_dc_name,
2632 remote_trust_verify.tc_connection_status[1],
2633 remote_trust_verify.pdc_connection_status[1])
2635 if remote_trust_status != werror.WERR_SUCCESS or remote_conn_status != werror.WERR_SUCCESS:
2636 raise CommandError(remote_validation)
2638 self.outf.write("OK: %s\n" % remote_validation)
2640 if remote_tdo_handle is not None:
2642 remote_lsa.Close(remote_tdo_handle)
2643 except RuntimeError as error:
2645 remote_tdo_handle = None
2646 if local_tdo_handle is not None:
2648 local_lsa.Close(local_tdo_handle)
2649 except RuntimeError as error:
2651 local_tdo_handle = None
2653 self.outf.write("Success.\n")
2656 class cmd_domain_trust_delete(DomainTrustCommand):
2657 """Delete a domain trust."""
2659 synopsis = "%prog DOMAIN [options]"
2661 takes_optiongroups = {
2662 "sambaopts": options.SambaOptions,
2663 "versionopts": options.VersionOptions,
2664 "credopts": options.CredentialsOptions,
2665 "localdcopts": LocalDCCredentialsOptions,
2669 Option("--delete-location", type="choice", metavar="LOCATION",
2670 choices=["local", "both"],
2671 help="Where to delete the trusted domain object: 'local' or 'both'.",
2672 dest='delete_location',
2676 takes_args = ["domain"]
2678 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2679 delete_location=None):
2681 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2682 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2683 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2685 if delete_location == "local":
2686 remote_policy_access = None
2688 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2689 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2690 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2692 local_server = self.setup_local_server(sambaopts, localdcopts)
2694 local_lsa = self.new_local_lsa_connection()
2695 except RuntimeError as error:
2696 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2699 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2700 except RuntimeError as error:
2701 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2703 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2704 local_lsa_info.name.string,
2705 local_lsa_info.dns_domain.string,
2706 local_lsa_info.sid))
2708 local_tdo_info = None
2709 local_tdo_handle = None
2710 remote_tdo_info = None
2711 remote_tdo_handle = None
2713 lsaString = lsa.String()
2715 lsaString.string = domain
2716 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2717 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2718 except NTSTATUSError as error:
2719 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2720 raise CommandError("Failed to find trust for domain '%s'" % domain)
2721 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2724 if remote_policy_access is not None:
2726 remote_server = self.setup_remote_server(credopts, domain)
2727 except RuntimeError as error:
2728 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2731 remote_lsa = self.new_remote_lsa_connection()
2732 except RuntimeError as error:
2733 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2736 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2737 except RuntimeError as error:
2738 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2740 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2741 remote_lsa_info.name.string,
2742 remote_lsa_info.dns_domain.string,
2743 remote_lsa_info.sid))
2745 if remote_lsa_info.sid != local_tdo_info.sid or \
2746 remote_lsa_info.name.string != local_tdo_info.netbios_name.string or \
2747 remote_lsa_info.dns_domain.string != local_tdo_info.domain_name.string:
2748 raise CommandError("LocalTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2749 local_tdo_info.netbios_name.string,
2750 local_tdo_info.domain_name.string,
2751 local_tdo_info.sid))
2754 lsaString.string = local_lsa_info.dns_domain.string
2755 remote_tdo_info = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2756 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2757 except NTSTATUSError as error:
2758 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2759 raise self.RemoteRuntimeError(self, error, "QueryTrustedDomainInfoByName(%s)" % (
2763 if remote_tdo_info is not None:
2764 if local_lsa_info.sid != remote_tdo_info.sid or \
2765 local_lsa_info.name.string != remote_tdo_info.netbios_name.string or \
2766 local_lsa_info.dns_domain.string != remote_tdo_info.domain_name.string:
2767 raise CommandError("RemoteTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2768 remote_tdo_info.netbios_name.string,
2769 remote_tdo_info.domain_name.string,
2770 remote_tdo_info.sid))
2772 if local_tdo_info is not None:
2774 lsaString.string = local_tdo_info.domain_name.string
2775 local_tdo_handle = local_lsa.OpenTrustedDomainByName(local_policy,
2777 security.SEC_STD_DELETE)
2778 except RuntimeError as error:
2779 raise self.LocalRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2782 local_lsa.DeleteObject(local_tdo_handle)
2783 local_tdo_handle = None
2785 if remote_tdo_info is not None:
2787 lsaString.string = remote_tdo_info.domain_name.string
2788 remote_tdo_handle = remote_lsa.OpenTrustedDomainByName(remote_policy,
2790 security.SEC_STD_DELETE)
2791 except RuntimeError as error:
2792 raise self.RemoteRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2795 if remote_tdo_handle is not None:
2797 remote_lsa.DeleteObject(remote_tdo_handle)
2798 remote_tdo_handle = None
2799 self.outf.write("RemoteTDO deleted.\n")
2800 except RuntimeError as error:
2801 self.outf.write("%s\n" % self.RemoteRuntimeError(self, error, "DeleteObject() failed"))
2803 if local_tdo_handle is not None:
2805 local_lsa.DeleteObject(local_tdo_handle)
2806 local_tdo_handle = None
2807 self.outf.write("LocalTDO deleted.\n")
2808 except RuntimeError as error:
2809 self.outf.write("%s\n" % self.LocalRuntimeError(self, error, "DeleteObject() failed"))
2813 class cmd_domain_trust_validate(DomainTrustCommand):
2814 """Validate a domain trust."""
2816 synopsis = "%prog DOMAIN [options]"
2818 takes_optiongroups = {
2819 "sambaopts": options.SambaOptions,
2820 "versionopts": options.VersionOptions,
2821 "credopts": options.CredentialsOptions,
2822 "localdcopts": LocalDCCredentialsOptions,
2826 Option("--validate-location", type="choice", metavar="LOCATION",
2827 choices=["local", "both"],
2828 help="Where to validate the trusted domain object: 'local' or 'both'.",
2829 dest='validate_location',
2833 takes_args = ["domain"]
2835 def run(self, domain, sambaopts=None, versionopts=None, credopts=None, localdcopts=None,
2836 validate_location=None):
2838 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2840 local_server = self.setup_local_server(sambaopts, localdcopts)
2842 local_lsa = self.new_local_lsa_connection()
2843 except RuntimeError as error:
2844 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2847 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2848 except RuntimeError as error:
2849 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2851 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2852 local_lsa_info.name.string,
2853 local_lsa_info.dns_domain.string,
2854 local_lsa_info.sid))
2857 lsaString = lsa.String()
2858 lsaString.string = domain
2859 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2860 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2861 except NTSTATUSError as error:
2862 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2863 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2865 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
2867 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
2868 local_tdo_info.netbios_name.string,
2869 local_tdo_info.domain_name.string,
2870 local_tdo_info.sid))
2873 local_netlogon = self.new_local_netlogon_connection()
2874 except RuntimeError as error:
2875 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2878 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_server,
2879 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2881 local_tdo_info.domain_name.string)
2882 except RuntimeError as error:
2883 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2885 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2886 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2888 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2889 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2890 local_trust_verify.trusted_dc_name,
2891 local_trust_verify.tc_connection_status[1],
2892 local_trust_verify.pdc_connection_status[1])
2894 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2895 local_trust_verify.trusted_dc_name,
2896 local_trust_verify.tc_connection_status[1],
2897 local_trust_verify.pdc_connection_status[1])
2899 if local_trust_status != werror.WERR_SUCCESS or local_conn_status != werror.WERR_SUCCESS:
2900 raise CommandError(local_validation)
2902 self.outf.write("OK: %s\n" % local_validation)
2905 server = local_trust_verify.trusted_dc_name.replace('\\', '')
2906 domain_and_server = "%s\\%s" % (local_tdo_info.domain_name.string, server)
2907 local_trust_rediscover = local_netlogon.netr_LogonControl2Ex(local_server,
2908 netlogon.NETLOGON_CONTROL_REDISCOVER,
2911 except RuntimeError as error:
2912 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
2914 local_conn_status = self._uint32(local_trust_rediscover.tc_connection_status[0])
2915 local_rediscover = "LocalRediscover: DC[%s] CONNECTION[%s]" % (
2916 local_trust_rediscover.trusted_dc_name,
2917 local_trust_rediscover.tc_connection_status[1])
2919 if local_conn_status != werror.WERR_SUCCESS:
2920 raise CommandError(local_rediscover)
2922 self.outf.write("OK: %s\n" % local_rediscover)
2924 if validate_location != "local":
2926 remote_server = self.setup_remote_server(credopts, domain, require_pdc=False)
2927 except RuntimeError as error:
2928 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2931 remote_netlogon = self.new_remote_netlogon_connection()
2932 except RuntimeError as error:
2933 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2936 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_server,
2937 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2939 local_lsa_info.dns_domain.string)
2940 except RuntimeError as error:
2941 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2943 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2944 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2946 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2947 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2948 remote_trust_verify.trusted_dc_name,
2949 remote_trust_verify.tc_connection_status[1],
2950 remote_trust_verify.pdc_connection_status[1])
2952 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2953 remote_trust_verify.trusted_dc_name,
2954 remote_trust_verify.tc_connection_status[1],
2955 remote_trust_verify.pdc_connection_status[1])
2957 if remote_trust_status != werror.WERR_SUCCESS or remote_conn_status != werror.WERR_SUCCESS:
2958 raise CommandError(remote_validation)
2960 self.outf.write("OK: %s\n" % remote_validation)
2963 server = remote_trust_verify.trusted_dc_name.replace('\\', '')
2964 domain_and_server = "%s\\%s" % (local_lsa_info.dns_domain.string, server)
2965 remote_trust_rediscover = remote_netlogon.netr_LogonControl2Ex(remote_server,
2966 netlogon.NETLOGON_CONTROL_REDISCOVER,
2969 except RuntimeError as error:
2970 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
2972 remote_conn_status = self._uint32(remote_trust_rediscover.tc_connection_status[0])
2974 remote_rediscover = "RemoteRediscover: DC[%s] CONNECTION[%s]" % (
2975 remote_trust_rediscover.trusted_dc_name,
2976 remote_trust_rediscover.tc_connection_status[1])
2978 if remote_conn_status != werror.WERR_SUCCESS:
2979 raise CommandError(remote_rediscover)
2981 self.outf.write("OK: %s\n" % remote_rediscover)
2985 class cmd_domain_trust_namespaces(DomainTrustCommand):
2986 """Manage forest trust namespaces."""
2988 synopsis = "%prog [DOMAIN] [options]"
2990 takes_optiongroups = {
2991 "sambaopts": options.SambaOptions,
2992 "versionopts": options.VersionOptions,
2993 "localdcopts": LocalDCCredentialsOptions,
2997 Option("--refresh", type="choice", metavar="check|store",
2998 choices=["check", "store", None],
2999 help="List and maybe store refreshed forest trust information: 'check' or 'store'.",
3002 Option("--enable-all", action="store_true",
3003 help="Try to update disabled entries, not allowed with --refresh=check.",
3006 Option("--enable-tln", action="append", metavar='DNSDOMAIN',
3007 help="Enable a top level name entry. Can be specified multiple times.",
3010 Option("--disable-tln", action="append", metavar='DNSDOMAIN',
3011 help="Disable a top level name entry. Can be specified multiple times.",
3014 Option("--add-tln-ex", action="append", metavar='DNSDOMAIN',
3015 help="Add a top level exclusion entry. Can be specified multiple times.",
3018 Option("--delete-tln-ex", action="append", metavar='DNSDOMAIN',
3019 help="Delete a top level exclusion entry. Can be specified multiple times.",
3020 dest='delete_tln_ex',
3022 Option("--enable-nb", action="append", metavar='NETBIOSDOMAIN',
3023 help="Enable a netbios name in a domain entry. Can be specified multiple times.",
3026 Option("--disable-nb", action="append", metavar='NETBIOSDOMAIN',
3027 help="Disable a netbios name in a domain entry. Can be specified multiple times.",
3030 Option("--enable-sid", action="append", metavar='DOMAINSID',
3031 help="Enable a SID in a domain entry. Can be specified multiple times.",
3032 dest='enable_sid_str',
3034 Option("--disable-sid", action="append", metavar='DOMAINSID',
3035 help="Disable a SID in a domain entry. Can be specified multiple times.",
3036 dest='disable_sid_str',
3038 Option("--add-upn-suffix", action="append", metavar='DNSDOMAIN',
3039 help="Add a new uPNSuffixes attribute for the local forest. Can be specified multiple times.",
3042 Option("--delete-upn-suffix", action="append", metavar='DNSDOMAIN',
3043 help="Delete an existing uPNSuffixes attribute of the local forest. Can be specified multiple times.",
3046 Option("--add-spn-suffix", action="append", metavar='DNSDOMAIN',
3047 help="Add a new msDS-SPNSuffixes attribute for the local forest. Can be specified multiple times.",
3050 Option("--delete-spn-suffix", action="append", metavar='DNSDOMAIN',
3051 help="Delete an existing msDS-SPNSuffixes attribute of the local forest. Can be specified multiple times.",
3056 takes_args = ["domain?"]
3058 def run(self, domain=None, sambaopts=None, localdcopts=None, versionopts=None,
3059 refresh=None, enable_all=False,
3060 enable_tln=[], disable_tln=[], add_tln_ex=[], delete_tln_ex=[],
3061 enable_sid_str=[], disable_sid_str=[], enable_nb=[], disable_nb=[],
3062 add_upn=[], delete_upn=[], add_spn=[], delete_spn=[]):
3064 require_update = False
3067 if refresh == "store":
3068 raise CommandError("--refresh=%s not allowed without DOMAIN" % refresh)
3071 raise CommandError("--enable-all not allowed without DOMAIN")
3073 if len(enable_tln) > 0:
3074 raise CommandError("--enable-tln not allowed without DOMAIN")
3075 if len(disable_tln) > 0:
3076 raise CommandError("--disable-tln not allowed without DOMAIN")
3078 if len(add_tln_ex) > 0:
3079 raise CommandError("--add-tln-ex not allowed without DOMAIN")
3080 if len(delete_tln_ex) > 0:
3081 raise CommandError("--delete-tln-ex not allowed without DOMAIN")
3083 if len(enable_nb) > 0:
3084 raise CommandError("--enable-nb not allowed without DOMAIN")
3085 if len(disable_nb) > 0:
3086 raise CommandError("--disable-nb not allowed without DOMAIN")
3088 if len(enable_sid_str) > 0:
3089 raise CommandError("--enable-sid not allowed without DOMAIN")
3090 if len(disable_sid_str) > 0:
3091 raise CommandError("--disable-sid not allowed without DOMAIN")
3093 if len(add_upn) > 0:
3095 if not n.startswith("*."):
3097 raise CommandError("value[%s] specified for --add-upn-suffix should not include with '*.'" % n)
3098 require_update = True
3099 if len(delete_upn) > 0:
3100 for n in delete_upn:
3101 if not n.startswith("*."):
3103 raise CommandError("value[%s] specified for --delete-upn-suffix should not include with '*.'" % n)
3104 require_update = True
3106 for d in delete_upn:
3107 if a.lower() != d.lower():
3109 raise CommandError("value[%s] specified for --add-upn-suffix and --delete-upn-suffix" % a)
3111 if len(add_spn) > 0:
3113 if not n.startswith("*."):
3115 raise CommandError("value[%s] specified for --add-spn-suffix should not include with '*.'" % n)
3116 require_update = True
3117 if len(delete_spn) > 0:
3118 for n in delete_spn:
3119 if not n.startswith("*."):
3121 raise CommandError("value[%s] specified for --delete-spn-suffix should not include with '*.'" % n)
3122 require_update = True
3124 for d in delete_spn:
3125 if a.lower() != d.lower():
3127 raise CommandError("value[%s] specified for --add-spn-suffix and --delete-spn-suffix" % a)
3129 if len(add_upn) > 0:
3130 raise CommandError("--add-upn-suffix not allowed together with DOMAIN")
3131 if len(delete_upn) > 0:
3132 raise CommandError("--delete-upn-suffix not allowed together with DOMAIN")
3133 if len(add_spn) > 0:
3134 raise CommandError("--add-spn-suffix not allowed together with DOMAIN")
3135 if len(delete_spn) > 0:
3136 raise CommandError("--delete-spn-suffix not allowed together with DOMAIN")
3138 if refresh is not None:
3139 if refresh == "store":
3140 require_update = True
3142 if enable_all and refresh != "store":
3143 raise CommandError("--enable-all not allowed together with --refresh=%s" % refresh)
3145 if len(enable_tln) > 0:
3146 raise CommandError("--enable-tln not allowed together with --refresh")
3147 if len(disable_tln) > 0:
3148 raise CommandError("--disable-tln not allowed together with --refresh")
3150 if len(add_tln_ex) > 0:
3151 raise CommandError("--add-tln-ex not allowed together with --refresh")
3152 if len(delete_tln_ex) > 0:
3153 raise CommandError("--delete-tln-ex not allowed together with --refresh")
3155 if len(enable_nb) > 0:
3156 raise CommandError("--enable-nb not allowed together with --refresh")
3157 if len(disable_nb) > 0:
3158 raise CommandError("--disable-nb not allowed together with --refresh")
3160 if len(enable_sid_str) > 0:
3161 raise CommandError("--enable-sid not allowed together with --refresh")
3162 if len(disable_sid_str) > 0:
3163 raise CommandError("--disable-sid not allowed together with --refresh")
3166 require_update = True
3168 if len(enable_tln) > 0:
3169 raise CommandError("--enable-tln not allowed together with --enable-all")
3171 if len(enable_nb) > 0:
3172 raise CommandError("--enable-nb not allowed together with --enable-all")
3174 if len(enable_sid_str) > 0:
3175 raise CommandError("--enable-sid not allowed together with --enable-all")
3177 if len(enable_tln) > 0:
3178 require_update = True
3179 if len(disable_tln) > 0:
3180 require_update = True
3181 for e in enable_tln:
3182 for d in disable_tln:
3183 if e.lower() != d.lower():
3185 raise CommandError("value[%s] specified for --enable-tln and --disable-tln" % e)
3187 if len(add_tln_ex) > 0:
3188 for n in add_tln_ex:
3189 if not n.startswith("*."):
3191 raise CommandError("value[%s] specified for --add-tln-ex should not include with '*.'" % n)
3192 require_update = True
3193 if len(delete_tln_ex) > 0:
3194 for n in delete_tln_ex:
3195 if not n.startswith("*."):
3197 raise CommandError("value[%s] specified for --delete-tln-ex should not include with '*.'" % n)
3198 require_update = True
3199 for a in add_tln_ex:
3200 for d in delete_tln_ex:
3201 if a.lower() != d.lower():
3203 raise CommandError("value[%s] specified for --add-tln-ex and --delete-tln-ex" % a)
3205 if len(enable_nb) > 0:
3206 require_update = True
3207 if len(disable_nb) > 0:
3208 require_update = True
3210 for d in disable_nb:
3211 if e.upper() != d.upper():
3213 raise CommandError("value[%s] specified for --enable-nb and --disable-nb" % e)
3216 for s in enable_sid_str:
3218 sid = security.dom_sid(s)
3219 except TypeError as error:
3220 raise CommandError("value[%s] specified for --enable-sid is not a valid SID" % s)
3221 enable_sid.append(sid)
3223 for s in disable_sid_str:
3225 sid = security.dom_sid(s)
3226 except TypeError as error:
3227 raise CommandError("value[%s] specified for --disable-sid is not a valid SID" % s)
3228 disable_sid.append(sid)
3229 if len(enable_sid) > 0:
3230 require_update = True
3231 if len(disable_sid) > 0:
3232 require_update = True
3233 for e in enable_sid:
3234 for d in disable_sid:
3237 raise CommandError("value[%s] specified for --enable-sid and --disable-sid" % e)
3239 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
3241 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
3243 local_server = self.setup_local_server(sambaopts, localdcopts)
3245 local_lsa = self.new_local_lsa_connection()
3246 except RuntimeError as error:
3247 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
3250 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
3251 except RuntimeError as error:
3252 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
3254 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
3255 local_lsa_info.name.string,
3256 local_lsa_info.dns_domain.string,
3257 local_lsa_info.sid))
3261 local_netlogon = self.new_local_netlogon_connection()
3262 except RuntimeError as error:
3263 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3266 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3267 except RuntimeError as error:
3268 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3270 if local_netlogon_info.domain_name != local_netlogon_info.forest_name:
3271 raise CommandError("The local domain [%s] is not the forest root [%s]" % (
3272 local_netlogon_info.domain_name,
3273 local_netlogon_info.forest_name))
3276 # get all information about our own forest
3277 own_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3279 except RuntimeError as error:
3280 if self.check_runtime_error(error, werror.WERR_RPC_S_PROCNUM_OUT_OF_RANGE):
3281 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3284 if self.check_runtime_error(error, werror.WERR_INVALID_FUNCTION):
3285 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3288 if self.check_runtime_error(error, werror.WERR_NERR_ACFNOTLOADED):
3289 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3292 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3294 self.outf.write("Own forest trust information...\n")
3295 self.write_forest_trust_info(own_forest_info,
3296 tln=local_lsa_info.dns_domain.string)
3299 local_samdb = self.new_local_ldap_connection()
3300 except RuntimeError as error:
3301 raise self.LocalRuntimeError(self, error, "failed to connect to SamDB")
3303 local_partitions_dn = "CN=Partitions,%s" % str(local_samdb.get_config_basedn())
3304 attrs = ['uPNSuffixes', 'msDS-SPNSuffixes']
3306 msgs = local_samdb.search(base=local_partitions_dn,
3307 scope=ldb.SCOPE_BASE,
3308 expression="(objectClass=crossRefContainer)",
3310 stored_msg = msgs[0]
3311 except ldb.LdbError as error:
3312 raise self.LocalLdbError(self, error, "failed to search partition dn")
3314 stored_upn_vals = []
3315 if 'uPNSuffixes' in stored_msg:
3316 stored_upn_vals.extend(stored_msg['uPNSuffixes'])
3318 stored_spn_vals = []
3319 if 'msDS-SPNSuffixes' in stored_msg:
3320 stored_spn_vals.extend(stored_msg['msDS-SPNSuffixes'])
3322 self.outf.write("Stored uPNSuffixes attributes[%d]:\n" % len(stored_upn_vals))
3323 for v in stored_upn_vals:
3324 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3325 self.outf.write("Stored msDS-SPNSuffixes attributes[%d]:\n" % len(stored_spn_vals))
3326 for v in stored_spn_vals:
3327 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3329 if not require_update:
3333 update_upn_vals = []
3334 update_upn_vals.extend(stored_upn_vals)
3337 update_spn_vals = []
3338 update_spn_vals.extend(stored_spn_vals)
3342 for i in xrange(0, len(update_upn_vals)):
3343 v = update_upn_vals[i]
3344 if v.lower() != upn.lower():
3349 raise CommandError("Entry already present for value[%s] specified for --add-upn-suffix" % upn)
3350 update_upn_vals.append(upn)
3353 for upn in delete_upn:
3355 for i in xrange(0, len(update_upn_vals)):
3356 v = update_upn_vals[i]
3357 if v.lower() != upn.lower():
3362 raise CommandError("Entry not found for value[%s] specified for --delete-upn-suffix" % upn)
3364 update_upn_vals.pop(idx)
3369 for i in xrange(0, len(update_spn_vals)):
3370 v = update_spn_vals[i]
3371 if v.lower() != spn.lower():
3376 raise CommandError("Entry already present for value[%s] specified for --add-spn-suffix" % spn)
3377 update_spn_vals.append(spn)
3380 for spn in delete_spn:
3382 for i in xrange(0, len(update_spn_vals)):
3383 v = update_spn_vals[i]
3384 if v.lower() != spn.lower():
3389 raise CommandError("Entry not found for value[%s] specified for --delete-spn-suffix" % spn)
3391 update_spn_vals.pop(idx)
3394 self.outf.write("Update uPNSuffixes attributes[%d]:\n" % len(update_upn_vals))
3395 for v in update_upn_vals:
3396 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3397 self.outf.write("Update msDS-SPNSuffixes attributes[%d]:\n" % len(update_spn_vals))
3398 for v in update_spn_vals:
3399 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3401 update_msg = ldb.Message()
3402 update_msg.dn = stored_msg.dn
3405 update_msg['uPNSuffixes'] = ldb.MessageElement(update_upn_vals,
3406 ldb.FLAG_MOD_REPLACE,
3409 update_msg['msDS-SPNSuffixes'] = ldb.MessageElement(update_spn_vals,
3410 ldb.FLAG_MOD_REPLACE,
3413 local_samdb.modify(update_msg)
3414 except ldb.LdbError as error:
3415 raise self.LocalLdbError(self, error, "failed to update partition dn")
3418 stored_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3420 except RuntimeError as error:
3421 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3423 self.outf.write("Stored forest trust information...\n")
3424 self.write_forest_trust_info(stored_forest_info,
3425 tln=local_lsa_info.dns_domain.string)
3429 lsaString = lsa.String()
3430 lsaString.string = domain
3431 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
3432 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
3433 except NTSTATUSError as error:
3434 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
3435 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
3437 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
3439 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
3440 local_tdo_info.netbios_name.string,
3441 local_tdo_info.domain_name.string,
3442 local_tdo_info.sid))
3444 if not local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
3445 raise CommandError("trusted domain object for domain [%s] is not marked as FOREST_TRANSITIVE." % domain)
3447 if refresh is not None:
3449 local_netlogon = self.new_local_netlogon_connection()
3450 except RuntimeError as error:
3451 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3454 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3455 except RuntimeError as error:
3456 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3458 lsa_update_check = 1
3459 if refresh == "store":
3460 netlogon_update_tdo = netlogon.DS_GFTI_UPDATE_TDO
3462 lsa_update_check = 0
3464 netlogon_update_tdo = 0
3467 # get all information about the remote trust
3468 # this triggers netr_GetForestTrustInformation to the remote domain
3469 # and lsaRSetForestTrustInformation() locally, but new top level
3470 # names are disabled by default.
3471 fresh_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3472 local_tdo_info.domain_name.string,
3473 netlogon_update_tdo)
3474 except RuntimeError as error:
3475 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3478 fresh_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3479 local_tdo_info.domain_name,
3480 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3483 except RuntimeError as error:
3484 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3486 self.outf.write("Fresh forest trust information...\n")
3487 self.write_forest_trust_info(fresh_forest_info,
3488 tln=local_tdo_info.domain_name.string,
3489 collisions=fresh_forest_collision)
3491 if refresh == "store":
3493 lsaString = lsa.String()
3494 lsaString.string = local_tdo_info.domain_name.string
3495 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3497 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3498 except RuntimeError as error:
3499 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3501 self.outf.write("Stored forest trust information...\n")
3502 self.write_forest_trust_info(stored_forest_info,
3503 tln=local_tdo_info.domain_name.string)
3508 # The none --refresh path
3512 lsaString = lsa.String()
3513 lsaString.string = local_tdo_info.domain_name.string
3514 local_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3516 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3517 except RuntimeError as error:
3518 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3520 self.outf.write("Local forest trust information...\n")
3521 self.write_forest_trust_info(local_forest_info,
3522 tln=local_tdo_info.domain_name.string)
3524 if not require_update:
3528 entries.extend(local_forest_info.entries)
3529 update_forest_info = lsa.ForestTrustInformation()
3530 update_forest_info.count = len(entries)
3531 update_forest_info.entries = entries
3534 for i in xrange(0, len(update_forest_info.entries)):
3535 r = update_forest_info.entries[i]
3536 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3538 if update_forest_info.entries[i].flags == 0:
3540 update_forest_info.entries[i].time = 0
3541 update_forest_info.entries[i].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3542 for i in xrange(0, len(update_forest_info.entries)):
3543 r = update_forest_info.entries[i]
3544 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3546 if update_forest_info.entries[i].flags == 0:
3548 update_forest_info.entries[i].time = 0
3549 update_forest_info.entries[i].flags &= ~lsa.LSA_NB_DISABLED_MASK
3550 update_forest_info.entries[i].flags &= ~lsa.LSA_SID_DISABLED_MASK
3552 for tln in enable_tln:
3554 for i in xrange(0, len(update_forest_info.entries)):
3555 r = update_forest_info.entries[i]
3556 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3558 if r.forest_trust_data.string.lower() != tln.lower():
3563 raise CommandError("Entry not found for value[%s] specified for --enable-tln" % tln)
3564 if not update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_MASK:
3565 raise CommandError("Entry found for value[%s] specified for --enable-tln is already enabled" % tln)
3566 update_forest_info.entries[idx].time = 0
3567 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3569 for tln in disable_tln:
3571 for i in xrange(0, len(update_forest_info.entries)):
3572 r = update_forest_info.entries[i]
3573 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3575 if r.forest_trust_data.string.lower() != tln.lower():
3580 raise CommandError("Entry not found for value[%s] specified for --disable-tln" % tln)
3581 if update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_ADMIN:
3582 raise CommandError("Entry found for value[%s] specified for --disable-tln is already disabled" % tln)
3583 update_forest_info.entries[idx].time = 0
3584 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3585 update_forest_info.entries[idx].flags |= lsa.LSA_TLN_DISABLED_ADMIN
3587 for tln_ex in add_tln_ex:
3589 for i in xrange(0, len(update_forest_info.entries)):
3590 r = update_forest_info.entries[i]
3591 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3593 if r.forest_trust_data.string.lower() != tln_ex.lower():
3598 raise CommandError("Entry already present for value[%s] specified for --add-tln-ex" % tln_ex)
3600 tln_dot = ".%s" % tln_ex.lower()
3602 for i in xrange(0, len(update_forest_info.entries)):
3603 r = update_forest_info.entries[i]
3604 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3606 r_dot = ".%s" % r.forest_trust_data.string.lower()
3607 if tln_dot == r_dot:
3608 raise CommandError("TLN entry present for value[%s] specified for --add-tln-ex" % tln_ex)
3609 if not tln_dot.endswith(r_dot):
3615 raise CommandError("No TLN parent present for value[%s] specified for --add-tln-ex" % tln_ex)
3617 r = lsa.ForestTrustRecord()
3618 r.type = lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX
3621 r.forest_trust_data.string = tln_ex
3624 entries.extend(update_forest_info.entries)
3625 entries.insert(idx + 1, r)
3626 update_forest_info.count = len(entries)
3627 update_forest_info.entries = entries
3629 for tln_ex in delete_tln_ex:
3631 for i in xrange(0, len(update_forest_info.entries)):
3632 r = update_forest_info.entries[i]
3633 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3635 if r.forest_trust_data.string.lower() != tln_ex.lower():
3640 raise CommandError("Entry not found for value[%s] specified for --delete-tln-ex" % tln_ex)
3643 entries.extend(update_forest_info.entries)
3645 update_forest_info.count = len(entries)
3646 update_forest_info.entries = entries
3648 for nb in enable_nb:
3650 for i in xrange(0, len(update_forest_info.entries)):
3651 r = update_forest_info.entries[i]
3652 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3654 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3659 raise CommandError("Entry not found for value[%s] specified for --enable-nb" % nb)
3660 if not update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_MASK:
3661 raise CommandError("Entry found for value[%s] specified for --enable-nb is already enabled" % nb)
3662 update_forest_info.entries[idx].time = 0
3663 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3665 for nb in disable_nb:
3667 for i in xrange(0, len(update_forest_info.entries)):
3668 r = update_forest_info.entries[i]
3669 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3671 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3676 raise CommandError("Entry not found for value[%s] specified for --delete-nb" % nb)
3677 if update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_ADMIN:
3678 raise CommandError("Entry found for value[%s] specified for --disable-nb is already disabled" % nb)
3679 update_forest_info.entries[idx].time = 0
3680 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3681 update_forest_info.entries[idx].flags |= lsa.LSA_NB_DISABLED_ADMIN
3683 for sid in enable_sid:
3685 for i in xrange(0, len(update_forest_info.entries)):
3686 r = update_forest_info.entries[i]
3687 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3689 if r.forest_trust_data.domain_sid != sid:
3694 raise CommandError("Entry not found for value[%s] specified for --enable-sid" % sid)
3695 if not update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_MASK:
3696 raise CommandError("Entry found for value[%s] specified for --enable-sid is already enabled" % nb)
3697 update_forest_info.entries[idx].time = 0
3698 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3700 for sid in disable_sid:
3702 for i in xrange(0, len(update_forest_info.entries)):
3703 r = update_forest_info.entries[i]
3704 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3706 if r.forest_trust_data.domain_sid != sid:
3711 raise CommandError("Entry not found for value[%s] specified for --delete-sid" % sid)
3712 if update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_ADMIN:
3713 raise CommandError("Entry found for value[%s] specified for --disable-sid is already disabled" % nb)
3714 update_forest_info.entries[idx].time = 0
3715 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3716 update_forest_info.entries[idx].flags |= lsa.LSA_SID_DISABLED_ADMIN
3719 update_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3720 local_tdo_info.domain_name,
3721 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3722 update_forest_info, 0)
3723 except RuntimeError as error:
3724 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3726 self.outf.write("Updated forest trust information...\n")
3727 self.write_forest_trust_info(update_forest_info,
3728 tln=local_tdo_info.domain_name.string,
3729 collisions=update_forest_collision)
3732 lsaString = lsa.String()
3733 lsaString.string = local_tdo_info.domain_name.string
3734 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3736 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3737 except RuntimeError as error:
3738 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3740 self.outf.write("Stored forest trust information...\n")
3741 self.write_forest_trust_info(stored_forest_info,
3742 tln=local_tdo_info.domain_name.string)
3745 class cmd_domain_tombstones_expunge(Command):
3746 """Expunge tombstones from the database.
3748 This command expunges tombstones from the database."""
3749 synopsis = "%prog NC [NC [...]] [options]"
3752 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
3753 metavar="URL", dest="H"),
3754 Option("--current-time",
3755 help="The current time to evaluate the tombstone lifetime from, expressed as YYYY-MM-DD",
3757 Option("--tombstone-lifetime", help="Number of days a tombstone should be preserved for", type=int),
3760 takes_args = ["nc*"]
3762 takes_optiongroups = {
3763 "sambaopts": options.SambaOptions,
3764 "credopts": options.CredentialsOptions,
3765 "versionopts": options.VersionOptions,
3768 def run(self, *ncs, **kwargs):
3769 sambaopts = kwargs.get("sambaopts")
3770 credopts = kwargs.get("credopts")
3771 versionpts = kwargs.get("versionopts")
3773 current_time_string = kwargs.get("current_time")
3774 tombstone_lifetime = kwargs.get("tombstone_lifetime")
3775 lp = sambaopts.get_loadparm()
3776 creds = credopts.get_credentials(lp)
3777 samdb = SamDB(url=H, session_info=system_session(),
3778 credentials=creds, lp=lp)
3780 if current_time_string is not None:
3781 current_time_obj = time.strptime(current_time_string, "%Y-%m-%d")
3782 current_time = long(time.mktime(current_time_obj))
3785 current_time = long(time.time())
3788 res = samdb.search(expression="", base="", scope=ldb.SCOPE_BASE,
3789 attrs=["namingContexts"])
3792 for nc in res[0]["namingContexts"]:
3797 started_transaction = False
3799 samdb.transaction_start()
3800 started_transaction = True
3802 removed_links) = samdb.garbage_collect_tombstones(ncs,
3803 current_time=current_time,
3804 tombstone_lifetime=tombstone_lifetime)
3806 except Exception, err:
3807 if started_transaction:
3808 samdb.transaction_cancel()
3809 raise CommandError("Failed to expunge / garbage collect tombstones", err)
3811 samdb.transaction_commit()
3813 self.outf.write("Removed %d objects and %d links successfully\n"
3814 % (removed_objects, removed_links))
3818 class cmd_domain_trust(SuperCommand):
3819 """Domain and forest trust management."""
3822 subcommands["list"] = cmd_domain_trust_list()
3823 subcommands["show"] = cmd_domain_trust_show()
3824 subcommands["create"] = cmd_domain_trust_create()
3825 subcommands["delete"] = cmd_domain_trust_delete()
3826 subcommands["validate"] = cmd_domain_trust_validate()
3827 subcommands["namespaces"] = cmd_domain_trust_namespaces()
3829 class cmd_domain_tombstones(SuperCommand):
3830 """Domain tombstone and recycled object management."""
3833 subcommands["expunge"] = cmd_domain_tombstones_expunge()
3835 class cmd_domain(SuperCommand):
3836 """Domain management."""
3839 subcommands["demote"] = cmd_domain_demote()
3840 if cmd_domain_export_keytab is not None:
3841 subcommands["exportkeytab"] = cmd_domain_export_keytab()
3842 subcommands["info"] = cmd_domain_info()
3843 subcommands["provision"] = cmd_domain_provision()
3844 subcommands["join"] = cmd_domain_join()
3845 subcommands["dcpromo"] = cmd_domain_dcpromo()
3846 subcommands["level"] = cmd_domain_level()
3847 subcommands["passwordsettings"] = cmd_domain_passwordsettings()
3848 subcommands["classicupgrade"] = cmd_domain_classicupgrade()
3849 subcommands["samba3upgrade"] = cmd_domain_samba3upgrade()
3850 subcommands["trust"] = cmd_domain_trust()
3851 subcommands["tombstones"] = cmd_domain_tombstones()