3 # Copyright Matthias Dieter Wallnoefer 2009
4 # Copyright Andrew Kroeger 2009
5 # Copyright Jelmer Vernooij 2007-2012
6 # Copyright Giampaolo Lauria 2011
7 # Copyright Matthieu Patou <mat@matws.net> 2011
8 # Copyright Andrew Bartlett 2008-2015
9 # Copyright Stefan Metzmacher 2012
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 3 of the License, or
14 # (at your option) any later version.
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 import samba.getopt as options
36 from getpass import getpass
37 from samba.net import Net, LIBNET_JOIN_AUTOMATIC
39 from samba.join import join_RODC, join_DC, join_subdomain
40 from samba.auth import system_session
41 from samba.samdb import SamDB
42 from samba.ndr import ndr_unpack, ndr_pack, ndr_print
43 from samba.dcerpc import drsuapi
44 from samba.dcerpc import drsblobs
45 from samba.dcerpc import lsa
46 from samba.dcerpc import netlogon
47 from samba.dcerpc import security
48 from samba.dcerpc import nbt
49 from samba.dcerpc import misc
50 from samba.dcerpc.samr import DOMAIN_PASSWORD_COMPLEX, DOMAIN_PASSWORD_STORE_CLEARTEXT
51 from samba.netcmd import (
57 from samba.netcmd.common import netcmd_get_domain_infos_via_cldap
58 from samba.samba3 import Samba3
59 from samba.samba3 import param as s3param
60 from samba.upgrade import upgrade_from_samba3
61 from samba.drs_utils import (
62 sendDsReplicaSync, drsuapi_connect, drsException,
64 from samba import remove_dc, arcfour_encrypt, string_to_byte_array
66 from samba.dsdb import (
67 DS_DOMAIN_FUNCTION_2000,
68 DS_DOMAIN_FUNCTION_2003,
69 DS_DOMAIN_FUNCTION_2003_MIXED,
70 DS_DOMAIN_FUNCTION_2008,
71 DS_DOMAIN_FUNCTION_2008_R2,
72 DS_DOMAIN_FUNCTION_2012,
73 DS_DOMAIN_FUNCTION_2012_R2,
74 DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL,
75 DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL,
76 UF_WORKSTATION_TRUST_ACCOUNT,
77 UF_SERVER_TRUST_ACCOUNT,
78 UF_TRUSTED_FOR_DELEGATION,
79 UF_PARTIAL_SECRETS_ACCOUNT
82 from samba.provision import (
87 from samba.provision.common import (
93 def get_testparm_var(testparm, smbconf, varname):
94 errfile = open(os.devnull, 'w')
95 p = subprocess.Popen([testparm, '-s', '-l',
96 '--parameter-name=%s' % varname, smbconf],
97 stdout=subprocess.PIPE, stderr=errfile)
98 (out,err) = p.communicate()
100 lines = out.split('\n')
102 return lines[0].strip()
106 import samba.dckeytab
108 cmd_domain_export_keytab = None
110 class cmd_domain_export_keytab(Command):
111 """Dump Kerberos keys of the domain into a keytab."""
113 synopsis = "%prog <keytab> [options]"
115 takes_optiongroups = {
116 "sambaopts": options.SambaOptions,
117 "credopts": options.CredentialsOptions,
118 "versionopts": options.VersionOptions,
122 Option("--principal", help="extract only this principal", type=str),
125 takes_args = ["keytab"]
127 def run(self, keytab, credopts=None, sambaopts=None, versionopts=None, principal=None):
128 lp = sambaopts.get_loadparm()
130 net.export_keytab(keytab=keytab, principal=principal)
133 class cmd_domain_info(Command):
134 """Print basic info about a domain and the DC passed as parameter."""
136 synopsis = "%prog <ip_address> [options]"
141 takes_optiongroups = {
142 "sambaopts": options.SambaOptions,
143 "credopts": options.CredentialsOptions,
144 "versionopts": options.VersionOptions,
147 takes_args = ["address"]
149 def run(self, address, credopts=None, sambaopts=None, versionopts=None):
150 lp = sambaopts.get_loadparm()
152 res = netcmd_get_domain_infos_via_cldap(lp, None, address)
154 raise CommandError("Invalid IP address '" + address + "'!")
155 self.outf.write("Forest : %s\n" % res.forest)
156 self.outf.write("Domain : %s\n" % res.dns_domain)
157 self.outf.write("Netbios domain : %s\n" % res.domain_name)
158 self.outf.write("DC name : %s\n" % res.pdc_dns_name)
159 self.outf.write("DC netbios name : %s\n" % res.pdc_name)
160 self.outf.write("Server site : %s\n" % res.server_site)
161 self.outf.write("Client site : %s\n" % res.client_site)
164 class cmd_domain_provision(Command):
165 """Provision a domain."""
167 synopsis = "%prog [options]"
169 takes_optiongroups = {
170 "sambaopts": options.SambaOptions,
171 "versionopts": options.VersionOptions,
175 Option("--interactive", help="Ask for names", action="store_true"),
176 Option("--domain", type="string", metavar="DOMAIN",
177 help="NetBIOS domain name to use"),
178 Option("--domain-guid", type="string", metavar="GUID",
179 help="set domainguid (otherwise random)"),
180 Option("--domain-sid", type="string", metavar="SID",
181 help="set domainsid (otherwise random)"),
182 Option("--ntds-guid", type="string", metavar="GUID",
183 help="set NTDS object GUID (otherwise random)"),
184 Option("--invocationid", type="string", metavar="GUID",
185 help="set invocationid (otherwise random)"),
186 Option("--host-name", type="string", metavar="HOSTNAME",
187 help="set hostname"),
188 Option("--host-ip", type="string", metavar="IPADDRESS",
189 help="set IPv4 ipaddress"),
190 Option("--host-ip6", type="string", metavar="IP6ADDRESS",
191 help="set IPv6 ipaddress"),
192 Option("--site", type="string", metavar="SITENAME",
193 help="set site name"),
194 Option("--adminpass", type="string", metavar="PASSWORD",
195 help="choose admin password (otherwise random)"),
196 Option("--krbtgtpass", type="string", metavar="PASSWORD",
197 help="choose krbtgt password (otherwise random)"),
198 Option("--machinepass", type="string", metavar="PASSWORD",
199 help="choose machine password (otherwise random)"),
200 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
201 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
202 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
203 "BIND9_FLATFILE uses bind9 text database to store zone information, "
204 "BIND9_DLZ uses samba4 AD to store zone information, "
205 "NONE skips the DNS setup entirely (not recommended)",
206 default="SAMBA_INTERNAL"),
207 Option("--dnspass", type="string", metavar="PASSWORD",
208 help="choose dns password (otherwise random)"),
209 Option("--ldapadminpass", type="string", metavar="PASSWORD",
210 help="choose password to set between Samba and its LDAP backend (otherwise random)"),
211 Option("--root", type="string", metavar="USERNAME",
212 help="choose 'root' unix username"),
213 Option("--nobody", type="string", metavar="USERNAME",
214 help="choose 'nobody' user"),
215 Option("--users", type="string", metavar="GROUPNAME",
216 help="choose 'users' group"),
217 Option("--quiet", help="Be quiet", action="store_true"),
218 Option("--blank", action="store_true",
219 help="do not add users or groups, just the structure"),
220 Option("--ldap-backend-type", type="choice", metavar="LDAP-BACKEND-TYPE",
221 help="Test initialisation support for unsupported LDAP backend type (fedora-ds or openldap) DO NOT USE",
222 choices=["fedora-ds", "openldap"]),
223 Option("--server-role", type="choice", metavar="ROLE",
224 choices=["domain controller", "dc", "member server", "member", "standalone"],
225 help="The server role (domain controller | dc | member server | member | standalone). Default is dc.",
226 default="domain controller"),
227 Option("--function-level", type="choice", metavar="FOR-FUN-LEVEL",
228 choices=["2000", "2003", "2008", "2008_R2"],
229 help="The domain and forest function level (2000 | 2003 | 2008 | 2008_R2 - always native). Default is (Windows) 2008_R2 Native.",
231 Option("--next-rid", type="int", metavar="NEXTRID", default=1000,
232 help="The initial nextRid value (only needed for upgrades). Default is 1000."),
233 Option("--partitions-only",
234 help="Configure Samba's partitions, but do not modify them (ie, join a BDC)", action="store_true"),
235 Option("--targetdir", type="string", metavar="DIR",
236 help="Set target directory"),
237 Option("--ol-mmr-urls", type="string", metavar="LDAPSERVER",
238 help="List of LDAP-URLS [ ldap://<FQHN>:<PORT>/ (where <PORT> has to be different than 389!) ] separated with comma (\",\") for use with OpenLDAP-MMR (Multi-Master-Replication), e.g.: \"ldap://s4dc1:9000,ldap://s4dc2:9000\""),
239 Option("--use-xattrs", type="choice", choices=["yes", "no", "auto"], help="Define if we should use the native fs capabilities or a tdb file for storing attributes likes ntacl, auto tries to make an inteligent guess based on the user rights and system capabilities", default="auto"),
241 Option("--use-rfc2307", action="store_true", help="Use AD to store posix attributes (default = no)"),
245 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",
246 action="store_true"),
247 Option("--slapd-path", type="string", metavar="SLAPD-PATH",
248 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."),
249 Option("--ldap-backend-extra-port", type="int", metavar="LDAP-BACKEND-EXTRA-PORT", help="Additional TCP port for LDAP backend server (to use for replication)"),
250 Option("--ldap-backend-forced-uri", type="string", metavar="LDAP-BACKEND-FORCED-URI",
251 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"),
252 Option("--ldap-backend-nosync", help="Configure LDAP backend not to call fsync() (for performance in test environments)", action="store_true"),
256 Option("--use-ntvfs", action="store_true", help="Use NTVFS for the fileserver (default = no)"),
259 if os.getenv('TEST_LDAP', "no") == "yes":
260 takes_options.extend(openldap_options)
262 if samba.is_ntvfs_fileserver_built():
263 takes_options.extend(ntvfs_options)
267 def run(self, sambaopts=None, versionopts=None,
290 ldap_backend_type=None,
294 partitions_only=None,
301 ldap_backend_nosync=None,
302 ldap_backend_extra_port=None,
303 ldap_backend_forced_uri=None,
304 ldap_dryrun_mode=None):
306 self.logger = self.get_logger("provision")
308 self.logger.setLevel(logging.WARNING)
310 self.logger.setLevel(logging.INFO)
312 lp = sambaopts.get_loadparm()
313 smbconf = lp.configfile
315 if dns_forwarder is not None:
316 suggested_forwarder = dns_forwarder
318 suggested_forwarder = self._get_nameserver_ip()
319 if suggested_forwarder is None:
320 suggested_forwarder = "none"
322 if len(self.raw_argv) == 1:
326 from getpass import getpass
329 def ask(prompt, default=None):
330 if default is not None:
331 print "%s [%s]: " % (prompt, default),
333 print "%s: " % (prompt,),
334 return sys.stdin.readline().rstrip("\n") or default
337 default = socket.getfqdn().split(".", 1)[1].upper()
340 realm = ask("Realm", default)
341 if realm in (None, ""):
342 raise CommandError("No realm set!")
345 default = realm.split(".")[0]
348 domain = ask("Domain", default)
350 raise CommandError("No domain set!")
352 server_role = ask("Server Role (dc, member, standalone)", "dc")
354 dns_backend = ask("DNS backend (SAMBA_INTERNAL, BIND9_FLATFILE, BIND9_DLZ, NONE)", "SAMBA_INTERNAL")
355 if dns_backend in (None, ''):
356 raise CommandError("No DNS backend set!")
358 if dns_backend == "SAMBA_INTERNAL":
359 dns_forwarder = ask("DNS forwarder IP address (write 'none' to disable forwarding)", suggested_forwarder)
360 if dns_forwarder.lower() in (None, 'none'):
361 suggested_forwarder = None
365 adminpassplain = getpass("Administrator password: ")
366 if not adminpassplain:
367 self.errf.write("Invalid administrator password.\n")
369 adminpassverify = getpass("Retype password: ")
370 if not adminpassplain == adminpassverify:
371 self.errf.write("Sorry, passwords do not match.\n")
373 adminpass = adminpassplain
377 realm = sambaopts._lp.get('realm')
379 raise CommandError("No realm set!")
381 raise CommandError("No domain set!")
384 self.logger.info("Administrator password will be set randomly!")
386 if function_level == "2000":
387 dom_for_fun_level = DS_DOMAIN_FUNCTION_2000
388 elif function_level == "2003":
389 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
390 elif function_level == "2008":
391 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008
392 elif function_level == "2008_R2":
393 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
395 if dns_backend == "SAMBA_INTERNAL" and dns_forwarder is None:
396 dns_forwarder = suggested_forwarder
398 samdb_fill = FILL_FULL
400 samdb_fill = FILL_NT4SYNC
401 elif partitions_only:
402 samdb_fill = FILL_DRS
404 if targetdir is not None:
405 if not os.path.isdir(targetdir):
410 if use_xattrs == "yes":
412 elif use_xattrs == "auto" and not lp.get("posix:eadb"):
414 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
416 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
419 samba.ntacls.setntacl(lp, file.name,
420 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
423 self.logger.info("You are not root or your system does not support xattr, using tdb backend for attributes. ")
428 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.")
429 if ldap_backend_type == "existing":
430 if ldap_backend_forced_uri is not None:
431 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)
433 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")
435 if ldap_backend_forced_uri is not None:
436 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")
438 if domain_sid is not None:
439 domain_sid = security.dom_sid(domain_sid)
441 session = system_session()
443 result = provision(self.logger,
444 session, smbconf=smbconf, targetdir=targetdir,
445 samdb_fill=samdb_fill, realm=realm, domain=domain,
446 domainguid=domain_guid, domainsid=domain_sid,
448 hostip=host_ip, hostip6=host_ip6,
449 sitename=site, ntdsguid=ntds_guid,
450 invocationid=invocationid, adminpass=adminpass,
451 krbtgtpass=krbtgtpass, machinepass=machinepass,
452 dns_backend=dns_backend, dns_forwarder=dns_forwarder,
453 dnspass=dnspass, root=root, nobody=nobody,
455 serverrole=server_role, dom_for_fun_level=dom_for_fun_level,
456 backend_type=ldap_backend_type,
457 ldapadminpass=ldapadminpass, ol_mmr_urls=ol_mmr_urls, slapd_path=slapd_path,
458 useeadb=eadb, next_rid=next_rid, lp=lp, use_ntvfs=use_ntvfs,
459 use_rfc2307=use_rfc2307, skip_sysvolacl=False,
460 ldap_backend_extra_port=ldap_backend_extra_port,
461 ldap_backend_forced_uri=ldap_backend_forced_uri,
462 nosync=ldap_backend_nosync, ldap_dryrun_mode=ldap_dryrun_mode)
464 except ProvisioningError, e:
465 raise CommandError("Provision failed", e)
467 result.report_logger(self.logger)
469 def _get_nameserver_ip(self):
470 """Grab the nameserver IP address from /etc/resolv.conf."""
472 RESOLV_CONF="/etc/resolv.conf"
474 if not path.isfile(RESOLV_CONF):
475 self.logger.warning("Failed to locate %s" % RESOLV_CONF)
480 handle = open(RESOLV_CONF, 'r')
482 if not line.startswith('nameserver'):
484 # we want the last non-space continuous string of the line
485 return line.strip().split()[-1]
487 if handle is not None:
490 self.logger.warning("No nameserver found in %s" % RESOLV_CONF)
493 class cmd_domain_dcpromo(Command):
494 """Promote an existing domain member or NT4 PDC to an AD DC."""
496 synopsis = "%prog <dnsdomain> [DC|RODC] [options]"
498 takes_optiongroups = {
499 "sambaopts": options.SambaOptions,
500 "versionopts": options.VersionOptions,
501 "credopts": options.CredentialsOptions,
505 Option("--server", help="DC to join", type=str),
506 Option("--site", help="site to join", type=str),
507 Option("--targetdir", help="where to store provision", type=str),
508 Option("--domain-critical-only",
509 help="only replicate critical domain objects",
510 action="store_true"),
511 Option("--machinepass", type=str, metavar="PASSWORD",
512 help="choose machine password (otherwise random)"),
513 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
514 choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
515 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
516 "BIND9_DLZ uses samba4 AD to store zone information, "
517 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
518 default="SAMBA_INTERNAL"),
519 Option("--quiet", help="Be quiet", action="store_true"),
520 Option("--verbose", help="Be verbose", action="store_true")
524 Option("--use-ntvfs", action="store_true", help="Use NTVFS for the fileserver (default = no)"),
527 if samba.is_ntvfs_fileserver_built():
528 takes_options.extend(ntvfs_options)
531 takes_args = ["domain", "role?"]
533 def run(self, domain, role=None, sambaopts=None, credopts=None,
534 versionopts=None, server=None, site=None, targetdir=None,
535 domain_critical_only=False, parent_domain=None, machinepass=None,
536 use_ntvfs=False, dns_backend=None,
537 quiet=False, verbose=False):
538 lp = sambaopts.get_loadparm()
539 creds = credopts.get_credentials(lp)
540 net = Net(creds, lp, server=credopts.ipaddress)
543 site = "Default-First-Site-Name"
545 logger = self.get_logger()
547 logger.setLevel(logging.DEBUG)
549 logger.setLevel(logging.WARNING)
551 logger.setLevel(logging.INFO)
553 netbios_name = lp.get("netbios name")
559 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
560 site=site, netbios_name=netbios_name, targetdir=targetdir,
561 domain_critical_only=domain_critical_only,
562 machinepass=machinepass, use_ntvfs=use_ntvfs,
563 dns_backend=dns_backend,
564 promote_existing=True)
566 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
567 site=site, netbios_name=netbios_name, targetdir=targetdir,
568 domain_critical_only=domain_critical_only,
569 machinepass=machinepass, use_ntvfs=use_ntvfs, dns_backend=dns_backend,
570 promote_existing=True)
572 raise CommandError("Invalid role '%s' (possible values: DC, RODC)" % role)
575 class cmd_domain_join(Command):
576 """Join domain as either member or backup domain controller."""
578 synopsis = "%prog <dnsdomain> [DC|RODC|MEMBER|SUBDOMAIN] [options]"
580 takes_optiongroups = {
581 "sambaopts": options.SambaOptions,
582 "versionopts": options.VersionOptions,
583 "credopts": options.CredentialsOptions,
587 Option("--server", help="DC to join", type=str),
588 Option("--site", help="site to join", type=str),
589 Option("--targetdir", help="where to store provision", type=str),
590 Option("--parent-domain", help="parent domain to create subdomain under", type=str),
591 Option("--domain-critical-only",
592 help="only replicate critical domain objects",
593 action="store_true"),
594 Option("--machinepass", type=str, metavar="PASSWORD",
595 help="choose machine password (otherwise random)"),
596 Option("--adminpass", type="string", metavar="PASSWORD",
597 help="choose adminstrator password when joining as a subdomain (otherwise random)"),
598 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
599 choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
600 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
601 "BIND9_DLZ uses samba4 AD to store zone information, "
602 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
603 default="SAMBA_INTERNAL"),
604 Option("--quiet", help="Be quiet", action="store_true"),
605 Option("--verbose", help="Be verbose", action="store_true")
609 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
612 if samba.is_ntvfs_fileserver_built():
613 takes_options.extend(ntvfs_options)
615 takes_args = ["domain", "role?"]
617 def run(self, domain, role=None, sambaopts=None, credopts=None,
618 versionopts=None, server=None, site=None, targetdir=None,
619 domain_critical_only=False, parent_domain=None, machinepass=None,
620 use_ntvfs=False, dns_backend=None, adminpass=None,
621 quiet=False, verbose=False):
622 lp = sambaopts.get_loadparm()
623 creds = credopts.get_credentials(lp)
624 net = Net(creds, lp, server=credopts.ipaddress)
627 site = "Default-First-Site-Name"
629 logger = self.get_logger()
631 logger.setLevel(logging.DEBUG)
633 logger.setLevel(logging.WARNING)
635 logger.setLevel(logging.INFO)
637 netbios_name = lp.get("netbios name")
642 if role is None or role == "MEMBER":
643 (join_password, sid, domain_name) = net.join_member(
644 domain, netbios_name, LIBNET_JOIN_AUTOMATIC,
645 machinepass=machinepass)
647 self.errf.write("Joined domain %s (%s)\n" % (domain_name, sid))
649 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
650 site=site, netbios_name=netbios_name, targetdir=targetdir,
651 domain_critical_only=domain_critical_only,
652 machinepass=machinepass, use_ntvfs=use_ntvfs, dns_backend=dns_backend)
654 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
655 site=site, netbios_name=netbios_name, targetdir=targetdir,
656 domain_critical_only=domain_critical_only,
657 machinepass=machinepass, use_ntvfs=use_ntvfs,
658 dns_backend=dns_backend)
659 elif role == "SUBDOMAIN":
661 logger.info("Administrator password will be set randomly!")
663 netbios_domain = lp.get("workgroup")
664 if parent_domain is None:
665 parent_domain = ".".join(domain.split(".")[1:])
666 join_subdomain(logger=logger, server=server, creds=creds, lp=lp, dnsdomain=domain,
667 parent_domain=parent_domain, site=site,
668 netbios_name=netbios_name, netbios_domain=netbios_domain,
669 targetdir=targetdir, machinepass=machinepass,
670 use_ntvfs=use_ntvfs, dns_backend=dns_backend,
673 raise CommandError("Invalid role '%s' (possible values: MEMBER, DC, RODC, SUBDOMAIN)" % role)
676 class cmd_domain_demote(Command):
677 """Demote ourselves from the role of Domain Controller."""
679 synopsis = "%prog [options]"
682 Option("--server", help="writable DC to write demotion changes on", type=str),
683 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
684 metavar="URL", dest="H"),
685 Option("--remove-other-dead-server", help="Dead DC (name or NTDS GUID) "
686 "to remove ALL references to (rather than this DC)", type=str),
687 Option("--quiet", help="Be quiet", action="store_true"),
688 Option("--verbose", help="Be verbose", action="store_true"),
691 takes_optiongroups = {
692 "sambaopts": options.SambaOptions,
693 "credopts": options.CredentialsOptions,
694 "versionopts": options.VersionOptions,
697 def run(self, sambaopts=None, credopts=None,
698 versionopts=None, server=None,
699 remove_other_dead_server=None, H=None,
700 verbose=False, quiet=False):
701 lp = sambaopts.get_loadparm()
702 creds = credopts.get_credentials(lp)
703 net = Net(creds, lp, server=credopts.ipaddress)
705 logger = self.get_logger()
707 logger.setLevel(logging.DEBUG)
709 logger.setLevel(logging.WARNING)
711 logger.setLevel(logging.INFO)
713 if remove_other_dead_server is not None:
714 if server is not None:
715 samdb = SamDB(url="ldap://%s" % server,
716 session_info=system_session(),
717 credentials=creds, lp=lp)
719 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
721 remove_dc.remove_dc(samdb, logger, remove_other_dead_server)
722 except remove_dc.DemoteException as err:
723 raise CommandError("Demote failed: %s" % err)
726 netbios_name = lp.get("netbios name")
727 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
729 res = samdb.search(expression='(&(objectClass=computer)(serverReferenceBL=*))', attrs=["dnsHostName", "name"])
731 raise CommandError("Unable to search for servers")
734 raise CommandError("You are the latest server in the domain")
738 if str(e["name"]).lower() != netbios_name.lower():
739 server = e["dnsHostName"]
742 ntds_guid = samdb.get_ntds_GUID()
743 msg = samdb.search(base=str(samdb.get_config_basedn()),
744 scope=ldb.SCOPE_SUBTREE, expression="(objectGUID=%s)" % ntds_guid,
746 if len(msg) == 0 or "options" not in msg[0]:
747 raise CommandError("Failed to find options on %s" % ntds_guid)
750 dsa_options = int(str(msg[0]['options']))
752 res = samdb.search(expression="(fSMORoleOwner=%s)" % str(ntds_dn),
753 controls=["search_options:1:2"])
756 raise CommandError("Current DC is still the owner of %d role(s), use the role command to transfer roles to another DC" % len(res))
758 self.errf.write("Using %s as partner server for the demotion\n" %
760 (drsuapiBind, drsuapi_handle, supportedExtensions) = drsuapi_connect(server, lp, creds)
762 self.errf.write("Deactivating inbound replication\n")
764 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
768 dsa_options |= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
769 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
773 self.errf.write("Asking partner server %s to synchronize from us\n"
775 for part in (samdb.get_schema_basedn(),
776 samdb.get_config_basedn(),
777 samdb.get_root_basedn()):
778 nc = drsuapi.DsReplicaObjectIdentifier()
781 req1 = drsuapi.DsReplicaSyncRequest1()
782 req1.naming_context = nc;
783 req1.options = drsuapi.DRSUAPI_DRS_WRIT_REP
784 req1.source_dsa_guid = misc.GUID(ntds_guid)
787 drsuapiBind.DsReplicaSync(drsuapi_handle, 1, req1)
788 except RuntimeError as (werr, string):
789 if werr == 8452: #WERR_DS_DRA_NO_REPLICA
793 "Error while replicating out last local changes from '%s' for demotion, "
794 "re-enabling inbound replication\n" % part)
795 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
796 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
798 raise CommandError("Error while sending a DsReplicaSync for partition '%s'" % str(part), string)
800 remote_samdb = SamDB(url="ldap://%s" % server,
801 session_info=system_session(),
802 credentials=creds, lp=lp)
804 self.errf.write("Changing userControl and container\n")
805 res = remote_samdb.search(base=str(remote_samdb.domain_dn()),
806 expression="(&(objectClass=user)(sAMAccountName=%s$))" %
807 netbios_name.upper(),
808 attrs=["userAccountControl"])
810 uac = int(str(res[0]["userAccountControl"]))
814 "Error while demoting, re-enabling inbound replication\n")
815 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
816 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
818 raise CommandError("Error while changing account control", e)
822 "Error while demoting, re-enabling inbound replication")
823 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
824 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
826 raise CommandError("Unable to find object with samaccountName = %s$"
827 " in the remote dc" % netbios_name.upper())
831 uac &= ~(UF_SERVER_TRUST_ACCOUNT|UF_TRUSTED_FOR_DELEGATION|UF_PARTIAL_SECRETS_ACCOUNT)
832 uac |= UF_WORKSTATION_TRUST_ACCOUNT
837 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
838 ldb.FLAG_MOD_REPLACE,
839 "userAccountControl")
841 remote_samdb.modify(msg)
844 "Error while demoting, re-enabling inbound replication")
845 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
846 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
849 raise CommandError("Error while changing account control", e)
851 parent = msg.dn.parent()
852 dc_name = res[0].dn.get_rdn_value()
853 rdn = "CN=%s" % dc_name
855 # Let's move to the Computer container
859 computer_dn = ldb.Dn(remote_samdb, "CN=Computers,%s" % str(remote_samdb.domain_dn()))
860 res = remote_samdb.search(base=computer_dn, expression=rdn, scope=ldb.SCOPE_ONELEVEL)
863 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
864 scope=ldb.SCOPE_ONELEVEL)
865 while(len(res) != 0 and i < 100):
867 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
868 scope=ldb.SCOPE_ONELEVEL)
872 "Error while demoting, re-enabling inbound replication\n")
873 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
874 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
880 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
881 ldb.FLAG_MOD_REPLACE,
882 "userAccountControl")
884 remote_samdb.modify(msg)
886 raise CommandError("Unable to find a slot for renaming %s,"
887 " all names from %s-1 to %s-%d seemed used" %
888 (str(dc_dn), rdn, rdn, i - 9))
890 newrdn = "%s-%d" % (rdn, i)
893 newdn = ldb.Dn(remote_samdb, "%s,%s" % (newrdn, str(computer_dn)))
894 remote_samdb.rename(dc_dn, newdn)
897 "Error while demoting, re-enabling inbound replication\n")
898 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
899 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
905 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
906 ldb.FLAG_MOD_REPLACE,
907 "userAccountControl")
909 remote_samdb.modify(msg)
910 raise CommandError("Error while renaming %s to %s" % (str(dc_dn), str(newdn)), e)
913 server_dsa_dn = samdb.get_serverName()
914 domain = remote_samdb.get_root_basedn()
917 req1 = drsuapi.DsRemoveDSServerRequest1()
918 req1.server_dn = str(server_dsa_dn)
919 req1.domain_dn = str(domain)
922 drsuapiBind.DsRemoveDSServer(drsuapi_handle, 1, req1)
923 except RuntimeError as (werr, string):
924 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
926 "Error while demoting, re-enabling inbound replication\n")
927 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
928 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
934 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
935 ldb.FLAG_MOD_REPLACE,
936 "userAccountControl")
937 remote_samdb.modify(msg)
938 remote_samdb.rename(newdn, dc_dn)
939 if werr == 8452: #WERR_DS_DRA_NO_REPLICA
940 raise CommandError("The DC %s is not present on (already removed from) the remote server: " % server_dsa_dn, e)
942 raise CommandError("Error while sending a removeDsServer of %s: " % server_dsa_dn, e)
944 remove_dc.remove_sysvol_references(remote_samdb, logger, dc_name)
946 # These are objects under the computer account that should be deleted
947 for s in ("CN=Enterprise,CN=NTFRS Subscriptions",
948 "CN=%s, CN=NTFRS Subscriptions" % lp.get("realm"),
949 "CN=Domain system Volumes (SYSVOL Share), CN=NTFRS Subscriptions",
950 "CN=NTFRS Subscriptions"):
952 remote_samdb.delete(ldb.Dn(remote_samdb,
953 "%s,%s" % (s, str(newdn))))
954 except ldb.LdbError, l:
957 self.errf.write("Demote successful\n")
960 class cmd_domain_level(Command):
961 """Raise domain and forest function levels."""
963 synopsis = "%prog (show|raise <options>) [options]"
965 takes_optiongroups = {
966 "sambaopts": options.SambaOptions,
967 "credopts": options.CredentialsOptions,
968 "versionopts": options.VersionOptions,
972 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
973 metavar="URL", dest="H"),
974 Option("--quiet", help="Be quiet", action="store_true"),
975 Option("--forest-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
976 help="The forest function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)"),
977 Option("--domain-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
978 help="The domain function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)")
981 takes_args = ["subcommand"]
983 def run(self, subcommand, H=None, forest_level=None, domain_level=None,
984 quiet=False, credopts=None, sambaopts=None, versionopts=None):
985 lp = sambaopts.get_loadparm()
986 creds = credopts.get_credentials(lp, fallback_machine=True)
988 samdb = SamDB(url=H, session_info=system_session(),
989 credentials=creds, lp=lp)
991 domain_dn = samdb.domain_dn()
993 res_forest = samdb.search("CN=Partitions,%s" % samdb.get_config_basedn(),
994 scope=ldb.SCOPE_BASE, attrs=["msDS-Behavior-Version"])
995 assert len(res_forest) == 1
997 res_domain = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
998 attrs=["msDS-Behavior-Version", "nTMixedDomain"])
999 assert len(res_domain) == 1
1001 res_dc_s = samdb.search("CN=Sites,%s" % samdb.get_config_basedn(),
1002 scope=ldb.SCOPE_SUBTREE, expression="(objectClass=nTDSDSA)",
1003 attrs=["msDS-Behavior-Version"])
1004 assert len(res_dc_s) >= 1
1006 # default values, since "msDS-Behavior-Version" does not exist on Windows 2000 AD
1007 level_forest = DS_DOMAIN_FUNCTION_2000
1008 level_domain = DS_DOMAIN_FUNCTION_2000
1010 if "msDS-Behavior-Version" in res_forest[0]:
1011 level_forest = int(res_forest[0]["msDS-Behavior-Version"][0])
1012 if "msDS-Behavior-Version" in res_domain[0]:
1013 level_domain = int(res_domain[0]["msDS-Behavior-Version"][0])
1014 level_domain_mixed = int(res_domain[0]["nTMixedDomain"][0])
1017 for msg in res_dc_s:
1018 if "msDS-Behavior-Version" in msg:
1019 if min_level_dc is None or int(msg["msDS-Behavior-Version"][0]) < min_level_dc:
1020 min_level_dc = int(msg["msDS-Behavior-Version"][0])
1022 min_level_dc = DS_DOMAIN_FUNCTION_2000
1023 # well, this is the least
1026 if level_forest < DS_DOMAIN_FUNCTION_2000 or level_domain < DS_DOMAIN_FUNCTION_2000:
1027 raise CommandError("Domain and/or forest function level(s) is/are invalid. Correct them or reprovision!")
1028 if min_level_dc < DS_DOMAIN_FUNCTION_2000:
1029 raise CommandError("Lowest function level of a DC is invalid. Correct this or reprovision!")
1030 if level_forest > level_domain:
1031 raise CommandError("Forest function level is higher than the domain level(s). Correct this or reprovision!")
1032 if level_domain > min_level_dc:
1033 raise CommandError("Domain function level is higher than the lowest function level of a DC. Correct this or reprovision!")
1035 if subcommand == "show":
1036 self.message("Domain and forest function level for domain '%s'" % domain_dn)
1037 if level_forest == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1038 self.message("\nATTENTION: You run SAMBA 4 on a forest function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1039 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1040 self.message("\nATTENTION: You run SAMBA 4 on a domain function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1041 if min_level_dc == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1042 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)!")
1046 if level_forest == DS_DOMAIN_FUNCTION_2000:
1048 elif level_forest == DS_DOMAIN_FUNCTION_2003_MIXED:
1049 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1050 elif level_forest == DS_DOMAIN_FUNCTION_2003:
1052 elif level_forest == DS_DOMAIN_FUNCTION_2008:
1054 elif level_forest == DS_DOMAIN_FUNCTION_2008_R2:
1056 elif level_forest == DS_DOMAIN_FUNCTION_2012:
1058 elif level_forest == DS_DOMAIN_FUNCTION_2012_R2:
1061 outstr = "higher than 2012 R2"
1062 self.message("Forest function level: (Windows) " + outstr)
1064 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1065 outstr = "2000 mixed (NT4 DC support)"
1066 elif level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed == 0:
1068 elif level_domain == DS_DOMAIN_FUNCTION_2003_MIXED:
1069 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1070 elif level_domain == DS_DOMAIN_FUNCTION_2003:
1072 elif level_domain == DS_DOMAIN_FUNCTION_2008:
1074 elif level_domain == DS_DOMAIN_FUNCTION_2008_R2:
1076 elif level_domain == DS_DOMAIN_FUNCTION_2012:
1078 elif level_domain == DS_DOMAIN_FUNCTION_2012_R2:
1081 outstr = "higher than 2012 R2"
1082 self.message("Domain function level: (Windows) " + outstr)
1084 if min_level_dc == DS_DOMAIN_FUNCTION_2000:
1086 elif min_level_dc == DS_DOMAIN_FUNCTION_2003:
1088 elif min_level_dc == DS_DOMAIN_FUNCTION_2008:
1090 elif min_level_dc == DS_DOMAIN_FUNCTION_2008_R2:
1092 elif min_level_dc == DS_DOMAIN_FUNCTION_2012:
1094 elif min_level_dc == DS_DOMAIN_FUNCTION_2012_R2:
1097 outstr = "higher than 2012 R2"
1098 self.message("Lowest function level of a DC: (Windows) " + outstr)
1100 elif subcommand == "raise":
1103 if domain_level is not None:
1104 if domain_level == "2003":
1105 new_level_domain = DS_DOMAIN_FUNCTION_2003
1106 elif domain_level == "2008":
1107 new_level_domain = DS_DOMAIN_FUNCTION_2008
1108 elif domain_level == "2008_R2":
1109 new_level_domain = DS_DOMAIN_FUNCTION_2008_R2
1110 elif domain_level == "2012":
1111 new_level_domain = DS_DOMAIN_FUNCTION_2012
1112 elif domain_level == "2012_R2":
1113 new_level_domain = DS_DOMAIN_FUNCTION_2012_R2
1115 if new_level_domain <= level_domain and level_domain_mixed == 0:
1116 raise CommandError("Domain function level can't be smaller than or equal to the actual one!")
1117 if new_level_domain > min_level_dc:
1118 raise CommandError("Domain function level can't be higher than the lowest function level of a DC!")
1120 # Deactivate mixed/interim domain support
1121 if level_domain_mixed != 0:
1122 # Directly on the base DN
1124 m.dn = ldb.Dn(samdb, domain_dn)
1125 m["nTMixedDomain"] = ldb.MessageElement("0",
1126 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1130 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup") + ",CN=Partitions,%s" % samdb.get_config_basedn())
1131 m["nTMixedDomain"] = ldb.MessageElement("0",
1132 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1135 except ldb.LdbError, (enum, emsg):
1136 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1139 # Directly on the base DN
1141 m.dn = ldb.Dn(samdb, domain_dn)
1142 m["msDS-Behavior-Version"]= ldb.MessageElement(
1143 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1144 "msDS-Behavior-Version")
1148 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup")
1149 + ",CN=Partitions,%s" % samdb.get_config_basedn())
1150 m["msDS-Behavior-Version"]= ldb.MessageElement(
1151 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1152 "msDS-Behavior-Version")
1155 except ldb.LdbError, (enum, emsg):
1156 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1159 level_domain = new_level_domain
1160 msgs.append("Domain function level changed!")
1162 if forest_level is not None:
1163 if forest_level == "2003":
1164 new_level_forest = DS_DOMAIN_FUNCTION_2003
1165 elif forest_level == "2008":
1166 new_level_forest = DS_DOMAIN_FUNCTION_2008
1167 elif forest_level == "2008_R2":
1168 new_level_forest = DS_DOMAIN_FUNCTION_2008_R2
1169 elif forest_level == "2012":
1170 new_level_forest = DS_DOMAIN_FUNCTION_2012
1171 elif forest_level == "2012_R2":
1172 new_level_forest = DS_DOMAIN_FUNCTION_2012_R2
1174 if new_level_forest <= level_forest:
1175 raise CommandError("Forest function level can't be smaller than or equal to the actual one!")
1176 if new_level_forest > level_domain:
1177 raise CommandError("Forest function level can't be higher than the domain function level(s). Please raise it/them first!")
1180 m.dn = ldb.Dn(samdb, "CN=Partitions,%s" % samdb.get_config_basedn())
1181 m["msDS-Behavior-Version"]= ldb.MessageElement(
1182 str(new_level_forest), ldb.FLAG_MOD_REPLACE,
1183 "msDS-Behavior-Version")
1185 msgs.append("Forest function level changed!")
1186 msgs.append("All changes applied successfully!")
1187 self.message("\n".join(msgs))
1189 raise CommandError("invalid argument: '%s' (choose from 'show', 'raise')" % subcommand)
1192 class cmd_domain_passwordsettings(Command):
1193 """Set password settings.
1195 Password complexity, password lockout policy, history length,
1196 minimum password length, the minimum and maximum password age) on
1197 a Samba AD DC server.
1199 Use against a Windows DC is possible, but group policy will override it.
1202 synopsis = "%prog (show|set <options>) [options]"
1204 takes_optiongroups = {
1205 "sambaopts": options.SambaOptions,
1206 "versionopts": options.VersionOptions,
1207 "credopts": options.CredentialsOptions,
1211 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1212 metavar="URL", dest="H"),
1213 Option("--quiet", help="Be quiet", action="store_true"),
1214 Option("--complexity", type="choice", choices=["on","off","default"],
1215 help="The password complexity (on | off | default). Default is 'on'"),
1216 Option("--store-plaintext", type="choice", choices=["on","off","default"],
1217 help="Store plaintext passwords where account have 'store passwords with reversible encryption' set (on | off | default). Default is 'off'"),
1218 Option("--history-length",
1219 help="The password history length (<integer> | default). Default is 24.", type=str),
1220 Option("--min-pwd-length",
1221 help="The minimum password length (<integer> | default). Default is 7.", type=str),
1222 Option("--min-pwd-age",
1223 help="The minimum password age (<integer in days> | default). Default is 1.", type=str),
1224 Option("--max-pwd-age",
1225 help="The maximum password age (<integer in days> | default). Default is 43.", type=str),
1226 Option("--account-lockout-duration",
1227 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),
1228 Option("--account-lockout-threshold",
1229 help="The number of bad password attempts allowed before locking out the account (<integer> | default). Default is 0 (never lock out).", type=str),
1230 Option("--reset-account-lockout-after",
1231 help="After this time is elapsed, the recorded number of attempts restarts from zero (<integer> | default). Default is 30.", type=str),
1234 takes_args = ["subcommand"]
1236 def run(self, subcommand, H=None, min_pwd_age=None, max_pwd_age=None,
1237 quiet=False, complexity=None, store_plaintext=None, history_length=None,
1238 min_pwd_length=None, account_lockout_duration=None, account_lockout_threshold=None,
1239 reset_account_lockout_after=None, credopts=None, sambaopts=None,
1241 lp = sambaopts.get_loadparm()
1242 creds = credopts.get_credentials(lp)
1244 samdb = SamDB(url=H, session_info=system_session(),
1245 credentials=creds, lp=lp)
1247 domain_dn = samdb.domain_dn()
1248 res = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1249 attrs=["pwdProperties", "pwdHistoryLength", "minPwdLength",
1250 "minPwdAge", "maxPwdAge", "lockoutDuration", "lockoutThreshold",
1251 "lockOutObservationWindow"])
1252 assert(len(res) == 1)
1254 pwd_props = int(res[0]["pwdProperties"][0])
1255 pwd_hist_len = int(res[0]["pwdHistoryLength"][0])
1256 cur_min_pwd_len = int(res[0]["minPwdLength"][0])
1258 cur_min_pwd_age = int(abs(int(res[0]["minPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1259 if int(res[0]["maxPwdAge"][0]) == -0x8000000000000000:
1262 cur_max_pwd_age = int(abs(int(res[0]["maxPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1263 cur_account_lockout_threshold = int(res[0]["lockoutThreshold"][0])
1265 if int(res[0]["lockoutDuration"][0]) == -0x8000000000000000:
1266 cur_account_lockout_duration = 0
1268 cur_account_lockout_duration = abs(int(res[0]["lockoutDuration"][0])) / (1e7 * 60)
1269 cur_reset_account_lockout_after = abs(int(res[0]["lockOutObservationWindow"][0])) / (1e7 * 60)
1270 except Exception, e:
1271 raise CommandError("Could not retrieve password properties!", e)
1273 if subcommand == "show":
1274 self.message("Password informations for domain '%s'" % domain_dn)
1276 if pwd_props & DOMAIN_PASSWORD_COMPLEX != 0:
1277 self.message("Password complexity: on")
1279 self.message("Password complexity: off")
1280 if pwd_props & DOMAIN_PASSWORD_STORE_CLEARTEXT != 0:
1281 self.message("Store plaintext passwords: on")
1283 self.message("Store plaintext passwords: off")
1284 self.message("Password history length: %d" % pwd_hist_len)
1285 self.message("Minimum password length: %d" % cur_min_pwd_len)
1286 self.message("Minimum password age (days): %d" % cur_min_pwd_age)
1287 self.message("Maximum password age (days): %d" % cur_max_pwd_age)
1288 self.message("Account lockout duration (mins): %d" % cur_account_lockout_duration)
1289 self.message("Account lockout threshold (attempts): %d" % cur_account_lockout_threshold)
1290 self.message("Reset account lockout after (mins): %d" % cur_reset_account_lockout_after)
1291 elif subcommand == "set":
1294 m.dn = ldb.Dn(samdb, domain_dn)
1296 if complexity is not None:
1297 if complexity == "on" or complexity == "default":
1298 pwd_props = pwd_props | DOMAIN_PASSWORD_COMPLEX
1299 msgs.append("Password complexity activated!")
1300 elif complexity == "off":
1301 pwd_props = pwd_props & (~DOMAIN_PASSWORD_COMPLEX)
1302 msgs.append("Password complexity deactivated!")
1304 if store_plaintext is not None:
1305 if store_plaintext == "on" or store_plaintext == "default":
1306 pwd_props = pwd_props | DOMAIN_PASSWORD_STORE_CLEARTEXT
1307 msgs.append("Plaintext password storage for changed passwords activated!")
1308 elif store_plaintext == "off":
1309 pwd_props = pwd_props & (~DOMAIN_PASSWORD_STORE_CLEARTEXT)
1310 msgs.append("Plaintext password storage for changed passwords deactivated!")
1312 if complexity is not None or store_plaintext is not None:
1313 m["pwdProperties"] = ldb.MessageElement(str(pwd_props),
1314 ldb.FLAG_MOD_REPLACE, "pwdProperties")
1316 if history_length is not None:
1317 if history_length == "default":
1320 pwd_hist_len = int(history_length)
1322 if pwd_hist_len < 0 or pwd_hist_len > 24:
1323 raise CommandError("Password history length must be in the range of 0 to 24!")
1325 m["pwdHistoryLength"] = ldb.MessageElement(str(pwd_hist_len),
1326 ldb.FLAG_MOD_REPLACE, "pwdHistoryLength")
1327 msgs.append("Password history length changed!")
1329 if min_pwd_length is not None:
1330 if min_pwd_length == "default":
1333 min_pwd_len = int(min_pwd_length)
1335 if min_pwd_len < 0 or min_pwd_len > 14:
1336 raise CommandError("Minimum password length must be in the range of 0 to 14!")
1338 m["minPwdLength"] = ldb.MessageElement(str(min_pwd_len),
1339 ldb.FLAG_MOD_REPLACE, "minPwdLength")
1340 msgs.append("Minimum password length changed!")
1342 if min_pwd_age is not None:
1343 if min_pwd_age == "default":
1346 min_pwd_age = int(min_pwd_age)
1348 if min_pwd_age < 0 or min_pwd_age > 998:
1349 raise CommandError("Minimum password age must be in the range of 0 to 998!")
1352 min_pwd_age_ticks = -int(min_pwd_age * (24 * 60 * 60 * 1e7))
1354 m["minPwdAge"] = ldb.MessageElement(str(min_pwd_age_ticks),
1355 ldb.FLAG_MOD_REPLACE, "minPwdAge")
1356 msgs.append("Minimum password age changed!")
1358 if max_pwd_age is not None:
1359 if max_pwd_age == "default":
1362 max_pwd_age = int(max_pwd_age)
1364 if max_pwd_age < 0 or max_pwd_age > 999:
1365 raise CommandError("Maximum password age must be in the range of 0 to 999!")
1368 if max_pwd_age == 0:
1369 max_pwd_age_ticks = -0x8000000000000000
1371 max_pwd_age_ticks = -int(max_pwd_age * (24 * 60 * 60 * 1e7))
1373 m["maxPwdAge"] = ldb.MessageElement(str(max_pwd_age_ticks),
1374 ldb.FLAG_MOD_REPLACE, "maxPwdAge")
1375 msgs.append("Maximum password age changed!")
1377 if account_lockout_duration is not None:
1378 if account_lockout_duration == "default":
1379 account_lockout_duration = 30
1381 account_lockout_duration = int(account_lockout_duration)
1383 if account_lockout_duration < 0 or account_lockout_duration > 99999:
1384 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1387 if account_lockout_duration == 0:
1388 account_lockout_duration_ticks = -0x8000000000000000
1390 account_lockout_duration_ticks = -int(account_lockout_duration * (60 * 1e7))
1392 m["lockoutDuration"] = ldb.MessageElement(str(account_lockout_duration_ticks),
1393 ldb.FLAG_MOD_REPLACE, "lockoutDuration")
1394 msgs.append("Account lockout duration changed!")
1396 if account_lockout_threshold is not None:
1397 if account_lockout_threshold == "default":
1398 account_lockout_threshold = 0
1400 account_lockout_threshold = int(account_lockout_threshold)
1402 m["lockoutThreshold"] = ldb.MessageElement(str(account_lockout_threshold),
1403 ldb.FLAG_MOD_REPLACE, "lockoutThreshold")
1404 msgs.append("Account lockout threshold changed!")
1406 if reset_account_lockout_after is not None:
1407 if reset_account_lockout_after == "default":
1408 reset_account_lockout_after = 30
1410 reset_account_lockout_after = int(reset_account_lockout_after)
1412 if reset_account_lockout_after < 0 or reset_account_lockout_after > 99999:
1413 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1416 if reset_account_lockout_after == 0:
1417 reset_account_lockout_after_ticks = -0x8000000000000000
1419 reset_account_lockout_after_ticks = -int(reset_account_lockout_after * (60 * 1e7))
1421 m["lockOutObservationWindow"] = ldb.MessageElement(str(reset_account_lockout_after_ticks),
1422 ldb.FLAG_MOD_REPLACE, "lockOutObservationWindow")
1423 msgs.append("Duration to reset account lockout after changed!")
1425 if max_pwd_age > 0 and min_pwd_age >= max_pwd_age:
1426 raise CommandError("Maximum password age (%d) must be greater than minimum password age (%d)!" % (max_pwd_age, min_pwd_age))
1429 raise CommandError("You must specify at least one option to set. Try --help")
1431 msgs.append("All changes applied successfully!")
1432 self.message("\n".join(msgs))
1434 raise CommandError("Wrong argument '%s'!" % subcommand)
1437 class cmd_domain_classicupgrade(Command):
1438 """Upgrade from Samba classic (NT4-like) database to Samba AD DC database.
1440 Specify either a directory with all Samba classic DC databases and state files (with --dbdir) or
1441 the testparm utility from your classic installation (with --testparm).
1444 synopsis = "%prog [options] <classic_smb_conf>"
1446 takes_optiongroups = {
1447 "sambaopts": options.SambaOptions,
1448 "versionopts": options.VersionOptions
1452 Option("--dbdir", type="string", metavar="DIR",
1453 help="Path to samba classic DC database directory"),
1454 Option("--testparm", type="string", metavar="PATH",
1455 help="Path to samba classic DC testparm utility from the previous installation. This allows the default paths of the previous installation to be followed"),
1456 Option("--targetdir", type="string", metavar="DIR",
1457 help="Path prefix where the new Samba 4.0 AD domain should be initialised"),
1458 Option("--quiet", help="Be quiet", action="store_true"),
1459 Option("--verbose", help="Be verbose", action="store_true"),
1460 Option("--use-xattrs", type="choice", choices=["yes","no","auto"], metavar="[yes|no|auto]",
1461 help="Define if we should use the native fs capabilities or a tdb file for storing attributes likes ntacl, auto tries to make an inteligent guess based on the user rights and system capabilities", default="auto"),
1462 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
1463 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
1464 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
1465 "BIND9_FLATFILE uses bind9 text database to store zone information, "
1466 "BIND9_DLZ uses samba4 AD to store zone information, "
1467 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
1468 default="SAMBA_INTERNAL")
1472 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
1473 action="store_true")
1475 if samba.is_ntvfs_fileserver_built():
1476 takes_options.extend(ntvfs_options)
1478 takes_args = ["smbconf"]
1480 def run(self, smbconf=None, targetdir=None, dbdir=None, testparm=None,
1481 quiet=False, verbose=False, use_xattrs=None, sambaopts=None, versionopts=None,
1482 dns_backend=None, use_ntvfs=False):
1484 if not os.path.exists(smbconf):
1485 raise CommandError("File %s does not exist" % smbconf)
1487 if testparm and not os.path.exists(testparm):
1488 raise CommandError("Testparm utility %s does not exist" % testparm)
1490 if dbdir and not os.path.exists(dbdir):
1491 raise CommandError("Directory %s does not exist" % dbdir)
1493 if not dbdir and not testparm:
1494 raise CommandError("Please specify either dbdir or testparm")
1496 logger = self.get_logger()
1498 logger.setLevel(logging.DEBUG)
1500 logger.setLevel(logging.WARNING)
1502 logger.setLevel(logging.INFO)
1504 if dbdir and testparm:
1505 logger.warning("both dbdir and testparm specified, ignoring dbdir.")
1508 lp = sambaopts.get_loadparm()
1510 s3conf = s3param.get_context()
1513 s3conf.set("realm", sambaopts.realm)
1515 if targetdir is not None:
1516 if not os.path.isdir(targetdir):
1520 if use_xattrs == "yes":
1522 elif use_xattrs == "auto" and not s3conf.get("posix:eadb"):
1524 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
1526 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
1529 samba.ntacls.setntacl(lp, tmpfile.name,
1530 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
1533 # FIXME: Don't catch all exceptions here
1534 logger.info("You are not root or your system does not support xattr, using tdb backend for attributes. "
1535 "If you intend to use this provision in production, rerun the script as root on a system supporting xattrs.")
1539 # Set correct default values from dbdir or testparm
1542 paths["state directory"] = dbdir
1543 paths["private dir"] = dbdir
1544 paths["lock directory"] = dbdir
1545 paths["smb passwd file"] = dbdir + "/smbpasswd"
1547 paths["state directory"] = get_testparm_var(testparm, smbconf, "state directory")
1548 paths["private dir"] = get_testparm_var(testparm, smbconf, "private dir")
1549 paths["smb passwd file"] = get_testparm_var(testparm, smbconf, "smb passwd file")
1550 paths["lock directory"] = get_testparm_var(testparm, smbconf, "lock directory")
1551 # "testparm" from Samba 3 < 3.4.x is not aware of the parameter
1552 # "state directory", instead make use of "lock directory"
1553 if len(paths["state directory"]) == 0:
1554 paths["state directory"] = paths["lock directory"]
1557 s3conf.set(p, paths[p])
1559 # load smb.conf parameters
1560 logger.info("Reading smb.conf")
1561 s3conf.load(smbconf)
1562 samba3 = Samba3(smbconf, s3conf)
1564 logger.info("Provisioning")
1565 upgrade_from_samba3(samba3, logger, targetdir, session_info=system_session(),
1566 useeadb=eadb, dns_backend=dns_backend, use_ntvfs=use_ntvfs)
1569 class cmd_domain_samba3upgrade(cmd_domain_classicupgrade):
1570 __doc__ = cmd_domain_classicupgrade.__doc__
1572 # This command is present for backwards compatibility only,
1573 # and should not be shown.
1577 class LocalDCCredentialsOptions(options.CredentialsOptions):
1578 def __init__(self, parser):
1579 options.CredentialsOptions.__init__(self, parser, special_name="local-dc")
1581 class DomainTrustCommand(Command):
1582 """List domain trusts."""
1585 Command.__init__(self)
1586 self.local_lp = None
1588 self.local_server = None
1589 self.local_binding_string = None
1590 self.local_creds = None
1592 self.remote_server = None
1593 self.remote_binding_string = None
1594 self.remote_creds = None
1596 WERR_OK = 0x00000000
1597 WERR_INVALID_FUNCTION = 0x00000001
1598 WERR_NERR_ACFNOTLOADED = 0x000008B3
1600 NT_STATUS_NOT_FOUND = 0xC0000225
1601 NT_STATUS_OBJECT_NAME_NOT_FOUND = 0xC0000034
1602 NT_STATUS_INVALID_PARAMETER = 0xC000000D
1603 NT_STATUS_INVALID_INFO_CLASS = 0xC0000003
1604 NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE = 0xC002002E
1606 def _uint32(self, v):
1607 return ctypes.c_uint32(v).value
1609 def check_runtime_error(self, runtime, val):
1613 err32 = self._uint32(runtime[0])
1619 class LocalRuntimeError(CommandError):
1620 def __init__(exception_self, self, runtime, message):
1621 err32 = self._uint32(runtime[0])
1623 msg = "LOCAL_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1624 self.local_server, message, err32, errstr)
1625 CommandError.__init__(exception_self, msg)
1627 class RemoteRuntimeError(CommandError):
1628 def __init__(exception_self, self, runtime, message):
1629 err32 = self._uint32(runtime[0])
1631 msg = "REMOTE_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1632 self.remote_server, message, err32, errstr)
1633 CommandError.__init__(exception_self, msg)
1635 class LocalLdbError(CommandError):
1636 def __init__(exception_self, self, ldb_error, message):
1637 errval = ldb_error[0]
1638 errstr = ldb_error[1]
1639 msg = "LOCAL_DC[%s]: %s - ERROR(%d) - %s" % (
1640 self.local_server, message, errval, errstr)
1641 CommandError.__init__(exception_self, msg)
1643 def setup_local_server(self, sambaopts, localdcopts):
1644 if self.local_server is not None:
1645 return self.local_server
1647 lp = sambaopts.get_loadparm()
1649 local_server = localdcopts.ipaddress
1650 if local_server is None:
1651 server_role = lp.server_role()
1652 if server_role != "ROLE_ACTIVE_DIRECTORY_DC":
1653 raise CommandError("Invalid server_role %s" % (server_role))
1654 local_server = lp.get('netbios name')
1655 local_transport = "ncalrpc"
1656 local_binding_options = ""
1657 local_binding_options += ",auth_type=ncalrpc_as_system"
1658 local_ldap_url = None
1661 local_transport = "ncacn_np"
1662 local_binding_options = ""
1663 local_ldap_url = "ldap://%s" % local_server
1664 local_creds = localdcopts.get_credentials(lp)
1668 self.local_server = local_server
1669 self.local_binding_string = "%s:%s[%s]" % (local_transport, local_server, local_binding_options)
1670 self.local_ldap_url = local_ldap_url
1671 self.local_creds = local_creds
1672 return self.local_server
1674 def new_local_lsa_connection(self):
1675 return lsa.lsarpc(self.local_binding_string, self.local_lp, self.local_creds)
1677 def new_local_netlogon_connection(self):
1678 return netlogon.netlogon(self.local_binding_string, self.local_lp, self.local_creds)
1680 def new_local_ldap_connection(self):
1681 return SamDB(url=self.local_ldap_url,
1682 session_info=system_session(),
1683 credentials=self.local_creds,
1686 def setup_remote_server(self, credopts, domain,
1688 require_writable=True):
1691 assert require_writable
1693 if self.remote_server is not None:
1694 return self.remote_server
1696 self.remote_server = "__unknown__remote_server__.%s" % domain
1697 assert self.local_server is not None
1699 remote_creds = credopts.get_credentials(self.local_lp)
1700 remote_server = credopts.ipaddress
1701 remote_binding_options = ""
1703 # TODO: we should also support NT4 domains
1704 # we could use local_netlogon.netr_DsRGetDCNameEx2() with the remote domain name
1705 # and delegate NBT or CLDAP to the local netlogon server
1707 remote_net = Net(remote_creds, self.local_lp, server=remote_server)
1708 remote_flags = nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS
1709 if require_writable:
1710 remote_flags |= nbt.NBT_SERVER_WRITABLE
1712 remote_flags |= nbt.NBT_SERVER_PDC
1713 remote_info = remote_net.finddc(flags=remote_flags, domain=domain, address=remote_server)
1715 raise CommandError("Failed to find a writeable DC for domain '%s'" % domain)
1717 nbt.NBT_SERVER_PDC: "PDC",
1718 nbt.NBT_SERVER_GC: "GC",
1719 nbt.NBT_SERVER_LDAP: "LDAP",
1720 nbt.NBT_SERVER_DS: "DS",
1721 nbt.NBT_SERVER_KDC: "KDC",
1722 nbt.NBT_SERVER_TIMESERV: "TIMESERV",
1723 nbt.NBT_SERVER_CLOSEST: "CLOSEST",
1724 nbt.NBT_SERVER_WRITABLE: "WRITABLE",
1725 nbt.NBT_SERVER_GOOD_TIMESERV: "GOOD_TIMESERV",
1726 nbt.NBT_SERVER_NDNC: "NDNC",
1727 nbt.NBT_SERVER_SELECT_SECRET_DOMAIN_6: "SELECT_SECRET_DOMAIN_6",
1728 nbt.NBT_SERVER_FULL_SECRET_DOMAIN_6: "FULL_SECRET_DOMAIN_6",
1729 nbt.NBT_SERVER_ADS_WEB_SERVICE: "ADS_WEB_SERVICE",
1730 nbt.NBT_SERVER_DS_8: "DS_8",
1731 nbt.NBT_SERVER_HAS_DNS_NAME: "HAS_DNS_NAME",
1732 nbt.NBT_SERVER_IS_DEFAULT_NC: "IS_DEFAULT_NC",
1733 nbt.NBT_SERVER_FOREST_ROOT: "FOREST_ROOT",
1735 server_type_string = self.generic_bitmap_to_string(flag_map,
1736 remote_info.server_type, names_only=True)
1737 self.outf.write("RemoteDC Netbios[%s] DNS[%s] ServerType[%s]\n" % (
1738 remote_info.pdc_name,
1739 remote_info.pdc_dns_name,
1740 server_type_string))
1742 self.remote_server = remote_info.pdc_dns_name
1743 self.remote_binding_string="ncacn_np:%s[%s]" % (self.remote_server, remote_binding_options)
1744 self.remote_creds = remote_creds
1745 return self.remote_server
1747 def new_remote_lsa_connection(self):
1748 return lsa.lsarpc(self.remote_binding_string, self.local_lp, self.remote_creds)
1750 def new_remote_netlogon_connection(self):
1751 return netlogon.netlogon(self.remote_binding_string, self.local_lp, self.remote_creds)
1753 def get_lsa_info(self, conn, policy_access):
1754 objectAttr = lsa.ObjectAttribute()
1755 objectAttr.sec_qos = lsa.QosInfo()
1757 policy = conn.OpenPolicy2(''.decode('utf-8'),
1758 objectAttr, policy_access)
1760 info = conn.QueryInfoPolicy2(policy, lsa.LSA_POLICY_INFO_DNS)
1762 return (policy, info)
1764 def get_netlogon_dc_info(self, conn, server):
1765 info = conn.netr_DsRGetDCNameEx2(server,
1766 None, 0, None, None, None,
1767 netlogon.DS_RETURN_DNS_NAME)
1770 def netr_DomainTrust_to_name(self, t):
1771 if t.trust_type == lsa.LSA_TRUST_TYPE_DOWNLEVEL:
1772 return t.netbios_name
1776 def netr_DomainTrust_to_type(self, a, t):
1778 primary_parent = None
1780 if _t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
1782 if not _t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1783 primary_parent = a[_t.parent_index]
1786 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1787 if t is primary_parent:
1790 if t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1793 parent = a[t.parent_index]
1794 if parent is primary:
1799 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1804 def netr_DomainTrust_to_transitive(self, t):
1805 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1808 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE:
1811 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1816 def netr_DomainTrust_to_direction(self, t):
1817 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND and \
1818 t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1821 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND:
1824 if t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1829 def generic_enum_to_string(self, e_dict, v, names_only=False):
1833 v32 = self._uint32(v)
1834 w = "__unknown__%08X__" % v32
1836 r = "0x%x (%s)" % (v, w)
1839 def generic_bitmap_to_string(self, b_dict, v, names_only=False):
1844 for b in sorted(b_dict.keys()):
1851 c32 = self._uint32(c)
1852 s += ["__unknown_%08X__" % c32]
1857 r = "0x%x (%s)" % (v, w)
1860 def trustType_string(self, v):
1862 lsa.LSA_TRUST_TYPE_DOWNLEVEL : "DOWNLEVEL",
1863 lsa.LSA_TRUST_TYPE_UPLEVEL : "UPLEVEL",
1864 lsa.LSA_TRUST_TYPE_MIT : "MIT",
1865 lsa.LSA_TRUST_TYPE_DCE : "DCE",
1867 return self.generic_enum_to_string(types, v)
1869 def trustDirection_string(self, v):
1871 lsa.LSA_TRUST_DIRECTION_INBOUND |
1872 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "BOTH",
1873 lsa.LSA_TRUST_DIRECTION_INBOUND : "INBOUND",
1874 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "OUTBOUND",
1876 return self.generic_enum_to_string(directions, v)
1878 def trustAttributes_string(self, v):
1880 lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE : "NON_TRANSITIVE",
1881 lsa.LSA_TRUST_ATTRIBUTE_UPLEVEL_ONLY : "UPLEVEL_ONLY",
1882 lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN : "QUARANTINED_DOMAIN",
1883 lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE : "FOREST_TRANSITIVE",
1884 lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION : "CROSS_ORGANIZATION",
1885 lsa.LSA_TRUST_ATTRIBUTE_WITHIN_FOREST : "WITHIN_FOREST",
1886 lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL : "TREAT_AS_EXTERNAL",
1887 lsa.LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION : "USES_RC4_ENCRYPTION",
1889 return self.generic_bitmap_to_string(attributes, v)
1891 def kerb_EncTypes_string(self, v):
1893 security.KERB_ENCTYPE_DES_CBC_CRC : "DES_CBC_CRC",
1894 security.KERB_ENCTYPE_DES_CBC_MD5 : "DES_CBC_MD5",
1895 security.KERB_ENCTYPE_RC4_HMAC_MD5 : "RC4_HMAC_MD5",
1896 security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96 : "AES128_CTS_HMAC_SHA1_96",
1897 security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96 : "AES256_CTS_HMAC_SHA1_96",
1898 security.KERB_ENCTYPE_FAST_SUPPORTED : "FAST_SUPPORTED",
1899 security.KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED : "COMPOUND_IDENTITY_SUPPORTED",
1900 security.KERB_ENCTYPE_CLAIMS_SUPPORTED : "CLAIMS_SUPPORTED",
1901 security.KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED : "RESOURCE_SID_COMPRESSION_DISABLED",
1903 return self.generic_bitmap_to_string(enctypes, v)
1905 def entry_tln_status(self, e_flags, ):
1907 return "Status[Enabled]"
1910 lsa.LSA_TLN_DISABLED_NEW : "Disabled-New",
1911 lsa.LSA_TLN_DISABLED_ADMIN : "Disabled",
1912 lsa.LSA_TLN_DISABLED_CONFLICT : "Disabled-Conflicting",
1914 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
1916 def entry_dom_status(self, e_flags):
1918 return "Status[Enabled]"
1921 lsa.LSA_SID_DISABLED_ADMIN : "Disabled-SID",
1922 lsa.LSA_SID_DISABLED_CONFLICT : "Disabled-SID-Conflicting",
1923 lsa.LSA_NB_DISABLED_ADMIN : "Disabled-NB",
1924 lsa.LSA_NB_DISABLED_CONFLICT : "Disabled-NB-Conflicting",
1926 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
1928 def write_forest_trust_info(self, fti, tln=None, collisions=None):
1930 tln_string = " TDO[%s]" % tln
1934 self.outf.write("Namespaces[%d]%s:\n" % (
1935 len(fti.entries), tln_string))
1937 for i in xrange(0, len(fti.entries)):
1941 collision_string = ""
1943 if collisions is not None:
1944 for c in collisions.entries:
1948 collision_string = " Collision[%s]" % (c.name.string)
1950 d = e.forest_trust_data
1951 if e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
1952 self.outf.write("TLN: %-32s DNS[*.%s]%s\n" % (
1953 self.entry_tln_status(flags),
1954 d.string, collision_string))
1955 elif e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
1956 self.outf.write("TLN_EX: %-29s DNS[*.%s]\n" % (
1958 elif e.type == lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
1959 self.outf.write("DOM: %-32s DNS[%s] Netbios[%s] SID[%s]%s\n" % (
1960 self.entry_dom_status(flags),
1961 d.dns_domain_name.string,
1962 d.netbios_domain_name.string,
1963 d.domain_sid, collision_string))
1966 class cmd_domain_trust_list(DomainTrustCommand):
1967 """List domain trusts."""
1969 synopsis = "%prog [options]"
1971 takes_optiongroups = {
1972 "sambaopts": options.SambaOptions,
1973 "versionopts": options.VersionOptions,
1974 "localdcopts": LocalDCCredentialsOptions,
1980 def run(self, sambaopts=None, versionopts=None, localdcopts=None):
1982 local_server = self.setup_local_server(sambaopts, localdcopts)
1984 local_netlogon = self.new_local_netlogon_connection()
1985 except RuntimeError as error:
1986 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
1989 local_netlogon_trusts = local_netlogon.netr_DsrEnumerateDomainTrusts(local_server,
1990 netlogon.NETR_TRUST_FLAG_IN_FOREST |
1991 netlogon.NETR_TRUST_FLAG_OUTBOUND |
1992 netlogon.NETR_TRUST_FLAG_INBOUND)
1993 except RuntimeError as error:
1994 if self.check_runtime_error(error, self.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
1995 # TODO: we could implement a fallback to lsa.EnumTrustDom()
1996 raise CommandError("LOCAL_DC[%s]: netr_DsrEnumerateDomainTrusts not supported." % (
1998 raise self.LocalRuntimeError(self, error, "netr_DsrEnumerateDomainTrusts failed")
2000 a = local_netlogon_trusts.array
2002 if t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
2004 self.outf.write("%-14s %-15s %-19s %s\n" % (
2005 "Type[%s]" % self.netr_DomainTrust_to_type(a, t),
2006 "Transitive[%s]" % self.netr_DomainTrust_to_transitive(t),
2007 "Direction[%s]" % self.netr_DomainTrust_to_direction(t),
2008 "Name[%s]" % self.netr_DomainTrust_to_name(t)))
2011 class cmd_domain_trust_show(DomainTrustCommand):
2012 """Show trusted domain details."""
2014 synopsis = "%prog NAME [options]"
2016 takes_optiongroups = {
2017 "sambaopts": options.SambaOptions,
2018 "versionopts": options.VersionOptions,
2019 "localdcopts": LocalDCCredentialsOptions,
2025 takes_args = ["domain"]
2027 def run(self, domain, sambaopts=None, versionopts=None, localdcopts=None):
2029 local_server = self.setup_local_server(sambaopts, localdcopts)
2031 local_lsa = self.new_local_lsa_connection()
2032 except RuntimeError as error:
2033 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2036 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2037 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2038 except RuntimeError as error:
2039 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2041 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2042 local_lsa_info.name.string,
2043 local_lsa_info.dns_domain.string,
2044 local_lsa_info.sid))
2046 lsaString = lsa.String()
2047 lsaString.string = domain
2049 local_tdo_full = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2050 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2051 local_tdo_info = local_tdo_full.info_ex
2052 local_tdo_posix = local_tdo_full.posix_offset
2053 except RuntimeError as error:
2054 if self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2055 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2057 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(FULL_INFO) failed")
2060 local_tdo_enctypes = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2061 lsaString, lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES)
2062 except RuntimeError as error:
2063 if self.check_runtime_error(error, self.NT_STATUS_INVALID_PARAMETER):
2065 if self.check_runtime_error(error, self.NT_STATUS_INVALID_INFO_CLASS):
2068 if error is not None:
2069 raise self.LocalRuntimeError(self, error,
2070 "QueryTrustedDomainInfoByName(SUPPORTED_ENCRYPTION_TYPES) failed")
2072 local_tdo_enctypes = lsa.TrustDomainInfoSupportedEncTypes()
2073 local_tdo_enctypes.enc_types = 0
2076 local_tdo_forest = None
2077 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2078 local_tdo_forest = local_lsa.lsaRQueryForestTrustInformation(local_policy,
2079 lsaString, lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
2080 except RuntimeError as error:
2081 if self.check_runtime_error(error, self.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
2083 if self.check_runtime_error(error, self.NT_STATUS_NOT_FOUND):
2085 if error is not None:
2086 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation failed")
2088 local_tdo_forest = lsa.ForestTrustInformation()
2089 local_tdo_forest.count = 0
2090 local_tdo_forest.entries = []
2092 self.outf.write("TrusteDomain:\n\n");
2093 self.outf.write("NetbiosName: %s\n" % local_tdo_info.netbios_name.string)
2094 if local_tdo_info.netbios_name.string != local_tdo_info.domain_name.string:
2095 self.outf.write("DnsName: %s\n" % local_tdo_info.domain_name.string)
2096 self.outf.write("SID: %s\n" % local_tdo_info.sid)
2097 self.outf.write("Type: %s\n" % self.trustType_string(local_tdo_info.trust_type))
2098 self.outf.write("Direction: %s\n" % self.trustDirection_string(local_tdo_info.trust_direction))
2099 self.outf.write("Attributes: %s\n" % self.trustAttributes_string(local_tdo_info.trust_attributes))
2100 posix_offset_u32 = ctypes.c_uint32(local_tdo_posix.posix_offset).value
2101 posix_offset_i32 = ctypes.c_int32(local_tdo_posix.posix_offset).value
2102 self.outf.write("PosixOffset: 0x%08X (%d)\n" % (posix_offset_u32, posix_offset_i32))
2103 self.outf.write("kerb_EncTypes: %s\n" % self.kerb_EncTypes_string(local_tdo_enctypes.enc_types))
2105 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2106 self.write_forest_trust_info(local_tdo_forest,
2107 tln=local_tdo_info.domain_name.string)
2111 class cmd_domain_trust_create(DomainTrustCommand):
2112 """Create a domain or forest trust."""
2114 synopsis = "%prog DOMAIN [options]"
2116 takes_optiongroups = {
2117 "sambaopts": options.SambaOptions,
2118 "versionopts": options.VersionOptions,
2119 "credopts": options.CredentialsOptions,
2120 "localdcopts": LocalDCCredentialsOptions,
2124 Option("--type", type="choice", metavar="TYPE",
2125 choices=["external", "forest"],
2126 help="The type of the trust: 'external' or 'forest'.",
2128 default="external"),
2129 Option("--direction", type="choice", metavar="DIRECTION",
2130 choices=["incoming", "outgoing", "both"],
2131 help="The trust direction: 'incoming', 'outgoing' or 'both'.",
2132 dest='trust_direction',
2134 Option("--create-location", type="choice", metavar="LOCATION",
2135 choices=["local", "both"],
2136 help="Where to create the trusted domain object: 'local' or 'both'.",
2137 dest='create_location',
2139 Option("--cross-organisation", action="store_true",
2140 help="The related domains does not belong to the same organisation.",
2141 dest='cross_organisation',
2143 Option("--quarantined", type="choice", metavar="yes|no",
2144 choices=["yes", "no", None],
2145 help="Special SID filtering rules are applied to the trust. "
2146 "With --type=external the default is yes. "
2147 "With --type=forest the default is no.",
2148 dest='quarantined_arg',
2150 Option("--not-transitive", action="store_true",
2151 help="The forest trust is not transitive.",
2152 dest='not_transitive',
2154 Option("--treat-as-external", action="store_true",
2155 help="The treat the forest trust as external.",
2156 dest='treat_as_external',
2158 Option("--no-aes-keys", action="store_false",
2159 help="The trust uses aes kerberos keys.",
2160 dest='use_aes_keys',
2162 Option("--skip-validation", action="store_false",
2163 help="Skip validation of the trust.",
2168 takes_args = ["domain"]
2170 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2171 trust_type=None, trust_direction=None, create_location=None,
2172 cross_organisation=False, quarantined_arg=None,
2173 not_transitive=False, treat_as_external=False,
2174 use_aes_keys=False, validate=True):
2176 lsaString = lsa.String()
2179 if quarantined_arg is None:
2180 if trust_type == 'external':
2182 elif quarantined_arg == 'yes':
2185 if trust_type != 'forest':
2187 raise CommandError("--not-transitive requires --type=forest")
2188 if treat_as_external:
2189 raise CommandError("--treat-as-external requires --type=forest")
2193 enc_types = lsa.TrustDomainInfoSupportedEncTypes()
2194 enc_types.enc_types = security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96
2195 enc_types.enc_types |= security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96
2197 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2198 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2199 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2201 local_trust_info = lsa.TrustDomainInfoInfoEx()
2202 local_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2203 local_trust_info.trust_direction = 0
2204 if trust_direction == "both":
2205 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2206 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2207 elif trust_direction == "incoming":
2208 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2209 elif trust_direction == "outgoing":
2210 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2211 local_trust_info.trust_attributes = 0
2212 if cross_organisation:
2213 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2215 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2216 if trust_type == "forest":
2217 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2219 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2220 if treat_as_external:
2221 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2223 def get_password(name):
2226 if password is not None and password is not '':
2228 password = getpass("New %s Password: " % name)
2229 passwordverify = getpass("Retype %s Password: " % name)
2230 if not password == passwordverify:
2232 self.outf.write("Sorry, passwords do not match.\n")
2234 incoming_secret = None
2235 outgoing_secret = None
2236 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2237 if create_location == "local":
2238 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2239 incoming_password = get_password("Incoming Trust")
2240 incoming_secret = string_to_byte_array(incoming_password.encode('utf-16-le'))
2241 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2242 outgoing_password = get_password("Outgoing Trust")
2243 outgoing_secret = string_to_byte_array(outgoing_password.encode('utf-16-le'))
2245 remote_trust_info = None
2247 # We use 240 random bytes.
2248 # Windows uses 28 or 240 random bytes. I guess it's
2249 # based on the trust type external vs. forest.
2251 # The initial trust password can be up to 512 bytes
2252 # while the versioned passwords used for periodic updates
2253 # can only be up to 498 bytes, as netr_ServerPasswordSet2()
2254 # needs to pass the NL_PASSWORD_VERSION structure within the
2255 # 512 bytes and a 2 bytes confounder is required.
2257 def random_trust_secret(length, use_aes_keys=True):
2258 secret = [0] * length
2260 pw1 = samba.generate_random_password(length/2, length/2)
2261 if not use_aes_keys:
2262 # With arcfour-hmac-md5 we have to use valid utf16
2263 # in order to generate the correct pre-auth key
2264 # based on a utf8 password.
2266 # We can remove this once our client libraries
2267 # support using the correct NTHASH.
2268 return string_to_byte_array(pw1.encode('utf-16-le'))
2270 # We mix characters from generate_random_password
2271 # with random numbers from random.randint()
2272 for i in range(len(secret)):
2274 secret[i] = ord(pw1[i])
2276 secret[i] = random.randint(0, 255)
2280 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2281 incoming_secret = random_trust_secret(240, use_aes_keys=use_aes_keys)
2282 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2283 outgoing_secret = random_trust_secret(240, use_aes_keys=use_aes_keys)
2285 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2286 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2288 remote_trust_info = lsa.TrustDomainInfoInfoEx()
2289 remote_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2290 remote_trust_info.trust_direction = 0
2291 if trust_direction == "both":
2292 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2293 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2294 elif trust_direction == "incoming":
2295 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2296 elif trust_direction == "outgoing":
2297 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2298 remote_trust_info.trust_attributes = 0
2299 if cross_organisation:
2300 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2302 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2303 if trust_type == "forest":
2304 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2306 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2307 if treat_as_external:
2308 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2310 local_server = self.setup_local_server(sambaopts, localdcopts)
2312 local_lsa = self.new_local_lsa_connection()
2313 except RuntimeError as error:
2314 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2317 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2318 except RuntimeError as error:
2319 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2321 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2322 local_lsa_info.name.string,
2323 local_lsa_info.dns_domain.string,
2324 local_lsa_info.sid))
2327 remote_server = self.setup_remote_server(credopts, domain)
2328 except RuntimeError as error:
2329 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2332 remote_lsa = self.new_remote_lsa_connection()
2333 except RuntimeError as error:
2334 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2337 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2338 except RuntimeError as error:
2339 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2341 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2342 remote_lsa_info.name.string,
2343 remote_lsa_info.dns_domain.string,
2344 remote_lsa_info.sid))
2346 local_trust_info.domain_name.string = remote_lsa_info.dns_domain.string
2347 local_trust_info.netbios_name.string = remote_lsa_info.name.string
2348 local_trust_info.sid = remote_lsa_info.sid
2350 if remote_trust_info:
2351 remote_trust_info.domain_name.string = local_lsa_info.dns_domain.string
2352 remote_trust_info.netbios_name.string = local_lsa_info.name.string
2353 remote_trust_info.sid = local_lsa_info.sid
2356 lsaString.string = local_trust_info.domain_name.string
2357 local_old_netbios = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2358 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2359 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2360 except RuntimeError as error:
2361 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2362 raise self.LocalRuntimeError(self, error,
2363 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2367 lsaString.string = local_trust_info.netbios_name.string
2368 local_old_dns = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2369 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2370 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2371 except RuntimeError as error:
2372 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2373 raise self.LocalRuntimeError(self, error,
2374 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2377 if remote_trust_info:
2379 lsaString.string = remote_trust_info.domain_name.string
2380 remote_old_netbios = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2381 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2382 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2383 except RuntimeError as error:
2384 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2385 raise self.RemoteRuntimeError(self, error,
2386 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2390 lsaString.string = remote_trust_info.netbios_name.string
2391 remote_old_dns = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2392 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2393 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2394 except RuntimeError as error:
2395 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2396 raise self.RemoteRuntimeError(self, error,
2397 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2401 local_netlogon = self.new_local_netlogon_connection()
2402 except RuntimeError as error:
2403 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2406 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
2407 except RuntimeError as error:
2408 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
2410 if remote_trust_info:
2412 remote_netlogon = self.new_remote_netlogon_connection()
2413 except RuntimeError as error:
2414 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2417 remote_netlogon_info = self.get_netlogon_dc_info(remote_netlogon, remote_server)
2418 except RuntimeError as error:
2419 raise self.RemoteRuntimeError(self, error, "failed to get netlogon dc info")
2421 def generate_AuthInOutBlob(secret, update_time):
2423 blob = drsblobs.trustAuthInOutBlob()
2428 clear = drsblobs.AuthInfoClear()
2429 clear.size = len(secret)
2430 clear.password = secret
2432 info = drsblobs.AuthenticationInformation()
2433 info.LastUpdateTime = samba.unix2nttime(update_time)
2434 info.AuthType = lsa.TRUST_AUTH_TYPE_CLEAR
2435 info.AuthInfo = clear
2437 array = drsblobs.AuthenticationInformationArray()
2439 array.array = [info]
2441 blob = drsblobs.trustAuthInOutBlob()
2443 blob.current = array
2447 def generate_AuthInfoInternal(session_key, incoming=None, outgoing=None):
2448 confounder = [0] * 512
2449 for i in range(len(confounder)):
2450 confounder[i] = random.randint(0, 255)
2452 trustpass = drsblobs.trustDomainPasswords()
2454 trustpass.confounder = confounder
2455 trustpass.outgoing = outgoing
2456 trustpass.incoming = incoming
2458 trustpass_blob = ndr_pack(trustpass)
2460 encrypted_trustpass = arcfour_encrypt(session_key, trustpass_blob)
2462 auth_blob = lsa.DATA_BUF2()
2463 auth_blob.size = len(encrypted_trustpass)
2464 auth_blob.data = string_to_byte_array(encrypted_trustpass)
2466 auth_info = lsa.TrustDomainInfoAuthInfoInternal()
2467 auth_info.auth_blob = auth_blob
2471 update_time = samba.current_unix_time()
2472 incoming_blob = generate_AuthInOutBlob(incoming_secret, update_time)
2473 outgoing_blob = generate_AuthInOutBlob(outgoing_secret, update_time)
2475 local_tdo_handle = None
2476 remote_tdo_handle = None
2478 local_auth_info = generate_AuthInfoInternal(local_lsa.session_key,
2479 incoming=incoming_blob,
2480 outgoing=outgoing_blob)
2481 if remote_trust_info:
2482 remote_auth_info = generate_AuthInfoInternal(remote_lsa.session_key,
2483 incoming=outgoing_blob,
2484 outgoing=incoming_blob)
2487 if remote_trust_info:
2488 self.outf.write("Creating remote TDO.\n")
2489 current_request = { "location": "remote", "name": "CreateTrustedDomainEx2"}
2490 remote_tdo_handle = remote_lsa.CreateTrustedDomainEx2(remote_policy,
2493 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2494 self.outf.write("Remote TDO created.\n")
2496 self.outf.write("Setting supported encryption types on remote TDO.\n")
2497 current_request = { "location": "remote", "name": "SetInformationTrustedDomain"}
2498 remote_lsa.SetInformationTrustedDomain(remote_tdo_handle,
2499 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2502 self.outf.write("Creating local TDO.\n")
2503 current_request = { "location": "local", "name": "CreateTrustedDomainEx2"}
2504 local_tdo_handle = local_lsa.CreateTrustedDomainEx2(local_policy,
2507 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2508 self.outf.write("Local TDO created\n")
2510 self.outf.write("Setting supported encryption types on local TDO.\n")
2511 current_request = { "location": "local", "name": "SetInformationTrustedDomain"}
2512 local_lsa.SetInformationTrustedDomain(local_tdo_handle,
2513 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2515 except RuntimeError as error:
2516 self.outf.write("Error: %s failed %sly - cleaning up\n" % (
2517 current_request['name'], current_request['location']))
2518 if remote_tdo_handle:
2519 self.outf.write("Deleting remote TDO.\n")
2520 remote_lsa.DeleteObject(remote_tdo_handle)
2521 remote_tdo_handle = None
2522 if local_tdo_handle:
2523 self.outf.write("Deleting local TDO.\n")
2524 local_lsa.DeleteObject(local_tdo_handle)
2525 local_tdo_handle = None
2526 if current_request['location'] is "remote":
2527 raise self.RemoteRuntimeError(self, error, "%s" % (
2528 current_request['name']))
2529 raise self.LocalRuntimeError(self, error, "%s" % (
2530 current_request['name']))
2533 if local_trust_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2534 self.outf.write("Setup local forest trust information...\n")
2536 # get all information about the remote trust
2537 # this triggers netr_GetForestTrustInformation to the remote domain
2538 # and lsaRSetForestTrustInformation() locally, but new top level
2539 # names are disabled by default.
2540 local_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
2541 remote_lsa_info.dns_domain.string,
2542 netlogon.DS_GFTI_UPDATE_TDO)
2543 except RuntimeError as error:
2544 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2547 # here we try to enable all top level names
2548 local_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
2549 remote_lsa_info.dns_domain,
2550 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2553 except RuntimeError as error:
2554 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2556 self.write_forest_trust_info(local_forest_info,
2557 tln=remote_lsa_info.dns_domain.string,
2558 collisions=local_forest_collision)
2560 if remote_trust_info:
2561 self.outf.write("Setup remote forest trust information...\n")
2563 # get all information about the local trust (from the perspective of the remote domain)
2564 # this triggers netr_GetForestTrustInformation to our domain.
2565 # and lsaRSetForestTrustInformation() remotely, but new top level
2566 # names are disabled by default.
2567 remote_forest_info = remote_netlogon.netr_DsRGetForestTrustInformation(remote_netlogon_info.dc_unc,
2568 local_lsa_info.dns_domain.string,
2569 netlogon.DS_GFTI_UPDATE_TDO)
2570 except RuntimeError as error:
2571 raise self.RemoteRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2574 # here we try to enable all top level names
2575 remote_forest_collision = remote_lsa.lsaRSetForestTrustInformation(remote_policy,
2576 local_lsa_info.dns_domain,
2577 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2580 except RuntimeError as error:
2581 raise self.RemoteRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2583 self.write_forest_trust_info(remote_forest_info,
2584 tln=local_lsa_info.dns_domain.string,
2585 collisions=remote_forest_collision)
2587 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2588 self.outf.write("Validating outgoing trust...\n")
2590 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_netlogon_info.dc_unc,
2591 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2593 remote_lsa_info.dns_domain.string)
2594 except RuntimeError as error:
2595 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2597 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2598 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2600 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2601 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2602 local_trust_verify.trusted_dc_name,
2603 local_trust_verify.tc_connection_status[1],
2604 local_trust_verify.pdc_connection_status[1])
2606 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2607 local_trust_verify.trusted_dc_name,
2608 local_trust_verify.tc_connection_status[1],
2609 local_trust_verify.pdc_connection_status[1])
2611 if local_trust_status != self.WERR_OK or local_conn_status != self.WERR_OK:
2612 raise CommandError(local_validation)
2614 self.outf.write("OK: %s\n" % local_validation)
2616 if remote_trust_info:
2617 if remote_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2618 self.outf.write("Validating incoming trust...\n")
2620 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_netlogon_info.dc_unc,
2621 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2623 local_lsa_info.dns_domain.string)
2624 except RuntimeError as error:
2625 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2627 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2628 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2630 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2631 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2632 remote_trust_verify.trusted_dc_name,
2633 remote_trust_verify.tc_connection_status[1],
2634 remote_trust_verify.pdc_connection_status[1])
2636 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2637 remote_trust_verify.trusted_dc_name,
2638 remote_trust_verify.tc_connection_status[1],
2639 remote_trust_verify.pdc_connection_status[1])
2641 if remote_trust_status != self.WERR_OK or remote_conn_status != self.WERR_OK:
2642 raise CommandError(remote_validation)
2644 self.outf.write("OK: %s\n" % remote_validation)
2646 if remote_tdo_handle is not None:
2648 remote_lsa.Close(remote_tdo_handle)
2649 except RuntimeError as error:
2651 remote_tdo_handle = None
2652 if local_tdo_handle is not None:
2654 local_lsa.Close(local_tdo_handle)
2655 except RuntimeError as error:
2657 local_tdo_handle = None
2659 self.outf.write("Success.\n")
2662 class cmd_domain_trust_delete(DomainTrustCommand):
2663 """Delete a domain trust."""
2665 synopsis = "%prog DOMAIN [options]"
2667 takes_optiongroups = {
2668 "sambaopts": options.SambaOptions,
2669 "versionopts": options.VersionOptions,
2670 "credopts": options.CredentialsOptions,
2671 "localdcopts": LocalDCCredentialsOptions,
2675 Option("--delete-location", type="choice", metavar="LOCATION",
2676 choices=["local", "both"],
2677 help="Where to delete the trusted domain object: 'local' or 'both'.",
2678 dest='delete_location',
2682 takes_args = ["domain"]
2684 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2685 delete_location=None):
2687 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2688 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2689 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2691 if delete_location == "local":
2692 remote_policy_access = None
2694 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2695 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2696 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2698 local_server = self.setup_local_server(sambaopts, localdcopts)
2700 local_lsa = self.new_local_lsa_connection()
2701 except RuntimeError as error:
2702 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2705 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2706 except RuntimeError as error:
2707 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2709 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2710 local_lsa_info.name.string,
2711 local_lsa_info.dns_domain.string,
2712 local_lsa_info.sid))
2714 local_tdo_info = None
2715 local_tdo_handle = None
2716 remote_tdo_info = None
2717 remote_tdo_handle = None
2719 lsaString = lsa.String()
2721 lsaString.string = domain
2722 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2723 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2724 except RuntimeError as error:
2725 if self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2726 raise CommandError("Failed to find trust for domain '%s'" % domain)
2727 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2730 if remote_policy_access is not None:
2732 remote_server = self.setup_remote_server(credopts, domain)
2733 except RuntimeError as error:
2734 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2737 remote_lsa = self.new_remote_lsa_connection()
2738 except RuntimeError as error:
2739 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2742 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2743 except RuntimeError as error:
2744 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2746 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2747 remote_lsa_info.name.string,
2748 remote_lsa_info.dns_domain.string,
2749 remote_lsa_info.sid))
2751 if remote_lsa_info.sid != local_tdo_info.sid or \
2752 remote_lsa_info.name.string != local_tdo_info.netbios_name.string or \
2753 remote_lsa_info.dns_domain.string != local_tdo_info.domain_name.string:
2754 raise CommandError("LocalTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2755 local_tdo_info.netbios_name.string,
2756 local_tdo_info.domain_name.string,
2757 local_tdo_info.sid))
2760 lsaString.string = local_lsa_info.dns_domain.string
2761 remote_tdo_info = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2762 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2763 except RuntimeError as error:
2764 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2765 raise self.RemoteRuntimeError(self, error, "QueryTrustedDomainInfoByName(%s)" % (
2769 if remote_tdo_info is not None:
2770 if local_lsa_info.sid != remote_tdo_info.sid or \
2771 local_lsa_info.name.string != remote_tdo_info.netbios_name.string or \
2772 local_lsa_info.dns_domain.string != remote_tdo_info.domain_name.string:
2773 raise CommandError("RemoteTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2774 remote_tdo_info.netbios_name.string,
2775 remote_tdo_info.domain_name.string,
2776 remote_tdo_info.sid))
2778 if local_tdo_info is not None:
2780 lsaString.string = local_tdo_info.domain_name.string
2781 local_tdo_handle = local_lsa.OpenTrustedDomainByName(local_policy,
2783 security.SEC_STD_DELETE)
2784 except RuntimeError as error:
2785 raise self.LocalRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2788 local_lsa.DeleteObject(local_tdo_handle)
2789 local_tdo_handle = None
2791 if remote_tdo_info is not None:
2793 lsaString.string = remote_tdo_info.domain_name.string
2794 remote_tdo_handle = remote_lsa.OpenTrustedDomainByName(remote_policy,
2796 security.SEC_STD_DELETE)
2797 except RuntimeError as error:
2798 raise self.RemoteRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2801 if remote_tdo_handle is not None:
2803 remote_lsa.DeleteObject(remote_tdo_handle)
2804 remote_tdo_handle = None
2805 self.outf.write("RemoteTDO deleted.\n")
2806 except RuntimeError as error:
2807 self.outf.write("%s\n" % self.RemoteRuntimeError(self, error, "DeleteObject() failed"))
2809 if local_tdo_handle is not None:
2811 local_lsa.DeleteObject(local_tdo_handle)
2812 local_tdo_handle = None
2813 self.outf.write("LocalTDO deleted.\n")
2814 except RuntimeError as error:
2815 self.outf.write("%s\n" % self.LocalRuntimeError(self, error, "DeleteObject() failed"))
2819 class cmd_domain_trust_validate(DomainTrustCommand):
2820 """Validate a domain trust."""
2822 synopsis = "%prog DOMAIN [options]"
2824 takes_optiongroups = {
2825 "sambaopts": options.SambaOptions,
2826 "versionopts": options.VersionOptions,
2827 "credopts": options.CredentialsOptions,
2828 "localdcopts": LocalDCCredentialsOptions,
2832 Option("--validate-location", type="choice", metavar="LOCATION",
2833 choices=["local", "both"],
2834 help="Where to validate the trusted domain object: 'local' or 'both'.",
2835 dest='validate_location',
2839 takes_args = ["domain"]
2841 def run(self, domain, sambaopts=None, versionopts=None, credopts=None, localdcopts=None,
2842 validate_location=None):
2844 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2846 local_server = self.setup_local_server(sambaopts, localdcopts)
2848 local_lsa = self.new_local_lsa_connection()
2849 except RuntimeError as error:
2850 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2853 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2854 except RuntimeError as error:
2855 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2857 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2858 local_lsa_info.name.string,
2859 local_lsa_info.dns_domain.string,
2860 local_lsa_info.sid))
2863 lsaString = lsa.String()
2864 lsaString.string = domain
2865 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2866 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2867 except RuntimeError as error:
2868 if self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2869 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2871 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
2873 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
2874 local_tdo_info.netbios_name.string,
2875 local_tdo_info.domain_name.string,
2876 local_tdo_info.sid))
2879 local_netlogon = self.new_local_netlogon_connection()
2880 except RuntimeError as error:
2881 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2884 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_server,
2885 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2887 local_tdo_info.domain_name.string)
2888 except RuntimeError as error:
2889 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2891 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2892 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2894 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2895 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2896 local_trust_verify.trusted_dc_name,
2897 local_trust_verify.tc_connection_status[1],
2898 local_trust_verify.pdc_connection_status[1])
2900 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2901 local_trust_verify.trusted_dc_name,
2902 local_trust_verify.tc_connection_status[1],
2903 local_trust_verify.pdc_connection_status[1])
2905 if local_trust_status != self.WERR_OK or local_conn_status != self.WERR_OK:
2906 raise CommandError(local_validation)
2908 self.outf.write("OK: %s\n" % local_validation)
2911 server = local_trust_verify.trusted_dc_name.replace('\\', '')
2912 domain_and_server = "%s\\%s" % (local_tdo_info.domain_name.string, server)
2913 local_trust_rediscover = local_netlogon.netr_LogonControl2Ex(local_server,
2914 netlogon.NETLOGON_CONTROL_REDISCOVER,
2917 except RuntimeError as error:
2918 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
2920 local_conn_status = self._uint32(local_trust_rediscover.tc_connection_status[0])
2921 local_rediscover = "LocalRediscover: DC[%s] CONNECTION[%s]" % (
2922 local_trust_rediscover.trusted_dc_name,
2923 local_trust_rediscover.tc_connection_status[1])
2925 if local_conn_status != self.WERR_OK:
2926 raise CommandError(local_rediscover)
2928 self.outf.write("OK: %s\n" % local_rediscover)
2930 if validate_location != "local":
2932 remote_server = self.setup_remote_server(credopts, domain, require_pdc=False)
2933 except RuntimeError as error:
2934 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2937 remote_netlogon = self.new_remote_netlogon_connection()
2938 except RuntimeError as error:
2939 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2942 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_server,
2943 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2945 local_lsa_info.dns_domain.string)
2946 except RuntimeError as error:
2947 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2949 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2950 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2952 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2953 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2954 remote_trust_verify.trusted_dc_name,
2955 remote_trust_verify.tc_connection_status[1],
2956 remote_trust_verify.pdc_connection_status[1])
2958 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2959 remote_trust_verify.trusted_dc_name,
2960 remote_trust_verify.tc_connection_status[1],
2961 remote_trust_verify.pdc_connection_status[1])
2963 if remote_trust_status != self.WERR_OK or remote_conn_status != self.WERR_OK:
2964 raise CommandError(remote_validation)
2966 self.outf.write("OK: %s\n" % remote_validation)
2969 server = remote_trust_verify.trusted_dc_name.replace('\\', '')
2970 domain_and_server = "%s\\%s" % (local_lsa_info.dns_domain.string, server)
2971 remote_trust_rediscover = remote_netlogon.netr_LogonControl2Ex(remote_server,
2972 netlogon.NETLOGON_CONTROL_REDISCOVER,
2975 except RuntimeError as error:
2976 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
2978 remote_conn_status = self._uint32(remote_trust_rediscover.tc_connection_status[0])
2980 remote_rediscover = "RemoteRediscover: DC[%s] CONNECTION[%s]" % (
2981 remote_trust_rediscover.trusted_dc_name,
2982 remote_trust_rediscover.tc_connection_status[1])
2984 if remote_conn_status != self.WERR_OK:
2985 raise CommandError(remote_rediscover)
2987 self.outf.write("OK: %s\n" % remote_rediscover)
2991 class cmd_domain_trust_namespaces(DomainTrustCommand):
2992 """Manage forest trust namespaces."""
2994 synopsis = "%prog [DOMAIN] [options]"
2996 takes_optiongroups = {
2997 "sambaopts": options.SambaOptions,
2998 "versionopts": options.VersionOptions,
2999 "localdcopts": LocalDCCredentialsOptions,
3003 Option("--refresh", type="choice", metavar="check|store",
3004 choices=["check", "store", None],
3005 help="List and maybe store refreshed forest trust information: 'check' or 'store'.",
3008 Option("--enable-all", action="store_true",
3009 help="Try to update disabled entries, not allowed with --refresh=check.",
3012 Option("--enable-tln", action="append", metavar='DNSDOMAIN',
3013 help="Enable a top level name entry. Can be specified multiple times.",
3016 Option("--disable-tln", action="append", metavar='DNSDOMAIN',
3017 help="Disable a top level name entry. Can be specified multiple times.",
3020 Option("--add-tln-ex", action="append", metavar='DNSDOMAIN',
3021 help="Add a top level exclusion entry. Can be specified multiple times.",
3024 Option("--delete-tln-ex", action="append", metavar='DNSDOMAIN',
3025 help="Delete a top level exclusion entry. Can be specified multiple times.",
3026 dest='delete_tln_ex',
3028 Option("--enable-nb", action="append", metavar='NETBIOSDOMAIN',
3029 help="Enable a netbios name in a domain entry. Can be specified multiple times.",
3032 Option("--disable-nb", action="append", metavar='NETBIOSDOMAIN',
3033 help="Disable a netbios name in a domain entry. Can be specified multiple times.",
3036 Option("--enable-sid", action="append", metavar='DOMAINSID',
3037 help="Enable a SID in a domain entry. Can be specified multiple times.",
3038 dest='enable_sid_str',
3040 Option("--disable-sid", action="append", metavar='DOMAINSID',
3041 help="Disable a SID in a domain entry. Can be specified multiple times.",
3042 dest='disable_sid_str',
3044 Option("--add-upn-suffix", action="append", metavar='DNSDOMAIN',
3045 help="Add a new uPNSuffixes attribute for the local forest. Can be specified multiple times.",
3048 Option("--delete-upn-suffix", action="append", metavar='DNSDOMAIN',
3049 help="Delete an existing uPNSuffixes attribute of the local forest. Can be specified multiple times.",
3052 Option("--add-spn-suffix", action="append", metavar='DNSDOMAIN',
3053 help="Add a new msDS-SPNSuffixes attribute for the local forest. Can be specified multiple times.",
3056 Option("--delete-spn-suffix", action="append", metavar='DNSDOMAIN',
3057 help="Delete an existing msDS-SPNSuffixes attribute of the local forest. Can be specified multiple times.",
3062 takes_args = ["domain?"]
3064 def run(self, domain=None, sambaopts=None, localdcopts=None, versionopts=None,
3065 refresh=None, enable_all=False,
3066 enable_tln=[], disable_tln=[], add_tln_ex=[], delete_tln_ex=[],
3067 enable_sid_str=[], disable_sid_str=[], enable_nb=[], disable_nb=[],
3068 add_upn=[], delete_upn=[], add_spn=[], delete_spn=[]):
3070 require_update = False
3073 if refresh == "store":
3074 raise CommandError("--refresh=%s not allowed without DOMAIN" % refresh)
3077 raise CommandError("--enable-all not allowed without DOMAIN")
3079 if len(enable_tln) > 0:
3080 raise CommandError("--enable-tln not allowed without DOMAIN")
3081 if len(disable_tln) > 0:
3082 raise CommandError("--disable-tln not allowed without DOMAIN")
3084 if len(add_tln_ex) > 0:
3085 raise CommandError("--add-tln-ex not allowed without DOMAIN")
3086 if len(delete_tln_ex) > 0:
3087 raise CommandError("--delete-tln-ex not allowed without DOMAIN")
3089 if len(enable_nb) > 0:
3090 raise CommandError("--enable-nb not allowed without DOMAIN")
3091 if len(disable_nb) > 0:
3092 raise CommandError("--disable-nb not allowed without DOMAIN")
3094 if len(enable_sid_str) > 0:
3095 raise CommandError("--enable-sid not allowed without DOMAIN")
3096 if len(disable_sid_str) > 0:
3097 raise CommandError("--disable-sid not allowed without DOMAIN")
3099 if len(add_upn) > 0:
3101 if not n.startswith("*."):
3103 raise CommandError("value[%s] specified for --add-upn-suffix should not include with '*.'" % n)
3104 require_update = True
3105 if len(delete_upn) > 0:
3106 for n in delete_upn:
3107 if not n.startswith("*."):
3109 raise CommandError("value[%s] specified for --delete-upn-suffix should not include with '*.'" % n)
3110 require_update = True
3112 for d in delete_upn:
3113 if a.lower() != d.lower():
3115 raise CommandError("value[%s] specified for --add-upn-suffix and --delete-upn-suffix" % a)
3117 if len(add_spn) > 0:
3119 if not n.startswith("*."):
3121 raise CommandError("value[%s] specified for --add-spn-suffix should not include with '*.'" % n)
3122 require_update = True
3123 if len(delete_spn) > 0:
3124 for n in delete_spn:
3125 if not n.startswith("*."):
3127 raise CommandError("value[%s] specified for --delete-spn-suffix should not include with '*.'" % n)
3128 require_update = True
3130 for d in delete_spn:
3131 if a.lower() != d.lower():
3133 raise CommandError("value[%s] specified for --add-spn-suffix and --delete-spn-suffix" % a)
3135 if len(add_upn) > 0:
3136 raise CommandError("--add-upn-suffix not allowed together with DOMAIN")
3137 if len(delete_upn) > 0:
3138 raise CommandError("--delete-upn-suffix not allowed together with DOMAIN")
3139 if len(add_spn) > 0:
3140 raise CommandError("--add-spn-suffix not allowed together with DOMAIN")
3141 if len(delete_spn) > 0:
3142 raise CommandError("--delete-spn-suffix not allowed together with DOMAIN")
3144 if refresh is not None:
3145 if refresh == "store":
3146 require_update = True
3148 if enable_all and refresh != "store":
3149 raise CommandError("--enable-all not allowed together with --refresh=%s" % refresh)
3151 if len(enable_tln) > 0:
3152 raise CommandError("--enable-tln not allowed together with --refresh")
3153 if len(disable_tln) > 0:
3154 raise CommandError("--disable-tln not allowed together with --refresh")
3156 if len(add_tln_ex) > 0:
3157 raise CommandError("--add-tln-ex not allowed together with --refresh")
3158 if len(delete_tln_ex) > 0:
3159 raise CommandError("--delete-tln-ex not allowed together with --refresh")
3161 if len(enable_nb) > 0:
3162 raise CommandError("--enable-nb not allowed together with --refresh")
3163 if len(disable_nb) > 0:
3164 raise CommandError("--disable-nb not allowed together with --refresh")
3166 if len(enable_sid_str) > 0:
3167 raise CommandError("--enable-sid not allowed together with --refresh")
3168 if len(disable_sid_str) > 0:
3169 raise CommandError("--disable-sid not allowed together with --refresh")
3172 require_update = True
3174 if len(enable_tln) > 0:
3175 raise CommandError("--enable-tln not allowed together with --enable-all")
3177 if len(enable_nb) > 0:
3178 raise CommandError("--enable-nb not allowed together with --enable-all")
3180 if len(enable_sid) > 0:
3181 raise CommandError("--enable-sid not allowed together with --enable-all")
3183 if len(enable_tln) > 0:
3184 require_update = True
3185 if len(disable_tln) > 0:
3186 require_update = True
3187 for e in enable_tln:
3188 for d in disable_tln:
3189 if e.lower() != d.lower():
3191 raise CommandError("value[%s] specified for --enable-tln and --disable-tln" % e)
3193 if len(add_tln_ex) > 0:
3194 for n in add_tln_ex:
3195 if not n.startswith("*."):
3197 raise CommandError("value[%s] specified for --add-tln-ex should not include with '*.'" % n)
3198 require_update = True
3199 if len(delete_tln_ex) > 0:
3200 for n in delete_tln_ex:
3201 if not n.startswith("*."):
3203 raise CommandError("value[%s] specified for --delete-tln-ex should not include with '*.'" % n)
3204 require_update = True
3205 for a in add_tln_ex:
3206 for d in delete_tln_ex:
3207 if a.lower() != d.lower():
3209 raise CommandError("value[%s] specified for --add-tln-ex and --delete-tln-ex" % a)
3211 if len(enable_nb) > 0:
3212 require_update = True
3213 if len(disable_nb) > 0:
3214 require_update = True
3216 for d in disable_nb:
3217 if e.upper() != d.upper():
3219 raise CommandError("value[%s] specified for --enable-nb and --disable-nb" % e)
3222 for s in enable_sid_str:
3224 sid = security.dom_sid(s)
3225 except TypeError as error:
3226 raise CommandError("value[%s] specified for --enable-sid is not a valid SID" % s)
3227 enable_sid.append(sid)
3229 for s in disable_sid_str:
3231 sid = security.dom_sid(s)
3232 except TypeError as error:
3233 raise CommandError("value[%s] specified for --disable-sid is not a valid SID" % s)
3234 disable_sid.append(sid)
3235 if len(enable_sid) > 0:
3236 require_update = True
3237 if len(disable_sid) > 0:
3238 require_update = True
3239 for e in enable_sid:
3240 for d in disable_sid:
3243 raise CommandError("value[%s] specified for --enable-sid and --disable-sid" % e)
3245 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
3247 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
3249 local_server = self.setup_local_server(sambaopts, localdcopts)
3251 local_lsa = self.new_local_lsa_connection()
3252 except RuntimeError as error:
3253 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
3256 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
3257 except RuntimeError as error:
3258 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
3260 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
3261 local_lsa_info.name.string,
3262 local_lsa_info.dns_domain.string,
3263 local_lsa_info.sid))
3267 local_netlogon = self.new_local_netlogon_connection()
3268 except RuntimeError as error:
3269 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3272 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3273 except RuntimeError as error:
3274 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3276 if local_netlogon_info.domain_name != local_netlogon_info.forest_name:
3277 raise CommandError("The local domain [%s] is not the forest root [%s]" % (
3278 local_netlogon_info.domain_name,
3279 local_netlogon_info.forest_name))
3282 # get all information about our own forest
3283 own_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3285 except RuntimeError as error:
3286 if self.check_runtime_error(error, self.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
3287 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3290 if self.check_runtime_error(error, self.WERR_INVALID_FUNCTION):
3291 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3294 if self.check_runtime_error(error, self.WERR_NERR_ACFNOTLOADED):
3295 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3298 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3300 self.outf.write("Own forest trust information...\n")
3301 self.write_forest_trust_info(own_forest_info,
3302 tln=local_lsa_info.dns_domain.string)
3305 local_samdb = self.new_local_ldap_connection()
3306 except RuntimeError as error:
3307 raise self.LocalRuntimeError(self, error, "failed to connect to SamDB")
3309 local_partitions_dn = "CN=Partitions,%s" % str(local_samdb.get_config_basedn())
3310 attrs = ['uPNSuffixes', 'msDS-SPNSuffixes']
3312 msgs = local_samdb.search(base=local_partitions_dn,
3313 scope=ldb.SCOPE_BASE,
3314 expression="(objectClass=crossRefContainer)",
3316 stored_msg = msgs[0]
3317 except ldb.LdbError as error:
3318 raise self.LocalLdbError(self, error, "failed to search partition dn")
3320 stored_upn_vals = []
3321 if 'uPNSuffixes' in stored_msg:
3322 stored_upn_vals.extend(stored_msg['uPNSuffixes'])
3324 stored_spn_vals = []
3325 if 'msDS-SPNSuffixes' in stored_msg:
3326 stored_spn_vals.extend(stored_msg['msDS-SPNSuffixes'])
3328 self.outf.write("Stored uPNSuffixes attributes[%d]:\n" % len(stored_upn_vals))
3329 for v in stored_upn_vals:
3330 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3331 self.outf.write("Stored msDS-SPNSuffixes attributes[%d]:\n" % len(stored_spn_vals))
3332 for v in stored_spn_vals:
3333 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3335 if not require_update:
3339 update_upn_vals = []
3340 update_upn_vals.extend(stored_upn_vals)
3343 update_spn_vals = []
3344 update_spn_vals.extend(stored_spn_vals)
3348 for i in xrange(0, len(update_upn_vals)):
3349 v = update_upn_vals[i]
3350 if v.lower() != upn.lower():
3355 raise CommandError("Entry already present for value[%s] specified for --add-upn-suffix" % upn)
3356 update_upn_vals.append(upn)
3359 for upn in delete_upn:
3361 for i in xrange(0, len(update_upn_vals)):
3362 v = update_upn_vals[i]
3363 if v.lower() != upn.lower():
3368 raise CommandError("Entry not found for value[%s] specified for --delete-upn-suffix" % upn)
3370 update_upn_vals.pop(idx)
3375 for i in xrange(0, len(update_spn_vals)):
3376 v = update_spn_vals[i]
3377 if v.lower() != spn.lower():
3382 raise CommandError("Entry already present for value[%s] specified for --add-spn-suffix" % spn)
3383 update_spn_vals.append(spn)
3386 for spn in delete_spn:
3388 for i in xrange(0, len(update_spn_vals)):
3389 v = update_spn_vals[i]
3390 if v.lower() != spn.lower():
3395 raise CommandError("Entry not found for value[%s] specified for --delete-spn-suffix" % spn)
3397 update_spn_vals.pop(idx)
3400 self.outf.write("Update uPNSuffixes attributes[%d]:\n" % len(update_upn_vals))
3401 for v in update_upn_vals:
3402 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3403 self.outf.write("Update msDS-SPNSuffixes attributes[%d]:\n" % len(update_spn_vals))
3404 for v in update_spn_vals:
3405 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3407 update_msg = ldb.Message()
3408 update_msg.dn = stored_msg.dn
3411 update_msg['uPNSuffixes'] = ldb.MessageElement(update_upn_vals,
3412 ldb.FLAG_MOD_REPLACE,
3415 update_msg['msDS-SPNSuffixes'] = ldb.MessageElement(update_spn_vals,
3416 ldb.FLAG_MOD_REPLACE,
3419 local_samdb.modify(update_msg)
3420 except ldb.LdbError as error:
3421 raise self.LocalLdbError(self, error, "failed to update partition dn")
3424 stored_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3426 except RuntimeError as error:
3427 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3429 self.outf.write("Stored forest trust information...\n")
3430 self.write_forest_trust_info(stored_forest_info,
3431 tln=local_lsa_info.dns_domain.string)
3435 lsaString = lsa.String()
3436 lsaString.string = domain
3437 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
3438 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
3439 except RuntimeError as error:
3440 if self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
3441 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
3443 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
3445 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
3446 local_tdo_info.netbios_name.string,
3447 local_tdo_info.domain_name.string,
3448 local_tdo_info.sid))
3450 if not local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
3451 raise CommandError("trusted domain object for domain [%s] is not marked as FOREST_TRANSITIVE." % domain)
3453 if refresh is not None:
3455 local_netlogon = self.new_local_netlogon_connection()
3456 except RuntimeError as error:
3457 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3460 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3461 except RuntimeError as error:
3462 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3464 lsa_update_check = 1
3465 if refresh == "store":
3466 netlogon_update_tdo = netlogon.DS_GFTI_UPDATE_TDO
3468 lsa_update_check = 0
3470 netlogon_update_tdo = 0
3473 # get all information about the remote trust
3474 # this triggers netr_GetForestTrustInformation to the remote domain
3475 # and lsaRSetForestTrustInformation() locally, but new top level
3476 # names are disabled by default.
3477 fresh_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3478 local_tdo_info.domain_name.string,
3479 netlogon_update_tdo)
3480 except RuntimeError as error:
3481 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3484 fresh_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3485 local_tdo_info.domain_name,
3486 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3489 except RuntimeError as error:
3490 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3492 self.outf.write("Fresh forest trust information...\n")
3493 self.write_forest_trust_info(fresh_forest_info,
3494 tln=local_tdo_info.domain_name.string,
3495 collisions=fresh_forest_collision)
3497 if refresh == "store":
3499 lsaString = lsa.String()
3500 lsaString.string = local_tdo_info.domain_name.string
3501 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3503 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3504 except RuntimeError as error:
3505 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3507 self.outf.write("Stored forest trust information...\n")
3508 self.write_forest_trust_info(stored_forest_info,
3509 tln=local_tdo_info.domain_name.string)
3514 # The none --refresh path
3518 lsaString = lsa.String()
3519 lsaString.string = local_tdo_info.domain_name.string
3520 local_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3522 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3523 except RuntimeError as error:
3524 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3526 self.outf.write("Local forest trust information...\n")
3527 self.write_forest_trust_info(local_forest_info,
3528 tln=local_tdo_info.domain_name.string)
3530 if not require_update:
3534 entries.extend(local_forest_info.entries)
3535 update_forest_info = lsa.ForestTrustInformation()
3536 update_forest_info.count = len(entries)
3537 update_forest_info.entries = entries
3540 for i in xrange(0, len(update_forest_info.entries)):
3541 r = update_forest_info.entries[i]
3542 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3544 if update_forest_info.entries[i].flags == 0:
3546 update_forest_info.entries[i].time = 0
3547 update_forest_info.entries[i].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3548 for i in xrange(0, len(update_forest_info.entries)):
3549 r = update_forest_info.entries[i]
3550 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3552 if update_forest_info.entries[i].flags == 0:
3554 update_forest_info.entries[i].time = 0
3555 update_forest_info.entries[i].flags &= ~lsa.LSA_NB_DISABLED_MASK
3556 update_forest_info.entries[i].flags &= ~lsa.LSA_SID_DISABLED_MASK
3558 for tln in enable_tln:
3560 for i in xrange(0, len(update_forest_info.entries)):
3561 r = update_forest_info.entries[i]
3562 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3564 if r.forest_trust_data.string.lower() != tln.lower():
3569 raise CommandError("Entry not found for value[%s] specified for --enable-tln" % tln)
3570 if not update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_MASK:
3571 raise CommandError("Entry found for value[%s] specified for --enable-tln is already enabled" % tln)
3572 update_forest_info.entries[idx].time = 0
3573 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3575 for tln in disable_tln:
3577 for i in xrange(0, len(update_forest_info.entries)):
3578 r = update_forest_info.entries[i]
3579 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3581 if r.forest_trust_data.string.lower() != tln.lower():
3586 raise CommandError("Entry not found for value[%s] specified for --disable-tln" % tln)
3587 if update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_ADMIN:
3588 raise CommandError("Entry found for value[%s] specified for --disable-tln is already disabled" % tln)
3589 update_forest_info.entries[idx].time = 0
3590 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3591 update_forest_info.entries[idx].flags |= lsa.LSA_TLN_DISABLED_ADMIN
3593 for tln_ex in add_tln_ex:
3595 for i in xrange(0, len(update_forest_info.entries)):
3596 r = update_forest_info.entries[i]
3597 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3599 if r.forest_trust_data.string.lower() != tln_ex.lower():
3604 raise CommandError("Entry already present for value[%s] specified for --add-tln-ex" % tln_ex)
3606 tln_dot = ".%s" % tln_ex.lower()
3608 for i in xrange(0, len(update_forest_info.entries)):
3609 r = update_forest_info.entries[i]
3610 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3612 r_dot = ".%s" % r.forest_trust_data.string.lower()
3613 if tln_dot == r_dot:
3614 raise CommandError("TLN entry present for value[%s] specified for --add-tln-ex" % tln_ex)
3615 if not tln_dot.endswith(r_dot):
3621 raise CommandError("No TLN parent present for value[%s] specified for --add-tln-ex" % tln_ex)
3623 r = lsa.ForestTrustRecord()
3624 r.type = lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX
3627 r.forest_trust_data.string = tln_ex
3630 entries.extend(update_forest_info.entries)
3631 entries.insert(idx + 1, r)
3632 update_forest_info.count = len(entries)
3633 update_forest_info.entries = entries
3635 for tln_ex in delete_tln_ex:
3637 for i in xrange(0, len(update_forest_info.entries)):
3638 r = update_forest_info.entries[i]
3639 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3641 if r.forest_trust_data.string.lower() != tln_ex.lower():
3646 raise CommandError("Entry not found for value[%s] specified for --delete-tln-ex" % tln_ex)
3649 entries.extend(update_forest_info.entries)
3651 update_forest_info.count = len(entries)
3652 update_forest_info.entries = entries
3654 for nb in enable_nb:
3656 for i in xrange(0, len(update_forest_info.entries)):
3657 r = update_forest_info.entries[i]
3658 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3660 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3665 raise CommandError("Entry not found for value[%s] specified for --enable-nb" % nb)
3666 if not update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_MASK:
3667 raise CommandError("Entry found for value[%s] specified for --enable-nb is already enabled" % nb)
3668 update_forest_info.entries[idx].time = 0
3669 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3671 for nb in disable_nb:
3673 for i in xrange(0, len(update_forest_info.entries)):
3674 r = update_forest_info.entries[i]
3675 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3677 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3682 raise CommandError("Entry not found for value[%s] specified for --delete-nb" % nb)
3683 if update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_ADMIN:
3684 raise CommandError("Entry found for value[%s] specified for --disable-nb is already disabled" % nb)
3685 update_forest_info.entries[idx].time = 0
3686 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3687 update_forest_info.entries[idx].flags |= lsa.LSA_NB_DISABLED_ADMIN
3689 for sid in enable_sid:
3691 for i in xrange(0, len(update_forest_info.entries)):
3692 r = update_forest_info.entries[i]
3693 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3695 if r.forest_trust_data.domain_sid != sid:
3700 raise CommandError("Entry not found for value[%s] specified for --enable-sid" % sid)
3701 if not update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_MASK:
3702 raise CommandError("Entry found for value[%s] specified for --enable-sid is already enabled" % nb)
3703 update_forest_info.entries[idx].time = 0
3704 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3706 for sid in disable_sid:
3708 for i in xrange(0, len(update_forest_info.entries)):
3709 r = update_forest_info.entries[i]
3710 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3712 if r.forest_trust_data.domain_sid != sid:
3717 raise CommandError("Entry not found for value[%s] specified for --delete-sid" % sid)
3718 if update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_ADMIN:
3719 raise CommandError("Entry found for value[%s] specified for --disable-sid is already disabled" % nb)
3720 update_forest_info.entries[idx].time = 0
3721 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3722 update_forest_info.entries[idx].flags |= lsa.LSA_SID_DISABLED_ADMIN
3725 update_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3726 local_tdo_info.domain_name,
3727 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3728 update_forest_info, 0)
3729 except RuntimeError as error:
3730 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3732 self.outf.write("Updated forest trust information...\n")
3733 self.write_forest_trust_info(update_forest_info,
3734 tln=local_tdo_info.domain_name.string,
3735 collisions=update_forest_collision)
3738 lsaString = lsa.String()
3739 lsaString.string = local_tdo_info.domain_name.string
3740 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3742 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3743 except RuntimeError as error:
3744 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3746 self.outf.write("Stored forest trust information...\n")
3747 self.write_forest_trust_info(stored_forest_info,
3748 tln=local_tdo_info.domain_name.string)
3751 class cmd_domain_tombstones_expunge(Command):
3752 """Expunge tombstones from the database.
3754 This command expunges tombstones from the database."""
3755 synopsis = "%prog NC [NC [...]] [options]"
3758 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
3759 metavar="URL", dest="H"),
3760 Option("--current-time",
3761 help="The current time to evaluate the tombstone lifetime from, expressed as YYYY-MM-DD",
3763 Option("--tombstone-lifetime", help="Number of days a tombstone should be preserved for", type=int),
3766 takes_args = ["nc*"]
3768 takes_optiongroups = {
3769 "sambaopts": options.SambaOptions,
3770 "credopts": options.CredentialsOptions,
3771 "versionopts": options.VersionOptions,
3774 def run(self, *ncs, **kwargs):
3775 sambaopts = kwargs.get("sambaopts")
3776 credopts = kwargs.get("credopts")
3777 versionpts = kwargs.get("versionopts")
3779 current_time_string = kwargs.get("current_time")
3780 tombstone_lifetime = kwargs.get("tombstone_lifetime")
3781 lp = sambaopts.get_loadparm()
3782 creds = credopts.get_credentials(lp)
3783 samdb = SamDB(url=H, session_info=system_session(),
3784 credentials=creds, lp=lp)
3786 if current_time_string is not None:
3787 current_time_obj = time.strptime(current_time_string, "%Y-%m-%d")
3788 current_time = long(time.mktime(current_time_obj))
3791 current_time = long(time.time())
3794 res = samdb.search(expression="", base="", scope=ldb.SCOPE_BASE,
3795 attrs=["namingContexts"])
3798 for nc in res[0]["namingContexts"]:
3805 removed_links) = samdb.garbage_collect_tombstones(ncs,
3806 current_time=current_time,
3807 tombstone_lifetime=tombstone_lifetime)
3809 except Exception, err:
3810 raise CommandError("Failed to expunge / garbage collect tombstones", err)
3812 self.outf.write("Removed %d objects and %d links successfully\n"
3813 % (removed_objects, removed_links))
3817 class cmd_domain_trust(SuperCommand):
3818 """Domain and forest trust management."""
3821 subcommands["list"] = cmd_domain_trust_list()
3822 subcommands["show"] = cmd_domain_trust_show()
3823 subcommands["create"] = cmd_domain_trust_create()
3824 subcommands["delete"] = cmd_domain_trust_delete()
3825 subcommands["validate"] = cmd_domain_trust_validate()
3826 subcommands["namespaces"] = cmd_domain_trust_namespaces()
3828 class cmd_domain_tombstones(SuperCommand):
3829 """Domain tombstone and recycled object management."""
3832 subcommands["expunge"] = cmd_domain_tombstones_expunge()
3834 class cmd_domain(SuperCommand):
3835 """Domain management."""
3838 subcommands["demote"] = cmd_domain_demote()
3839 if cmd_domain_export_keytab is not None:
3840 subcommands["exportkeytab"] = cmd_domain_export_keytab()
3841 subcommands["info"] = cmd_domain_info()
3842 subcommands["provision"] = cmd_domain_provision()
3843 subcommands["join"] = cmd_domain_join()
3844 subcommands["dcpromo"] = cmd_domain_dcpromo()
3845 subcommands["level"] = cmd_domain_level()
3846 subcommands["passwordsettings"] = cmd_domain_passwordsettings()
3847 subcommands["classicupgrade"] = cmd_domain_classicupgrade()
3848 subcommands["samba3upgrade"] = cmd_domain_samba3upgrade()
3849 subcommands["trust"] = cmd_domain_trust()
3850 subcommands["tombstones"] = cmd_domain_tombstones()