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 getpass import getpass
38 from samba.net import Net, LIBNET_JOIN_AUTOMATIC
40 from samba.join import join_RODC, join_DC, join_subdomain
41 from samba.auth import system_session
42 from samba.samdb import SamDB
43 from samba.ndr import ndr_unpack, ndr_pack, ndr_print
44 from samba.dcerpc import drsuapi
45 from samba.dcerpc import drsblobs
46 from samba.dcerpc import lsa
47 from samba.dcerpc import netlogon
48 from samba.dcerpc import security
49 from samba.dcerpc import nbt
50 from samba.dcerpc import misc
51 from samba.dcerpc.samr import DOMAIN_PASSWORD_COMPLEX, DOMAIN_PASSWORD_STORE_CLEARTEXT
52 from samba.netcmd import (
58 from samba.netcmd.common import netcmd_get_domain_infos_via_cldap
59 from samba.samba3 import Samba3
60 from samba.samba3 import param as s3param
61 from samba.upgrade import upgrade_from_samba3
62 from samba.drs_utils import (
63 sendDsReplicaSync, drsuapi_connect, drsException,
65 from samba import remove_dc, arcfour_encrypt, string_to_byte_array
67 from samba.dsdb import (
68 DS_DOMAIN_FUNCTION_2000,
69 DS_DOMAIN_FUNCTION_2003,
70 DS_DOMAIN_FUNCTION_2003_MIXED,
71 DS_DOMAIN_FUNCTION_2008,
72 DS_DOMAIN_FUNCTION_2008_R2,
73 DS_DOMAIN_FUNCTION_2012,
74 DS_DOMAIN_FUNCTION_2012_R2,
75 DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL,
76 DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL,
77 UF_WORKSTATION_TRUST_ACCOUNT,
78 UF_SERVER_TRUST_ACCOUNT,
79 UF_TRUSTED_FOR_DELEGATION,
80 UF_PARTIAL_SECRETS_ACCOUNT
83 from samba.provision import (
88 from samba.provision.common import (
94 def get_testparm_var(testparm, smbconf, varname):
95 errfile = open(os.devnull, 'w')
96 p = subprocess.Popen([testparm, '-s', '-l',
97 '--parameter-name=%s' % varname, smbconf],
98 stdout=subprocess.PIPE, stderr=errfile)
99 (out,err) = p.communicate()
101 lines = out.split('\n')
103 return lines[0].strip()
107 import samba.dckeytab
109 cmd_domain_export_keytab = None
111 class cmd_domain_export_keytab(Command):
112 """Dump Kerberos keys of the domain into a keytab."""
114 synopsis = "%prog <keytab> [options]"
116 takes_optiongroups = {
117 "sambaopts": options.SambaOptions,
118 "credopts": options.CredentialsOptions,
119 "versionopts": options.VersionOptions,
123 Option("--principal", help="extract only this principal", type=str),
126 takes_args = ["keytab"]
128 def run(self, keytab, credopts=None, sambaopts=None, versionopts=None, principal=None):
129 lp = sambaopts.get_loadparm()
131 net.export_keytab(keytab=keytab, principal=principal)
134 class cmd_domain_info(Command):
135 """Print basic info about a domain and the DC passed as parameter."""
137 synopsis = "%prog <ip_address> [options]"
142 takes_optiongroups = {
143 "sambaopts": options.SambaOptions,
144 "credopts": options.CredentialsOptions,
145 "versionopts": options.VersionOptions,
148 takes_args = ["address"]
150 def run(self, address, credopts=None, sambaopts=None, versionopts=None):
151 lp = sambaopts.get_loadparm()
153 res = netcmd_get_domain_infos_via_cldap(lp, None, address)
155 raise CommandError("Invalid IP address '" + address + "'!")
156 self.outf.write("Forest : %s\n" % res.forest)
157 self.outf.write("Domain : %s\n" % res.dns_domain)
158 self.outf.write("Netbios domain : %s\n" % res.domain_name)
159 self.outf.write("DC name : %s\n" % res.pdc_dns_name)
160 self.outf.write("DC netbios name : %s\n" % res.pdc_name)
161 self.outf.write("Server site : %s\n" % res.server_site)
162 self.outf.write("Client site : %s\n" % res.client_site)
165 class cmd_domain_provision(Command):
166 """Provision a domain."""
168 synopsis = "%prog [options]"
170 takes_optiongroups = {
171 "sambaopts": options.SambaOptions,
172 "versionopts": options.VersionOptions,
176 Option("--interactive", help="Ask for names", action="store_true"),
177 Option("--domain", type="string", metavar="DOMAIN",
178 help="NetBIOS domain name to use"),
179 Option("--domain-guid", type="string", metavar="GUID",
180 help="set domainguid (otherwise random)"),
181 Option("--domain-sid", type="string", metavar="SID",
182 help="set domainsid (otherwise random)"),
183 Option("--ntds-guid", type="string", metavar="GUID",
184 help="set NTDS object GUID (otherwise random)"),
185 Option("--invocationid", type="string", metavar="GUID",
186 help="set invocationid (otherwise random)"),
187 Option("--host-name", type="string", metavar="HOSTNAME",
188 help="set hostname"),
189 Option("--host-ip", type="string", metavar="IPADDRESS",
190 help="set IPv4 ipaddress"),
191 Option("--host-ip6", type="string", metavar="IP6ADDRESS",
192 help="set IPv6 ipaddress"),
193 Option("--site", type="string", metavar="SITENAME",
194 help="set site name"),
195 Option("--adminpass", type="string", metavar="PASSWORD",
196 help="choose admin password (otherwise random)"),
197 Option("--krbtgtpass", type="string", metavar="PASSWORD",
198 help="choose krbtgt password (otherwise random)"),
199 Option("--machinepass", type="string", metavar="PASSWORD",
200 help="choose machine password (otherwise random)"),
201 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
202 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
203 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
204 "BIND9_FLATFILE uses bind9 text database to store zone information, "
205 "BIND9_DLZ uses samba4 AD to store zone information, "
206 "NONE skips the DNS setup entirely (not recommended)",
207 default="SAMBA_INTERNAL"),
208 Option("--dnspass", type="string", metavar="PASSWORD",
209 help="choose dns password (otherwise random)"),
210 Option("--ldapadminpass", type="string", metavar="PASSWORD",
211 help="choose password to set between Samba and its LDAP backend (otherwise random)"),
212 Option("--root", type="string", metavar="USERNAME",
213 help="choose 'root' unix username"),
214 Option("--nobody", type="string", metavar="USERNAME",
215 help="choose 'nobody' user"),
216 Option("--users", type="string", metavar="GROUPNAME",
217 help="choose 'users' group"),
218 Option("--quiet", help="Be quiet", action="store_true"),
219 Option("--blank", action="store_true",
220 help="do not add users or groups, just the structure"),
221 Option("--ldap-backend-type", type="choice", metavar="LDAP-BACKEND-TYPE",
222 help="Test initialisation support for unsupported LDAP backend type (fedora-ds or openldap) DO NOT USE",
223 choices=["fedora-ds", "openldap"]),
224 Option("--server-role", type="choice", metavar="ROLE",
225 choices=["domain controller", "dc", "member server", "member", "standalone"],
226 help="The server role (domain controller | dc | member server | member | standalone). Default is dc.",
227 default="domain controller"),
228 Option("--function-level", type="choice", metavar="FOR-FUN-LEVEL",
229 choices=["2000", "2003", "2008", "2008_R2"],
230 help="The domain and forest function level (2000 | 2003 | 2008 | 2008_R2 - always native). Default is (Windows) 2008_R2 Native.",
232 Option("--next-rid", type="int", metavar="NEXTRID", default=1000,
233 help="The initial nextRid value (only needed for upgrades). Default is 1000."),
234 Option("--partitions-only",
235 help="Configure Samba's partitions, but do not modify them (ie, join a BDC)", action="store_true"),
236 Option("--targetdir", type="string", metavar="DIR",
237 help="Set target directory"),
238 Option("--ol-mmr-urls", type="string", metavar="LDAPSERVER",
239 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\""),
240 Option("--use-rfc2307", action="store_true", help="Use AD to store posix attributes (default = no)"),
244 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",
245 action="store_true"),
246 Option("--slapd-path", type="string", metavar="SLAPD-PATH",
247 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."),
248 Option("--ldap-backend-extra-port", type="int", metavar="LDAP-BACKEND-EXTRA-PORT", help="Additional TCP port for LDAP backend server (to use for replication)"),
249 Option("--ldap-backend-forced-uri", type="string", metavar="LDAP-BACKEND-FORCED-URI",
250 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"),
251 Option("--ldap-backend-nosync", help="Configure LDAP backend not to call fsync() (for performance in test environments)", action="store_true"),
255 Option("--use-ntvfs", action="store_true", help="Use NTVFS for the fileserver (default = no)"),
256 Option("--use-xattrs", type="choice", choices=["yes","no","auto"],
257 metavar="[yes|no|auto]",
258 help="Define if we should use the native fs capabilities or a tdb file for "
259 "storing attributes likes ntacl when --use-ntvfs is set. "
260 "auto tries to make an inteligent guess based on the user rights and system capabilities",
264 if os.getenv('TEST_LDAP', "no") == "yes":
265 takes_options.extend(openldap_options)
267 if samba.is_ntvfs_fileserver_built():
268 takes_options.extend(ntvfs_options)
272 def run(self, sambaopts=None, versionopts=None,
295 ldap_backend_type=None,
299 partitions_only=None,
306 ldap_backend_nosync=None,
307 ldap_backend_extra_port=None,
308 ldap_backend_forced_uri=None,
309 ldap_dryrun_mode=None):
311 self.logger = self.get_logger("provision")
313 self.logger.setLevel(logging.WARNING)
315 self.logger.setLevel(logging.INFO)
317 lp = sambaopts.get_loadparm()
318 smbconf = lp.configfile
320 if dns_forwarder is not None:
321 suggested_forwarder = dns_forwarder
323 suggested_forwarder = self._get_nameserver_ip()
324 if suggested_forwarder is None:
325 suggested_forwarder = "none"
327 if len(self.raw_argv) == 1:
331 from getpass import getpass
334 def ask(prompt, default=None):
335 if default is not None:
336 print "%s [%s]: " % (prompt, default),
338 print "%s: " % (prompt,),
339 return sys.stdin.readline().rstrip("\n") or default
342 default = socket.getfqdn().split(".", 1)[1].upper()
345 realm = ask("Realm", default)
346 if realm in (None, ""):
347 raise CommandError("No realm set!")
350 default = realm.split(".")[0]
353 domain = ask("Domain", default)
355 raise CommandError("No domain set!")
357 server_role = ask("Server Role (dc, member, standalone)", "dc")
359 dns_backend = ask("DNS backend (SAMBA_INTERNAL, BIND9_FLATFILE, BIND9_DLZ, NONE)", "SAMBA_INTERNAL")
360 if dns_backend in (None, ''):
361 raise CommandError("No DNS backend set!")
363 if dns_backend == "SAMBA_INTERNAL":
364 dns_forwarder = ask("DNS forwarder IP address (write 'none' to disable forwarding)", suggested_forwarder)
365 if dns_forwarder.lower() in (None, 'none'):
366 suggested_forwarder = None
370 adminpassplain = getpass("Administrator password: ")
371 if not adminpassplain:
372 self.errf.write("Invalid administrator password.\n")
374 adminpassverify = getpass("Retype password: ")
375 if not adminpassplain == adminpassverify:
376 self.errf.write("Sorry, passwords do not match.\n")
378 adminpass = adminpassplain
382 realm = sambaopts._lp.get('realm')
384 raise CommandError("No realm set!")
386 raise CommandError("No domain set!")
389 self.logger.info("Administrator password will be set randomly!")
391 if function_level == "2000":
392 dom_for_fun_level = DS_DOMAIN_FUNCTION_2000
393 elif function_level == "2003":
394 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
395 elif function_level == "2008":
396 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008
397 elif function_level == "2008_R2":
398 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
400 if dns_backend == "SAMBA_INTERNAL" and dns_forwarder is None:
401 dns_forwarder = suggested_forwarder
403 samdb_fill = FILL_FULL
405 samdb_fill = FILL_NT4SYNC
406 elif partitions_only:
407 samdb_fill = FILL_DRS
409 if targetdir is not None:
410 if not os.path.isdir(targetdir):
415 if use_xattrs == "yes":
417 elif use_xattrs == "auto" and use_ntvfs == False:
419 elif use_ntvfs == False:
420 raise CommandError("--use-xattrs=no requires --use-ntvfs (not supported for production use). "
421 "Please re-run with --use-xattrs omitted.")
422 elif use_xattrs == "auto" and not lp.get("posix:eadb"):
424 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
426 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
429 samba.ntacls.setntacl(lp, file.name,
430 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
433 self.logger.info("You are not root or your system does not support xattr, using tdb backend for attributes. ")
438 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.")
439 if ldap_backend_type == "existing":
440 if ldap_backend_forced_uri is not None:
441 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)
443 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")
445 if ldap_backend_forced_uri is not None:
446 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")
448 if domain_sid is not None:
449 domain_sid = security.dom_sid(domain_sid)
451 session = system_session()
453 result = provision(self.logger,
454 session, smbconf=smbconf, targetdir=targetdir,
455 samdb_fill=samdb_fill, realm=realm, domain=domain,
456 domainguid=domain_guid, domainsid=domain_sid,
458 hostip=host_ip, hostip6=host_ip6,
459 sitename=site, ntdsguid=ntds_guid,
460 invocationid=invocationid, adminpass=adminpass,
461 krbtgtpass=krbtgtpass, machinepass=machinepass,
462 dns_backend=dns_backend, dns_forwarder=dns_forwarder,
463 dnspass=dnspass, root=root, nobody=nobody,
465 serverrole=server_role, dom_for_fun_level=dom_for_fun_level,
466 backend_type=ldap_backend_type,
467 ldapadminpass=ldapadminpass, ol_mmr_urls=ol_mmr_urls, slapd_path=slapd_path,
468 useeadb=eadb, next_rid=next_rid, lp=lp, use_ntvfs=use_ntvfs,
469 use_rfc2307=use_rfc2307, skip_sysvolacl=False,
470 ldap_backend_extra_port=ldap_backend_extra_port,
471 ldap_backend_forced_uri=ldap_backend_forced_uri,
472 nosync=ldap_backend_nosync, ldap_dryrun_mode=ldap_dryrun_mode)
474 except ProvisioningError, e:
475 raise CommandError("Provision failed", e)
477 result.report_logger(self.logger)
479 def _get_nameserver_ip(self):
480 """Grab the nameserver IP address from /etc/resolv.conf."""
482 RESOLV_CONF="/etc/resolv.conf"
484 if not path.isfile(RESOLV_CONF):
485 self.logger.warning("Failed to locate %s" % RESOLV_CONF)
490 handle = open(RESOLV_CONF, 'r')
492 if not line.startswith('nameserver'):
494 # we want the last non-space continuous string of the line
495 return line.strip().split()[-1]
497 if handle is not None:
500 self.logger.warning("No nameserver found in %s" % RESOLV_CONF)
503 class cmd_domain_dcpromo(Command):
504 """Promote an existing domain member or NT4 PDC to an AD DC."""
506 synopsis = "%prog <dnsdomain> [DC|RODC] [options]"
508 takes_optiongroups = {
509 "sambaopts": options.SambaOptions,
510 "versionopts": options.VersionOptions,
511 "credopts": options.CredentialsOptions,
515 Option("--server", help="DC to join", type=str),
516 Option("--site", help="site to join", type=str),
517 Option("--targetdir", help="where to store provision", type=str),
518 Option("--domain-critical-only",
519 help="only replicate critical domain objects",
520 action="store_true"),
521 Option("--machinepass", type=str, metavar="PASSWORD",
522 help="choose machine password (otherwise random)"),
523 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
524 choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
525 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
526 "BIND9_DLZ uses samba4 AD to store zone information, "
527 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
528 default="SAMBA_INTERNAL"),
529 Option("--quiet", help="Be quiet", action="store_true"),
530 Option("--verbose", help="Be verbose", action="store_true")
534 Option("--use-ntvfs", action="store_true", help="Use NTVFS for the fileserver (default = no)"),
537 if samba.is_ntvfs_fileserver_built():
538 takes_options.extend(ntvfs_options)
541 takes_args = ["domain", "role?"]
543 def run(self, domain, role=None, sambaopts=None, credopts=None,
544 versionopts=None, server=None, site=None, targetdir=None,
545 domain_critical_only=False, parent_domain=None, machinepass=None,
546 use_ntvfs=False, dns_backend=None,
547 quiet=False, verbose=False):
548 lp = sambaopts.get_loadparm()
549 creds = credopts.get_credentials(lp)
550 net = Net(creds, lp, server=credopts.ipaddress)
553 site = "Default-First-Site-Name"
555 logger = self.get_logger()
557 logger.setLevel(logging.DEBUG)
559 logger.setLevel(logging.WARNING)
561 logger.setLevel(logging.INFO)
563 netbios_name = lp.get("netbios name")
569 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
570 site=site, netbios_name=netbios_name, targetdir=targetdir,
571 domain_critical_only=domain_critical_only,
572 machinepass=machinepass, use_ntvfs=use_ntvfs,
573 dns_backend=dns_backend,
574 promote_existing=True)
576 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
577 site=site, netbios_name=netbios_name, targetdir=targetdir,
578 domain_critical_only=domain_critical_only,
579 machinepass=machinepass, use_ntvfs=use_ntvfs, dns_backend=dns_backend,
580 promote_existing=True)
582 raise CommandError("Invalid role '%s' (possible values: DC, RODC)" % role)
585 class cmd_domain_join(Command):
586 """Join domain as either member or backup domain controller."""
588 synopsis = "%prog <dnsdomain> [DC|RODC|MEMBER|SUBDOMAIN] [options]"
590 takes_optiongroups = {
591 "sambaopts": options.SambaOptions,
592 "versionopts": options.VersionOptions,
593 "credopts": options.CredentialsOptions,
597 Option("--server", help="DC to join", type=str),
598 Option("--site", help="site to join", type=str),
599 Option("--targetdir", help="where to store provision", type=str),
600 Option("--parent-domain", help="parent domain to create subdomain under", type=str),
601 Option("--domain-critical-only",
602 help="only replicate critical domain objects",
603 action="store_true"),
604 Option("--machinepass", type=str, metavar="PASSWORD",
605 help="choose machine password (otherwise random)"),
606 Option("--adminpass", type="string", metavar="PASSWORD",
607 help="choose adminstrator password when joining as a subdomain (otherwise random)"),
608 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
609 choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
610 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
611 "BIND9_DLZ uses samba4 AD to store zone information, "
612 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
613 default="SAMBA_INTERNAL"),
614 Option("--quiet", help="Be quiet", action="store_true"),
615 Option("--verbose", help="Be verbose", action="store_true")
619 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
622 if samba.is_ntvfs_fileserver_built():
623 takes_options.extend(ntvfs_options)
625 takes_args = ["domain", "role?"]
627 def run(self, domain, role=None, sambaopts=None, credopts=None,
628 versionopts=None, server=None, site=None, targetdir=None,
629 domain_critical_only=False, parent_domain=None, machinepass=None,
630 use_ntvfs=False, dns_backend=None, adminpass=None,
631 quiet=False, verbose=False):
632 lp = sambaopts.get_loadparm()
633 creds = credopts.get_credentials(lp)
634 net = Net(creds, lp, server=credopts.ipaddress)
637 site = "Default-First-Site-Name"
639 logger = self.get_logger()
641 logger.setLevel(logging.DEBUG)
643 logger.setLevel(logging.WARNING)
645 logger.setLevel(logging.INFO)
647 netbios_name = lp.get("netbios name")
652 if role is None or role == "MEMBER":
653 (join_password, sid, domain_name) = net.join_member(
654 domain, netbios_name, LIBNET_JOIN_AUTOMATIC,
655 machinepass=machinepass)
657 self.errf.write("Joined domain %s (%s)\n" % (domain_name, sid))
659 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
660 site=site, netbios_name=netbios_name, targetdir=targetdir,
661 domain_critical_only=domain_critical_only,
662 machinepass=machinepass, use_ntvfs=use_ntvfs, dns_backend=dns_backend)
664 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
665 site=site, netbios_name=netbios_name, targetdir=targetdir,
666 domain_critical_only=domain_critical_only,
667 machinepass=machinepass, use_ntvfs=use_ntvfs,
668 dns_backend=dns_backend)
669 elif role == "SUBDOMAIN":
671 logger.info("Administrator password will be set randomly!")
673 netbios_domain = lp.get("workgroup")
674 if parent_domain is None:
675 parent_domain = ".".join(domain.split(".")[1:])
676 join_subdomain(logger=logger, server=server, creds=creds, lp=lp, dnsdomain=domain,
677 parent_domain=parent_domain, site=site,
678 netbios_name=netbios_name, netbios_domain=netbios_domain,
679 targetdir=targetdir, machinepass=machinepass,
680 use_ntvfs=use_ntvfs, dns_backend=dns_backend,
683 raise CommandError("Invalid role '%s' (possible values: MEMBER, DC, RODC, SUBDOMAIN)" % role)
686 class cmd_domain_demote(Command):
687 """Demote ourselves from the role of Domain Controller."""
689 synopsis = "%prog [options]"
692 Option("--server", help="writable DC to write demotion changes on", type=str),
693 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
694 metavar="URL", dest="H"),
695 Option("--remove-other-dead-server", help="Dead DC (name or NTDS GUID) "
696 "to remove ALL references to (rather than this DC)", type=str),
697 Option("--quiet", help="Be quiet", action="store_true"),
698 Option("--verbose", help="Be verbose", action="store_true"),
701 takes_optiongroups = {
702 "sambaopts": options.SambaOptions,
703 "credopts": options.CredentialsOptions,
704 "versionopts": options.VersionOptions,
707 def run(self, sambaopts=None, credopts=None,
708 versionopts=None, server=None,
709 remove_other_dead_server=None, H=None,
710 verbose=False, quiet=False):
711 lp = sambaopts.get_loadparm()
712 creds = credopts.get_credentials(lp)
713 net = Net(creds, lp, server=credopts.ipaddress)
715 logger = self.get_logger()
717 logger.setLevel(logging.DEBUG)
719 logger.setLevel(logging.WARNING)
721 logger.setLevel(logging.INFO)
723 if remove_other_dead_server is not None:
724 if server is not None:
725 samdb = SamDB(url="ldap://%s" % server,
726 session_info=system_session(),
727 credentials=creds, lp=lp)
729 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
731 remove_dc.remove_dc(samdb, logger, remove_other_dead_server)
732 except remove_dc.DemoteException as err:
733 raise CommandError("Demote failed: %s" % err)
736 netbios_name = lp.get("netbios name")
737 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
739 res = samdb.search(expression='(&(objectClass=computer)(serverReferenceBL=*))', attrs=["dnsHostName", "name"])
741 raise CommandError("Unable to search for servers")
744 raise CommandError("You are the latest server in the domain")
748 if str(e["name"]).lower() != netbios_name.lower():
749 server = e["dnsHostName"]
752 ntds_guid = samdb.get_ntds_GUID()
753 msg = samdb.search(base=str(samdb.get_config_basedn()),
754 scope=ldb.SCOPE_SUBTREE, expression="(objectGUID=%s)" % ntds_guid,
756 if len(msg) == 0 or "options" not in msg[0]:
757 raise CommandError("Failed to find options on %s" % ntds_guid)
760 dsa_options = int(str(msg[0]['options']))
762 res = samdb.search(expression="(fSMORoleOwner=%s)" % str(ntds_dn),
763 controls=["search_options:1:2"])
766 raise CommandError("Current DC is still the owner of %d role(s), use the role command to transfer roles to another DC" % len(res))
768 self.errf.write("Using %s as partner server for the demotion\n" %
770 (drsuapiBind, drsuapi_handle, supportedExtensions) = drsuapi_connect(server, lp, creds)
772 self.errf.write("Deactivating inbound replication\n")
774 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
778 dsa_options |= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
779 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
783 self.errf.write("Asking partner server %s to synchronize from us\n"
785 for part in (samdb.get_schema_basedn(),
786 samdb.get_config_basedn(),
787 samdb.get_root_basedn()):
788 nc = drsuapi.DsReplicaObjectIdentifier()
791 req1 = drsuapi.DsReplicaSyncRequest1()
792 req1.naming_context = nc;
793 req1.options = drsuapi.DRSUAPI_DRS_WRIT_REP
794 req1.source_dsa_guid = misc.GUID(ntds_guid)
797 drsuapiBind.DsReplicaSync(drsuapi_handle, 1, req1)
798 except RuntimeError as (werr, string):
799 if werr == 8452: #WERR_DS_DRA_NO_REPLICA
803 "Error while replicating out last local changes from '%s' for demotion, "
804 "re-enabling inbound replication\n" % part)
805 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
806 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
808 raise CommandError("Error while sending a DsReplicaSync for partition '%s'" % str(part), string)
810 remote_samdb = SamDB(url="ldap://%s" % server,
811 session_info=system_session(),
812 credentials=creds, lp=lp)
814 self.errf.write("Changing userControl and container\n")
815 res = remote_samdb.search(base=str(remote_samdb.domain_dn()),
816 expression="(&(objectClass=user)(sAMAccountName=%s$))" %
817 netbios_name.upper(),
818 attrs=["userAccountControl"])
820 uac = int(str(res[0]["userAccountControl"]))
824 "Error while demoting, re-enabling inbound replication\n")
825 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
826 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
828 raise CommandError("Error while changing account control", e)
832 "Error while demoting, re-enabling inbound replication")
833 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
834 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
836 raise CommandError("Unable to find object with samaccountName = %s$"
837 " in the remote dc" % netbios_name.upper())
841 uac &= ~(UF_SERVER_TRUST_ACCOUNT|UF_TRUSTED_FOR_DELEGATION|UF_PARTIAL_SECRETS_ACCOUNT)
842 uac |= UF_WORKSTATION_TRUST_ACCOUNT
847 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
848 ldb.FLAG_MOD_REPLACE,
849 "userAccountControl")
851 remote_samdb.modify(msg)
854 "Error while demoting, re-enabling inbound replication")
855 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
856 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
859 raise CommandError("Error while changing account control", e)
861 parent = msg.dn.parent()
862 dc_name = res[0].dn.get_rdn_value()
863 rdn = "CN=%s" % dc_name
865 # Let's move to the Computer container
869 computer_dn = ldb.Dn(remote_samdb, "CN=Computers,%s" % str(remote_samdb.domain_dn()))
870 res = remote_samdb.search(base=computer_dn, expression=rdn, scope=ldb.SCOPE_ONELEVEL)
873 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
874 scope=ldb.SCOPE_ONELEVEL)
875 while(len(res) != 0 and i < 100):
877 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
878 scope=ldb.SCOPE_ONELEVEL)
882 "Error while demoting, re-enabling inbound replication\n")
883 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
884 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
890 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
891 ldb.FLAG_MOD_REPLACE,
892 "userAccountControl")
894 remote_samdb.modify(msg)
896 raise CommandError("Unable to find a slot for renaming %s,"
897 " all names from %s-1 to %s-%d seemed used" %
898 (str(dc_dn), rdn, rdn, i - 9))
900 newrdn = "%s-%d" % (rdn, i)
903 newdn = ldb.Dn(remote_samdb, "%s,%s" % (newrdn, str(computer_dn)))
904 remote_samdb.rename(dc_dn, newdn)
907 "Error while demoting, re-enabling inbound replication\n")
908 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
909 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
915 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
916 ldb.FLAG_MOD_REPLACE,
917 "userAccountControl")
919 remote_samdb.modify(msg)
920 raise CommandError("Error while renaming %s to %s" % (str(dc_dn), str(newdn)), e)
923 server_dsa_dn = samdb.get_serverName()
924 domain = remote_samdb.get_root_basedn()
927 req1 = drsuapi.DsRemoveDSServerRequest1()
928 req1.server_dn = str(server_dsa_dn)
929 req1.domain_dn = str(domain)
932 drsuapiBind.DsRemoveDSServer(drsuapi_handle, 1, req1)
933 except RuntimeError as (werr, string):
934 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
936 "Error while demoting, re-enabling inbound replication\n")
937 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
938 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
944 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
945 ldb.FLAG_MOD_REPLACE,
946 "userAccountControl")
947 remote_samdb.modify(msg)
948 remote_samdb.rename(newdn, dc_dn)
949 if werr == 8452: #WERR_DS_DRA_NO_REPLICA
950 raise CommandError("The DC %s is not present on (already removed from) the remote server: " % server_dsa_dn, e)
952 raise CommandError("Error while sending a removeDsServer of %s: " % server_dsa_dn, e)
954 remove_dc.remove_sysvol_references(remote_samdb, logger, dc_name)
956 # These are objects under the computer account that should be deleted
957 for s in ("CN=Enterprise,CN=NTFRS Subscriptions",
958 "CN=%s, CN=NTFRS Subscriptions" % lp.get("realm"),
959 "CN=Domain system Volumes (SYSVOL Share), CN=NTFRS Subscriptions",
960 "CN=NTFRS Subscriptions"):
962 remote_samdb.delete(ldb.Dn(remote_samdb,
963 "%s,%s" % (s, str(newdn))))
964 except ldb.LdbError, l:
967 self.errf.write("Demote successful\n")
970 class cmd_domain_level(Command):
971 """Raise domain and forest function levels."""
973 synopsis = "%prog (show|raise <options>) [options]"
975 takes_optiongroups = {
976 "sambaopts": options.SambaOptions,
977 "credopts": options.CredentialsOptions,
978 "versionopts": options.VersionOptions,
982 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
983 metavar="URL", dest="H"),
984 Option("--quiet", help="Be quiet", action="store_true"),
985 Option("--forest-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
986 help="The forest function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)"),
987 Option("--domain-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
988 help="The domain function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)")
991 takes_args = ["subcommand"]
993 def run(self, subcommand, H=None, forest_level=None, domain_level=None,
994 quiet=False, credopts=None, sambaopts=None, versionopts=None):
995 lp = sambaopts.get_loadparm()
996 creds = credopts.get_credentials(lp, fallback_machine=True)
998 samdb = SamDB(url=H, session_info=system_session(),
999 credentials=creds, lp=lp)
1001 domain_dn = samdb.domain_dn()
1003 res_forest = samdb.search("CN=Partitions,%s" % samdb.get_config_basedn(),
1004 scope=ldb.SCOPE_BASE, attrs=["msDS-Behavior-Version"])
1005 assert len(res_forest) == 1
1007 res_domain = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1008 attrs=["msDS-Behavior-Version", "nTMixedDomain"])
1009 assert len(res_domain) == 1
1011 res_dc_s = samdb.search("CN=Sites,%s" % samdb.get_config_basedn(),
1012 scope=ldb.SCOPE_SUBTREE, expression="(objectClass=nTDSDSA)",
1013 attrs=["msDS-Behavior-Version"])
1014 assert len(res_dc_s) >= 1
1016 # default values, since "msDS-Behavior-Version" does not exist on Windows 2000 AD
1017 level_forest = DS_DOMAIN_FUNCTION_2000
1018 level_domain = DS_DOMAIN_FUNCTION_2000
1020 if "msDS-Behavior-Version" in res_forest[0]:
1021 level_forest = int(res_forest[0]["msDS-Behavior-Version"][0])
1022 if "msDS-Behavior-Version" in res_domain[0]:
1023 level_domain = int(res_domain[0]["msDS-Behavior-Version"][0])
1024 level_domain_mixed = int(res_domain[0]["nTMixedDomain"][0])
1027 for msg in res_dc_s:
1028 if "msDS-Behavior-Version" in msg:
1029 if min_level_dc is None or int(msg["msDS-Behavior-Version"][0]) < min_level_dc:
1030 min_level_dc = int(msg["msDS-Behavior-Version"][0])
1032 min_level_dc = DS_DOMAIN_FUNCTION_2000
1033 # well, this is the least
1036 if level_forest < DS_DOMAIN_FUNCTION_2000 or level_domain < DS_DOMAIN_FUNCTION_2000:
1037 raise CommandError("Domain and/or forest function level(s) is/are invalid. Correct them or reprovision!")
1038 if min_level_dc < DS_DOMAIN_FUNCTION_2000:
1039 raise CommandError("Lowest function level of a DC is invalid. Correct this or reprovision!")
1040 if level_forest > level_domain:
1041 raise CommandError("Forest function level is higher than the domain level(s). Correct this or reprovision!")
1042 if level_domain > min_level_dc:
1043 raise CommandError("Domain function level is higher than the lowest function level of a DC. Correct this or reprovision!")
1045 if subcommand == "show":
1046 self.message("Domain and forest function level for domain '%s'" % domain_dn)
1047 if level_forest == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1048 self.message("\nATTENTION: You run SAMBA 4 on a forest function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1049 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1050 self.message("\nATTENTION: You run SAMBA 4 on a domain function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1051 if min_level_dc == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1052 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)!")
1056 if level_forest == DS_DOMAIN_FUNCTION_2000:
1058 elif level_forest == DS_DOMAIN_FUNCTION_2003_MIXED:
1059 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1060 elif level_forest == DS_DOMAIN_FUNCTION_2003:
1062 elif level_forest == DS_DOMAIN_FUNCTION_2008:
1064 elif level_forest == DS_DOMAIN_FUNCTION_2008_R2:
1066 elif level_forest == DS_DOMAIN_FUNCTION_2012:
1068 elif level_forest == DS_DOMAIN_FUNCTION_2012_R2:
1071 outstr = "higher than 2012 R2"
1072 self.message("Forest function level: (Windows) " + outstr)
1074 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1075 outstr = "2000 mixed (NT4 DC support)"
1076 elif level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed == 0:
1078 elif level_domain == DS_DOMAIN_FUNCTION_2003_MIXED:
1079 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1080 elif level_domain == DS_DOMAIN_FUNCTION_2003:
1082 elif level_domain == DS_DOMAIN_FUNCTION_2008:
1084 elif level_domain == DS_DOMAIN_FUNCTION_2008_R2:
1086 elif level_domain == DS_DOMAIN_FUNCTION_2012:
1088 elif level_domain == DS_DOMAIN_FUNCTION_2012_R2:
1091 outstr = "higher than 2012 R2"
1092 self.message("Domain function level: (Windows) " + outstr)
1094 if min_level_dc == DS_DOMAIN_FUNCTION_2000:
1096 elif min_level_dc == DS_DOMAIN_FUNCTION_2003:
1098 elif min_level_dc == DS_DOMAIN_FUNCTION_2008:
1100 elif min_level_dc == DS_DOMAIN_FUNCTION_2008_R2:
1102 elif min_level_dc == DS_DOMAIN_FUNCTION_2012:
1104 elif min_level_dc == DS_DOMAIN_FUNCTION_2012_R2:
1107 outstr = "higher than 2012 R2"
1108 self.message("Lowest function level of a DC: (Windows) " + outstr)
1110 elif subcommand == "raise":
1113 if domain_level is not None:
1114 if domain_level == "2003":
1115 new_level_domain = DS_DOMAIN_FUNCTION_2003
1116 elif domain_level == "2008":
1117 new_level_domain = DS_DOMAIN_FUNCTION_2008
1118 elif domain_level == "2008_R2":
1119 new_level_domain = DS_DOMAIN_FUNCTION_2008_R2
1120 elif domain_level == "2012":
1121 new_level_domain = DS_DOMAIN_FUNCTION_2012
1122 elif domain_level == "2012_R2":
1123 new_level_domain = DS_DOMAIN_FUNCTION_2012_R2
1125 if new_level_domain <= level_domain and level_domain_mixed == 0:
1126 raise CommandError("Domain function level can't be smaller than or equal to the actual one!")
1127 if new_level_domain > min_level_dc:
1128 raise CommandError("Domain function level can't be higher than the lowest function level of a DC!")
1130 # Deactivate mixed/interim domain support
1131 if level_domain_mixed != 0:
1132 # Directly on the base DN
1134 m.dn = ldb.Dn(samdb, domain_dn)
1135 m["nTMixedDomain"] = ldb.MessageElement("0",
1136 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1140 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup") + ",CN=Partitions,%s" % samdb.get_config_basedn())
1141 m["nTMixedDomain"] = ldb.MessageElement("0",
1142 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1145 except ldb.LdbError, (enum, emsg):
1146 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1149 # Directly on the base DN
1151 m.dn = ldb.Dn(samdb, domain_dn)
1152 m["msDS-Behavior-Version"]= ldb.MessageElement(
1153 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1154 "msDS-Behavior-Version")
1158 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup")
1159 + ",CN=Partitions,%s" % samdb.get_config_basedn())
1160 m["msDS-Behavior-Version"]= ldb.MessageElement(
1161 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1162 "msDS-Behavior-Version")
1165 except ldb.LdbError, (enum, emsg):
1166 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1169 level_domain = new_level_domain
1170 msgs.append("Domain function level changed!")
1172 if forest_level is not None:
1173 if forest_level == "2003":
1174 new_level_forest = DS_DOMAIN_FUNCTION_2003
1175 elif forest_level == "2008":
1176 new_level_forest = DS_DOMAIN_FUNCTION_2008
1177 elif forest_level == "2008_R2":
1178 new_level_forest = DS_DOMAIN_FUNCTION_2008_R2
1179 elif forest_level == "2012":
1180 new_level_forest = DS_DOMAIN_FUNCTION_2012
1181 elif forest_level == "2012_R2":
1182 new_level_forest = DS_DOMAIN_FUNCTION_2012_R2
1184 if new_level_forest <= level_forest:
1185 raise CommandError("Forest function level can't be smaller than or equal to the actual one!")
1186 if new_level_forest > level_domain:
1187 raise CommandError("Forest function level can't be higher than the domain function level(s). Please raise it/them first!")
1190 m.dn = ldb.Dn(samdb, "CN=Partitions,%s" % samdb.get_config_basedn())
1191 m["msDS-Behavior-Version"]= ldb.MessageElement(
1192 str(new_level_forest), ldb.FLAG_MOD_REPLACE,
1193 "msDS-Behavior-Version")
1195 msgs.append("Forest function level changed!")
1196 msgs.append("All changes applied successfully!")
1197 self.message("\n".join(msgs))
1199 raise CommandError("invalid argument: '%s' (choose from 'show', 'raise')" % subcommand)
1202 class cmd_domain_passwordsettings(Command):
1203 """Set password settings.
1205 Password complexity, password lockout policy, history length,
1206 minimum password length, the minimum and maximum password age) on
1207 a Samba AD DC server.
1209 Use against a Windows DC is possible, but group policy will override it.
1212 synopsis = "%prog (show|set <options>) [options]"
1214 takes_optiongroups = {
1215 "sambaopts": options.SambaOptions,
1216 "versionopts": options.VersionOptions,
1217 "credopts": options.CredentialsOptions,
1221 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1222 metavar="URL", dest="H"),
1223 Option("--quiet", help="Be quiet", action="store_true"),
1224 Option("--complexity", type="choice", choices=["on","off","default"],
1225 help="The password complexity (on | off | default). Default is 'on'"),
1226 Option("--store-plaintext", type="choice", choices=["on","off","default"],
1227 help="Store plaintext passwords where account have 'store passwords with reversible encryption' set (on | off | default). Default is 'off'"),
1228 Option("--history-length",
1229 help="The password history length (<integer> | default). Default is 24.", type=str),
1230 Option("--min-pwd-length",
1231 help="The minimum password length (<integer> | default). Default is 7.", type=str),
1232 Option("--min-pwd-age",
1233 help="The minimum password age (<integer in days> | default). Default is 1.", type=str),
1234 Option("--max-pwd-age",
1235 help="The maximum password age (<integer in days> | default). Default is 43.", type=str),
1236 Option("--account-lockout-duration",
1237 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),
1238 Option("--account-lockout-threshold",
1239 help="The number of bad password attempts allowed before locking out the account (<integer> | default). Default is 0 (never lock out).", type=str),
1240 Option("--reset-account-lockout-after",
1241 help="After this time is elapsed, the recorded number of attempts restarts from zero (<integer> | default). Default is 30.", type=str),
1244 takes_args = ["subcommand"]
1246 def run(self, subcommand, H=None, min_pwd_age=None, max_pwd_age=None,
1247 quiet=False, complexity=None, store_plaintext=None, history_length=None,
1248 min_pwd_length=None, account_lockout_duration=None, account_lockout_threshold=None,
1249 reset_account_lockout_after=None, credopts=None, sambaopts=None,
1251 lp = sambaopts.get_loadparm()
1252 creds = credopts.get_credentials(lp)
1254 samdb = SamDB(url=H, session_info=system_session(),
1255 credentials=creds, lp=lp)
1257 domain_dn = samdb.domain_dn()
1258 res = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1259 attrs=["pwdProperties", "pwdHistoryLength", "minPwdLength",
1260 "minPwdAge", "maxPwdAge", "lockoutDuration", "lockoutThreshold",
1261 "lockOutObservationWindow"])
1262 assert(len(res) == 1)
1264 pwd_props = int(res[0]["pwdProperties"][0])
1265 pwd_hist_len = int(res[0]["pwdHistoryLength"][0])
1266 cur_min_pwd_len = int(res[0]["minPwdLength"][0])
1268 cur_min_pwd_age = int(abs(int(res[0]["minPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1269 if int(res[0]["maxPwdAge"][0]) == -0x8000000000000000:
1272 cur_max_pwd_age = int(abs(int(res[0]["maxPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1273 cur_account_lockout_threshold = int(res[0]["lockoutThreshold"][0])
1275 if int(res[0]["lockoutDuration"][0]) == -0x8000000000000000:
1276 cur_account_lockout_duration = 0
1278 cur_account_lockout_duration = abs(int(res[0]["lockoutDuration"][0])) / (1e7 * 60)
1279 cur_reset_account_lockout_after = abs(int(res[0]["lockOutObservationWindow"][0])) / (1e7 * 60)
1280 except Exception, e:
1281 raise CommandError("Could not retrieve password properties!", e)
1283 if subcommand == "show":
1284 self.message("Password informations for domain '%s'" % domain_dn)
1286 if pwd_props & DOMAIN_PASSWORD_COMPLEX != 0:
1287 self.message("Password complexity: on")
1289 self.message("Password complexity: off")
1290 if pwd_props & DOMAIN_PASSWORD_STORE_CLEARTEXT != 0:
1291 self.message("Store plaintext passwords: on")
1293 self.message("Store plaintext passwords: off")
1294 self.message("Password history length: %d" % pwd_hist_len)
1295 self.message("Minimum password length: %d" % cur_min_pwd_len)
1296 self.message("Minimum password age (days): %d" % cur_min_pwd_age)
1297 self.message("Maximum password age (days): %d" % cur_max_pwd_age)
1298 self.message("Account lockout duration (mins): %d" % cur_account_lockout_duration)
1299 self.message("Account lockout threshold (attempts): %d" % cur_account_lockout_threshold)
1300 self.message("Reset account lockout after (mins): %d" % cur_reset_account_lockout_after)
1301 elif subcommand == "set":
1304 m.dn = ldb.Dn(samdb, domain_dn)
1306 if complexity is not None:
1307 if complexity == "on" or complexity == "default":
1308 pwd_props = pwd_props | DOMAIN_PASSWORD_COMPLEX
1309 msgs.append("Password complexity activated!")
1310 elif complexity == "off":
1311 pwd_props = pwd_props & (~DOMAIN_PASSWORD_COMPLEX)
1312 msgs.append("Password complexity deactivated!")
1314 if store_plaintext is not None:
1315 if store_plaintext == "on" or store_plaintext == "default":
1316 pwd_props = pwd_props | DOMAIN_PASSWORD_STORE_CLEARTEXT
1317 msgs.append("Plaintext password storage for changed passwords activated!")
1318 elif store_plaintext == "off":
1319 pwd_props = pwd_props & (~DOMAIN_PASSWORD_STORE_CLEARTEXT)
1320 msgs.append("Plaintext password storage for changed passwords deactivated!")
1322 if complexity is not None or store_plaintext is not None:
1323 m["pwdProperties"] = ldb.MessageElement(str(pwd_props),
1324 ldb.FLAG_MOD_REPLACE, "pwdProperties")
1326 if history_length is not None:
1327 if history_length == "default":
1330 pwd_hist_len = int(history_length)
1332 if pwd_hist_len < 0 or pwd_hist_len > 24:
1333 raise CommandError("Password history length must be in the range of 0 to 24!")
1335 m["pwdHistoryLength"] = ldb.MessageElement(str(pwd_hist_len),
1336 ldb.FLAG_MOD_REPLACE, "pwdHistoryLength")
1337 msgs.append("Password history length changed!")
1339 if min_pwd_length is not None:
1340 if min_pwd_length == "default":
1343 min_pwd_len = int(min_pwd_length)
1345 if min_pwd_len < 0 or min_pwd_len > 14:
1346 raise CommandError("Minimum password length must be in the range of 0 to 14!")
1348 m["minPwdLength"] = ldb.MessageElement(str(min_pwd_len),
1349 ldb.FLAG_MOD_REPLACE, "minPwdLength")
1350 msgs.append("Minimum password length changed!")
1352 if min_pwd_age is not None:
1353 if min_pwd_age == "default":
1356 min_pwd_age = int(min_pwd_age)
1358 if min_pwd_age < 0 or min_pwd_age > 998:
1359 raise CommandError("Minimum password age must be in the range of 0 to 998!")
1362 min_pwd_age_ticks = -int(min_pwd_age * (24 * 60 * 60 * 1e7))
1364 m["minPwdAge"] = ldb.MessageElement(str(min_pwd_age_ticks),
1365 ldb.FLAG_MOD_REPLACE, "minPwdAge")
1366 msgs.append("Minimum password age changed!")
1368 if max_pwd_age is not None:
1369 if max_pwd_age == "default":
1372 max_pwd_age = int(max_pwd_age)
1374 if max_pwd_age < 0 or max_pwd_age > 999:
1375 raise CommandError("Maximum password age must be in the range of 0 to 999!")
1378 if max_pwd_age == 0:
1379 max_pwd_age_ticks = -0x8000000000000000
1381 max_pwd_age_ticks = -int(max_pwd_age * (24 * 60 * 60 * 1e7))
1383 m["maxPwdAge"] = ldb.MessageElement(str(max_pwd_age_ticks),
1384 ldb.FLAG_MOD_REPLACE, "maxPwdAge")
1385 msgs.append("Maximum password age changed!")
1387 if account_lockout_duration is not None:
1388 if account_lockout_duration == "default":
1389 account_lockout_duration = 30
1391 account_lockout_duration = int(account_lockout_duration)
1393 if account_lockout_duration < 0 or account_lockout_duration > 99999:
1394 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1397 if account_lockout_duration == 0:
1398 account_lockout_duration_ticks = -0x8000000000000000
1400 account_lockout_duration_ticks = -int(account_lockout_duration * (60 * 1e7))
1402 m["lockoutDuration"] = ldb.MessageElement(str(account_lockout_duration_ticks),
1403 ldb.FLAG_MOD_REPLACE, "lockoutDuration")
1404 msgs.append("Account lockout duration changed!")
1406 if account_lockout_threshold is not None:
1407 if account_lockout_threshold == "default":
1408 account_lockout_threshold = 0
1410 account_lockout_threshold = int(account_lockout_threshold)
1412 m["lockoutThreshold"] = ldb.MessageElement(str(account_lockout_threshold),
1413 ldb.FLAG_MOD_REPLACE, "lockoutThreshold")
1414 msgs.append("Account lockout threshold changed!")
1416 if reset_account_lockout_after is not None:
1417 if reset_account_lockout_after == "default":
1418 reset_account_lockout_after = 30
1420 reset_account_lockout_after = int(reset_account_lockout_after)
1422 if reset_account_lockout_after < 0 or reset_account_lockout_after > 99999:
1423 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1426 if reset_account_lockout_after == 0:
1427 reset_account_lockout_after_ticks = -0x8000000000000000
1429 reset_account_lockout_after_ticks = -int(reset_account_lockout_after * (60 * 1e7))
1431 m["lockOutObservationWindow"] = ldb.MessageElement(str(reset_account_lockout_after_ticks),
1432 ldb.FLAG_MOD_REPLACE, "lockOutObservationWindow")
1433 msgs.append("Duration to reset account lockout after changed!")
1435 if max_pwd_age > 0 and min_pwd_age >= max_pwd_age:
1436 raise CommandError("Maximum password age (%d) must be greater than minimum password age (%d)!" % (max_pwd_age, min_pwd_age))
1439 raise CommandError("You must specify at least one option to set. Try --help")
1441 msgs.append("All changes applied successfully!")
1442 self.message("\n".join(msgs))
1444 raise CommandError("Wrong argument '%s'!" % subcommand)
1447 class cmd_domain_classicupgrade(Command):
1448 """Upgrade from Samba classic (NT4-like) database to Samba AD DC database.
1450 Specify either a directory with all Samba classic DC databases and state files (with --dbdir) or
1451 the testparm utility from your classic installation (with --testparm).
1454 synopsis = "%prog [options] <classic_smb_conf>"
1456 takes_optiongroups = {
1457 "sambaopts": options.SambaOptions,
1458 "versionopts": options.VersionOptions
1462 Option("--dbdir", type="string", metavar="DIR",
1463 help="Path to samba classic DC database directory"),
1464 Option("--testparm", type="string", metavar="PATH",
1465 help="Path to samba classic DC testparm utility from the previous installation. This allows the default paths of the previous installation to be followed"),
1466 Option("--targetdir", type="string", metavar="DIR",
1467 help="Path prefix where the new Samba 4.0 AD domain should be initialised"),
1468 Option("--quiet", help="Be quiet", action="store_true"),
1469 Option("--verbose", help="Be verbose", action="store_true"),
1470 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
1471 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
1472 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
1473 "BIND9_FLATFILE uses bind9 text database to store zone information, "
1474 "BIND9_DLZ uses samba4 AD to store zone information, "
1475 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
1476 default="SAMBA_INTERNAL")
1480 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
1481 action="store_true"),
1482 Option("--use-xattrs", type="choice", choices=["yes","no","auto"],
1483 metavar="[yes|no|auto]",
1484 help="Define if we should use the native fs capabilities or a tdb file for "
1485 "storing attributes likes ntacl when --use-ntvfs is set. "
1486 "auto tries to make an inteligent guess based on the user rights and system capabilities",
1489 if samba.is_ntvfs_fileserver_built():
1490 takes_options.extend(ntvfs_options)
1492 takes_args = ["smbconf"]
1494 def run(self, smbconf=None, targetdir=None, dbdir=None, testparm=None,
1495 quiet=False, verbose=False, use_xattrs="auto", sambaopts=None, versionopts=None,
1496 dns_backend=None, use_ntvfs=False):
1498 if not os.path.exists(smbconf):
1499 raise CommandError("File %s does not exist" % smbconf)
1501 if testparm and not os.path.exists(testparm):
1502 raise CommandError("Testparm utility %s does not exist" % testparm)
1504 if dbdir and not os.path.exists(dbdir):
1505 raise CommandError("Directory %s does not exist" % dbdir)
1507 if not dbdir and not testparm:
1508 raise CommandError("Please specify either dbdir or testparm")
1510 logger = self.get_logger()
1512 logger.setLevel(logging.DEBUG)
1514 logger.setLevel(logging.WARNING)
1516 logger.setLevel(logging.INFO)
1518 if dbdir and testparm:
1519 logger.warning("both dbdir and testparm specified, ignoring dbdir.")
1522 lp = sambaopts.get_loadparm()
1524 s3conf = s3param.get_context()
1527 s3conf.set("realm", sambaopts.realm)
1529 if targetdir is not None:
1530 if not os.path.isdir(targetdir):
1534 if use_xattrs == "yes":
1536 elif use_xattrs == "auto" and use_ntvfs == False:
1538 elif use_ntvfs == False:
1539 raise CommandError("--use-xattrs=no requires --use-ntvfs (not supported for production use). "
1540 "Please re-run with --use-xattrs omitted.")
1541 elif use_xattrs == "auto" and not s3conf.get("posix:eadb"):
1543 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
1545 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
1548 samba.ntacls.setntacl(lp, tmpfile.name,
1549 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
1552 # FIXME: Don't catch all exceptions here
1553 logger.info("You are not root or your system does not support xattr, using tdb backend for attributes. "
1554 "If you intend to use this provision in production, rerun the script as root on a system supporting xattrs.")
1558 # Set correct default values from dbdir or testparm
1561 paths["state directory"] = dbdir
1562 paths["private dir"] = dbdir
1563 paths["lock directory"] = dbdir
1564 paths["smb passwd file"] = dbdir + "/smbpasswd"
1566 paths["state directory"] = get_testparm_var(testparm, smbconf, "state directory")
1567 paths["private dir"] = get_testparm_var(testparm, smbconf, "private dir")
1568 paths["smb passwd file"] = get_testparm_var(testparm, smbconf, "smb passwd file")
1569 paths["lock directory"] = get_testparm_var(testparm, smbconf, "lock directory")
1570 # "testparm" from Samba 3 < 3.4.x is not aware of the parameter
1571 # "state directory", instead make use of "lock directory"
1572 if len(paths["state directory"]) == 0:
1573 paths["state directory"] = paths["lock directory"]
1576 s3conf.set(p, paths[p])
1578 # load smb.conf parameters
1579 logger.info("Reading smb.conf")
1580 s3conf.load(smbconf)
1581 samba3 = Samba3(smbconf, s3conf)
1583 logger.info("Provisioning")
1584 upgrade_from_samba3(samba3, logger, targetdir, session_info=system_session(),
1585 useeadb=eadb, dns_backend=dns_backend, use_ntvfs=use_ntvfs)
1588 class cmd_domain_samba3upgrade(cmd_domain_classicupgrade):
1589 __doc__ = cmd_domain_classicupgrade.__doc__
1591 # This command is present for backwards compatibility only,
1592 # and should not be shown.
1596 class LocalDCCredentialsOptions(options.CredentialsOptions):
1597 def __init__(self, parser):
1598 options.CredentialsOptions.__init__(self, parser, special_name="local-dc")
1600 class DomainTrustCommand(Command):
1601 """List domain trusts."""
1604 Command.__init__(self)
1605 self.local_lp = None
1607 self.local_server = None
1608 self.local_binding_string = None
1609 self.local_creds = None
1611 self.remote_server = None
1612 self.remote_binding_string = None
1613 self.remote_creds = None
1615 WERR_OK = 0x00000000
1616 WERR_INVALID_FUNCTION = 0x00000001
1617 WERR_NERR_ACFNOTLOADED = 0x000008B3
1618 WERR_RPC_S_PROCNUM_OUT_OF_RANGE = 0x000006D1
1620 def _uint32(self, v):
1621 return ctypes.c_uint32(v).value
1623 def check_runtime_error(self, runtime, val):
1627 err32 = self._uint32(runtime[0])
1633 class LocalRuntimeError(CommandError):
1634 def __init__(exception_self, self, runtime, message):
1635 err32 = self._uint32(runtime[0])
1637 msg = "LOCAL_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1638 self.local_server, message, err32, errstr)
1639 CommandError.__init__(exception_self, msg)
1641 class RemoteRuntimeError(CommandError):
1642 def __init__(exception_self, self, runtime, message):
1643 err32 = self._uint32(runtime[0])
1645 msg = "REMOTE_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1646 self.remote_server, message, err32, errstr)
1647 CommandError.__init__(exception_self, msg)
1649 class LocalLdbError(CommandError):
1650 def __init__(exception_self, self, ldb_error, message):
1651 errval = ldb_error[0]
1652 errstr = ldb_error[1]
1653 msg = "LOCAL_DC[%s]: %s - ERROR(%d) - %s" % (
1654 self.local_server, message, errval, errstr)
1655 CommandError.__init__(exception_self, msg)
1657 def setup_local_server(self, sambaopts, localdcopts):
1658 if self.local_server is not None:
1659 return self.local_server
1661 lp = sambaopts.get_loadparm()
1663 local_server = localdcopts.ipaddress
1664 if local_server is None:
1665 server_role = lp.server_role()
1666 if server_role != "ROLE_ACTIVE_DIRECTORY_DC":
1667 raise CommandError("Invalid server_role %s" % (server_role))
1668 local_server = lp.get('netbios name')
1669 local_transport = "ncalrpc"
1670 local_binding_options = ""
1671 local_binding_options += ",auth_type=ncalrpc_as_system"
1672 local_ldap_url = None
1675 local_transport = "ncacn_np"
1676 local_binding_options = ""
1677 local_ldap_url = "ldap://%s" % local_server
1678 local_creds = localdcopts.get_credentials(lp)
1682 self.local_server = local_server
1683 self.local_binding_string = "%s:%s[%s]" % (local_transport, local_server, local_binding_options)
1684 self.local_ldap_url = local_ldap_url
1685 self.local_creds = local_creds
1686 return self.local_server
1688 def new_local_lsa_connection(self):
1689 return lsa.lsarpc(self.local_binding_string, self.local_lp, self.local_creds)
1691 def new_local_netlogon_connection(self):
1692 return netlogon.netlogon(self.local_binding_string, self.local_lp, self.local_creds)
1694 def new_local_ldap_connection(self):
1695 return SamDB(url=self.local_ldap_url,
1696 session_info=system_session(),
1697 credentials=self.local_creds,
1700 def setup_remote_server(self, credopts, domain,
1702 require_writable=True):
1705 assert require_writable
1707 if self.remote_server is not None:
1708 return self.remote_server
1710 self.remote_server = "__unknown__remote_server__.%s" % domain
1711 assert self.local_server is not None
1713 remote_creds = credopts.get_credentials(self.local_lp)
1714 remote_server = credopts.ipaddress
1715 remote_binding_options = ""
1717 # TODO: we should also support NT4 domains
1718 # we could use local_netlogon.netr_DsRGetDCNameEx2() with the remote domain name
1719 # and delegate NBT or CLDAP to the local netlogon server
1721 remote_net = Net(remote_creds, self.local_lp, server=remote_server)
1722 remote_flags = nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS
1723 if require_writable:
1724 remote_flags |= nbt.NBT_SERVER_WRITABLE
1726 remote_flags |= nbt.NBT_SERVER_PDC
1727 remote_info = remote_net.finddc(flags=remote_flags, domain=domain, address=remote_server)
1729 raise CommandError("Failed to find a writeable DC for domain '%s'" % domain)
1731 nbt.NBT_SERVER_PDC: "PDC",
1732 nbt.NBT_SERVER_GC: "GC",
1733 nbt.NBT_SERVER_LDAP: "LDAP",
1734 nbt.NBT_SERVER_DS: "DS",
1735 nbt.NBT_SERVER_KDC: "KDC",
1736 nbt.NBT_SERVER_TIMESERV: "TIMESERV",
1737 nbt.NBT_SERVER_CLOSEST: "CLOSEST",
1738 nbt.NBT_SERVER_WRITABLE: "WRITABLE",
1739 nbt.NBT_SERVER_GOOD_TIMESERV: "GOOD_TIMESERV",
1740 nbt.NBT_SERVER_NDNC: "NDNC",
1741 nbt.NBT_SERVER_SELECT_SECRET_DOMAIN_6: "SELECT_SECRET_DOMAIN_6",
1742 nbt.NBT_SERVER_FULL_SECRET_DOMAIN_6: "FULL_SECRET_DOMAIN_6",
1743 nbt.NBT_SERVER_ADS_WEB_SERVICE: "ADS_WEB_SERVICE",
1744 nbt.NBT_SERVER_DS_8: "DS_8",
1745 nbt.NBT_SERVER_HAS_DNS_NAME: "HAS_DNS_NAME",
1746 nbt.NBT_SERVER_IS_DEFAULT_NC: "IS_DEFAULT_NC",
1747 nbt.NBT_SERVER_FOREST_ROOT: "FOREST_ROOT",
1749 server_type_string = self.generic_bitmap_to_string(flag_map,
1750 remote_info.server_type, names_only=True)
1751 self.outf.write("RemoteDC Netbios[%s] DNS[%s] ServerType[%s]\n" % (
1752 remote_info.pdc_name,
1753 remote_info.pdc_dns_name,
1754 server_type_string))
1756 self.remote_server = remote_info.pdc_dns_name
1757 self.remote_binding_string="ncacn_np:%s[%s]" % (self.remote_server, remote_binding_options)
1758 self.remote_creds = remote_creds
1759 return self.remote_server
1761 def new_remote_lsa_connection(self):
1762 return lsa.lsarpc(self.remote_binding_string, self.local_lp, self.remote_creds)
1764 def new_remote_netlogon_connection(self):
1765 return netlogon.netlogon(self.remote_binding_string, self.local_lp, self.remote_creds)
1767 def get_lsa_info(self, conn, policy_access):
1768 objectAttr = lsa.ObjectAttribute()
1769 objectAttr.sec_qos = lsa.QosInfo()
1771 policy = conn.OpenPolicy2(''.decode('utf-8'),
1772 objectAttr, policy_access)
1774 info = conn.QueryInfoPolicy2(policy, lsa.LSA_POLICY_INFO_DNS)
1776 return (policy, info)
1778 def get_netlogon_dc_info(self, conn, server):
1779 info = conn.netr_DsRGetDCNameEx2(server,
1780 None, 0, None, None, None,
1781 netlogon.DS_RETURN_DNS_NAME)
1784 def netr_DomainTrust_to_name(self, t):
1785 if t.trust_type == lsa.LSA_TRUST_TYPE_DOWNLEVEL:
1786 return t.netbios_name
1790 def netr_DomainTrust_to_type(self, a, t):
1792 primary_parent = None
1794 if _t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
1796 if not _t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1797 primary_parent = a[_t.parent_index]
1800 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1801 if t is primary_parent:
1804 if t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1807 parent = a[t.parent_index]
1808 if parent is primary:
1813 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1818 def netr_DomainTrust_to_transitive(self, t):
1819 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1822 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE:
1825 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1830 def netr_DomainTrust_to_direction(self, t):
1831 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND and \
1832 t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1835 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND:
1838 if t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1843 def generic_enum_to_string(self, e_dict, v, names_only=False):
1847 v32 = self._uint32(v)
1848 w = "__unknown__%08X__" % v32
1850 r = "0x%x (%s)" % (v, w)
1853 def generic_bitmap_to_string(self, b_dict, v, names_only=False):
1858 for b in sorted(b_dict.keys()):
1865 c32 = self._uint32(c)
1866 s += ["__unknown_%08X__" % c32]
1871 r = "0x%x (%s)" % (v, w)
1874 def trustType_string(self, v):
1876 lsa.LSA_TRUST_TYPE_DOWNLEVEL : "DOWNLEVEL",
1877 lsa.LSA_TRUST_TYPE_UPLEVEL : "UPLEVEL",
1878 lsa.LSA_TRUST_TYPE_MIT : "MIT",
1879 lsa.LSA_TRUST_TYPE_DCE : "DCE",
1881 return self.generic_enum_to_string(types, v)
1883 def trustDirection_string(self, v):
1885 lsa.LSA_TRUST_DIRECTION_INBOUND |
1886 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "BOTH",
1887 lsa.LSA_TRUST_DIRECTION_INBOUND : "INBOUND",
1888 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "OUTBOUND",
1890 return self.generic_enum_to_string(directions, v)
1892 def trustAttributes_string(self, v):
1894 lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE : "NON_TRANSITIVE",
1895 lsa.LSA_TRUST_ATTRIBUTE_UPLEVEL_ONLY : "UPLEVEL_ONLY",
1896 lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN : "QUARANTINED_DOMAIN",
1897 lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE : "FOREST_TRANSITIVE",
1898 lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION : "CROSS_ORGANIZATION",
1899 lsa.LSA_TRUST_ATTRIBUTE_WITHIN_FOREST : "WITHIN_FOREST",
1900 lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL : "TREAT_AS_EXTERNAL",
1901 lsa.LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION : "USES_RC4_ENCRYPTION",
1903 return self.generic_bitmap_to_string(attributes, v)
1905 def kerb_EncTypes_string(self, v):
1907 security.KERB_ENCTYPE_DES_CBC_CRC : "DES_CBC_CRC",
1908 security.KERB_ENCTYPE_DES_CBC_MD5 : "DES_CBC_MD5",
1909 security.KERB_ENCTYPE_RC4_HMAC_MD5 : "RC4_HMAC_MD5",
1910 security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96 : "AES128_CTS_HMAC_SHA1_96",
1911 security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96 : "AES256_CTS_HMAC_SHA1_96",
1912 security.KERB_ENCTYPE_FAST_SUPPORTED : "FAST_SUPPORTED",
1913 security.KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED : "COMPOUND_IDENTITY_SUPPORTED",
1914 security.KERB_ENCTYPE_CLAIMS_SUPPORTED : "CLAIMS_SUPPORTED",
1915 security.KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED : "RESOURCE_SID_COMPRESSION_DISABLED",
1917 return self.generic_bitmap_to_string(enctypes, v)
1919 def entry_tln_status(self, e_flags, ):
1921 return "Status[Enabled]"
1924 lsa.LSA_TLN_DISABLED_NEW : "Disabled-New",
1925 lsa.LSA_TLN_DISABLED_ADMIN : "Disabled",
1926 lsa.LSA_TLN_DISABLED_CONFLICT : "Disabled-Conflicting",
1928 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
1930 def entry_dom_status(self, e_flags):
1932 return "Status[Enabled]"
1935 lsa.LSA_SID_DISABLED_ADMIN : "Disabled-SID",
1936 lsa.LSA_SID_DISABLED_CONFLICT : "Disabled-SID-Conflicting",
1937 lsa.LSA_NB_DISABLED_ADMIN : "Disabled-NB",
1938 lsa.LSA_NB_DISABLED_CONFLICT : "Disabled-NB-Conflicting",
1940 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
1942 def write_forest_trust_info(self, fti, tln=None, collisions=None):
1944 tln_string = " TDO[%s]" % tln
1948 self.outf.write("Namespaces[%d]%s:\n" % (
1949 len(fti.entries), tln_string))
1951 for i in xrange(0, len(fti.entries)):
1955 collision_string = ""
1957 if collisions is not None:
1958 for c in collisions.entries:
1962 collision_string = " Collision[%s]" % (c.name.string)
1964 d = e.forest_trust_data
1965 if e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
1966 self.outf.write("TLN: %-32s DNS[*.%s]%s\n" % (
1967 self.entry_tln_status(flags),
1968 d.string, collision_string))
1969 elif e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
1970 self.outf.write("TLN_EX: %-29s DNS[*.%s]\n" % (
1972 elif e.type == lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
1973 self.outf.write("DOM: %-32s DNS[%s] Netbios[%s] SID[%s]%s\n" % (
1974 self.entry_dom_status(flags),
1975 d.dns_domain_name.string,
1976 d.netbios_domain_name.string,
1977 d.domain_sid, collision_string))
1980 class cmd_domain_trust_list(DomainTrustCommand):
1981 """List domain trusts."""
1983 synopsis = "%prog [options]"
1985 takes_optiongroups = {
1986 "sambaopts": options.SambaOptions,
1987 "versionopts": options.VersionOptions,
1988 "localdcopts": LocalDCCredentialsOptions,
1994 def run(self, sambaopts=None, versionopts=None, localdcopts=None):
1996 local_server = self.setup_local_server(sambaopts, localdcopts)
1998 local_netlogon = self.new_local_netlogon_connection()
1999 except RuntimeError as error:
2000 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2003 local_netlogon_trusts = local_netlogon.netr_DsrEnumerateDomainTrusts(local_server,
2004 netlogon.NETR_TRUST_FLAG_IN_FOREST |
2005 netlogon.NETR_TRUST_FLAG_OUTBOUND |
2006 netlogon.NETR_TRUST_FLAG_INBOUND)
2007 except RuntimeError as error:
2008 if self.check_runtime_error(error, self.WERR_RPC_S_PROCNUM_OUT_OF_RANGE):
2009 # TODO: we could implement a fallback to lsa.EnumTrustDom()
2010 raise CommandError("LOCAL_DC[%s]: netr_DsrEnumerateDomainTrusts not supported." % (
2012 raise self.LocalRuntimeError(self, error, "netr_DsrEnumerateDomainTrusts failed")
2014 a = local_netlogon_trusts.array
2016 if t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
2018 self.outf.write("%-14s %-15s %-19s %s\n" % (
2019 "Type[%s]" % self.netr_DomainTrust_to_type(a, t),
2020 "Transitive[%s]" % self.netr_DomainTrust_to_transitive(t),
2021 "Direction[%s]" % self.netr_DomainTrust_to_direction(t),
2022 "Name[%s]" % self.netr_DomainTrust_to_name(t)))
2025 class cmd_domain_trust_show(DomainTrustCommand):
2026 """Show trusted domain details."""
2028 synopsis = "%prog NAME [options]"
2030 takes_optiongroups = {
2031 "sambaopts": options.SambaOptions,
2032 "versionopts": options.VersionOptions,
2033 "localdcopts": LocalDCCredentialsOptions,
2039 takes_args = ["domain"]
2041 def run(self, domain, sambaopts=None, versionopts=None, localdcopts=None):
2043 local_server = self.setup_local_server(sambaopts, localdcopts)
2045 local_lsa = self.new_local_lsa_connection()
2046 except RuntimeError as error:
2047 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2050 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2051 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2052 except RuntimeError as error:
2053 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2055 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2056 local_lsa_info.name.string,
2057 local_lsa_info.dns_domain.string,
2058 local_lsa_info.sid))
2060 lsaString = lsa.String()
2061 lsaString.string = domain
2063 local_tdo_full = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2064 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2065 local_tdo_info = local_tdo_full.info_ex
2066 local_tdo_posix = local_tdo_full.posix_offset
2067 except RuntimeError as error:
2068 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2069 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2071 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(FULL_INFO) failed")
2074 local_tdo_enctypes = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2075 lsaString, lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES)
2076 except RuntimeError as error:
2077 if self.check_runtime_error(error, ntstatus.NT_STATUS_INVALID_PARAMETER):
2079 if self.check_runtime_error(error, ntstatus.NT_STATUS_INVALID_INFO_CLASS):
2082 if error is not None:
2083 raise self.LocalRuntimeError(self, error,
2084 "QueryTrustedDomainInfoByName(SUPPORTED_ENCRYPTION_TYPES) failed")
2086 local_tdo_enctypes = lsa.TrustDomainInfoSupportedEncTypes()
2087 local_tdo_enctypes.enc_types = 0
2090 local_tdo_forest = None
2091 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2092 local_tdo_forest = local_lsa.lsaRQueryForestTrustInformation(local_policy,
2093 lsaString, lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
2094 except RuntimeError as error:
2095 if self.check_runtime_error(error, self.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
2097 if self.check_runtime_error(error, ntstatus.NT_STATUS_NOT_FOUND):
2099 if error is not None:
2100 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation failed")
2102 local_tdo_forest = lsa.ForestTrustInformation()
2103 local_tdo_forest.count = 0
2104 local_tdo_forest.entries = []
2106 self.outf.write("TrusteDomain:\n\n");
2107 self.outf.write("NetbiosName: %s\n" % local_tdo_info.netbios_name.string)
2108 if local_tdo_info.netbios_name.string != local_tdo_info.domain_name.string:
2109 self.outf.write("DnsName: %s\n" % local_tdo_info.domain_name.string)
2110 self.outf.write("SID: %s\n" % local_tdo_info.sid)
2111 self.outf.write("Type: %s\n" % self.trustType_string(local_tdo_info.trust_type))
2112 self.outf.write("Direction: %s\n" % self.trustDirection_string(local_tdo_info.trust_direction))
2113 self.outf.write("Attributes: %s\n" % self.trustAttributes_string(local_tdo_info.trust_attributes))
2114 posix_offset_u32 = ctypes.c_uint32(local_tdo_posix.posix_offset).value
2115 posix_offset_i32 = ctypes.c_int32(local_tdo_posix.posix_offset).value
2116 self.outf.write("PosixOffset: 0x%08X (%d)\n" % (posix_offset_u32, posix_offset_i32))
2117 self.outf.write("kerb_EncTypes: %s\n" % self.kerb_EncTypes_string(local_tdo_enctypes.enc_types))
2119 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2120 self.write_forest_trust_info(local_tdo_forest,
2121 tln=local_tdo_info.domain_name.string)
2125 class cmd_domain_trust_create(DomainTrustCommand):
2126 """Create a domain or forest trust."""
2128 synopsis = "%prog DOMAIN [options]"
2130 takes_optiongroups = {
2131 "sambaopts": options.SambaOptions,
2132 "versionopts": options.VersionOptions,
2133 "credopts": options.CredentialsOptions,
2134 "localdcopts": LocalDCCredentialsOptions,
2138 Option("--type", type="choice", metavar="TYPE",
2139 choices=["external", "forest"],
2140 help="The type of the trust: 'external' or 'forest'.",
2142 default="external"),
2143 Option("--direction", type="choice", metavar="DIRECTION",
2144 choices=["incoming", "outgoing", "both"],
2145 help="The trust direction: 'incoming', 'outgoing' or 'both'.",
2146 dest='trust_direction',
2148 Option("--create-location", type="choice", metavar="LOCATION",
2149 choices=["local", "both"],
2150 help="Where to create the trusted domain object: 'local' or 'both'.",
2151 dest='create_location',
2153 Option("--cross-organisation", action="store_true",
2154 help="The related domains does not belong to the same organisation.",
2155 dest='cross_organisation',
2157 Option("--quarantined", type="choice", metavar="yes|no",
2158 choices=["yes", "no", None],
2159 help="Special SID filtering rules are applied to the trust. "
2160 "With --type=external the default is yes. "
2161 "With --type=forest the default is no.",
2162 dest='quarantined_arg',
2164 Option("--not-transitive", action="store_true",
2165 help="The forest trust is not transitive.",
2166 dest='not_transitive',
2168 Option("--treat-as-external", action="store_true",
2169 help="The treat the forest trust as external.",
2170 dest='treat_as_external',
2172 Option("--no-aes-keys", action="store_false",
2173 help="The trust uses aes kerberos keys.",
2174 dest='use_aes_keys',
2176 Option("--skip-validation", action="store_false",
2177 help="Skip validation of the trust.",
2182 takes_args = ["domain"]
2184 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2185 trust_type=None, trust_direction=None, create_location=None,
2186 cross_organisation=False, quarantined_arg=None,
2187 not_transitive=False, treat_as_external=False,
2188 use_aes_keys=False, validate=True):
2190 lsaString = lsa.String()
2193 if quarantined_arg is None:
2194 if trust_type == 'external':
2196 elif quarantined_arg == 'yes':
2199 if trust_type != 'forest':
2201 raise CommandError("--not-transitive requires --type=forest")
2202 if treat_as_external:
2203 raise CommandError("--treat-as-external requires --type=forest")
2207 enc_types = lsa.TrustDomainInfoSupportedEncTypes()
2208 enc_types.enc_types = security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96
2209 enc_types.enc_types |= security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96
2211 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2212 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2213 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2215 local_trust_info = lsa.TrustDomainInfoInfoEx()
2216 local_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2217 local_trust_info.trust_direction = 0
2218 if trust_direction == "both":
2219 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2220 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2221 elif trust_direction == "incoming":
2222 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2223 elif trust_direction == "outgoing":
2224 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2225 local_trust_info.trust_attributes = 0
2226 if cross_organisation:
2227 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2229 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2230 if trust_type == "forest":
2231 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2233 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2234 if treat_as_external:
2235 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2237 def get_password(name):
2240 if password is not None and password is not '':
2242 password = getpass("New %s Password: " % name)
2243 passwordverify = getpass("Retype %s Password: " % name)
2244 if not password == passwordverify:
2246 self.outf.write("Sorry, passwords do not match.\n")
2248 incoming_secret = None
2249 outgoing_secret = None
2250 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2251 if create_location == "local":
2252 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2253 incoming_password = get_password("Incoming Trust")
2254 incoming_secret = string_to_byte_array(incoming_password.encode('utf-16-le'))
2255 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2256 outgoing_password = get_password("Outgoing Trust")
2257 outgoing_secret = string_to_byte_array(outgoing_password.encode('utf-16-le'))
2259 remote_trust_info = None
2261 # We use 240 random bytes.
2262 # Windows uses 28 or 240 random bytes. I guess it's
2263 # based on the trust type external vs. forest.
2265 # The initial trust password can be up to 512 bytes
2266 # while the versioned passwords used for periodic updates
2267 # can only be up to 498 bytes, as netr_ServerPasswordSet2()
2268 # needs to pass the NL_PASSWORD_VERSION structure within the
2269 # 512 bytes and a 2 bytes confounder is required.
2271 def random_trust_secret(length, use_aes_keys=True):
2272 secret = [0] * length
2274 pw1 = samba.generate_random_password(length/2, length/2)
2275 if not use_aes_keys:
2276 # With arcfour-hmac-md5 we have to use valid utf16
2277 # in order to generate the correct pre-auth key
2278 # based on a utf8 password.
2280 # We can remove this once our client libraries
2281 # support using the correct NTHASH.
2282 return string_to_byte_array(pw1.encode('utf-16-le'))
2284 # We mix characters from generate_random_password
2285 # with random numbers from random.randint()
2286 for i in range(len(secret)):
2288 secret[i] = ord(pw1[i])
2290 secret[i] = random.randint(0, 255)
2294 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2295 incoming_secret = random_trust_secret(240, use_aes_keys=use_aes_keys)
2296 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2297 outgoing_secret = random_trust_secret(240, use_aes_keys=use_aes_keys)
2299 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2300 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2302 remote_trust_info = lsa.TrustDomainInfoInfoEx()
2303 remote_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2304 remote_trust_info.trust_direction = 0
2305 if trust_direction == "both":
2306 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2307 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2308 elif trust_direction == "incoming":
2309 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2310 elif trust_direction == "outgoing":
2311 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2312 remote_trust_info.trust_attributes = 0
2313 if cross_organisation:
2314 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2316 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2317 if trust_type == "forest":
2318 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2320 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2321 if treat_as_external:
2322 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2324 local_server = self.setup_local_server(sambaopts, localdcopts)
2326 local_lsa = self.new_local_lsa_connection()
2327 except RuntimeError as error:
2328 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2331 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2332 except RuntimeError as error:
2333 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2335 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2336 local_lsa_info.name.string,
2337 local_lsa_info.dns_domain.string,
2338 local_lsa_info.sid))
2341 remote_server = self.setup_remote_server(credopts, domain)
2342 except RuntimeError as error:
2343 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2346 remote_lsa = self.new_remote_lsa_connection()
2347 except RuntimeError as error:
2348 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2351 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2352 except RuntimeError as error:
2353 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2355 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2356 remote_lsa_info.name.string,
2357 remote_lsa_info.dns_domain.string,
2358 remote_lsa_info.sid))
2360 local_trust_info.domain_name.string = remote_lsa_info.dns_domain.string
2361 local_trust_info.netbios_name.string = remote_lsa_info.name.string
2362 local_trust_info.sid = remote_lsa_info.sid
2364 if remote_trust_info:
2365 remote_trust_info.domain_name.string = local_lsa_info.dns_domain.string
2366 remote_trust_info.netbios_name.string = local_lsa_info.name.string
2367 remote_trust_info.sid = local_lsa_info.sid
2370 lsaString.string = local_trust_info.domain_name.string
2371 local_old_netbios = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2372 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2373 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2374 except RuntimeError as error:
2375 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2376 raise self.LocalRuntimeError(self, error,
2377 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2381 lsaString.string = local_trust_info.netbios_name.string
2382 local_old_dns = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2383 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2384 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2385 except RuntimeError as error:
2386 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2387 raise self.LocalRuntimeError(self, error,
2388 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2391 if remote_trust_info:
2393 lsaString.string = remote_trust_info.domain_name.string
2394 remote_old_netbios = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2395 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2396 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2397 except RuntimeError as error:
2398 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2399 raise self.RemoteRuntimeError(self, error,
2400 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2404 lsaString.string = remote_trust_info.netbios_name.string
2405 remote_old_dns = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2406 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2407 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2408 except RuntimeError as error:
2409 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2410 raise self.RemoteRuntimeError(self, error,
2411 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2415 local_netlogon = self.new_local_netlogon_connection()
2416 except RuntimeError as error:
2417 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2420 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
2421 except RuntimeError as error:
2422 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
2424 if remote_trust_info:
2426 remote_netlogon = self.new_remote_netlogon_connection()
2427 except RuntimeError as error:
2428 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2431 remote_netlogon_info = self.get_netlogon_dc_info(remote_netlogon, remote_server)
2432 except RuntimeError as error:
2433 raise self.RemoteRuntimeError(self, error, "failed to get netlogon dc info")
2435 def generate_AuthInOutBlob(secret, update_time):
2437 blob = drsblobs.trustAuthInOutBlob()
2442 clear = drsblobs.AuthInfoClear()
2443 clear.size = len(secret)
2444 clear.password = secret
2446 info = drsblobs.AuthenticationInformation()
2447 info.LastUpdateTime = samba.unix2nttime(update_time)
2448 info.AuthType = lsa.TRUST_AUTH_TYPE_CLEAR
2449 info.AuthInfo = clear
2451 array = drsblobs.AuthenticationInformationArray()
2453 array.array = [info]
2455 blob = drsblobs.trustAuthInOutBlob()
2457 blob.current = array
2461 def generate_AuthInfoInternal(session_key, incoming=None, outgoing=None):
2462 confounder = [0] * 512
2463 for i in range(len(confounder)):
2464 confounder[i] = random.randint(0, 255)
2466 trustpass = drsblobs.trustDomainPasswords()
2468 trustpass.confounder = confounder
2469 trustpass.outgoing = outgoing
2470 trustpass.incoming = incoming
2472 trustpass_blob = ndr_pack(trustpass)
2474 encrypted_trustpass = arcfour_encrypt(session_key, trustpass_blob)
2476 auth_blob = lsa.DATA_BUF2()
2477 auth_blob.size = len(encrypted_trustpass)
2478 auth_blob.data = string_to_byte_array(encrypted_trustpass)
2480 auth_info = lsa.TrustDomainInfoAuthInfoInternal()
2481 auth_info.auth_blob = auth_blob
2485 update_time = samba.current_unix_time()
2486 incoming_blob = generate_AuthInOutBlob(incoming_secret, update_time)
2487 outgoing_blob = generate_AuthInOutBlob(outgoing_secret, update_time)
2489 local_tdo_handle = None
2490 remote_tdo_handle = None
2492 local_auth_info = generate_AuthInfoInternal(local_lsa.session_key,
2493 incoming=incoming_blob,
2494 outgoing=outgoing_blob)
2495 if remote_trust_info:
2496 remote_auth_info = generate_AuthInfoInternal(remote_lsa.session_key,
2497 incoming=outgoing_blob,
2498 outgoing=incoming_blob)
2501 if remote_trust_info:
2502 self.outf.write("Creating remote TDO.\n")
2503 current_request = { "location": "remote", "name": "CreateTrustedDomainEx2"}
2504 remote_tdo_handle = remote_lsa.CreateTrustedDomainEx2(remote_policy,
2507 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2508 self.outf.write("Remote TDO created.\n")
2510 self.outf.write("Setting supported encryption types on remote TDO.\n")
2511 current_request = { "location": "remote", "name": "SetInformationTrustedDomain"}
2512 remote_lsa.SetInformationTrustedDomain(remote_tdo_handle,
2513 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2516 self.outf.write("Creating local TDO.\n")
2517 current_request = { "location": "local", "name": "CreateTrustedDomainEx2"}
2518 local_tdo_handle = local_lsa.CreateTrustedDomainEx2(local_policy,
2521 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2522 self.outf.write("Local TDO created\n")
2524 self.outf.write("Setting supported encryption types on local TDO.\n")
2525 current_request = { "location": "local", "name": "SetInformationTrustedDomain"}
2526 local_lsa.SetInformationTrustedDomain(local_tdo_handle,
2527 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2529 except RuntimeError as error:
2530 self.outf.write("Error: %s failed %sly - cleaning up\n" % (
2531 current_request['name'], current_request['location']))
2532 if remote_tdo_handle:
2533 self.outf.write("Deleting remote TDO.\n")
2534 remote_lsa.DeleteObject(remote_tdo_handle)
2535 remote_tdo_handle = None
2536 if local_tdo_handle:
2537 self.outf.write("Deleting local TDO.\n")
2538 local_lsa.DeleteObject(local_tdo_handle)
2539 local_tdo_handle = None
2540 if current_request['location'] is "remote":
2541 raise self.RemoteRuntimeError(self, error, "%s" % (
2542 current_request['name']))
2543 raise self.LocalRuntimeError(self, error, "%s" % (
2544 current_request['name']))
2547 if local_trust_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2548 self.outf.write("Setup local forest trust information...\n")
2550 # get all information about the remote trust
2551 # this triggers netr_GetForestTrustInformation to the remote domain
2552 # and lsaRSetForestTrustInformation() locally, but new top level
2553 # names are disabled by default.
2554 local_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
2555 remote_lsa_info.dns_domain.string,
2556 netlogon.DS_GFTI_UPDATE_TDO)
2557 except RuntimeError as error:
2558 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2561 # here we try to enable all top level names
2562 local_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
2563 remote_lsa_info.dns_domain,
2564 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2567 except RuntimeError as error:
2568 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2570 self.write_forest_trust_info(local_forest_info,
2571 tln=remote_lsa_info.dns_domain.string,
2572 collisions=local_forest_collision)
2574 if remote_trust_info:
2575 self.outf.write("Setup remote forest trust information...\n")
2577 # get all information about the local trust (from the perspective of the remote domain)
2578 # this triggers netr_GetForestTrustInformation to our domain.
2579 # and lsaRSetForestTrustInformation() remotely, but new top level
2580 # names are disabled by default.
2581 remote_forest_info = remote_netlogon.netr_DsRGetForestTrustInformation(remote_netlogon_info.dc_unc,
2582 local_lsa_info.dns_domain.string,
2583 netlogon.DS_GFTI_UPDATE_TDO)
2584 except RuntimeError as error:
2585 raise self.RemoteRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2588 # here we try to enable all top level names
2589 remote_forest_collision = remote_lsa.lsaRSetForestTrustInformation(remote_policy,
2590 local_lsa_info.dns_domain,
2591 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2594 except RuntimeError as error:
2595 raise self.RemoteRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2597 self.write_forest_trust_info(remote_forest_info,
2598 tln=local_lsa_info.dns_domain.string,
2599 collisions=remote_forest_collision)
2601 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2602 self.outf.write("Validating outgoing trust...\n")
2604 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_netlogon_info.dc_unc,
2605 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2607 remote_lsa_info.dns_domain.string)
2608 except RuntimeError as error:
2609 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2611 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2612 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2614 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2615 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2616 local_trust_verify.trusted_dc_name,
2617 local_trust_verify.tc_connection_status[1],
2618 local_trust_verify.pdc_connection_status[1])
2620 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2621 local_trust_verify.trusted_dc_name,
2622 local_trust_verify.tc_connection_status[1],
2623 local_trust_verify.pdc_connection_status[1])
2625 if local_trust_status != self.WERR_OK or local_conn_status != self.WERR_OK:
2626 raise CommandError(local_validation)
2628 self.outf.write("OK: %s\n" % local_validation)
2630 if remote_trust_info:
2631 if remote_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2632 self.outf.write("Validating incoming trust...\n")
2634 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_netlogon_info.dc_unc,
2635 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2637 local_lsa_info.dns_domain.string)
2638 except RuntimeError as error:
2639 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2641 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2642 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2644 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2645 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2646 remote_trust_verify.trusted_dc_name,
2647 remote_trust_verify.tc_connection_status[1],
2648 remote_trust_verify.pdc_connection_status[1])
2650 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2651 remote_trust_verify.trusted_dc_name,
2652 remote_trust_verify.tc_connection_status[1],
2653 remote_trust_verify.pdc_connection_status[1])
2655 if remote_trust_status != self.WERR_OK or remote_conn_status != self.WERR_OK:
2656 raise CommandError(remote_validation)
2658 self.outf.write("OK: %s\n" % remote_validation)
2660 if remote_tdo_handle is not None:
2662 remote_lsa.Close(remote_tdo_handle)
2663 except RuntimeError as error:
2665 remote_tdo_handle = None
2666 if local_tdo_handle is not None:
2668 local_lsa.Close(local_tdo_handle)
2669 except RuntimeError as error:
2671 local_tdo_handle = None
2673 self.outf.write("Success.\n")
2676 class cmd_domain_trust_delete(DomainTrustCommand):
2677 """Delete a domain trust."""
2679 synopsis = "%prog DOMAIN [options]"
2681 takes_optiongroups = {
2682 "sambaopts": options.SambaOptions,
2683 "versionopts": options.VersionOptions,
2684 "credopts": options.CredentialsOptions,
2685 "localdcopts": LocalDCCredentialsOptions,
2689 Option("--delete-location", type="choice", metavar="LOCATION",
2690 choices=["local", "both"],
2691 help="Where to delete the trusted domain object: 'local' or 'both'.",
2692 dest='delete_location',
2696 takes_args = ["domain"]
2698 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2699 delete_location=None):
2701 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2702 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2703 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2705 if delete_location == "local":
2706 remote_policy_access = None
2708 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2709 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2710 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2712 local_server = self.setup_local_server(sambaopts, localdcopts)
2714 local_lsa = self.new_local_lsa_connection()
2715 except RuntimeError as error:
2716 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2719 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2720 except RuntimeError as error:
2721 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2723 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2724 local_lsa_info.name.string,
2725 local_lsa_info.dns_domain.string,
2726 local_lsa_info.sid))
2728 local_tdo_info = None
2729 local_tdo_handle = None
2730 remote_tdo_info = None
2731 remote_tdo_handle = None
2733 lsaString = lsa.String()
2735 lsaString.string = domain
2736 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2737 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2738 except RuntimeError as error:
2739 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2740 raise CommandError("Failed to find trust for domain '%s'" % domain)
2741 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2744 if remote_policy_access is not None:
2746 remote_server = self.setup_remote_server(credopts, domain)
2747 except RuntimeError as error:
2748 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2751 remote_lsa = self.new_remote_lsa_connection()
2752 except RuntimeError as error:
2753 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2756 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2757 except RuntimeError as error:
2758 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2760 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2761 remote_lsa_info.name.string,
2762 remote_lsa_info.dns_domain.string,
2763 remote_lsa_info.sid))
2765 if remote_lsa_info.sid != local_tdo_info.sid or \
2766 remote_lsa_info.name.string != local_tdo_info.netbios_name.string or \
2767 remote_lsa_info.dns_domain.string != local_tdo_info.domain_name.string:
2768 raise CommandError("LocalTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2769 local_tdo_info.netbios_name.string,
2770 local_tdo_info.domain_name.string,
2771 local_tdo_info.sid))
2774 lsaString.string = local_lsa_info.dns_domain.string
2775 remote_tdo_info = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2776 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2777 except RuntimeError as error:
2778 if not self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2779 raise self.RemoteRuntimeError(self, error, "QueryTrustedDomainInfoByName(%s)" % (
2783 if remote_tdo_info is not None:
2784 if local_lsa_info.sid != remote_tdo_info.sid or \
2785 local_lsa_info.name.string != remote_tdo_info.netbios_name.string or \
2786 local_lsa_info.dns_domain.string != remote_tdo_info.domain_name.string:
2787 raise CommandError("RemoteTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2788 remote_tdo_info.netbios_name.string,
2789 remote_tdo_info.domain_name.string,
2790 remote_tdo_info.sid))
2792 if local_tdo_info is not None:
2794 lsaString.string = local_tdo_info.domain_name.string
2795 local_tdo_handle = local_lsa.OpenTrustedDomainByName(local_policy,
2797 security.SEC_STD_DELETE)
2798 except RuntimeError as error:
2799 raise self.LocalRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2802 local_lsa.DeleteObject(local_tdo_handle)
2803 local_tdo_handle = None
2805 if remote_tdo_info is not None:
2807 lsaString.string = remote_tdo_info.domain_name.string
2808 remote_tdo_handle = remote_lsa.OpenTrustedDomainByName(remote_policy,
2810 security.SEC_STD_DELETE)
2811 except RuntimeError as error:
2812 raise self.RemoteRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2815 if remote_tdo_handle is not None:
2817 remote_lsa.DeleteObject(remote_tdo_handle)
2818 remote_tdo_handle = None
2819 self.outf.write("RemoteTDO deleted.\n")
2820 except RuntimeError as error:
2821 self.outf.write("%s\n" % self.RemoteRuntimeError(self, error, "DeleteObject() failed"))
2823 if local_tdo_handle is not None:
2825 local_lsa.DeleteObject(local_tdo_handle)
2826 local_tdo_handle = None
2827 self.outf.write("LocalTDO deleted.\n")
2828 except RuntimeError as error:
2829 self.outf.write("%s\n" % self.LocalRuntimeError(self, error, "DeleteObject() failed"))
2833 class cmd_domain_trust_validate(DomainTrustCommand):
2834 """Validate a domain trust."""
2836 synopsis = "%prog DOMAIN [options]"
2838 takes_optiongroups = {
2839 "sambaopts": options.SambaOptions,
2840 "versionopts": options.VersionOptions,
2841 "credopts": options.CredentialsOptions,
2842 "localdcopts": LocalDCCredentialsOptions,
2846 Option("--validate-location", type="choice", metavar="LOCATION",
2847 choices=["local", "both"],
2848 help="Where to validate the trusted domain object: 'local' or 'both'.",
2849 dest='validate_location',
2853 takes_args = ["domain"]
2855 def run(self, domain, sambaopts=None, versionopts=None, credopts=None, localdcopts=None,
2856 validate_location=None):
2858 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2860 local_server = self.setup_local_server(sambaopts, localdcopts)
2862 local_lsa = self.new_local_lsa_connection()
2863 except RuntimeError as error:
2864 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2867 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2868 except RuntimeError as error:
2869 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2871 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2872 local_lsa_info.name.string,
2873 local_lsa_info.dns_domain.string,
2874 local_lsa_info.sid))
2877 lsaString = lsa.String()
2878 lsaString.string = domain
2879 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2880 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2881 except RuntimeError as error:
2882 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2883 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2885 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
2887 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
2888 local_tdo_info.netbios_name.string,
2889 local_tdo_info.domain_name.string,
2890 local_tdo_info.sid))
2893 local_netlogon = self.new_local_netlogon_connection()
2894 except RuntimeError as error:
2895 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2898 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_server,
2899 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2901 local_tdo_info.domain_name.string)
2902 except RuntimeError as error:
2903 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2905 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2906 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2908 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2909 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2910 local_trust_verify.trusted_dc_name,
2911 local_trust_verify.tc_connection_status[1],
2912 local_trust_verify.pdc_connection_status[1])
2914 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2915 local_trust_verify.trusted_dc_name,
2916 local_trust_verify.tc_connection_status[1],
2917 local_trust_verify.pdc_connection_status[1])
2919 if local_trust_status != self.WERR_OK or local_conn_status != self.WERR_OK:
2920 raise CommandError(local_validation)
2922 self.outf.write("OK: %s\n" % local_validation)
2925 server = local_trust_verify.trusted_dc_name.replace('\\', '')
2926 domain_and_server = "%s\\%s" % (local_tdo_info.domain_name.string, server)
2927 local_trust_rediscover = local_netlogon.netr_LogonControl2Ex(local_server,
2928 netlogon.NETLOGON_CONTROL_REDISCOVER,
2931 except RuntimeError as error:
2932 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
2934 local_conn_status = self._uint32(local_trust_rediscover.tc_connection_status[0])
2935 local_rediscover = "LocalRediscover: DC[%s] CONNECTION[%s]" % (
2936 local_trust_rediscover.trusted_dc_name,
2937 local_trust_rediscover.tc_connection_status[1])
2939 if local_conn_status != self.WERR_OK:
2940 raise CommandError(local_rediscover)
2942 self.outf.write("OK: %s\n" % local_rediscover)
2944 if validate_location != "local":
2946 remote_server = self.setup_remote_server(credopts, domain, require_pdc=False)
2947 except RuntimeError as error:
2948 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2951 remote_netlogon = self.new_remote_netlogon_connection()
2952 except RuntimeError as error:
2953 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2956 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_server,
2957 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2959 local_lsa_info.dns_domain.string)
2960 except RuntimeError as error:
2961 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2963 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2964 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2966 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2967 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2968 remote_trust_verify.trusted_dc_name,
2969 remote_trust_verify.tc_connection_status[1],
2970 remote_trust_verify.pdc_connection_status[1])
2972 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2973 remote_trust_verify.trusted_dc_name,
2974 remote_trust_verify.tc_connection_status[1],
2975 remote_trust_verify.pdc_connection_status[1])
2977 if remote_trust_status != self.WERR_OK or remote_conn_status != self.WERR_OK:
2978 raise CommandError(remote_validation)
2980 self.outf.write("OK: %s\n" % remote_validation)
2983 server = remote_trust_verify.trusted_dc_name.replace('\\', '')
2984 domain_and_server = "%s\\%s" % (local_lsa_info.dns_domain.string, server)
2985 remote_trust_rediscover = remote_netlogon.netr_LogonControl2Ex(remote_server,
2986 netlogon.NETLOGON_CONTROL_REDISCOVER,
2989 except RuntimeError as error:
2990 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
2992 remote_conn_status = self._uint32(remote_trust_rediscover.tc_connection_status[0])
2994 remote_rediscover = "RemoteRediscover: DC[%s] CONNECTION[%s]" % (
2995 remote_trust_rediscover.trusted_dc_name,
2996 remote_trust_rediscover.tc_connection_status[1])
2998 if remote_conn_status != self.WERR_OK:
2999 raise CommandError(remote_rediscover)
3001 self.outf.write("OK: %s\n" % remote_rediscover)
3005 class cmd_domain_trust_namespaces(DomainTrustCommand):
3006 """Manage forest trust namespaces."""
3008 synopsis = "%prog [DOMAIN] [options]"
3010 takes_optiongroups = {
3011 "sambaopts": options.SambaOptions,
3012 "versionopts": options.VersionOptions,
3013 "localdcopts": LocalDCCredentialsOptions,
3017 Option("--refresh", type="choice", metavar="check|store",
3018 choices=["check", "store", None],
3019 help="List and maybe store refreshed forest trust information: 'check' or 'store'.",
3022 Option("--enable-all", action="store_true",
3023 help="Try to update disabled entries, not allowed with --refresh=check.",
3026 Option("--enable-tln", action="append", metavar='DNSDOMAIN',
3027 help="Enable a top level name entry. Can be specified multiple times.",
3030 Option("--disable-tln", action="append", metavar='DNSDOMAIN',
3031 help="Disable a top level name entry. Can be specified multiple times.",
3034 Option("--add-tln-ex", action="append", metavar='DNSDOMAIN',
3035 help="Add a top level exclusion entry. Can be specified multiple times.",
3038 Option("--delete-tln-ex", action="append", metavar='DNSDOMAIN',
3039 help="Delete a top level exclusion entry. Can be specified multiple times.",
3040 dest='delete_tln_ex',
3042 Option("--enable-nb", action="append", metavar='NETBIOSDOMAIN',
3043 help="Enable a netbios name in a domain entry. Can be specified multiple times.",
3046 Option("--disable-nb", action="append", metavar='NETBIOSDOMAIN',
3047 help="Disable a netbios name in a domain entry. Can be specified multiple times.",
3050 Option("--enable-sid", action="append", metavar='DOMAINSID',
3051 help="Enable a SID in a domain entry. Can be specified multiple times.",
3052 dest='enable_sid_str',
3054 Option("--disable-sid", action="append", metavar='DOMAINSID',
3055 help="Disable a SID in a domain entry. Can be specified multiple times.",
3056 dest='disable_sid_str',
3058 Option("--add-upn-suffix", action="append", metavar='DNSDOMAIN',
3059 help="Add a new uPNSuffixes attribute for the local forest. Can be specified multiple times.",
3062 Option("--delete-upn-suffix", action="append", metavar='DNSDOMAIN',
3063 help="Delete an existing uPNSuffixes attribute of the local forest. Can be specified multiple times.",
3066 Option("--add-spn-suffix", action="append", metavar='DNSDOMAIN',
3067 help="Add a new msDS-SPNSuffixes attribute for the local forest. Can be specified multiple times.",
3070 Option("--delete-spn-suffix", action="append", metavar='DNSDOMAIN',
3071 help="Delete an existing msDS-SPNSuffixes attribute of the local forest. Can be specified multiple times.",
3076 takes_args = ["domain?"]
3078 def run(self, domain=None, sambaopts=None, localdcopts=None, versionopts=None,
3079 refresh=None, enable_all=False,
3080 enable_tln=[], disable_tln=[], add_tln_ex=[], delete_tln_ex=[],
3081 enable_sid_str=[], disable_sid_str=[], enable_nb=[], disable_nb=[],
3082 add_upn=[], delete_upn=[], add_spn=[], delete_spn=[]):
3084 require_update = False
3087 if refresh == "store":
3088 raise CommandError("--refresh=%s not allowed without DOMAIN" % refresh)
3091 raise CommandError("--enable-all not allowed without DOMAIN")
3093 if len(enable_tln) > 0:
3094 raise CommandError("--enable-tln not allowed without DOMAIN")
3095 if len(disable_tln) > 0:
3096 raise CommandError("--disable-tln not allowed without DOMAIN")
3098 if len(add_tln_ex) > 0:
3099 raise CommandError("--add-tln-ex not allowed without DOMAIN")
3100 if len(delete_tln_ex) > 0:
3101 raise CommandError("--delete-tln-ex not allowed without DOMAIN")
3103 if len(enable_nb) > 0:
3104 raise CommandError("--enable-nb not allowed without DOMAIN")
3105 if len(disable_nb) > 0:
3106 raise CommandError("--disable-nb not allowed without DOMAIN")
3108 if len(enable_sid_str) > 0:
3109 raise CommandError("--enable-sid not allowed without DOMAIN")
3110 if len(disable_sid_str) > 0:
3111 raise CommandError("--disable-sid not allowed without DOMAIN")
3113 if len(add_upn) > 0:
3115 if not n.startswith("*."):
3117 raise CommandError("value[%s] specified for --add-upn-suffix should not include with '*.'" % n)
3118 require_update = True
3119 if len(delete_upn) > 0:
3120 for n in delete_upn:
3121 if not n.startswith("*."):
3123 raise CommandError("value[%s] specified for --delete-upn-suffix should not include with '*.'" % n)
3124 require_update = True
3126 for d in delete_upn:
3127 if a.lower() != d.lower():
3129 raise CommandError("value[%s] specified for --add-upn-suffix and --delete-upn-suffix" % a)
3131 if len(add_spn) > 0:
3133 if not n.startswith("*."):
3135 raise CommandError("value[%s] specified for --add-spn-suffix should not include with '*.'" % n)
3136 require_update = True
3137 if len(delete_spn) > 0:
3138 for n in delete_spn:
3139 if not n.startswith("*."):
3141 raise CommandError("value[%s] specified for --delete-spn-suffix should not include with '*.'" % n)
3142 require_update = True
3144 for d in delete_spn:
3145 if a.lower() != d.lower():
3147 raise CommandError("value[%s] specified for --add-spn-suffix and --delete-spn-suffix" % a)
3149 if len(add_upn) > 0:
3150 raise CommandError("--add-upn-suffix not allowed together with DOMAIN")
3151 if len(delete_upn) > 0:
3152 raise CommandError("--delete-upn-suffix not allowed together with DOMAIN")
3153 if len(add_spn) > 0:
3154 raise CommandError("--add-spn-suffix not allowed together with DOMAIN")
3155 if len(delete_spn) > 0:
3156 raise CommandError("--delete-spn-suffix not allowed together with DOMAIN")
3158 if refresh is not None:
3159 if refresh == "store":
3160 require_update = True
3162 if enable_all and refresh != "store":
3163 raise CommandError("--enable-all not allowed together with --refresh=%s" % refresh)
3165 if len(enable_tln) > 0:
3166 raise CommandError("--enable-tln not allowed together with --refresh")
3167 if len(disable_tln) > 0:
3168 raise CommandError("--disable-tln not allowed together with --refresh")
3170 if len(add_tln_ex) > 0:
3171 raise CommandError("--add-tln-ex not allowed together with --refresh")
3172 if len(delete_tln_ex) > 0:
3173 raise CommandError("--delete-tln-ex not allowed together with --refresh")
3175 if len(enable_nb) > 0:
3176 raise CommandError("--enable-nb not allowed together with --refresh")
3177 if len(disable_nb) > 0:
3178 raise CommandError("--disable-nb not allowed together with --refresh")
3180 if len(enable_sid_str) > 0:
3181 raise CommandError("--enable-sid not allowed together with --refresh")
3182 if len(disable_sid_str) > 0:
3183 raise CommandError("--disable-sid not allowed together with --refresh")
3186 require_update = True
3188 if len(enable_tln) > 0:
3189 raise CommandError("--enable-tln not allowed together with --enable-all")
3191 if len(enable_nb) > 0:
3192 raise CommandError("--enable-nb not allowed together with --enable-all")
3194 if len(enable_sid) > 0:
3195 raise CommandError("--enable-sid not allowed together with --enable-all")
3197 if len(enable_tln) > 0:
3198 require_update = True
3199 if len(disable_tln) > 0:
3200 require_update = True
3201 for e in enable_tln:
3202 for d in disable_tln:
3203 if e.lower() != d.lower():
3205 raise CommandError("value[%s] specified for --enable-tln and --disable-tln" % e)
3207 if len(add_tln_ex) > 0:
3208 for n in add_tln_ex:
3209 if not n.startswith("*."):
3211 raise CommandError("value[%s] specified for --add-tln-ex should not include with '*.'" % n)
3212 require_update = True
3213 if len(delete_tln_ex) > 0:
3214 for n in delete_tln_ex:
3215 if not n.startswith("*."):
3217 raise CommandError("value[%s] specified for --delete-tln-ex should not include with '*.'" % n)
3218 require_update = True
3219 for a in add_tln_ex:
3220 for d in delete_tln_ex:
3221 if a.lower() != d.lower():
3223 raise CommandError("value[%s] specified for --add-tln-ex and --delete-tln-ex" % a)
3225 if len(enable_nb) > 0:
3226 require_update = True
3227 if len(disable_nb) > 0:
3228 require_update = True
3230 for d in disable_nb:
3231 if e.upper() != d.upper():
3233 raise CommandError("value[%s] specified for --enable-nb and --disable-nb" % e)
3236 for s in enable_sid_str:
3238 sid = security.dom_sid(s)
3239 except TypeError as error:
3240 raise CommandError("value[%s] specified for --enable-sid is not a valid SID" % s)
3241 enable_sid.append(sid)
3243 for s in disable_sid_str:
3245 sid = security.dom_sid(s)
3246 except TypeError as error:
3247 raise CommandError("value[%s] specified for --disable-sid is not a valid SID" % s)
3248 disable_sid.append(sid)
3249 if len(enable_sid) > 0:
3250 require_update = True
3251 if len(disable_sid) > 0:
3252 require_update = True
3253 for e in enable_sid:
3254 for d in disable_sid:
3257 raise CommandError("value[%s] specified for --enable-sid and --disable-sid" % e)
3259 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
3261 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
3263 local_server = self.setup_local_server(sambaopts, localdcopts)
3265 local_lsa = self.new_local_lsa_connection()
3266 except RuntimeError as error:
3267 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
3270 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
3271 except RuntimeError as error:
3272 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
3274 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
3275 local_lsa_info.name.string,
3276 local_lsa_info.dns_domain.string,
3277 local_lsa_info.sid))
3281 local_netlogon = self.new_local_netlogon_connection()
3282 except RuntimeError as error:
3283 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3286 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3287 except RuntimeError as error:
3288 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3290 if local_netlogon_info.domain_name != local_netlogon_info.forest_name:
3291 raise CommandError("The local domain [%s] is not the forest root [%s]" % (
3292 local_netlogon_info.domain_name,
3293 local_netlogon_info.forest_name))
3296 # get all information about our own forest
3297 own_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3299 except RuntimeError as error:
3300 if self.check_runtime_error(error, self.WERR_RPC_S_PROCNUM_OUT_OF_RANGE):
3301 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3304 if self.check_runtime_error(error, self.WERR_INVALID_FUNCTION):
3305 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3308 if self.check_runtime_error(error, self.WERR_NERR_ACFNOTLOADED):
3309 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3312 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3314 self.outf.write("Own forest trust information...\n")
3315 self.write_forest_trust_info(own_forest_info,
3316 tln=local_lsa_info.dns_domain.string)
3319 local_samdb = self.new_local_ldap_connection()
3320 except RuntimeError as error:
3321 raise self.LocalRuntimeError(self, error, "failed to connect to SamDB")
3323 local_partitions_dn = "CN=Partitions,%s" % str(local_samdb.get_config_basedn())
3324 attrs = ['uPNSuffixes', 'msDS-SPNSuffixes']
3326 msgs = local_samdb.search(base=local_partitions_dn,
3327 scope=ldb.SCOPE_BASE,
3328 expression="(objectClass=crossRefContainer)",
3330 stored_msg = msgs[0]
3331 except ldb.LdbError as error:
3332 raise self.LocalLdbError(self, error, "failed to search partition dn")
3334 stored_upn_vals = []
3335 if 'uPNSuffixes' in stored_msg:
3336 stored_upn_vals.extend(stored_msg['uPNSuffixes'])
3338 stored_spn_vals = []
3339 if 'msDS-SPNSuffixes' in stored_msg:
3340 stored_spn_vals.extend(stored_msg['msDS-SPNSuffixes'])
3342 self.outf.write("Stored uPNSuffixes attributes[%d]:\n" % len(stored_upn_vals))
3343 for v in stored_upn_vals:
3344 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3345 self.outf.write("Stored msDS-SPNSuffixes attributes[%d]:\n" % len(stored_spn_vals))
3346 for v in stored_spn_vals:
3347 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3349 if not require_update:
3353 update_upn_vals = []
3354 update_upn_vals.extend(stored_upn_vals)
3357 update_spn_vals = []
3358 update_spn_vals.extend(stored_spn_vals)
3362 for i in xrange(0, len(update_upn_vals)):
3363 v = update_upn_vals[i]
3364 if v.lower() != upn.lower():
3369 raise CommandError("Entry already present for value[%s] specified for --add-upn-suffix" % upn)
3370 update_upn_vals.append(upn)
3373 for upn in delete_upn:
3375 for i in xrange(0, len(update_upn_vals)):
3376 v = update_upn_vals[i]
3377 if v.lower() != upn.lower():
3382 raise CommandError("Entry not found for value[%s] specified for --delete-upn-suffix" % upn)
3384 update_upn_vals.pop(idx)
3389 for i in xrange(0, len(update_spn_vals)):
3390 v = update_spn_vals[i]
3391 if v.lower() != spn.lower():
3396 raise CommandError("Entry already present for value[%s] specified for --add-spn-suffix" % spn)
3397 update_spn_vals.append(spn)
3400 for spn in delete_spn:
3402 for i in xrange(0, len(update_spn_vals)):
3403 v = update_spn_vals[i]
3404 if v.lower() != spn.lower():
3409 raise CommandError("Entry not found for value[%s] specified for --delete-spn-suffix" % spn)
3411 update_spn_vals.pop(idx)
3414 self.outf.write("Update uPNSuffixes attributes[%d]:\n" % len(update_upn_vals))
3415 for v in update_upn_vals:
3416 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3417 self.outf.write("Update msDS-SPNSuffixes attributes[%d]:\n" % len(update_spn_vals))
3418 for v in update_spn_vals:
3419 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3421 update_msg = ldb.Message()
3422 update_msg.dn = stored_msg.dn
3425 update_msg['uPNSuffixes'] = ldb.MessageElement(update_upn_vals,
3426 ldb.FLAG_MOD_REPLACE,
3429 update_msg['msDS-SPNSuffixes'] = ldb.MessageElement(update_spn_vals,
3430 ldb.FLAG_MOD_REPLACE,
3433 local_samdb.modify(update_msg)
3434 except ldb.LdbError as error:
3435 raise self.LocalLdbError(self, error, "failed to update partition dn")
3438 stored_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3440 except RuntimeError as error:
3441 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3443 self.outf.write("Stored forest trust information...\n")
3444 self.write_forest_trust_info(stored_forest_info,
3445 tln=local_lsa_info.dns_domain.string)
3449 lsaString = lsa.String()
3450 lsaString.string = domain
3451 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
3452 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
3453 except RuntimeError as error:
3454 if self.check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND):
3455 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
3457 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
3459 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
3460 local_tdo_info.netbios_name.string,
3461 local_tdo_info.domain_name.string,
3462 local_tdo_info.sid))
3464 if not local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
3465 raise CommandError("trusted domain object for domain [%s] is not marked as FOREST_TRANSITIVE." % domain)
3467 if refresh is not None:
3469 local_netlogon = self.new_local_netlogon_connection()
3470 except RuntimeError as error:
3471 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3474 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3475 except RuntimeError as error:
3476 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3478 lsa_update_check = 1
3479 if refresh == "store":
3480 netlogon_update_tdo = netlogon.DS_GFTI_UPDATE_TDO
3482 lsa_update_check = 0
3484 netlogon_update_tdo = 0
3487 # get all information about the remote trust
3488 # this triggers netr_GetForestTrustInformation to the remote domain
3489 # and lsaRSetForestTrustInformation() locally, but new top level
3490 # names are disabled by default.
3491 fresh_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3492 local_tdo_info.domain_name.string,
3493 netlogon_update_tdo)
3494 except RuntimeError as error:
3495 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3498 fresh_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3499 local_tdo_info.domain_name,
3500 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3503 except RuntimeError as error:
3504 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3506 self.outf.write("Fresh forest trust information...\n")
3507 self.write_forest_trust_info(fresh_forest_info,
3508 tln=local_tdo_info.domain_name.string,
3509 collisions=fresh_forest_collision)
3511 if refresh == "store":
3513 lsaString = lsa.String()
3514 lsaString.string = local_tdo_info.domain_name.string
3515 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3517 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3518 except RuntimeError as error:
3519 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3521 self.outf.write("Stored forest trust information...\n")
3522 self.write_forest_trust_info(stored_forest_info,
3523 tln=local_tdo_info.domain_name.string)
3528 # The none --refresh path
3532 lsaString = lsa.String()
3533 lsaString.string = local_tdo_info.domain_name.string
3534 local_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3536 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3537 except RuntimeError as error:
3538 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3540 self.outf.write("Local forest trust information...\n")
3541 self.write_forest_trust_info(local_forest_info,
3542 tln=local_tdo_info.domain_name.string)
3544 if not require_update:
3548 entries.extend(local_forest_info.entries)
3549 update_forest_info = lsa.ForestTrustInformation()
3550 update_forest_info.count = len(entries)
3551 update_forest_info.entries = entries
3554 for i in xrange(0, len(update_forest_info.entries)):
3555 r = update_forest_info.entries[i]
3556 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3558 if update_forest_info.entries[i].flags == 0:
3560 update_forest_info.entries[i].time = 0
3561 update_forest_info.entries[i].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3562 for i in xrange(0, len(update_forest_info.entries)):
3563 r = update_forest_info.entries[i]
3564 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3566 if update_forest_info.entries[i].flags == 0:
3568 update_forest_info.entries[i].time = 0
3569 update_forest_info.entries[i].flags &= ~lsa.LSA_NB_DISABLED_MASK
3570 update_forest_info.entries[i].flags &= ~lsa.LSA_SID_DISABLED_MASK
3572 for tln in enable_tln:
3574 for i in xrange(0, len(update_forest_info.entries)):
3575 r = update_forest_info.entries[i]
3576 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3578 if r.forest_trust_data.string.lower() != tln.lower():
3583 raise CommandError("Entry not found for value[%s] specified for --enable-tln" % tln)
3584 if not update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_MASK:
3585 raise CommandError("Entry found for value[%s] specified for --enable-tln is already enabled" % tln)
3586 update_forest_info.entries[idx].time = 0
3587 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3589 for tln in disable_tln:
3591 for i in xrange(0, len(update_forest_info.entries)):
3592 r = update_forest_info.entries[i]
3593 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3595 if r.forest_trust_data.string.lower() != tln.lower():
3600 raise CommandError("Entry not found for value[%s] specified for --disable-tln" % tln)
3601 if update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_ADMIN:
3602 raise CommandError("Entry found for value[%s] specified for --disable-tln is already disabled" % tln)
3603 update_forest_info.entries[idx].time = 0
3604 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3605 update_forest_info.entries[idx].flags |= lsa.LSA_TLN_DISABLED_ADMIN
3607 for tln_ex in add_tln_ex:
3609 for i in xrange(0, len(update_forest_info.entries)):
3610 r = update_forest_info.entries[i]
3611 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3613 if r.forest_trust_data.string.lower() != tln_ex.lower():
3618 raise CommandError("Entry already present for value[%s] specified for --add-tln-ex" % tln_ex)
3620 tln_dot = ".%s" % tln_ex.lower()
3622 for i in xrange(0, len(update_forest_info.entries)):
3623 r = update_forest_info.entries[i]
3624 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3626 r_dot = ".%s" % r.forest_trust_data.string.lower()
3627 if tln_dot == r_dot:
3628 raise CommandError("TLN entry present for value[%s] specified for --add-tln-ex" % tln_ex)
3629 if not tln_dot.endswith(r_dot):
3635 raise CommandError("No TLN parent present for value[%s] specified for --add-tln-ex" % tln_ex)
3637 r = lsa.ForestTrustRecord()
3638 r.type = lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX
3641 r.forest_trust_data.string = tln_ex
3644 entries.extend(update_forest_info.entries)
3645 entries.insert(idx + 1, r)
3646 update_forest_info.count = len(entries)
3647 update_forest_info.entries = entries
3649 for tln_ex in delete_tln_ex:
3651 for i in xrange(0, len(update_forest_info.entries)):
3652 r = update_forest_info.entries[i]
3653 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3655 if r.forest_trust_data.string.lower() != tln_ex.lower():
3660 raise CommandError("Entry not found for value[%s] specified for --delete-tln-ex" % tln_ex)
3663 entries.extend(update_forest_info.entries)
3665 update_forest_info.count = len(entries)
3666 update_forest_info.entries = entries
3668 for nb in enable_nb:
3670 for i in xrange(0, len(update_forest_info.entries)):
3671 r = update_forest_info.entries[i]
3672 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3674 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3679 raise CommandError("Entry not found for value[%s] specified for --enable-nb" % nb)
3680 if not update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_MASK:
3681 raise CommandError("Entry found for value[%s] specified for --enable-nb is already enabled" % nb)
3682 update_forest_info.entries[idx].time = 0
3683 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3685 for nb in disable_nb:
3687 for i in xrange(0, len(update_forest_info.entries)):
3688 r = update_forest_info.entries[i]
3689 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3691 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3696 raise CommandError("Entry not found for value[%s] specified for --delete-nb" % nb)
3697 if update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_ADMIN:
3698 raise CommandError("Entry found for value[%s] specified for --disable-nb is already disabled" % nb)
3699 update_forest_info.entries[idx].time = 0
3700 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3701 update_forest_info.entries[idx].flags |= lsa.LSA_NB_DISABLED_ADMIN
3703 for sid in enable_sid:
3705 for i in xrange(0, len(update_forest_info.entries)):
3706 r = update_forest_info.entries[i]
3707 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3709 if r.forest_trust_data.domain_sid != sid:
3714 raise CommandError("Entry not found for value[%s] specified for --enable-sid" % sid)
3715 if not update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_MASK:
3716 raise CommandError("Entry found for value[%s] specified for --enable-sid is already enabled" % nb)
3717 update_forest_info.entries[idx].time = 0
3718 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3720 for sid in disable_sid:
3722 for i in xrange(0, len(update_forest_info.entries)):
3723 r = update_forest_info.entries[i]
3724 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3726 if r.forest_trust_data.domain_sid != sid:
3731 raise CommandError("Entry not found for value[%s] specified for --delete-sid" % sid)
3732 if update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_ADMIN:
3733 raise CommandError("Entry found for value[%s] specified for --disable-sid is already disabled" % nb)
3734 update_forest_info.entries[idx].time = 0
3735 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3736 update_forest_info.entries[idx].flags |= lsa.LSA_SID_DISABLED_ADMIN
3739 update_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3740 local_tdo_info.domain_name,
3741 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3742 update_forest_info, 0)
3743 except RuntimeError as error:
3744 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3746 self.outf.write("Updated forest trust information...\n")
3747 self.write_forest_trust_info(update_forest_info,
3748 tln=local_tdo_info.domain_name.string,
3749 collisions=update_forest_collision)
3752 lsaString = lsa.String()
3753 lsaString.string = local_tdo_info.domain_name.string
3754 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3756 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3757 except RuntimeError as error:
3758 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3760 self.outf.write("Stored forest trust information...\n")
3761 self.write_forest_trust_info(stored_forest_info,
3762 tln=local_tdo_info.domain_name.string)
3765 class cmd_domain_tombstones_expunge(Command):
3766 """Expunge tombstones from the database.
3768 This command expunges tombstones from the database."""
3769 synopsis = "%prog NC [NC [...]] [options]"
3772 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
3773 metavar="URL", dest="H"),
3774 Option("--current-time",
3775 help="The current time to evaluate the tombstone lifetime from, expressed as YYYY-MM-DD",
3777 Option("--tombstone-lifetime", help="Number of days a tombstone should be preserved for", type=int),
3780 takes_args = ["nc*"]
3782 takes_optiongroups = {
3783 "sambaopts": options.SambaOptions,
3784 "credopts": options.CredentialsOptions,
3785 "versionopts": options.VersionOptions,
3788 def run(self, *ncs, **kwargs):
3789 sambaopts = kwargs.get("sambaopts")
3790 credopts = kwargs.get("credopts")
3791 versionpts = kwargs.get("versionopts")
3793 current_time_string = kwargs.get("current_time")
3794 tombstone_lifetime = kwargs.get("tombstone_lifetime")
3795 lp = sambaopts.get_loadparm()
3796 creds = credopts.get_credentials(lp)
3797 samdb = SamDB(url=H, session_info=system_session(),
3798 credentials=creds, lp=lp)
3800 if current_time_string is not None:
3801 current_time_obj = time.strptime(current_time_string, "%Y-%m-%d")
3802 current_time = long(time.mktime(current_time_obj))
3805 current_time = long(time.time())
3808 res = samdb.search(expression="", base="", scope=ldb.SCOPE_BASE,
3809 attrs=["namingContexts"])
3812 for nc in res[0]["namingContexts"]:
3817 started_transaction = False
3819 samdb.transaction_start()
3820 started_transaction = True
3822 removed_links) = samdb.garbage_collect_tombstones(ncs,
3823 current_time=current_time,
3824 tombstone_lifetime=tombstone_lifetime)
3826 except Exception, err:
3827 if started_transaction:
3828 samdb.transaction_cancel()
3829 raise CommandError("Failed to expunge / garbage collect tombstones", err)
3831 samdb.transaction_commit()
3833 self.outf.write("Removed %d objects and %d links successfully\n"
3834 % (removed_objects, removed_links))
3838 class cmd_domain_trust(SuperCommand):
3839 """Domain and forest trust management."""
3842 subcommands["list"] = cmd_domain_trust_list()
3843 subcommands["show"] = cmd_domain_trust_show()
3844 subcommands["create"] = cmd_domain_trust_create()
3845 subcommands["delete"] = cmd_domain_trust_delete()
3846 subcommands["validate"] = cmd_domain_trust_validate()
3847 subcommands["namespaces"] = cmd_domain_trust_namespaces()
3849 class cmd_domain_tombstones(SuperCommand):
3850 """Domain tombstone and recycled object management."""
3853 subcommands["expunge"] = cmd_domain_tombstones_expunge()
3855 class cmd_domain(SuperCommand):
3856 """Domain management."""
3859 subcommands["demote"] = cmd_domain_demote()
3860 if cmd_domain_export_keytab is not None:
3861 subcommands["exportkeytab"] = cmd_domain_export_keytab()
3862 subcommands["info"] = cmd_domain_info()
3863 subcommands["provision"] = cmd_domain_provision()
3864 subcommands["join"] = cmd_domain_join()
3865 subcommands["dcpromo"] = cmd_domain_dcpromo()
3866 subcommands["level"] = cmd_domain_level()
3867 subcommands["passwordsettings"] = cmd_domain_passwordsettings()
3868 subcommands["classicupgrade"] = cmd_domain_classicupgrade()
3869 subcommands["samba3upgrade"] = cmd_domain_samba3upgrade()
3870 subcommands["trust"] = cmd_domain_trust()
3871 subcommands["tombstones"] = cmd_domain_tombstones()