3 # Copyright Matthias Dieter Wallnoefer 2009
4 # Copyright Andrew Kroeger 2009
5 # Copyright Jelmer Vernooij 2007-2012
6 # Copyright Giampaolo Lauria 2011
7 # Copyright Matthieu Patou <mat@matws.net> 2011
8 # Copyright Andrew Bartlett 2008-2015
9 # Copyright Stefan Metzmacher 2012
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 3 of the License, or
14 # (at your option) any later version.
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 import samba.getopt as options
36 from samba import ntstatus
37 from samba import NTSTATUSError
38 from samba import werror
39 from getpass import getpass
40 from samba.net import Net, LIBNET_JOIN_AUTOMATIC
42 from samba.join import join_RODC, join_DC, join_subdomain
43 from samba.auth import system_session
44 from samba.samdb import SamDB
45 from samba.ndr import ndr_unpack, ndr_pack, ndr_print
46 from samba.dcerpc import drsuapi
47 from samba.dcerpc import drsblobs
48 from samba.dcerpc import lsa
49 from samba.dcerpc import netlogon
50 from samba.dcerpc import security
51 from samba.dcerpc import nbt
52 from samba.dcerpc import misc
53 from samba.dcerpc.samr import DOMAIN_PASSWORD_COMPLEX, DOMAIN_PASSWORD_STORE_CLEARTEXT
54 from samba.netcmd import (
60 from samba.netcmd.common import netcmd_get_domain_infos_via_cldap
61 from samba.samba3 import Samba3
62 from samba.samba3 import param as s3param
63 from samba.upgrade import upgrade_from_samba3
64 from samba.drs_utils import (
65 sendDsReplicaSync, drsuapi_connect, drsException,
67 from samba import remove_dc, arcfour_encrypt, string_to_byte_array
69 from samba.dsdb import (
70 DS_DOMAIN_FUNCTION_2000,
71 DS_DOMAIN_FUNCTION_2003,
72 DS_DOMAIN_FUNCTION_2003_MIXED,
73 DS_DOMAIN_FUNCTION_2008,
74 DS_DOMAIN_FUNCTION_2008_R2,
75 DS_DOMAIN_FUNCTION_2012,
76 DS_DOMAIN_FUNCTION_2012_R2,
77 DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL,
78 DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL,
79 UF_WORKSTATION_TRUST_ACCOUNT,
80 UF_SERVER_TRUST_ACCOUNT,
81 UF_TRUSTED_FOR_DELEGATION,
82 UF_PARTIAL_SECRETS_ACCOUNT
85 from samba.provision import (
90 from samba.provision.common import (
96 def get_testparm_var(testparm, smbconf, varname):
97 errfile = open(os.devnull, 'w')
98 p = subprocess.Popen([testparm, '-s', '-l',
99 '--parameter-name=%s' % varname, smbconf],
100 stdout=subprocess.PIPE, stderr=errfile)
101 (out,err) = p.communicate()
103 lines = out.split('\n')
105 return lines[0].strip()
109 import samba.dckeytab
111 cmd_domain_export_keytab = None
113 class cmd_domain_export_keytab(Command):
114 """Dump Kerberos keys of the domain into a keytab."""
116 synopsis = "%prog <keytab> [options]"
118 takes_optiongroups = {
119 "sambaopts": options.SambaOptions,
120 "credopts": options.CredentialsOptions,
121 "versionopts": options.VersionOptions,
125 Option("--principal", help="extract only this principal", type=str),
128 takes_args = ["keytab"]
130 def run(self, keytab, credopts=None, sambaopts=None, versionopts=None, principal=None):
131 lp = sambaopts.get_loadparm()
133 net.export_keytab(keytab=keytab, principal=principal)
136 class cmd_domain_info(Command):
137 """Print basic info about a domain and the DC passed as parameter."""
139 synopsis = "%prog <ip_address> [options]"
144 takes_optiongroups = {
145 "sambaopts": options.SambaOptions,
146 "credopts": options.CredentialsOptions,
147 "versionopts": options.VersionOptions,
150 takes_args = ["address"]
152 def run(self, address, credopts=None, sambaopts=None, versionopts=None):
153 lp = sambaopts.get_loadparm()
155 res = netcmd_get_domain_infos_via_cldap(lp, None, address)
157 raise CommandError("Invalid IP address '" + address + "'!")
158 self.outf.write("Forest : %s\n" % res.forest)
159 self.outf.write("Domain : %s\n" % res.dns_domain)
160 self.outf.write("Netbios domain : %s\n" % res.domain_name)
161 self.outf.write("DC name : %s\n" % res.pdc_dns_name)
162 self.outf.write("DC netbios name : %s\n" % res.pdc_name)
163 self.outf.write("Server site : %s\n" % res.server_site)
164 self.outf.write("Client site : %s\n" % res.client_site)
167 class cmd_domain_provision(Command):
168 """Provision a domain."""
170 synopsis = "%prog [options]"
172 takes_optiongroups = {
173 "sambaopts": options.SambaOptions,
174 "versionopts": options.VersionOptions,
178 Option("--interactive", help="Ask for names", action="store_true"),
179 Option("--domain", type="string", metavar="DOMAIN",
180 help="NetBIOS domain name to use"),
181 Option("--domain-guid", type="string", metavar="GUID",
182 help="set domainguid (otherwise random)"),
183 Option("--domain-sid", type="string", metavar="SID",
184 help="set domainsid (otherwise random)"),
185 Option("--ntds-guid", type="string", metavar="GUID",
186 help="set NTDS object GUID (otherwise random)"),
187 Option("--invocationid", type="string", metavar="GUID",
188 help="set invocationid (otherwise random)"),
189 Option("--host-name", type="string", metavar="HOSTNAME",
190 help="set hostname"),
191 Option("--host-ip", type="string", metavar="IPADDRESS",
192 help="set IPv4 ipaddress"),
193 Option("--host-ip6", type="string", metavar="IP6ADDRESS",
194 help="set IPv6 ipaddress"),
195 Option("--site", type="string", metavar="SITENAME",
196 help="set site name"),
197 Option("--adminpass", type="string", metavar="PASSWORD",
198 help="choose admin password (otherwise random)"),
199 Option("--krbtgtpass", type="string", metavar="PASSWORD",
200 help="choose krbtgt password (otherwise random)"),
201 Option("--machinepass", type="string", metavar="PASSWORD",
202 help="choose machine password (otherwise random)"),
203 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
204 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
205 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
206 "BIND9_FLATFILE uses bind9 text database to store zone information, "
207 "BIND9_DLZ uses samba4 AD to store zone information, "
208 "NONE skips the DNS setup entirely (not recommended)",
209 default="SAMBA_INTERNAL"),
210 Option("--dnspass", type="string", metavar="PASSWORD",
211 help="choose dns password (otherwise random)"),
212 Option("--ldapadminpass", type="string", metavar="PASSWORD",
213 help="choose password to set between Samba and its LDAP backend (otherwise random)"),
214 Option("--root", type="string", metavar="USERNAME",
215 help="choose 'root' unix username"),
216 Option("--nobody", type="string", metavar="USERNAME",
217 help="choose 'nobody' user"),
218 Option("--users", type="string", metavar="GROUPNAME",
219 help="choose 'users' group"),
220 Option("--quiet", help="Be quiet", action="store_true"),
221 Option("--blank", action="store_true",
222 help="do not add users or groups, just the structure"),
223 Option("--ldap-backend-type", type="choice", metavar="LDAP-BACKEND-TYPE",
224 help="Test initialisation support for unsupported LDAP backend type (fedora-ds or openldap) DO NOT USE",
225 choices=["fedora-ds", "openldap"]),
226 Option("--server-role", type="choice", metavar="ROLE",
227 choices=["domain controller", "dc", "member server", "member", "standalone"],
228 help="The server role (domain controller | dc | member server | member | standalone). Default is dc.",
229 default="domain controller"),
230 Option("--function-level", type="choice", metavar="FOR-FUN-LEVEL",
231 choices=["2000", "2003", "2008", "2008_R2"],
232 help="The domain and forest function level (2000 | 2003 | 2008 | 2008_R2 - always native). Default is (Windows) 2008_R2 Native.",
234 Option("--next-rid", type="int", metavar="NEXTRID", default=1000,
235 help="The initial nextRid value (only needed for upgrades). Default is 1000."),
236 Option("--partitions-only",
237 help="Configure Samba's partitions, but do not modify them (ie, join a BDC)", action="store_true"),
238 Option("--targetdir", type="string", metavar="DIR",
239 help="Set target directory"),
240 Option("--ol-mmr-urls", type="string", metavar="LDAPSERVER",
241 help="List of LDAP-URLS [ ldap://<FQHN>:<PORT>/ (where <PORT> has to be different than 389!) ] separated with comma (\",\") for use with OpenLDAP-MMR (Multi-Master-Replication), e.g.: \"ldap://s4dc1:9000,ldap://s4dc2:9000\""),
242 Option("--use-rfc2307", action="store_true", help="Use AD to store posix attributes (default = no)"),
246 Option("--ldap-dryrun-mode", help="Configure LDAP backend, but do not run any binaries and exit early. Used only for the test environment. DO NOT USE",
247 action="store_true"),
248 Option("--slapd-path", type="string", metavar="SLAPD-PATH",
249 help="Path to slapd for LDAP backend [e.g.:'/usr/local/libexec/slapd']. Required for Setup with LDAP-Backend. OpenLDAP Version >= 2.4.17 should be used."),
250 Option("--ldap-backend-extra-port", type="int", metavar="LDAP-BACKEND-EXTRA-PORT", help="Additional TCP port for LDAP backend server (to use for replication)"),
251 Option("--ldap-backend-forced-uri", type="string", metavar="LDAP-BACKEND-FORCED-URI",
252 help="Force the LDAP backend connection to be to a particular URI. Use this ONLY for 'existing' backends, or when debugging the interaction with the LDAP backend and you need to intercept the LDA"),
253 Option("--ldap-backend-nosync", help="Configure LDAP backend not to call fsync() (for performance in test environments)", action="store_true"),
257 Option("--use-ntvfs", action="store_true", help="Use NTVFS for the fileserver (default = no)"),
258 Option("--use-xattrs", type="choice", choices=["yes","no","auto"],
259 metavar="[yes|no|auto]",
260 help="Define if we should use the native fs capabilities or a tdb file for "
261 "storing attributes likes ntacl when --use-ntvfs is set. "
262 "auto tries to make an inteligent guess based on the user rights and system capabilities",
266 if os.getenv('TEST_LDAP', "no") == "yes":
267 takes_options.extend(openldap_options)
269 if samba.is_ntvfs_fileserver_built():
270 takes_options.extend(ntvfs_options)
274 def run(self, sambaopts=None, versionopts=None,
297 ldap_backend_type=None,
301 partitions_only=None,
308 ldap_backend_nosync=None,
309 ldap_backend_extra_port=None,
310 ldap_backend_forced_uri=None,
311 ldap_dryrun_mode=None):
313 self.logger = self.get_logger("provision")
315 self.logger.setLevel(logging.WARNING)
317 self.logger.setLevel(logging.INFO)
319 lp = sambaopts.get_loadparm()
320 smbconf = lp.configfile
322 if dns_forwarder is not None:
323 suggested_forwarder = dns_forwarder
325 suggested_forwarder = self._get_nameserver_ip()
326 if suggested_forwarder is None:
327 suggested_forwarder = "none"
329 if len(self.raw_argv) == 1:
333 from getpass import getpass
336 def ask(prompt, default=None):
337 if default is not None:
338 print "%s [%s]: " % (prompt, default),
340 print "%s: " % (prompt,),
341 return sys.stdin.readline().rstrip("\n") or default
344 default = socket.getfqdn().split(".", 1)[1].upper()
347 realm = ask("Realm", default)
348 if realm in (None, ""):
349 raise CommandError("No realm set!")
352 default = realm.split(".")[0]
355 domain = ask("Domain", default)
357 raise CommandError("No domain set!")
359 server_role = ask("Server Role (dc, member, standalone)", "dc")
361 dns_backend = ask("DNS backend (SAMBA_INTERNAL, BIND9_FLATFILE, BIND9_DLZ, NONE)", "SAMBA_INTERNAL")
362 if dns_backend in (None, ''):
363 raise CommandError("No DNS backend set!")
365 if dns_backend == "SAMBA_INTERNAL":
366 dns_forwarder = ask("DNS forwarder IP address (write 'none' to disable forwarding)", suggested_forwarder)
367 if dns_forwarder.lower() in (None, 'none'):
368 suggested_forwarder = None
372 adminpassplain = getpass("Administrator password: ")
373 if not adminpassplain:
374 self.errf.write("Invalid administrator password.\n")
376 adminpassverify = getpass("Retype password: ")
377 if not adminpassplain == adminpassverify:
378 self.errf.write("Sorry, passwords do not match.\n")
380 adminpass = adminpassplain
384 realm = sambaopts._lp.get('realm')
386 raise CommandError("No realm set!")
388 raise CommandError("No domain set!")
391 self.logger.info("Administrator password will be set randomly!")
393 if function_level == "2000":
394 dom_for_fun_level = DS_DOMAIN_FUNCTION_2000
395 elif function_level == "2003":
396 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
397 elif function_level == "2008":
398 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008
399 elif function_level == "2008_R2":
400 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
402 if dns_backend == "SAMBA_INTERNAL" and dns_forwarder is None:
403 dns_forwarder = suggested_forwarder
405 samdb_fill = FILL_FULL
407 samdb_fill = FILL_NT4SYNC
408 elif partitions_only:
409 samdb_fill = FILL_DRS
411 if targetdir is not None:
412 if not os.path.isdir(targetdir):
417 if use_xattrs == "yes":
419 elif use_xattrs == "auto" and use_ntvfs == False:
421 elif use_ntvfs == False:
422 raise CommandError("--use-xattrs=no requires --use-ntvfs (not supported for production use). "
423 "Please re-run with --use-xattrs omitted.")
424 elif use_xattrs == "auto" and not lp.get("posix:eadb"):
426 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
428 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
431 samba.ntacls.setntacl(lp, file.name,
432 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
435 self.logger.info("You are not root or your system does not support xattr, using tdb backend for attributes. ")
440 self.logger.info("not using extended attributes to store ACLs and other metadata. If you intend to use this provision in production, rerun the script as root on a system supporting xattrs.")
441 if ldap_backend_type == "existing":
442 if ldap_backend_forced_uri is not None:
443 self.logger.warn("You have specified to use an existing LDAP server as the backend, please make sure an LDAP server is running at %s" % ldap_backend_forced_uri)
445 self.logger.info("You have specified to use an existing LDAP server as the backend, please make sure an LDAP server is running at the default location")
447 if ldap_backend_forced_uri is not None:
448 self.logger.warn("You have specified to use an fixed URI %s for connecting to your LDAP server backend. This is NOT RECOMMENDED, as our default communiation over ldapi:// is more secure and much less")
450 if domain_sid is not None:
451 domain_sid = security.dom_sid(domain_sid)
453 session = system_session()
455 result = provision(self.logger,
456 session, smbconf=smbconf, targetdir=targetdir,
457 samdb_fill=samdb_fill, realm=realm, domain=domain,
458 domainguid=domain_guid, domainsid=domain_sid,
460 hostip=host_ip, hostip6=host_ip6,
461 sitename=site, ntdsguid=ntds_guid,
462 invocationid=invocationid, adminpass=adminpass,
463 krbtgtpass=krbtgtpass, machinepass=machinepass,
464 dns_backend=dns_backend, dns_forwarder=dns_forwarder,
465 dnspass=dnspass, root=root, nobody=nobody,
467 serverrole=server_role, dom_for_fun_level=dom_for_fun_level,
468 backend_type=ldap_backend_type,
469 ldapadminpass=ldapadminpass, ol_mmr_urls=ol_mmr_urls, slapd_path=slapd_path,
470 useeadb=eadb, next_rid=next_rid, lp=lp, use_ntvfs=use_ntvfs,
471 use_rfc2307=use_rfc2307, skip_sysvolacl=False,
472 ldap_backend_extra_port=ldap_backend_extra_port,
473 ldap_backend_forced_uri=ldap_backend_forced_uri,
474 nosync=ldap_backend_nosync, ldap_dryrun_mode=ldap_dryrun_mode)
476 except ProvisioningError, e:
477 raise CommandError("Provision failed", e)
479 result.report_logger(self.logger)
481 def _get_nameserver_ip(self):
482 """Grab the nameserver IP address from /etc/resolv.conf."""
484 RESOLV_CONF="/etc/resolv.conf"
486 if not path.isfile(RESOLV_CONF):
487 self.logger.warning("Failed to locate %s" % RESOLV_CONF)
492 handle = open(RESOLV_CONF, 'r')
494 if not line.startswith('nameserver'):
496 # we want the last non-space continuous string of the line
497 return line.strip().split()[-1]
499 if handle is not None:
502 self.logger.warning("No nameserver found in %s" % RESOLV_CONF)
505 class cmd_domain_dcpromo(Command):
506 """Promote an existing domain member or NT4 PDC to an AD DC."""
508 synopsis = "%prog <dnsdomain> [DC|RODC] [options]"
510 takes_optiongroups = {
511 "sambaopts": options.SambaOptions,
512 "versionopts": options.VersionOptions,
513 "credopts": options.CredentialsOptions,
517 Option("--server", help="DC to join", type=str),
518 Option("--site", help="site to join", type=str),
519 Option("--targetdir", help="where to store provision", type=str),
520 Option("--domain-critical-only",
521 help="only replicate critical domain objects",
522 action="store_true"),
523 Option("--machinepass", type=str, metavar="PASSWORD",
524 help="choose machine password (otherwise random)"),
525 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
526 choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
527 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
528 "BIND9_DLZ uses samba4 AD to store zone information, "
529 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
530 default="SAMBA_INTERNAL"),
531 Option("--quiet", help="Be quiet", action="store_true"),
532 Option("--verbose", help="Be verbose", action="store_true")
536 Option("--use-ntvfs", action="store_true", help="Use NTVFS for the fileserver (default = no)"),
539 if samba.is_ntvfs_fileserver_built():
540 takes_options.extend(ntvfs_options)
543 takes_args = ["domain", "role?"]
545 def run(self, domain, role=None, sambaopts=None, credopts=None,
546 versionopts=None, server=None, site=None, targetdir=None,
547 domain_critical_only=False, parent_domain=None, machinepass=None,
548 use_ntvfs=False, dns_backend=None,
549 quiet=False, verbose=False):
550 lp = sambaopts.get_loadparm()
551 creds = credopts.get_credentials(lp)
552 net = Net(creds, lp, server=credopts.ipaddress)
555 site = "Default-First-Site-Name"
557 logger = self.get_logger()
559 logger.setLevel(logging.DEBUG)
561 logger.setLevel(logging.WARNING)
563 logger.setLevel(logging.INFO)
565 netbios_name = lp.get("netbios name")
571 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
572 site=site, netbios_name=netbios_name, targetdir=targetdir,
573 domain_critical_only=domain_critical_only,
574 machinepass=machinepass, use_ntvfs=use_ntvfs,
575 dns_backend=dns_backend,
576 promote_existing=True)
578 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
579 site=site, netbios_name=netbios_name, targetdir=targetdir,
580 domain_critical_only=domain_critical_only,
581 machinepass=machinepass, use_ntvfs=use_ntvfs, dns_backend=dns_backend,
582 promote_existing=True)
584 raise CommandError("Invalid role '%s' (possible values: DC, RODC)" % role)
587 class cmd_domain_join(Command):
588 """Join domain as either member or backup domain controller."""
590 synopsis = "%prog <dnsdomain> [DC|RODC|MEMBER|SUBDOMAIN] [options]"
592 takes_optiongroups = {
593 "sambaopts": options.SambaOptions,
594 "versionopts": options.VersionOptions,
595 "credopts": options.CredentialsOptions,
599 Option("--server", help="DC to join", type=str),
600 Option("--site", help="site to join", type=str),
601 Option("--targetdir", help="where to store provision", type=str),
602 Option("--parent-domain", help="parent domain to create subdomain under", type=str),
603 Option("--domain-critical-only",
604 help="only replicate critical domain objects",
605 action="store_true"),
606 Option("--machinepass", type=str, metavar="PASSWORD",
607 help="choose machine password (otherwise random)"),
608 Option("--adminpass", type="string", metavar="PASSWORD",
609 help="choose adminstrator password when joining as a subdomain (otherwise random)"),
610 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
611 choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
612 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
613 "BIND9_DLZ uses samba4 AD to store zone information, "
614 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
615 default="SAMBA_INTERNAL"),
616 Option("--quiet", help="Be quiet", action="store_true"),
617 Option("--verbose", help="Be verbose", action="store_true")
621 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
624 if samba.is_ntvfs_fileserver_built():
625 takes_options.extend(ntvfs_options)
627 takes_args = ["domain", "role?"]
629 def run(self, domain, role=None, sambaopts=None, credopts=None,
630 versionopts=None, server=None, site=None, targetdir=None,
631 domain_critical_only=False, parent_domain=None, machinepass=None,
632 use_ntvfs=False, dns_backend=None, adminpass=None,
633 quiet=False, verbose=False):
634 lp = sambaopts.get_loadparm()
635 creds = credopts.get_credentials(lp)
636 net = Net(creds, lp, server=credopts.ipaddress)
639 site = "Default-First-Site-Name"
641 logger = self.get_logger()
643 logger.setLevel(logging.DEBUG)
645 logger.setLevel(logging.WARNING)
647 logger.setLevel(logging.INFO)
649 netbios_name = lp.get("netbios name")
654 if role is None or role == "MEMBER":
655 (join_password, sid, domain_name) = net.join_member(
656 domain, netbios_name, LIBNET_JOIN_AUTOMATIC,
657 machinepass=machinepass)
659 self.errf.write("Joined domain %s (%s)\n" % (domain_name, sid))
661 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
662 site=site, netbios_name=netbios_name, targetdir=targetdir,
663 domain_critical_only=domain_critical_only,
664 machinepass=machinepass, use_ntvfs=use_ntvfs, dns_backend=dns_backend)
666 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
667 site=site, netbios_name=netbios_name, targetdir=targetdir,
668 domain_critical_only=domain_critical_only,
669 machinepass=machinepass, use_ntvfs=use_ntvfs,
670 dns_backend=dns_backend)
671 elif role == "SUBDOMAIN":
673 logger.info("Administrator password will be set randomly!")
675 netbios_domain = lp.get("workgroup")
676 if parent_domain is None:
677 parent_domain = ".".join(domain.split(".")[1:])
678 join_subdomain(logger=logger, server=server, creds=creds, lp=lp, dnsdomain=domain,
679 parent_domain=parent_domain, site=site,
680 netbios_name=netbios_name, netbios_domain=netbios_domain,
681 targetdir=targetdir, machinepass=machinepass,
682 use_ntvfs=use_ntvfs, dns_backend=dns_backend,
685 raise CommandError("Invalid role '%s' (possible values: MEMBER, DC, RODC, SUBDOMAIN)" % role)
688 class cmd_domain_demote(Command):
689 """Demote ourselves from the role of Domain Controller."""
691 synopsis = "%prog [options]"
694 Option("--server", help="writable DC to write demotion changes on", type=str),
695 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
696 metavar="URL", dest="H"),
697 Option("--remove-other-dead-server", help="Dead DC (name or NTDS GUID) "
698 "to remove ALL references to (rather than this DC)", type=str),
699 Option("--quiet", help="Be quiet", action="store_true"),
700 Option("--verbose", help="Be verbose", action="store_true"),
703 takes_optiongroups = {
704 "sambaopts": options.SambaOptions,
705 "credopts": options.CredentialsOptions,
706 "versionopts": options.VersionOptions,
709 def run(self, sambaopts=None, credopts=None,
710 versionopts=None, server=None,
711 remove_other_dead_server=None, H=None,
712 verbose=False, quiet=False):
713 lp = sambaopts.get_loadparm()
714 creds = credopts.get_credentials(lp)
715 net = Net(creds, lp, server=credopts.ipaddress)
717 logger = self.get_logger()
719 logger.setLevel(logging.DEBUG)
721 logger.setLevel(logging.WARNING)
723 logger.setLevel(logging.INFO)
725 if remove_other_dead_server is not None:
726 if server is not None:
727 samdb = SamDB(url="ldap://%s" % server,
728 session_info=system_session(),
729 credentials=creds, lp=lp)
731 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
733 remove_dc.remove_dc(samdb, logger, remove_other_dead_server)
734 except remove_dc.DemoteException as err:
735 raise CommandError("Demote failed: %s" % err)
738 netbios_name = lp.get("netbios name")
739 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
741 res = samdb.search(expression='(&(objectClass=computer)(serverReferenceBL=*))', attrs=["dnsHostName", "name"])
743 raise CommandError("Unable to search for servers")
746 raise CommandError("You are the latest server in the domain")
750 if str(e["name"]).lower() != netbios_name.lower():
751 server = e["dnsHostName"]
754 ntds_guid = samdb.get_ntds_GUID()
755 msg = samdb.search(base=str(samdb.get_config_basedn()),
756 scope=ldb.SCOPE_SUBTREE, expression="(objectGUID=%s)" % ntds_guid,
758 if len(msg) == 0 or "options" not in msg[0]:
759 raise CommandError("Failed to find options on %s" % ntds_guid)
762 dsa_options = int(str(msg[0]['options']))
764 res = samdb.search(expression="(fSMORoleOwner=%s)" % str(ntds_dn),
765 controls=["search_options:1:2"])
768 raise CommandError("Current DC is still the owner of %d role(s), use the role command to transfer roles to another DC" % len(res))
770 self.errf.write("Using %s as partner server for the demotion\n" %
772 (drsuapiBind, drsuapi_handle, supportedExtensions) = drsuapi_connect(server, lp, creds)
774 self.errf.write("Deactivating inbound replication\n")
779 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
780 dsa_options |= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
781 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
785 self.errf.write("Asking partner server %s to synchronize from us\n"
787 for part in (samdb.get_schema_basedn(),
788 samdb.get_config_basedn(),
789 samdb.get_root_basedn()):
790 nc = drsuapi.DsReplicaObjectIdentifier()
793 req1 = drsuapi.DsReplicaSyncRequest1()
794 req1.naming_context = nc;
795 req1.options = drsuapi.DRSUAPI_DRS_WRIT_REP
796 req1.source_dsa_guid = misc.GUID(ntds_guid)
799 drsuapiBind.DsReplicaSync(drsuapi_handle, 1, req1)
800 except RuntimeError as (werr, string):
801 if werr == werror.WERR_DS_DRA_NO_REPLICA:
805 "Error while replicating out last local changes from '%s' for demotion, "
806 "re-enabling inbound replication\n" % part)
807 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
808 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
810 raise CommandError("Error while sending a DsReplicaSync for partition '%s'" % str(part), string)
812 remote_samdb = SamDB(url="ldap://%s" % server,
813 session_info=system_session(),
814 credentials=creds, lp=lp)
816 self.errf.write("Changing userControl and container\n")
817 res = remote_samdb.search(base=str(remote_samdb.domain_dn()),
818 expression="(&(objectClass=user)(sAMAccountName=%s$))" %
819 netbios_name.upper(),
820 attrs=["userAccountControl"])
822 uac = int(str(res[0]["userAccountControl"]))
825 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
827 "Error while demoting, re-enabling inbound replication\n")
828 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
829 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
831 raise CommandError("Error while changing account control", e)
834 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
836 "Error while demoting, re-enabling inbound replication")
837 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
838 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
840 raise CommandError("Unable to find object with samaccountName = %s$"
841 " in the remote dc" % netbios_name.upper())
845 uac &= ~(UF_SERVER_TRUST_ACCOUNT|UF_TRUSTED_FOR_DELEGATION|UF_PARTIAL_SECRETS_ACCOUNT)
846 uac |= UF_WORKSTATION_TRUST_ACCOUNT
851 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
852 ldb.FLAG_MOD_REPLACE,
853 "userAccountControl")
855 remote_samdb.modify(msg)
857 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
859 "Error while demoting, re-enabling inbound replication")
860 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
861 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
864 raise CommandError("Error while changing account control", e)
866 parent = msg.dn.parent()
867 dc_name = res[0].dn.get_rdn_value()
868 rdn = "CN=%s" % dc_name
870 # Let's move to the Computer container
874 computer_dn = ldb.Dn(remote_samdb, "CN=Computers,%s" % str(remote_samdb.domain_dn()))
875 res = remote_samdb.search(base=computer_dn, expression=rdn, scope=ldb.SCOPE_ONELEVEL)
878 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
879 scope=ldb.SCOPE_ONELEVEL)
880 while(len(res) != 0 and i < 100):
882 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
883 scope=ldb.SCOPE_ONELEVEL)
886 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
888 "Error while demoting, re-enabling inbound replication\n")
889 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
890 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
896 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
897 ldb.FLAG_MOD_REPLACE,
898 "userAccountControl")
900 remote_samdb.modify(msg)
902 raise CommandError("Unable to find a slot for renaming %s,"
903 " all names from %s-1 to %s-%d seemed used" %
904 (str(dc_dn), rdn, rdn, i - 9))
906 newrdn = "%s-%d" % (rdn, i)
909 newdn = ldb.Dn(remote_samdb, "%s,%s" % (newrdn, str(computer_dn)))
910 remote_samdb.rename(dc_dn, newdn)
912 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
914 "Error while demoting, re-enabling inbound replication\n")
915 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
916 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
922 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
923 ldb.FLAG_MOD_REPLACE,
924 "userAccountControl")
926 remote_samdb.modify(msg)
927 raise CommandError("Error while renaming %s to %s" % (str(dc_dn), str(newdn)), e)
930 server_dsa_dn = samdb.get_serverName()
931 domain = remote_samdb.get_root_basedn()
934 req1 = drsuapi.DsRemoveDSServerRequest1()
935 req1.server_dn = str(server_dsa_dn)
936 req1.domain_dn = str(domain)
939 drsuapiBind.DsRemoveDSServer(drsuapi_handle, 1, req1)
940 except RuntimeError as (werr, string):
941 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
943 "Error while demoting, re-enabling inbound replication\n")
944 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
945 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
951 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
952 ldb.FLAG_MOD_REPLACE,
953 "userAccountControl")
954 remote_samdb.modify(msg)
955 remote_samdb.rename(newdn, dc_dn)
956 if werr == werror.WERR_DS_DRA_NO_REPLICA:
957 raise CommandError("The DC %s is not present on (already removed from) the remote server: " % server_dsa_dn, e)
959 raise CommandError("Error while sending a removeDsServer of %s: " % server_dsa_dn, e)
961 remove_dc.remove_sysvol_references(remote_samdb, logger, dc_name)
963 # These are objects under the computer account that should be deleted
964 for s in ("CN=Enterprise,CN=NTFRS Subscriptions",
965 "CN=%s, CN=NTFRS Subscriptions" % lp.get("realm"),
966 "CN=Domain system Volumes (SYSVOL Share), CN=NTFRS Subscriptions",
967 "CN=NTFRS Subscriptions"):
969 remote_samdb.delete(ldb.Dn(remote_samdb,
970 "%s,%s" % (s, str(newdn))))
971 except ldb.LdbError, l:
974 self.errf.write("Demote successful\n")
977 class cmd_domain_level(Command):
978 """Raise domain and forest function levels."""
980 synopsis = "%prog (show|raise <options>) [options]"
982 takes_optiongroups = {
983 "sambaopts": options.SambaOptions,
984 "credopts": options.CredentialsOptions,
985 "versionopts": options.VersionOptions,
989 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
990 metavar="URL", dest="H"),
991 Option("--quiet", help="Be quiet", action="store_true"),
992 Option("--forest-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
993 help="The forest function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)"),
994 Option("--domain-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
995 help="The domain function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)")
998 takes_args = ["subcommand"]
1000 def run(self, subcommand, H=None, forest_level=None, domain_level=None,
1001 quiet=False, credopts=None, sambaopts=None, versionopts=None):
1002 lp = sambaopts.get_loadparm()
1003 creds = credopts.get_credentials(lp, fallback_machine=True)
1005 samdb = SamDB(url=H, session_info=system_session(),
1006 credentials=creds, lp=lp)
1008 domain_dn = samdb.domain_dn()
1010 res_forest = samdb.search("CN=Partitions,%s" % samdb.get_config_basedn(),
1011 scope=ldb.SCOPE_BASE, attrs=["msDS-Behavior-Version"])
1012 assert len(res_forest) == 1
1014 res_domain = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1015 attrs=["msDS-Behavior-Version", "nTMixedDomain"])
1016 assert len(res_domain) == 1
1018 res_dc_s = samdb.search("CN=Sites,%s" % samdb.get_config_basedn(),
1019 scope=ldb.SCOPE_SUBTREE, expression="(objectClass=nTDSDSA)",
1020 attrs=["msDS-Behavior-Version"])
1021 assert len(res_dc_s) >= 1
1023 # default values, since "msDS-Behavior-Version" does not exist on Windows 2000 AD
1024 level_forest = DS_DOMAIN_FUNCTION_2000
1025 level_domain = DS_DOMAIN_FUNCTION_2000
1027 if "msDS-Behavior-Version" in res_forest[0]:
1028 level_forest = int(res_forest[0]["msDS-Behavior-Version"][0])
1029 if "msDS-Behavior-Version" in res_domain[0]:
1030 level_domain = int(res_domain[0]["msDS-Behavior-Version"][0])
1031 level_domain_mixed = int(res_domain[0]["nTMixedDomain"][0])
1034 for msg in res_dc_s:
1035 if "msDS-Behavior-Version" in msg:
1036 if min_level_dc is None or int(msg["msDS-Behavior-Version"][0]) < min_level_dc:
1037 min_level_dc = int(msg["msDS-Behavior-Version"][0])
1039 min_level_dc = DS_DOMAIN_FUNCTION_2000
1040 # well, this is the least
1043 if level_forest < DS_DOMAIN_FUNCTION_2000 or level_domain < DS_DOMAIN_FUNCTION_2000:
1044 raise CommandError("Domain and/or forest function level(s) is/are invalid. Correct them or reprovision!")
1045 if min_level_dc < DS_DOMAIN_FUNCTION_2000:
1046 raise CommandError("Lowest function level of a DC is invalid. Correct this or reprovision!")
1047 if level_forest > level_domain:
1048 raise CommandError("Forest function level is higher than the domain level(s). Correct this or reprovision!")
1049 if level_domain > min_level_dc:
1050 raise CommandError("Domain function level is higher than the lowest function level of a DC. Correct this or reprovision!")
1052 if subcommand == "show":
1053 self.message("Domain and forest function level for domain '%s'" % domain_dn)
1054 if level_forest == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1055 self.message("\nATTENTION: You run SAMBA 4 on a forest function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1056 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1057 self.message("\nATTENTION: You run SAMBA 4 on a domain function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1058 if min_level_dc == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1059 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)!")
1063 if level_forest == DS_DOMAIN_FUNCTION_2000:
1065 elif level_forest == DS_DOMAIN_FUNCTION_2003_MIXED:
1066 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1067 elif level_forest == DS_DOMAIN_FUNCTION_2003:
1069 elif level_forest == DS_DOMAIN_FUNCTION_2008:
1071 elif level_forest == DS_DOMAIN_FUNCTION_2008_R2:
1073 elif level_forest == DS_DOMAIN_FUNCTION_2012:
1075 elif level_forest == DS_DOMAIN_FUNCTION_2012_R2:
1078 outstr = "higher than 2012 R2"
1079 self.message("Forest function level: (Windows) " + outstr)
1081 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1082 outstr = "2000 mixed (NT4 DC support)"
1083 elif level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed == 0:
1085 elif level_domain == DS_DOMAIN_FUNCTION_2003_MIXED:
1086 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1087 elif level_domain == DS_DOMAIN_FUNCTION_2003:
1089 elif level_domain == DS_DOMAIN_FUNCTION_2008:
1091 elif level_domain == DS_DOMAIN_FUNCTION_2008_R2:
1093 elif level_domain == DS_DOMAIN_FUNCTION_2012:
1095 elif level_domain == DS_DOMAIN_FUNCTION_2012_R2:
1098 outstr = "higher than 2012 R2"
1099 self.message("Domain function level: (Windows) " + outstr)
1101 if min_level_dc == DS_DOMAIN_FUNCTION_2000:
1103 elif min_level_dc == DS_DOMAIN_FUNCTION_2003:
1105 elif min_level_dc == DS_DOMAIN_FUNCTION_2008:
1107 elif min_level_dc == DS_DOMAIN_FUNCTION_2008_R2:
1109 elif min_level_dc == DS_DOMAIN_FUNCTION_2012:
1111 elif min_level_dc == DS_DOMAIN_FUNCTION_2012_R2:
1114 outstr = "higher than 2012 R2"
1115 self.message("Lowest function level of a DC: (Windows) " + outstr)
1117 elif subcommand == "raise":
1120 if domain_level is not None:
1121 if domain_level == "2003":
1122 new_level_domain = DS_DOMAIN_FUNCTION_2003
1123 elif domain_level == "2008":
1124 new_level_domain = DS_DOMAIN_FUNCTION_2008
1125 elif domain_level == "2008_R2":
1126 new_level_domain = DS_DOMAIN_FUNCTION_2008_R2
1127 elif domain_level == "2012":
1128 new_level_domain = DS_DOMAIN_FUNCTION_2012
1129 elif domain_level == "2012_R2":
1130 new_level_domain = DS_DOMAIN_FUNCTION_2012_R2
1132 if new_level_domain <= level_domain and level_domain_mixed == 0:
1133 raise CommandError("Domain function level can't be smaller than or equal to the actual one!")
1134 if new_level_domain > min_level_dc:
1135 raise CommandError("Domain function level can't be higher than the lowest function level of a DC!")
1137 # Deactivate mixed/interim domain support
1138 if level_domain_mixed != 0:
1139 # Directly on the base DN
1141 m.dn = ldb.Dn(samdb, domain_dn)
1142 m["nTMixedDomain"] = ldb.MessageElement("0",
1143 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1147 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup") + ",CN=Partitions,%s" % samdb.get_config_basedn())
1148 m["nTMixedDomain"] = ldb.MessageElement("0",
1149 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1152 except ldb.LdbError, (enum, emsg):
1153 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1156 # Directly on the base DN
1158 m.dn = ldb.Dn(samdb, domain_dn)
1159 m["msDS-Behavior-Version"]= ldb.MessageElement(
1160 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1161 "msDS-Behavior-Version")
1165 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup")
1166 + ",CN=Partitions,%s" % samdb.get_config_basedn())
1167 m["msDS-Behavior-Version"]= ldb.MessageElement(
1168 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1169 "msDS-Behavior-Version")
1172 except ldb.LdbError, (enum, emsg):
1173 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1176 level_domain = new_level_domain
1177 msgs.append("Domain function level changed!")
1179 if forest_level is not None:
1180 if forest_level == "2003":
1181 new_level_forest = DS_DOMAIN_FUNCTION_2003
1182 elif forest_level == "2008":
1183 new_level_forest = DS_DOMAIN_FUNCTION_2008
1184 elif forest_level == "2008_R2":
1185 new_level_forest = DS_DOMAIN_FUNCTION_2008_R2
1186 elif forest_level == "2012":
1187 new_level_forest = DS_DOMAIN_FUNCTION_2012
1188 elif forest_level == "2012_R2":
1189 new_level_forest = DS_DOMAIN_FUNCTION_2012_R2
1191 if new_level_forest <= level_forest:
1192 raise CommandError("Forest function level can't be smaller than or equal to the actual one!")
1193 if new_level_forest > level_domain:
1194 raise CommandError("Forest function level can't be higher than the domain function level(s). Please raise it/them first!")
1197 m.dn = ldb.Dn(samdb, "CN=Partitions,%s" % samdb.get_config_basedn())
1198 m["msDS-Behavior-Version"]= ldb.MessageElement(
1199 str(new_level_forest), ldb.FLAG_MOD_REPLACE,
1200 "msDS-Behavior-Version")
1202 msgs.append("Forest function level changed!")
1203 msgs.append("All changes applied successfully!")
1204 self.message("\n".join(msgs))
1206 raise CommandError("invalid argument: '%s' (choose from 'show', 'raise')" % subcommand)
1209 class cmd_domain_passwordsettings(Command):
1210 """Set password settings.
1212 Password complexity, password lockout policy, history length,
1213 minimum password length, the minimum and maximum password age) on
1214 a Samba AD DC server.
1216 Use against a Windows DC is possible, but group policy will override it.
1219 synopsis = "%prog (show|set <options>) [options]"
1221 takes_optiongroups = {
1222 "sambaopts": options.SambaOptions,
1223 "versionopts": options.VersionOptions,
1224 "credopts": options.CredentialsOptions,
1228 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1229 metavar="URL", dest="H"),
1230 Option("--quiet", help="Be quiet", action="store_true"),
1231 Option("--complexity", type="choice", choices=["on","off","default"],
1232 help="The password complexity (on | off | default). Default is 'on'"),
1233 Option("--store-plaintext", type="choice", choices=["on","off","default"],
1234 help="Store plaintext passwords where account have 'store passwords with reversible encryption' set (on | off | default). Default is 'off'"),
1235 Option("--history-length",
1236 help="The password history length (<integer> | default). Default is 24.", type=str),
1237 Option("--min-pwd-length",
1238 help="The minimum password length (<integer> | default). Default is 7.", type=str),
1239 Option("--min-pwd-age",
1240 help="The minimum password age (<integer in days> | default). Default is 1.", type=str),
1241 Option("--max-pwd-age",
1242 help="The maximum password age (<integer in days> | default). Default is 43.", type=str),
1243 Option("--account-lockout-duration",
1244 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),
1245 Option("--account-lockout-threshold",
1246 help="The number of bad password attempts allowed before locking out the account (<integer> | default). Default is 0 (never lock out).", type=str),
1247 Option("--reset-account-lockout-after",
1248 help="After this time is elapsed, the recorded number of attempts restarts from zero (<integer> | default). Default is 30.", type=str),
1251 takes_args = ["subcommand"]
1253 def run(self, subcommand, H=None, min_pwd_age=None, max_pwd_age=None,
1254 quiet=False, complexity=None, store_plaintext=None, history_length=None,
1255 min_pwd_length=None, account_lockout_duration=None, account_lockout_threshold=None,
1256 reset_account_lockout_after=None, credopts=None, sambaopts=None,
1258 lp = sambaopts.get_loadparm()
1259 creds = credopts.get_credentials(lp)
1261 samdb = SamDB(url=H, session_info=system_session(),
1262 credentials=creds, lp=lp)
1264 domain_dn = samdb.domain_dn()
1265 res = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1266 attrs=["pwdProperties", "pwdHistoryLength", "minPwdLength",
1267 "minPwdAge", "maxPwdAge", "lockoutDuration", "lockoutThreshold",
1268 "lockOutObservationWindow"])
1269 assert(len(res) == 1)
1271 pwd_props = int(res[0]["pwdProperties"][0])
1272 pwd_hist_len = int(res[0]["pwdHistoryLength"][0])
1273 cur_min_pwd_len = int(res[0]["minPwdLength"][0])
1275 cur_min_pwd_age = int(abs(int(res[0]["minPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1276 if int(res[0]["maxPwdAge"][0]) == -0x8000000000000000:
1279 cur_max_pwd_age = int(abs(int(res[0]["maxPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1280 cur_account_lockout_threshold = int(res[0]["lockoutThreshold"][0])
1282 if int(res[0]["lockoutDuration"][0]) == -0x8000000000000000:
1283 cur_account_lockout_duration = 0
1285 cur_account_lockout_duration = abs(int(res[0]["lockoutDuration"][0])) / (1e7 * 60)
1286 cur_reset_account_lockout_after = abs(int(res[0]["lockOutObservationWindow"][0])) / (1e7 * 60)
1287 except Exception, e:
1288 raise CommandError("Could not retrieve password properties!", e)
1290 if subcommand == "show":
1291 self.message("Password informations for domain '%s'" % domain_dn)
1293 if pwd_props & DOMAIN_PASSWORD_COMPLEX != 0:
1294 self.message("Password complexity: on")
1296 self.message("Password complexity: off")
1297 if pwd_props & DOMAIN_PASSWORD_STORE_CLEARTEXT != 0:
1298 self.message("Store plaintext passwords: on")
1300 self.message("Store plaintext passwords: off")
1301 self.message("Password history length: %d" % pwd_hist_len)
1302 self.message("Minimum password length: %d" % cur_min_pwd_len)
1303 self.message("Minimum password age (days): %d" % cur_min_pwd_age)
1304 self.message("Maximum password age (days): %d" % cur_max_pwd_age)
1305 self.message("Account lockout duration (mins): %d" % cur_account_lockout_duration)
1306 self.message("Account lockout threshold (attempts): %d" % cur_account_lockout_threshold)
1307 self.message("Reset account lockout after (mins): %d" % cur_reset_account_lockout_after)
1308 elif subcommand == "set":
1311 m.dn = ldb.Dn(samdb, domain_dn)
1313 if complexity is not None:
1314 if complexity == "on" or complexity == "default":
1315 pwd_props = pwd_props | DOMAIN_PASSWORD_COMPLEX
1316 msgs.append("Password complexity activated!")
1317 elif complexity == "off":
1318 pwd_props = pwd_props & (~DOMAIN_PASSWORD_COMPLEX)
1319 msgs.append("Password complexity deactivated!")
1321 if store_plaintext is not None:
1322 if store_plaintext == "on" or store_plaintext == "default":
1323 pwd_props = pwd_props | DOMAIN_PASSWORD_STORE_CLEARTEXT
1324 msgs.append("Plaintext password storage for changed passwords activated!")
1325 elif store_plaintext == "off":
1326 pwd_props = pwd_props & (~DOMAIN_PASSWORD_STORE_CLEARTEXT)
1327 msgs.append("Plaintext password storage for changed passwords deactivated!")
1329 if complexity is not None or store_plaintext is not None:
1330 m["pwdProperties"] = ldb.MessageElement(str(pwd_props),
1331 ldb.FLAG_MOD_REPLACE, "pwdProperties")
1333 if history_length is not None:
1334 if history_length == "default":
1337 pwd_hist_len = int(history_length)
1339 if pwd_hist_len < 0 or pwd_hist_len > 24:
1340 raise CommandError("Password history length must be in the range of 0 to 24!")
1342 m["pwdHistoryLength"] = ldb.MessageElement(str(pwd_hist_len),
1343 ldb.FLAG_MOD_REPLACE, "pwdHistoryLength")
1344 msgs.append("Password history length changed!")
1346 if min_pwd_length is not None:
1347 if min_pwd_length == "default":
1350 min_pwd_len = int(min_pwd_length)
1352 if min_pwd_len < 0 or min_pwd_len > 14:
1353 raise CommandError("Minimum password length must be in the range of 0 to 14!")
1355 m["minPwdLength"] = ldb.MessageElement(str(min_pwd_len),
1356 ldb.FLAG_MOD_REPLACE, "minPwdLength")
1357 msgs.append("Minimum password length changed!")
1359 if min_pwd_age is not None:
1360 if min_pwd_age == "default":
1363 min_pwd_age = int(min_pwd_age)
1365 if min_pwd_age < 0 or min_pwd_age > 998:
1366 raise CommandError("Minimum password age must be in the range of 0 to 998!")
1369 min_pwd_age_ticks = -int(min_pwd_age * (24 * 60 * 60 * 1e7))
1371 m["minPwdAge"] = ldb.MessageElement(str(min_pwd_age_ticks),
1372 ldb.FLAG_MOD_REPLACE, "minPwdAge")
1373 msgs.append("Minimum password age changed!")
1375 if max_pwd_age is not None:
1376 if max_pwd_age == "default":
1379 max_pwd_age = int(max_pwd_age)
1381 if max_pwd_age < 0 or max_pwd_age > 999:
1382 raise CommandError("Maximum password age must be in the range of 0 to 999!")
1385 if max_pwd_age == 0:
1386 max_pwd_age_ticks = -0x8000000000000000
1388 max_pwd_age_ticks = -int(max_pwd_age * (24 * 60 * 60 * 1e7))
1390 m["maxPwdAge"] = ldb.MessageElement(str(max_pwd_age_ticks),
1391 ldb.FLAG_MOD_REPLACE, "maxPwdAge")
1392 msgs.append("Maximum password age changed!")
1394 if account_lockout_duration is not None:
1395 if account_lockout_duration == "default":
1396 account_lockout_duration = 30
1398 account_lockout_duration = int(account_lockout_duration)
1400 if account_lockout_duration < 0 or account_lockout_duration > 99999:
1401 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1404 if account_lockout_duration == 0:
1405 account_lockout_duration_ticks = -0x8000000000000000
1407 account_lockout_duration_ticks = -int(account_lockout_duration * (60 * 1e7))
1409 m["lockoutDuration"] = ldb.MessageElement(str(account_lockout_duration_ticks),
1410 ldb.FLAG_MOD_REPLACE, "lockoutDuration")
1411 msgs.append("Account lockout duration changed!")
1413 if account_lockout_threshold is not None:
1414 if account_lockout_threshold == "default":
1415 account_lockout_threshold = 0
1417 account_lockout_threshold = int(account_lockout_threshold)
1419 m["lockoutThreshold"] = ldb.MessageElement(str(account_lockout_threshold),
1420 ldb.FLAG_MOD_REPLACE, "lockoutThreshold")
1421 msgs.append("Account lockout threshold changed!")
1423 if reset_account_lockout_after is not None:
1424 if reset_account_lockout_after == "default":
1425 reset_account_lockout_after = 30
1427 reset_account_lockout_after = int(reset_account_lockout_after)
1429 if reset_account_lockout_after < 0 or reset_account_lockout_after > 99999:
1430 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1433 if reset_account_lockout_after == 0:
1434 reset_account_lockout_after_ticks = -0x8000000000000000
1436 reset_account_lockout_after_ticks = -int(reset_account_lockout_after * (60 * 1e7))
1438 m["lockOutObservationWindow"] = ldb.MessageElement(str(reset_account_lockout_after_ticks),
1439 ldb.FLAG_MOD_REPLACE, "lockOutObservationWindow")
1440 msgs.append("Duration to reset account lockout after changed!")
1442 if max_pwd_age > 0 and min_pwd_age >= max_pwd_age:
1443 raise CommandError("Maximum password age (%d) must be greater than minimum password age (%d)!" % (max_pwd_age, min_pwd_age))
1446 raise CommandError("You must specify at least one option to set. Try --help")
1448 msgs.append("All changes applied successfully!")
1449 self.message("\n".join(msgs))
1451 raise CommandError("Wrong argument '%s'!" % subcommand)
1454 class cmd_domain_classicupgrade(Command):
1455 """Upgrade from Samba classic (NT4-like) database to Samba AD DC database.
1457 Specify either a directory with all Samba classic DC databases and state files (with --dbdir) or
1458 the testparm utility from your classic installation (with --testparm).
1461 synopsis = "%prog [options] <classic_smb_conf>"
1463 takes_optiongroups = {
1464 "sambaopts": options.SambaOptions,
1465 "versionopts": options.VersionOptions
1469 Option("--dbdir", type="string", metavar="DIR",
1470 help="Path to samba classic DC database directory"),
1471 Option("--testparm", type="string", metavar="PATH",
1472 help="Path to samba classic DC testparm utility from the previous installation. This allows the default paths of the previous installation to be followed"),
1473 Option("--targetdir", type="string", metavar="DIR",
1474 help="Path prefix where the new Samba 4.0 AD domain should be initialised"),
1475 Option("--quiet", help="Be quiet", action="store_true"),
1476 Option("--verbose", help="Be verbose", action="store_true"),
1477 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
1478 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
1479 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
1480 "BIND9_FLATFILE uses bind9 text database to store zone information, "
1481 "BIND9_DLZ uses samba4 AD to store zone information, "
1482 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
1483 default="SAMBA_INTERNAL")
1487 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
1488 action="store_true"),
1489 Option("--use-xattrs", type="choice", choices=["yes","no","auto"],
1490 metavar="[yes|no|auto]",
1491 help="Define if we should use the native fs capabilities or a tdb file for "
1492 "storing attributes likes ntacl when --use-ntvfs is set. "
1493 "auto tries to make an inteligent guess based on the user rights and system capabilities",
1496 if samba.is_ntvfs_fileserver_built():
1497 takes_options.extend(ntvfs_options)
1499 takes_args = ["smbconf"]
1501 def run(self, smbconf=None, targetdir=None, dbdir=None, testparm=None,
1502 quiet=False, verbose=False, use_xattrs="auto", sambaopts=None, versionopts=None,
1503 dns_backend=None, use_ntvfs=False):
1505 if not os.path.exists(smbconf):
1506 raise CommandError("File %s does not exist" % smbconf)
1508 if testparm and not os.path.exists(testparm):
1509 raise CommandError("Testparm utility %s does not exist" % testparm)
1511 if dbdir and not os.path.exists(dbdir):
1512 raise CommandError("Directory %s does not exist" % dbdir)
1514 if not dbdir and not testparm:
1515 raise CommandError("Please specify either dbdir or testparm")
1517 logger = self.get_logger()
1519 logger.setLevel(logging.DEBUG)
1521 logger.setLevel(logging.WARNING)
1523 logger.setLevel(logging.INFO)
1525 if dbdir and testparm:
1526 logger.warning("both dbdir and testparm specified, ignoring dbdir.")
1529 lp = sambaopts.get_loadparm()
1531 s3conf = s3param.get_context()
1534 s3conf.set("realm", sambaopts.realm)
1536 if targetdir is not None:
1537 if not os.path.isdir(targetdir):
1541 if use_xattrs == "yes":
1543 elif use_xattrs == "auto" and use_ntvfs == False:
1545 elif use_ntvfs == False:
1546 raise CommandError("--use-xattrs=no requires --use-ntvfs (not supported for production use). "
1547 "Please re-run with --use-xattrs omitted.")
1548 elif use_xattrs == "auto" and not s3conf.get("posix:eadb"):
1550 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
1552 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
1555 samba.ntacls.setntacl(lp, tmpfile.name,
1556 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
1559 # FIXME: Don't catch all exceptions here
1560 logger.info("You are not root or your system does not support xattr, using tdb backend for attributes. "
1561 "If you intend to use this provision in production, rerun the script as root on a system supporting xattrs.")
1565 # Set correct default values from dbdir or testparm
1568 paths["state directory"] = dbdir
1569 paths["private dir"] = dbdir
1570 paths["lock directory"] = dbdir
1571 paths["smb passwd file"] = dbdir + "/smbpasswd"
1573 paths["state directory"] = get_testparm_var(testparm, smbconf, "state directory")
1574 paths["private dir"] = get_testparm_var(testparm, smbconf, "private dir")
1575 paths["smb passwd file"] = get_testparm_var(testparm, smbconf, "smb passwd file")
1576 paths["lock directory"] = get_testparm_var(testparm, smbconf, "lock directory")
1577 # "testparm" from Samba 3 < 3.4.x is not aware of the parameter
1578 # "state directory", instead make use of "lock directory"
1579 if len(paths["state directory"]) == 0:
1580 paths["state directory"] = paths["lock directory"]
1583 s3conf.set(p, paths[p])
1585 # load smb.conf parameters
1586 logger.info("Reading smb.conf")
1587 s3conf.load(smbconf)
1588 samba3 = Samba3(smbconf, s3conf)
1590 logger.info("Provisioning")
1591 upgrade_from_samba3(samba3, logger, targetdir, session_info=system_session(),
1592 useeadb=eadb, dns_backend=dns_backend, use_ntvfs=use_ntvfs)
1595 class cmd_domain_samba3upgrade(cmd_domain_classicupgrade):
1596 __doc__ = cmd_domain_classicupgrade.__doc__
1598 # This command is present for backwards compatibility only,
1599 # and should not be shown.
1603 class LocalDCCredentialsOptions(options.CredentialsOptions):
1604 def __init__(self, parser):
1605 options.CredentialsOptions.__init__(self, parser, special_name="local-dc")
1607 class DomainTrustCommand(Command):
1608 """List domain trusts."""
1611 Command.__init__(self)
1612 self.local_lp = None
1614 self.local_server = None
1615 self.local_binding_string = None
1616 self.local_creds = None
1618 self.remote_server = None
1619 self.remote_binding_string = None
1620 self.remote_creds = None
1622 def _uint32(self, v):
1623 return ctypes.c_uint32(v).value
1625 def check_runtime_error(self, runtime, val):
1629 err32 = self._uint32(runtime[0])
1635 class LocalRuntimeError(CommandError):
1636 def __init__(exception_self, self, runtime, message):
1637 err32 = self._uint32(runtime[0])
1639 msg = "LOCAL_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1640 self.local_server, message, err32, errstr)
1641 CommandError.__init__(exception_self, msg)
1643 class RemoteRuntimeError(CommandError):
1644 def __init__(exception_self, self, runtime, message):
1645 err32 = self._uint32(runtime[0])
1647 msg = "REMOTE_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1648 self.remote_server, message, err32, errstr)
1649 CommandError.__init__(exception_self, msg)
1651 class LocalLdbError(CommandError):
1652 def __init__(exception_self, self, ldb_error, message):
1653 errval = ldb_error[0]
1654 errstr = ldb_error[1]
1655 msg = "LOCAL_DC[%s]: %s - ERROR(%d) - %s" % (
1656 self.local_server, message, errval, errstr)
1657 CommandError.__init__(exception_self, msg)
1659 def setup_local_server(self, sambaopts, localdcopts):
1660 if self.local_server is not None:
1661 return self.local_server
1663 lp = sambaopts.get_loadparm()
1665 local_server = localdcopts.ipaddress
1666 if local_server is None:
1667 server_role = lp.server_role()
1668 if server_role != "ROLE_ACTIVE_DIRECTORY_DC":
1669 raise CommandError("Invalid server_role %s" % (server_role))
1670 local_server = lp.get('netbios name')
1671 local_transport = "ncalrpc"
1672 local_binding_options = ""
1673 local_binding_options += ",auth_type=ncalrpc_as_system"
1674 local_ldap_url = None
1677 local_transport = "ncacn_np"
1678 local_binding_options = ""
1679 local_ldap_url = "ldap://%s" % local_server
1680 local_creds = localdcopts.get_credentials(lp)
1684 self.local_server = local_server
1685 self.local_binding_string = "%s:%s[%s]" % (local_transport, local_server, local_binding_options)
1686 self.local_ldap_url = local_ldap_url
1687 self.local_creds = local_creds
1688 return self.local_server
1690 def new_local_lsa_connection(self):
1691 return lsa.lsarpc(self.local_binding_string, self.local_lp, self.local_creds)
1693 def new_local_netlogon_connection(self):
1694 return netlogon.netlogon(self.local_binding_string, self.local_lp, self.local_creds)
1696 def new_local_ldap_connection(self):
1697 return SamDB(url=self.local_ldap_url,
1698 session_info=system_session(),
1699 credentials=self.local_creds,
1702 def setup_remote_server(self, credopts, domain,
1704 require_writable=True):
1707 assert require_writable
1709 if self.remote_server is not None:
1710 return self.remote_server
1712 self.remote_server = "__unknown__remote_server__.%s" % domain
1713 assert self.local_server is not None
1715 remote_creds = credopts.get_credentials(self.local_lp)
1716 remote_server = credopts.ipaddress
1717 remote_binding_options = ""
1719 # TODO: we should also support NT4 domains
1720 # we could use local_netlogon.netr_DsRGetDCNameEx2() with the remote domain name
1721 # and delegate NBT or CLDAP to the local netlogon server
1723 remote_net = Net(remote_creds, self.local_lp, server=remote_server)
1724 remote_flags = nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS
1725 if require_writable:
1726 remote_flags |= nbt.NBT_SERVER_WRITABLE
1728 remote_flags |= nbt.NBT_SERVER_PDC
1729 remote_info = remote_net.finddc(flags=remote_flags, domain=domain, address=remote_server)
1731 raise CommandError("Failed to find a writeable DC for domain '%s'" % domain)
1733 nbt.NBT_SERVER_PDC: "PDC",
1734 nbt.NBT_SERVER_GC: "GC",
1735 nbt.NBT_SERVER_LDAP: "LDAP",
1736 nbt.NBT_SERVER_DS: "DS",
1737 nbt.NBT_SERVER_KDC: "KDC",
1738 nbt.NBT_SERVER_TIMESERV: "TIMESERV",
1739 nbt.NBT_SERVER_CLOSEST: "CLOSEST",
1740 nbt.NBT_SERVER_WRITABLE: "WRITABLE",
1741 nbt.NBT_SERVER_GOOD_TIMESERV: "GOOD_TIMESERV",
1742 nbt.NBT_SERVER_NDNC: "NDNC",
1743 nbt.NBT_SERVER_SELECT_SECRET_DOMAIN_6: "SELECT_SECRET_DOMAIN_6",
1744 nbt.NBT_SERVER_FULL_SECRET_DOMAIN_6: "FULL_SECRET_DOMAIN_6",
1745 nbt.NBT_SERVER_ADS_WEB_SERVICE: "ADS_WEB_SERVICE",
1746 nbt.NBT_SERVER_DS_8: "DS_8",
1747 nbt.NBT_SERVER_HAS_DNS_NAME: "HAS_DNS_NAME",
1748 nbt.NBT_SERVER_IS_DEFAULT_NC: "IS_DEFAULT_NC",
1749 nbt.NBT_SERVER_FOREST_ROOT: "FOREST_ROOT",
1751 server_type_string = self.generic_bitmap_to_string(flag_map,
1752 remote_info.server_type, names_only=True)
1753 self.outf.write("RemoteDC Netbios[%s] DNS[%s] ServerType[%s]\n" % (
1754 remote_info.pdc_name,
1755 remote_info.pdc_dns_name,
1756 server_type_string))
1758 self.remote_server = remote_info.pdc_dns_name
1759 self.remote_binding_string="ncacn_np:%s[%s]" % (self.remote_server, remote_binding_options)
1760 self.remote_creds = remote_creds
1761 return self.remote_server
1763 def new_remote_lsa_connection(self):
1764 return lsa.lsarpc(self.remote_binding_string, self.local_lp, self.remote_creds)
1766 def new_remote_netlogon_connection(self):
1767 return netlogon.netlogon(self.remote_binding_string, self.local_lp, self.remote_creds)
1769 def get_lsa_info(self, conn, policy_access):
1770 objectAttr = lsa.ObjectAttribute()
1771 objectAttr.sec_qos = lsa.QosInfo()
1773 policy = conn.OpenPolicy2(''.decode('utf-8'),
1774 objectAttr, policy_access)
1776 info = conn.QueryInfoPolicy2(policy, lsa.LSA_POLICY_INFO_DNS)
1778 return (policy, info)
1780 def get_netlogon_dc_info(self, conn, server):
1781 info = conn.netr_DsRGetDCNameEx2(server,
1782 None, 0, None, None, None,
1783 netlogon.DS_RETURN_DNS_NAME)
1786 def netr_DomainTrust_to_name(self, t):
1787 if t.trust_type == lsa.LSA_TRUST_TYPE_DOWNLEVEL:
1788 return t.netbios_name
1792 def netr_DomainTrust_to_type(self, a, t):
1794 primary_parent = None
1796 if _t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
1798 if not _t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1799 primary_parent = a[_t.parent_index]
1802 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1803 if t is primary_parent:
1806 if t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1809 parent = a[t.parent_index]
1810 if parent is primary:
1815 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1820 def netr_DomainTrust_to_transitive(self, t):
1821 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1824 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE:
1827 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1832 def netr_DomainTrust_to_direction(self, t):
1833 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND and \
1834 t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1837 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND:
1840 if t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1845 def generic_enum_to_string(self, e_dict, v, names_only=False):
1849 v32 = self._uint32(v)
1850 w = "__unknown__%08X__" % v32
1852 r = "0x%x (%s)" % (v, w)
1855 def generic_bitmap_to_string(self, b_dict, v, names_only=False):
1860 for b in sorted(b_dict.keys()):
1867 c32 = self._uint32(c)
1868 s += ["__unknown_%08X__" % c32]
1873 r = "0x%x (%s)" % (v, w)
1876 def trustType_string(self, v):
1878 lsa.LSA_TRUST_TYPE_DOWNLEVEL : "DOWNLEVEL",
1879 lsa.LSA_TRUST_TYPE_UPLEVEL : "UPLEVEL",
1880 lsa.LSA_TRUST_TYPE_MIT : "MIT",
1881 lsa.LSA_TRUST_TYPE_DCE : "DCE",
1883 return self.generic_enum_to_string(types, v)
1885 def trustDirection_string(self, v):
1887 lsa.LSA_TRUST_DIRECTION_INBOUND |
1888 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "BOTH",
1889 lsa.LSA_TRUST_DIRECTION_INBOUND : "INBOUND",
1890 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "OUTBOUND",
1892 return self.generic_enum_to_string(directions, v)
1894 def trustAttributes_string(self, v):
1896 lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE : "NON_TRANSITIVE",
1897 lsa.LSA_TRUST_ATTRIBUTE_UPLEVEL_ONLY : "UPLEVEL_ONLY",
1898 lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN : "QUARANTINED_DOMAIN",
1899 lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE : "FOREST_TRANSITIVE",
1900 lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION : "CROSS_ORGANIZATION",
1901 lsa.LSA_TRUST_ATTRIBUTE_WITHIN_FOREST : "WITHIN_FOREST",
1902 lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL : "TREAT_AS_EXTERNAL",
1903 lsa.LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION : "USES_RC4_ENCRYPTION",
1905 return self.generic_bitmap_to_string(attributes, v)
1907 def kerb_EncTypes_string(self, v):
1909 security.KERB_ENCTYPE_DES_CBC_CRC : "DES_CBC_CRC",
1910 security.KERB_ENCTYPE_DES_CBC_MD5 : "DES_CBC_MD5",
1911 security.KERB_ENCTYPE_RC4_HMAC_MD5 : "RC4_HMAC_MD5",
1912 security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96 : "AES128_CTS_HMAC_SHA1_96",
1913 security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96 : "AES256_CTS_HMAC_SHA1_96",
1914 security.KERB_ENCTYPE_FAST_SUPPORTED : "FAST_SUPPORTED",
1915 security.KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED : "COMPOUND_IDENTITY_SUPPORTED",
1916 security.KERB_ENCTYPE_CLAIMS_SUPPORTED : "CLAIMS_SUPPORTED",
1917 security.KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED : "RESOURCE_SID_COMPRESSION_DISABLED",
1919 return self.generic_bitmap_to_string(enctypes, v)
1921 def entry_tln_status(self, e_flags, ):
1923 return "Status[Enabled]"
1926 lsa.LSA_TLN_DISABLED_NEW : "Disabled-New",
1927 lsa.LSA_TLN_DISABLED_ADMIN : "Disabled",
1928 lsa.LSA_TLN_DISABLED_CONFLICT : "Disabled-Conflicting",
1930 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
1932 def entry_dom_status(self, e_flags):
1934 return "Status[Enabled]"
1937 lsa.LSA_SID_DISABLED_ADMIN : "Disabled-SID",
1938 lsa.LSA_SID_DISABLED_CONFLICT : "Disabled-SID-Conflicting",
1939 lsa.LSA_NB_DISABLED_ADMIN : "Disabled-NB",
1940 lsa.LSA_NB_DISABLED_CONFLICT : "Disabled-NB-Conflicting",
1942 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
1944 def write_forest_trust_info(self, fti, tln=None, collisions=None):
1946 tln_string = " TDO[%s]" % tln
1950 self.outf.write("Namespaces[%d]%s:\n" % (
1951 len(fti.entries), tln_string))
1953 for i in xrange(0, len(fti.entries)):
1957 collision_string = ""
1959 if collisions is not None:
1960 for c in collisions.entries:
1964 collision_string = " Collision[%s]" % (c.name.string)
1966 d = e.forest_trust_data
1967 if e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
1968 self.outf.write("TLN: %-32s DNS[*.%s]%s\n" % (
1969 self.entry_tln_status(flags),
1970 d.string, collision_string))
1971 elif e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
1972 self.outf.write("TLN_EX: %-29s DNS[*.%s]\n" % (
1974 elif e.type == lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
1975 self.outf.write("DOM: %-32s DNS[%s] Netbios[%s] SID[%s]%s\n" % (
1976 self.entry_dom_status(flags),
1977 d.dns_domain_name.string,
1978 d.netbios_domain_name.string,
1979 d.domain_sid, collision_string))
1982 class cmd_domain_trust_list(DomainTrustCommand):
1983 """List domain trusts."""
1985 synopsis = "%prog [options]"
1987 takes_optiongroups = {
1988 "sambaopts": options.SambaOptions,
1989 "versionopts": options.VersionOptions,
1990 "localdcopts": LocalDCCredentialsOptions,
1996 def run(self, sambaopts=None, versionopts=None, localdcopts=None):
1998 local_server = self.setup_local_server(sambaopts, localdcopts)
2000 local_netlogon = self.new_local_netlogon_connection()
2001 except RuntimeError as error:
2002 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2005 local_netlogon_trusts = local_netlogon.netr_DsrEnumerateDomainTrusts(local_server,
2006 netlogon.NETR_TRUST_FLAG_IN_FOREST |
2007 netlogon.NETR_TRUST_FLAG_OUTBOUND |
2008 netlogon.NETR_TRUST_FLAG_INBOUND)
2009 except RuntimeError as error:
2010 if self.check_runtime_error(error, werror.WERR_RPC_S_PROCNUM_OUT_OF_RANGE):
2011 # TODO: we could implement a fallback to lsa.EnumTrustDom()
2012 raise CommandError("LOCAL_DC[%s]: netr_DsrEnumerateDomainTrusts not supported." % (
2014 raise self.LocalRuntimeError(self, error, "netr_DsrEnumerateDomainTrusts failed")
2016 a = local_netlogon_trusts.array
2018 if t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
2020 self.outf.write("%-14s %-15s %-19s %s\n" % (
2021 "Type[%s]" % self.netr_DomainTrust_to_type(a, t),
2022 "Transitive[%s]" % self.netr_DomainTrust_to_transitive(t),
2023 "Direction[%s]" % self.netr_DomainTrust_to_direction(t),
2024 "Name[%s]" % self.netr_DomainTrust_to_name(t)))
2027 class cmd_domain_trust_show(DomainTrustCommand):
2028 """Show trusted domain details."""
2030 synopsis = "%prog NAME [options]"
2032 takes_optiongroups = {
2033 "sambaopts": options.SambaOptions,
2034 "versionopts": options.VersionOptions,
2035 "localdcopts": LocalDCCredentialsOptions,
2041 takes_args = ["domain"]
2043 def run(self, domain, sambaopts=None, versionopts=None, localdcopts=None):
2045 local_server = self.setup_local_server(sambaopts, localdcopts)
2047 local_lsa = self.new_local_lsa_connection()
2048 except RuntimeError as error:
2049 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2052 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2053 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2054 except RuntimeError as error:
2055 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2057 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2058 local_lsa_info.name.string,
2059 local_lsa_info.dns_domain.string,
2060 local_lsa_info.sid))
2062 lsaString = lsa.String()
2063 lsaString.string = domain
2065 local_tdo_full = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2066 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2067 local_tdo_info = local_tdo_full.info_ex
2068 local_tdo_posix = local_tdo_full.posix_offset
2069 except NTSTATUSError as error:
2070 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2071 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2073 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(FULL_INFO) failed")
2076 local_tdo_enctypes = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2077 lsaString, lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES)
2078 except NTSTATUSError as error:
2079 if self.check_runtime_error(error, ntstatus.NT_STATUS_INVALID_PARAMETER):
2081 if self.check_runtime_error(error, ntstatus.NT_STATUS_INVALID_INFO_CLASS):
2084 if error is not None:
2085 raise self.LocalRuntimeError(self, error,
2086 "QueryTrustedDomainInfoByName(SUPPORTED_ENCRYPTION_TYPES) failed")
2088 local_tdo_enctypes = lsa.TrustDomainInfoSupportedEncTypes()
2089 local_tdo_enctypes.enc_types = 0
2092 local_tdo_forest = None
2093 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2094 local_tdo_forest = local_lsa.lsaRQueryForestTrustInformation(local_policy,
2095 lsaString, lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
2096 except RuntimeError as error:
2097 if self.check_runtime_error(error, ntstatus.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
2099 if self.check_runtime_error(error, ntstatus.NT_STATUS_NOT_FOUND):
2101 if error is not None:
2102 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation failed")
2104 local_tdo_forest = lsa.ForestTrustInformation()
2105 local_tdo_forest.count = 0
2106 local_tdo_forest.entries = []
2108 self.outf.write("TrusteDomain:\n\n");
2109 self.outf.write("NetbiosName: %s\n" % local_tdo_info.netbios_name.string)
2110 if local_tdo_info.netbios_name.string != local_tdo_info.domain_name.string:
2111 self.outf.write("DnsName: %s\n" % local_tdo_info.domain_name.string)
2112 self.outf.write("SID: %s\n" % local_tdo_info.sid)
2113 self.outf.write("Type: %s\n" % self.trustType_string(local_tdo_info.trust_type))
2114 self.outf.write("Direction: %s\n" % self.trustDirection_string(local_tdo_info.trust_direction))
2115 self.outf.write("Attributes: %s\n" % self.trustAttributes_string(local_tdo_info.trust_attributes))
2116 posix_offset_u32 = ctypes.c_uint32(local_tdo_posix.posix_offset).value
2117 posix_offset_i32 = ctypes.c_int32(local_tdo_posix.posix_offset).value
2118 self.outf.write("PosixOffset: 0x%08X (%d)\n" % (posix_offset_u32, posix_offset_i32))
2119 self.outf.write("kerb_EncTypes: %s\n" % self.kerb_EncTypes_string(local_tdo_enctypes.enc_types))
2121 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2122 self.write_forest_trust_info(local_tdo_forest,
2123 tln=local_tdo_info.domain_name.string)
2127 class cmd_domain_trust_create(DomainTrustCommand):
2128 """Create a domain or forest trust."""
2130 synopsis = "%prog DOMAIN [options]"
2132 takes_optiongroups = {
2133 "sambaopts": options.SambaOptions,
2134 "versionopts": options.VersionOptions,
2135 "credopts": options.CredentialsOptions,
2136 "localdcopts": LocalDCCredentialsOptions,
2140 Option("--type", type="choice", metavar="TYPE",
2141 choices=["external", "forest"],
2142 help="The type of the trust: 'external' or 'forest'.",
2144 default="external"),
2145 Option("--direction", type="choice", metavar="DIRECTION",
2146 choices=["incoming", "outgoing", "both"],
2147 help="The trust direction: 'incoming', 'outgoing' or 'both'.",
2148 dest='trust_direction',
2150 Option("--create-location", type="choice", metavar="LOCATION",
2151 choices=["local", "both"],
2152 help="Where to create the trusted domain object: 'local' or 'both'.",
2153 dest='create_location',
2155 Option("--cross-organisation", action="store_true",
2156 help="The related domains does not belong to the same organisation.",
2157 dest='cross_organisation',
2159 Option("--quarantined", type="choice", metavar="yes|no",
2160 choices=["yes", "no", None],
2161 help="Special SID filtering rules are applied to the trust. "
2162 "With --type=external the default is yes. "
2163 "With --type=forest the default is no.",
2164 dest='quarantined_arg',
2166 Option("--not-transitive", action="store_true",
2167 help="The forest trust is not transitive.",
2168 dest='not_transitive',
2170 Option("--treat-as-external", action="store_true",
2171 help="The treat the forest trust as external.",
2172 dest='treat_as_external',
2174 Option("--no-aes-keys", action="store_false",
2175 help="The trust uses aes kerberos keys.",
2176 dest='use_aes_keys',
2178 Option("--skip-validation", action="store_false",
2179 help="Skip validation of the trust.",
2184 takes_args = ["domain"]
2186 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2187 trust_type=None, trust_direction=None, create_location=None,
2188 cross_organisation=False, quarantined_arg=None,
2189 not_transitive=False, treat_as_external=False,
2190 use_aes_keys=False, validate=True):
2192 lsaString = lsa.String()
2195 if quarantined_arg is None:
2196 if trust_type == 'external':
2198 elif quarantined_arg == 'yes':
2201 if trust_type != 'forest':
2203 raise CommandError("--not-transitive requires --type=forest")
2204 if treat_as_external:
2205 raise CommandError("--treat-as-external requires --type=forest")
2209 enc_types = lsa.TrustDomainInfoSupportedEncTypes()
2210 enc_types.enc_types = security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96
2211 enc_types.enc_types |= security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96
2213 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2214 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2215 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2217 local_trust_info = lsa.TrustDomainInfoInfoEx()
2218 local_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2219 local_trust_info.trust_direction = 0
2220 if trust_direction == "both":
2221 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2222 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2223 elif trust_direction == "incoming":
2224 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2225 elif trust_direction == "outgoing":
2226 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2227 local_trust_info.trust_attributes = 0
2228 if cross_organisation:
2229 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2231 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2232 if trust_type == "forest":
2233 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2235 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2236 if treat_as_external:
2237 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2239 def get_password(name):
2242 if password is not None and password is not '':
2244 password = getpass("New %s Password: " % name)
2245 passwordverify = getpass("Retype %s Password: " % name)
2246 if not password == passwordverify:
2248 self.outf.write("Sorry, passwords do not match.\n")
2250 incoming_secret = None
2251 outgoing_secret = None
2252 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2253 if create_location == "local":
2254 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2255 incoming_password = get_password("Incoming Trust")
2256 incoming_secret = string_to_byte_array(incoming_password.encode('utf-16-le'))
2257 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2258 outgoing_password = get_password("Outgoing Trust")
2259 outgoing_secret = string_to_byte_array(outgoing_password.encode('utf-16-le'))
2261 remote_trust_info = None
2263 # We use 240 random bytes.
2264 # Windows uses 28 or 240 random bytes. I guess it's
2265 # based on the trust type external vs. forest.
2267 # The initial trust password can be up to 512 bytes
2268 # while the versioned passwords used for periodic updates
2269 # can only be up to 498 bytes, as netr_ServerPasswordSet2()
2270 # needs to pass the NL_PASSWORD_VERSION structure within the
2271 # 512 bytes and a 2 bytes confounder is required.
2273 def random_trust_secret(length):
2274 pw = samba.generate_random_machine_password(length/2, length/2)
2275 return string_to_byte_array(pw.encode('utf-16-le'))
2277 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2278 incoming_secret = random_trust_secret(240)
2279 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2280 outgoing_secret = random_trust_secret(240)
2282 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2283 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2285 remote_trust_info = lsa.TrustDomainInfoInfoEx()
2286 remote_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2287 remote_trust_info.trust_direction = 0
2288 if trust_direction == "both":
2289 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2290 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2291 elif trust_direction == "incoming":
2292 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2293 elif trust_direction == "outgoing":
2294 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2295 remote_trust_info.trust_attributes = 0
2296 if cross_organisation:
2297 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2299 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2300 if trust_type == "forest":
2301 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2303 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2304 if treat_as_external:
2305 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2307 local_server = self.setup_local_server(sambaopts, localdcopts)
2309 local_lsa = self.new_local_lsa_connection()
2310 except RuntimeError as error:
2311 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2314 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2315 except RuntimeError as error:
2316 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2318 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2319 local_lsa_info.name.string,
2320 local_lsa_info.dns_domain.string,
2321 local_lsa_info.sid))
2324 remote_server = self.setup_remote_server(credopts, domain)
2325 except RuntimeError as error:
2326 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2329 remote_lsa = self.new_remote_lsa_connection()
2330 except RuntimeError as error:
2331 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2334 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2335 except RuntimeError as error:
2336 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2338 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2339 remote_lsa_info.name.string,
2340 remote_lsa_info.dns_domain.string,
2341 remote_lsa_info.sid))
2343 local_trust_info.domain_name.string = remote_lsa_info.dns_domain.string
2344 local_trust_info.netbios_name.string = remote_lsa_info.name.string
2345 local_trust_info.sid = remote_lsa_info.sid
2347 if remote_trust_info:
2348 remote_trust_info.domain_name.string = local_lsa_info.dns_domain.string
2349 remote_trust_info.netbios_name.string = local_lsa_info.name.string
2350 remote_trust_info.sid = local_lsa_info.sid
2353 lsaString.string = local_trust_info.domain_name.string
2354 local_old_netbios = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2355 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2356 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2357 except NTSTATUSError as error:
2358 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2359 raise self.LocalRuntimeError(self, error,
2360 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2364 lsaString.string = local_trust_info.netbios_name.string
2365 local_old_dns = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2366 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2367 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2368 except NTSTATUSError as error:
2369 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2370 raise self.LocalRuntimeError(self, error,
2371 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2374 if remote_trust_info:
2376 lsaString.string = remote_trust_info.domain_name.string
2377 remote_old_netbios = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2378 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2379 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2380 except NTSTATUSError as error:
2381 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2382 raise self.RemoteRuntimeError(self, error,
2383 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2387 lsaString.string = remote_trust_info.netbios_name.string
2388 remote_old_dns = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2389 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2390 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2391 except NTSTATUSError as error:
2392 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2393 raise self.RemoteRuntimeError(self, error,
2394 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2398 local_netlogon = self.new_local_netlogon_connection()
2399 except RuntimeError as error:
2400 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2403 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
2404 except RuntimeError as error:
2405 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
2407 if remote_trust_info:
2409 remote_netlogon = self.new_remote_netlogon_connection()
2410 except RuntimeError as error:
2411 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2414 remote_netlogon_info = self.get_netlogon_dc_info(remote_netlogon, remote_server)
2415 except RuntimeError as error:
2416 raise self.RemoteRuntimeError(self, error, "failed to get netlogon dc info")
2418 def generate_AuthInOutBlob(secret, update_time):
2420 blob = drsblobs.trustAuthInOutBlob()
2425 clear = drsblobs.AuthInfoClear()
2426 clear.size = len(secret)
2427 clear.password = secret
2429 info = drsblobs.AuthenticationInformation()
2430 info.LastUpdateTime = samba.unix2nttime(update_time)
2431 info.AuthType = lsa.TRUST_AUTH_TYPE_CLEAR
2432 info.AuthInfo = clear
2434 array = drsblobs.AuthenticationInformationArray()
2436 array.array = [info]
2438 blob = drsblobs.trustAuthInOutBlob()
2440 blob.current = array
2444 def generate_AuthInfoInternal(session_key, incoming=None, outgoing=None):
2445 confounder = [0] * 512
2446 for i in range(len(confounder)):
2447 confounder[i] = random.randint(0, 255)
2449 trustpass = drsblobs.trustDomainPasswords()
2451 trustpass.confounder = confounder
2452 trustpass.outgoing = outgoing
2453 trustpass.incoming = incoming
2455 trustpass_blob = ndr_pack(trustpass)
2457 encrypted_trustpass = arcfour_encrypt(session_key, trustpass_blob)
2459 auth_blob = lsa.DATA_BUF2()
2460 auth_blob.size = len(encrypted_trustpass)
2461 auth_blob.data = string_to_byte_array(encrypted_trustpass)
2463 auth_info = lsa.TrustDomainInfoAuthInfoInternal()
2464 auth_info.auth_blob = auth_blob
2468 update_time = samba.current_unix_time()
2469 incoming_blob = generate_AuthInOutBlob(incoming_secret, update_time)
2470 outgoing_blob = generate_AuthInOutBlob(outgoing_secret, update_time)
2472 local_tdo_handle = None
2473 remote_tdo_handle = None
2475 local_auth_info = generate_AuthInfoInternal(local_lsa.session_key,
2476 incoming=incoming_blob,
2477 outgoing=outgoing_blob)
2478 if remote_trust_info:
2479 remote_auth_info = generate_AuthInfoInternal(remote_lsa.session_key,
2480 incoming=outgoing_blob,
2481 outgoing=incoming_blob)
2484 if remote_trust_info:
2485 self.outf.write("Creating remote TDO.\n")
2486 current_request = { "location": "remote", "name": "CreateTrustedDomainEx2"}
2487 remote_tdo_handle = remote_lsa.CreateTrustedDomainEx2(remote_policy,
2490 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2491 self.outf.write("Remote TDO created.\n")
2493 self.outf.write("Setting supported encryption types on remote TDO.\n")
2494 current_request = { "location": "remote", "name": "SetInformationTrustedDomain"}
2495 remote_lsa.SetInformationTrustedDomain(remote_tdo_handle,
2496 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2499 self.outf.write("Creating local TDO.\n")
2500 current_request = { "location": "local", "name": "CreateTrustedDomainEx2"}
2501 local_tdo_handle = local_lsa.CreateTrustedDomainEx2(local_policy,
2504 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2505 self.outf.write("Local TDO created\n")
2507 self.outf.write("Setting supported encryption types on local TDO.\n")
2508 current_request = { "location": "local", "name": "SetInformationTrustedDomain"}
2509 local_lsa.SetInformationTrustedDomain(local_tdo_handle,
2510 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2512 except RuntimeError as error:
2513 self.outf.write("Error: %s failed %sly - cleaning up\n" % (
2514 current_request['name'], current_request['location']))
2515 if remote_tdo_handle:
2516 self.outf.write("Deleting remote TDO.\n")
2517 remote_lsa.DeleteObject(remote_tdo_handle)
2518 remote_tdo_handle = None
2519 if local_tdo_handle:
2520 self.outf.write("Deleting local TDO.\n")
2521 local_lsa.DeleteObject(local_tdo_handle)
2522 local_tdo_handle = None
2523 if current_request['location'] is "remote":
2524 raise self.RemoteRuntimeError(self, error, "%s" % (
2525 current_request['name']))
2526 raise self.LocalRuntimeError(self, error, "%s" % (
2527 current_request['name']))
2530 if local_trust_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2531 self.outf.write("Setup local forest trust information...\n")
2533 # get all information about the remote trust
2534 # this triggers netr_GetForestTrustInformation to the remote domain
2535 # and lsaRSetForestTrustInformation() locally, but new top level
2536 # names are disabled by default.
2537 local_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
2538 remote_lsa_info.dns_domain.string,
2539 netlogon.DS_GFTI_UPDATE_TDO)
2540 except RuntimeError as error:
2541 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2544 # here we try to enable all top level names
2545 local_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
2546 remote_lsa_info.dns_domain,
2547 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2550 except RuntimeError as error:
2551 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2553 self.write_forest_trust_info(local_forest_info,
2554 tln=remote_lsa_info.dns_domain.string,
2555 collisions=local_forest_collision)
2557 if remote_trust_info:
2558 self.outf.write("Setup remote forest trust information...\n")
2560 # get all information about the local trust (from the perspective of the remote domain)
2561 # this triggers netr_GetForestTrustInformation to our domain.
2562 # and lsaRSetForestTrustInformation() remotely, but new top level
2563 # names are disabled by default.
2564 remote_forest_info = remote_netlogon.netr_DsRGetForestTrustInformation(remote_netlogon_info.dc_unc,
2565 local_lsa_info.dns_domain.string,
2566 netlogon.DS_GFTI_UPDATE_TDO)
2567 except RuntimeError as error:
2568 raise self.RemoteRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2571 # here we try to enable all top level names
2572 remote_forest_collision = remote_lsa.lsaRSetForestTrustInformation(remote_policy,
2573 local_lsa_info.dns_domain,
2574 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2577 except RuntimeError as error:
2578 raise self.RemoteRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2580 self.write_forest_trust_info(remote_forest_info,
2581 tln=local_lsa_info.dns_domain.string,
2582 collisions=remote_forest_collision)
2584 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2585 self.outf.write("Validating outgoing trust...\n")
2587 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_netlogon_info.dc_unc,
2588 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2590 remote_lsa_info.dns_domain.string)
2591 except RuntimeError as error:
2592 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2594 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2595 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2597 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2598 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2599 local_trust_verify.trusted_dc_name,
2600 local_trust_verify.tc_connection_status[1],
2601 local_trust_verify.pdc_connection_status[1])
2603 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2604 local_trust_verify.trusted_dc_name,
2605 local_trust_verify.tc_connection_status[1],
2606 local_trust_verify.pdc_connection_status[1])
2608 if local_trust_status != werror.WERR_SUCCESS or local_conn_status != werror.WERR_SUCCESS:
2609 raise CommandError(local_validation)
2611 self.outf.write("OK: %s\n" % local_validation)
2613 if remote_trust_info:
2614 if remote_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2615 self.outf.write("Validating incoming trust...\n")
2617 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_netlogon_info.dc_unc,
2618 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2620 local_lsa_info.dns_domain.string)
2621 except RuntimeError as error:
2622 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2624 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2625 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2627 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2628 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2629 remote_trust_verify.trusted_dc_name,
2630 remote_trust_verify.tc_connection_status[1],
2631 remote_trust_verify.pdc_connection_status[1])
2633 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2634 remote_trust_verify.trusted_dc_name,
2635 remote_trust_verify.tc_connection_status[1],
2636 remote_trust_verify.pdc_connection_status[1])
2638 if remote_trust_status != werror.WERR_SUCCESS or remote_conn_status != werror.WERR_SUCCESS:
2639 raise CommandError(remote_validation)
2641 self.outf.write("OK: %s\n" % remote_validation)
2643 if remote_tdo_handle is not None:
2645 remote_lsa.Close(remote_tdo_handle)
2646 except RuntimeError as error:
2648 remote_tdo_handle = None
2649 if local_tdo_handle is not None:
2651 local_lsa.Close(local_tdo_handle)
2652 except RuntimeError as error:
2654 local_tdo_handle = None
2656 self.outf.write("Success.\n")
2659 class cmd_domain_trust_delete(DomainTrustCommand):
2660 """Delete a domain trust."""
2662 synopsis = "%prog DOMAIN [options]"
2664 takes_optiongroups = {
2665 "sambaopts": options.SambaOptions,
2666 "versionopts": options.VersionOptions,
2667 "credopts": options.CredentialsOptions,
2668 "localdcopts": LocalDCCredentialsOptions,
2672 Option("--delete-location", type="choice", metavar="LOCATION",
2673 choices=["local", "both"],
2674 help="Where to delete the trusted domain object: 'local' or 'both'.",
2675 dest='delete_location',
2679 takes_args = ["domain"]
2681 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2682 delete_location=None):
2684 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2685 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2686 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2688 if delete_location == "local":
2689 remote_policy_access = None
2691 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2692 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2693 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2695 local_server = self.setup_local_server(sambaopts, localdcopts)
2697 local_lsa = self.new_local_lsa_connection()
2698 except RuntimeError as error:
2699 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2702 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2703 except RuntimeError as error:
2704 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2706 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2707 local_lsa_info.name.string,
2708 local_lsa_info.dns_domain.string,
2709 local_lsa_info.sid))
2711 local_tdo_info = None
2712 local_tdo_handle = None
2713 remote_tdo_info = None
2714 remote_tdo_handle = None
2716 lsaString = lsa.String()
2718 lsaString.string = domain
2719 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2720 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2721 except NTSTATUSError as error:
2722 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2723 raise CommandError("Failed to find trust for domain '%s'" % domain)
2724 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2727 if remote_policy_access is not None:
2729 remote_server = self.setup_remote_server(credopts, domain)
2730 except RuntimeError as error:
2731 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2734 remote_lsa = self.new_remote_lsa_connection()
2735 except RuntimeError as error:
2736 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2739 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2740 except RuntimeError as error:
2741 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2743 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2744 remote_lsa_info.name.string,
2745 remote_lsa_info.dns_domain.string,
2746 remote_lsa_info.sid))
2748 if remote_lsa_info.sid != local_tdo_info.sid or \
2749 remote_lsa_info.name.string != local_tdo_info.netbios_name.string or \
2750 remote_lsa_info.dns_domain.string != local_tdo_info.domain_name.string:
2751 raise CommandError("LocalTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2752 local_tdo_info.netbios_name.string,
2753 local_tdo_info.domain_name.string,
2754 local_tdo_info.sid))
2757 lsaString.string = local_lsa_info.dns_domain.string
2758 remote_tdo_info = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2759 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2760 except NTSTATUSError as error:
2761 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2762 raise self.RemoteRuntimeError(self, error, "QueryTrustedDomainInfoByName(%s)" % (
2766 if remote_tdo_info is not None:
2767 if local_lsa_info.sid != remote_tdo_info.sid or \
2768 local_lsa_info.name.string != remote_tdo_info.netbios_name.string or \
2769 local_lsa_info.dns_domain.string != remote_tdo_info.domain_name.string:
2770 raise CommandError("RemoteTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2771 remote_tdo_info.netbios_name.string,
2772 remote_tdo_info.domain_name.string,
2773 remote_tdo_info.sid))
2775 if local_tdo_info is not None:
2777 lsaString.string = local_tdo_info.domain_name.string
2778 local_tdo_handle = local_lsa.OpenTrustedDomainByName(local_policy,
2780 security.SEC_STD_DELETE)
2781 except RuntimeError as error:
2782 raise self.LocalRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2785 local_lsa.DeleteObject(local_tdo_handle)
2786 local_tdo_handle = None
2788 if remote_tdo_info is not None:
2790 lsaString.string = remote_tdo_info.domain_name.string
2791 remote_tdo_handle = remote_lsa.OpenTrustedDomainByName(remote_policy,
2793 security.SEC_STD_DELETE)
2794 except RuntimeError as error:
2795 raise self.RemoteRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2798 if remote_tdo_handle is not None:
2800 remote_lsa.DeleteObject(remote_tdo_handle)
2801 remote_tdo_handle = None
2802 self.outf.write("RemoteTDO deleted.\n")
2803 except RuntimeError as error:
2804 self.outf.write("%s\n" % self.RemoteRuntimeError(self, error, "DeleteObject() failed"))
2806 if local_tdo_handle is not None:
2808 local_lsa.DeleteObject(local_tdo_handle)
2809 local_tdo_handle = None
2810 self.outf.write("LocalTDO deleted.\n")
2811 except RuntimeError as error:
2812 self.outf.write("%s\n" % self.LocalRuntimeError(self, error, "DeleteObject() failed"))
2816 class cmd_domain_trust_validate(DomainTrustCommand):
2817 """Validate a domain trust."""
2819 synopsis = "%prog DOMAIN [options]"
2821 takes_optiongroups = {
2822 "sambaopts": options.SambaOptions,
2823 "versionopts": options.VersionOptions,
2824 "credopts": options.CredentialsOptions,
2825 "localdcopts": LocalDCCredentialsOptions,
2829 Option("--validate-location", type="choice", metavar="LOCATION",
2830 choices=["local", "both"],
2831 help="Where to validate the trusted domain object: 'local' or 'both'.",
2832 dest='validate_location',
2836 takes_args = ["domain"]
2838 def run(self, domain, sambaopts=None, versionopts=None, credopts=None, localdcopts=None,
2839 validate_location=None):
2841 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2843 local_server = self.setup_local_server(sambaopts, localdcopts)
2845 local_lsa = self.new_local_lsa_connection()
2846 except RuntimeError as error:
2847 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2850 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2851 except RuntimeError as error:
2852 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2854 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2855 local_lsa_info.name.string,
2856 local_lsa_info.dns_domain.string,
2857 local_lsa_info.sid))
2860 lsaString = lsa.String()
2861 lsaString.string = domain
2862 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2863 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2864 except NTSTATUSError as error:
2865 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2866 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2868 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
2870 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
2871 local_tdo_info.netbios_name.string,
2872 local_tdo_info.domain_name.string,
2873 local_tdo_info.sid))
2876 local_netlogon = self.new_local_netlogon_connection()
2877 except RuntimeError as error:
2878 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2881 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_server,
2882 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2884 local_tdo_info.domain_name.string)
2885 except RuntimeError as error:
2886 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2888 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2889 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2891 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2892 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2893 local_trust_verify.trusted_dc_name,
2894 local_trust_verify.tc_connection_status[1],
2895 local_trust_verify.pdc_connection_status[1])
2897 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2898 local_trust_verify.trusted_dc_name,
2899 local_trust_verify.tc_connection_status[1],
2900 local_trust_verify.pdc_connection_status[1])
2902 if local_trust_status != werror.WERR_SUCCESS or local_conn_status != werror.WERR_SUCCESS:
2903 raise CommandError(local_validation)
2905 self.outf.write("OK: %s\n" % local_validation)
2908 server = local_trust_verify.trusted_dc_name.replace('\\', '')
2909 domain_and_server = "%s\\%s" % (local_tdo_info.domain_name.string, server)
2910 local_trust_rediscover = local_netlogon.netr_LogonControl2Ex(local_server,
2911 netlogon.NETLOGON_CONTROL_REDISCOVER,
2914 except RuntimeError as error:
2915 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
2917 local_conn_status = self._uint32(local_trust_rediscover.tc_connection_status[0])
2918 local_rediscover = "LocalRediscover: DC[%s] CONNECTION[%s]" % (
2919 local_trust_rediscover.trusted_dc_name,
2920 local_trust_rediscover.tc_connection_status[1])
2922 if local_conn_status != werror.WERR_SUCCESS:
2923 raise CommandError(local_rediscover)
2925 self.outf.write("OK: %s\n" % local_rediscover)
2927 if validate_location != "local":
2929 remote_server = self.setup_remote_server(credopts, domain, require_pdc=False)
2930 except RuntimeError as error:
2931 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2934 remote_netlogon = self.new_remote_netlogon_connection()
2935 except RuntimeError as error:
2936 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2939 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_server,
2940 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2942 local_lsa_info.dns_domain.string)
2943 except RuntimeError as error:
2944 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2946 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2947 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2949 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2950 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2951 remote_trust_verify.trusted_dc_name,
2952 remote_trust_verify.tc_connection_status[1],
2953 remote_trust_verify.pdc_connection_status[1])
2955 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2956 remote_trust_verify.trusted_dc_name,
2957 remote_trust_verify.tc_connection_status[1],
2958 remote_trust_verify.pdc_connection_status[1])
2960 if remote_trust_status != werror.WERR_SUCCESS or remote_conn_status != werror.WERR_SUCCESS:
2961 raise CommandError(remote_validation)
2963 self.outf.write("OK: %s\n" % remote_validation)
2966 server = remote_trust_verify.trusted_dc_name.replace('\\', '')
2967 domain_and_server = "%s\\%s" % (local_lsa_info.dns_domain.string, server)
2968 remote_trust_rediscover = remote_netlogon.netr_LogonControl2Ex(remote_server,
2969 netlogon.NETLOGON_CONTROL_REDISCOVER,
2972 except RuntimeError as error:
2973 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
2975 remote_conn_status = self._uint32(remote_trust_rediscover.tc_connection_status[0])
2977 remote_rediscover = "RemoteRediscover: DC[%s] CONNECTION[%s]" % (
2978 remote_trust_rediscover.trusted_dc_name,
2979 remote_trust_rediscover.tc_connection_status[1])
2981 if remote_conn_status != werror.WERR_SUCCESS:
2982 raise CommandError(remote_rediscover)
2984 self.outf.write("OK: %s\n" % remote_rediscover)
2988 class cmd_domain_trust_namespaces(DomainTrustCommand):
2989 """Manage forest trust namespaces."""
2991 synopsis = "%prog [DOMAIN] [options]"
2993 takes_optiongroups = {
2994 "sambaopts": options.SambaOptions,
2995 "versionopts": options.VersionOptions,
2996 "localdcopts": LocalDCCredentialsOptions,
3000 Option("--refresh", type="choice", metavar="check|store",
3001 choices=["check", "store", None],
3002 help="List and maybe store refreshed forest trust information: 'check' or 'store'.",
3005 Option("--enable-all", action="store_true",
3006 help="Try to update disabled entries, not allowed with --refresh=check.",
3009 Option("--enable-tln", action="append", metavar='DNSDOMAIN',
3010 help="Enable a top level name entry. Can be specified multiple times.",
3013 Option("--disable-tln", action="append", metavar='DNSDOMAIN',
3014 help="Disable a top level name entry. Can be specified multiple times.",
3017 Option("--add-tln-ex", action="append", metavar='DNSDOMAIN',
3018 help="Add a top level exclusion entry. Can be specified multiple times.",
3021 Option("--delete-tln-ex", action="append", metavar='DNSDOMAIN',
3022 help="Delete a top level exclusion entry. Can be specified multiple times.",
3023 dest='delete_tln_ex',
3025 Option("--enable-nb", action="append", metavar='NETBIOSDOMAIN',
3026 help="Enable a netbios name in a domain entry. Can be specified multiple times.",
3029 Option("--disable-nb", action="append", metavar='NETBIOSDOMAIN',
3030 help="Disable a netbios name in a domain entry. Can be specified multiple times.",
3033 Option("--enable-sid", action="append", metavar='DOMAINSID',
3034 help="Enable a SID in a domain entry. Can be specified multiple times.",
3035 dest='enable_sid_str',
3037 Option("--disable-sid", action="append", metavar='DOMAINSID',
3038 help="Disable a SID in a domain entry. Can be specified multiple times.",
3039 dest='disable_sid_str',
3041 Option("--add-upn-suffix", action="append", metavar='DNSDOMAIN',
3042 help="Add a new uPNSuffixes attribute for the local forest. Can be specified multiple times.",
3045 Option("--delete-upn-suffix", action="append", metavar='DNSDOMAIN',
3046 help="Delete an existing uPNSuffixes attribute of the local forest. Can be specified multiple times.",
3049 Option("--add-spn-suffix", action="append", metavar='DNSDOMAIN',
3050 help="Add a new msDS-SPNSuffixes attribute for the local forest. Can be specified multiple times.",
3053 Option("--delete-spn-suffix", action="append", metavar='DNSDOMAIN',
3054 help="Delete an existing msDS-SPNSuffixes attribute of the local forest. Can be specified multiple times.",
3059 takes_args = ["domain?"]
3061 def run(self, domain=None, sambaopts=None, localdcopts=None, versionopts=None,
3062 refresh=None, enable_all=False,
3063 enable_tln=[], disable_tln=[], add_tln_ex=[], delete_tln_ex=[],
3064 enable_sid_str=[], disable_sid_str=[], enable_nb=[], disable_nb=[],
3065 add_upn=[], delete_upn=[], add_spn=[], delete_spn=[]):
3067 require_update = False
3070 if refresh == "store":
3071 raise CommandError("--refresh=%s not allowed without DOMAIN" % refresh)
3074 raise CommandError("--enable-all not allowed without DOMAIN")
3076 if len(enable_tln) > 0:
3077 raise CommandError("--enable-tln not allowed without DOMAIN")
3078 if len(disable_tln) > 0:
3079 raise CommandError("--disable-tln not allowed without DOMAIN")
3081 if len(add_tln_ex) > 0:
3082 raise CommandError("--add-tln-ex not allowed without DOMAIN")
3083 if len(delete_tln_ex) > 0:
3084 raise CommandError("--delete-tln-ex not allowed without DOMAIN")
3086 if len(enable_nb) > 0:
3087 raise CommandError("--enable-nb not allowed without DOMAIN")
3088 if len(disable_nb) > 0:
3089 raise CommandError("--disable-nb not allowed without DOMAIN")
3091 if len(enable_sid_str) > 0:
3092 raise CommandError("--enable-sid not allowed without DOMAIN")
3093 if len(disable_sid_str) > 0:
3094 raise CommandError("--disable-sid not allowed without DOMAIN")
3096 if len(add_upn) > 0:
3098 if not n.startswith("*."):
3100 raise CommandError("value[%s] specified for --add-upn-suffix should not include with '*.'" % n)
3101 require_update = True
3102 if len(delete_upn) > 0:
3103 for n in delete_upn:
3104 if not n.startswith("*."):
3106 raise CommandError("value[%s] specified for --delete-upn-suffix should not include with '*.'" % n)
3107 require_update = True
3109 for d in delete_upn:
3110 if a.lower() != d.lower():
3112 raise CommandError("value[%s] specified for --add-upn-suffix and --delete-upn-suffix" % a)
3114 if len(add_spn) > 0:
3116 if not n.startswith("*."):
3118 raise CommandError("value[%s] specified for --add-spn-suffix should not include with '*.'" % n)
3119 require_update = True
3120 if len(delete_spn) > 0:
3121 for n in delete_spn:
3122 if not n.startswith("*."):
3124 raise CommandError("value[%s] specified for --delete-spn-suffix should not include with '*.'" % n)
3125 require_update = True
3127 for d in delete_spn:
3128 if a.lower() != d.lower():
3130 raise CommandError("value[%s] specified for --add-spn-suffix and --delete-spn-suffix" % a)
3132 if len(add_upn) > 0:
3133 raise CommandError("--add-upn-suffix not allowed together with DOMAIN")
3134 if len(delete_upn) > 0:
3135 raise CommandError("--delete-upn-suffix not allowed together with DOMAIN")
3136 if len(add_spn) > 0:
3137 raise CommandError("--add-spn-suffix not allowed together with DOMAIN")
3138 if len(delete_spn) > 0:
3139 raise CommandError("--delete-spn-suffix not allowed together with DOMAIN")
3141 if refresh is not None:
3142 if refresh == "store":
3143 require_update = True
3145 if enable_all and refresh != "store":
3146 raise CommandError("--enable-all not allowed together with --refresh=%s" % refresh)
3148 if len(enable_tln) > 0:
3149 raise CommandError("--enable-tln not allowed together with --refresh")
3150 if len(disable_tln) > 0:
3151 raise CommandError("--disable-tln not allowed together with --refresh")
3153 if len(add_tln_ex) > 0:
3154 raise CommandError("--add-tln-ex not allowed together with --refresh")
3155 if len(delete_tln_ex) > 0:
3156 raise CommandError("--delete-tln-ex not allowed together with --refresh")
3158 if len(enable_nb) > 0:
3159 raise CommandError("--enable-nb not allowed together with --refresh")
3160 if len(disable_nb) > 0:
3161 raise CommandError("--disable-nb not allowed together with --refresh")
3163 if len(enable_sid_str) > 0:
3164 raise CommandError("--enable-sid not allowed together with --refresh")
3165 if len(disable_sid_str) > 0:
3166 raise CommandError("--disable-sid not allowed together with --refresh")
3169 require_update = True
3171 if len(enable_tln) > 0:
3172 raise CommandError("--enable-tln not allowed together with --enable-all")
3174 if len(enable_nb) > 0:
3175 raise CommandError("--enable-nb not allowed together with --enable-all")
3177 if len(enable_sid_str) > 0:
3178 raise CommandError("--enable-sid not allowed together with --enable-all")
3180 if len(enable_tln) > 0:
3181 require_update = True
3182 if len(disable_tln) > 0:
3183 require_update = True
3184 for e in enable_tln:
3185 for d in disable_tln:
3186 if e.lower() != d.lower():
3188 raise CommandError("value[%s] specified for --enable-tln and --disable-tln" % e)
3190 if len(add_tln_ex) > 0:
3191 for n in add_tln_ex:
3192 if not n.startswith("*."):
3194 raise CommandError("value[%s] specified for --add-tln-ex should not include with '*.'" % n)
3195 require_update = True
3196 if len(delete_tln_ex) > 0:
3197 for n in delete_tln_ex:
3198 if not n.startswith("*."):
3200 raise CommandError("value[%s] specified for --delete-tln-ex should not include with '*.'" % n)
3201 require_update = True
3202 for a in add_tln_ex:
3203 for d in delete_tln_ex:
3204 if a.lower() != d.lower():
3206 raise CommandError("value[%s] specified for --add-tln-ex and --delete-tln-ex" % a)
3208 if len(enable_nb) > 0:
3209 require_update = True
3210 if len(disable_nb) > 0:
3211 require_update = True
3213 for d in disable_nb:
3214 if e.upper() != d.upper():
3216 raise CommandError("value[%s] specified for --enable-nb and --disable-nb" % e)
3219 for s in enable_sid_str:
3221 sid = security.dom_sid(s)
3222 except TypeError as error:
3223 raise CommandError("value[%s] specified for --enable-sid is not a valid SID" % s)
3224 enable_sid.append(sid)
3226 for s in disable_sid_str:
3228 sid = security.dom_sid(s)
3229 except TypeError as error:
3230 raise CommandError("value[%s] specified for --disable-sid is not a valid SID" % s)
3231 disable_sid.append(sid)
3232 if len(enable_sid) > 0:
3233 require_update = True
3234 if len(disable_sid) > 0:
3235 require_update = True
3236 for e in enable_sid:
3237 for d in disable_sid:
3240 raise CommandError("value[%s] specified for --enable-sid and --disable-sid" % e)
3242 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
3244 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
3246 local_server = self.setup_local_server(sambaopts, localdcopts)
3248 local_lsa = self.new_local_lsa_connection()
3249 except RuntimeError as error:
3250 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
3253 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
3254 except RuntimeError as error:
3255 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
3257 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
3258 local_lsa_info.name.string,
3259 local_lsa_info.dns_domain.string,
3260 local_lsa_info.sid))
3264 local_netlogon = self.new_local_netlogon_connection()
3265 except RuntimeError as error:
3266 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3269 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3270 except RuntimeError as error:
3271 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3273 if local_netlogon_info.domain_name != local_netlogon_info.forest_name:
3274 raise CommandError("The local domain [%s] is not the forest root [%s]" % (
3275 local_netlogon_info.domain_name,
3276 local_netlogon_info.forest_name))
3279 # get all information about our own forest
3280 own_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3282 except RuntimeError as error:
3283 if self.check_runtime_error(error, werror.WERR_RPC_S_PROCNUM_OUT_OF_RANGE):
3284 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3287 if self.check_runtime_error(error, werror.WERR_INVALID_FUNCTION):
3288 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3291 if self.check_runtime_error(error, werror.WERR_NERR_ACFNOTLOADED):
3292 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3295 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3297 self.outf.write("Own forest trust information...\n")
3298 self.write_forest_trust_info(own_forest_info,
3299 tln=local_lsa_info.dns_domain.string)
3302 local_samdb = self.new_local_ldap_connection()
3303 except RuntimeError as error:
3304 raise self.LocalRuntimeError(self, error, "failed to connect to SamDB")
3306 local_partitions_dn = "CN=Partitions,%s" % str(local_samdb.get_config_basedn())
3307 attrs = ['uPNSuffixes', 'msDS-SPNSuffixes']
3309 msgs = local_samdb.search(base=local_partitions_dn,
3310 scope=ldb.SCOPE_BASE,
3311 expression="(objectClass=crossRefContainer)",
3313 stored_msg = msgs[0]
3314 except ldb.LdbError as error:
3315 raise self.LocalLdbError(self, error, "failed to search partition dn")
3317 stored_upn_vals = []
3318 if 'uPNSuffixes' in stored_msg:
3319 stored_upn_vals.extend(stored_msg['uPNSuffixes'])
3321 stored_spn_vals = []
3322 if 'msDS-SPNSuffixes' in stored_msg:
3323 stored_spn_vals.extend(stored_msg['msDS-SPNSuffixes'])
3325 self.outf.write("Stored uPNSuffixes attributes[%d]:\n" % len(stored_upn_vals))
3326 for v in stored_upn_vals:
3327 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3328 self.outf.write("Stored msDS-SPNSuffixes attributes[%d]:\n" % len(stored_spn_vals))
3329 for v in stored_spn_vals:
3330 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3332 if not require_update:
3336 update_upn_vals = []
3337 update_upn_vals.extend(stored_upn_vals)
3340 update_spn_vals = []
3341 update_spn_vals.extend(stored_spn_vals)
3345 for i in xrange(0, len(update_upn_vals)):
3346 v = update_upn_vals[i]
3347 if v.lower() != upn.lower():
3352 raise CommandError("Entry already present for value[%s] specified for --add-upn-suffix" % upn)
3353 update_upn_vals.append(upn)
3356 for upn in delete_upn:
3358 for i in xrange(0, len(update_upn_vals)):
3359 v = update_upn_vals[i]
3360 if v.lower() != upn.lower():
3365 raise CommandError("Entry not found for value[%s] specified for --delete-upn-suffix" % upn)
3367 update_upn_vals.pop(idx)
3372 for i in xrange(0, len(update_spn_vals)):
3373 v = update_spn_vals[i]
3374 if v.lower() != spn.lower():
3379 raise CommandError("Entry already present for value[%s] specified for --add-spn-suffix" % spn)
3380 update_spn_vals.append(spn)
3383 for spn in delete_spn:
3385 for i in xrange(0, len(update_spn_vals)):
3386 v = update_spn_vals[i]
3387 if v.lower() != spn.lower():
3392 raise CommandError("Entry not found for value[%s] specified for --delete-spn-suffix" % spn)
3394 update_spn_vals.pop(idx)
3397 self.outf.write("Update uPNSuffixes attributes[%d]:\n" % len(update_upn_vals))
3398 for v in update_upn_vals:
3399 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3400 self.outf.write("Update msDS-SPNSuffixes attributes[%d]:\n" % len(update_spn_vals))
3401 for v in update_spn_vals:
3402 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3404 update_msg = ldb.Message()
3405 update_msg.dn = stored_msg.dn
3408 update_msg['uPNSuffixes'] = ldb.MessageElement(update_upn_vals,
3409 ldb.FLAG_MOD_REPLACE,
3412 update_msg['msDS-SPNSuffixes'] = ldb.MessageElement(update_spn_vals,
3413 ldb.FLAG_MOD_REPLACE,
3416 local_samdb.modify(update_msg)
3417 except ldb.LdbError as error:
3418 raise self.LocalLdbError(self, error, "failed to update partition dn")
3421 stored_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3423 except RuntimeError as error:
3424 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3426 self.outf.write("Stored forest trust information...\n")
3427 self.write_forest_trust_info(stored_forest_info,
3428 tln=local_lsa_info.dns_domain.string)
3432 lsaString = lsa.String()
3433 lsaString.string = domain
3434 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
3435 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
3436 except NTSTATUSError as error:
3437 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
3438 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
3440 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
3442 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
3443 local_tdo_info.netbios_name.string,
3444 local_tdo_info.domain_name.string,
3445 local_tdo_info.sid))
3447 if not local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
3448 raise CommandError("trusted domain object for domain [%s] is not marked as FOREST_TRANSITIVE." % domain)
3450 if refresh is not None:
3452 local_netlogon = self.new_local_netlogon_connection()
3453 except RuntimeError as error:
3454 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3457 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3458 except RuntimeError as error:
3459 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3461 lsa_update_check = 1
3462 if refresh == "store":
3463 netlogon_update_tdo = netlogon.DS_GFTI_UPDATE_TDO
3465 lsa_update_check = 0
3467 netlogon_update_tdo = 0
3470 # get all information about the remote trust
3471 # this triggers netr_GetForestTrustInformation to the remote domain
3472 # and lsaRSetForestTrustInformation() locally, but new top level
3473 # names are disabled by default.
3474 fresh_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3475 local_tdo_info.domain_name.string,
3476 netlogon_update_tdo)
3477 except RuntimeError as error:
3478 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3481 fresh_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3482 local_tdo_info.domain_name,
3483 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3486 except RuntimeError as error:
3487 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3489 self.outf.write("Fresh forest trust information...\n")
3490 self.write_forest_trust_info(fresh_forest_info,
3491 tln=local_tdo_info.domain_name.string,
3492 collisions=fresh_forest_collision)
3494 if refresh == "store":
3496 lsaString = lsa.String()
3497 lsaString.string = local_tdo_info.domain_name.string
3498 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3500 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3501 except RuntimeError as error:
3502 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3504 self.outf.write("Stored forest trust information...\n")
3505 self.write_forest_trust_info(stored_forest_info,
3506 tln=local_tdo_info.domain_name.string)
3511 # The none --refresh path
3515 lsaString = lsa.String()
3516 lsaString.string = local_tdo_info.domain_name.string
3517 local_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3519 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3520 except RuntimeError as error:
3521 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3523 self.outf.write("Local forest trust information...\n")
3524 self.write_forest_trust_info(local_forest_info,
3525 tln=local_tdo_info.domain_name.string)
3527 if not require_update:
3531 entries.extend(local_forest_info.entries)
3532 update_forest_info = lsa.ForestTrustInformation()
3533 update_forest_info.count = len(entries)
3534 update_forest_info.entries = entries
3537 for i in xrange(0, len(update_forest_info.entries)):
3538 r = update_forest_info.entries[i]
3539 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3541 if update_forest_info.entries[i].flags == 0:
3543 update_forest_info.entries[i].time = 0
3544 update_forest_info.entries[i].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3545 for i in xrange(0, len(update_forest_info.entries)):
3546 r = update_forest_info.entries[i]
3547 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3549 if update_forest_info.entries[i].flags == 0:
3551 update_forest_info.entries[i].time = 0
3552 update_forest_info.entries[i].flags &= ~lsa.LSA_NB_DISABLED_MASK
3553 update_forest_info.entries[i].flags &= ~lsa.LSA_SID_DISABLED_MASK
3555 for tln in enable_tln:
3557 for i in xrange(0, len(update_forest_info.entries)):
3558 r = update_forest_info.entries[i]
3559 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3561 if r.forest_trust_data.string.lower() != tln.lower():
3566 raise CommandError("Entry not found for value[%s] specified for --enable-tln" % tln)
3567 if not update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_MASK:
3568 raise CommandError("Entry found for value[%s] specified for --enable-tln is already enabled" % tln)
3569 update_forest_info.entries[idx].time = 0
3570 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3572 for tln in disable_tln:
3574 for i in xrange(0, len(update_forest_info.entries)):
3575 r = update_forest_info.entries[i]
3576 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3578 if r.forest_trust_data.string.lower() != tln.lower():
3583 raise CommandError("Entry not found for value[%s] specified for --disable-tln" % tln)
3584 if update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_ADMIN:
3585 raise CommandError("Entry found for value[%s] specified for --disable-tln is already disabled" % tln)
3586 update_forest_info.entries[idx].time = 0
3587 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3588 update_forest_info.entries[idx].flags |= lsa.LSA_TLN_DISABLED_ADMIN
3590 for tln_ex in add_tln_ex:
3592 for i in xrange(0, len(update_forest_info.entries)):
3593 r = update_forest_info.entries[i]
3594 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3596 if r.forest_trust_data.string.lower() != tln_ex.lower():
3601 raise CommandError("Entry already present for value[%s] specified for --add-tln-ex" % tln_ex)
3603 tln_dot = ".%s" % tln_ex.lower()
3605 for i in xrange(0, len(update_forest_info.entries)):
3606 r = update_forest_info.entries[i]
3607 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3609 r_dot = ".%s" % r.forest_trust_data.string.lower()
3610 if tln_dot == r_dot:
3611 raise CommandError("TLN entry present for value[%s] specified for --add-tln-ex" % tln_ex)
3612 if not tln_dot.endswith(r_dot):
3618 raise CommandError("No TLN parent present for value[%s] specified for --add-tln-ex" % tln_ex)
3620 r = lsa.ForestTrustRecord()
3621 r.type = lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX
3624 r.forest_trust_data.string = tln_ex
3627 entries.extend(update_forest_info.entries)
3628 entries.insert(idx + 1, r)
3629 update_forest_info.count = len(entries)
3630 update_forest_info.entries = entries
3632 for tln_ex in delete_tln_ex:
3634 for i in xrange(0, len(update_forest_info.entries)):
3635 r = update_forest_info.entries[i]
3636 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3638 if r.forest_trust_data.string.lower() != tln_ex.lower():
3643 raise CommandError("Entry not found for value[%s] specified for --delete-tln-ex" % tln_ex)
3646 entries.extend(update_forest_info.entries)
3648 update_forest_info.count = len(entries)
3649 update_forest_info.entries = entries
3651 for nb in enable_nb:
3653 for i in xrange(0, len(update_forest_info.entries)):
3654 r = update_forest_info.entries[i]
3655 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3657 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3662 raise CommandError("Entry not found for value[%s] specified for --enable-nb" % nb)
3663 if not update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_MASK:
3664 raise CommandError("Entry found for value[%s] specified for --enable-nb is already enabled" % nb)
3665 update_forest_info.entries[idx].time = 0
3666 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3668 for nb in disable_nb:
3670 for i in xrange(0, len(update_forest_info.entries)):
3671 r = update_forest_info.entries[i]
3672 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3674 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3679 raise CommandError("Entry not found for value[%s] specified for --delete-nb" % nb)
3680 if update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_ADMIN:
3681 raise CommandError("Entry found for value[%s] specified for --disable-nb is already disabled" % nb)
3682 update_forest_info.entries[idx].time = 0
3683 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3684 update_forest_info.entries[idx].flags |= lsa.LSA_NB_DISABLED_ADMIN
3686 for sid in enable_sid:
3688 for i in xrange(0, len(update_forest_info.entries)):
3689 r = update_forest_info.entries[i]
3690 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3692 if r.forest_trust_data.domain_sid != sid:
3697 raise CommandError("Entry not found for value[%s] specified for --enable-sid" % sid)
3698 if not update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_MASK:
3699 raise CommandError("Entry found for value[%s] specified for --enable-sid is already enabled" % nb)
3700 update_forest_info.entries[idx].time = 0
3701 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3703 for sid in disable_sid:
3705 for i in xrange(0, len(update_forest_info.entries)):
3706 r = update_forest_info.entries[i]
3707 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3709 if r.forest_trust_data.domain_sid != sid:
3714 raise CommandError("Entry not found for value[%s] specified for --delete-sid" % sid)
3715 if update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_ADMIN:
3716 raise CommandError("Entry found for value[%s] specified for --disable-sid is already disabled" % nb)
3717 update_forest_info.entries[idx].time = 0
3718 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3719 update_forest_info.entries[idx].flags |= lsa.LSA_SID_DISABLED_ADMIN
3722 update_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3723 local_tdo_info.domain_name,
3724 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3725 update_forest_info, 0)
3726 except RuntimeError as error:
3727 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3729 self.outf.write("Updated forest trust information...\n")
3730 self.write_forest_trust_info(update_forest_info,
3731 tln=local_tdo_info.domain_name.string,
3732 collisions=update_forest_collision)
3735 lsaString = lsa.String()
3736 lsaString.string = local_tdo_info.domain_name.string
3737 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3739 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3740 except RuntimeError as error:
3741 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3743 self.outf.write("Stored forest trust information...\n")
3744 self.write_forest_trust_info(stored_forest_info,
3745 tln=local_tdo_info.domain_name.string)
3748 class cmd_domain_tombstones_expunge(Command):
3749 """Expunge tombstones from the database.
3751 This command expunges tombstones from the database."""
3752 synopsis = "%prog NC [NC [...]] [options]"
3755 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
3756 metavar="URL", dest="H"),
3757 Option("--current-time",
3758 help="The current time to evaluate the tombstone lifetime from, expressed as YYYY-MM-DD",
3760 Option("--tombstone-lifetime", help="Number of days a tombstone should be preserved for", type=int),
3763 takes_args = ["nc*"]
3765 takes_optiongroups = {
3766 "sambaopts": options.SambaOptions,
3767 "credopts": options.CredentialsOptions,
3768 "versionopts": options.VersionOptions,
3771 def run(self, *ncs, **kwargs):
3772 sambaopts = kwargs.get("sambaopts")
3773 credopts = kwargs.get("credopts")
3774 versionpts = kwargs.get("versionopts")
3776 current_time_string = kwargs.get("current_time")
3777 tombstone_lifetime = kwargs.get("tombstone_lifetime")
3778 lp = sambaopts.get_loadparm()
3779 creds = credopts.get_credentials(lp)
3780 samdb = SamDB(url=H, session_info=system_session(),
3781 credentials=creds, lp=lp)
3783 if current_time_string is not None:
3784 current_time_obj = time.strptime(current_time_string, "%Y-%m-%d")
3785 current_time = long(time.mktime(current_time_obj))
3788 current_time = long(time.time())
3791 res = samdb.search(expression="", base="", scope=ldb.SCOPE_BASE,
3792 attrs=["namingContexts"])
3795 for nc in res[0]["namingContexts"]:
3800 started_transaction = False
3802 samdb.transaction_start()
3803 started_transaction = True
3805 removed_links) = samdb.garbage_collect_tombstones(ncs,
3806 current_time=current_time,
3807 tombstone_lifetime=tombstone_lifetime)
3809 except Exception, err:
3810 if started_transaction:
3811 samdb.transaction_cancel()
3812 raise CommandError("Failed to expunge / garbage collect tombstones", err)
3814 samdb.transaction_commit()
3816 self.outf.write("Removed %d objects and %d links successfully\n"
3817 % (removed_objects, removed_links))
3821 class cmd_domain_trust(SuperCommand):
3822 """Domain and forest trust management."""
3825 subcommands["list"] = cmd_domain_trust_list()
3826 subcommands["show"] = cmd_domain_trust_show()
3827 subcommands["create"] = cmd_domain_trust_create()
3828 subcommands["delete"] = cmd_domain_trust_delete()
3829 subcommands["validate"] = cmd_domain_trust_validate()
3830 subcommands["namespaces"] = cmd_domain_trust_namespaces()
3832 class cmd_domain_tombstones(SuperCommand):
3833 """Domain tombstone and recycled object management."""
3836 subcommands["expunge"] = cmd_domain_tombstones_expunge()
3838 class cmd_domain(SuperCommand):
3839 """Domain management."""
3842 subcommands["demote"] = cmd_domain_demote()
3843 if cmd_domain_export_keytab is not None:
3844 subcommands["exportkeytab"] = cmd_domain_export_keytab()
3845 subcommands["info"] = cmd_domain_info()
3846 subcommands["provision"] = cmd_domain_provision()
3847 subcommands["join"] = cmd_domain_join()
3848 subcommands["dcpromo"] = cmd_domain_dcpromo()
3849 subcommands["level"] = cmd_domain_level()
3850 subcommands["passwordsettings"] = cmd_domain_passwordsettings()
3851 subcommands["classicupgrade"] = cmd_domain_classicupgrade()
3852 subcommands["samba3upgrade"] = cmd_domain_samba3upgrade()
3853 subcommands["trust"] = cmd_domain_trust()
3854 subcommands["tombstones"] = cmd_domain_tombstones()