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")
776 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"]))
826 "Error while demoting, re-enabling inbound replication\n")
827 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
828 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
830 raise CommandError("Error while changing account control", e)
834 "Error while demoting, re-enabling inbound replication")
835 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
836 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
838 raise CommandError("Unable to find object with samaccountName = %s$"
839 " in the remote dc" % netbios_name.upper())
843 uac &= ~(UF_SERVER_TRUST_ACCOUNT|UF_TRUSTED_FOR_DELEGATION|UF_PARTIAL_SECRETS_ACCOUNT)
844 uac |= UF_WORKSTATION_TRUST_ACCOUNT
849 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
850 ldb.FLAG_MOD_REPLACE,
851 "userAccountControl")
853 remote_samdb.modify(msg)
856 "Error while demoting, re-enabling inbound replication")
857 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
858 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
861 raise CommandError("Error while changing account control", e)
863 parent = msg.dn.parent()
864 dc_name = res[0].dn.get_rdn_value()
865 rdn = "CN=%s" % dc_name
867 # Let's move to the Computer container
871 computer_dn = ldb.Dn(remote_samdb, "CN=Computers,%s" % str(remote_samdb.domain_dn()))
872 res = remote_samdb.search(base=computer_dn, expression=rdn, scope=ldb.SCOPE_ONELEVEL)
875 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
876 scope=ldb.SCOPE_ONELEVEL)
877 while(len(res) != 0 and i < 100):
879 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
880 scope=ldb.SCOPE_ONELEVEL)
884 "Error while demoting, re-enabling inbound replication\n")
885 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
886 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
892 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
893 ldb.FLAG_MOD_REPLACE,
894 "userAccountControl")
896 remote_samdb.modify(msg)
898 raise CommandError("Unable to find a slot for renaming %s,"
899 " all names from %s-1 to %s-%d seemed used" %
900 (str(dc_dn), rdn, rdn, i - 9))
902 newrdn = "%s-%d" % (rdn, i)
905 newdn = ldb.Dn(remote_samdb, "%s,%s" % (newrdn, str(computer_dn)))
906 remote_samdb.rename(dc_dn, newdn)
909 "Error while demoting, re-enabling inbound replication\n")
910 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
911 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
917 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
918 ldb.FLAG_MOD_REPLACE,
919 "userAccountControl")
921 remote_samdb.modify(msg)
922 raise CommandError("Error while renaming %s to %s" % (str(dc_dn), str(newdn)), e)
925 server_dsa_dn = samdb.get_serverName()
926 domain = remote_samdb.get_root_basedn()
929 req1 = drsuapi.DsRemoveDSServerRequest1()
930 req1.server_dn = str(server_dsa_dn)
931 req1.domain_dn = str(domain)
934 drsuapiBind.DsRemoveDSServer(drsuapi_handle, 1, req1)
935 except RuntimeError as (werr, string):
936 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
938 "Error while demoting, re-enabling inbound replication\n")
939 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
940 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
946 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
947 ldb.FLAG_MOD_REPLACE,
948 "userAccountControl")
949 remote_samdb.modify(msg)
950 remote_samdb.rename(newdn, dc_dn)
951 if werr == werror.WERR_DS_DRA_NO_REPLICA:
952 raise CommandError("The DC %s is not present on (already removed from) the remote server: " % server_dsa_dn, e)
954 raise CommandError("Error while sending a removeDsServer of %s: " % server_dsa_dn, e)
956 remove_dc.remove_sysvol_references(remote_samdb, logger, dc_name)
958 # These are objects under the computer account that should be deleted
959 for s in ("CN=Enterprise,CN=NTFRS Subscriptions",
960 "CN=%s, CN=NTFRS Subscriptions" % lp.get("realm"),
961 "CN=Domain system Volumes (SYSVOL Share), CN=NTFRS Subscriptions",
962 "CN=NTFRS Subscriptions"):
964 remote_samdb.delete(ldb.Dn(remote_samdb,
965 "%s,%s" % (s, str(newdn))))
966 except ldb.LdbError, l:
969 self.errf.write("Demote successful\n")
972 class cmd_domain_level(Command):
973 """Raise domain and forest function levels."""
975 synopsis = "%prog (show|raise <options>) [options]"
977 takes_optiongroups = {
978 "sambaopts": options.SambaOptions,
979 "credopts": options.CredentialsOptions,
980 "versionopts": options.VersionOptions,
984 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
985 metavar="URL", dest="H"),
986 Option("--quiet", help="Be quiet", action="store_true"),
987 Option("--forest-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
988 help="The forest function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)"),
989 Option("--domain-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
990 help="The domain function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)")
993 takes_args = ["subcommand"]
995 def run(self, subcommand, H=None, forest_level=None, domain_level=None,
996 quiet=False, credopts=None, sambaopts=None, versionopts=None):
997 lp = sambaopts.get_loadparm()
998 creds = credopts.get_credentials(lp, fallback_machine=True)
1000 samdb = SamDB(url=H, session_info=system_session(),
1001 credentials=creds, lp=lp)
1003 domain_dn = samdb.domain_dn()
1005 res_forest = samdb.search("CN=Partitions,%s" % samdb.get_config_basedn(),
1006 scope=ldb.SCOPE_BASE, attrs=["msDS-Behavior-Version"])
1007 assert len(res_forest) == 1
1009 res_domain = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1010 attrs=["msDS-Behavior-Version", "nTMixedDomain"])
1011 assert len(res_domain) == 1
1013 res_dc_s = samdb.search("CN=Sites,%s" % samdb.get_config_basedn(),
1014 scope=ldb.SCOPE_SUBTREE, expression="(objectClass=nTDSDSA)",
1015 attrs=["msDS-Behavior-Version"])
1016 assert len(res_dc_s) >= 1
1018 # default values, since "msDS-Behavior-Version" does not exist on Windows 2000 AD
1019 level_forest = DS_DOMAIN_FUNCTION_2000
1020 level_domain = DS_DOMAIN_FUNCTION_2000
1022 if "msDS-Behavior-Version" in res_forest[0]:
1023 level_forest = int(res_forest[0]["msDS-Behavior-Version"][0])
1024 if "msDS-Behavior-Version" in res_domain[0]:
1025 level_domain = int(res_domain[0]["msDS-Behavior-Version"][0])
1026 level_domain_mixed = int(res_domain[0]["nTMixedDomain"][0])
1029 for msg in res_dc_s:
1030 if "msDS-Behavior-Version" in msg:
1031 if min_level_dc is None or int(msg["msDS-Behavior-Version"][0]) < min_level_dc:
1032 min_level_dc = int(msg["msDS-Behavior-Version"][0])
1034 min_level_dc = DS_DOMAIN_FUNCTION_2000
1035 # well, this is the least
1038 if level_forest < DS_DOMAIN_FUNCTION_2000 or level_domain < DS_DOMAIN_FUNCTION_2000:
1039 raise CommandError("Domain and/or forest function level(s) is/are invalid. Correct them or reprovision!")
1040 if min_level_dc < DS_DOMAIN_FUNCTION_2000:
1041 raise CommandError("Lowest function level of a DC is invalid. Correct this or reprovision!")
1042 if level_forest > level_domain:
1043 raise CommandError("Forest function level is higher than the domain level(s). Correct this or reprovision!")
1044 if level_domain > min_level_dc:
1045 raise CommandError("Domain function level is higher than the lowest function level of a DC. Correct this or reprovision!")
1047 if subcommand == "show":
1048 self.message("Domain and forest function level for domain '%s'" % domain_dn)
1049 if level_forest == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1050 self.message("\nATTENTION: You run SAMBA 4 on a forest function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1051 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1052 self.message("\nATTENTION: You run SAMBA 4 on a domain function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1053 if min_level_dc == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1054 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)!")
1058 if level_forest == DS_DOMAIN_FUNCTION_2000:
1060 elif level_forest == DS_DOMAIN_FUNCTION_2003_MIXED:
1061 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1062 elif level_forest == DS_DOMAIN_FUNCTION_2003:
1064 elif level_forest == DS_DOMAIN_FUNCTION_2008:
1066 elif level_forest == DS_DOMAIN_FUNCTION_2008_R2:
1068 elif level_forest == DS_DOMAIN_FUNCTION_2012:
1070 elif level_forest == DS_DOMAIN_FUNCTION_2012_R2:
1073 outstr = "higher than 2012 R2"
1074 self.message("Forest function level: (Windows) " + outstr)
1076 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1077 outstr = "2000 mixed (NT4 DC support)"
1078 elif level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed == 0:
1080 elif level_domain == DS_DOMAIN_FUNCTION_2003_MIXED:
1081 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1082 elif level_domain == DS_DOMAIN_FUNCTION_2003:
1084 elif level_domain == DS_DOMAIN_FUNCTION_2008:
1086 elif level_domain == DS_DOMAIN_FUNCTION_2008_R2:
1088 elif level_domain == DS_DOMAIN_FUNCTION_2012:
1090 elif level_domain == DS_DOMAIN_FUNCTION_2012_R2:
1093 outstr = "higher than 2012 R2"
1094 self.message("Domain function level: (Windows) " + outstr)
1096 if min_level_dc == DS_DOMAIN_FUNCTION_2000:
1098 elif min_level_dc == DS_DOMAIN_FUNCTION_2003:
1100 elif min_level_dc == DS_DOMAIN_FUNCTION_2008:
1102 elif min_level_dc == DS_DOMAIN_FUNCTION_2008_R2:
1104 elif min_level_dc == DS_DOMAIN_FUNCTION_2012:
1106 elif min_level_dc == DS_DOMAIN_FUNCTION_2012_R2:
1109 outstr = "higher than 2012 R2"
1110 self.message("Lowest function level of a DC: (Windows) " + outstr)
1112 elif subcommand == "raise":
1115 if domain_level is not None:
1116 if domain_level == "2003":
1117 new_level_domain = DS_DOMAIN_FUNCTION_2003
1118 elif domain_level == "2008":
1119 new_level_domain = DS_DOMAIN_FUNCTION_2008
1120 elif domain_level == "2008_R2":
1121 new_level_domain = DS_DOMAIN_FUNCTION_2008_R2
1122 elif domain_level == "2012":
1123 new_level_domain = DS_DOMAIN_FUNCTION_2012
1124 elif domain_level == "2012_R2":
1125 new_level_domain = DS_DOMAIN_FUNCTION_2012_R2
1127 if new_level_domain <= level_domain and level_domain_mixed == 0:
1128 raise CommandError("Domain function level can't be smaller than or equal to the actual one!")
1129 if new_level_domain > min_level_dc:
1130 raise CommandError("Domain function level can't be higher than the lowest function level of a DC!")
1132 # Deactivate mixed/interim domain support
1133 if level_domain_mixed != 0:
1134 # Directly on the base DN
1136 m.dn = ldb.Dn(samdb, domain_dn)
1137 m["nTMixedDomain"] = ldb.MessageElement("0",
1138 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1142 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup") + ",CN=Partitions,%s" % samdb.get_config_basedn())
1143 m["nTMixedDomain"] = ldb.MessageElement("0",
1144 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1147 except ldb.LdbError, (enum, emsg):
1148 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1151 # Directly on the base DN
1153 m.dn = ldb.Dn(samdb, domain_dn)
1154 m["msDS-Behavior-Version"]= ldb.MessageElement(
1155 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1156 "msDS-Behavior-Version")
1160 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup")
1161 + ",CN=Partitions,%s" % samdb.get_config_basedn())
1162 m["msDS-Behavior-Version"]= ldb.MessageElement(
1163 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1164 "msDS-Behavior-Version")
1167 except ldb.LdbError, (enum, emsg):
1168 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1171 level_domain = new_level_domain
1172 msgs.append("Domain function level changed!")
1174 if forest_level is not None:
1175 if forest_level == "2003":
1176 new_level_forest = DS_DOMAIN_FUNCTION_2003
1177 elif forest_level == "2008":
1178 new_level_forest = DS_DOMAIN_FUNCTION_2008
1179 elif forest_level == "2008_R2":
1180 new_level_forest = DS_DOMAIN_FUNCTION_2008_R2
1181 elif forest_level == "2012":
1182 new_level_forest = DS_DOMAIN_FUNCTION_2012
1183 elif forest_level == "2012_R2":
1184 new_level_forest = DS_DOMAIN_FUNCTION_2012_R2
1186 if new_level_forest <= level_forest:
1187 raise CommandError("Forest function level can't be smaller than or equal to the actual one!")
1188 if new_level_forest > level_domain:
1189 raise CommandError("Forest function level can't be higher than the domain function level(s). Please raise it/them first!")
1192 m.dn = ldb.Dn(samdb, "CN=Partitions,%s" % samdb.get_config_basedn())
1193 m["msDS-Behavior-Version"]= ldb.MessageElement(
1194 str(new_level_forest), ldb.FLAG_MOD_REPLACE,
1195 "msDS-Behavior-Version")
1197 msgs.append("Forest function level changed!")
1198 msgs.append("All changes applied successfully!")
1199 self.message("\n".join(msgs))
1201 raise CommandError("invalid argument: '%s' (choose from 'show', 'raise')" % subcommand)
1204 class cmd_domain_passwordsettings(Command):
1205 """Set password settings.
1207 Password complexity, password lockout policy, history length,
1208 minimum password length, the minimum and maximum password age) on
1209 a Samba AD DC server.
1211 Use against a Windows DC is possible, but group policy will override it.
1214 synopsis = "%prog (show|set <options>) [options]"
1216 takes_optiongroups = {
1217 "sambaopts": options.SambaOptions,
1218 "versionopts": options.VersionOptions,
1219 "credopts": options.CredentialsOptions,
1223 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1224 metavar="URL", dest="H"),
1225 Option("--quiet", help="Be quiet", action="store_true"),
1226 Option("--complexity", type="choice", choices=["on","off","default"],
1227 help="The password complexity (on | off | default). Default is 'on'"),
1228 Option("--store-plaintext", type="choice", choices=["on","off","default"],
1229 help="Store plaintext passwords where account have 'store passwords with reversible encryption' set (on | off | default). Default is 'off'"),
1230 Option("--history-length",
1231 help="The password history length (<integer> | default). Default is 24.", type=str),
1232 Option("--min-pwd-length",
1233 help="The minimum password length (<integer> | default). Default is 7.", type=str),
1234 Option("--min-pwd-age",
1235 help="The minimum password age (<integer in days> | default). Default is 1.", type=str),
1236 Option("--max-pwd-age",
1237 help="The maximum password age (<integer in days> | default). Default is 43.", type=str),
1238 Option("--account-lockout-duration",
1239 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),
1240 Option("--account-lockout-threshold",
1241 help="The number of bad password attempts allowed before locking out the account (<integer> | default). Default is 0 (never lock out).", type=str),
1242 Option("--reset-account-lockout-after",
1243 help="After this time is elapsed, the recorded number of attempts restarts from zero (<integer> | default). Default is 30.", type=str),
1246 takes_args = ["subcommand"]
1248 def run(self, subcommand, H=None, min_pwd_age=None, max_pwd_age=None,
1249 quiet=False, complexity=None, store_plaintext=None, history_length=None,
1250 min_pwd_length=None, account_lockout_duration=None, account_lockout_threshold=None,
1251 reset_account_lockout_after=None, credopts=None, sambaopts=None,
1253 lp = sambaopts.get_loadparm()
1254 creds = credopts.get_credentials(lp)
1256 samdb = SamDB(url=H, session_info=system_session(),
1257 credentials=creds, lp=lp)
1259 domain_dn = samdb.domain_dn()
1260 res = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1261 attrs=["pwdProperties", "pwdHistoryLength", "minPwdLength",
1262 "minPwdAge", "maxPwdAge", "lockoutDuration", "lockoutThreshold",
1263 "lockOutObservationWindow"])
1264 assert(len(res) == 1)
1266 pwd_props = int(res[0]["pwdProperties"][0])
1267 pwd_hist_len = int(res[0]["pwdHistoryLength"][0])
1268 cur_min_pwd_len = int(res[0]["minPwdLength"][0])
1270 cur_min_pwd_age = int(abs(int(res[0]["minPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1271 if int(res[0]["maxPwdAge"][0]) == -0x8000000000000000:
1274 cur_max_pwd_age = int(abs(int(res[0]["maxPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1275 cur_account_lockout_threshold = int(res[0]["lockoutThreshold"][0])
1277 if int(res[0]["lockoutDuration"][0]) == -0x8000000000000000:
1278 cur_account_lockout_duration = 0
1280 cur_account_lockout_duration = abs(int(res[0]["lockoutDuration"][0])) / (1e7 * 60)
1281 cur_reset_account_lockout_after = abs(int(res[0]["lockOutObservationWindow"][0])) / (1e7 * 60)
1282 except Exception, e:
1283 raise CommandError("Could not retrieve password properties!", e)
1285 if subcommand == "show":
1286 self.message("Password informations for domain '%s'" % domain_dn)
1288 if pwd_props & DOMAIN_PASSWORD_COMPLEX != 0:
1289 self.message("Password complexity: on")
1291 self.message("Password complexity: off")
1292 if pwd_props & DOMAIN_PASSWORD_STORE_CLEARTEXT != 0:
1293 self.message("Store plaintext passwords: on")
1295 self.message("Store plaintext passwords: off")
1296 self.message("Password history length: %d" % pwd_hist_len)
1297 self.message("Minimum password length: %d" % cur_min_pwd_len)
1298 self.message("Minimum password age (days): %d" % cur_min_pwd_age)
1299 self.message("Maximum password age (days): %d" % cur_max_pwd_age)
1300 self.message("Account lockout duration (mins): %d" % cur_account_lockout_duration)
1301 self.message("Account lockout threshold (attempts): %d" % cur_account_lockout_threshold)
1302 self.message("Reset account lockout after (mins): %d" % cur_reset_account_lockout_after)
1303 elif subcommand == "set":
1306 m.dn = ldb.Dn(samdb, domain_dn)
1308 if complexity is not None:
1309 if complexity == "on" or complexity == "default":
1310 pwd_props = pwd_props | DOMAIN_PASSWORD_COMPLEX
1311 msgs.append("Password complexity activated!")
1312 elif complexity == "off":
1313 pwd_props = pwd_props & (~DOMAIN_PASSWORD_COMPLEX)
1314 msgs.append("Password complexity deactivated!")
1316 if store_plaintext is not None:
1317 if store_plaintext == "on" or store_plaintext == "default":
1318 pwd_props = pwd_props | DOMAIN_PASSWORD_STORE_CLEARTEXT
1319 msgs.append("Plaintext password storage for changed passwords activated!")
1320 elif store_plaintext == "off":
1321 pwd_props = pwd_props & (~DOMAIN_PASSWORD_STORE_CLEARTEXT)
1322 msgs.append("Plaintext password storage for changed passwords deactivated!")
1324 if complexity is not None or store_plaintext is not None:
1325 m["pwdProperties"] = ldb.MessageElement(str(pwd_props),
1326 ldb.FLAG_MOD_REPLACE, "pwdProperties")
1328 if history_length is not None:
1329 if history_length == "default":
1332 pwd_hist_len = int(history_length)
1334 if pwd_hist_len < 0 or pwd_hist_len > 24:
1335 raise CommandError("Password history length must be in the range of 0 to 24!")
1337 m["pwdHistoryLength"] = ldb.MessageElement(str(pwd_hist_len),
1338 ldb.FLAG_MOD_REPLACE, "pwdHistoryLength")
1339 msgs.append("Password history length changed!")
1341 if min_pwd_length is not None:
1342 if min_pwd_length == "default":
1345 min_pwd_len = int(min_pwd_length)
1347 if min_pwd_len < 0 or min_pwd_len > 14:
1348 raise CommandError("Minimum password length must be in the range of 0 to 14!")
1350 m["minPwdLength"] = ldb.MessageElement(str(min_pwd_len),
1351 ldb.FLAG_MOD_REPLACE, "minPwdLength")
1352 msgs.append("Minimum password length changed!")
1354 if min_pwd_age is not None:
1355 if min_pwd_age == "default":
1358 min_pwd_age = int(min_pwd_age)
1360 if min_pwd_age < 0 or min_pwd_age > 998:
1361 raise CommandError("Minimum password age must be in the range of 0 to 998!")
1364 min_pwd_age_ticks = -int(min_pwd_age * (24 * 60 * 60 * 1e7))
1366 m["minPwdAge"] = ldb.MessageElement(str(min_pwd_age_ticks),
1367 ldb.FLAG_MOD_REPLACE, "minPwdAge")
1368 msgs.append("Minimum password age changed!")
1370 if max_pwd_age is not None:
1371 if max_pwd_age == "default":
1374 max_pwd_age = int(max_pwd_age)
1376 if max_pwd_age < 0 or max_pwd_age > 999:
1377 raise CommandError("Maximum password age must be in the range of 0 to 999!")
1380 if max_pwd_age == 0:
1381 max_pwd_age_ticks = -0x8000000000000000
1383 max_pwd_age_ticks = -int(max_pwd_age * (24 * 60 * 60 * 1e7))
1385 m["maxPwdAge"] = ldb.MessageElement(str(max_pwd_age_ticks),
1386 ldb.FLAG_MOD_REPLACE, "maxPwdAge")
1387 msgs.append("Maximum password age changed!")
1389 if account_lockout_duration is not None:
1390 if account_lockout_duration == "default":
1391 account_lockout_duration = 30
1393 account_lockout_duration = int(account_lockout_duration)
1395 if account_lockout_duration < 0 or account_lockout_duration > 99999:
1396 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1399 if account_lockout_duration == 0:
1400 account_lockout_duration_ticks = -0x8000000000000000
1402 account_lockout_duration_ticks = -int(account_lockout_duration * (60 * 1e7))
1404 m["lockoutDuration"] = ldb.MessageElement(str(account_lockout_duration_ticks),
1405 ldb.FLAG_MOD_REPLACE, "lockoutDuration")
1406 msgs.append("Account lockout duration changed!")
1408 if account_lockout_threshold is not None:
1409 if account_lockout_threshold == "default":
1410 account_lockout_threshold = 0
1412 account_lockout_threshold = int(account_lockout_threshold)
1414 m["lockoutThreshold"] = ldb.MessageElement(str(account_lockout_threshold),
1415 ldb.FLAG_MOD_REPLACE, "lockoutThreshold")
1416 msgs.append("Account lockout threshold changed!")
1418 if reset_account_lockout_after is not None:
1419 if reset_account_lockout_after == "default":
1420 reset_account_lockout_after = 30
1422 reset_account_lockout_after = int(reset_account_lockout_after)
1424 if reset_account_lockout_after < 0 or reset_account_lockout_after > 99999:
1425 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1428 if reset_account_lockout_after == 0:
1429 reset_account_lockout_after_ticks = -0x8000000000000000
1431 reset_account_lockout_after_ticks = -int(reset_account_lockout_after * (60 * 1e7))
1433 m["lockOutObservationWindow"] = ldb.MessageElement(str(reset_account_lockout_after_ticks),
1434 ldb.FLAG_MOD_REPLACE, "lockOutObservationWindow")
1435 msgs.append("Duration to reset account lockout after changed!")
1437 if max_pwd_age > 0 and min_pwd_age >= max_pwd_age:
1438 raise CommandError("Maximum password age (%d) must be greater than minimum password age (%d)!" % (max_pwd_age, min_pwd_age))
1441 raise CommandError("You must specify at least one option to set. Try --help")
1443 msgs.append("All changes applied successfully!")
1444 self.message("\n".join(msgs))
1446 raise CommandError("Wrong argument '%s'!" % subcommand)
1449 class cmd_domain_classicupgrade(Command):
1450 """Upgrade from Samba classic (NT4-like) database to Samba AD DC database.
1452 Specify either a directory with all Samba classic DC databases and state files (with --dbdir) or
1453 the testparm utility from your classic installation (with --testparm).
1456 synopsis = "%prog [options] <classic_smb_conf>"
1458 takes_optiongroups = {
1459 "sambaopts": options.SambaOptions,
1460 "versionopts": options.VersionOptions
1464 Option("--dbdir", type="string", metavar="DIR",
1465 help="Path to samba classic DC database directory"),
1466 Option("--testparm", type="string", metavar="PATH",
1467 help="Path to samba classic DC testparm utility from the previous installation. This allows the default paths of the previous installation to be followed"),
1468 Option("--targetdir", type="string", metavar="DIR",
1469 help="Path prefix where the new Samba 4.0 AD domain should be initialised"),
1470 Option("--quiet", help="Be quiet", action="store_true"),
1471 Option("--verbose", help="Be verbose", action="store_true"),
1472 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
1473 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
1474 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
1475 "BIND9_FLATFILE uses bind9 text database to store zone information, "
1476 "BIND9_DLZ uses samba4 AD to store zone information, "
1477 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
1478 default="SAMBA_INTERNAL")
1482 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
1483 action="store_true"),
1484 Option("--use-xattrs", type="choice", choices=["yes","no","auto"],
1485 metavar="[yes|no|auto]",
1486 help="Define if we should use the native fs capabilities or a tdb file for "
1487 "storing attributes likes ntacl when --use-ntvfs is set. "
1488 "auto tries to make an inteligent guess based on the user rights and system capabilities",
1491 if samba.is_ntvfs_fileserver_built():
1492 takes_options.extend(ntvfs_options)
1494 takes_args = ["smbconf"]
1496 def run(self, smbconf=None, targetdir=None, dbdir=None, testparm=None,
1497 quiet=False, verbose=False, use_xattrs="auto", sambaopts=None, versionopts=None,
1498 dns_backend=None, use_ntvfs=False):
1500 if not os.path.exists(smbconf):
1501 raise CommandError("File %s does not exist" % smbconf)
1503 if testparm and not os.path.exists(testparm):
1504 raise CommandError("Testparm utility %s does not exist" % testparm)
1506 if dbdir and not os.path.exists(dbdir):
1507 raise CommandError("Directory %s does not exist" % dbdir)
1509 if not dbdir and not testparm:
1510 raise CommandError("Please specify either dbdir or testparm")
1512 logger = self.get_logger()
1514 logger.setLevel(logging.DEBUG)
1516 logger.setLevel(logging.WARNING)
1518 logger.setLevel(logging.INFO)
1520 if dbdir and testparm:
1521 logger.warning("both dbdir and testparm specified, ignoring dbdir.")
1524 lp = sambaopts.get_loadparm()
1526 s3conf = s3param.get_context()
1529 s3conf.set("realm", sambaopts.realm)
1531 if targetdir is not None:
1532 if not os.path.isdir(targetdir):
1536 if use_xattrs == "yes":
1538 elif use_xattrs == "auto" and use_ntvfs == False:
1540 elif use_ntvfs == False:
1541 raise CommandError("--use-xattrs=no requires --use-ntvfs (not supported for production use). "
1542 "Please re-run with --use-xattrs omitted.")
1543 elif use_xattrs == "auto" and not s3conf.get("posix:eadb"):
1545 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
1547 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
1550 samba.ntacls.setntacl(lp, tmpfile.name,
1551 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
1554 # FIXME: Don't catch all exceptions here
1555 logger.info("You are not root or your system does not support xattr, using tdb backend for attributes. "
1556 "If you intend to use this provision in production, rerun the script as root on a system supporting xattrs.")
1560 # Set correct default values from dbdir or testparm
1563 paths["state directory"] = dbdir
1564 paths["private dir"] = dbdir
1565 paths["lock directory"] = dbdir
1566 paths["smb passwd file"] = dbdir + "/smbpasswd"
1568 paths["state directory"] = get_testparm_var(testparm, smbconf, "state directory")
1569 paths["private dir"] = get_testparm_var(testparm, smbconf, "private dir")
1570 paths["smb passwd file"] = get_testparm_var(testparm, smbconf, "smb passwd file")
1571 paths["lock directory"] = get_testparm_var(testparm, smbconf, "lock directory")
1572 # "testparm" from Samba 3 < 3.4.x is not aware of the parameter
1573 # "state directory", instead make use of "lock directory"
1574 if len(paths["state directory"]) == 0:
1575 paths["state directory"] = paths["lock directory"]
1578 s3conf.set(p, paths[p])
1580 # load smb.conf parameters
1581 logger.info("Reading smb.conf")
1582 s3conf.load(smbconf)
1583 samba3 = Samba3(smbconf, s3conf)
1585 logger.info("Provisioning")
1586 upgrade_from_samba3(samba3, logger, targetdir, session_info=system_session(),
1587 useeadb=eadb, dns_backend=dns_backend, use_ntvfs=use_ntvfs)
1590 class cmd_domain_samba3upgrade(cmd_domain_classicupgrade):
1591 __doc__ = cmd_domain_classicupgrade.__doc__
1593 # This command is present for backwards compatibility only,
1594 # and should not be shown.
1598 class LocalDCCredentialsOptions(options.CredentialsOptions):
1599 def __init__(self, parser):
1600 options.CredentialsOptions.__init__(self, parser, special_name="local-dc")
1602 class DomainTrustCommand(Command):
1603 """List domain trusts."""
1606 Command.__init__(self)
1607 self.local_lp = None
1609 self.local_server = None
1610 self.local_binding_string = None
1611 self.local_creds = None
1613 self.remote_server = None
1614 self.remote_binding_string = None
1615 self.remote_creds = None
1617 def _uint32(self, v):
1618 return ctypes.c_uint32(v).value
1620 def check_runtime_error(self, runtime, val):
1624 err32 = self._uint32(runtime[0])
1630 class LocalRuntimeError(CommandError):
1631 def __init__(exception_self, self, runtime, message):
1632 err32 = self._uint32(runtime[0])
1634 msg = "LOCAL_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1635 self.local_server, message, err32, errstr)
1636 CommandError.__init__(exception_self, msg)
1638 class RemoteRuntimeError(CommandError):
1639 def __init__(exception_self, self, runtime, message):
1640 err32 = self._uint32(runtime[0])
1642 msg = "REMOTE_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1643 self.remote_server, message, err32, errstr)
1644 CommandError.__init__(exception_self, msg)
1646 class LocalLdbError(CommandError):
1647 def __init__(exception_self, self, ldb_error, message):
1648 errval = ldb_error[0]
1649 errstr = ldb_error[1]
1650 msg = "LOCAL_DC[%s]: %s - ERROR(%d) - %s" % (
1651 self.local_server, message, errval, errstr)
1652 CommandError.__init__(exception_self, msg)
1654 def setup_local_server(self, sambaopts, localdcopts):
1655 if self.local_server is not None:
1656 return self.local_server
1658 lp = sambaopts.get_loadparm()
1660 local_server = localdcopts.ipaddress
1661 if local_server is None:
1662 server_role = lp.server_role()
1663 if server_role != "ROLE_ACTIVE_DIRECTORY_DC":
1664 raise CommandError("Invalid server_role %s" % (server_role))
1665 local_server = lp.get('netbios name')
1666 local_transport = "ncalrpc"
1667 local_binding_options = ""
1668 local_binding_options += ",auth_type=ncalrpc_as_system"
1669 local_ldap_url = None
1672 local_transport = "ncacn_np"
1673 local_binding_options = ""
1674 local_ldap_url = "ldap://%s" % local_server
1675 local_creds = localdcopts.get_credentials(lp)
1679 self.local_server = local_server
1680 self.local_binding_string = "%s:%s[%s]" % (local_transport, local_server, local_binding_options)
1681 self.local_ldap_url = local_ldap_url
1682 self.local_creds = local_creds
1683 return self.local_server
1685 def new_local_lsa_connection(self):
1686 return lsa.lsarpc(self.local_binding_string, self.local_lp, self.local_creds)
1688 def new_local_netlogon_connection(self):
1689 return netlogon.netlogon(self.local_binding_string, self.local_lp, self.local_creds)
1691 def new_local_ldap_connection(self):
1692 return SamDB(url=self.local_ldap_url,
1693 session_info=system_session(),
1694 credentials=self.local_creds,
1697 def setup_remote_server(self, credopts, domain,
1699 require_writable=True):
1702 assert require_writable
1704 if self.remote_server is not None:
1705 return self.remote_server
1707 self.remote_server = "__unknown__remote_server__.%s" % domain
1708 assert self.local_server is not None
1710 remote_creds = credopts.get_credentials(self.local_lp)
1711 remote_server = credopts.ipaddress
1712 remote_binding_options = ""
1714 # TODO: we should also support NT4 domains
1715 # we could use local_netlogon.netr_DsRGetDCNameEx2() with the remote domain name
1716 # and delegate NBT or CLDAP to the local netlogon server
1718 remote_net = Net(remote_creds, self.local_lp, server=remote_server)
1719 remote_flags = nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS
1720 if require_writable:
1721 remote_flags |= nbt.NBT_SERVER_WRITABLE
1723 remote_flags |= nbt.NBT_SERVER_PDC
1724 remote_info = remote_net.finddc(flags=remote_flags, domain=domain, address=remote_server)
1726 raise CommandError("Failed to find a writeable DC for domain '%s'" % domain)
1728 nbt.NBT_SERVER_PDC: "PDC",
1729 nbt.NBT_SERVER_GC: "GC",
1730 nbt.NBT_SERVER_LDAP: "LDAP",
1731 nbt.NBT_SERVER_DS: "DS",
1732 nbt.NBT_SERVER_KDC: "KDC",
1733 nbt.NBT_SERVER_TIMESERV: "TIMESERV",
1734 nbt.NBT_SERVER_CLOSEST: "CLOSEST",
1735 nbt.NBT_SERVER_WRITABLE: "WRITABLE",
1736 nbt.NBT_SERVER_GOOD_TIMESERV: "GOOD_TIMESERV",
1737 nbt.NBT_SERVER_NDNC: "NDNC",
1738 nbt.NBT_SERVER_SELECT_SECRET_DOMAIN_6: "SELECT_SECRET_DOMAIN_6",
1739 nbt.NBT_SERVER_FULL_SECRET_DOMAIN_6: "FULL_SECRET_DOMAIN_6",
1740 nbt.NBT_SERVER_ADS_WEB_SERVICE: "ADS_WEB_SERVICE",
1741 nbt.NBT_SERVER_DS_8: "DS_8",
1742 nbt.NBT_SERVER_HAS_DNS_NAME: "HAS_DNS_NAME",
1743 nbt.NBT_SERVER_IS_DEFAULT_NC: "IS_DEFAULT_NC",
1744 nbt.NBT_SERVER_FOREST_ROOT: "FOREST_ROOT",
1746 server_type_string = self.generic_bitmap_to_string(flag_map,
1747 remote_info.server_type, names_only=True)
1748 self.outf.write("RemoteDC Netbios[%s] DNS[%s] ServerType[%s]\n" % (
1749 remote_info.pdc_name,
1750 remote_info.pdc_dns_name,
1751 server_type_string))
1753 self.remote_server = remote_info.pdc_dns_name
1754 self.remote_binding_string="ncacn_np:%s[%s]" % (self.remote_server, remote_binding_options)
1755 self.remote_creds = remote_creds
1756 return self.remote_server
1758 def new_remote_lsa_connection(self):
1759 return lsa.lsarpc(self.remote_binding_string, self.local_lp, self.remote_creds)
1761 def new_remote_netlogon_connection(self):
1762 return netlogon.netlogon(self.remote_binding_string, self.local_lp, self.remote_creds)
1764 def get_lsa_info(self, conn, policy_access):
1765 objectAttr = lsa.ObjectAttribute()
1766 objectAttr.sec_qos = lsa.QosInfo()
1768 policy = conn.OpenPolicy2(''.decode('utf-8'),
1769 objectAttr, policy_access)
1771 info = conn.QueryInfoPolicy2(policy, lsa.LSA_POLICY_INFO_DNS)
1773 return (policy, info)
1775 def get_netlogon_dc_info(self, conn, server):
1776 info = conn.netr_DsRGetDCNameEx2(server,
1777 None, 0, None, None, None,
1778 netlogon.DS_RETURN_DNS_NAME)
1781 def netr_DomainTrust_to_name(self, t):
1782 if t.trust_type == lsa.LSA_TRUST_TYPE_DOWNLEVEL:
1783 return t.netbios_name
1787 def netr_DomainTrust_to_type(self, a, t):
1789 primary_parent = None
1791 if _t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
1793 if not _t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1794 primary_parent = a[_t.parent_index]
1797 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1798 if t is primary_parent:
1801 if t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1804 parent = a[t.parent_index]
1805 if parent is primary:
1810 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1815 def netr_DomainTrust_to_transitive(self, t):
1816 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1819 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE:
1822 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1827 def netr_DomainTrust_to_direction(self, t):
1828 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND and \
1829 t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1832 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND:
1835 if t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1840 def generic_enum_to_string(self, e_dict, v, names_only=False):
1844 v32 = self._uint32(v)
1845 w = "__unknown__%08X__" % v32
1847 r = "0x%x (%s)" % (v, w)
1850 def generic_bitmap_to_string(self, b_dict, v, names_only=False):
1855 for b in sorted(b_dict.keys()):
1862 c32 = self._uint32(c)
1863 s += ["__unknown_%08X__" % c32]
1868 r = "0x%x (%s)" % (v, w)
1871 def trustType_string(self, v):
1873 lsa.LSA_TRUST_TYPE_DOWNLEVEL : "DOWNLEVEL",
1874 lsa.LSA_TRUST_TYPE_UPLEVEL : "UPLEVEL",
1875 lsa.LSA_TRUST_TYPE_MIT : "MIT",
1876 lsa.LSA_TRUST_TYPE_DCE : "DCE",
1878 return self.generic_enum_to_string(types, v)
1880 def trustDirection_string(self, v):
1882 lsa.LSA_TRUST_DIRECTION_INBOUND |
1883 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "BOTH",
1884 lsa.LSA_TRUST_DIRECTION_INBOUND : "INBOUND",
1885 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "OUTBOUND",
1887 return self.generic_enum_to_string(directions, v)
1889 def trustAttributes_string(self, v):
1891 lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE : "NON_TRANSITIVE",
1892 lsa.LSA_TRUST_ATTRIBUTE_UPLEVEL_ONLY : "UPLEVEL_ONLY",
1893 lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN : "QUARANTINED_DOMAIN",
1894 lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE : "FOREST_TRANSITIVE",
1895 lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION : "CROSS_ORGANIZATION",
1896 lsa.LSA_TRUST_ATTRIBUTE_WITHIN_FOREST : "WITHIN_FOREST",
1897 lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL : "TREAT_AS_EXTERNAL",
1898 lsa.LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION : "USES_RC4_ENCRYPTION",
1900 return self.generic_bitmap_to_string(attributes, v)
1902 def kerb_EncTypes_string(self, v):
1904 security.KERB_ENCTYPE_DES_CBC_CRC : "DES_CBC_CRC",
1905 security.KERB_ENCTYPE_DES_CBC_MD5 : "DES_CBC_MD5",
1906 security.KERB_ENCTYPE_RC4_HMAC_MD5 : "RC4_HMAC_MD5",
1907 security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96 : "AES128_CTS_HMAC_SHA1_96",
1908 security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96 : "AES256_CTS_HMAC_SHA1_96",
1909 security.KERB_ENCTYPE_FAST_SUPPORTED : "FAST_SUPPORTED",
1910 security.KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED : "COMPOUND_IDENTITY_SUPPORTED",
1911 security.KERB_ENCTYPE_CLAIMS_SUPPORTED : "CLAIMS_SUPPORTED",
1912 security.KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED : "RESOURCE_SID_COMPRESSION_DISABLED",
1914 return self.generic_bitmap_to_string(enctypes, v)
1916 def entry_tln_status(self, e_flags, ):
1918 return "Status[Enabled]"
1921 lsa.LSA_TLN_DISABLED_NEW : "Disabled-New",
1922 lsa.LSA_TLN_DISABLED_ADMIN : "Disabled",
1923 lsa.LSA_TLN_DISABLED_CONFLICT : "Disabled-Conflicting",
1925 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
1927 def entry_dom_status(self, e_flags):
1929 return "Status[Enabled]"
1932 lsa.LSA_SID_DISABLED_ADMIN : "Disabled-SID",
1933 lsa.LSA_SID_DISABLED_CONFLICT : "Disabled-SID-Conflicting",
1934 lsa.LSA_NB_DISABLED_ADMIN : "Disabled-NB",
1935 lsa.LSA_NB_DISABLED_CONFLICT : "Disabled-NB-Conflicting",
1937 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
1939 def write_forest_trust_info(self, fti, tln=None, collisions=None):
1941 tln_string = " TDO[%s]" % tln
1945 self.outf.write("Namespaces[%d]%s:\n" % (
1946 len(fti.entries), tln_string))
1948 for i in xrange(0, len(fti.entries)):
1952 collision_string = ""
1954 if collisions is not None:
1955 for c in collisions.entries:
1959 collision_string = " Collision[%s]" % (c.name.string)
1961 d = e.forest_trust_data
1962 if e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
1963 self.outf.write("TLN: %-32s DNS[*.%s]%s\n" % (
1964 self.entry_tln_status(flags),
1965 d.string, collision_string))
1966 elif e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
1967 self.outf.write("TLN_EX: %-29s DNS[*.%s]\n" % (
1969 elif e.type == lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
1970 self.outf.write("DOM: %-32s DNS[%s] Netbios[%s] SID[%s]%s\n" % (
1971 self.entry_dom_status(flags),
1972 d.dns_domain_name.string,
1973 d.netbios_domain_name.string,
1974 d.domain_sid, collision_string))
1977 class cmd_domain_trust_list(DomainTrustCommand):
1978 """List domain trusts."""
1980 synopsis = "%prog [options]"
1982 takes_optiongroups = {
1983 "sambaopts": options.SambaOptions,
1984 "versionopts": options.VersionOptions,
1985 "localdcopts": LocalDCCredentialsOptions,
1991 def run(self, sambaopts=None, versionopts=None, localdcopts=None):
1993 local_server = self.setup_local_server(sambaopts, localdcopts)
1995 local_netlogon = self.new_local_netlogon_connection()
1996 except RuntimeError as error:
1997 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2000 local_netlogon_trusts = local_netlogon.netr_DsrEnumerateDomainTrusts(local_server,
2001 netlogon.NETR_TRUST_FLAG_IN_FOREST |
2002 netlogon.NETR_TRUST_FLAG_OUTBOUND |
2003 netlogon.NETR_TRUST_FLAG_INBOUND)
2004 except RuntimeError as error:
2005 if self.check_runtime_error(error, werror.WERR_RPC_S_PROCNUM_OUT_OF_RANGE):
2006 # TODO: we could implement a fallback to lsa.EnumTrustDom()
2007 raise CommandError("LOCAL_DC[%s]: netr_DsrEnumerateDomainTrusts not supported." % (
2009 raise self.LocalRuntimeError(self, error, "netr_DsrEnumerateDomainTrusts failed")
2011 a = local_netlogon_trusts.array
2013 if t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
2015 self.outf.write("%-14s %-15s %-19s %s\n" % (
2016 "Type[%s]" % self.netr_DomainTrust_to_type(a, t),
2017 "Transitive[%s]" % self.netr_DomainTrust_to_transitive(t),
2018 "Direction[%s]" % self.netr_DomainTrust_to_direction(t),
2019 "Name[%s]" % self.netr_DomainTrust_to_name(t)))
2022 class cmd_domain_trust_show(DomainTrustCommand):
2023 """Show trusted domain details."""
2025 synopsis = "%prog NAME [options]"
2027 takes_optiongroups = {
2028 "sambaopts": options.SambaOptions,
2029 "versionopts": options.VersionOptions,
2030 "localdcopts": LocalDCCredentialsOptions,
2036 takes_args = ["domain"]
2038 def run(self, domain, sambaopts=None, versionopts=None, localdcopts=None):
2040 local_server = self.setup_local_server(sambaopts, localdcopts)
2042 local_lsa = self.new_local_lsa_connection()
2043 except RuntimeError as error:
2044 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2047 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2048 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2049 except RuntimeError as error:
2050 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2052 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2053 local_lsa_info.name.string,
2054 local_lsa_info.dns_domain.string,
2055 local_lsa_info.sid))
2057 lsaString = lsa.String()
2058 lsaString.string = domain
2060 local_tdo_full = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2061 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2062 local_tdo_info = local_tdo_full.info_ex
2063 local_tdo_posix = local_tdo_full.posix_offset
2064 except NTSTATUSError as error:
2065 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2066 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2068 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(FULL_INFO) failed")
2071 local_tdo_enctypes = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2072 lsaString, lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES)
2073 except NTSTATUSError as error:
2074 if self.check_runtime_error(error, ntstatus.NT_STATUS_INVALID_PARAMETER):
2076 if self.check_runtime_error(error, ntstatus.NT_STATUS_INVALID_INFO_CLASS):
2079 if error is not None:
2080 raise self.LocalRuntimeError(self, error,
2081 "QueryTrustedDomainInfoByName(SUPPORTED_ENCRYPTION_TYPES) failed")
2083 local_tdo_enctypes = lsa.TrustDomainInfoSupportedEncTypes()
2084 local_tdo_enctypes.enc_types = 0
2087 local_tdo_forest = None
2088 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2089 local_tdo_forest = local_lsa.lsaRQueryForestTrustInformation(local_policy,
2090 lsaString, lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
2091 except RuntimeError as error:
2092 if self.check_runtime_error(error, self.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
2094 if self.check_runtime_error(error, ntstatus.NT_STATUS_NOT_FOUND):
2096 if error is not None:
2097 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation failed")
2099 local_tdo_forest = lsa.ForestTrustInformation()
2100 local_tdo_forest.count = 0
2101 local_tdo_forest.entries = []
2103 self.outf.write("TrusteDomain:\n\n");
2104 self.outf.write("NetbiosName: %s\n" % local_tdo_info.netbios_name.string)
2105 if local_tdo_info.netbios_name.string != local_tdo_info.domain_name.string:
2106 self.outf.write("DnsName: %s\n" % local_tdo_info.domain_name.string)
2107 self.outf.write("SID: %s\n" % local_tdo_info.sid)
2108 self.outf.write("Type: %s\n" % self.trustType_string(local_tdo_info.trust_type))
2109 self.outf.write("Direction: %s\n" % self.trustDirection_string(local_tdo_info.trust_direction))
2110 self.outf.write("Attributes: %s\n" % self.trustAttributes_string(local_tdo_info.trust_attributes))
2111 posix_offset_u32 = ctypes.c_uint32(local_tdo_posix.posix_offset).value
2112 posix_offset_i32 = ctypes.c_int32(local_tdo_posix.posix_offset).value
2113 self.outf.write("PosixOffset: 0x%08X (%d)\n" % (posix_offset_u32, posix_offset_i32))
2114 self.outf.write("kerb_EncTypes: %s\n" % self.kerb_EncTypes_string(local_tdo_enctypes.enc_types))
2116 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2117 self.write_forest_trust_info(local_tdo_forest,
2118 tln=local_tdo_info.domain_name.string)
2122 class cmd_domain_trust_create(DomainTrustCommand):
2123 """Create a domain or forest trust."""
2125 synopsis = "%prog DOMAIN [options]"
2127 takes_optiongroups = {
2128 "sambaopts": options.SambaOptions,
2129 "versionopts": options.VersionOptions,
2130 "credopts": options.CredentialsOptions,
2131 "localdcopts": LocalDCCredentialsOptions,
2135 Option("--type", type="choice", metavar="TYPE",
2136 choices=["external", "forest"],
2137 help="The type of the trust: 'external' or 'forest'.",
2139 default="external"),
2140 Option("--direction", type="choice", metavar="DIRECTION",
2141 choices=["incoming", "outgoing", "both"],
2142 help="The trust direction: 'incoming', 'outgoing' or 'both'.",
2143 dest='trust_direction',
2145 Option("--create-location", type="choice", metavar="LOCATION",
2146 choices=["local", "both"],
2147 help="Where to create the trusted domain object: 'local' or 'both'.",
2148 dest='create_location',
2150 Option("--cross-organisation", action="store_true",
2151 help="The related domains does not belong to the same organisation.",
2152 dest='cross_organisation',
2154 Option("--quarantined", type="choice", metavar="yes|no",
2155 choices=["yes", "no", None],
2156 help="Special SID filtering rules are applied to the trust. "
2157 "With --type=external the default is yes. "
2158 "With --type=forest the default is no.",
2159 dest='quarantined_arg',
2161 Option("--not-transitive", action="store_true",
2162 help="The forest trust is not transitive.",
2163 dest='not_transitive',
2165 Option("--treat-as-external", action="store_true",
2166 help="The treat the forest trust as external.",
2167 dest='treat_as_external',
2169 Option("--no-aes-keys", action="store_false",
2170 help="The trust uses aes kerberos keys.",
2171 dest='use_aes_keys',
2173 Option("--skip-validation", action="store_false",
2174 help="Skip validation of the trust.",
2179 takes_args = ["domain"]
2181 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2182 trust_type=None, trust_direction=None, create_location=None,
2183 cross_organisation=False, quarantined_arg=None,
2184 not_transitive=False, treat_as_external=False,
2185 use_aes_keys=False, validate=True):
2187 lsaString = lsa.String()
2190 if quarantined_arg is None:
2191 if trust_type == 'external':
2193 elif quarantined_arg == 'yes':
2196 if trust_type != 'forest':
2198 raise CommandError("--not-transitive requires --type=forest")
2199 if treat_as_external:
2200 raise CommandError("--treat-as-external requires --type=forest")
2204 enc_types = lsa.TrustDomainInfoSupportedEncTypes()
2205 enc_types.enc_types = security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96
2206 enc_types.enc_types |= security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96
2208 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2209 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2210 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2212 local_trust_info = lsa.TrustDomainInfoInfoEx()
2213 local_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2214 local_trust_info.trust_direction = 0
2215 if trust_direction == "both":
2216 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2217 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2218 elif trust_direction == "incoming":
2219 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2220 elif trust_direction == "outgoing":
2221 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2222 local_trust_info.trust_attributes = 0
2223 if cross_organisation:
2224 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2226 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2227 if trust_type == "forest":
2228 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2230 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2231 if treat_as_external:
2232 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2234 def get_password(name):
2237 if password is not None and password is not '':
2239 password = getpass("New %s Password: " % name)
2240 passwordverify = getpass("Retype %s Password: " % name)
2241 if not password == passwordverify:
2243 self.outf.write("Sorry, passwords do not match.\n")
2245 incoming_secret = None
2246 outgoing_secret = None
2247 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2248 if create_location == "local":
2249 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2250 incoming_password = get_password("Incoming Trust")
2251 incoming_secret = string_to_byte_array(incoming_password.encode('utf-16-le'))
2252 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2253 outgoing_password = get_password("Outgoing Trust")
2254 outgoing_secret = string_to_byte_array(outgoing_password.encode('utf-16-le'))
2256 remote_trust_info = None
2258 # We use 240 random bytes.
2259 # Windows uses 28 or 240 random bytes. I guess it's
2260 # based on the trust type external vs. forest.
2262 # The initial trust password can be up to 512 bytes
2263 # while the versioned passwords used for periodic updates
2264 # can only be up to 498 bytes, as netr_ServerPasswordSet2()
2265 # needs to pass the NL_PASSWORD_VERSION structure within the
2266 # 512 bytes and a 2 bytes confounder is required.
2268 def random_trust_secret(length, use_aes_keys=True):
2269 secret = [0] * length
2271 pw1 = samba.generate_random_password(length/2, length/2)
2272 if not use_aes_keys:
2273 # With arcfour-hmac-md5 we have to use valid utf16
2274 # in order to generate the correct pre-auth key
2275 # based on a utf8 password.
2277 # We can remove this once our client libraries
2278 # support using the correct NTHASH.
2279 return string_to_byte_array(pw1.encode('utf-16-le'))
2281 # We mix characters from generate_random_password
2282 # with random numbers from random.randint()
2283 for i in range(len(secret)):
2285 secret[i] = ord(pw1[i])
2287 secret[i] = random.randint(0, 255)
2291 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2292 incoming_secret = random_trust_secret(240, use_aes_keys=use_aes_keys)
2293 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2294 outgoing_secret = random_trust_secret(240, use_aes_keys=use_aes_keys)
2296 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2297 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2299 remote_trust_info = lsa.TrustDomainInfoInfoEx()
2300 remote_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2301 remote_trust_info.trust_direction = 0
2302 if trust_direction == "both":
2303 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2304 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2305 elif trust_direction == "incoming":
2306 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2307 elif trust_direction == "outgoing":
2308 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2309 remote_trust_info.trust_attributes = 0
2310 if cross_organisation:
2311 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2313 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2314 if trust_type == "forest":
2315 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2317 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2318 if treat_as_external:
2319 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2321 local_server = self.setup_local_server(sambaopts, localdcopts)
2323 local_lsa = self.new_local_lsa_connection()
2324 except RuntimeError as error:
2325 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2328 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2329 except RuntimeError as error:
2330 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2332 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2333 local_lsa_info.name.string,
2334 local_lsa_info.dns_domain.string,
2335 local_lsa_info.sid))
2338 remote_server = self.setup_remote_server(credopts, domain)
2339 except RuntimeError as error:
2340 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2343 remote_lsa = self.new_remote_lsa_connection()
2344 except RuntimeError as error:
2345 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2348 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2349 except RuntimeError as error:
2350 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2352 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2353 remote_lsa_info.name.string,
2354 remote_lsa_info.dns_domain.string,
2355 remote_lsa_info.sid))
2357 local_trust_info.domain_name.string = remote_lsa_info.dns_domain.string
2358 local_trust_info.netbios_name.string = remote_lsa_info.name.string
2359 local_trust_info.sid = remote_lsa_info.sid
2361 if remote_trust_info:
2362 remote_trust_info.domain_name.string = local_lsa_info.dns_domain.string
2363 remote_trust_info.netbios_name.string = local_lsa_info.name.string
2364 remote_trust_info.sid = local_lsa_info.sid
2367 lsaString.string = local_trust_info.domain_name.string
2368 local_old_netbios = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2369 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2370 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2371 except NTSTATUSError as error:
2372 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2373 raise self.LocalRuntimeError(self, error,
2374 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2378 lsaString.string = local_trust_info.netbios_name.string
2379 local_old_dns = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2380 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2381 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2382 except NTSTATUSError as error:
2383 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2384 raise self.LocalRuntimeError(self, error,
2385 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2388 if remote_trust_info:
2390 lsaString.string = remote_trust_info.domain_name.string
2391 remote_old_netbios = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2392 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2393 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2394 except NTSTATUSError as error:
2395 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2396 raise self.RemoteRuntimeError(self, error,
2397 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2401 lsaString.string = remote_trust_info.netbios_name.string
2402 remote_old_dns = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2403 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2404 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2405 except NTSTATUSError as error:
2406 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2407 raise self.RemoteRuntimeError(self, error,
2408 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2412 local_netlogon = self.new_local_netlogon_connection()
2413 except RuntimeError as error:
2414 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2417 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
2418 except RuntimeError as error:
2419 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
2421 if remote_trust_info:
2423 remote_netlogon = self.new_remote_netlogon_connection()
2424 except RuntimeError as error:
2425 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2428 remote_netlogon_info = self.get_netlogon_dc_info(remote_netlogon, remote_server)
2429 except RuntimeError as error:
2430 raise self.RemoteRuntimeError(self, error, "failed to get netlogon dc info")
2432 def generate_AuthInOutBlob(secret, update_time):
2434 blob = drsblobs.trustAuthInOutBlob()
2439 clear = drsblobs.AuthInfoClear()
2440 clear.size = len(secret)
2441 clear.password = secret
2443 info = drsblobs.AuthenticationInformation()
2444 info.LastUpdateTime = samba.unix2nttime(update_time)
2445 info.AuthType = lsa.TRUST_AUTH_TYPE_CLEAR
2446 info.AuthInfo = clear
2448 array = drsblobs.AuthenticationInformationArray()
2450 array.array = [info]
2452 blob = drsblobs.trustAuthInOutBlob()
2454 blob.current = array
2458 def generate_AuthInfoInternal(session_key, incoming=None, outgoing=None):
2459 confounder = [0] * 512
2460 for i in range(len(confounder)):
2461 confounder[i] = random.randint(0, 255)
2463 trustpass = drsblobs.trustDomainPasswords()
2465 trustpass.confounder = confounder
2466 trustpass.outgoing = outgoing
2467 trustpass.incoming = incoming
2469 trustpass_blob = ndr_pack(trustpass)
2471 encrypted_trustpass = arcfour_encrypt(session_key, trustpass_blob)
2473 auth_blob = lsa.DATA_BUF2()
2474 auth_blob.size = len(encrypted_trustpass)
2475 auth_blob.data = string_to_byte_array(encrypted_trustpass)
2477 auth_info = lsa.TrustDomainInfoAuthInfoInternal()
2478 auth_info.auth_blob = auth_blob
2482 update_time = samba.current_unix_time()
2483 incoming_blob = generate_AuthInOutBlob(incoming_secret, update_time)
2484 outgoing_blob = generate_AuthInOutBlob(outgoing_secret, update_time)
2486 local_tdo_handle = None
2487 remote_tdo_handle = None
2489 local_auth_info = generate_AuthInfoInternal(local_lsa.session_key,
2490 incoming=incoming_blob,
2491 outgoing=outgoing_blob)
2492 if remote_trust_info:
2493 remote_auth_info = generate_AuthInfoInternal(remote_lsa.session_key,
2494 incoming=outgoing_blob,
2495 outgoing=incoming_blob)
2498 if remote_trust_info:
2499 self.outf.write("Creating remote TDO.\n")
2500 current_request = { "location": "remote", "name": "CreateTrustedDomainEx2"}
2501 remote_tdo_handle = remote_lsa.CreateTrustedDomainEx2(remote_policy,
2504 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2505 self.outf.write("Remote TDO created.\n")
2507 self.outf.write("Setting supported encryption types on remote TDO.\n")
2508 current_request = { "location": "remote", "name": "SetInformationTrustedDomain"}
2509 remote_lsa.SetInformationTrustedDomain(remote_tdo_handle,
2510 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2513 self.outf.write("Creating local TDO.\n")
2514 current_request = { "location": "local", "name": "CreateTrustedDomainEx2"}
2515 local_tdo_handle = local_lsa.CreateTrustedDomainEx2(local_policy,
2518 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2519 self.outf.write("Local TDO created\n")
2521 self.outf.write("Setting supported encryption types on local TDO.\n")
2522 current_request = { "location": "local", "name": "SetInformationTrustedDomain"}
2523 local_lsa.SetInformationTrustedDomain(local_tdo_handle,
2524 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2526 except RuntimeError as error:
2527 self.outf.write("Error: %s failed %sly - cleaning up\n" % (
2528 current_request['name'], current_request['location']))
2529 if remote_tdo_handle:
2530 self.outf.write("Deleting remote TDO.\n")
2531 remote_lsa.DeleteObject(remote_tdo_handle)
2532 remote_tdo_handle = None
2533 if local_tdo_handle:
2534 self.outf.write("Deleting local TDO.\n")
2535 local_lsa.DeleteObject(local_tdo_handle)
2536 local_tdo_handle = None
2537 if current_request['location'] is "remote":
2538 raise self.RemoteRuntimeError(self, error, "%s" % (
2539 current_request['name']))
2540 raise self.LocalRuntimeError(self, error, "%s" % (
2541 current_request['name']))
2544 if local_trust_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2545 self.outf.write("Setup local forest trust information...\n")
2547 # get all information about the remote trust
2548 # this triggers netr_GetForestTrustInformation to the remote domain
2549 # and lsaRSetForestTrustInformation() locally, but new top level
2550 # names are disabled by default.
2551 local_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
2552 remote_lsa_info.dns_domain.string,
2553 netlogon.DS_GFTI_UPDATE_TDO)
2554 except RuntimeError as error:
2555 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2558 # here we try to enable all top level names
2559 local_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
2560 remote_lsa_info.dns_domain,
2561 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2564 except RuntimeError as error:
2565 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2567 self.write_forest_trust_info(local_forest_info,
2568 tln=remote_lsa_info.dns_domain.string,
2569 collisions=local_forest_collision)
2571 if remote_trust_info:
2572 self.outf.write("Setup remote forest trust information...\n")
2574 # get all information about the local trust (from the perspective of the remote domain)
2575 # this triggers netr_GetForestTrustInformation to our domain.
2576 # and lsaRSetForestTrustInformation() remotely, but new top level
2577 # names are disabled by default.
2578 remote_forest_info = remote_netlogon.netr_DsRGetForestTrustInformation(remote_netlogon_info.dc_unc,
2579 local_lsa_info.dns_domain.string,
2580 netlogon.DS_GFTI_UPDATE_TDO)
2581 except RuntimeError as error:
2582 raise self.RemoteRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2585 # here we try to enable all top level names
2586 remote_forest_collision = remote_lsa.lsaRSetForestTrustInformation(remote_policy,
2587 local_lsa_info.dns_domain,
2588 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2591 except RuntimeError as error:
2592 raise self.RemoteRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2594 self.write_forest_trust_info(remote_forest_info,
2595 tln=local_lsa_info.dns_domain.string,
2596 collisions=remote_forest_collision)
2598 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2599 self.outf.write("Validating outgoing trust...\n")
2601 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_netlogon_info.dc_unc,
2602 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2604 remote_lsa_info.dns_domain.string)
2605 except RuntimeError as error:
2606 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2608 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2609 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2611 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2612 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2613 local_trust_verify.trusted_dc_name,
2614 local_trust_verify.tc_connection_status[1],
2615 local_trust_verify.pdc_connection_status[1])
2617 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2618 local_trust_verify.trusted_dc_name,
2619 local_trust_verify.tc_connection_status[1],
2620 local_trust_verify.pdc_connection_status[1])
2622 if local_trust_status != werror.WERR_SUCCESS or local_conn_status != werror.WERR_SUCCESS:
2623 raise CommandError(local_validation)
2625 self.outf.write("OK: %s\n" % local_validation)
2627 if remote_trust_info:
2628 if remote_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2629 self.outf.write("Validating incoming trust...\n")
2631 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_netlogon_info.dc_unc,
2632 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2634 local_lsa_info.dns_domain.string)
2635 except RuntimeError as error:
2636 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2638 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2639 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2641 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2642 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2643 remote_trust_verify.trusted_dc_name,
2644 remote_trust_verify.tc_connection_status[1],
2645 remote_trust_verify.pdc_connection_status[1])
2647 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2648 remote_trust_verify.trusted_dc_name,
2649 remote_trust_verify.tc_connection_status[1],
2650 remote_trust_verify.pdc_connection_status[1])
2652 if remote_trust_status != werror.WERR_SUCCESS or remote_conn_status != werror.WERR_SUCCESS:
2653 raise CommandError(remote_validation)
2655 self.outf.write("OK: %s\n" % remote_validation)
2657 if remote_tdo_handle is not None:
2659 remote_lsa.Close(remote_tdo_handle)
2660 except RuntimeError as error:
2662 remote_tdo_handle = None
2663 if local_tdo_handle is not None:
2665 local_lsa.Close(local_tdo_handle)
2666 except RuntimeError as error:
2668 local_tdo_handle = None
2670 self.outf.write("Success.\n")
2673 class cmd_domain_trust_delete(DomainTrustCommand):
2674 """Delete a domain trust."""
2676 synopsis = "%prog DOMAIN [options]"
2678 takes_optiongroups = {
2679 "sambaopts": options.SambaOptions,
2680 "versionopts": options.VersionOptions,
2681 "credopts": options.CredentialsOptions,
2682 "localdcopts": LocalDCCredentialsOptions,
2686 Option("--delete-location", type="choice", metavar="LOCATION",
2687 choices=["local", "both"],
2688 help="Where to delete the trusted domain object: 'local' or 'both'.",
2689 dest='delete_location',
2693 takes_args = ["domain"]
2695 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2696 delete_location=None):
2698 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2699 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2700 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2702 if delete_location == "local":
2703 remote_policy_access = None
2705 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2706 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2707 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2709 local_server = self.setup_local_server(sambaopts, localdcopts)
2711 local_lsa = self.new_local_lsa_connection()
2712 except RuntimeError as error:
2713 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2716 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2717 except RuntimeError as error:
2718 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2720 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2721 local_lsa_info.name.string,
2722 local_lsa_info.dns_domain.string,
2723 local_lsa_info.sid))
2725 local_tdo_info = None
2726 local_tdo_handle = None
2727 remote_tdo_info = None
2728 remote_tdo_handle = None
2730 lsaString = lsa.String()
2732 lsaString.string = domain
2733 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2734 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2735 except NTSTATUSError as error:
2736 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2737 raise CommandError("Failed to find trust for domain '%s'" % domain)
2738 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2741 if remote_policy_access is not None:
2743 remote_server = self.setup_remote_server(credopts, domain)
2744 except RuntimeError as error:
2745 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2748 remote_lsa = self.new_remote_lsa_connection()
2749 except RuntimeError as error:
2750 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2753 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2754 except RuntimeError as error:
2755 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2757 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2758 remote_lsa_info.name.string,
2759 remote_lsa_info.dns_domain.string,
2760 remote_lsa_info.sid))
2762 if remote_lsa_info.sid != local_tdo_info.sid or \
2763 remote_lsa_info.name.string != local_tdo_info.netbios_name.string or \
2764 remote_lsa_info.dns_domain.string != local_tdo_info.domain_name.string:
2765 raise CommandError("LocalTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2766 local_tdo_info.netbios_name.string,
2767 local_tdo_info.domain_name.string,
2768 local_tdo_info.sid))
2771 lsaString.string = local_lsa_info.dns_domain.string
2772 remote_tdo_info = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2773 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2774 except NTSTATUSError as error:
2775 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2776 raise self.RemoteRuntimeError(self, error, "QueryTrustedDomainInfoByName(%s)" % (
2780 if remote_tdo_info is not None:
2781 if local_lsa_info.sid != remote_tdo_info.sid or \
2782 local_lsa_info.name.string != remote_tdo_info.netbios_name.string or \
2783 local_lsa_info.dns_domain.string != remote_tdo_info.domain_name.string:
2784 raise CommandError("RemoteTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2785 remote_tdo_info.netbios_name.string,
2786 remote_tdo_info.domain_name.string,
2787 remote_tdo_info.sid))
2789 if local_tdo_info is not None:
2791 lsaString.string = local_tdo_info.domain_name.string
2792 local_tdo_handle = local_lsa.OpenTrustedDomainByName(local_policy,
2794 security.SEC_STD_DELETE)
2795 except RuntimeError as error:
2796 raise self.LocalRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2799 local_lsa.DeleteObject(local_tdo_handle)
2800 local_tdo_handle = None
2802 if remote_tdo_info is not None:
2804 lsaString.string = remote_tdo_info.domain_name.string
2805 remote_tdo_handle = remote_lsa.OpenTrustedDomainByName(remote_policy,
2807 security.SEC_STD_DELETE)
2808 except RuntimeError as error:
2809 raise self.RemoteRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2812 if remote_tdo_handle is not None:
2814 remote_lsa.DeleteObject(remote_tdo_handle)
2815 remote_tdo_handle = None
2816 self.outf.write("RemoteTDO deleted.\n")
2817 except RuntimeError as error:
2818 self.outf.write("%s\n" % self.RemoteRuntimeError(self, error, "DeleteObject() failed"))
2820 if local_tdo_handle is not None:
2822 local_lsa.DeleteObject(local_tdo_handle)
2823 local_tdo_handle = None
2824 self.outf.write("LocalTDO deleted.\n")
2825 except RuntimeError as error:
2826 self.outf.write("%s\n" % self.LocalRuntimeError(self, error, "DeleteObject() failed"))
2830 class cmd_domain_trust_validate(DomainTrustCommand):
2831 """Validate a domain trust."""
2833 synopsis = "%prog DOMAIN [options]"
2835 takes_optiongroups = {
2836 "sambaopts": options.SambaOptions,
2837 "versionopts": options.VersionOptions,
2838 "credopts": options.CredentialsOptions,
2839 "localdcopts": LocalDCCredentialsOptions,
2843 Option("--validate-location", type="choice", metavar="LOCATION",
2844 choices=["local", "both"],
2845 help="Where to validate the trusted domain object: 'local' or 'both'.",
2846 dest='validate_location',
2850 takes_args = ["domain"]
2852 def run(self, domain, sambaopts=None, versionopts=None, credopts=None, localdcopts=None,
2853 validate_location=None):
2855 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2857 local_server = self.setup_local_server(sambaopts, localdcopts)
2859 local_lsa = self.new_local_lsa_connection()
2860 except RuntimeError as error:
2861 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2864 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2865 except RuntimeError as error:
2866 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2868 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2869 local_lsa_info.name.string,
2870 local_lsa_info.dns_domain.string,
2871 local_lsa_info.sid))
2874 lsaString = lsa.String()
2875 lsaString.string = domain
2876 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2877 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2878 except NTSTATUSError as error:
2879 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2880 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2882 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
2884 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
2885 local_tdo_info.netbios_name.string,
2886 local_tdo_info.domain_name.string,
2887 local_tdo_info.sid))
2890 local_netlogon = self.new_local_netlogon_connection()
2891 except RuntimeError as error:
2892 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2895 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_server,
2896 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2898 local_tdo_info.domain_name.string)
2899 except RuntimeError as error:
2900 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2902 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2903 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2905 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2906 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2907 local_trust_verify.trusted_dc_name,
2908 local_trust_verify.tc_connection_status[1],
2909 local_trust_verify.pdc_connection_status[1])
2911 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2912 local_trust_verify.trusted_dc_name,
2913 local_trust_verify.tc_connection_status[1],
2914 local_trust_verify.pdc_connection_status[1])
2916 if local_trust_status != werror.WERR_SUCCESS or local_conn_status != werror.WERR_SUCCESS:
2917 raise CommandError(local_validation)
2919 self.outf.write("OK: %s\n" % local_validation)
2922 server = local_trust_verify.trusted_dc_name.replace('\\', '')
2923 domain_and_server = "%s\\%s" % (local_tdo_info.domain_name.string, server)
2924 local_trust_rediscover = local_netlogon.netr_LogonControl2Ex(local_server,
2925 netlogon.NETLOGON_CONTROL_REDISCOVER,
2928 except RuntimeError as error:
2929 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
2931 local_conn_status = self._uint32(local_trust_rediscover.tc_connection_status[0])
2932 local_rediscover = "LocalRediscover: DC[%s] CONNECTION[%s]" % (
2933 local_trust_rediscover.trusted_dc_name,
2934 local_trust_rediscover.tc_connection_status[1])
2936 if local_conn_status != werror.WERR_SUCCESS:
2937 raise CommandError(local_rediscover)
2939 self.outf.write("OK: %s\n" % local_rediscover)
2941 if validate_location != "local":
2943 remote_server = self.setup_remote_server(credopts, domain, require_pdc=False)
2944 except RuntimeError as error:
2945 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2948 remote_netlogon = self.new_remote_netlogon_connection()
2949 except RuntimeError as error:
2950 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2953 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_server,
2954 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2956 local_lsa_info.dns_domain.string)
2957 except RuntimeError as error:
2958 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2960 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2961 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2963 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2964 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2965 remote_trust_verify.trusted_dc_name,
2966 remote_trust_verify.tc_connection_status[1],
2967 remote_trust_verify.pdc_connection_status[1])
2969 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2970 remote_trust_verify.trusted_dc_name,
2971 remote_trust_verify.tc_connection_status[1],
2972 remote_trust_verify.pdc_connection_status[1])
2974 if remote_trust_status != werror.WERR_SUCCESS or remote_conn_status != werror.WERR_SUCCESS:
2975 raise CommandError(remote_validation)
2977 self.outf.write("OK: %s\n" % remote_validation)
2980 server = remote_trust_verify.trusted_dc_name.replace('\\', '')
2981 domain_and_server = "%s\\%s" % (local_lsa_info.dns_domain.string, server)
2982 remote_trust_rediscover = remote_netlogon.netr_LogonControl2Ex(remote_server,
2983 netlogon.NETLOGON_CONTROL_REDISCOVER,
2986 except RuntimeError as error:
2987 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
2989 remote_conn_status = self._uint32(remote_trust_rediscover.tc_connection_status[0])
2991 remote_rediscover = "RemoteRediscover: DC[%s] CONNECTION[%s]" % (
2992 remote_trust_rediscover.trusted_dc_name,
2993 remote_trust_rediscover.tc_connection_status[1])
2995 if remote_conn_status != werror.WERR_SUCCESS:
2996 raise CommandError(remote_rediscover)
2998 self.outf.write("OK: %s\n" % remote_rediscover)
3002 class cmd_domain_trust_namespaces(DomainTrustCommand):
3003 """Manage forest trust namespaces."""
3005 synopsis = "%prog [DOMAIN] [options]"
3007 takes_optiongroups = {
3008 "sambaopts": options.SambaOptions,
3009 "versionopts": options.VersionOptions,
3010 "localdcopts": LocalDCCredentialsOptions,
3014 Option("--refresh", type="choice", metavar="check|store",
3015 choices=["check", "store", None],
3016 help="List and maybe store refreshed forest trust information: 'check' or 'store'.",
3019 Option("--enable-all", action="store_true",
3020 help="Try to update disabled entries, not allowed with --refresh=check.",
3023 Option("--enable-tln", action="append", metavar='DNSDOMAIN',
3024 help="Enable a top level name entry. Can be specified multiple times.",
3027 Option("--disable-tln", action="append", metavar='DNSDOMAIN',
3028 help="Disable a top level name entry. Can be specified multiple times.",
3031 Option("--add-tln-ex", action="append", metavar='DNSDOMAIN',
3032 help="Add a top level exclusion entry. Can be specified multiple times.",
3035 Option("--delete-tln-ex", action="append", metavar='DNSDOMAIN',
3036 help="Delete a top level exclusion entry. Can be specified multiple times.",
3037 dest='delete_tln_ex',
3039 Option("--enable-nb", action="append", metavar='NETBIOSDOMAIN',
3040 help="Enable a netbios name in a domain entry. Can be specified multiple times.",
3043 Option("--disable-nb", action="append", metavar='NETBIOSDOMAIN',
3044 help="Disable a netbios name in a domain entry. Can be specified multiple times.",
3047 Option("--enable-sid", action="append", metavar='DOMAINSID',
3048 help="Enable a SID in a domain entry. Can be specified multiple times.",
3049 dest='enable_sid_str',
3051 Option("--disable-sid", action="append", metavar='DOMAINSID',
3052 help="Disable a SID in a domain entry. Can be specified multiple times.",
3053 dest='disable_sid_str',
3055 Option("--add-upn-suffix", action="append", metavar='DNSDOMAIN',
3056 help="Add a new uPNSuffixes attribute for the local forest. Can be specified multiple times.",
3059 Option("--delete-upn-suffix", action="append", metavar='DNSDOMAIN',
3060 help="Delete an existing uPNSuffixes attribute of the local forest. Can be specified multiple times.",
3063 Option("--add-spn-suffix", action="append", metavar='DNSDOMAIN',
3064 help="Add a new msDS-SPNSuffixes attribute for the local forest. Can be specified multiple times.",
3067 Option("--delete-spn-suffix", action="append", metavar='DNSDOMAIN',
3068 help="Delete an existing msDS-SPNSuffixes attribute of the local forest. Can be specified multiple times.",
3073 takes_args = ["domain?"]
3075 def run(self, domain=None, sambaopts=None, localdcopts=None, versionopts=None,
3076 refresh=None, enable_all=False,
3077 enable_tln=[], disable_tln=[], add_tln_ex=[], delete_tln_ex=[],
3078 enable_sid_str=[], disable_sid_str=[], enable_nb=[], disable_nb=[],
3079 add_upn=[], delete_upn=[], add_spn=[], delete_spn=[]):
3081 require_update = False
3084 if refresh == "store":
3085 raise CommandError("--refresh=%s not allowed without DOMAIN" % refresh)
3088 raise CommandError("--enable-all not allowed without DOMAIN")
3090 if len(enable_tln) > 0:
3091 raise CommandError("--enable-tln not allowed without DOMAIN")
3092 if len(disable_tln) > 0:
3093 raise CommandError("--disable-tln not allowed without DOMAIN")
3095 if len(add_tln_ex) > 0:
3096 raise CommandError("--add-tln-ex not allowed without DOMAIN")
3097 if len(delete_tln_ex) > 0:
3098 raise CommandError("--delete-tln-ex not allowed without DOMAIN")
3100 if len(enable_nb) > 0:
3101 raise CommandError("--enable-nb not allowed without DOMAIN")
3102 if len(disable_nb) > 0:
3103 raise CommandError("--disable-nb not allowed without DOMAIN")
3105 if len(enable_sid_str) > 0:
3106 raise CommandError("--enable-sid not allowed without DOMAIN")
3107 if len(disable_sid_str) > 0:
3108 raise CommandError("--disable-sid not allowed without DOMAIN")
3110 if len(add_upn) > 0:
3112 if not n.startswith("*."):
3114 raise CommandError("value[%s] specified for --add-upn-suffix should not include with '*.'" % n)
3115 require_update = True
3116 if len(delete_upn) > 0:
3117 for n in delete_upn:
3118 if not n.startswith("*."):
3120 raise CommandError("value[%s] specified for --delete-upn-suffix should not include with '*.'" % n)
3121 require_update = True
3123 for d in delete_upn:
3124 if a.lower() != d.lower():
3126 raise CommandError("value[%s] specified for --add-upn-suffix and --delete-upn-suffix" % a)
3128 if len(add_spn) > 0:
3130 if not n.startswith("*."):
3132 raise CommandError("value[%s] specified for --add-spn-suffix should not include with '*.'" % n)
3133 require_update = True
3134 if len(delete_spn) > 0:
3135 for n in delete_spn:
3136 if not n.startswith("*."):
3138 raise CommandError("value[%s] specified for --delete-spn-suffix should not include with '*.'" % n)
3139 require_update = True
3141 for d in delete_spn:
3142 if a.lower() != d.lower():
3144 raise CommandError("value[%s] specified for --add-spn-suffix and --delete-spn-suffix" % a)
3146 if len(add_upn) > 0:
3147 raise CommandError("--add-upn-suffix not allowed together with DOMAIN")
3148 if len(delete_upn) > 0:
3149 raise CommandError("--delete-upn-suffix not allowed together with DOMAIN")
3150 if len(add_spn) > 0:
3151 raise CommandError("--add-spn-suffix not allowed together with DOMAIN")
3152 if len(delete_spn) > 0:
3153 raise CommandError("--delete-spn-suffix not allowed together with DOMAIN")
3155 if refresh is not None:
3156 if refresh == "store":
3157 require_update = True
3159 if enable_all and refresh != "store":
3160 raise CommandError("--enable-all not allowed together with --refresh=%s" % refresh)
3162 if len(enable_tln) > 0:
3163 raise CommandError("--enable-tln not allowed together with --refresh")
3164 if len(disable_tln) > 0:
3165 raise CommandError("--disable-tln not allowed together with --refresh")
3167 if len(add_tln_ex) > 0:
3168 raise CommandError("--add-tln-ex not allowed together with --refresh")
3169 if len(delete_tln_ex) > 0:
3170 raise CommandError("--delete-tln-ex not allowed together with --refresh")
3172 if len(enable_nb) > 0:
3173 raise CommandError("--enable-nb not allowed together with --refresh")
3174 if len(disable_nb) > 0:
3175 raise CommandError("--disable-nb not allowed together with --refresh")
3177 if len(enable_sid_str) > 0:
3178 raise CommandError("--enable-sid not allowed together with --refresh")
3179 if len(disable_sid_str) > 0:
3180 raise CommandError("--disable-sid not allowed together with --refresh")
3183 require_update = True
3185 if len(enable_tln) > 0:
3186 raise CommandError("--enable-tln not allowed together with --enable-all")
3188 if len(enable_nb) > 0:
3189 raise CommandError("--enable-nb not allowed together with --enable-all")
3191 if len(enable_sid) > 0:
3192 raise CommandError("--enable-sid not allowed together with --enable-all")
3194 if len(enable_tln) > 0:
3195 require_update = True
3196 if len(disable_tln) > 0:
3197 require_update = True
3198 for e in enable_tln:
3199 for d in disable_tln:
3200 if e.lower() != d.lower():
3202 raise CommandError("value[%s] specified for --enable-tln and --disable-tln" % e)
3204 if len(add_tln_ex) > 0:
3205 for n in add_tln_ex:
3206 if not n.startswith("*."):
3208 raise CommandError("value[%s] specified for --add-tln-ex should not include with '*.'" % n)
3209 require_update = True
3210 if len(delete_tln_ex) > 0:
3211 for n in delete_tln_ex:
3212 if not n.startswith("*."):
3214 raise CommandError("value[%s] specified for --delete-tln-ex should not include with '*.'" % n)
3215 require_update = True
3216 for a in add_tln_ex:
3217 for d in delete_tln_ex:
3218 if a.lower() != d.lower():
3220 raise CommandError("value[%s] specified for --add-tln-ex and --delete-tln-ex" % a)
3222 if len(enable_nb) > 0:
3223 require_update = True
3224 if len(disable_nb) > 0:
3225 require_update = True
3227 for d in disable_nb:
3228 if e.upper() != d.upper():
3230 raise CommandError("value[%s] specified for --enable-nb and --disable-nb" % e)
3233 for s in enable_sid_str:
3235 sid = security.dom_sid(s)
3236 except TypeError as error:
3237 raise CommandError("value[%s] specified for --enable-sid is not a valid SID" % s)
3238 enable_sid.append(sid)
3240 for s in disable_sid_str:
3242 sid = security.dom_sid(s)
3243 except TypeError as error:
3244 raise CommandError("value[%s] specified for --disable-sid is not a valid SID" % s)
3245 disable_sid.append(sid)
3246 if len(enable_sid) > 0:
3247 require_update = True
3248 if len(disable_sid) > 0:
3249 require_update = True
3250 for e in enable_sid:
3251 for d in disable_sid:
3254 raise CommandError("value[%s] specified for --enable-sid and --disable-sid" % e)
3256 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
3258 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
3260 local_server = self.setup_local_server(sambaopts, localdcopts)
3262 local_lsa = self.new_local_lsa_connection()
3263 except RuntimeError as error:
3264 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
3267 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
3268 except RuntimeError as error:
3269 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
3271 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
3272 local_lsa_info.name.string,
3273 local_lsa_info.dns_domain.string,
3274 local_lsa_info.sid))
3278 local_netlogon = self.new_local_netlogon_connection()
3279 except RuntimeError as error:
3280 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3283 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3284 except RuntimeError as error:
3285 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3287 if local_netlogon_info.domain_name != local_netlogon_info.forest_name:
3288 raise CommandError("The local domain [%s] is not the forest root [%s]" % (
3289 local_netlogon_info.domain_name,
3290 local_netlogon_info.forest_name))
3293 # get all information about our own forest
3294 own_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3296 except RuntimeError as error:
3297 if self.check_runtime_error(error, werror.WERR_RPC_S_PROCNUM_OUT_OF_RANGE):
3298 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3301 if self.check_runtime_error(error, werror.WERR_INVALID_FUNCTION):
3302 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3305 if self.check_runtime_error(error, werror.WERR_NERR_ACFNOTLOADED):
3306 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3309 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3311 self.outf.write("Own forest trust information...\n")
3312 self.write_forest_trust_info(own_forest_info,
3313 tln=local_lsa_info.dns_domain.string)
3316 local_samdb = self.new_local_ldap_connection()
3317 except RuntimeError as error:
3318 raise self.LocalRuntimeError(self, error, "failed to connect to SamDB")
3320 local_partitions_dn = "CN=Partitions,%s" % str(local_samdb.get_config_basedn())
3321 attrs = ['uPNSuffixes', 'msDS-SPNSuffixes']
3323 msgs = local_samdb.search(base=local_partitions_dn,
3324 scope=ldb.SCOPE_BASE,
3325 expression="(objectClass=crossRefContainer)",
3327 stored_msg = msgs[0]
3328 except ldb.LdbError as error:
3329 raise self.LocalLdbError(self, error, "failed to search partition dn")
3331 stored_upn_vals = []
3332 if 'uPNSuffixes' in stored_msg:
3333 stored_upn_vals.extend(stored_msg['uPNSuffixes'])
3335 stored_spn_vals = []
3336 if 'msDS-SPNSuffixes' in stored_msg:
3337 stored_spn_vals.extend(stored_msg['msDS-SPNSuffixes'])
3339 self.outf.write("Stored uPNSuffixes attributes[%d]:\n" % len(stored_upn_vals))
3340 for v in stored_upn_vals:
3341 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3342 self.outf.write("Stored msDS-SPNSuffixes attributes[%d]:\n" % len(stored_spn_vals))
3343 for v in stored_spn_vals:
3344 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3346 if not require_update:
3350 update_upn_vals = []
3351 update_upn_vals.extend(stored_upn_vals)
3354 update_spn_vals = []
3355 update_spn_vals.extend(stored_spn_vals)
3359 for i in xrange(0, len(update_upn_vals)):
3360 v = update_upn_vals[i]
3361 if v.lower() != upn.lower():
3366 raise CommandError("Entry already present for value[%s] specified for --add-upn-suffix" % upn)
3367 update_upn_vals.append(upn)
3370 for upn in delete_upn:
3372 for i in xrange(0, len(update_upn_vals)):
3373 v = update_upn_vals[i]
3374 if v.lower() != upn.lower():
3379 raise CommandError("Entry not found for value[%s] specified for --delete-upn-suffix" % upn)
3381 update_upn_vals.pop(idx)
3386 for i in xrange(0, len(update_spn_vals)):
3387 v = update_spn_vals[i]
3388 if v.lower() != spn.lower():
3393 raise CommandError("Entry already present for value[%s] specified for --add-spn-suffix" % spn)
3394 update_spn_vals.append(spn)
3397 for spn in delete_spn:
3399 for i in xrange(0, len(update_spn_vals)):
3400 v = update_spn_vals[i]
3401 if v.lower() != spn.lower():
3406 raise CommandError("Entry not found for value[%s] specified for --delete-spn-suffix" % spn)
3408 update_spn_vals.pop(idx)
3411 self.outf.write("Update uPNSuffixes attributes[%d]:\n" % len(update_upn_vals))
3412 for v in update_upn_vals:
3413 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3414 self.outf.write("Update msDS-SPNSuffixes attributes[%d]:\n" % len(update_spn_vals))
3415 for v in update_spn_vals:
3416 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3418 update_msg = ldb.Message()
3419 update_msg.dn = stored_msg.dn
3422 update_msg['uPNSuffixes'] = ldb.MessageElement(update_upn_vals,
3423 ldb.FLAG_MOD_REPLACE,
3426 update_msg['msDS-SPNSuffixes'] = ldb.MessageElement(update_spn_vals,
3427 ldb.FLAG_MOD_REPLACE,
3430 local_samdb.modify(update_msg)
3431 except ldb.LdbError as error:
3432 raise self.LocalLdbError(self, error, "failed to update partition dn")
3435 stored_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3437 except RuntimeError as error:
3438 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3440 self.outf.write("Stored forest trust information...\n")
3441 self.write_forest_trust_info(stored_forest_info,
3442 tln=local_lsa_info.dns_domain.string)
3446 lsaString = lsa.String()
3447 lsaString.string = domain
3448 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
3449 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
3450 except NTSTATUSError as error:
3451 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
3452 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
3454 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
3456 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
3457 local_tdo_info.netbios_name.string,
3458 local_tdo_info.domain_name.string,
3459 local_tdo_info.sid))
3461 if not local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
3462 raise CommandError("trusted domain object for domain [%s] is not marked as FOREST_TRANSITIVE." % domain)
3464 if refresh is not None:
3466 local_netlogon = self.new_local_netlogon_connection()
3467 except RuntimeError as error:
3468 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3471 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3472 except RuntimeError as error:
3473 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3475 lsa_update_check = 1
3476 if refresh == "store":
3477 netlogon_update_tdo = netlogon.DS_GFTI_UPDATE_TDO
3479 lsa_update_check = 0
3481 netlogon_update_tdo = 0
3484 # get all information about the remote trust
3485 # this triggers netr_GetForestTrustInformation to the remote domain
3486 # and lsaRSetForestTrustInformation() locally, but new top level
3487 # names are disabled by default.
3488 fresh_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3489 local_tdo_info.domain_name.string,
3490 netlogon_update_tdo)
3491 except RuntimeError as error:
3492 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3495 fresh_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3496 local_tdo_info.domain_name,
3497 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3500 except RuntimeError as error:
3501 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3503 self.outf.write("Fresh forest trust information...\n")
3504 self.write_forest_trust_info(fresh_forest_info,
3505 tln=local_tdo_info.domain_name.string,
3506 collisions=fresh_forest_collision)
3508 if refresh == "store":
3510 lsaString = lsa.String()
3511 lsaString.string = local_tdo_info.domain_name.string
3512 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3514 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3515 except RuntimeError as error:
3516 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3518 self.outf.write("Stored forest trust information...\n")
3519 self.write_forest_trust_info(stored_forest_info,
3520 tln=local_tdo_info.domain_name.string)
3525 # The none --refresh path
3529 lsaString = lsa.String()
3530 lsaString.string = local_tdo_info.domain_name.string
3531 local_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3533 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3534 except RuntimeError as error:
3535 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3537 self.outf.write("Local forest trust information...\n")
3538 self.write_forest_trust_info(local_forest_info,
3539 tln=local_tdo_info.domain_name.string)
3541 if not require_update:
3545 entries.extend(local_forest_info.entries)
3546 update_forest_info = lsa.ForestTrustInformation()
3547 update_forest_info.count = len(entries)
3548 update_forest_info.entries = entries
3551 for i in xrange(0, len(update_forest_info.entries)):
3552 r = update_forest_info.entries[i]
3553 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3555 if update_forest_info.entries[i].flags == 0:
3557 update_forest_info.entries[i].time = 0
3558 update_forest_info.entries[i].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3559 for i in xrange(0, len(update_forest_info.entries)):
3560 r = update_forest_info.entries[i]
3561 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3563 if update_forest_info.entries[i].flags == 0:
3565 update_forest_info.entries[i].time = 0
3566 update_forest_info.entries[i].flags &= ~lsa.LSA_NB_DISABLED_MASK
3567 update_forest_info.entries[i].flags &= ~lsa.LSA_SID_DISABLED_MASK
3569 for tln in enable_tln:
3571 for i in xrange(0, len(update_forest_info.entries)):
3572 r = update_forest_info.entries[i]
3573 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3575 if r.forest_trust_data.string.lower() != tln.lower():
3580 raise CommandError("Entry not found for value[%s] specified for --enable-tln" % tln)
3581 if not update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_MASK:
3582 raise CommandError("Entry found for value[%s] specified for --enable-tln is already enabled" % tln)
3583 update_forest_info.entries[idx].time = 0
3584 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3586 for tln in disable_tln:
3588 for i in xrange(0, len(update_forest_info.entries)):
3589 r = update_forest_info.entries[i]
3590 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3592 if r.forest_trust_data.string.lower() != tln.lower():
3597 raise CommandError("Entry not found for value[%s] specified for --disable-tln" % tln)
3598 if update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_ADMIN:
3599 raise CommandError("Entry found for value[%s] specified for --disable-tln is already disabled" % tln)
3600 update_forest_info.entries[idx].time = 0
3601 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3602 update_forest_info.entries[idx].flags |= lsa.LSA_TLN_DISABLED_ADMIN
3604 for tln_ex in add_tln_ex:
3606 for i in xrange(0, len(update_forest_info.entries)):
3607 r = update_forest_info.entries[i]
3608 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3610 if r.forest_trust_data.string.lower() != tln_ex.lower():
3615 raise CommandError("Entry already present for value[%s] specified for --add-tln-ex" % tln_ex)
3617 tln_dot = ".%s" % tln_ex.lower()
3619 for i in xrange(0, len(update_forest_info.entries)):
3620 r = update_forest_info.entries[i]
3621 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3623 r_dot = ".%s" % r.forest_trust_data.string.lower()
3624 if tln_dot == r_dot:
3625 raise CommandError("TLN entry present for value[%s] specified for --add-tln-ex" % tln_ex)
3626 if not tln_dot.endswith(r_dot):
3632 raise CommandError("No TLN parent present for value[%s] specified for --add-tln-ex" % tln_ex)
3634 r = lsa.ForestTrustRecord()
3635 r.type = lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX
3638 r.forest_trust_data.string = tln_ex
3641 entries.extend(update_forest_info.entries)
3642 entries.insert(idx + 1, r)
3643 update_forest_info.count = len(entries)
3644 update_forest_info.entries = entries
3646 for tln_ex in delete_tln_ex:
3648 for i in xrange(0, len(update_forest_info.entries)):
3649 r = update_forest_info.entries[i]
3650 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3652 if r.forest_trust_data.string.lower() != tln_ex.lower():
3657 raise CommandError("Entry not found for value[%s] specified for --delete-tln-ex" % tln_ex)
3660 entries.extend(update_forest_info.entries)
3662 update_forest_info.count = len(entries)
3663 update_forest_info.entries = entries
3665 for nb in enable_nb:
3667 for i in xrange(0, len(update_forest_info.entries)):
3668 r = update_forest_info.entries[i]
3669 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3671 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3676 raise CommandError("Entry not found for value[%s] specified for --enable-nb" % nb)
3677 if not update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_MASK:
3678 raise CommandError("Entry found for value[%s] specified for --enable-nb is already enabled" % nb)
3679 update_forest_info.entries[idx].time = 0
3680 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3682 for nb in disable_nb:
3684 for i in xrange(0, len(update_forest_info.entries)):
3685 r = update_forest_info.entries[i]
3686 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3688 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3693 raise CommandError("Entry not found for value[%s] specified for --delete-nb" % nb)
3694 if update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_ADMIN:
3695 raise CommandError("Entry found for value[%s] specified for --disable-nb is already disabled" % nb)
3696 update_forest_info.entries[idx].time = 0
3697 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3698 update_forest_info.entries[idx].flags |= lsa.LSA_NB_DISABLED_ADMIN
3700 for sid in enable_sid:
3702 for i in xrange(0, len(update_forest_info.entries)):
3703 r = update_forest_info.entries[i]
3704 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3706 if r.forest_trust_data.domain_sid != sid:
3711 raise CommandError("Entry not found for value[%s] specified for --enable-sid" % sid)
3712 if not update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_MASK:
3713 raise CommandError("Entry found for value[%s] specified for --enable-sid is already enabled" % nb)
3714 update_forest_info.entries[idx].time = 0
3715 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3717 for sid in disable_sid:
3719 for i in xrange(0, len(update_forest_info.entries)):
3720 r = update_forest_info.entries[i]
3721 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3723 if r.forest_trust_data.domain_sid != sid:
3728 raise CommandError("Entry not found for value[%s] specified for --delete-sid" % sid)
3729 if update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_ADMIN:
3730 raise CommandError("Entry found for value[%s] specified for --disable-sid is already disabled" % nb)
3731 update_forest_info.entries[idx].time = 0
3732 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3733 update_forest_info.entries[idx].flags |= lsa.LSA_SID_DISABLED_ADMIN
3736 update_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3737 local_tdo_info.domain_name,
3738 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3739 update_forest_info, 0)
3740 except RuntimeError as error:
3741 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3743 self.outf.write("Updated forest trust information...\n")
3744 self.write_forest_trust_info(update_forest_info,
3745 tln=local_tdo_info.domain_name.string,
3746 collisions=update_forest_collision)
3749 lsaString = lsa.String()
3750 lsaString.string = local_tdo_info.domain_name.string
3751 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3753 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3754 except RuntimeError as error:
3755 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3757 self.outf.write("Stored forest trust information...\n")
3758 self.write_forest_trust_info(stored_forest_info,
3759 tln=local_tdo_info.domain_name.string)
3762 class cmd_domain_tombstones_expunge(Command):
3763 """Expunge tombstones from the database.
3765 This command expunges tombstones from the database."""
3766 synopsis = "%prog NC [NC [...]] [options]"
3769 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
3770 metavar="URL", dest="H"),
3771 Option("--current-time",
3772 help="The current time to evaluate the tombstone lifetime from, expressed as YYYY-MM-DD",
3774 Option("--tombstone-lifetime", help="Number of days a tombstone should be preserved for", type=int),
3777 takes_args = ["nc*"]
3779 takes_optiongroups = {
3780 "sambaopts": options.SambaOptions,
3781 "credopts": options.CredentialsOptions,
3782 "versionopts": options.VersionOptions,
3785 def run(self, *ncs, **kwargs):
3786 sambaopts = kwargs.get("sambaopts")
3787 credopts = kwargs.get("credopts")
3788 versionpts = kwargs.get("versionopts")
3790 current_time_string = kwargs.get("current_time")
3791 tombstone_lifetime = kwargs.get("tombstone_lifetime")
3792 lp = sambaopts.get_loadparm()
3793 creds = credopts.get_credentials(lp)
3794 samdb = SamDB(url=H, session_info=system_session(),
3795 credentials=creds, lp=lp)
3797 if current_time_string is not None:
3798 current_time_obj = time.strptime(current_time_string, "%Y-%m-%d")
3799 current_time = long(time.mktime(current_time_obj))
3802 current_time = long(time.time())
3805 res = samdb.search(expression="", base="", scope=ldb.SCOPE_BASE,
3806 attrs=["namingContexts"])
3809 for nc in res[0]["namingContexts"]:
3814 started_transaction = False
3816 samdb.transaction_start()
3817 started_transaction = True
3819 removed_links) = samdb.garbage_collect_tombstones(ncs,
3820 current_time=current_time,
3821 tombstone_lifetime=tombstone_lifetime)
3823 except Exception, err:
3824 if started_transaction:
3825 samdb.transaction_cancel()
3826 raise CommandError("Failed to expunge / garbage collect tombstones", err)
3828 samdb.transaction_commit()
3830 self.outf.write("Removed %d objects and %d links successfully\n"
3831 % (removed_objects, removed_links))
3835 class cmd_domain_trust(SuperCommand):
3836 """Domain and forest trust management."""
3839 subcommands["list"] = cmd_domain_trust_list()
3840 subcommands["show"] = cmd_domain_trust_show()
3841 subcommands["create"] = cmd_domain_trust_create()
3842 subcommands["delete"] = cmd_domain_trust_delete()
3843 subcommands["validate"] = cmd_domain_trust_validate()
3844 subcommands["namespaces"] = cmd_domain_trust_namespaces()
3846 class cmd_domain_tombstones(SuperCommand):
3847 """Domain tombstone and recycled object management."""
3850 subcommands["expunge"] = cmd_domain_tombstones_expunge()
3852 class cmd_domain(SuperCommand):
3853 """Domain management."""
3856 subcommands["demote"] = cmd_domain_demote()
3857 if cmd_domain_export_keytab is not None:
3858 subcommands["exportkeytab"] = cmd_domain_export_keytab()
3859 subcommands["info"] = cmd_domain_info()
3860 subcommands["provision"] = cmd_domain_provision()
3861 subcommands["join"] = cmd_domain_join()
3862 subcommands["dcpromo"] = cmd_domain_dcpromo()
3863 subcommands["level"] = cmd_domain_level()
3864 subcommands["passwordsettings"] = cmd_domain_passwordsettings()
3865 subcommands["classicupgrade"] = cmd_domain_classicupgrade()
3866 subcommands["samba3upgrade"] = cmd_domain_samba3upgrade()
3867 subcommands["trust"] = cmd_domain_trust()
3868 subcommands["tombstones"] = cmd_domain_tombstones()