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.kerberos_implementation import (
91 kdc_default_config_dir)
93 from samba.provision.common import (
99 def get_testparm_var(testparm, smbconf, varname):
100 errfile = open(os.devnull, 'w')
101 p = subprocess.Popen([testparm, '-s', '-l',
102 '--parameter-name=%s' % varname, smbconf],
103 stdout=subprocess.PIPE, stderr=errfile)
104 (out,err) = p.communicate()
106 lines = out.split('\n')
108 return lines[0].strip()
112 import samba.dckeytab
114 cmd_domain_export_keytab = None
116 class cmd_domain_export_keytab(Command):
117 """Dump Kerberos keys of the domain into a keytab."""
119 synopsis = "%prog <keytab> [options]"
121 takes_optiongroups = {
122 "sambaopts": options.SambaOptions,
123 "credopts": options.CredentialsOptions,
124 "versionopts": options.VersionOptions,
128 Option("--principal", help="extract only this principal", type=str),
131 takes_args = ["keytab"]
133 def run(self, keytab, credopts=None, sambaopts=None, versionopts=None, principal=None):
134 lp = sambaopts.get_loadparm()
136 net.export_keytab(keytab=keytab, principal=principal)
139 class cmd_domain_info(Command):
140 """Print basic info about a domain and the DC passed as parameter."""
142 synopsis = "%prog <ip_address> [options]"
147 takes_optiongroups = {
148 "sambaopts": options.SambaOptions,
149 "credopts": options.CredentialsOptions,
150 "versionopts": options.VersionOptions,
153 takes_args = ["address"]
155 def run(self, address, credopts=None, sambaopts=None, versionopts=None):
156 lp = sambaopts.get_loadparm()
158 res = netcmd_get_domain_infos_via_cldap(lp, None, address)
160 raise CommandError("Invalid IP address '" + address + "'!")
161 self.outf.write("Forest : %s\n" % res.forest)
162 self.outf.write("Domain : %s\n" % res.dns_domain)
163 self.outf.write("Netbios domain : %s\n" % res.domain_name)
164 self.outf.write("DC name : %s\n" % res.pdc_dns_name)
165 self.outf.write("DC netbios name : %s\n" % res.pdc_name)
166 self.outf.write("Server site : %s\n" % res.server_site)
167 self.outf.write("Client site : %s\n" % res.client_site)
170 class cmd_domain_provision(Command):
171 """Provision a domain."""
173 synopsis = "%prog [options]"
175 takes_optiongroups = {
176 "sambaopts": options.SambaOptions,
177 "versionopts": options.VersionOptions,
181 Option("--interactive", help="Ask for names", action="store_true"),
182 Option("--domain", type="string", metavar="DOMAIN",
183 help="NetBIOS domain name to use"),
184 Option("--domain-guid", type="string", metavar="GUID",
185 help="set domainguid (otherwise random)"),
186 Option("--domain-sid", type="string", metavar="SID",
187 help="set domainsid (otherwise random)"),
188 Option("--ntds-guid", type="string", metavar="GUID",
189 help="set NTDS object GUID (otherwise random)"),
190 Option("--invocationid", type="string", metavar="GUID",
191 help="set invocationid (otherwise random)"),
192 Option("--host-name", type="string", metavar="HOSTNAME",
193 help="set hostname"),
194 Option("--host-ip", type="string", metavar="IPADDRESS",
195 help="set IPv4 ipaddress"),
196 Option("--host-ip6", type="string", metavar="IP6ADDRESS",
197 help="set IPv6 ipaddress"),
198 Option("--site", type="string", metavar="SITENAME",
199 help="set site name"),
200 Option("--adminpass", type="string", metavar="PASSWORD",
201 help="choose admin password (otherwise random)"),
202 Option("--krbtgtpass", type="string", metavar="PASSWORD",
203 help="choose krbtgt password (otherwise random)"),
204 Option("--machinepass", type="string", metavar="PASSWORD",
205 help="choose machine password (otherwise random)"),
206 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
207 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
208 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
209 "BIND9_FLATFILE uses bind9 text database to store zone information, "
210 "BIND9_DLZ uses samba4 AD to store zone information, "
211 "NONE skips the DNS setup entirely (not recommended)",
212 default="SAMBA_INTERNAL"),
213 Option("--dnspass", type="string", metavar="PASSWORD",
214 help="choose dns password (otherwise random)"),
215 Option("--ldapadminpass", type="string", metavar="PASSWORD",
216 help="choose password to set between Samba and its LDAP backend (otherwise random)"),
217 Option("--root", type="string", metavar="USERNAME",
218 help="choose 'root' unix username"),
219 Option("--nobody", type="string", metavar="USERNAME",
220 help="choose 'nobody' user"),
221 Option("--users", type="string", metavar="GROUPNAME",
222 help="choose 'users' group"),
223 Option("--quiet", help="Be quiet", action="store_true"),
224 Option("--blank", action="store_true",
225 help="do not add users or groups, just the structure"),
226 Option("--ldap-backend-type", type="choice", metavar="LDAP-BACKEND-TYPE",
227 help="Test initialisation support for unsupported LDAP backend type (fedora-ds or openldap) DO NOT USE",
228 choices=["fedora-ds", "openldap"]),
229 Option("--server-role", type="choice", metavar="ROLE",
230 choices=["domain controller", "dc", "member server", "member", "standalone"],
231 help="The server role (domain controller | dc | member server | member | standalone). Default is dc.",
232 default="domain controller"),
233 Option("--function-level", type="choice", metavar="FOR-FUN-LEVEL",
234 choices=["2000", "2003", "2008", "2008_R2"],
235 help="The domain and forest function level (2000 | 2003 | 2008 | 2008_R2 - always native). Default is (Windows) 2008_R2 Native.",
237 Option("--next-rid", type="int", metavar="NEXTRID", default=1000,
238 help="The initial nextRid value (only needed for upgrades). Default is 1000."),
239 Option("--partitions-only",
240 help="Configure Samba's partitions, but do not modify them (ie, join a BDC)", action="store_true"),
241 Option("--targetdir", type="string", metavar="DIR",
242 help="Set target directory"),
243 Option("--ol-mmr-urls", type="string", metavar="LDAPSERVER",
244 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\""),
245 Option("--use-rfc2307", action="store_true", help="Use AD to store posix attributes (default = no)"),
249 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",
250 action="store_true"),
251 Option("--slapd-path", type="string", metavar="SLAPD-PATH",
252 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."),
253 Option("--ldap-backend-extra-port", type="int", metavar="LDAP-BACKEND-EXTRA-PORT", help="Additional TCP port for LDAP backend server (to use for replication)"),
254 Option("--ldap-backend-forced-uri", type="string", metavar="LDAP-BACKEND-FORCED-URI",
255 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"),
256 Option("--ldap-backend-nosync", help="Configure LDAP backend not to call fsync() (for performance in test environments)", action="store_true"),
260 Option("--use-ntvfs", action="store_true", help="Use NTVFS for the fileserver (default = no)"),
261 Option("--use-xattrs", type="choice", choices=["yes","no","auto"],
262 metavar="[yes|no|auto]",
263 help="Define if we should use the native fs capabilities or a tdb file for "
264 "storing attributes likes ntacl when --use-ntvfs is set. "
265 "auto tries to make an inteligent guess based on the user rights and system capabilities",
270 Option("--kdc-config-dir", type="string", metavar="KDC-CONFIG-DIR",
271 help="Set the MIT KDC config directory (default='%s')" % kdc_default_config_dir),
274 if os.getenv('TEST_LDAP', "no") == "yes":
275 takes_options.extend(openldap_options)
277 if samba.is_ntvfs_fileserver_built():
278 takes_options.extend(ntvfs_options)
280 if not samba.is_heimdal_built():
281 takes_options.extend(kdc_options)
285 def run(self, sambaopts=None, versionopts=None,
308 ldap_backend_type=None,
312 partitions_only=None,
320 ldap_backend_nosync=None,
321 ldap_backend_extra_port=None,
322 ldap_backend_forced_uri=None,
323 ldap_dryrun_mode=None):
325 self.logger = self.get_logger("provision")
327 self.logger.setLevel(logging.WARNING)
329 self.logger.setLevel(logging.INFO)
331 lp = sambaopts.get_loadparm()
332 smbconf = lp.configfile
334 if dns_forwarder is not None:
335 suggested_forwarder = dns_forwarder
337 suggested_forwarder = self._get_nameserver_ip()
338 if suggested_forwarder is None:
339 suggested_forwarder = "none"
341 if len(self.raw_argv) == 1:
345 from getpass import getpass
348 def ask(prompt, default=None):
349 if default is not None:
350 print "%s [%s]: " % (prompt, default),
352 print "%s: " % (prompt,),
353 return sys.stdin.readline().rstrip("\n") or default
356 default = socket.getfqdn().split(".", 1)[1].upper()
359 realm = ask("Realm", default)
360 if realm in (None, ""):
361 raise CommandError("No realm set!")
364 default = realm.split(".")[0]
367 domain = ask("Domain", default)
369 raise CommandError("No domain set!")
371 server_role = ask("Server Role (dc, member, standalone)", "dc")
373 dns_backend = ask("DNS backend (SAMBA_INTERNAL, BIND9_FLATFILE, BIND9_DLZ, NONE)", "SAMBA_INTERNAL")
374 if dns_backend in (None, ''):
375 raise CommandError("No DNS backend set!")
377 if dns_backend == "SAMBA_INTERNAL":
378 dns_forwarder = ask("DNS forwarder IP address (write 'none' to disable forwarding)", suggested_forwarder)
379 if dns_forwarder.lower() in (None, 'none'):
380 suggested_forwarder = None
384 adminpassplain = getpass("Administrator password: ")
385 if not adminpassplain:
386 self.errf.write("Invalid administrator password.\n")
388 adminpassverify = getpass("Retype password: ")
389 if not adminpassplain == adminpassverify:
390 self.errf.write("Sorry, passwords do not match.\n")
392 adminpass = adminpassplain
396 realm = sambaopts._lp.get('realm')
398 raise CommandError("No realm set!")
400 raise CommandError("No domain set!")
403 self.logger.info("Administrator password will be set randomly!")
405 if function_level == "2000":
406 dom_for_fun_level = DS_DOMAIN_FUNCTION_2000
407 elif function_level == "2003":
408 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
409 elif function_level == "2008":
410 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008
411 elif function_level == "2008_R2":
412 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
414 if dns_backend == "SAMBA_INTERNAL" and dns_forwarder is None:
415 dns_forwarder = suggested_forwarder
417 samdb_fill = FILL_FULL
419 samdb_fill = FILL_NT4SYNC
420 elif partitions_only:
421 samdb_fill = FILL_DRS
423 if targetdir is not None:
424 if not os.path.isdir(targetdir):
429 if use_xattrs == "yes":
431 elif use_xattrs == "auto" and use_ntvfs == False:
433 elif use_ntvfs == False:
434 raise CommandError("--use-xattrs=no requires --use-ntvfs (not supported for production use). "
435 "Please re-run with --use-xattrs omitted.")
436 elif use_xattrs == "auto" and not lp.get("posix:eadb"):
438 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
440 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
443 samba.ntacls.setntacl(lp, file.name,
444 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
447 self.logger.info("You are not root or your system does not support xattr, using tdb backend for attributes. ")
452 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.")
453 if ldap_backend_type == "existing":
454 if ldap_backend_forced_uri is not None:
455 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)
457 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")
459 if ldap_backend_forced_uri is not None:
460 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")
462 if domain_sid is not None:
463 domain_sid = security.dom_sid(domain_sid)
465 session = system_session()
467 result = provision(self.logger,
468 session, smbconf=smbconf, targetdir=targetdir,
469 samdb_fill=samdb_fill, realm=realm, domain=domain,
470 domainguid=domain_guid, domainsid=domain_sid,
472 hostip=host_ip, hostip6=host_ip6,
473 sitename=site, ntdsguid=ntds_guid,
474 invocationid=invocationid, adminpass=adminpass,
475 krbtgtpass=krbtgtpass, machinepass=machinepass,
476 dns_backend=dns_backend, dns_forwarder=dns_forwarder,
477 dnspass=dnspass, root=root, nobody=nobody,
479 serverrole=server_role, dom_for_fun_level=dom_for_fun_level,
480 backend_type=ldap_backend_type,
481 ldapadminpass=ldapadminpass, ol_mmr_urls=ol_mmr_urls, slapd_path=slapd_path,
482 useeadb=eadb, next_rid=next_rid, lp=lp, use_ntvfs=use_ntvfs,
483 use_rfc2307=use_rfc2307, skip_sysvolacl=False,
484 ldap_backend_extra_port=ldap_backend_extra_port,
485 ldap_backend_forced_uri=ldap_backend_forced_uri,
486 nosync=ldap_backend_nosync, ldap_dryrun_mode=ldap_dryrun_mode,
487 kdcconfdir=kdc_config_dir)
489 except ProvisioningError, e:
490 raise CommandError("Provision failed", e)
492 result.report_logger(self.logger)
494 def _get_nameserver_ip(self):
495 """Grab the nameserver IP address from /etc/resolv.conf."""
497 RESOLV_CONF="/etc/resolv.conf"
499 if not path.isfile(RESOLV_CONF):
500 self.logger.warning("Failed to locate %s" % RESOLV_CONF)
505 handle = open(RESOLV_CONF, 'r')
507 if not line.startswith('nameserver'):
509 # we want the last non-space continuous string of the line
510 return line.strip().split()[-1]
512 if handle is not None:
515 self.logger.warning("No nameserver found in %s" % RESOLV_CONF)
518 class cmd_domain_dcpromo(Command):
519 """Promote an existing domain member or NT4 PDC to an AD DC."""
521 synopsis = "%prog <dnsdomain> [DC|RODC] [options]"
523 takes_optiongroups = {
524 "sambaopts": options.SambaOptions,
525 "versionopts": options.VersionOptions,
526 "credopts": options.CredentialsOptions,
530 Option("--server", help="DC to join", type=str),
531 Option("--site", help="site to join", type=str),
532 Option("--targetdir", help="where to store provision", type=str),
533 Option("--domain-critical-only",
534 help="only replicate critical domain objects",
535 action="store_true"),
536 Option("--machinepass", type=str, metavar="PASSWORD",
537 help="choose machine password (otherwise random)"),
538 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
539 choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
540 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
541 "BIND9_DLZ uses samba4 AD to store zone information, "
542 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
543 default="SAMBA_INTERNAL"),
544 Option("--quiet", help="Be quiet", action="store_true"),
545 Option("--verbose", help="Be verbose", action="store_true")
549 Option("--use-ntvfs", action="store_true", help="Use NTVFS for the fileserver (default = no)"),
552 if samba.is_ntvfs_fileserver_built():
553 takes_options.extend(ntvfs_options)
556 takes_args = ["domain", "role?"]
558 def run(self, domain, role=None, sambaopts=None, credopts=None,
559 versionopts=None, server=None, site=None, targetdir=None,
560 domain_critical_only=False, parent_domain=None, machinepass=None,
561 use_ntvfs=False, dns_backend=None,
562 quiet=False, verbose=False):
563 lp = sambaopts.get_loadparm()
564 creds = credopts.get_credentials(lp)
565 net = Net(creds, lp, server=credopts.ipaddress)
568 site = "Default-First-Site-Name"
570 logger = self.get_logger()
572 logger.setLevel(logging.DEBUG)
574 logger.setLevel(logging.WARNING)
576 logger.setLevel(logging.INFO)
578 netbios_name = lp.get("netbios name")
584 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
585 site=site, netbios_name=netbios_name, targetdir=targetdir,
586 domain_critical_only=domain_critical_only,
587 machinepass=machinepass, use_ntvfs=use_ntvfs,
588 dns_backend=dns_backend,
589 promote_existing=True)
591 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
592 site=site, netbios_name=netbios_name, targetdir=targetdir,
593 domain_critical_only=domain_critical_only,
594 machinepass=machinepass, use_ntvfs=use_ntvfs, dns_backend=dns_backend,
595 promote_existing=True)
597 raise CommandError("Invalid role '%s' (possible values: DC, RODC)" % role)
600 class cmd_domain_join(Command):
601 """Join domain as either member or backup domain controller."""
603 synopsis = "%prog <dnsdomain> [DC|RODC|MEMBER|SUBDOMAIN] [options]"
605 takes_optiongroups = {
606 "sambaopts": options.SambaOptions,
607 "versionopts": options.VersionOptions,
608 "credopts": options.CredentialsOptions,
612 Option("--server", help="DC to join", type=str),
613 Option("--site", help="site to join", type=str),
614 Option("--targetdir", help="where to store provision", type=str),
615 Option("--parent-domain", help="parent domain to create subdomain under", type=str),
616 Option("--domain-critical-only",
617 help="only replicate critical domain objects",
618 action="store_true"),
619 Option("--machinepass", type=str, metavar="PASSWORD",
620 help="choose machine password (otherwise random)"),
621 Option("--adminpass", type="string", metavar="PASSWORD",
622 help="choose adminstrator password when joining as a subdomain (otherwise random)"),
623 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
624 choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
625 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
626 "BIND9_DLZ uses samba4 AD to store zone information, "
627 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
628 default="SAMBA_INTERNAL"),
629 Option("--quiet", help="Be quiet", action="store_true"),
630 Option("--verbose", help="Be verbose", action="store_true")
634 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
637 if samba.is_ntvfs_fileserver_built():
638 takes_options.extend(ntvfs_options)
640 takes_args = ["domain", "role?"]
642 def run(self, domain, role=None, sambaopts=None, credopts=None,
643 versionopts=None, server=None, site=None, targetdir=None,
644 domain_critical_only=False, parent_domain=None, machinepass=None,
645 use_ntvfs=False, dns_backend=None, adminpass=None,
646 quiet=False, verbose=False):
647 lp = sambaopts.get_loadparm()
648 creds = credopts.get_credentials(lp)
649 net = Net(creds, lp, server=credopts.ipaddress)
652 site = "Default-First-Site-Name"
654 logger = self.get_logger()
656 logger.setLevel(logging.DEBUG)
658 logger.setLevel(logging.WARNING)
660 logger.setLevel(logging.INFO)
662 netbios_name = lp.get("netbios name")
667 if role is None or role == "MEMBER":
668 (join_password, sid, domain_name) = net.join_member(
669 domain, netbios_name, LIBNET_JOIN_AUTOMATIC,
670 machinepass=machinepass)
672 self.errf.write("Joined domain %s (%s)\n" % (domain_name, sid))
674 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
675 site=site, netbios_name=netbios_name, targetdir=targetdir,
676 domain_critical_only=domain_critical_only,
677 machinepass=machinepass, use_ntvfs=use_ntvfs, dns_backend=dns_backend)
679 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
680 site=site, netbios_name=netbios_name, targetdir=targetdir,
681 domain_critical_only=domain_critical_only,
682 machinepass=machinepass, use_ntvfs=use_ntvfs,
683 dns_backend=dns_backend)
684 elif role == "SUBDOMAIN":
686 logger.info("Administrator password will be set randomly!")
688 netbios_domain = lp.get("workgroup")
689 if parent_domain is None:
690 parent_domain = ".".join(domain.split(".")[1:])
691 join_subdomain(logger=logger, server=server, creds=creds, lp=lp, dnsdomain=domain,
692 parent_domain=parent_domain, site=site,
693 netbios_name=netbios_name, netbios_domain=netbios_domain,
694 targetdir=targetdir, machinepass=machinepass,
695 use_ntvfs=use_ntvfs, dns_backend=dns_backend,
698 raise CommandError("Invalid role '%s' (possible values: MEMBER, DC, RODC, SUBDOMAIN)" % role)
701 class cmd_domain_demote(Command):
702 """Demote ourselves from the role of Domain Controller."""
704 synopsis = "%prog [options]"
707 Option("--server", help="writable DC to write demotion changes on", type=str),
708 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
709 metavar="URL", dest="H"),
710 Option("--remove-other-dead-server", help="Dead DC (name or NTDS GUID) "
711 "to remove ALL references to (rather than this DC)", type=str),
712 Option("--quiet", help="Be quiet", action="store_true"),
713 Option("--verbose", help="Be verbose", action="store_true"),
716 takes_optiongroups = {
717 "sambaopts": options.SambaOptions,
718 "credopts": options.CredentialsOptions,
719 "versionopts": options.VersionOptions,
722 def run(self, sambaopts=None, credopts=None,
723 versionopts=None, server=None,
724 remove_other_dead_server=None, H=None,
725 verbose=False, quiet=False):
726 lp = sambaopts.get_loadparm()
727 creds = credopts.get_credentials(lp)
728 net = Net(creds, lp, server=credopts.ipaddress)
730 logger = self.get_logger()
732 logger.setLevel(logging.DEBUG)
734 logger.setLevel(logging.WARNING)
736 logger.setLevel(logging.INFO)
738 if remove_other_dead_server is not None:
739 if server is not None:
740 samdb = SamDB(url="ldap://%s" % server,
741 session_info=system_session(),
742 credentials=creds, lp=lp)
744 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
746 remove_dc.remove_dc(samdb, logger, remove_other_dead_server)
747 except remove_dc.DemoteException as err:
748 raise CommandError("Demote failed: %s" % err)
751 netbios_name = lp.get("netbios name")
752 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
754 res = samdb.search(expression='(&(objectClass=computer)(serverReferenceBL=*))', attrs=["dnsHostName", "name"])
756 raise CommandError("Unable to search for servers")
759 raise CommandError("You are the latest server in the domain")
763 if str(e["name"]).lower() != netbios_name.lower():
764 server = e["dnsHostName"]
767 ntds_guid = samdb.get_ntds_GUID()
768 msg = samdb.search(base=str(samdb.get_config_basedn()),
769 scope=ldb.SCOPE_SUBTREE, expression="(objectGUID=%s)" % ntds_guid,
771 if len(msg) == 0 or "options" not in msg[0]:
772 raise CommandError("Failed to find options on %s" % ntds_guid)
775 dsa_options = int(str(msg[0]['options']))
777 res = samdb.search(expression="(fSMORoleOwner=%s)" % str(ntds_dn),
778 controls=["search_options:1:2"])
781 raise CommandError("Current DC is still the owner of %d role(s), use the role command to transfer roles to another DC" % len(res))
783 self.errf.write("Using %s as partner server for the demotion\n" %
785 (drsuapiBind, drsuapi_handle, supportedExtensions) = drsuapi_connect(server, lp, creds)
787 self.errf.write("Deactivating inbound replication\n")
792 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
793 dsa_options |= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
794 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
798 self.errf.write("Asking partner server %s to synchronize from us\n"
800 for part in (samdb.get_schema_basedn(),
801 samdb.get_config_basedn(),
802 samdb.get_root_basedn()):
803 nc = drsuapi.DsReplicaObjectIdentifier()
806 req1 = drsuapi.DsReplicaSyncRequest1()
807 req1.naming_context = nc;
808 req1.options = drsuapi.DRSUAPI_DRS_WRIT_REP
809 req1.source_dsa_guid = misc.GUID(ntds_guid)
812 drsuapiBind.DsReplicaSync(drsuapi_handle, 1, req1)
813 except RuntimeError as (werr, string):
814 if werr == werror.WERR_DS_DRA_NO_REPLICA:
818 "Error while replicating out last local changes from '%s' for demotion, "
819 "re-enabling inbound replication\n" % part)
820 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
821 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
823 raise CommandError("Error while sending a DsReplicaSync for partition '%s'" % str(part), string)
825 remote_samdb = SamDB(url="ldap://%s" % server,
826 session_info=system_session(),
827 credentials=creds, lp=lp)
829 self.errf.write("Changing userControl and container\n")
830 res = remote_samdb.search(base=str(remote_samdb.domain_dn()),
831 expression="(&(objectClass=user)(sAMAccountName=%s$))" %
832 netbios_name.upper(),
833 attrs=["userAccountControl"])
835 uac = int(str(res[0]["userAccountControl"]))
838 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
840 "Error while demoting, re-enabling inbound replication\n")
841 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
842 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
844 raise CommandError("Error while changing account control", e)
847 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
849 "Error while demoting, re-enabling inbound replication")
850 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
851 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
853 raise CommandError("Unable to find object with samaccountName = %s$"
854 " in the remote dc" % netbios_name.upper())
858 uac &= ~(UF_SERVER_TRUST_ACCOUNT|UF_TRUSTED_FOR_DELEGATION|UF_PARTIAL_SECRETS_ACCOUNT)
859 uac |= UF_WORKSTATION_TRUST_ACCOUNT
864 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
865 ldb.FLAG_MOD_REPLACE,
866 "userAccountControl")
868 remote_samdb.modify(msg)
870 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
872 "Error while demoting, re-enabling inbound replication")
873 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
874 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
877 raise CommandError("Error while changing account control", e)
879 parent = msg.dn.parent()
880 dc_name = res[0].dn.get_rdn_value()
881 rdn = "CN=%s" % dc_name
883 # Let's move to the Computer container
887 computer_dn = ldb.Dn(remote_samdb, "CN=Computers,%s" % str(remote_samdb.domain_dn()))
888 res = remote_samdb.search(base=computer_dn, expression=rdn, scope=ldb.SCOPE_ONELEVEL)
891 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
892 scope=ldb.SCOPE_ONELEVEL)
893 while(len(res) != 0 and i < 100):
895 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
896 scope=ldb.SCOPE_ONELEVEL)
899 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
901 "Error while demoting, re-enabling inbound replication\n")
902 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
903 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
909 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
910 ldb.FLAG_MOD_REPLACE,
911 "userAccountControl")
913 remote_samdb.modify(msg)
915 raise CommandError("Unable to find a slot for renaming %s,"
916 " all names from %s-1 to %s-%d seemed used" %
917 (str(dc_dn), rdn, rdn, i - 9))
919 newrdn = "%s-%d" % (rdn, i)
922 newdn = ldb.Dn(remote_samdb, "%s,%s" % (newrdn, str(computer_dn)))
923 remote_samdb.rename(dc_dn, newdn)
925 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
927 "Error while demoting, re-enabling inbound replication\n")
928 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
929 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
935 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
936 ldb.FLAG_MOD_REPLACE,
937 "userAccountControl")
939 remote_samdb.modify(msg)
940 raise CommandError("Error while renaming %s to %s" % (str(dc_dn), str(newdn)), e)
943 server_dsa_dn = samdb.get_serverName()
944 domain = remote_samdb.get_root_basedn()
947 req1 = drsuapi.DsRemoveDSServerRequest1()
948 req1.server_dn = str(server_dsa_dn)
949 req1.domain_dn = str(domain)
952 drsuapiBind.DsRemoveDSServer(drsuapi_handle, 1, req1)
953 except RuntimeError as (werr, string):
954 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
956 "Error while demoting, re-enabling inbound replication\n")
957 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
958 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
964 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
965 ldb.FLAG_MOD_REPLACE,
966 "userAccountControl")
967 remote_samdb.modify(msg)
968 remote_samdb.rename(newdn, dc_dn)
969 if werr == werror.WERR_DS_DRA_NO_REPLICA:
970 raise CommandError("The DC %s is not present on (already removed from) the remote server: " % server_dsa_dn, e)
972 raise CommandError("Error while sending a removeDsServer of %s: " % server_dsa_dn, e)
974 remove_dc.remove_sysvol_references(remote_samdb, logger, dc_name)
976 # These are objects under the computer account that should be deleted
977 for s in ("CN=Enterprise,CN=NTFRS Subscriptions",
978 "CN=%s, CN=NTFRS Subscriptions" % lp.get("realm"),
979 "CN=Domain system Volumes (SYSVOL Share), CN=NTFRS Subscriptions",
980 "CN=NTFRS Subscriptions"):
982 remote_samdb.delete(ldb.Dn(remote_samdb,
983 "%s,%s" % (s, str(newdn))))
984 except ldb.LdbError, l:
987 self.errf.write("Demote successful\n")
990 class cmd_domain_level(Command):
991 """Raise domain and forest function levels."""
993 synopsis = "%prog (show|raise <options>) [options]"
995 takes_optiongroups = {
996 "sambaopts": options.SambaOptions,
997 "credopts": options.CredentialsOptions,
998 "versionopts": options.VersionOptions,
1002 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1003 metavar="URL", dest="H"),
1004 Option("--quiet", help="Be quiet", action="store_true"),
1005 Option("--forest-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
1006 help="The forest function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)"),
1007 Option("--domain-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
1008 help="The domain function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)")
1011 takes_args = ["subcommand"]
1013 def run(self, subcommand, H=None, forest_level=None, domain_level=None,
1014 quiet=False, credopts=None, sambaopts=None, versionopts=None):
1015 lp = sambaopts.get_loadparm()
1016 creds = credopts.get_credentials(lp, fallback_machine=True)
1018 samdb = SamDB(url=H, session_info=system_session(),
1019 credentials=creds, lp=lp)
1021 domain_dn = samdb.domain_dn()
1023 res_forest = samdb.search("CN=Partitions,%s" % samdb.get_config_basedn(),
1024 scope=ldb.SCOPE_BASE, attrs=["msDS-Behavior-Version"])
1025 assert len(res_forest) == 1
1027 res_domain = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1028 attrs=["msDS-Behavior-Version", "nTMixedDomain"])
1029 assert len(res_domain) == 1
1031 res_dc_s = samdb.search("CN=Sites,%s" % samdb.get_config_basedn(),
1032 scope=ldb.SCOPE_SUBTREE, expression="(objectClass=nTDSDSA)",
1033 attrs=["msDS-Behavior-Version"])
1034 assert len(res_dc_s) >= 1
1036 # default values, since "msDS-Behavior-Version" does not exist on Windows 2000 AD
1037 level_forest = DS_DOMAIN_FUNCTION_2000
1038 level_domain = DS_DOMAIN_FUNCTION_2000
1040 if "msDS-Behavior-Version" in res_forest[0]:
1041 level_forest = int(res_forest[0]["msDS-Behavior-Version"][0])
1042 if "msDS-Behavior-Version" in res_domain[0]:
1043 level_domain = int(res_domain[0]["msDS-Behavior-Version"][0])
1044 level_domain_mixed = int(res_domain[0]["nTMixedDomain"][0])
1047 for msg in res_dc_s:
1048 if "msDS-Behavior-Version" in msg:
1049 if min_level_dc is None or int(msg["msDS-Behavior-Version"][0]) < min_level_dc:
1050 min_level_dc = int(msg["msDS-Behavior-Version"][0])
1052 min_level_dc = DS_DOMAIN_FUNCTION_2000
1053 # well, this is the least
1056 if level_forest < DS_DOMAIN_FUNCTION_2000 or level_domain < DS_DOMAIN_FUNCTION_2000:
1057 raise CommandError("Domain and/or forest function level(s) is/are invalid. Correct them or reprovision!")
1058 if min_level_dc < DS_DOMAIN_FUNCTION_2000:
1059 raise CommandError("Lowest function level of a DC is invalid. Correct this or reprovision!")
1060 if level_forest > level_domain:
1061 raise CommandError("Forest function level is higher than the domain level(s). Correct this or reprovision!")
1062 if level_domain > min_level_dc:
1063 raise CommandError("Domain function level is higher than the lowest function level of a DC. Correct this or reprovision!")
1065 if subcommand == "show":
1066 self.message("Domain and forest function level for domain '%s'" % domain_dn)
1067 if level_forest == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1068 self.message("\nATTENTION: You run SAMBA 4 on a forest function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1069 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1070 self.message("\nATTENTION: You run SAMBA 4 on a domain function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1071 if min_level_dc == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1072 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)!")
1076 if level_forest == DS_DOMAIN_FUNCTION_2000:
1078 elif level_forest == DS_DOMAIN_FUNCTION_2003_MIXED:
1079 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1080 elif level_forest == DS_DOMAIN_FUNCTION_2003:
1082 elif level_forest == DS_DOMAIN_FUNCTION_2008:
1084 elif level_forest == DS_DOMAIN_FUNCTION_2008_R2:
1086 elif level_forest == DS_DOMAIN_FUNCTION_2012:
1088 elif level_forest == DS_DOMAIN_FUNCTION_2012_R2:
1091 outstr = "higher than 2012 R2"
1092 self.message("Forest function level: (Windows) " + outstr)
1094 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1095 outstr = "2000 mixed (NT4 DC support)"
1096 elif level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed == 0:
1098 elif level_domain == DS_DOMAIN_FUNCTION_2003_MIXED:
1099 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1100 elif level_domain == DS_DOMAIN_FUNCTION_2003:
1102 elif level_domain == DS_DOMAIN_FUNCTION_2008:
1104 elif level_domain == DS_DOMAIN_FUNCTION_2008_R2:
1106 elif level_domain == DS_DOMAIN_FUNCTION_2012:
1108 elif level_domain == DS_DOMAIN_FUNCTION_2012_R2:
1111 outstr = "higher than 2012 R2"
1112 self.message("Domain function level: (Windows) " + outstr)
1114 if min_level_dc == DS_DOMAIN_FUNCTION_2000:
1116 elif min_level_dc == DS_DOMAIN_FUNCTION_2003:
1118 elif min_level_dc == DS_DOMAIN_FUNCTION_2008:
1120 elif min_level_dc == DS_DOMAIN_FUNCTION_2008_R2:
1122 elif min_level_dc == DS_DOMAIN_FUNCTION_2012:
1124 elif min_level_dc == DS_DOMAIN_FUNCTION_2012_R2:
1127 outstr = "higher than 2012 R2"
1128 self.message("Lowest function level of a DC: (Windows) " + outstr)
1130 elif subcommand == "raise":
1133 if domain_level is not None:
1134 if domain_level == "2003":
1135 new_level_domain = DS_DOMAIN_FUNCTION_2003
1136 elif domain_level == "2008":
1137 new_level_domain = DS_DOMAIN_FUNCTION_2008
1138 elif domain_level == "2008_R2":
1139 new_level_domain = DS_DOMAIN_FUNCTION_2008_R2
1140 elif domain_level == "2012":
1141 new_level_domain = DS_DOMAIN_FUNCTION_2012
1142 elif domain_level == "2012_R2":
1143 new_level_domain = DS_DOMAIN_FUNCTION_2012_R2
1145 if new_level_domain <= level_domain and level_domain_mixed == 0:
1146 raise CommandError("Domain function level can't be smaller than or equal to the actual one!")
1147 if new_level_domain > min_level_dc:
1148 raise CommandError("Domain function level can't be higher than the lowest function level of a DC!")
1150 # Deactivate mixed/interim domain support
1151 if level_domain_mixed != 0:
1152 # Directly on the base DN
1154 m.dn = ldb.Dn(samdb, domain_dn)
1155 m["nTMixedDomain"] = ldb.MessageElement("0",
1156 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1160 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup") + ",CN=Partitions,%s" % samdb.get_config_basedn())
1161 m["nTMixedDomain"] = ldb.MessageElement("0",
1162 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1165 except ldb.LdbError, (enum, emsg):
1166 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1169 # Directly on the base DN
1171 m.dn = ldb.Dn(samdb, domain_dn)
1172 m["msDS-Behavior-Version"]= ldb.MessageElement(
1173 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1174 "msDS-Behavior-Version")
1178 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup")
1179 + ",CN=Partitions,%s" % samdb.get_config_basedn())
1180 m["msDS-Behavior-Version"]= ldb.MessageElement(
1181 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1182 "msDS-Behavior-Version")
1185 except ldb.LdbError, (enum, emsg):
1186 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1189 level_domain = new_level_domain
1190 msgs.append("Domain function level changed!")
1192 if forest_level is not None:
1193 if forest_level == "2003":
1194 new_level_forest = DS_DOMAIN_FUNCTION_2003
1195 elif forest_level == "2008":
1196 new_level_forest = DS_DOMAIN_FUNCTION_2008
1197 elif forest_level == "2008_R2":
1198 new_level_forest = DS_DOMAIN_FUNCTION_2008_R2
1199 elif forest_level == "2012":
1200 new_level_forest = DS_DOMAIN_FUNCTION_2012
1201 elif forest_level == "2012_R2":
1202 new_level_forest = DS_DOMAIN_FUNCTION_2012_R2
1204 if new_level_forest <= level_forest:
1205 raise CommandError("Forest function level can't be smaller than or equal to the actual one!")
1206 if new_level_forest > level_domain:
1207 raise CommandError("Forest function level can't be higher than the domain function level(s). Please raise it/them first!")
1210 m.dn = ldb.Dn(samdb, "CN=Partitions,%s" % samdb.get_config_basedn())
1211 m["msDS-Behavior-Version"]= ldb.MessageElement(
1212 str(new_level_forest), ldb.FLAG_MOD_REPLACE,
1213 "msDS-Behavior-Version")
1215 msgs.append("Forest function level changed!")
1216 msgs.append("All changes applied successfully!")
1217 self.message("\n".join(msgs))
1219 raise CommandError("invalid argument: '%s' (choose from 'show', 'raise')" % subcommand)
1222 class cmd_domain_passwordsettings(Command):
1223 """Set password settings.
1225 Password complexity, password lockout policy, history length,
1226 minimum password length, the minimum and maximum password age) on
1227 a Samba AD DC server.
1229 Use against a Windows DC is possible, but group policy will override it.
1232 synopsis = "%prog (show|set <options>) [options]"
1234 takes_optiongroups = {
1235 "sambaopts": options.SambaOptions,
1236 "versionopts": options.VersionOptions,
1237 "credopts": options.CredentialsOptions,
1241 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1242 metavar="URL", dest="H"),
1243 Option("--quiet", help="Be quiet", action="store_true"),
1244 Option("--complexity", type="choice", choices=["on","off","default"],
1245 help="The password complexity (on | off | default). Default is 'on'"),
1246 Option("--store-plaintext", type="choice", choices=["on","off","default"],
1247 help="Store plaintext passwords where account have 'store passwords with reversible encryption' set (on | off | default). Default is 'off'"),
1248 Option("--history-length",
1249 help="The password history length (<integer> | default). Default is 24.", type=str),
1250 Option("--min-pwd-length",
1251 help="The minimum password length (<integer> | default). Default is 7.", type=str),
1252 Option("--min-pwd-age",
1253 help="The minimum password age (<integer in days> | default). Default is 1.", type=str),
1254 Option("--max-pwd-age",
1255 help="The maximum password age (<integer in days> | default). Default is 43.", type=str),
1256 Option("--account-lockout-duration",
1257 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),
1258 Option("--account-lockout-threshold",
1259 help="The number of bad password attempts allowed before locking out the account (<integer> | default). Default is 0 (never lock out).", type=str),
1260 Option("--reset-account-lockout-after",
1261 help="After this time is elapsed, the recorded number of attempts restarts from zero (<integer> | default). Default is 30.", type=str),
1264 takes_args = ["subcommand"]
1266 def run(self, subcommand, H=None, min_pwd_age=None, max_pwd_age=None,
1267 quiet=False, complexity=None, store_plaintext=None, history_length=None,
1268 min_pwd_length=None, account_lockout_duration=None, account_lockout_threshold=None,
1269 reset_account_lockout_after=None, credopts=None, sambaopts=None,
1271 lp = sambaopts.get_loadparm()
1272 creds = credopts.get_credentials(lp)
1274 samdb = SamDB(url=H, session_info=system_session(),
1275 credentials=creds, lp=lp)
1277 domain_dn = samdb.domain_dn()
1278 res = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1279 attrs=["pwdProperties", "pwdHistoryLength", "minPwdLength",
1280 "minPwdAge", "maxPwdAge", "lockoutDuration", "lockoutThreshold",
1281 "lockOutObservationWindow"])
1282 assert(len(res) == 1)
1284 pwd_props = int(res[0]["pwdProperties"][0])
1285 pwd_hist_len = int(res[0]["pwdHistoryLength"][0])
1286 cur_min_pwd_len = int(res[0]["minPwdLength"][0])
1288 cur_min_pwd_age = int(abs(int(res[0]["minPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1289 if int(res[0]["maxPwdAge"][0]) == -0x8000000000000000:
1292 cur_max_pwd_age = int(abs(int(res[0]["maxPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1293 cur_account_lockout_threshold = int(res[0]["lockoutThreshold"][0])
1295 if int(res[0]["lockoutDuration"][0]) == -0x8000000000000000:
1296 cur_account_lockout_duration = 0
1298 cur_account_lockout_duration = abs(int(res[0]["lockoutDuration"][0])) / (1e7 * 60)
1299 cur_reset_account_lockout_after = abs(int(res[0]["lockOutObservationWindow"][0])) / (1e7 * 60)
1300 except Exception, e:
1301 raise CommandError("Could not retrieve password properties!", e)
1303 if subcommand == "show":
1304 self.message("Password informations for domain '%s'" % domain_dn)
1306 if pwd_props & DOMAIN_PASSWORD_COMPLEX != 0:
1307 self.message("Password complexity: on")
1309 self.message("Password complexity: off")
1310 if pwd_props & DOMAIN_PASSWORD_STORE_CLEARTEXT != 0:
1311 self.message("Store plaintext passwords: on")
1313 self.message("Store plaintext passwords: off")
1314 self.message("Password history length: %d" % pwd_hist_len)
1315 self.message("Minimum password length: %d" % cur_min_pwd_len)
1316 self.message("Minimum password age (days): %d" % cur_min_pwd_age)
1317 self.message("Maximum password age (days): %d" % cur_max_pwd_age)
1318 self.message("Account lockout duration (mins): %d" % cur_account_lockout_duration)
1319 self.message("Account lockout threshold (attempts): %d" % cur_account_lockout_threshold)
1320 self.message("Reset account lockout after (mins): %d" % cur_reset_account_lockout_after)
1321 elif subcommand == "set":
1324 m.dn = ldb.Dn(samdb, domain_dn)
1326 if complexity is not None:
1327 if complexity == "on" or complexity == "default":
1328 pwd_props = pwd_props | DOMAIN_PASSWORD_COMPLEX
1329 msgs.append("Password complexity activated!")
1330 elif complexity == "off":
1331 pwd_props = pwd_props & (~DOMAIN_PASSWORD_COMPLEX)
1332 msgs.append("Password complexity deactivated!")
1334 if store_plaintext is not None:
1335 if store_plaintext == "on" or store_plaintext == "default":
1336 pwd_props = pwd_props | DOMAIN_PASSWORD_STORE_CLEARTEXT
1337 msgs.append("Plaintext password storage for changed passwords activated!")
1338 elif store_plaintext == "off":
1339 pwd_props = pwd_props & (~DOMAIN_PASSWORD_STORE_CLEARTEXT)
1340 msgs.append("Plaintext password storage for changed passwords deactivated!")
1342 if complexity is not None or store_plaintext is not None:
1343 m["pwdProperties"] = ldb.MessageElement(str(pwd_props),
1344 ldb.FLAG_MOD_REPLACE, "pwdProperties")
1346 if history_length is not None:
1347 if history_length == "default":
1350 pwd_hist_len = int(history_length)
1352 if pwd_hist_len < 0 or pwd_hist_len > 24:
1353 raise CommandError("Password history length must be in the range of 0 to 24!")
1355 m["pwdHistoryLength"] = ldb.MessageElement(str(pwd_hist_len),
1356 ldb.FLAG_MOD_REPLACE, "pwdHistoryLength")
1357 msgs.append("Password history length changed!")
1359 if min_pwd_length is not None:
1360 if min_pwd_length == "default":
1363 min_pwd_len = int(min_pwd_length)
1365 if min_pwd_len < 0 or min_pwd_len > 14:
1366 raise CommandError("Minimum password length must be in the range of 0 to 14!")
1368 m["minPwdLength"] = ldb.MessageElement(str(min_pwd_len),
1369 ldb.FLAG_MOD_REPLACE, "minPwdLength")
1370 msgs.append("Minimum password length changed!")
1372 if min_pwd_age is not None:
1373 if min_pwd_age == "default":
1376 min_pwd_age = int(min_pwd_age)
1378 if min_pwd_age < 0 or min_pwd_age > 998:
1379 raise CommandError("Minimum password age must be in the range of 0 to 998!")
1382 min_pwd_age_ticks = -int(min_pwd_age * (24 * 60 * 60 * 1e7))
1384 m["minPwdAge"] = ldb.MessageElement(str(min_pwd_age_ticks),
1385 ldb.FLAG_MOD_REPLACE, "minPwdAge")
1386 msgs.append("Minimum password age changed!")
1388 if max_pwd_age is not None:
1389 if max_pwd_age == "default":
1392 max_pwd_age = int(max_pwd_age)
1394 if max_pwd_age < 0 or max_pwd_age > 999:
1395 raise CommandError("Maximum password age must be in the range of 0 to 999!")
1398 if max_pwd_age == 0:
1399 max_pwd_age_ticks = -0x8000000000000000
1401 max_pwd_age_ticks = -int(max_pwd_age * (24 * 60 * 60 * 1e7))
1403 m["maxPwdAge"] = ldb.MessageElement(str(max_pwd_age_ticks),
1404 ldb.FLAG_MOD_REPLACE, "maxPwdAge")
1405 msgs.append("Maximum password age changed!")
1407 if account_lockout_duration is not None:
1408 if account_lockout_duration == "default":
1409 account_lockout_duration = 30
1411 account_lockout_duration = int(account_lockout_duration)
1413 if account_lockout_duration < 0 or account_lockout_duration > 99999:
1414 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1417 if account_lockout_duration == 0:
1418 account_lockout_duration_ticks = -0x8000000000000000
1420 account_lockout_duration_ticks = -int(account_lockout_duration * (60 * 1e7))
1422 m["lockoutDuration"] = ldb.MessageElement(str(account_lockout_duration_ticks),
1423 ldb.FLAG_MOD_REPLACE, "lockoutDuration")
1424 msgs.append("Account lockout duration changed!")
1426 if account_lockout_threshold is not None:
1427 if account_lockout_threshold == "default":
1428 account_lockout_threshold = 0
1430 account_lockout_threshold = int(account_lockout_threshold)
1432 m["lockoutThreshold"] = ldb.MessageElement(str(account_lockout_threshold),
1433 ldb.FLAG_MOD_REPLACE, "lockoutThreshold")
1434 msgs.append("Account lockout threshold changed!")
1436 if reset_account_lockout_after is not None:
1437 if reset_account_lockout_after == "default":
1438 reset_account_lockout_after = 30
1440 reset_account_lockout_after = int(reset_account_lockout_after)
1442 if reset_account_lockout_after < 0 or reset_account_lockout_after > 99999:
1443 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1446 if reset_account_lockout_after == 0:
1447 reset_account_lockout_after_ticks = -0x8000000000000000
1449 reset_account_lockout_after_ticks = -int(reset_account_lockout_after * (60 * 1e7))
1451 m["lockOutObservationWindow"] = ldb.MessageElement(str(reset_account_lockout_after_ticks),
1452 ldb.FLAG_MOD_REPLACE, "lockOutObservationWindow")
1453 msgs.append("Duration to reset account lockout after changed!")
1455 if max_pwd_age > 0 and min_pwd_age >= max_pwd_age:
1456 raise CommandError("Maximum password age (%d) must be greater than minimum password age (%d)!" % (max_pwd_age, min_pwd_age))
1459 raise CommandError("You must specify at least one option to set. Try --help")
1461 msgs.append("All changes applied successfully!")
1462 self.message("\n".join(msgs))
1464 raise CommandError("Wrong argument '%s'!" % subcommand)
1467 class cmd_domain_classicupgrade(Command):
1468 """Upgrade from Samba classic (NT4-like) database to Samba AD DC database.
1470 Specify either a directory with all Samba classic DC databases and state files (with --dbdir) or
1471 the testparm utility from your classic installation (with --testparm).
1474 synopsis = "%prog [options] <classic_smb_conf>"
1476 takes_optiongroups = {
1477 "sambaopts": options.SambaOptions,
1478 "versionopts": options.VersionOptions
1482 Option("--dbdir", type="string", metavar="DIR",
1483 help="Path to samba classic DC database directory"),
1484 Option("--testparm", type="string", metavar="PATH",
1485 help="Path to samba classic DC testparm utility from the previous installation. This allows the default paths of the previous installation to be followed"),
1486 Option("--targetdir", type="string", metavar="DIR",
1487 help="Path prefix where the new Samba 4.0 AD domain should be initialised"),
1488 Option("--quiet", help="Be quiet", action="store_true"),
1489 Option("--verbose", help="Be verbose", action="store_true"),
1490 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
1491 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
1492 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
1493 "BIND9_FLATFILE uses bind9 text database to store zone information, "
1494 "BIND9_DLZ uses samba4 AD to store zone information, "
1495 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
1496 default="SAMBA_INTERNAL")
1500 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
1501 action="store_true"),
1502 Option("--use-xattrs", type="choice", choices=["yes","no","auto"],
1503 metavar="[yes|no|auto]",
1504 help="Define if we should use the native fs capabilities or a tdb file for "
1505 "storing attributes likes ntacl when --use-ntvfs is set. "
1506 "auto tries to make an inteligent guess based on the user rights and system capabilities",
1509 if samba.is_ntvfs_fileserver_built():
1510 takes_options.extend(ntvfs_options)
1512 takes_args = ["smbconf"]
1514 def run(self, smbconf=None, targetdir=None, dbdir=None, testparm=None,
1515 quiet=False, verbose=False, use_xattrs="auto", sambaopts=None, versionopts=None,
1516 dns_backend=None, use_ntvfs=False):
1518 if not os.path.exists(smbconf):
1519 raise CommandError("File %s does not exist" % smbconf)
1521 if testparm and not os.path.exists(testparm):
1522 raise CommandError("Testparm utility %s does not exist" % testparm)
1524 if dbdir and not os.path.exists(dbdir):
1525 raise CommandError("Directory %s does not exist" % dbdir)
1527 if not dbdir and not testparm:
1528 raise CommandError("Please specify either dbdir or testparm")
1530 logger = self.get_logger()
1532 logger.setLevel(logging.DEBUG)
1534 logger.setLevel(logging.WARNING)
1536 logger.setLevel(logging.INFO)
1538 if dbdir and testparm:
1539 logger.warning("both dbdir and testparm specified, ignoring dbdir.")
1542 lp = sambaopts.get_loadparm()
1544 s3conf = s3param.get_context()
1547 s3conf.set("realm", sambaopts.realm)
1549 if targetdir is not None:
1550 if not os.path.isdir(targetdir):
1554 if use_xattrs == "yes":
1556 elif use_xattrs == "auto" and use_ntvfs == False:
1558 elif use_ntvfs == False:
1559 raise CommandError("--use-xattrs=no requires --use-ntvfs (not supported for production use). "
1560 "Please re-run with --use-xattrs omitted.")
1561 elif use_xattrs == "auto" and not s3conf.get("posix:eadb"):
1563 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
1565 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
1568 samba.ntacls.setntacl(lp, tmpfile.name,
1569 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
1572 # FIXME: Don't catch all exceptions here
1573 logger.info("You are not root or your system does not support xattr, using tdb backend for attributes. "
1574 "If you intend to use this provision in production, rerun the script as root on a system supporting xattrs.")
1578 # Set correct default values from dbdir or testparm
1581 paths["state directory"] = dbdir
1582 paths["private dir"] = dbdir
1583 paths["lock directory"] = dbdir
1584 paths["smb passwd file"] = dbdir + "/smbpasswd"
1586 paths["state directory"] = get_testparm_var(testparm, smbconf, "state directory")
1587 paths["private dir"] = get_testparm_var(testparm, smbconf, "private dir")
1588 paths["smb passwd file"] = get_testparm_var(testparm, smbconf, "smb passwd file")
1589 paths["lock directory"] = get_testparm_var(testparm, smbconf, "lock directory")
1590 # "testparm" from Samba 3 < 3.4.x is not aware of the parameter
1591 # "state directory", instead make use of "lock directory"
1592 if len(paths["state directory"]) == 0:
1593 paths["state directory"] = paths["lock directory"]
1596 s3conf.set(p, paths[p])
1598 # load smb.conf parameters
1599 logger.info("Reading smb.conf")
1600 s3conf.load(smbconf)
1601 samba3 = Samba3(smbconf, s3conf)
1603 logger.info("Provisioning")
1604 upgrade_from_samba3(samba3, logger, targetdir, session_info=system_session(),
1605 useeadb=eadb, dns_backend=dns_backend, use_ntvfs=use_ntvfs)
1608 class cmd_domain_samba3upgrade(cmd_domain_classicupgrade):
1609 __doc__ = cmd_domain_classicupgrade.__doc__
1611 # This command is present for backwards compatibility only,
1612 # and should not be shown.
1616 class LocalDCCredentialsOptions(options.CredentialsOptions):
1617 def __init__(self, parser):
1618 options.CredentialsOptions.__init__(self, parser, special_name="local-dc")
1620 class DomainTrustCommand(Command):
1621 """List domain trusts."""
1624 Command.__init__(self)
1625 self.local_lp = None
1627 self.local_server = None
1628 self.local_binding_string = None
1629 self.local_creds = None
1631 self.remote_server = None
1632 self.remote_binding_string = None
1633 self.remote_creds = None
1635 def _uint32(self, v):
1636 return ctypes.c_uint32(v).value
1638 def check_runtime_error(self, runtime, val):
1642 err32 = self._uint32(runtime[0])
1648 class LocalRuntimeError(CommandError):
1649 def __init__(exception_self, self, runtime, message):
1650 err32 = self._uint32(runtime[0])
1652 msg = "LOCAL_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1653 self.local_server, message, err32, errstr)
1654 CommandError.__init__(exception_self, msg)
1656 class RemoteRuntimeError(CommandError):
1657 def __init__(exception_self, self, runtime, message):
1658 err32 = self._uint32(runtime[0])
1660 msg = "REMOTE_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1661 self.remote_server, message, err32, errstr)
1662 CommandError.__init__(exception_self, msg)
1664 class LocalLdbError(CommandError):
1665 def __init__(exception_self, self, ldb_error, message):
1666 errval = ldb_error[0]
1667 errstr = ldb_error[1]
1668 msg = "LOCAL_DC[%s]: %s - ERROR(%d) - %s" % (
1669 self.local_server, message, errval, errstr)
1670 CommandError.__init__(exception_self, msg)
1672 def setup_local_server(self, sambaopts, localdcopts):
1673 if self.local_server is not None:
1674 return self.local_server
1676 lp = sambaopts.get_loadparm()
1678 local_server = localdcopts.ipaddress
1679 if local_server is None:
1680 server_role = lp.server_role()
1681 if server_role != "ROLE_ACTIVE_DIRECTORY_DC":
1682 raise CommandError("Invalid server_role %s" % (server_role))
1683 local_server = lp.get('netbios name')
1684 local_transport = "ncalrpc"
1685 local_binding_options = ""
1686 local_binding_options += ",auth_type=ncalrpc_as_system"
1687 local_ldap_url = None
1690 local_transport = "ncacn_np"
1691 local_binding_options = ""
1692 local_ldap_url = "ldap://%s" % local_server
1693 local_creds = localdcopts.get_credentials(lp)
1697 self.local_server = local_server
1698 self.local_binding_string = "%s:%s[%s]" % (local_transport, local_server, local_binding_options)
1699 self.local_ldap_url = local_ldap_url
1700 self.local_creds = local_creds
1701 return self.local_server
1703 def new_local_lsa_connection(self):
1704 return lsa.lsarpc(self.local_binding_string, self.local_lp, self.local_creds)
1706 def new_local_netlogon_connection(self):
1707 return netlogon.netlogon(self.local_binding_string, self.local_lp, self.local_creds)
1709 def new_local_ldap_connection(self):
1710 return SamDB(url=self.local_ldap_url,
1711 session_info=system_session(),
1712 credentials=self.local_creds,
1715 def setup_remote_server(self, credopts, domain,
1717 require_writable=True):
1720 assert require_writable
1722 if self.remote_server is not None:
1723 return self.remote_server
1725 self.remote_server = "__unknown__remote_server__.%s" % domain
1726 assert self.local_server is not None
1728 remote_creds = credopts.get_credentials(self.local_lp)
1729 remote_server = credopts.ipaddress
1730 remote_binding_options = ""
1732 # TODO: we should also support NT4 domains
1733 # we could use local_netlogon.netr_DsRGetDCNameEx2() with the remote domain name
1734 # and delegate NBT or CLDAP to the local netlogon server
1736 remote_net = Net(remote_creds, self.local_lp, server=remote_server)
1737 remote_flags = nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS
1738 if require_writable:
1739 remote_flags |= nbt.NBT_SERVER_WRITABLE
1741 remote_flags |= nbt.NBT_SERVER_PDC
1742 remote_info = remote_net.finddc(flags=remote_flags, domain=domain, address=remote_server)
1744 raise CommandError("Failed to find a writeable DC for domain '%s'" % domain)
1746 nbt.NBT_SERVER_PDC: "PDC",
1747 nbt.NBT_SERVER_GC: "GC",
1748 nbt.NBT_SERVER_LDAP: "LDAP",
1749 nbt.NBT_SERVER_DS: "DS",
1750 nbt.NBT_SERVER_KDC: "KDC",
1751 nbt.NBT_SERVER_TIMESERV: "TIMESERV",
1752 nbt.NBT_SERVER_CLOSEST: "CLOSEST",
1753 nbt.NBT_SERVER_WRITABLE: "WRITABLE",
1754 nbt.NBT_SERVER_GOOD_TIMESERV: "GOOD_TIMESERV",
1755 nbt.NBT_SERVER_NDNC: "NDNC",
1756 nbt.NBT_SERVER_SELECT_SECRET_DOMAIN_6: "SELECT_SECRET_DOMAIN_6",
1757 nbt.NBT_SERVER_FULL_SECRET_DOMAIN_6: "FULL_SECRET_DOMAIN_6",
1758 nbt.NBT_SERVER_ADS_WEB_SERVICE: "ADS_WEB_SERVICE",
1759 nbt.NBT_SERVER_DS_8: "DS_8",
1760 nbt.NBT_SERVER_HAS_DNS_NAME: "HAS_DNS_NAME",
1761 nbt.NBT_SERVER_IS_DEFAULT_NC: "IS_DEFAULT_NC",
1762 nbt.NBT_SERVER_FOREST_ROOT: "FOREST_ROOT",
1764 server_type_string = self.generic_bitmap_to_string(flag_map,
1765 remote_info.server_type, names_only=True)
1766 self.outf.write("RemoteDC Netbios[%s] DNS[%s] ServerType[%s]\n" % (
1767 remote_info.pdc_name,
1768 remote_info.pdc_dns_name,
1769 server_type_string))
1771 self.remote_server = remote_info.pdc_dns_name
1772 self.remote_binding_string="ncacn_np:%s[%s]" % (self.remote_server, remote_binding_options)
1773 self.remote_creds = remote_creds
1774 return self.remote_server
1776 def new_remote_lsa_connection(self):
1777 return lsa.lsarpc(self.remote_binding_string, self.local_lp, self.remote_creds)
1779 def new_remote_netlogon_connection(self):
1780 return netlogon.netlogon(self.remote_binding_string, self.local_lp, self.remote_creds)
1782 def get_lsa_info(self, conn, policy_access):
1783 objectAttr = lsa.ObjectAttribute()
1784 objectAttr.sec_qos = lsa.QosInfo()
1786 policy = conn.OpenPolicy2(''.decode('utf-8'),
1787 objectAttr, policy_access)
1789 info = conn.QueryInfoPolicy2(policy, lsa.LSA_POLICY_INFO_DNS)
1791 return (policy, info)
1793 def get_netlogon_dc_info(self, conn, server):
1794 info = conn.netr_DsRGetDCNameEx2(server,
1795 None, 0, None, None, None,
1796 netlogon.DS_RETURN_DNS_NAME)
1799 def netr_DomainTrust_to_name(self, t):
1800 if t.trust_type == lsa.LSA_TRUST_TYPE_DOWNLEVEL:
1801 return t.netbios_name
1805 def netr_DomainTrust_to_type(self, a, t):
1807 primary_parent = None
1809 if _t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
1811 if not _t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1812 primary_parent = a[_t.parent_index]
1815 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1816 if t is primary_parent:
1819 if t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1822 parent = a[t.parent_index]
1823 if parent is primary:
1828 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1833 def netr_DomainTrust_to_transitive(self, t):
1834 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1837 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE:
1840 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1845 def netr_DomainTrust_to_direction(self, t):
1846 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND and \
1847 t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1850 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND:
1853 if t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1858 def generic_enum_to_string(self, e_dict, v, names_only=False):
1862 v32 = self._uint32(v)
1863 w = "__unknown__%08X__" % v32
1865 r = "0x%x (%s)" % (v, w)
1868 def generic_bitmap_to_string(self, b_dict, v, names_only=False):
1873 for b in sorted(b_dict.keys()):
1880 c32 = self._uint32(c)
1881 s += ["__unknown_%08X__" % c32]
1886 r = "0x%x (%s)" % (v, w)
1889 def trustType_string(self, v):
1891 lsa.LSA_TRUST_TYPE_DOWNLEVEL : "DOWNLEVEL",
1892 lsa.LSA_TRUST_TYPE_UPLEVEL : "UPLEVEL",
1893 lsa.LSA_TRUST_TYPE_MIT : "MIT",
1894 lsa.LSA_TRUST_TYPE_DCE : "DCE",
1896 return self.generic_enum_to_string(types, v)
1898 def trustDirection_string(self, v):
1900 lsa.LSA_TRUST_DIRECTION_INBOUND |
1901 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "BOTH",
1902 lsa.LSA_TRUST_DIRECTION_INBOUND : "INBOUND",
1903 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "OUTBOUND",
1905 return self.generic_enum_to_string(directions, v)
1907 def trustAttributes_string(self, v):
1909 lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE : "NON_TRANSITIVE",
1910 lsa.LSA_TRUST_ATTRIBUTE_UPLEVEL_ONLY : "UPLEVEL_ONLY",
1911 lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN : "QUARANTINED_DOMAIN",
1912 lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE : "FOREST_TRANSITIVE",
1913 lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION : "CROSS_ORGANIZATION",
1914 lsa.LSA_TRUST_ATTRIBUTE_WITHIN_FOREST : "WITHIN_FOREST",
1915 lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL : "TREAT_AS_EXTERNAL",
1916 lsa.LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION : "USES_RC4_ENCRYPTION",
1918 return self.generic_bitmap_to_string(attributes, v)
1920 def kerb_EncTypes_string(self, v):
1922 security.KERB_ENCTYPE_DES_CBC_CRC : "DES_CBC_CRC",
1923 security.KERB_ENCTYPE_DES_CBC_MD5 : "DES_CBC_MD5",
1924 security.KERB_ENCTYPE_RC4_HMAC_MD5 : "RC4_HMAC_MD5",
1925 security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96 : "AES128_CTS_HMAC_SHA1_96",
1926 security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96 : "AES256_CTS_HMAC_SHA1_96",
1927 security.KERB_ENCTYPE_FAST_SUPPORTED : "FAST_SUPPORTED",
1928 security.KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED : "COMPOUND_IDENTITY_SUPPORTED",
1929 security.KERB_ENCTYPE_CLAIMS_SUPPORTED : "CLAIMS_SUPPORTED",
1930 security.KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED : "RESOURCE_SID_COMPRESSION_DISABLED",
1932 return self.generic_bitmap_to_string(enctypes, v)
1934 def entry_tln_status(self, e_flags, ):
1936 return "Status[Enabled]"
1939 lsa.LSA_TLN_DISABLED_NEW : "Disabled-New",
1940 lsa.LSA_TLN_DISABLED_ADMIN : "Disabled",
1941 lsa.LSA_TLN_DISABLED_CONFLICT : "Disabled-Conflicting",
1943 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
1945 def entry_dom_status(self, e_flags):
1947 return "Status[Enabled]"
1950 lsa.LSA_SID_DISABLED_ADMIN : "Disabled-SID",
1951 lsa.LSA_SID_DISABLED_CONFLICT : "Disabled-SID-Conflicting",
1952 lsa.LSA_NB_DISABLED_ADMIN : "Disabled-NB",
1953 lsa.LSA_NB_DISABLED_CONFLICT : "Disabled-NB-Conflicting",
1955 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
1957 def write_forest_trust_info(self, fti, tln=None, collisions=None):
1959 tln_string = " TDO[%s]" % tln
1963 self.outf.write("Namespaces[%d]%s:\n" % (
1964 len(fti.entries), tln_string))
1966 for i in xrange(0, len(fti.entries)):
1970 collision_string = ""
1972 if collisions is not None:
1973 for c in collisions.entries:
1977 collision_string = " Collision[%s]" % (c.name.string)
1979 d = e.forest_trust_data
1980 if e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
1981 self.outf.write("TLN: %-32s DNS[*.%s]%s\n" % (
1982 self.entry_tln_status(flags),
1983 d.string, collision_string))
1984 elif e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
1985 self.outf.write("TLN_EX: %-29s DNS[*.%s]\n" % (
1987 elif e.type == lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
1988 self.outf.write("DOM: %-32s DNS[%s] Netbios[%s] SID[%s]%s\n" % (
1989 self.entry_dom_status(flags),
1990 d.dns_domain_name.string,
1991 d.netbios_domain_name.string,
1992 d.domain_sid, collision_string))
1995 class cmd_domain_trust_list(DomainTrustCommand):
1996 """List domain trusts."""
1998 synopsis = "%prog [options]"
2000 takes_optiongroups = {
2001 "sambaopts": options.SambaOptions,
2002 "versionopts": options.VersionOptions,
2003 "localdcopts": LocalDCCredentialsOptions,
2009 def run(self, sambaopts=None, versionopts=None, localdcopts=None):
2011 local_server = self.setup_local_server(sambaopts, localdcopts)
2013 local_netlogon = self.new_local_netlogon_connection()
2014 except RuntimeError as error:
2015 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2018 local_netlogon_trusts = local_netlogon.netr_DsrEnumerateDomainTrusts(local_server,
2019 netlogon.NETR_TRUST_FLAG_IN_FOREST |
2020 netlogon.NETR_TRUST_FLAG_OUTBOUND |
2021 netlogon.NETR_TRUST_FLAG_INBOUND)
2022 except RuntimeError as error:
2023 if self.check_runtime_error(error, werror.WERR_RPC_S_PROCNUM_OUT_OF_RANGE):
2024 # TODO: we could implement a fallback to lsa.EnumTrustDom()
2025 raise CommandError("LOCAL_DC[%s]: netr_DsrEnumerateDomainTrusts not supported." % (
2027 raise self.LocalRuntimeError(self, error, "netr_DsrEnumerateDomainTrusts failed")
2029 a = local_netlogon_trusts.array
2031 if t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
2033 self.outf.write("%-14s %-15s %-19s %s\n" % (
2034 "Type[%s]" % self.netr_DomainTrust_to_type(a, t),
2035 "Transitive[%s]" % self.netr_DomainTrust_to_transitive(t),
2036 "Direction[%s]" % self.netr_DomainTrust_to_direction(t),
2037 "Name[%s]" % self.netr_DomainTrust_to_name(t)))
2040 class cmd_domain_trust_show(DomainTrustCommand):
2041 """Show trusted domain details."""
2043 synopsis = "%prog NAME [options]"
2045 takes_optiongroups = {
2046 "sambaopts": options.SambaOptions,
2047 "versionopts": options.VersionOptions,
2048 "localdcopts": LocalDCCredentialsOptions,
2054 takes_args = ["domain"]
2056 def run(self, domain, sambaopts=None, versionopts=None, localdcopts=None):
2058 local_server = self.setup_local_server(sambaopts, localdcopts)
2060 local_lsa = self.new_local_lsa_connection()
2061 except RuntimeError as error:
2062 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2065 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2066 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2067 except RuntimeError as error:
2068 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2070 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2071 local_lsa_info.name.string,
2072 local_lsa_info.dns_domain.string,
2073 local_lsa_info.sid))
2075 lsaString = lsa.String()
2076 lsaString.string = domain
2078 local_tdo_full = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2079 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2080 local_tdo_info = local_tdo_full.info_ex
2081 local_tdo_posix = local_tdo_full.posix_offset
2082 except NTSTATUSError as error:
2083 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2084 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2086 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(FULL_INFO) failed")
2089 local_tdo_enctypes = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2090 lsaString, lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES)
2091 except NTSTATUSError as error:
2092 if self.check_runtime_error(error, ntstatus.NT_STATUS_INVALID_PARAMETER):
2094 if self.check_runtime_error(error, ntstatus.NT_STATUS_INVALID_INFO_CLASS):
2097 if error is not None:
2098 raise self.LocalRuntimeError(self, error,
2099 "QueryTrustedDomainInfoByName(SUPPORTED_ENCRYPTION_TYPES) failed")
2101 local_tdo_enctypes = lsa.TrustDomainInfoSupportedEncTypes()
2102 local_tdo_enctypes.enc_types = 0
2105 local_tdo_forest = None
2106 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2107 local_tdo_forest = local_lsa.lsaRQueryForestTrustInformation(local_policy,
2108 lsaString, lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
2109 except RuntimeError as error:
2110 if self.check_runtime_error(error, ntstatus.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
2112 if self.check_runtime_error(error, ntstatus.NT_STATUS_NOT_FOUND):
2114 if error is not None:
2115 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation failed")
2117 local_tdo_forest = lsa.ForestTrustInformation()
2118 local_tdo_forest.count = 0
2119 local_tdo_forest.entries = []
2121 self.outf.write("TrusteDomain:\n\n");
2122 self.outf.write("NetbiosName: %s\n" % local_tdo_info.netbios_name.string)
2123 if local_tdo_info.netbios_name.string != local_tdo_info.domain_name.string:
2124 self.outf.write("DnsName: %s\n" % local_tdo_info.domain_name.string)
2125 self.outf.write("SID: %s\n" % local_tdo_info.sid)
2126 self.outf.write("Type: %s\n" % self.trustType_string(local_tdo_info.trust_type))
2127 self.outf.write("Direction: %s\n" % self.trustDirection_string(local_tdo_info.trust_direction))
2128 self.outf.write("Attributes: %s\n" % self.trustAttributes_string(local_tdo_info.trust_attributes))
2129 posix_offset_u32 = ctypes.c_uint32(local_tdo_posix.posix_offset).value
2130 posix_offset_i32 = ctypes.c_int32(local_tdo_posix.posix_offset).value
2131 self.outf.write("PosixOffset: 0x%08X (%d)\n" % (posix_offset_u32, posix_offset_i32))
2132 self.outf.write("kerb_EncTypes: %s\n" % self.kerb_EncTypes_string(local_tdo_enctypes.enc_types))
2134 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2135 self.write_forest_trust_info(local_tdo_forest,
2136 tln=local_tdo_info.domain_name.string)
2140 class cmd_domain_trust_create(DomainTrustCommand):
2141 """Create a domain or forest trust."""
2143 synopsis = "%prog DOMAIN [options]"
2145 takes_optiongroups = {
2146 "sambaopts": options.SambaOptions,
2147 "versionopts": options.VersionOptions,
2148 "credopts": options.CredentialsOptions,
2149 "localdcopts": LocalDCCredentialsOptions,
2153 Option("--type", type="choice", metavar="TYPE",
2154 choices=["external", "forest"],
2155 help="The type of the trust: 'external' or 'forest'.",
2157 default="external"),
2158 Option("--direction", type="choice", metavar="DIRECTION",
2159 choices=["incoming", "outgoing", "both"],
2160 help="The trust direction: 'incoming', 'outgoing' or 'both'.",
2161 dest='trust_direction',
2163 Option("--create-location", type="choice", metavar="LOCATION",
2164 choices=["local", "both"],
2165 help="Where to create the trusted domain object: 'local' or 'both'.",
2166 dest='create_location',
2168 Option("--cross-organisation", action="store_true",
2169 help="The related domains does not belong to the same organisation.",
2170 dest='cross_organisation',
2172 Option("--quarantined", type="choice", metavar="yes|no",
2173 choices=["yes", "no", None],
2174 help="Special SID filtering rules are applied to the trust. "
2175 "With --type=external the default is yes. "
2176 "With --type=forest the default is no.",
2177 dest='quarantined_arg',
2179 Option("--not-transitive", action="store_true",
2180 help="The forest trust is not transitive.",
2181 dest='not_transitive',
2183 Option("--treat-as-external", action="store_true",
2184 help="The treat the forest trust as external.",
2185 dest='treat_as_external',
2187 Option("--no-aes-keys", action="store_false",
2188 help="The trust uses aes kerberos keys.",
2189 dest='use_aes_keys',
2191 Option("--skip-validation", action="store_false",
2192 help="Skip validation of the trust.",
2197 takes_args = ["domain"]
2199 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2200 trust_type=None, trust_direction=None, create_location=None,
2201 cross_organisation=False, quarantined_arg=None,
2202 not_transitive=False, treat_as_external=False,
2203 use_aes_keys=False, validate=True):
2205 lsaString = lsa.String()
2208 if quarantined_arg is None:
2209 if trust_type == 'external':
2211 elif quarantined_arg == 'yes':
2214 if trust_type != 'forest':
2216 raise CommandError("--not-transitive requires --type=forest")
2217 if treat_as_external:
2218 raise CommandError("--treat-as-external requires --type=forest")
2222 enc_types = lsa.TrustDomainInfoSupportedEncTypes()
2223 enc_types.enc_types = security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96
2224 enc_types.enc_types |= security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96
2226 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2227 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2228 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2230 local_trust_info = lsa.TrustDomainInfoInfoEx()
2231 local_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2232 local_trust_info.trust_direction = 0
2233 if trust_direction == "both":
2234 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2235 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2236 elif trust_direction == "incoming":
2237 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2238 elif trust_direction == "outgoing":
2239 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2240 local_trust_info.trust_attributes = 0
2241 if cross_organisation:
2242 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2244 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2245 if trust_type == "forest":
2246 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2248 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2249 if treat_as_external:
2250 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2252 def get_password(name):
2255 if password is not None and password is not '':
2257 password = getpass("New %s Password: " % name)
2258 passwordverify = getpass("Retype %s Password: " % name)
2259 if not password == passwordverify:
2261 self.outf.write("Sorry, passwords do not match.\n")
2263 incoming_secret = None
2264 outgoing_secret = None
2265 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2266 if create_location == "local":
2267 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2268 incoming_password = get_password("Incoming Trust")
2269 incoming_secret = string_to_byte_array(incoming_password.encode('utf-16-le'))
2270 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2271 outgoing_password = get_password("Outgoing Trust")
2272 outgoing_secret = string_to_byte_array(outgoing_password.encode('utf-16-le'))
2274 remote_trust_info = None
2276 # We use 240 random bytes.
2277 # Windows uses 28 or 240 random bytes. I guess it's
2278 # based on the trust type external vs. forest.
2280 # The initial trust password can be up to 512 bytes
2281 # while the versioned passwords used for periodic updates
2282 # can only be up to 498 bytes, as netr_ServerPasswordSet2()
2283 # needs to pass the NL_PASSWORD_VERSION structure within the
2284 # 512 bytes and a 2 bytes confounder is required.
2286 def random_trust_secret(length):
2287 pw = samba.generate_random_machine_password(length/2, length/2)
2288 return string_to_byte_array(pw.encode('utf-16-le'))
2290 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2291 incoming_secret = random_trust_secret(240)
2292 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2293 outgoing_secret = random_trust_secret(240)
2295 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2296 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2298 remote_trust_info = lsa.TrustDomainInfoInfoEx()
2299 remote_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2300 remote_trust_info.trust_direction = 0
2301 if trust_direction == "both":
2302 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2303 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2304 elif trust_direction == "incoming":
2305 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2306 elif trust_direction == "outgoing":
2307 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2308 remote_trust_info.trust_attributes = 0
2309 if cross_organisation:
2310 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2312 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2313 if trust_type == "forest":
2314 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2316 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2317 if treat_as_external:
2318 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2320 local_server = self.setup_local_server(sambaopts, localdcopts)
2322 local_lsa = self.new_local_lsa_connection()
2323 except RuntimeError as error:
2324 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2327 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2328 except RuntimeError as error:
2329 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2331 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2332 local_lsa_info.name.string,
2333 local_lsa_info.dns_domain.string,
2334 local_lsa_info.sid))
2337 remote_server = self.setup_remote_server(credopts, domain)
2338 except RuntimeError as error:
2339 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2342 remote_lsa = self.new_remote_lsa_connection()
2343 except RuntimeError as error:
2344 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2347 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2348 except RuntimeError as error:
2349 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2351 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2352 remote_lsa_info.name.string,
2353 remote_lsa_info.dns_domain.string,
2354 remote_lsa_info.sid))
2356 local_trust_info.domain_name.string = remote_lsa_info.dns_domain.string
2357 local_trust_info.netbios_name.string = remote_lsa_info.name.string
2358 local_trust_info.sid = remote_lsa_info.sid
2360 if remote_trust_info:
2361 remote_trust_info.domain_name.string = local_lsa_info.dns_domain.string
2362 remote_trust_info.netbios_name.string = local_lsa_info.name.string
2363 remote_trust_info.sid = local_lsa_info.sid
2366 lsaString.string = local_trust_info.domain_name.string
2367 local_old_netbios = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2368 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2369 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2370 except NTSTATUSError as error:
2371 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2372 raise self.LocalRuntimeError(self, error,
2373 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2377 lsaString.string = local_trust_info.netbios_name.string
2378 local_old_dns = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2379 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2380 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2381 except NTSTATUSError as error:
2382 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2383 raise self.LocalRuntimeError(self, error,
2384 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2387 if remote_trust_info:
2389 lsaString.string = remote_trust_info.domain_name.string
2390 remote_old_netbios = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2391 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2392 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2393 except NTSTATUSError as error:
2394 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2395 raise self.RemoteRuntimeError(self, error,
2396 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2400 lsaString.string = remote_trust_info.netbios_name.string
2401 remote_old_dns = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2402 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2403 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2404 except NTSTATUSError as error:
2405 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2406 raise self.RemoteRuntimeError(self, error,
2407 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2411 local_netlogon = self.new_local_netlogon_connection()
2412 except RuntimeError as error:
2413 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2416 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
2417 except RuntimeError as error:
2418 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
2420 if remote_trust_info:
2422 remote_netlogon = self.new_remote_netlogon_connection()
2423 except RuntimeError as error:
2424 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2427 remote_netlogon_info = self.get_netlogon_dc_info(remote_netlogon, remote_server)
2428 except RuntimeError as error:
2429 raise self.RemoteRuntimeError(self, error, "failed to get netlogon dc info")
2431 def generate_AuthInOutBlob(secret, update_time):
2433 blob = drsblobs.trustAuthInOutBlob()
2438 clear = drsblobs.AuthInfoClear()
2439 clear.size = len(secret)
2440 clear.password = secret
2442 info = drsblobs.AuthenticationInformation()
2443 info.LastUpdateTime = samba.unix2nttime(update_time)
2444 info.AuthType = lsa.TRUST_AUTH_TYPE_CLEAR
2445 info.AuthInfo = clear
2447 array = drsblobs.AuthenticationInformationArray()
2449 array.array = [info]
2451 blob = drsblobs.trustAuthInOutBlob()
2453 blob.current = array
2457 def generate_AuthInfoInternal(session_key, incoming=None, outgoing=None):
2458 confounder = [0] * 512
2459 for i in range(len(confounder)):
2460 confounder[i] = random.randint(0, 255)
2462 trustpass = drsblobs.trustDomainPasswords()
2464 trustpass.confounder = confounder
2465 trustpass.outgoing = outgoing
2466 trustpass.incoming = incoming
2468 trustpass_blob = ndr_pack(trustpass)
2470 encrypted_trustpass = arcfour_encrypt(session_key, trustpass_blob)
2472 auth_blob = lsa.DATA_BUF2()
2473 auth_blob.size = len(encrypted_trustpass)
2474 auth_blob.data = string_to_byte_array(encrypted_trustpass)
2476 auth_info = lsa.TrustDomainInfoAuthInfoInternal()
2477 auth_info.auth_blob = auth_blob
2481 update_time = samba.current_unix_time()
2482 incoming_blob = generate_AuthInOutBlob(incoming_secret, update_time)
2483 outgoing_blob = generate_AuthInOutBlob(outgoing_secret, update_time)
2485 local_tdo_handle = None
2486 remote_tdo_handle = None
2488 local_auth_info = generate_AuthInfoInternal(local_lsa.session_key,
2489 incoming=incoming_blob,
2490 outgoing=outgoing_blob)
2491 if remote_trust_info:
2492 remote_auth_info = generate_AuthInfoInternal(remote_lsa.session_key,
2493 incoming=outgoing_blob,
2494 outgoing=incoming_blob)
2497 if remote_trust_info:
2498 self.outf.write("Creating remote TDO.\n")
2499 current_request = { "location": "remote", "name": "CreateTrustedDomainEx2"}
2500 remote_tdo_handle = remote_lsa.CreateTrustedDomainEx2(remote_policy,
2503 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2504 self.outf.write("Remote TDO created.\n")
2506 self.outf.write("Setting supported encryption types on remote TDO.\n")
2507 current_request = { "location": "remote", "name": "SetInformationTrustedDomain"}
2508 remote_lsa.SetInformationTrustedDomain(remote_tdo_handle,
2509 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2512 self.outf.write("Creating local TDO.\n")
2513 current_request = { "location": "local", "name": "CreateTrustedDomainEx2"}
2514 local_tdo_handle = local_lsa.CreateTrustedDomainEx2(local_policy,
2517 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2518 self.outf.write("Local TDO created\n")
2520 self.outf.write("Setting supported encryption types on local TDO.\n")
2521 current_request = { "location": "local", "name": "SetInformationTrustedDomain"}
2522 local_lsa.SetInformationTrustedDomain(local_tdo_handle,
2523 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2525 except RuntimeError as error:
2526 self.outf.write("Error: %s failed %sly - cleaning up\n" % (
2527 current_request['name'], current_request['location']))
2528 if remote_tdo_handle:
2529 self.outf.write("Deleting remote TDO.\n")
2530 remote_lsa.DeleteObject(remote_tdo_handle)
2531 remote_tdo_handle = None
2532 if local_tdo_handle:
2533 self.outf.write("Deleting local TDO.\n")
2534 local_lsa.DeleteObject(local_tdo_handle)
2535 local_tdo_handle = None
2536 if current_request['location'] is "remote":
2537 raise self.RemoteRuntimeError(self, error, "%s" % (
2538 current_request['name']))
2539 raise self.LocalRuntimeError(self, error, "%s" % (
2540 current_request['name']))
2543 if local_trust_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2544 self.outf.write("Setup local forest trust information...\n")
2546 # get all information about the remote trust
2547 # this triggers netr_GetForestTrustInformation to the remote domain
2548 # and lsaRSetForestTrustInformation() locally, but new top level
2549 # names are disabled by default.
2550 local_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
2551 remote_lsa_info.dns_domain.string,
2552 netlogon.DS_GFTI_UPDATE_TDO)
2553 except RuntimeError as error:
2554 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2557 # here we try to enable all top level names
2558 local_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
2559 remote_lsa_info.dns_domain,
2560 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2563 except RuntimeError as error:
2564 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2566 self.write_forest_trust_info(local_forest_info,
2567 tln=remote_lsa_info.dns_domain.string,
2568 collisions=local_forest_collision)
2570 if remote_trust_info:
2571 self.outf.write("Setup remote forest trust information...\n")
2573 # get all information about the local trust (from the perspective of the remote domain)
2574 # this triggers netr_GetForestTrustInformation to our domain.
2575 # and lsaRSetForestTrustInformation() remotely, but new top level
2576 # names are disabled by default.
2577 remote_forest_info = remote_netlogon.netr_DsRGetForestTrustInformation(remote_netlogon_info.dc_unc,
2578 local_lsa_info.dns_domain.string,
2579 netlogon.DS_GFTI_UPDATE_TDO)
2580 except RuntimeError as error:
2581 raise self.RemoteRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2584 # here we try to enable all top level names
2585 remote_forest_collision = remote_lsa.lsaRSetForestTrustInformation(remote_policy,
2586 local_lsa_info.dns_domain,
2587 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2590 except RuntimeError as error:
2591 raise self.RemoteRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2593 self.write_forest_trust_info(remote_forest_info,
2594 tln=local_lsa_info.dns_domain.string,
2595 collisions=remote_forest_collision)
2597 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2598 self.outf.write("Validating outgoing trust...\n")
2600 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_netlogon_info.dc_unc,
2601 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2603 remote_lsa_info.dns_domain.string)
2604 except RuntimeError as error:
2605 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2607 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2608 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2610 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2611 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2612 local_trust_verify.trusted_dc_name,
2613 local_trust_verify.tc_connection_status[1],
2614 local_trust_verify.pdc_connection_status[1])
2616 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2617 local_trust_verify.trusted_dc_name,
2618 local_trust_verify.tc_connection_status[1],
2619 local_trust_verify.pdc_connection_status[1])
2621 if local_trust_status != werror.WERR_SUCCESS or local_conn_status != werror.WERR_SUCCESS:
2622 raise CommandError(local_validation)
2624 self.outf.write("OK: %s\n" % local_validation)
2626 if remote_trust_info:
2627 if remote_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2628 self.outf.write("Validating incoming trust...\n")
2630 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_netlogon_info.dc_unc,
2631 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2633 local_lsa_info.dns_domain.string)
2634 except RuntimeError as error:
2635 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2637 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2638 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2640 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2641 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2642 remote_trust_verify.trusted_dc_name,
2643 remote_trust_verify.tc_connection_status[1],
2644 remote_trust_verify.pdc_connection_status[1])
2646 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2647 remote_trust_verify.trusted_dc_name,
2648 remote_trust_verify.tc_connection_status[1],
2649 remote_trust_verify.pdc_connection_status[1])
2651 if remote_trust_status != werror.WERR_SUCCESS or remote_conn_status != werror.WERR_SUCCESS:
2652 raise CommandError(remote_validation)
2654 self.outf.write("OK: %s\n" % remote_validation)
2656 if remote_tdo_handle is not None:
2658 remote_lsa.Close(remote_tdo_handle)
2659 except RuntimeError as error:
2661 remote_tdo_handle = None
2662 if local_tdo_handle is not None:
2664 local_lsa.Close(local_tdo_handle)
2665 except RuntimeError as error:
2667 local_tdo_handle = None
2669 self.outf.write("Success.\n")
2672 class cmd_domain_trust_delete(DomainTrustCommand):
2673 """Delete a domain trust."""
2675 synopsis = "%prog DOMAIN [options]"
2677 takes_optiongroups = {
2678 "sambaopts": options.SambaOptions,
2679 "versionopts": options.VersionOptions,
2680 "credopts": options.CredentialsOptions,
2681 "localdcopts": LocalDCCredentialsOptions,
2685 Option("--delete-location", type="choice", metavar="LOCATION",
2686 choices=["local", "both"],
2687 help="Where to delete the trusted domain object: 'local' or 'both'.",
2688 dest='delete_location',
2692 takes_args = ["domain"]
2694 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2695 delete_location=None):
2697 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2698 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2699 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2701 if delete_location == "local":
2702 remote_policy_access = None
2704 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2705 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2706 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2708 local_server = self.setup_local_server(sambaopts, localdcopts)
2710 local_lsa = self.new_local_lsa_connection()
2711 except RuntimeError as error:
2712 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2715 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2716 except RuntimeError as error:
2717 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2719 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2720 local_lsa_info.name.string,
2721 local_lsa_info.dns_domain.string,
2722 local_lsa_info.sid))
2724 local_tdo_info = None
2725 local_tdo_handle = None
2726 remote_tdo_info = None
2727 remote_tdo_handle = None
2729 lsaString = lsa.String()
2731 lsaString.string = domain
2732 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2733 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2734 except NTSTATUSError as error:
2735 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2736 raise CommandError("Failed to find trust for domain '%s'" % domain)
2737 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2740 if remote_policy_access is not None:
2742 remote_server = self.setup_remote_server(credopts, domain)
2743 except RuntimeError as error:
2744 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2747 remote_lsa = self.new_remote_lsa_connection()
2748 except RuntimeError as error:
2749 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2752 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2753 except RuntimeError as error:
2754 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2756 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2757 remote_lsa_info.name.string,
2758 remote_lsa_info.dns_domain.string,
2759 remote_lsa_info.sid))
2761 if remote_lsa_info.sid != local_tdo_info.sid or \
2762 remote_lsa_info.name.string != local_tdo_info.netbios_name.string or \
2763 remote_lsa_info.dns_domain.string != local_tdo_info.domain_name.string:
2764 raise CommandError("LocalTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2765 local_tdo_info.netbios_name.string,
2766 local_tdo_info.domain_name.string,
2767 local_tdo_info.sid))
2770 lsaString.string = local_lsa_info.dns_domain.string
2771 remote_tdo_info = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2772 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2773 except NTSTATUSError as error:
2774 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2775 raise self.RemoteRuntimeError(self, error, "QueryTrustedDomainInfoByName(%s)" % (
2779 if remote_tdo_info is not None:
2780 if local_lsa_info.sid != remote_tdo_info.sid or \
2781 local_lsa_info.name.string != remote_tdo_info.netbios_name.string or \
2782 local_lsa_info.dns_domain.string != remote_tdo_info.domain_name.string:
2783 raise CommandError("RemoteTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2784 remote_tdo_info.netbios_name.string,
2785 remote_tdo_info.domain_name.string,
2786 remote_tdo_info.sid))
2788 if local_tdo_info is not None:
2790 lsaString.string = local_tdo_info.domain_name.string
2791 local_tdo_handle = local_lsa.OpenTrustedDomainByName(local_policy,
2793 security.SEC_STD_DELETE)
2794 except RuntimeError as error:
2795 raise self.LocalRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2798 local_lsa.DeleteObject(local_tdo_handle)
2799 local_tdo_handle = None
2801 if remote_tdo_info is not None:
2803 lsaString.string = remote_tdo_info.domain_name.string
2804 remote_tdo_handle = remote_lsa.OpenTrustedDomainByName(remote_policy,
2806 security.SEC_STD_DELETE)
2807 except RuntimeError as error:
2808 raise self.RemoteRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2811 if remote_tdo_handle is not None:
2813 remote_lsa.DeleteObject(remote_tdo_handle)
2814 remote_tdo_handle = None
2815 self.outf.write("RemoteTDO deleted.\n")
2816 except RuntimeError as error:
2817 self.outf.write("%s\n" % self.RemoteRuntimeError(self, error, "DeleteObject() failed"))
2819 if local_tdo_handle is not None:
2821 local_lsa.DeleteObject(local_tdo_handle)
2822 local_tdo_handle = None
2823 self.outf.write("LocalTDO deleted.\n")
2824 except RuntimeError as error:
2825 self.outf.write("%s\n" % self.LocalRuntimeError(self, error, "DeleteObject() failed"))
2829 class cmd_domain_trust_validate(DomainTrustCommand):
2830 """Validate a domain trust."""
2832 synopsis = "%prog DOMAIN [options]"
2834 takes_optiongroups = {
2835 "sambaopts": options.SambaOptions,
2836 "versionopts": options.VersionOptions,
2837 "credopts": options.CredentialsOptions,
2838 "localdcopts": LocalDCCredentialsOptions,
2842 Option("--validate-location", type="choice", metavar="LOCATION",
2843 choices=["local", "both"],
2844 help="Where to validate the trusted domain object: 'local' or 'both'.",
2845 dest='validate_location',
2849 takes_args = ["domain"]
2851 def run(self, domain, sambaopts=None, versionopts=None, credopts=None, localdcopts=None,
2852 validate_location=None):
2854 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2856 local_server = self.setup_local_server(sambaopts, localdcopts)
2858 local_lsa = self.new_local_lsa_connection()
2859 except RuntimeError as error:
2860 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2863 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2864 except RuntimeError as error:
2865 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2867 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2868 local_lsa_info.name.string,
2869 local_lsa_info.dns_domain.string,
2870 local_lsa_info.sid))
2873 lsaString = lsa.String()
2874 lsaString.string = domain
2875 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2876 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2877 except NTSTATUSError as error:
2878 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2879 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2881 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
2883 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
2884 local_tdo_info.netbios_name.string,
2885 local_tdo_info.domain_name.string,
2886 local_tdo_info.sid))
2889 local_netlogon = self.new_local_netlogon_connection()
2890 except RuntimeError as error:
2891 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2894 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_server,
2895 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2897 local_tdo_info.domain_name.string)
2898 except RuntimeError as error:
2899 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2901 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2902 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2904 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2905 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2906 local_trust_verify.trusted_dc_name,
2907 local_trust_verify.tc_connection_status[1],
2908 local_trust_verify.pdc_connection_status[1])
2910 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2911 local_trust_verify.trusted_dc_name,
2912 local_trust_verify.tc_connection_status[1],
2913 local_trust_verify.pdc_connection_status[1])
2915 if local_trust_status != werror.WERR_SUCCESS or local_conn_status != werror.WERR_SUCCESS:
2916 raise CommandError(local_validation)
2918 self.outf.write("OK: %s\n" % local_validation)
2921 server = local_trust_verify.trusted_dc_name.replace('\\', '')
2922 domain_and_server = "%s\\%s" % (local_tdo_info.domain_name.string, server)
2923 local_trust_rediscover = local_netlogon.netr_LogonControl2Ex(local_server,
2924 netlogon.NETLOGON_CONTROL_REDISCOVER,
2927 except RuntimeError as error:
2928 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
2930 local_conn_status = self._uint32(local_trust_rediscover.tc_connection_status[0])
2931 local_rediscover = "LocalRediscover: DC[%s] CONNECTION[%s]" % (
2932 local_trust_rediscover.trusted_dc_name,
2933 local_trust_rediscover.tc_connection_status[1])
2935 if local_conn_status != werror.WERR_SUCCESS:
2936 raise CommandError(local_rediscover)
2938 self.outf.write("OK: %s\n" % local_rediscover)
2940 if validate_location != "local":
2942 remote_server = self.setup_remote_server(credopts, domain, require_pdc=False)
2943 except RuntimeError as error:
2944 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2947 remote_netlogon = self.new_remote_netlogon_connection()
2948 except RuntimeError as error:
2949 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2952 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_server,
2953 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2955 local_lsa_info.dns_domain.string)
2956 except RuntimeError as error:
2957 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2959 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2960 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2962 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2963 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2964 remote_trust_verify.trusted_dc_name,
2965 remote_trust_verify.tc_connection_status[1],
2966 remote_trust_verify.pdc_connection_status[1])
2968 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2969 remote_trust_verify.trusted_dc_name,
2970 remote_trust_verify.tc_connection_status[1],
2971 remote_trust_verify.pdc_connection_status[1])
2973 if remote_trust_status != werror.WERR_SUCCESS or remote_conn_status != werror.WERR_SUCCESS:
2974 raise CommandError(remote_validation)
2976 self.outf.write("OK: %s\n" % remote_validation)
2979 server = remote_trust_verify.trusted_dc_name.replace('\\', '')
2980 domain_and_server = "%s\\%s" % (local_lsa_info.dns_domain.string, server)
2981 remote_trust_rediscover = remote_netlogon.netr_LogonControl2Ex(remote_server,
2982 netlogon.NETLOGON_CONTROL_REDISCOVER,
2985 except RuntimeError as error:
2986 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
2988 remote_conn_status = self._uint32(remote_trust_rediscover.tc_connection_status[0])
2990 remote_rediscover = "RemoteRediscover: DC[%s] CONNECTION[%s]" % (
2991 remote_trust_rediscover.trusted_dc_name,
2992 remote_trust_rediscover.tc_connection_status[1])
2994 if remote_conn_status != werror.WERR_SUCCESS:
2995 raise CommandError(remote_rediscover)
2997 self.outf.write("OK: %s\n" % remote_rediscover)
3001 class cmd_domain_trust_namespaces(DomainTrustCommand):
3002 """Manage forest trust namespaces."""
3004 synopsis = "%prog [DOMAIN] [options]"
3006 takes_optiongroups = {
3007 "sambaopts": options.SambaOptions,
3008 "versionopts": options.VersionOptions,
3009 "localdcopts": LocalDCCredentialsOptions,
3013 Option("--refresh", type="choice", metavar="check|store",
3014 choices=["check", "store", None],
3015 help="List and maybe store refreshed forest trust information: 'check' or 'store'.",
3018 Option("--enable-all", action="store_true",
3019 help="Try to update disabled entries, not allowed with --refresh=check.",
3022 Option("--enable-tln", action="append", metavar='DNSDOMAIN',
3023 help="Enable a top level name entry. Can be specified multiple times.",
3026 Option("--disable-tln", action="append", metavar='DNSDOMAIN',
3027 help="Disable a top level name entry. Can be specified multiple times.",
3030 Option("--add-tln-ex", action="append", metavar='DNSDOMAIN',
3031 help="Add a top level exclusion entry. Can be specified multiple times.",
3034 Option("--delete-tln-ex", action="append", metavar='DNSDOMAIN',
3035 help="Delete a top level exclusion entry. Can be specified multiple times.",
3036 dest='delete_tln_ex',
3038 Option("--enable-nb", action="append", metavar='NETBIOSDOMAIN',
3039 help="Enable a netbios name in a domain entry. Can be specified multiple times.",
3042 Option("--disable-nb", action="append", metavar='NETBIOSDOMAIN',
3043 help="Disable a netbios name in a domain entry. Can be specified multiple times.",
3046 Option("--enable-sid", action="append", metavar='DOMAINSID',
3047 help="Enable a SID in a domain entry. Can be specified multiple times.",
3048 dest='enable_sid_str',
3050 Option("--disable-sid", action="append", metavar='DOMAINSID',
3051 help="Disable a SID in a domain entry. Can be specified multiple times.",
3052 dest='disable_sid_str',
3054 Option("--add-upn-suffix", action="append", metavar='DNSDOMAIN',
3055 help="Add a new uPNSuffixes attribute for the local forest. Can be specified multiple times.",
3058 Option("--delete-upn-suffix", action="append", metavar='DNSDOMAIN',
3059 help="Delete an existing uPNSuffixes attribute of the local forest. Can be specified multiple times.",
3062 Option("--add-spn-suffix", action="append", metavar='DNSDOMAIN',
3063 help="Add a new msDS-SPNSuffixes attribute for the local forest. Can be specified multiple times.",
3066 Option("--delete-spn-suffix", action="append", metavar='DNSDOMAIN',
3067 help="Delete an existing msDS-SPNSuffixes attribute of the local forest. Can be specified multiple times.",
3072 takes_args = ["domain?"]
3074 def run(self, domain=None, sambaopts=None, localdcopts=None, versionopts=None,
3075 refresh=None, enable_all=False,
3076 enable_tln=[], disable_tln=[], add_tln_ex=[], delete_tln_ex=[],
3077 enable_sid_str=[], disable_sid_str=[], enable_nb=[], disable_nb=[],
3078 add_upn=[], delete_upn=[], add_spn=[], delete_spn=[]):
3080 require_update = False
3083 if refresh == "store":
3084 raise CommandError("--refresh=%s not allowed without DOMAIN" % refresh)
3087 raise CommandError("--enable-all not allowed without DOMAIN")
3089 if len(enable_tln) > 0:
3090 raise CommandError("--enable-tln not allowed without DOMAIN")
3091 if len(disable_tln) > 0:
3092 raise CommandError("--disable-tln not allowed without DOMAIN")
3094 if len(add_tln_ex) > 0:
3095 raise CommandError("--add-tln-ex not allowed without DOMAIN")
3096 if len(delete_tln_ex) > 0:
3097 raise CommandError("--delete-tln-ex not allowed without DOMAIN")
3099 if len(enable_nb) > 0:
3100 raise CommandError("--enable-nb not allowed without DOMAIN")
3101 if len(disable_nb) > 0:
3102 raise CommandError("--disable-nb not allowed without DOMAIN")
3104 if len(enable_sid_str) > 0:
3105 raise CommandError("--enable-sid not allowed without DOMAIN")
3106 if len(disable_sid_str) > 0:
3107 raise CommandError("--disable-sid not allowed without DOMAIN")
3109 if len(add_upn) > 0:
3111 if not n.startswith("*."):
3113 raise CommandError("value[%s] specified for --add-upn-suffix should not include with '*.'" % n)
3114 require_update = True
3115 if len(delete_upn) > 0:
3116 for n in delete_upn:
3117 if not n.startswith("*."):
3119 raise CommandError("value[%s] specified for --delete-upn-suffix should not include with '*.'" % n)
3120 require_update = True
3122 for d in delete_upn:
3123 if a.lower() != d.lower():
3125 raise CommandError("value[%s] specified for --add-upn-suffix and --delete-upn-suffix" % a)
3127 if len(add_spn) > 0:
3129 if not n.startswith("*."):
3131 raise CommandError("value[%s] specified for --add-spn-suffix should not include with '*.'" % n)
3132 require_update = True
3133 if len(delete_spn) > 0:
3134 for n in delete_spn:
3135 if not n.startswith("*."):
3137 raise CommandError("value[%s] specified for --delete-spn-suffix should not include with '*.'" % n)
3138 require_update = True
3140 for d in delete_spn:
3141 if a.lower() != d.lower():
3143 raise CommandError("value[%s] specified for --add-spn-suffix and --delete-spn-suffix" % a)
3145 if len(add_upn) > 0:
3146 raise CommandError("--add-upn-suffix not allowed together with DOMAIN")
3147 if len(delete_upn) > 0:
3148 raise CommandError("--delete-upn-suffix not allowed together with DOMAIN")
3149 if len(add_spn) > 0:
3150 raise CommandError("--add-spn-suffix not allowed together with DOMAIN")
3151 if len(delete_spn) > 0:
3152 raise CommandError("--delete-spn-suffix not allowed together with DOMAIN")
3154 if refresh is not None:
3155 if refresh == "store":
3156 require_update = True
3158 if enable_all and refresh != "store":
3159 raise CommandError("--enable-all not allowed together with --refresh=%s" % refresh)
3161 if len(enable_tln) > 0:
3162 raise CommandError("--enable-tln not allowed together with --refresh")
3163 if len(disable_tln) > 0:
3164 raise CommandError("--disable-tln not allowed together with --refresh")
3166 if len(add_tln_ex) > 0:
3167 raise CommandError("--add-tln-ex not allowed together with --refresh")
3168 if len(delete_tln_ex) > 0:
3169 raise CommandError("--delete-tln-ex not allowed together with --refresh")
3171 if len(enable_nb) > 0:
3172 raise CommandError("--enable-nb not allowed together with --refresh")
3173 if len(disable_nb) > 0:
3174 raise CommandError("--disable-nb not allowed together with --refresh")
3176 if len(enable_sid_str) > 0:
3177 raise CommandError("--enable-sid not allowed together with --refresh")
3178 if len(disable_sid_str) > 0:
3179 raise CommandError("--disable-sid not allowed together with --refresh")
3182 require_update = True
3184 if len(enable_tln) > 0:
3185 raise CommandError("--enable-tln not allowed together with --enable-all")
3187 if len(enable_nb) > 0:
3188 raise CommandError("--enable-nb not allowed together with --enable-all")
3190 if len(enable_sid_str) > 0:
3191 raise CommandError("--enable-sid not allowed together with --enable-all")
3193 if len(enable_tln) > 0:
3194 require_update = True
3195 if len(disable_tln) > 0:
3196 require_update = True
3197 for e in enable_tln:
3198 for d in disable_tln:
3199 if e.lower() != d.lower():
3201 raise CommandError("value[%s] specified for --enable-tln and --disable-tln" % e)
3203 if len(add_tln_ex) > 0:
3204 for n in add_tln_ex:
3205 if not n.startswith("*."):
3207 raise CommandError("value[%s] specified for --add-tln-ex should not include with '*.'" % n)
3208 require_update = True
3209 if len(delete_tln_ex) > 0:
3210 for n in delete_tln_ex:
3211 if not n.startswith("*."):
3213 raise CommandError("value[%s] specified for --delete-tln-ex should not include with '*.'" % n)
3214 require_update = True
3215 for a in add_tln_ex:
3216 for d in delete_tln_ex:
3217 if a.lower() != d.lower():
3219 raise CommandError("value[%s] specified for --add-tln-ex and --delete-tln-ex" % a)
3221 if len(enable_nb) > 0:
3222 require_update = True
3223 if len(disable_nb) > 0:
3224 require_update = True
3226 for d in disable_nb:
3227 if e.upper() != d.upper():
3229 raise CommandError("value[%s] specified for --enable-nb and --disable-nb" % e)
3232 for s in enable_sid_str:
3234 sid = security.dom_sid(s)
3235 except TypeError as error:
3236 raise CommandError("value[%s] specified for --enable-sid is not a valid SID" % s)
3237 enable_sid.append(sid)
3239 for s in disable_sid_str:
3241 sid = security.dom_sid(s)
3242 except TypeError as error:
3243 raise CommandError("value[%s] specified for --disable-sid is not a valid SID" % s)
3244 disable_sid.append(sid)
3245 if len(enable_sid) > 0:
3246 require_update = True
3247 if len(disable_sid) > 0:
3248 require_update = True
3249 for e in enable_sid:
3250 for d in disable_sid:
3253 raise CommandError("value[%s] specified for --enable-sid and --disable-sid" % e)
3255 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
3257 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
3259 local_server = self.setup_local_server(sambaopts, localdcopts)
3261 local_lsa = self.new_local_lsa_connection()
3262 except RuntimeError as error:
3263 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
3266 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
3267 except RuntimeError as error:
3268 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
3270 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
3271 local_lsa_info.name.string,
3272 local_lsa_info.dns_domain.string,
3273 local_lsa_info.sid))
3277 local_netlogon = self.new_local_netlogon_connection()
3278 except RuntimeError as error:
3279 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3282 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3283 except RuntimeError as error:
3284 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3286 if local_netlogon_info.domain_name != local_netlogon_info.forest_name:
3287 raise CommandError("The local domain [%s] is not the forest root [%s]" % (
3288 local_netlogon_info.domain_name,
3289 local_netlogon_info.forest_name))
3292 # get all information about our own forest
3293 own_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3295 except RuntimeError as error:
3296 if self.check_runtime_error(error, werror.WERR_RPC_S_PROCNUM_OUT_OF_RANGE):
3297 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3300 if self.check_runtime_error(error, werror.WERR_INVALID_FUNCTION):
3301 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3304 if self.check_runtime_error(error, werror.WERR_NERR_ACFNOTLOADED):
3305 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3308 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3310 self.outf.write("Own forest trust information...\n")
3311 self.write_forest_trust_info(own_forest_info,
3312 tln=local_lsa_info.dns_domain.string)
3315 local_samdb = self.new_local_ldap_connection()
3316 except RuntimeError as error:
3317 raise self.LocalRuntimeError(self, error, "failed to connect to SamDB")
3319 local_partitions_dn = "CN=Partitions,%s" % str(local_samdb.get_config_basedn())
3320 attrs = ['uPNSuffixes', 'msDS-SPNSuffixes']
3322 msgs = local_samdb.search(base=local_partitions_dn,
3323 scope=ldb.SCOPE_BASE,
3324 expression="(objectClass=crossRefContainer)",
3326 stored_msg = msgs[0]
3327 except ldb.LdbError as error:
3328 raise self.LocalLdbError(self, error, "failed to search partition dn")
3330 stored_upn_vals = []
3331 if 'uPNSuffixes' in stored_msg:
3332 stored_upn_vals.extend(stored_msg['uPNSuffixes'])
3334 stored_spn_vals = []
3335 if 'msDS-SPNSuffixes' in stored_msg:
3336 stored_spn_vals.extend(stored_msg['msDS-SPNSuffixes'])
3338 self.outf.write("Stored uPNSuffixes attributes[%d]:\n" % len(stored_upn_vals))
3339 for v in stored_upn_vals:
3340 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3341 self.outf.write("Stored msDS-SPNSuffixes attributes[%d]:\n" % len(stored_spn_vals))
3342 for v in stored_spn_vals:
3343 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3345 if not require_update:
3349 update_upn_vals = []
3350 update_upn_vals.extend(stored_upn_vals)
3353 update_spn_vals = []
3354 update_spn_vals.extend(stored_spn_vals)
3358 for i in xrange(0, len(update_upn_vals)):
3359 v = update_upn_vals[i]
3360 if v.lower() != upn.lower():
3365 raise CommandError("Entry already present for value[%s] specified for --add-upn-suffix" % upn)
3366 update_upn_vals.append(upn)
3369 for upn in delete_upn:
3371 for i in xrange(0, len(update_upn_vals)):
3372 v = update_upn_vals[i]
3373 if v.lower() != upn.lower():
3378 raise CommandError("Entry not found for value[%s] specified for --delete-upn-suffix" % upn)
3380 update_upn_vals.pop(idx)
3385 for i in xrange(0, len(update_spn_vals)):
3386 v = update_spn_vals[i]
3387 if v.lower() != spn.lower():
3392 raise CommandError("Entry already present for value[%s] specified for --add-spn-suffix" % spn)
3393 update_spn_vals.append(spn)
3396 for spn in delete_spn:
3398 for i in xrange(0, len(update_spn_vals)):
3399 v = update_spn_vals[i]
3400 if v.lower() != spn.lower():
3405 raise CommandError("Entry not found for value[%s] specified for --delete-spn-suffix" % spn)
3407 update_spn_vals.pop(idx)
3410 self.outf.write("Update uPNSuffixes attributes[%d]:\n" % len(update_upn_vals))
3411 for v in update_upn_vals:
3412 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3413 self.outf.write("Update msDS-SPNSuffixes attributes[%d]:\n" % len(update_spn_vals))
3414 for v in update_spn_vals:
3415 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3417 update_msg = ldb.Message()
3418 update_msg.dn = stored_msg.dn
3421 update_msg['uPNSuffixes'] = ldb.MessageElement(update_upn_vals,
3422 ldb.FLAG_MOD_REPLACE,
3425 update_msg['msDS-SPNSuffixes'] = ldb.MessageElement(update_spn_vals,
3426 ldb.FLAG_MOD_REPLACE,
3429 local_samdb.modify(update_msg)
3430 except ldb.LdbError as error:
3431 raise self.LocalLdbError(self, error, "failed to update partition dn")
3434 stored_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3436 except RuntimeError as error:
3437 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3439 self.outf.write("Stored forest trust information...\n")
3440 self.write_forest_trust_info(stored_forest_info,
3441 tln=local_lsa_info.dns_domain.string)
3445 lsaString = lsa.String()
3446 lsaString.string = domain
3447 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
3448 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
3449 except NTSTATUSError as error:
3450 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
3451 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
3453 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
3455 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
3456 local_tdo_info.netbios_name.string,
3457 local_tdo_info.domain_name.string,
3458 local_tdo_info.sid))
3460 if not local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
3461 raise CommandError("trusted domain object for domain [%s] is not marked as FOREST_TRANSITIVE." % domain)
3463 if refresh is not None:
3465 local_netlogon = self.new_local_netlogon_connection()
3466 except RuntimeError as error:
3467 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3470 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3471 except RuntimeError as error:
3472 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3474 lsa_update_check = 1
3475 if refresh == "store":
3476 netlogon_update_tdo = netlogon.DS_GFTI_UPDATE_TDO
3478 lsa_update_check = 0
3480 netlogon_update_tdo = 0
3483 # get all information about the remote trust
3484 # this triggers netr_GetForestTrustInformation to the remote domain
3485 # and lsaRSetForestTrustInformation() locally, but new top level
3486 # names are disabled by default.
3487 fresh_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3488 local_tdo_info.domain_name.string,
3489 netlogon_update_tdo)
3490 except RuntimeError as error:
3491 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3494 fresh_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3495 local_tdo_info.domain_name,
3496 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3499 except RuntimeError as error:
3500 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3502 self.outf.write("Fresh forest trust information...\n")
3503 self.write_forest_trust_info(fresh_forest_info,
3504 tln=local_tdo_info.domain_name.string,
3505 collisions=fresh_forest_collision)
3507 if refresh == "store":
3509 lsaString = lsa.String()
3510 lsaString.string = local_tdo_info.domain_name.string
3511 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3513 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3514 except RuntimeError as error:
3515 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3517 self.outf.write("Stored forest trust information...\n")
3518 self.write_forest_trust_info(stored_forest_info,
3519 tln=local_tdo_info.domain_name.string)
3524 # The none --refresh path
3528 lsaString = lsa.String()
3529 lsaString.string = local_tdo_info.domain_name.string
3530 local_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3532 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3533 except RuntimeError as error:
3534 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3536 self.outf.write("Local forest trust information...\n")
3537 self.write_forest_trust_info(local_forest_info,
3538 tln=local_tdo_info.domain_name.string)
3540 if not require_update:
3544 entries.extend(local_forest_info.entries)
3545 update_forest_info = lsa.ForestTrustInformation()
3546 update_forest_info.count = len(entries)
3547 update_forest_info.entries = entries
3550 for i in xrange(0, len(update_forest_info.entries)):
3551 r = update_forest_info.entries[i]
3552 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3554 if update_forest_info.entries[i].flags == 0:
3556 update_forest_info.entries[i].time = 0
3557 update_forest_info.entries[i].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3558 for i in xrange(0, len(update_forest_info.entries)):
3559 r = update_forest_info.entries[i]
3560 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3562 if update_forest_info.entries[i].flags == 0:
3564 update_forest_info.entries[i].time = 0
3565 update_forest_info.entries[i].flags &= ~lsa.LSA_NB_DISABLED_MASK
3566 update_forest_info.entries[i].flags &= ~lsa.LSA_SID_DISABLED_MASK
3568 for tln in enable_tln:
3570 for i in xrange(0, len(update_forest_info.entries)):
3571 r = update_forest_info.entries[i]
3572 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3574 if r.forest_trust_data.string.lower() != tln.lower():
3579 raise CommandError("Entry not found for value[%s] specified for --enable-tln" % tln)
3580 if not update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_MASK:
3581 raise CommandError("Entry found for value[%s] specified for --enable-tln is already enabled" % tln)
3582 update_forest_info.entries[idx].time = 0
3583 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3585 for tln in disable_tln:
3587 for i in xrange(0, len(update_forest_info.entries)):
3588 r = update_forest_info.entries[i]
3589 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3591 if r.forest_trust_data.string.lower() != tln.lower():
3596 raise CommandError("Entry not found for value[%s] specified for --disable-tln" % tln)
3597 if update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_ADMIN:
3598 raise CommandError("Entry found for value[%s] specified for --disable-tln is already disabled" % tln)
3599 update_forest_info.entries[idx].time = 0
3600 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3601 update_forest_info.entries[idx].flags |= lsa.LSA_TLN_DISABLED_ADMIN
3603 for tln_ex in add_tln_ex:
3605 for i in xrange(0, len(update_forest_info.entries)):
3606 r = update_forest_info.entries[i]
3607 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3609 if r.forest_trust_data.string.lower() != tln_ex.lower():
3614 raise CommandError("Entry already present for value[%s] specified for --add-tln-ex" % tln_ex)
3616 tln_dot = ".%s" % tln_ex.lower()
3618 for i in xrange(0, len(update_forest_info.entries)):
3619 r = update_forest_info.entries[i]
3620 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3622 r_dot = ".%s" % r.forest_trust_data.string.lower()
3623 if tln_dot == r_dot:
3624 raise CommandError("TLN entry present for value[%s] specified for --add-tln-ex" % tln_ex)
3625 if not tln_dot.endswith(r_dot):
3631 raise CommandError("No TLN parent present for value[%s] specified for --add-tln-ex" % tln_ex)
3633 r = lsa.ForestTrustRecord()
3634 r.type = lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX
3637 r.forest_trust_data.string = tln_ex
3640 entries.extend(update_forest_info.entries)
3641 entries.insert(idx + 1, r)
3642 update_forest_info.count = len(entries)
3643 update_forest_info.entries = entries
3645 for tln_ex in delete_tln_ex:
3647 for i in xrange(0, len(update_forest_info.entries)):
3648 r = update_forest_info.entries[i]
3649 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3651 if r.forest_trust_data.string.lower() != tln_ex.lower():
3656 raise CommandError("Entry not found for value[%s] specified for --delete-tln-ex" % tln_ex)
3659 entries.extend(update_forest_info.entries)
3661 update_forest_info.count = len(entries)
3662 update_forest_info.entries = entries
3664 for nb in enable_nb:
3666 for i in xrange(0, len(update_forest_info.entries)):
3667 r = update_forest_info.entries[i]
3668 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3670 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3675 raise CommandError("Entry not found for value[%s] specified for --enable-nb" % nb)
3676 if not update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_MASK:
3677 raise CommandError("Entry found for value[%s] specified for --enable-nb is already enabled" % nb)
3678 update_forest_info.entries[idx].time = 0
3679 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3681 for nb in disable_nb:
3683 for i in xrange(0, len(update_forest_info.entries)):
3684 r = update_forest_info.entries[i]
3685 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3687 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3692 raise CommandError("Entry not found for value[%s] specified for --delete-nb" % nb)
3693 if update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_ADMIN:
3694 raise CommandError("Entry found for value[%s] specified for --disable-nb is already disabled" % nb)
3695 update_forest_info.entries[idx].time = 0
3696 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3697 update_forest_info.entries[idx].flags |= lsa.LSA_NB_DISABLED_ADMIN
3699 for sid in enable_sid:
3701 for i in xrange(0, len(update_forest_info.entries)):
3702 r = update_forest_info.entries[i]
3703 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3705 if r.forest_trust_data.domain_sid != sid:
3710 raise CommandError("Entry not found for value[%s] specified for --enable-sid" % sid)
3711 if not update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_MASK:
3712 raise CommandError("Entry found for value[%s] specified for --enable-sid is already enabled" % nb)
3713 update_forest_info.entries[idx].time = 0
3714 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3716 for sid in disable_sid:
3718 for i in xrange(0, len(update_forest_info.entries)):
3719 r = update_forest_info.entries[i]
3720 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3722 if r.forest_trust_data.domain_sid != sid:
3727 raise CommandError("Entry not found for value[%s] specified for --delete-sid" % sid)
3728 if update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_ADMIN:
3729 raise CommandError("Entry found for value[%s] specified for --disable-sid is already disabled" % nb)
3730 update_forest_info.entries[idx].time = 0
3731 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3732 update_forest_info.entries[idx].flags |= lsa.LSA_SID_DISABLED_ADMIN
3735 update_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3736 local_tdo_info.domain_name,
3737 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3738 update_forest_info, 0)
3739 except RuntimeError as error:
3740 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3742 self.outf.write("Updated forest trust information...\n")
3743 self.write_forest_trust_info(update_forest_info,
3744 tln=local_tdo_info.domain_name.string,
3745 collisions=update_forest_collision)
3748 lsaString = lsa.String()
3749 lsaString.string = local_tdo_info.domain_name.string
3750 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3752 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3753 except RuntimeError as error:
3754 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3756 self.outf.write("Stored forest trust information...\n")
3757 self.write_forest_trust_info(stored_forest_info,
3758 tln=local_tdo_info.domain_name.string)
3761 class cmd_domain_tombstones_expunge(Command):
3762 """Expunge tombstones from the database.
3764 This command expunges tombstones from the database."""
3765 synopsis = "%prog NC [NC [...]] [options]"
3768 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
3769 metavar="URL", dest="H"),
3770 Option("--current-time",
3771 help="The current time to evaluate the tombstone lifetime from, expressed as YYYY-MM-DD",
3773 Option("--tombstone-lifetime", help="Number of days a tombstone should be preserved for", type=int),
3776 takes_args = ["nc*"]
3778 takes_optiongroups = {
3779 "sambaopts": options.SambaOptions,
3780 "credopts": options.CredentialsOptions,
3781 "versionopts": options.VersionOptions,
3784 def run(self, *ncs, **kwargs):
3785 sambaopts = kwargs.get("sambaopts")
3786 credopts = kwargs.get("credopts")
3787 versionpts = kwargs.get("versionopts")
3789 current_time_string = kwargs.get("current_time")
3790 tombstone_lifetime = kwargs.get("tombstone_lifetime")
3791 lp = sambaopts.get_loadparm()
3792 creds = credopts.get_credentials(lp)
3793 samdb = SamDB(url=H, session_info=system_session(),
3794 credentials=creds, lp=lp)
3796 if current_time_string is not None:
3797 current_time_obj = time.strptime(current_time_string, "%Y-%m-%d")
3798 current_time = long(time.mktime(current_time_obj))
3801 current_time = long(time.time())
3804 res = samdb.search(expression="", base="", scope=ldb.SCOPE_BASE,
3805 attrs=["namingContexts"])
3808 for nc in res[0]["namingContexts"]:
3813 started_transaction = False
3815 samdb.transaction_start()
3816 started_transaction = True
3818 removed_links) = samdb.garbage_collect_tombstones(ncs,
3819 current_time=current_time,
3820 tombstone_lifetime=tombstone_lifetime)
3822 except Exception, err:
3823 if started_transaction:
3824 samdb.transaction_cancel()
3825 raise CommandError("Failed to expunge / garbage collect tombstones", err)
3827 samdb.transaction_commit()
3829 self.outf.write("Removed %d objects and %d links successfully\n"
3830 % (removed_objects, removed_links))
3834 class cmd_domain_trust(SuperCommand):
3835 """Domain and forest trust management."""
3838 subcommands["list"] = cmd_domain_trust_list()
3839 subcommands["show"] = cmd_domain_trust_show()
3840 subcommands["create"] = cmd_domain_trust_create()
3841 subcommands["delete"] = cmd_domain_trust_delete()
3842 subcommands["validate"] = cmd_domain_trust_validate()
3843 subcommands["namespaces"] = cmd_domain_trust_namespaces()
3845 class cmd_domain_tombstones(SuperCommand):
3846 """Domain tombstone and recycled object management."""
3849 subcommands["expunge"] = cmd_domain_tombstones_expunge()
3851 class cmd_domain(SuperCommand):
3852 """Domain management."""
3855 subcommands["demote"] = cmd_domain_demote()
3856 if cmd_domain_export_keytab is not None:
3857 subcommands["exportkeytab"] = cmd_domain_export_keytab()
3858 subcommands["info"] = cmd_domain_info()
3859 subcommands["provision"] = cmd_domain_provision()
3860 subcommands["join"] = cmd_domain_join()
3861 subcommands["dcpromo"] = cmd_domain_dcpromo()
3862 subcommands["level"] = cmd_domain_level()
3863 subcommands["passwordsettings"] = cmd_domain_passwordsettings()
3864 subcommands["classicupgrade"] = cmd_domain_classicupgrade()
3865 subcommands["samba3upgrade"] = cmd_domain_samba3upgrade()
3866 subcommands["trust"] = cmd_domain_trust()
3867 subcommands["tombstones"] = cmd_domain_tombstones()