Bug 11818 : obvious missing word When trying to demote a dc, 'remove_dc.remove_sysvol...
[Samba.git] / python / samba / netcmd / domain.py
blob68775ec4f113095b541501872ebe25bd3a9d15d6
1 # domain management
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
26 import ldb
27 import string
28 import os
29 import sys
30 import ctypes
31 import random
32 import tempfile
33 import logging
34 import subprocess
35 from getpass import getpass
36 from samba.net import Net, LIBNET_JOIN_AUTOMATIC
37 import samba.ntacls
38 from samba.join import join_RODC, join_DC, join_subdomain
39 from samba.auth import system_session
40 from samba.samdb import SamDB
41 from samba.ndr import ndr_unpack, ndr_pack, ndr_print
42 from samba.dcerpc import drsuapi
43 from samba.dcerpc import drsblobs
44 from samba.dcerpc import lsa
45 from samba.dcerpc import netlogon
46 from samba.dcerpc import security
47 from samba.dcerpc import nbt
48 from samba.dcerpc import misc
49 from samba.dcerpc.samr import DOMAIN_PASSWORD_COMPLEX, DOMAIN_PASSWORD_STORE_CLEARTEXT
50 from samba.netcmd import (
51 Command,
52 CommandError,
53 SuperCommand,
54 Option
56 from samba.netcmd.common import netcmd_get_domain_infos_via_cldap
57 from samba.samba3 import Samba3
58 from samba.samba3 import param as s3param
59 from samba.upgrade import upgrade_from_samba3
60 from samba.drs_utils import (
61 sendDsReplicaSync, drsuapi_connect, drsException,
62 sendRemoveDsServer)
63 from samba import remove_dc, arcfour_encrypt, string_to_byte_array
65 from samba.dsdb import (
66 DS_DOMAIN_FUNCTION_2000,
67 DS_DOMAIN_FUNCTION_2003,
68 DS_DOMAIN_FUNCTION_2003_MIXED,
69 DS_DOMAIN_FUNCTION_2008,
70 DS_DOMAIN_FUNCTION_2008_R2,
71 DS_DOMAIN_FUNCTION_2012,
72 DS_DOMAIN_FUNCTION_2012_R2,
73 DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL,
74 DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL,
75 UF_WORKSTATION_TRUST_ACCOUNT,
76 UF_SERVER_TRUST_ACCOUNT,
77 UF_TRUSTED_FOR_DELEGATION,
78 UF_PARTIAL_SECRETS_ACCOUNT
81 from samba.provision import (
82 provision,
83 ProvisioningError
86 from samba.provision.common import (
87 FILL_FULL,
88 FILL_NT4SYNC,
89 FILL_DRS
92 def get_testparm_var(testparm, smbconf, varname):
93 errfile = open(os.devnull, 'w')
94 p = subprocess.Popen([testparm, '-s', '-l',
95 '--parameter-name=%s' % varname, smbconf],
96 stdout=subprocess.PIPE, stderr=errfile)
97 (out,err) = p.communicate()
98 errfile.close()
99 lines = out.split('\n')
100 if lines:
101 return lines[0].strip()
102 return ""
104 try:
105 import samba.dckeytab
106 except ImportError:
107 cmd_domain_export_keytab = None
108 else:
109 class cmd_domain_export_keytab(Command):
110 """Dump Kerberos keys of the domain into a keytab."""
112 synopsis = "%prog <keytab> [options]"
114 takes_optiongroups = {
115 "sambaopts": options.SambaOptions,
116 "credopts": options.CredentialsOptions,
117 "versionopts": options.VersionOptions,
120 takes_options = [
121 Option("--principal", help="extract only this principal", type=str),
124 takes_args = ["keytab"]
126 def run(self, keytab, credopts=None, sambaopts=None, versionopts=None, principal=None):
127 lp = sambaopts.get_loadparm()
128 net = Net(None, lp)
129 net.export_keytab(keytab=keytab, principal=principal)
132 class cmd_domain_info(Command):
133 """Print basic info about a domain and the DC passed as parameter."""
135 synopsis = "%prog <ip_address> [options]"
137 takes_options = [
140 takes_optiongroups = {
141 "sambaopts": options.SambaOptions,
142 "credopts": options.CredentialsOptions,
143 "versionopts": options.VersionOptions,
146 takes_args = ["address"]
148 def run(self, address, credopts=None, sambaopts=None, versionopts=None):
149 lp = sambaopts.get_loadparm()
150 try:
151 res = netcmd_get_domain_infos_via_cldap(lp, None, address)
152 except RuntimeError:
153 raise CommandError("Invalid IP address '" + address + "'!")
154 self.outf.write("Forest : %s\n" % res.forest)
155 self.outf.write("Domain : %s\n" % res.dns_domain)
156 self.outf.write("Netbios domain : %s\n" % res.domain_name)
157 self.outf.write("DC name : %s\n" % res.pdc_dns_name)
158 self.outf.write("DC netbios name : %s\n" % res.pdc_name)
159 self.outf.write("Server site : %s\n" % res.server_site)
160 self.outf.write("Client site : %s\n" % res.client_site)
163 class cmd_domain_provision(Command):
164 """Provision a domain."""
166 synopsis = "%prog [options]"
168 takes_optiongroups = {
169 "sambaopts": options.SambaOptions,
170 "versionopts": options.VersionOptions,
173 takes_options = [
174 Option("--interactive", help="Ask for names", action="store_true"),
175 Option("--domain", type="string", metavar="DOMAIN",
176 help="set domain"),
177 Option("--domain-guid", type="string", metavar="GUID",
178 help="set domainguid (otherwise random)"),
179 Option("--domain-sid", type="string", metavar="SID",
180 help="set domainsid (otherwise random)"),
181 Option("--ntds-guid", type="string", metavar="GUID",
182 help="set NTDS object GUID (otherwise random)"),
183 Option("--invocationid", type="string", metavar="GUID",
184 help="set invocationid (otherwise random)"),
185 Option("--host-name", type="string", metavar="HOSTNAME",
186 help="set hostname"),
187 Option("--host-ip", type="string", metavar="IPADDRESS",
188 help="set IPv4 ipaddress"),
189 Option("--host-ip6", type="string", metavar="IP6ADDRESS",
190 help="set IPv6 ipaddress"),
191 Option("--site", type="string", metavar="SITENAME",
192 help="set site name"),
193 Option("--adminpass", type="string", metavar="PASSWORD",
194 help="choose admin password (otherwise random)"),
195 Option("--krbtgtpass", type="string", metavar="PASSWORD",
196 help="choose krbtgt password (otherwise random)"),
197 Option("--machinepass", type="string", metavar="PASSWORD",
198 help="choose machine password (otherwise random)"),
199 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
200 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
201 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
202 "BIND9_FLATFILE uses bind9 text database to store zone information, "
203 "BIND9_DLZ uses samba4 AD to store zone information, "
204 "NONE skips the DNS setup entirely (not recommended)",
205 default="SAMBA_INTERNAL"),
206 Option("--dnspass", type="string", metavar="PASSWORD",
207 help="choose dns password (otherwise random)"),
208 Option("--ldapadminpass", type="string", metavar="PASSWORD",
209 help="choose password to set between Samba and its LDAP backend (otherwise random)"),
210 Option("--root", type="string", metavar="USERNAME",
211 help="choose 'root' unix username"),
212 Option("--nobody", type="string", metavar="USERNAME",
213 help="choose 'nobody' user"),
214 Option("--users", type="string", metavar="GROUPNAME",
215 help="choose 'users' group"),
216 Option("--quiet", help="Be quiet", action="store_true"),
217 Option("--blank", action="store_true",
218 help="do not add users or groups, just the structure"),
219 Option("--ldap-backend-type", type="choice", metavar="LDAP-BACKEND-TYPE",
220 help="Test initialisation support for unsupported LDAP backend type (fedora-ds or openldap) DO NOT USE",
221 choices=["fedora-ds", "openldap"]),
222 Option("--server-role", type="choice", metavar="ROLE",
223 choices=["domain controller", "dc", "member server", "member", "standalone"],
224 help="The server role (domain controller | dc | member server | member | standalone). Default is dc.",
225 default="domain controller"),
226 Option("--function-level", type="choice", metavar="FOR-FUN-LEVEL",
227 choices=["2000", "2003", "2008", "2008_R2"],
228 help="The domain and forest function level (2000 | 2003 | 2008 | 2008_R2 - always native). Default is (Windows) 2008_R2 Native.",
229 default="2008_R2"),
230 Option("--next-rid", type="int", metavar="NEXTRID", default=1000,
231 help="The initial nextRid value (only needed for upgrades). Default is 1000."),
232 Option("--partitions-only",
233 help="Configure Samba's partitions, but do not modify them (ie, join a BDC)", action="store_true"),
234 Option("--targetdir", type="string", metavar="DIR",
235 help="Set target directory"),
236 Option("--ol-mmr-urls", type="string", metavar="LDAPSERVER",
237 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\""),
238 Option("--use-xattrs", type="choice", choices=["yes", "no", "auto"], help="Define if we should use the native fs capabilities or a tdb file for storing attributes likes ntacl, auto tries to make an inteligent guess based on the user rights and system capabilities", default="auto"),
240 Option("--use-rfc2307", action="store_true", help="Use AD to store posix attributes (default = no)"),
243 openldap_options = [
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"),
254 ntvfs_options = [
255 Option("--use-ntvfs", action="store_true", help="Use NTVFS for the fileserver (default = no)"),
258 if os.getenv('TEST_LDAP', "no") == "yes":
259 takes_options.extend(openldap_options)
261 if samba.is_ntvfs_fileserver_built():
262 takes_options.extend(ntvfs_options)
264 takes_args = []
266 def run(self, sambaopts=None, versionopts=None,
267 interactive=None,
268 domain=None,
269 domain_guid=None,
270 domain_sid=None,
271 ntds_guid=None,
272 invocationid=None,
273 host_name=None,
274 host_ip=None,
275 host_ip6=None,
276 adminpass=None,
277 site=None,
278 krbtgtpass=None,
279 machinepass=None,
280 dns_backend=None,
281 dns_forwarder=None,
282 dnspass=None,
283 ldapadminpass=None,
284 root=None,
285 nobody=None,
286 users=None,
287 quiet=None,
288 blank=None,
289 ldap_backend_type=None,
290 server_role=None,
291 function_level=None,
292 next_rid=None,
293 partitions_only=None,
294 targetdir=None,
295 ol_mmr_urls=None,
296 use_xattrs=None,
297 slapd_path=None,
298 use_ntvfs=None,
299 use_rfc2307=None,
300 ldap_backend_nosync=None,
301 ldap_backend_extra_port=None,
302 ldap_backend_forced_uri=None,
303 ldap_dryrun_mode=None):
305 self.logger = self.get_logger("provision")
306 if quiet:
307 self.logger.setLevel(logging.WARNING)
308 else:
309 self.logger.setLevel(logging.INFO)
311 lp = sambaopts.get_loadparm()
312 smbconf = lp.configfile
314 if dns_forwarder is not None:
315 suggested_forwarder = dns_forwarder
316 else:
317 suggested_forwarder = self._get_nameserver_ip()
318 if suggested_forwarder is None:
319 suggested_forwarder = "none"
321 if len(self.raw_argv) == 1:
322 interactive = True
324 if interactive:
325 from getpass import getpass
326 import socket
328 def ask(prompt, default=None):
329 if default is not None:
330 print "%s [%s]: " % (prompt, default),
331 else:
332 print "%s: " % (prompt,),
333 return sys.stdin.readline().rstrip("\n") or default
335 try:
336 default = socket.getfqdn().split(".", 1)[1].upper()
337 except IndexError:
338 default = None
339 realm = ask("Realm", default)
340 if realm in (None, ""):
341 raise CommandError("No realm set!")
343 try:
344 default = realm.split(".")[0]
345 except IndexError:
346 default = None
347 domain = ask("Domain", default)
348 if domain is None:
349 raise CommandError("No domain set!")
351 server_role = ask("Server Role (dc, member, standalone)", "dc")
353 dns_backend = ask("DNS backend (SAMBA_INTERNAL, BIND9_FLATFILE, BIND9_DLZ, NONE)", "SAMBA_INTERNAL")
354 if dns_backend in (None, ''):
355 raise CommandError("No DNS backend set!")
357 if dns_backend == "SAMBA_INTERNAL":
358 dns_forwarder = ask("DNS forwarder IP address (write 'none' to disable forwarding)", suggested_forwarder)
359 if dns_forwarder.lower() in (None, 'none'):
360 suggested_forwarder = None
361 dns_forwarder = None
363 while True:
364 adminpassplain = getpass("Administrator password: ")
365 if not adminpassplain:
366 self.errf.write("Invalid administrator password.\n")
367 else:
368 adminpassverify = getpass("Retype password: ")
369 if not adminpassplain == adminpassverify:
370 self.errf.write("Sorry, passwords do not match.\n")
371 else:
372 adminpass = adminpassplain
373 break
375 else:
376 realm = sambaopts._lp.get('realm')
377 if realm is None:
378 raise CommandError("No realm set!")
379 if domain is None:
380 raise CommandError("No domain set!")
382 if not adminpass:
383 self.logger.info("Administrator password will be set randomly!")
385 if function_level == "2000":
386 dom_for_fun_level = DS_DOMAIN_FUNCTION_2000
387 elif function_level == "2003":
388 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
389 elif function_level == "2008":
390 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008
391 elif function_level == "2008_R2":
392 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
394 if dns_backend == "SAMBA_INTERNAL" and dns_forwarder is None:
395 dns_forwarder = suggested_forwarder
397 samdb_fill = FILL_FULL
398 if blank:
399 samdb_fill = FILL_NT4SYNC
400 elif partitions_only:
401 samdb_fill = FILL_DRS
403 if targetdir is not None:
404 if not os.path.isdir(targetdir):
405 os.mkdir(targetdir)
407 eadb = True
409 if use_xattrs == "yes":
410 eadb = False
411 elif use_xattrs == "auto" and not lp.get("posix:eadb"):
412 if targetdir:
413 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
414 else:
415 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
416 try:
417 try:
418 samba.ntacls.setntacl(lp, file.name,
419 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
420 eadb = False
421 except Exception:
422 self.logger.info("You are not root or your system do not support xattr, using tdb backend for attributes. ")
423 finally:
424 file.close()
426 if eadb:
427 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.")
428 if ldap_backend_type == "existing":
429 if ldap_backend_forced_uri is not None:
430 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)
431 else:
432 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")
433 else:
434 if ldap_backend_forced_uri is not None:
435 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")
437 if domain_sid is not None:
438 domain_sid = security.dom_sid(domain_sid)
440 session = system_session()
441 try:
442 result = provision(self.logger,
443 session, smbconf=smbconf, targetdir=targetdir,
444 samdb_fill=samdb_fill, realm=realm, domain=domain,
445 domainguid=domain_guid, domainsid=domain_sid,
446 hostname=host_name,
447 hostip=host_ip, hostip6=host_ip6,
448 sitename=site, ntdsguid=ntds_guid,
449 invocationid=invocationid, adminpass=adminpass,
450 krbtgtpass=krbtgtpass, machinepass=machinepass,
451 dns_backend=dns_backend, dns_forwarder=dns_forwarder,
452 dnspass=dnspass, root=root, nobody=nobody,
453 users=users,
454 serverrole=server_role, dom_for_fun_level=dom_for_fun_level,
455 backend_type=ldap_backend_type,
456 ldapadminpass=ldapadminpass, ol_mmr_urls=ol_mmr_urls, slapd_path=slapd_path,
457 useeadb=eadb, next_rid=next_rid, lp=lp, use_ntvfs=use_ntvfs,
458 use_rfc2307=use_rfc2307, skip_sysvolacl=False,
459 ldap_backend_extra_port=ldap_backend_extra_port,
460 ldap_backend_forced_uri=ldap_backend_forced_uri,
461 nosync=ldap_backend_nosync, ldap_dryrun_mode=ldap_dryrun_mode)
463 except ProvisioningError, e:
464 raise CommandError("Provision failed", e)
466 result.report_logger(self.logger)
468 def _get_nameserver_ip(self):
469 """Grab the nameserver IP address from /etc/resolv.conf."""
470 from os import path
471 RESOLV_CONF="/etc/resolv.conf"
473 if not path.isfile(RESOLV_CONF):
474 self.logger.warning("Failed to locate %s" % RESOLV_CONF)
475 return None
477 handle = None
478 try:
479 handle = open(RESOLV_CONF, 'r')
480 for line in handle:
481 if not line.startswith('nameserver'):
482 continue
483 # we want the last non-space continuous string of the line
484 return line.strip().split()[-1]
485 finally:
486 if handle is not None:
487 handle.close()
489 self.logger.warning("No nameserver found in %s" % RESOLV_CONF)
492 class cmd_domain_dcpromo(Command):
493 """Promote an existing domain member or NT4 PDC to an AD DC."""
495 synopsis = "%prog <dnsdomain> [DC|RODC] [options]"
497 takes_optiongroups = {
498 "sambaopts": options.SambaOptions,
499 "versionopts": options.VersionOptions,
500 "credopts": options.CredentialsOptions,
503 takes_options = [
504 Option("--server", help="DC to join", type=str),
505 Option("--site", help="site to join", type=str),
506 Option("--targetdir", help="where to store provision", type=str),
507 Option("--domain-critical-only",
508 help="only replicate critical domain objects",
509 action="store_true"),
510 Option("--machinepass", type=str, metavar="PASSWORD",
511 help="choose machine password (otherwise random)"),
512 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
513 choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
514 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
515 "BIND9_DLZ uses samba4 AD to store zone information, "
516 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
517 default="SAMBA_INTERNAL"),
518 Option("--quiet", help="Be quiet", action="store_true"),
519 Option("--verbose", help="Be verbose", action="store_true")
522 ntvfs_options = [
523 Option("--use-ntvfs", action="store_true", help="Use NTVFS for the fileserver (default = no)"),
526 if samba.is_ntvfs_fileserver_built():
527 takes_options.extend(ntvfs_options)
530 takes_args = ["domain", "role?"]
532 def run(self, domain, role=None, sambaopts=None, credopts=None,
533 versionopts=None, server=None, site=None, targetdir=None,
534 domain_critical_only=False, parent_domain=None, machinepass=None,
535 use_ntvfs=False, dns_backend=None,
536 quiet=False, verbose=False):
537 lp = sambaopts.get_loadparm()
538 creds = credopts.get_credentials(lp)
539 net = Net(creds, lp, server=credopts.ipaddress)
541 if site is None:
542 site = "Default-First-Site-Name"
544 logger = self.get_logger()
545 if verbose:
546 logger.setLevel(logging.DEBUG)
547 elif quiet:
548 logger.setLevel(logging.WARNING)
549 else:
550 logger.setLevel(logging.INFO)
552 netbios_name = lp.get("netbios name")
554 if not role is None:
555 role = role.upper()
557 if role == "DC":
558 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
559 site=site, netbios_name=netbios_name, targetdir=targetdir,
560 domain_critical_only=domain_critical_only,
561 machinepass=machinepass, use_ntvfs=use_ntvfs,
562 dns_backend=dns_backend,
563 promote_existing=True)
564 elif role == "RODC":
565 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
566 site=site, netbios_name=netbios_name, targetdir=targetdir,
567 domain_critical_only=domain_critical_only,
568 machinepass=machinepass, use_ntvfs=use_ntvfs, dns_backend=dns_backend,
569 promote_existing=True)
570 else:
571 raise CommandError("Invalid role '%s' (possible values: DC, RODC)" % role)
574 class cmd_domain_join(Command):
575 """Join domain as either member or backup domain controller."""
577 synopsis = "%prog <dnsdomain> [DC|RODC|MEMBER|SUBDOMAIN] [options]"
579 takes_optiongroups = {
580 "sambaopts": options.SambaOptions,
581 "versionopts": options.VersionOptions,
582 "credopts": options.CredentialsOptions,
585 takes_options = [
586 Option("--server", help="DC to join", type=str),
587 Option("--site", help="site to join", type=str),
588 Option("--targetdir", help="where to store provision", type=str),
589 Option("--parent-domain", help="parent domain to create subdomain under", type=str),
590 Option("--domain-critical-only",
591 help="only replicate critical domain objects",
592 action="store_true"),
593 Option("--machinepass", type=str, metavar="PASSWORD",
594 help="choose machine password (otherwise random)"),
595 Option("--adminpass", type="string", metavar="PASSWORD",
596 help="choose adminstrator password when joining as a subdomain (otherwise random)"),
597 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
598 choices=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
599 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
600 "BIND9_DLZ uses samba4 AD to store zone information, "
601 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
602 default="SAMBA_INTERNAL"),
603 Option("--quiet", help="Be quiet", action="store_true"),
604 Option("--verbose", help="Be verbose", action="store_true")
607 ntvfs_options = [
608 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
609 action="store_true")
611 if samba.is_ntvfs_fileserver_built():
612 takes_options.extend(ntvfs_options)
614 takes_args = ["domain", "role?"]
616 def run(self, domain, role=None, sambaopts=None, credopts=None,
617 versionopts=None, server=None, site=None, targetdir=None,
618 domain_critical_only=False, parent_domain=None, machinepass=None,
619 use_ntvfs=False, dns_backend=None, adminpass=None,
620 quiet=False, verbose=False):
621 lp = sambaopts.get_loadparm()
622 creds = credopts.get_credentials(lp)
623 net = Net(creds, lp, server=credopts.ipaddress)
625 if site is None:
626 site = "Default-First-Site-Name"
628 logger = self.get_logger()
629 if verbose:
630 logger.setLevel(logging.DEBUG)
631 elif quiet:
632 logger.setLevel(logging.WARNING)
633 else:
634 logger.setLevel(logging.INFO)
636 netbios_name = lp.get("netbios name")
638 if not role is None:
639 role = role.upper()
641 if role is None or role == "MEMBER":
642 (join_password, sid, domain_name) = net.join_member(
643 domain, netbios_name, LIBNET_JOIN_AUTOMATIC,
644 machinepass=machinepass)
646 self.errf.write("Joined domain %s (%s)\n" % (domain_name, sid))
647 elif role == "DC":
648 join_DC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
649 site=site, netbios_name=netbios_name, targetdir=targetdir,
650 domain_critical_only=domain_critical_only,
651 machinepass=machinepass, use_ntvfs=use_ntvfs, dns_backend=dns_backend)
652 elif role == "RODC":
653 join_RODC(logger=logger, server=server, creds=creds, lp=lp, domain=domain,
654 site=site, netbios_name=netbios_name, targetdir=targetdir,
655 domain_critical_only=domain_critical_only,
656 machinepass=machinepass, use_ntvfs=use_ntvfs,
657 dns_backend=dns_backend)
658 elif role == "SUBDOMAIN":
659 if not adminpass:
660 logger.info("Administrator password will be set randomly!")
662 netbios_domain = lp.get("workgroup")
663 if parent_domain is None:
664 parent_domain = ".".join(domain.split(".")[1:])
665 join_subdomain(logger=logger, server=server, creds=creds, lp=lp, dnsdomain=domain,
666 parent_domain=parent_domain, site=site,
667 netbios_name=netbios_name, netbios_domain=netbios_domain,
668 targetdir=targetdir, machinepass=machinepass,
669 use_ntvfs=use_ntvfs, dns_backend=dns_backend,
670 adminpass=adminpass)
671 else:
672 raise CommandError("Invalid role '%s' (possible values: MEMBER, DC, RODC, SUBDOMAIN)" % role)
675 class cmd_domain_demote(Command):
676 """Demote ourselves from the role of Domain Controller."""
678 synopsis = "%prog [options]"
680 takes_options = [
681 Option("--server", help="writable DC to write demotion changes on", type=str),
682 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
683 metavar="URL", dest="H"),
684 Option("--remove-other-dead-server", help="Dead DC (name or NTDS GUID) "
685 "to remove ALL references to (rather than this DC)", type=str),
686 Option("--quiet", help="Be quiet", action="store_true"),
687 Option("--verbose", help="Be verbose", action="store_true"),
690 takes_optiongroups = {
691 "sambaopts": options.SambaOptions,
692 "credopts": options.CredentialsOptions,
693 "versionopts": options.VersionOptions,
696 def run(self, sambaopts=None, credopts=None,
697 versionopts=None, server=None,
698 remove_other_dead_server=None, H=None,
699 verbose=False, quiet=False):
700 lp = sambaopts.get_loadparm()
701 creds = credopts.get_credentials(lp)
702 net = Net(creds, lp, server=credopts.ipaddress)
704 logger = self.get_logger()
705 if verbose:
706 logger.setLevel(logging.DEBUG)
707 elif quiet:
708 logger.setLevel(logging.WARNING)
709 else:
710 logger.setLevel(logging.INFO)
712 if remove_other_dead_server is not None:
713 if server is not None:
714 samdb = SamDB(url="ldap://%s" % server,
715 session_info=system_session(),
716 credentials=creds, lp=lp)
717 else:
718 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
719 try:
720 remove_dc.remove_dc(samdb, logger, remove_other_dead_server)
721 except remove_dc.DemoteException as err:
722 raise CommandError("Demote failed: %s" % err)
723 return
725 netbios_name = lp.get("netbios name")
726 samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)
727 if not server:
728 res = samdb.search(expression='(&(objectClass=computer)(serverReferenceBL=*))', attrs=["dnsHostName", "name"])
729 if (len(res) == 0):
730 raise CommandError("Unable to search for servers")
732 if (len(res) == 1):
733 raise CommandError("You are the latest server in the domain")
735 server = None
736 for e in res:
737 if str(e["name"]).lower() != netbios_name.lower():
738 server = e["dnsHostName"]
739 break
741 ntds_guid = samdb.get_ntds_GUID()
742 msg = samdb.search(base=str(samdb.get_config_basedn()),
743 scope=ldb.SCOPE_SUBTREE, expression="(objectGUID=%s)" % ntds_guid,
744 attrs=['options'])
745 if len(msg) == 0 or "options" not in msg[0]:
746 raise CommandError("Failed to find options on %s" % ntds_guid)
748 ntds_dn = msg[0].dn
749 dsa_options = int(str(msg[0]['options']))
751 res = samdb.search(expression="(fSMORoleOwner=%s)" % str(ntds_dn),
752 controls=["search_options:1:2"])
754 if len(res) != 0:
755 raise CommandError("Current DC is still the owner of %d role(s), use the role command to transfer roles to another DC" % len(res))
757 self.errf.write("Using %s as partner server for the demotion\n" %
758 server)
759 (drsuapiBind, drsuapi_handle, supportedExtensions) = drsuapi_connect(server, lp, creds)
761 self.errf.write("Deactivating inbound replication\n")
763 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
764 nmsg = ldb.Message()
765 nmsg.dn = msg[0].dn
767 dsa_options |= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
768 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
769 samdb.modify(nmsg)
772 self.errf.write("Asking partner server %s to synchronize from us\n"
773 % server)
774 for part in (samdb.get_schema_basedn(),
775 samdb.get_config_basedn(),
776 samdb.get_root_basedn()):
777 nc = drsuapi.DsReplicaObjectIdentifier()
778 nc.dn = str(part)
780 req1 = drsuapi.DsReplicaSyncRequest1()
781 req1.naming_context = nc;
782 req1.options = drsuapi.DRSUAPI_DRS_WRIT_REP
783 req1.source_dsa_guid = misc.GUID(ntds_guid)
785 try:
786 drsuapiBind.DsReplicaSync(drsuapi_handle, 1, req1)
787 except RuntimeError as (werr, string):
788 if werr == 8452: #WERR_DS_DRA_NO_REPLICA
789 pass
790 else:
791 self.errf.write(
792 "Error while demoting, "
793 "re-enabling inbound replication\n")
794 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
795 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
796 samdb.modify(nmsg)
797 raise CommandError("Error while sending a DsReplicaSync for partion %s" % str(part), e)
798 try:
799 remote_samdb = SamDB(url="ldap://%s" % server,
800 session_info=system_session(),
801 credentials=creds, lp=lp)
803 self.errf.write("Changing userControl and container\n")
804 res = remote_samdb.search(base=str(remote_samdb.domain_dn()),
805 expression="(&(objectClass=user)(sAMAccountName=%s$))" %
806 netbios_name.upper(),
807 attrs=["userAccountControl"])
808 dc_dn = res[0].dn
809 uac = int(str(res[0]["userAccountControl"]))
811 except Exception, e:
812 self.errf.write(
813 "Error while demoting, re-enabling inbound replication\n")
814 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
815 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
816 samdb.modify(nmsg)
817 raise CommandError("Error while changing account control", e)
819 if (len(res) != 1):
820 self.errf.write(
821 "Error while demoting, re-enabling inbound replication")
822 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
823 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
824 samdb.modify(nmsg)
825 raise CommandError("Unable to find object with samaccountName = %s$"
826 " in the remote dc" % netbios_name.upper())
828 olduac = uac
830 uac &= ~(UF_SERVER_TRUST_ACCOUNT|UF_TRUSTED_FOR_DELEGATION|UF_PARTIAL_SECRETS_ACCOUNT)
831 uac |= UF_WORKSTATION_TRUST_ACCOUNT
833 msg = ldb.Message()
834 msg.dn = dc_dn
836 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
837 ldb.FLAG_MOD_REPLACE,
838 "userAccountControl")
839 try:
840 remote_samdb.modify(msg)
841 except Exception, e:
842 self.errf.write(
843 "Error while demoting, re-enabling inbound replication")
844 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
845 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
846 samdb.modify(nmsg)
848 raise CommandError("Error while changing account control", e)
850 parent = msg.dn.parent()
851 dc_name = res[0].dn.get_rdn_value()
852 rdn = "CN=%s" % dc_name
854 # Let's move to the Computer container
855 i = 0
856 newrdn = str(rdn)
858 computer_dn = ldb.Dn(remote_samdb, "CN=Computers,%s" % str(remote_samdb.domain_dn()))
859 res = remote_samdb.search(base=computer_dn, expression=rdn, scope=ldb.SCOPE_ONELEVEL)
861 if (len(res) != 0):
862 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
863 scope=ldb.SCOPE_ONELEVEL)
864 while(len(res) != 0 and i < 100):
865 i = i + 1
866 res = remote_samdb.search(base=computer_dn, expression="%s-%d" % (rdn, i),
867 scope=ldb.SCOPE_ONELEVEL)
869 if i == 100:
870 self.errf.write(
871 "Error while demoting, re-enabling inbound replication\n")
872 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
873 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
874 samdb.modify(nmsg)
876 msg = ldb.Message()
877 msg.dn = dc_dn
879 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
880 ldb.FLAG_MOD_REPLACE,
881 "userAccountControl")
883 remote_samdb.modify(msg)
885 raise CommandError("Unable to find a slot for renaming %s,"
886 " all names from %s-1 to %s-%d seemed used" %
887 (str(dc_dn), rdn, rdn, i - 9))
889 newrdn = "%s-%d" % (rdn, i)
891 try:
892 newdn = ldb.Dn(remote_samdb, "%s,%s" % (newrdn, str(computer_dn)))
893 remote_samdb.rename(dc_dn, newdn)
894 except Exception, e:
895 self.errf.write(
896 "Error while demoting, re-enabling inbound replication\n")
897 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
898 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
899 samdb.modify(nmsg)
901 msg = ldb.Message()
902 msg.dn = dc_dn
904 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
905 ldb.FLAG_MOD_REPLACE,
906 "userAccountControl")
908 remote_samdb.modify(msg)
909 raise CommandError("Error while renaming %s to %s" % (str(dc_dn), str(newdn)), e)
912 server_dsa_dn = samdb.get_serverName()
913 domain = remote_samdb.get_root_basedn()
915 try:
916 req1 = drsuapi.DsRemoveDSServerRequest1()
917 req1.server_dn = str(server_dsa_dn)
918 req1.domain_dn = str(domain)
919 req1.commit = 1
921 drsuapiBind.DsRemoveDSServer(drsuapi_handle, 1, req1)
922 except RuntimeError as (werr, string):
923 if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():
924 self.errf.write(
925 "Error while demoting, re-enabling inbound replication\n")
926 dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
927 nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
928 samdb.modify(nmsg)
930 msg = ldb.Message()
931 msg.dn = newdn
933 msg["userAccountControl"] = ldb.MessageElement("%d" % uac,
934 ldb.FLAG_MOD_REPLACE,
935 "userAccountControl")
936 remote_samdb.modify(msg)
937 remote_samdb.rename(newdn, dc_dn)
938 if werr == 8452: #WERR_DS_DRA_NO_REPLICA
939 raise CommandError("The DC %s is not present on (already removed from) the remote server: " % server_dsa_dn, e)
940 else:
941 raise CommandError("Error while sending a removeDsServer of %s: " % server_dsa_dn, e)
943 remove_dc.remove_sysvol_references(remote_samdb, logger, dc_name)
945 # These are objects under the computer account that should be deleted
946 for s in ("CN=Enterprise,CN=NTFRS Subscriptions",
947 "CN=%s, CN=NTFRS Subscriptions" % lp.get("realm"),
948 "CN=Domain system Volumes (SYSVOL Share), CN=NTFRS Subscriptions",
949 "CN=NTFRS Subscriptions"):
950 try:
951 remote_samdb.delete(ldb.Dn(remote_samdb,
952 "%s,%s" % (s, str(newdn))))
953 except ldb.LdbError, l:
954 pass
956 self.errf.write("Demote successful\n")
959 class cmd_domain_level(Command):
960 """Raise domain and forest function levels."""
962 synopsis = "%prog (show|raise <options>) [options]"
964 takes_optiongroups = {
965 "sambaopts": options.SambaOptions,
966 "credopts": options.CredentialsOptions,
967 "versionopts": options.VersionOptions,
970 takes_options = [
971 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
972 metavar="URL", dest="H"),
973 Option("--quiet", help="Be quiet", action="store_true"),
974 Option("--forest-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
975 help="The forest function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)"),
976 Option("--domain-level", type="choice", choices=["2003", "2008", "2008_R2", "2012", "2012_R2"],
977 help="The domain function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)")
980 takes_args = ["subcommand"]
982 def run(self, subcommand, H=None, forest_level=None, domain_level=None,
983 quiet=False, credopts=None, sambaopts=None, versionopts=None):
984 lp = sambaopts.get_loadparm()
985 creds = credopts.get_credentials(lp, fallback_machine=True)
987 samdb = SamDB(url=H, session_info=system_session(),
988 credentials=creds, lp=lp)
990 domain_dn = samdb.domain_dn()
992 res_forest = samdb.search("CN=Partitions,%s" % samdb.get_config_basedn(),
993 scope=ldb.SCOPE_BASE, attrs=["msDS-Behavior-Version"])
994 assert len(res_forest) == 1
996 res_domain = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
997 attrs=["msDS-Behavior-Version", "nTMixedDomain"])
998 assert len(res_domain) == 1
1000 res_dc_s = samdb.search("CN=Sites,%s" % samdb.get_config_basedn(),
1001 scope=ldb.SCOPE_SUBTREE, expression="(objectClass=nTDSDSA)",
1002 attrs=["msDS-Behavior-Version"])
1003 assert len(res_dc_s) >= 1
1005 # default values, since "msDS-Behavior-Version" does not exist on Windows 2000 AD
1006 level_forest = DS_DOMAIN_FUNCTION_2000
1007 level_domain = DS_DOMAIN_FUNCTION_2000
1009 if "msDS-Behavior-Version" in res_forest[0]:
1010 level_forest = int(res_forest[0]["msDS-Behavior-Version"][0])
1011 if "msDS-Behavior-Version" in res_domain[0]:
1012 level_domain = int(res_domain[0]["msDS-Behavior-Version"][0])
1013 level_domain_mixed = int(res_domain[0]["nTMixedDomain"][0])
1015 min_level_dc = None
1016 for msg in res_dc_s:
1017 if "msDS-Behavior-Version" in msg:
1018 if min_level_dc is None or int(msg["msDS-Behavior-Version"][0]) < min_level_dc:
1019 min_level_dc = int(msg["msDS-Behavior-Version"][0])
1020 else:
1021 min_level_dc = DS_DOMAIN_FUNCTION_2000
1022 # well, this is the least
1023 break
1025 if level_forest < DS_DOMAIN_FUNCTION_2000 or level_domain < DS_DOMAIN_FUNCTION_2000:
1026 raise CommandError("Domain and/or forest function level(s) is/are invalid. Correct them or reprovision!")
1027 if min_level_dc < DS_DOMAIN_FUNCTION_2000:
1028 raise CommandError("Lowest function level of a DC is invalid. Correct this or reprovision!")
1029 if level_forest > level_domain:
1030 raise CommandError("Forest function level is higher than the domain level(s). Correct this or reprovision!")
1031 if level_domain > min_level_dc:
1032 raise CommandError("Domain function level is higher than the lowest function level of a DC. Correct this or reprovision!")
1034 if subcommand == "show":
1035 self.message("Domain and forest function level for domain '%s'" % domain_dn)
1036 if level_forest == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1037 self.message("\nATTENTION: You run SAMBA 4 on a forest function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1038 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1039 self.message("\nATTENTION: You run SAMBA 4 on a domain function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1040 if min_level_dc == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1041 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)!")
1043 self.message("")
1045 if level_forest == DS_DOMAIN_FUNCTION_2000:
1046 outstr = "2000"
1047 elif level_forest == DS_DOMAIN_FUNCTION_2003_MIXED:
1048 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1049 elif level_forest == DS_DOMAIN_FUNCTION_2003:
1050 outstr = "2003"
1051 elif level_forest == DS_DOMAIN_FUNCTION_2008:
1052 outstr = "2008"
1053 elif level_forest == DS_DOMAIN_FUNCTION_2008_R2:
1054 outstr = "2008 R2"
1055 elif level_forest == DS_DOMAIN_FUNCTION_2012:
1056 outstr = "2012"
1057 elif level_forest == DS_DOMAIN_FUNCTION_2012_R2:
1058 outstr = "2012 R2"
1059 else:
1060 outstr = "higher than 2012 R2"
1061 self.message("Forest function level: (Windows) " + outstr)
1063 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
1064 outstr = "2000 mixed (NT4 DC support)"
1065 elif level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed == 0:
1066 outstr = "2000"
1067 elif level_domain == DS_DOMAIN_FUNCTION_2003_MIXED:
1068 outstr = "2003 with mixed domains/interim (NT4 DC support)"
1069 elif level_domain == DS_DOMAIN_FUNCTION_2003:
1070 outstr = "2003"
1071 elif level_domain == DS_DOMAIN_FUNCTION_2008:
1072 outstr = "2008"
1073 elif level_domain == DS_DOMAIN_FUNCTION_2008_R2:
1074 outstr = "2008 R2"
1075 elif level_domain == DS_DOMAIN_FUNCTION_2012:
1076 outstr = "2012"
1077 elif level_domain == DS_DOMAIN_FUNCTION_2012_R2:
1078 outstr = "2012 R2"
1079 else:
1080 outstr = "higher than 2012 R2"
1081 self.message("Domain function level: (Windows) " + outstr)
1083 if min_level_dc == DS_DOMAIN_FUNCTION_2000:
1084 outstr = "2000"
1085 elif min_level_dc == DS_DOMAIN_FUNCTION_2003:
1086 outstr = "2003"
1087 elif min_level_dc == DS_DOMAIN_FUNCTION_2008:
1088 outstr = "2008"
1089 elif min_level_dc == DS_DOMAIN_FUNCTION_2008_R2:
1090 outstr = "2008 R2"
1091 elif min_level_dc == DS_DOMAIN_FUNCTION_2012:
1092 outstr = "2012"
1093 elif min_level_dc == DS_DOMAIN_FUNCTION_2012_R2:
1094 outstr = "2012 R2"
1095 else:
1096 outstr = "higher than 2012 R2"
1097 self.message("Lowest function level of a DC: (Windows) " + outstr)
1099 elif subcommand == "raise":
1100 msgs = []
1102 if domain_level is not None:
1103 if domain_level == "2003":
1104 new_level_domain = DS_DOMAIN_FUNCTION_2003
1105 elif domain_level == "2008":
1106 new_level_domain = DS_DOMAIN_FUNCTION_2008
1107 elif domain_level == "2008_R2":
1108 new_level_domain = DS_DOMAIN_FUNCTION_2008_R2
1109 elif domain_level == "2012":
1110 new_level_domain = DS_DOMAIN_FUNCTION_2012
1111 elif domain_level == "2012_R2":
1112 new_level_domain = DS_DOMAIN_FUNCTION_2012_R2
1114 if new_level_domain <= level_domain and level_domain_mixed == 0:
1115 raise CommandError("Domain function level can't be smaller than or equal to the actual one!")
1116 if new_level_domain > min_level_dc:
1117 raise CommandError("Domain function level can't be higher than the lowest function level of a DC!")
1119 # Deactivate mixed/interim domain support
1120 if level_domain_mixed != 0:
1121 # Directly on the base DN
1122 m = ldb.Message()
1123 m.dn = ldb.Dn(samdb, domain_dn)
1124 m["nTMixedDomain"] = ldb.MessageElement("0",
1125 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1126 samdb.modify(m)
1127 # Under partitions
1128 m = ldb.Message()
1129 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup") + ",CN=Partitions,%s" % samdb.get_config_basedn())
1130 m["nTMixedDomain"] = ldb.MessageElement("0",
1131 ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
1132 try:
1133 samdb.modify(m)
1134 except ldb.LdbError, (enum, emsg):
1135 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1136 raise
1138 # Directly on the base DN
1139 m = ldb.Message()
1140 m.dn = ldb.Dn(samdb, domain_dn)
1141 m["msDS-Behavior-Version"]= ldb.MessageElement(
1142 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1143 "msDS-Behavior-Version")
1144 samdb.modify(m)
1145 # Under partitions
1146 m = ldb.Message()
1147 m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup")
1148 + ",CN=Partitions,%s" % samdb.get_config_basedn())
1149 m["msDS-Behavior-Version"]= ldb.MessageElement(
1150 str(new_level_domain), ldb.FLAG_MOD_REPLACE,
1151 "msDS-Behavior-Version")
1152 try:
1153 samdb.modify(m)
1154 except ldb.LdbError, (enum, emsg):
1155 if enum != ldb.ERR_UNWILLING_TO_PERFORM:
1156 raise
1158 level_domain = new_level_domain
1159 msgs.append("Domain function level changed!")
1161 if forest_level is not None:
1162 if forest_level == "2003":
1163 new_level_forest = DS_DOMAIN_FUNCTION_2003
1164 elif forest_level == "2008":
1165 new_level_forest = DS_DOMAIN_FUNCTION_2008
1166 elif forest_level == "2008_R2":
1167 new_level_forest = DS_DOMAIN_FUNCTION_2008_R2
1168 elif forest_level == "2012":
1169 new_level_forest = DS_DOMAIN_FUNCTION_2012
1170 elif forest_level == "2012_R2":
1171 new_level_forest = DS_DOMAIN_FUNCTION_2012_R2
1173 if new_level_forest <= level_forest:
1174 raise CommandError("Forest function level can't be smaller than or equal to the actual one!")
1175 if new_level_forest > level_domain:
1176 raise CommandError("Forest function level can't be higher than the domain function level(s). Please raise it/them first!")
1178 m = ldb.Message()
1179 m.dn = ldb.Dn(samdb, "CN=Partitions,%s" % samdb.get_config_basedn())
1180 m["msDS-Behavior-Version"]= ldb.MessageElement(
1181 str(new_level_forest), ldb.FLAG_MOD_REPLACE,
1182 "msDS-Behavior-Version")
1183 samdb.modify(m)
1184 msgs.append("Forest function level changed!")
1185 msgs.append("All changes applied successfully!")
1186 self.message("\n".join(msgs))
1187 else:
1188 raise CommandError("invalid argument: '%s' (choose from 'show', 'raise')" % subcommand)
1191 class cmd_domain_passwordsettings(Command):
1192 """Set password settings.
1194 Password complexity, password lockout policy, history length,
1195 minimum password length, the minimum and maximum password age) on
1196 a Samba AD DC server.
1198 Use against a Windows DC is possible, but group policy will override it.
1201 synopsis = "%prog (show|set <options>) [options]"
1203 takes_optiongroups = {
1204 "sambaopts": options.SambaOptions,
1205 "versionopts": options.VersionOptions,
1206 "credopts": options.CredentialsOptions,
1209 takes_options = [
1210 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1211 metavar="URL", dest="H"),
1212 Option("--quiet", help="Be quiet", action="store_true"),
1213 Option("--complexity", type="choice", choices=["on","off","default"],
1214 help="The password complexity (on | off | default). Default is 'on'"),
1215 Option("--store-plaintext", type="choice", choices=["on","off","default"],
1216 help="Store plaintext passwords where account have 'store passwords with reversible encryption' set (on | off | default). Default is 'off'"),
1217 Option("--history-length",
1218 help="The password history length (<integer> | default). Default is 24.", type=str),
1219 Option("--min-pwd-length",
1220 help="The minimum password length (<integer> | default). Default is 7.", type=str),
1221 Option("--min-pwd-age",
1222 help="The minimum password age (<integer in days> | default). Default is 1.", type=str),
1223 Option("--max-pwd-age",
1224 help="The maximum password age (<integer in days> | default). Default is 43.", type=str),
1225 Option("--account-lockout-duration",
1226 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),
1227 Option("--account-lockout-threshold",
1228 help="The number of bad password attempts allowed before locking out the account (<integer> | default). Default is 0 (never lock out).", type=str),
1229 Option("--reset-account-lockout-after",
1230 help="After this time is elapsed, the recorded number of attempts restarts from zero (<integer> | default). Default is 30.", type=str),
1233 takes_args = ["subcommand"]
1235 def run(self, subcommand, H=None, min_pwd_age=None, max_pwd_age=None,
1236 quiet=False, complexity=None, store_plaintext=None, history_length=None,
1237 min_pwd_length=None, account_lockout_duration=None, account_lockout_threshold=None,
1238 reset_account_lockout_after=None, credopts=None, sambaopts=None,
1239 versionopts=None):
1240 lp = sambaopts.get_loadparm()
1241 creds = credopts.get_credentials(lp)
1243 samdb = SamDB(url=H, session_info=system_session(),
1244 credentials=creds, lp=lp)
1246 domain_dn = samdb.domain_dn()
1247 res = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
1248 attrs=["pwdProperties", "pwdHistoryLength", "minPwdLength",
1249 "minPwdAge", "maxPwdAge", "lockoutDuration", "lockoutThreshold",
1250 "lockOutObservationWindow"])
1251 assert(len(res) == 1)
1252 try:
1253 pwd_props = int(res[0]["pwdProperties"][0])
1254 pwd_hist_len = int(res[0]["pwdHistoryLength"][0])
1255 cur_min_pwd_len = int(res[0]["minPwdLength"][0])
1256 # ticks -> days
1257 cur_min_pwd_age = int(abs(int(res[0]["minPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1258 if int(res[0]["maxPwdAge"][0]) == -0x8000000000000000:
1259 cur_max_pwd_age = 0
1260 else:
1261 cur_max_pwd_age = int(abs(int(res[0]["maxPwdAge"][0])) / (1e7 * 60 * 60 * 24))
1262 cur_account_lockout_threshold = int(res[0]["lockoutThreshold"][0])
1263 # ticks -> mins
1264 if int(res[0]["lockoutDuration"][0]) == -0x8000000000000000:
1265 cur_account_lockout_duration = 0
1266 else:
1267 cur_account_lockout_duration = abs(int(res[0]["lockoutDuration"][0])) / (1e7 * 60)
1268 cur_reset_account_lockout_after = abs(int(res[0]["lockOutObservationWindow"][0])) / (1e7 * 60)
1269 except Exception, e:
1270 raise CommandError("Could not retrieve password properties!", e)
1272 if subcommand == "show":
1273 self.message("Password informations for domain '%s'" % domain_dn)
1274 self.message("")
1275 if pwd_props & DOMAIN_PASSWORD_COMPLEX != 0:
1276 self.message("Password complexity: on")
1277 else:
1278 self.message("Password complexity: off")
1279 if pwd_props & DOMAIN_PASSWORD_STORE_CLEARTEXT != 0:
1280 self.message("Store plaintext passwords: on")
1281 else:
1282 self.message("Store plaintext passwords: off")
1283 self.message("Password history length: %d" % pwd_hist_len)
1284 self.message("Minimum password length: %d" % cur_min_pwd_len)
1285 self.message("Minimum password age (days): %d" % cur_min_pwd_age)
1286 self.message("Maximum password age (days): %d" % cur_max_pwd_age)
1287 self.message("Account lockout duration (mins): %d" % cur_account_lockout_duration)
1288 self.message("Account lockout threshold (attempts): %d" % cur_account_lockout_threshold)
1289 self.message("Reset account lockout after (mins): %d" % cur_reset_account_lockout_after)
1290 elif subcommand == "set":
1291 msgs = []
1292 m = ldb.Message()
1293 m.dn = ldb.Dn(samdb, domain_dn)
1295 if complexity is not None:
1296 if complexity == "on" or complexity == "default":
1297 pwd_props = pwd_props | DOMAIN_PASSWORD_COMPLEX
1298 msgs.append("Password complexity activated!")
1299 elif complexity == "off":
1300 pwd_props = pwd_props & (~DOMAIN_PASSWORD_COMPLEX)
1301 msgs.append("Password complexity deactivated!")
1303 if store_plaintext is not None:
1304 if store_plaintext == "on" or store_plaintext == "default":
1305 pwd_props = pwd_props | DOMAIN_PASSWORD_STORE_CLEARTEXT
1306 msgs.append("Plaintext password storage for changed passwords activated!")
1307 elif store_plaintext == "off":
1308 pwd_props = pwd_props & (~DOMAIN_PASSWORD_STORE_CLEARTEXT)
1309 msgs.append("Plaintext password storage for changed passwords deactivated!")
1311 if complexity is not None or store_plaintext is not None:
1312 m["pwdProperties"] = ldb.MessageElement(str(pwd_props),
1313 ldb.FLAG_MOD_REPLACE, "pwdProperties")
1315 if history_length is not None:
1316 if history_length == "default":
1317 pwd_hist_len = 24
1318 else:
1319 pwd_hist_len = int(history_length)
1321 if pwd_hist_len < 0 or pwd_hist_len > 24:
1322 raise CommandError("Password history length must be in the range of 0 to 24!")
1324 m["pwdHistoryLength"] = ldb.MessageElement(str(pwd_hist_len),
1325 ldb.FLAG_MOD_REPLACE, "pwdHistoryLength")
1326 msgs.append("Password history length changed!")
1328 if min_pwd_length is not None:
1329 if min_pwd_length == "default":
1330 min_pwd_len = 7
1331 else:
1332 min_pwd_len = int(min_pwd_length)
1334 if min_pwd_len < 0 or min_pwd_len > 14:
1335 raise CommandError("Minimum password length must be in the range of 0 to 14!")
1337 m["minPwdLength"] = ldb.MessageElement(str(min_pwd_len),
1338 ldb.FLAG_MOD_REPLACE, "minPwdLength")
1339 msgs.append("Minimum password length changed!")
1341 if min_pwd_age is not None:
1342 if min_pwd_age == "default":
1343 min_pwd_age = 1
1344 else:
1345 min_pwd_age = int(min_pwd_age)
1347 if min_pwd_age < 0 or min_pwd_age > 998:
1348 raise CommandError("Minimum password age must be in the range of 0 to 998!")
1350 # days -> ticks
1351 min_pwd_age_ticks = -int(min_pwd_age * (24 * 60 * 60 * 1e7))
1353 m["minPwdAge"] = ldb.MessageElement(str(min_pwd_age_ticks),
1354 ldb.FLAG_MOD_REPLACE, "minPwdAge")
1355 msgs.append("Minimum password age changed!")
1357 if max_pwd_age is not None:
1358 if max_pwd_age == "default":
1359 max_pwd_age = 43
1360 else:
1361 max_pwd_age = int(max_pwd_age)
1363 if max_pwd_age < 0 or max_pwd_age > 999:
1364 raise CommandError("Maximum password age must be in the range of 0 to 999!")
1366 # days -> ticks
1367 if max_pwd_age == 0:
1368 max_pwd_age_ticks = -0x8000000000000000
1369 else:
1370 max_pwd_age_ticks = -int(max_pwd_age * (24 * 60 * 60 * 1e7))
1372 m["maxPwdAge"] = ldb.MessageElement(str(max_pwd_age_ticks),
1373 ldb.FLAG_MOD_REPLACE, "maxPwdAge")
1374 msgs.append("Maximum password age changed!")
1376 if account_lockout_duration is not None:
1377 if account_lockout_duration == "default":
1378 account_lockout_duration = 30
1379 else:
1380 account_lockout_duration = int(account_lockout_duration)
1382 if account_lockout_duration < 0 or account_lockout_duration > 99999:
1383 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1385 # days -> ticks
1386 if account_lockout_duration == 0:
1387 account_lockout_duration_ticks = -0x8000000000000000
1388 else:
1389 account_lockout_duration_ticks = -int(account_lockout_duration * (60 * 1e7))
1391 m["lockoutDuration"] = ldb.MessageElement(str(account_lockout_duration_ticks),
1392 ldb.FLAG_MOD_REPLACE, "lockoutDuration")
1393 msgs.append("Account lockout duration changed!")
1395 if account_lockout_threshold is not None:
1396 if account_lockout_threshold == "default":
1397 account_lockout_threshold = 0
1398 else:
1399 account_lockout_threshold = int(account_lockout_threshold)
1401 m["lockoutThreshold"] = ldb.MessageElement(str(account_lockout_threshold),
1402 ldb.FLAG_MOD_REPLACE, "lockoutThreshold")
1403 msgs.append("Account lockout threshold changed!")
1405 if reset_account_lockout_after is not None:
1406 if reset_account_lockout_after == "default":
1407 reset_account_lockout_after = 30
1408 else:
1409 reset_account_lockout_after = int(reset_account_lockout_after)
1411 if reset_account_lockout_after < 0 or reset_account_lockout_after > 99999:
1412 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1414 # days -> ticks
1415 if reset_account_lockout_after == 0:
1416 reset_account_lockout_after_ticks = -0x8000000000000000
1417 else:
1418 reset_account_lockout_after_ticks = -int(reset_account_lockout_after * (60 * 1e7))
1420 m["lockOutObservationWindow"] = ldb.MessageElement(str(reset_account_lockout_after_ticks),
1421 ldb.FLAG_MOD_REPLACE, "lockOutObservationWindow")
1422 msgs.append("Duration to reset account lockout after changed!")
1424 if max_pwd_age > 0 and min_pwd_age >= max_pwd_age:
1425 raise CommandError("Maximum password age (%d) must be greater than minimum password age (%d)!" % (max_pwd_age, min_pwd_age))
1427 if len(m) == 0:
1428 raise CommandError("You must specify at least one option to set. Try --help")
1429 samdb.modify(m)
1430 msgs.append("All changes applied successfully!")
1431 self.message("\n".join(msgs))
1432 else:
1433 raise CommandError("Wrong argument '%s'!" % subcommand)
1436 class cmd_domain_classicupgrade(Command):
1437 """Upgrade from Samba classic (NT4-like) database to Samba AD DC database.
1439 Specify either a directory with all Samba classic DC databases and state files (with --dbdir) or
1440 the testparm utility from your classic installation (with --testparm).
1443 synopsis = "%prog [options] <classic_smb_conf>"
1445 takes_optiongroups = {
1446 "sambaopts": options.SambaOptions,
1447 "versionopts": options.VersionOptions
1450 takes_options = [
1451 Option("--dbdir", type="string", metavar="DIR",
1452 help="Path to samba classic DC database directory"),
1453 Option("--testparm", type="string", metavar="PATH",
1454 help="Path to samba classic DC testparm utility from the previous installation. This allows the default paths of the previous installation to be followed"),
1455 Option("--targetdir", type="string", metavar="DIR",
1456 help="Path prefix where the new Samba 4.0 AD domain should be initialised"),
1457 Option("--quiet", help="Be quiet", action="store_true"),
1458 Option("--verbose", help="Be verbose", action="store_true"),
1459 Option("--use-xattrs", type="choice", choices=["yes","no","auto"], metavar="[yes|no|auto]",
1460 help="Define if we should use the native fs capabilities or a tdb file for storing attributes likes ntacl, auto tries to make an inteligent guess based on the user rights and system capabilities", default="auto"),
1461 Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
1462 choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
1463 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
1464 "BIND9_FLATFILE uses bind9 text database to store zone information, "
1465 "BIND9_DLZ uses samba4 AD to store zone information, "
1466 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
1467 default="SAMBA_INTERNAL")
1470 ntvfs_options = [
1471 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
1472 action="store_true")
1474 if samba.is_ntvfs_fileserver_built():
1475 takes_options.extend(ntvfs_options)
1477 takes_args = ["smbconf"]
1479 def run(self, smbconf=None, targetdir=None, dbdir=None, testparm=None,
1480 quiet=False, verbose=False, use_xattrs=None, sambaopts=None, versionopts=None,
1481 dns_backend=None, use_ntvfs=False):
1483 if not os.path.exists(smbconf):
1484 raise CommandError("File %s does not exist" % smbconf)
1486 if testparm and not os.path.exists(testparm):
1487 raise CommandError("Testparm utility %s does not exist" % testparm)
1489 if dbdir and not os.path.exists(dbdir):
1490 raise CommandError("Directory %s does not exist" % dbdir)
1492 if not dbdir and not testparm:
1493 raise CommandError("Please specify either dbdir or testparm")
1495 logger = self.get_logger()
1496 if verbose:
1497 logger.setLevel(logging.DEBUG)
1498 elif quiet:
1499 logger.setLevel(logging.WARNING)
1500 else:
1501 logger.setLevel(logging.INFO)
1503 if dbdir and testparm:
1504 logger.warning("both dbdir and testparm specified, ignoring dbdir.")
1505 dbdir = None
1507 lp = sambaopts.get_loadparm()
1509 s3conf = s3param.get_context()
1511 if sambaopts.realm:
1512 s3conf.set("realm", sambaopts.realm)
1514 if targetdir is not None:
1515 if not os.path.isdir(targetdir):
1516 os.mkdir(targetdir)
1518 eadb = True
1519 if use_xattrs == "yes":
1520 eadb = False
1521 elif use_xattrs == "auto" and not s3conf.get("posix:eadb"):
1522 if targetdir:
1523 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
1524 else:
1525 tmpfile = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
1526 try:
1527 try:
1528 samba.ntacls.setntacl(lp, tmpfile.name,
1529 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
1530 eadb = False
1531 except Exception:
1532 # FIXME: Don't catch all exceptions here
1533 logger.info("You are not root or your system do not support xattr, using tdb backend for attributes. "
1534 "If you intend to use this provision in production, rerun the script as root on a system supporting xattrs.")
1535 finally:
1536 tmpfile.close()
1538 # Set correct default values from dbdir or testparm
1539 paths = {}
1540 if dbdir:
1541 paths["state directory"] = dbdir
1542 paths["private dir"] = dbdir
1543 paths["lock directory"] = dbdir
1544 paths["smb passwd file"] = dbdir + "/smbpasswd"
1545 else:
1546 paths["state directory"] = get_testparm_var(testparm, smbconf, "state directory")
1547 paths["private dir"] = get_testparm_var(testparm, smbconf, "private dir")
1548 paths["smb passwd file"] = get_testparm_var(testparm, smbconf, "smb passwd file")
1549 paths["lock directory"] = get_testparm_var(testparm, smbconf, "lock directory")
1550 # "testparm" from Samba 3 < 3.4.x is not aware of the parameter
1551 # "state directory", instead make use of "lock directory"
1552 if len(paths["state directory"]) == 0:
1553 paths["state directory"] = paths["lock directory"]
1555 for p in paths:
1556 s3conf.set(p, paths[p])
1558 # load smb.conf parameters
1559 logger.info("Reading smb.conf")
1560 s3conf.load(smbconf)
1561 samba3 = Samba3(smbconf, s3conf)
1563 logger.info("Provisioning")
1564 upgrade_from_samba3(samba3, logger, targetdir, session_info=system_session(),
1565 useeadb=eadb, dns_backend=dns_backend, use_ntvfs=use_ntvfs)
1568 class cmd_domain_samba3upgrade(cmd_domain_classicupgrade):
1569 __doc__ = cmd_domain_classicupgrade.__doc__
1571 # This command is present for backwards compatibility only,
1572 # and should not be shown.
1574 hidden = True
1576 class LocalDCCredentialsOptions(options.CredentialsOptions):
1577 def __init__(self, parser):
1578 options.CredentialsOptions.__init__(self, parser, special_name="local-dc")
1580 class DomainTrustCommand(Command):
1581 """List domain trusts."""
1583 def __init__(self):
1584 Command.__init__(self)
1585 self.local_lp = None
1587 self.local_server = None
1588 self.local_binding_string = None
1589 self.local_creds = None
1591 self.remote_server = None
1592 self.remote_binding_string = None
1593 self.remote_creds = None
1595 WERR_OK = 0x00000000
1596 WERR_INVALID_FUNCTION = 0x00000001
1597 WERR_NERR_ACFNOTLOADED = 0x000008B3
1599 NT_STATUS_NOT_FOUND = 0xC0000225
1600 NT_STATUS_OBJECT_NAME_NOT_FOUND = 0xC0000034
1601 NT_STATUS_INVALID_PARAMETER = 0xC000000D
1602 NT_STATUS_INVALID_INFO_CLASS = 0xC0000003
1603 NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE = 0xC002002E
1605 def _uint32(self, v):
1606 return ctypes.c_uint32(v).value
1608 def check_runtime_error(self, runtime, val):
1609 if runtime is None:
1610 return False
1612 err32 = self._uint32(runtime[0])
1613 if err32 == val:
1614 return True
1616 return False
1618 class LocalRuntimeError(CommandError):
1619 def __init__(exception_self, self, runtime, message):
1620 err32 = self._uint32(runtime[0])
1621 errstr = runtime[1]
1622 msg = "LOCAL_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1623 self.local_server, message, err32, errstr)
1624 CommandError.__init__(exception_self, msg)
1626 class RemoteRuntimeError(CommandError):
1627 def __init__(exception_self, self, runtime, message):
1628 err32 = self._uint32(runtime[0])
1629 errstr = runtime[1]
1630 msg = "REMOTE_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1631 self.remote_server, message, err32, errstr)
1632 CommandError.__init__(exception_self, msg)
1634 class LocalLdbError(CommandError):
1635 def __init__(exception_self, self, ldb_error, message):
1636 errval = ldb_error[0]
1637 errstr = ldb_error[1]
1638 msg = "LOCAL_DC[%s]: %s - ERROR(%d) - %s" % (
1639 self.local_server, message, errval, errstr)
1640 CommandError.__init__(exception_self, msg)
1642 def setup_local_server(self, sambaopts, localdcopts):
1643 if self.local_server is not None:
1644 return self.local_server
1646 lp = sambaopts.get_loadparm()
1648 local_server = localdcopts.ipaddress
1649 if local_server is None:
1650 server_role = lp.server_role()
1651 if server_role != "ROLE_ACTIVE_DIRECTORY_DC":
1652 raise CommandError("Invalid server_role %s" % (server_role))
1653 local_server = lp.get('netbios name')
1654 local_transport = "ncalrpc"
1655 local_binding_options = ""
1656 local_binding_options += ",auth_type=ncalrpc_as_system"
1657 local_ldap_url = None
1658 local_creds = None
1659 else:
1660 local_transport = "ncacn_np"
1661 local_binding_options = ""
1662 local_ldap_url = "ldap://%s" % local_server
1663 local_creds = localdcopts.get_credentials(lp)
1665 self.local_lp = lp
1667 self.local_server = local_server
1668 self.local_binding_string = "%s:%s[%s]" % (local_transport, local_server, local_binding_options)
1669 self.local_ldap_url = local_ldap_url
1670 self.local_creds = local_creds
1671 return self.local_server
1673 def new_local_lsa_connection(self):
1674 return lsa.lsarpc(self.local_binding_string, self.local_lp, self.local_creds)
1676 def new_local_netlogon_connection(self):
1677 return netlogon.netlogon(self.local_binding_string, self.local_lp, self.local_creds)
1679 def new_local_ldap_connection(self):
1680 return SamDB(url=self.local_ldap_url,
1681 session_info=system_session(),
1682 credentials=self.local_creds,
1683 lp=self.local_lp)
1685 def setup_remote_server(self, credopts, domain,
1686 require_pdc=True,
1687 require_writable=True):
1689 if require_pdc:
1690 assert require_writable
1692 if self.remote_server is not None:
1693 return self.remote_server
1695 self.remote_server = "__unknown__remote_server__.%s" % domain
1696 assert self.local_server is not None
1698 remote_creds = credopts.get_credentials(self.local_lp)
1699 remote_server = credopts.ipaddress
1700 remote_binding_options = ""
1702 # TODO: we should also support NT4 domains
1703 # we could use local_netlogon.netr_DsRGetDCNameEx2() with the remote domain name
1704 # and delegate NBT or CLDAP to the local netlogon server
1705 try:
1706 remote_net = Net(remote_creds, self.local_lp, server=remote_server)
1707 remote_flags = nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS
1708 if require_writable:
1709 remote_flags |= nbt.NBT_SERVER_WRITABLE
1710 if require_pdc:
1711 remote_flags |= nbt.NBT_SERVER_PDC
1712 remote_info = remote_net.finddc(flags=remote_flags, domain=domain, address=remote_server)
1713 except Exception:
1714 raise CommandError("Failed to find a writeable DC for domain '%s'" % domain)
1715 flag_map = {
1716 nbt.NBT_SERVER_PDC: "PDC",
1717 nbt.NBT_SERVER_GC: "GC",
1718 nbt.NBT_SERVER_LDAP: "LDAP",
1719 nbt.NBT_SERVER_DS: "DS",
1720 nbt.NBT_SERVER_KDC: "KDC",
1721 nbt.NBT_SERVER_TIMESERV: "TIMESERV",
1722 nbt.NBT_SERVER_CLOSEST: "CLOSEST",
1723 nbt.NBT_SERVER_WRITABLE: "WRITABLE",
1724 nbt.NBT_SERVER_GOOD_TIMESERV: "GOOD_TIMESERV",
1725 nbt.NBT_SERVER_NDNC: "NDNC",
1726 nbt.NBT_SERVER_SELECT_SECRET_DOMAIN_6: "SELECT_SECRET_DOMAIN_6",
1727 nbt.NBT_SERVER_FULL_SECRET_DOMAIN_6: "FULL_SECRET_DOMAIN_6",
1728 nbt.NBT_SERVER_ADS_WEB_SERVICE: "ADS_WEB_SERVICE",
1729 nbt.NBT_SERVER_DS_8: "DS_8",
1730 nbt.NBT_SERVER_HAS_DNS_NAME: "HAS_DNS_NAME",
1731 nbt.NBT_SERVER_IS_DEFAULT_NC: "IS_DEFAULT_NC",
1732 nbt.NBT_SERVER_FOREST_ROOT: "FOREST_ROOT",
1734 server_type_string = self.generic_bitmap_to_string(flag_map,
1735 remote_info.server_type, names_only=True)
1736 self.outf.write("RemoteDC Netbios[%s] DNS[%s] ServerType[%s]\n" % (
1737 remote_info.pdc_name,
1738 remote_info.pdc_dns_name,
1739 server_type_string))
1741 self.remote_server = remote_info.pdc_dns_name
1742 self.remote_binding_string="ncacn_np:%s[%s]" % (self.remote_server, remote_binding_options)
1743 self.remote_creds = remote_creds
1744 return self.remote_server
1746 def new_remote_lsa_connection(self):
1747 return lsa.lsarpc(self.remote_binding_string, self.local_lp, self.remote_creds)
1749 def new_remote_netlogon_connection(self):
1750 return netlogon.netlogon(self.remote_binding_string, self.local_lp, self.remote_creds)
1752 def get_lsa_info(self, conn, policy_access):
1753 objectAttr = lsa.ObjectAttribute()
1754 objectAttr.sec_qos = lsa.QosInfo()
1756 policy = conn.OpenPolicy2(''.decode('utf-8'),
1757 objectAttr, policy_access)
1759 info = conn.QueryInfoPolicy2(policy, lsa.LSA_POLICY_INFO_DNS)
1761 return (policy, info)
1763 def get_netlogon_dc_info(self, conn, server):
1764 info = conn.netr_DsRGetDCNameEx2(server,
1765 None, 0, None, None, None,
1766 netlogon.DS_RETURN_DNS_NAME)
1767 return info
1769 def netr_DomainTrust_to_name(self, t):
1770 if t.trust_type == lsa.LSA_TRUST_TYPE_DOWNLEVEL:
1771 return t.netbios_name
1773 return t.dns_name
1775 def netr_DomainTrust_to_type(self, a, t):
1776 primary = None
1777 primary_parent = None
1778 for _t in a:
1779 if _t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
1780 primary = _t
1781 if not _t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1782 primary_parent = a[_t.parent_index]
1783 break
1785 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1786 if t is primary_parent:
1787 return "Parent"
1789 if t.trust_flags & netlogon.NETR_TRUST_FLAG_TREEROOT:
1790 return "TreeRoot"
1792 parent = a[t.parent_index]
1793 if parent is primary:
1794 return "Child"
1796 return "Shortcut"
1798 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1799 return "Forest"
1801 return "External"
1803 def netr_DomainTrust_to_transitive(self, t):
1804 if t.trust_flags & netlogon.NETR_TRUST_FLAG_IN_FOREST:
1805 return "Yes"
1807 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE:
1808 return "No"
1810 if t.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
1811 return "Yes"
1813 return "No"
1815 def netr_DomainTrust_to_direction(self, t):
1816 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND and \
1817 t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1818 return "BOTH"
1820 if t.trust_flags & netlogon.NETR_TRUST_FLAG_INBOUND:
1821 return "INCOMING"
1823 if t.trust_flags & netlogon.NETR_TRUST_FLAG_OUTBOUND:
1824 return "OUTGOING"
1826 return "INVALID"
1828 def generic_enum_to_string(self, e_dict, v, names_only=False):
1829 try:
1830 w = e_dict[v]
1831 except KeyError:
1832 v32 = self._uint32(v)
1833 w = "__unknown__%08X__" % v32
1835 r = "0x%x (%s)" % (v, w)
1836 return r;
1838 def generic_bitmap_to_string(self, b_dict, v, names_only=False):
1840 s = []
1842 c = v
1843 for b in sorted(b_dict.keys()):
1844 if not (c & b):
1845 continue
1846 c &= ~b
1847 s += [b_dict[b]]
1849 if c != 0:
1850 c32 = self._uint32(c)
1851 s += ["__unknown_%08X__" % c32]
1853 w = ",".join(s)
1854 if names_only:
1855 return w
1856 r = "0x%x (%s)" % (v, w)
1857 return r;
1859 def trustType_string(self, v):
1860 types = {
1861 lsa.LSA_TRUST_TYPE_DOWNLEVEL : "DOWNLEVEL",
1862 lsa.LSA_TRUST_TYPE_UPLEVEL : "UPLEVEL",
1863 lsa.LSA_TRUST_TYPE_MIT : "MIT",
1864 lsa.LSA_TRUST_TYPE_DCE : "DCE",
1866 return self.generic_enum_to_string(types, v)
1868 def trustDirection_string(self, v):
1869 directions = {
1870 lsa.LSA_TRUST_DIRECTION_INBOUND |
1871 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "BOTH",
1872 lsa.LSA_TRUST_DIRECTION_INBOUND : "INBOUND",
1873 lsa.LSA_TRUST_DIRECTION_OUTBOUND : "OUTBOUND",
1875 return self.generic_enum_to_string(directions, v)
1877 def trustAttributes_string(self, v):
1878 attributes = {
1879 lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE : "NON_TRANSITIVE",
1880 lsa.LSA_TRUST_ATTRIBUTE_UPLEVEL_ONLY : "UPLEVEL_ONLY",
1881 lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN : "QUARANTINED_DOMAIN",
1882 lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE : "FOREST_TRANSITIVE",
1883 lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION : "CROSS_ORGANIZATION",
1884 lsa.LSA_TRUST_ATTRIBUTE_WITHIN_FOREST : "WITHIN_FOREST",
1885 lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL : "TREAT_AS_EXTERNAL",
1886 lsa.LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION : "USES_RC4_ENCRYPTION",
1888 return self.generic_bitmap_to_string(attributes, v)
1890 def kerb_EncTypes_string(self, v):
1891 enctypes = {
1892 security.KERB_ENCTYPE_DES_CBC_CRC : "DES_CBC_CRC",
1893 security.KERB_ENCTYPE_DES_CBC_MD5 : "DES_CBC_MD5",
1894 security.KERB_ENCTYPE_RC4_HMAC_MD5 : "RC4_HMAC_MD5",
1895 security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96 : "AES128_CTS_HMAC_SHA1_96",
1896 security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96 : "AES256_CTS_HMAC_SHA1_96",
1897 security.KERB_ENCTYPE_FAST_SUPPORTED : "FAST_SUPPORTED",
1898 security.KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED : "COMPOUND_IDENTITY_SUPPORTED",
1899 security.KERB_ENCTYPE_CLAIMS_SUPPORTED : "CLAIMS_SUPPORTED",
1900 security.KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED : "RESOURCE_SID_COMPRESSION_DISABLED",
1902 return self.generic_bitmap_to_string(enctypes, v)
1904 def entry_tln_status(self, e_flags, ):
1905 if e_flags == 0:
1906 return "Status[Enabled]"
1908 flags = {
1909 lsa.LSA_TLN_DISABLED_NEW : "Disabled-New",
1910 lsa.LSA_TLN_DISABLED_ADMIN : "Disabled",
1911 lsa.LSA_TLN_DISABLED_CONFLICT : "Disabled-Conflicting",
1913 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
1915 def entry_dom_status(self, e_flags):
1916 if e_flags == 0:
1917 return "Status[Enabled]"
1919 flags = {
1920 lsa.LSA_SID_DISABLED_ADMIN : "Disabled-SID",
1921 lsa.LSA_SID_DISABLED_CONFLICT : "Disabled-SID-Conflicting",
1922 lsa.LSA_NB_DISABLED_ADMIN : "Disabled-NB",
1923 lsa.LSA_NB_DISABLED_CONFLICT : "Disabled-NB-Conflicting",
1925 return "Status[%s]" % self.generic_bitmap_to_string(flags, e_flags, names_only=True)
1927 def write_forest_trust_info(self, fti, tln=None, collisions=None):
1928 if tln is not None:
1929 tln_string = " TDO[%s]" % tln
1930 else:
1931 tln_string = ""
1933 self.outf.write("Namespaces[%d]%s:\n" % (
1934 len(fti.entries), tln_string))
1936 for i in xrange(0, len(fti.entries)):
1937 e = fti.entries[i]
1939 flags = e.flags
1940 collision_string = ""
1942 if collisions is not None:
1943 for c in collisions.entries:
1944 if c.index != i:
1945 continue
1946 flags = c.flags
1947 collision_string = " Collision[%s]" % (c.name.string)
1949 d = e.forest_trust_data
1950 if e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
1951 self.outf.write("TLN: %-32s DNS[*.%s]%s\n" % (
1952 self.entry_tln_status(flags),
1953 d.string, collision_string))
1954 elif e.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
1955 self.outf.write("TLN_EX: %-29s DNS[*.%s]\n" % (
1956 "", d.string))
1957 elif e.type == lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
1958 self.outf.write("DOM: %-32s DNS[%s] Netbios[%s] SID[%s]%s\n" % (
1959 self.entry_dom_status(flags),
1960 d.dns_domain_name.string,
1961 d.netbios_domain_name.string,
1962 d.domain_sid, collision_string))
1963 return
1965 class cmd_domain_trust_list(DomainTrustCommand):
1966 """List domain trusts."""
1968 synopsis = "%prog [options]"
1970 takes_optiongroups = {
1971 "sambaopts": options.SambaOptions,
1972 "versionopts": options.VersionOptions,
1973 "localdcopts": LocalDCCredentialsOptions,
1976 takes_options = [
1979 def run(self, sambaopts=None, versionopts=None, localdcopts=None):
1981 local_server = self.setup_local_server(sambaopts, localdcopts)
1982 try:
1983 local_netlogon = self.new_local_netlogon_connection()
1984 except RuntimeError as error:
1985 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
1987 try:
1988 local_netlogon_trusts = local_netlogon.netr_DsrEnumerateDomainTrusts(local_server,
1989 netlogon.NETR_TRUST_FLAG_IN_FOREST |
1990 netlogon.NETR_TRUST_FLAG_OUTBOUND |
1991 netlogon.NETR_TRUST_FLAG_INBOUND)
1992 except RuntimeError as error:
1993 if self.check_runtime_error(error, self.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
1994 # TODO: we could implement a fallback to lsa.EnumTrustDom()
1995 raise CommandError("LOCAL_DC[%s]: netr_DsrEnumerateDomainTrusts not supported." % (
1996 self.local_server))
1997 raise self.LocalRuntimeError(self, error, "netr_DsrEnumerateDomainTrusts failed")
1999 a = local_netlogon_trusts.array
2000 for t in a:
2001 if t.trust_flags & netlogon.NETR_TRUST_FLAG_PRIMARY:
2002 continue
2003 self.outf.write("%-14s %-15s %-19s %s\n" % (
2004 "Type[%s]" % self.netr_DomainTrust_to_type(a, t),
2005 "Transitive[%s]" % self.netr_DomainTrust_to_transitive(t),
2006 "Direction[%s]" % self.netr_DomainTrust_to_direction(t),
2007 "Name[%s]" % self.netr_DomainTrust_to_name(t)))
2008 return
2010 class cmd_domain_trust_show(DomainTrustCommand):
2011 """Show trusted domain details."""
2013 synopsis = "%prog NAME [options]"
2015 takes_optiongroups = {
2016 "sambaopts": options.SambaOptions,
2017 "versionopts": options.VersionOptions,
2018 "localdcopts": LocalDCCredentialsOptions,
2021 takes_options = [
2024 takes_args = ["domain"]
2026 def run(self, domain, sambaopts=None, versionopts=None, localdcopts=None):
2028 local_server = self.setup_local_server(sambaopts, localdcopts)
2029 try:
2030 local_lsa = self.new_local_lsa_connection()
2031 except RuntimeError as error:
2032 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2034 try:
2035 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2036 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2037 except RuntimeError as error:
2038 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2040 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2041 local_lsa_info.name.string,
2042 local_lsa_info.dns_domain.string,
2043 local_lsa_info.sid))
2045 lsaString = lsa.String()
2046 lsaString.string = domain
2047 try:
2048 local_tdo_full = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2049 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2050 local_tdo_info = local_tdo_full.info_ex
2051 local_tdo_posix = local_tdo_full.posix_offset
2052 except RuntimeError as error:
2053 if self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2054 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2056 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(FULL_INFO) failed")
2058 try:
2059 local_tdo_enctypes = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2060 lsaString, lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES)
2061 except RuntimeError as error:
2062 if self.check_runtime_error(error, self.NT_STATUS_INVALID_PARAMETER):
2063 error = None
2064 if self.check_runtime_error(error, self.NT_STATUS_INVALID_INFO_CLASS):
2065 error = None
2067 if error is not None:
2068 raise self.LocalRuntimeError(self, error,
2069 "QueryTrustedDomainInfoByName(SUPPORTED_ENCRYPTION_TYPES) failed")
2071 local_tdo_enctypes = lsa.TrustDomainInfoSupportedEncTypes()
2072 local_tdo_enctypes.enc_types = 0
2074 try:
2075 local_tdo_forest = None
2076 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2077 local_tdo_forest = local_lsa.lsaRQueryForestTrustInformation(local_policy,
2078 lsaString, lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
2079 except RuntimeError as error:
2080 if self.check_runtime_error(error, self.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
2081 error = None
2082 if self.check_runtime_error(error, self.NT_STATUS_NOT_FOUND):
2083 error = None
2084 if error is not None:
2085 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation failed")
2087 local_tdo_forest = lsa.ForestTrustInformation()
2088 local_tdo_forest.count = 0
2089 local_tdo_forest.entries = []
2091 self.outf.write("TrusteDomain:\n\n");
2092 self.outf.write("NetbiosName: %s\n" % local_tdo_info.netbios_name.string)
2093 if local_tdo_info.netbios_name.string != local_tdo_info.domain_name.string:
2094 self.outf.write("DnsName: %s\n" % local_tdo_info.domain_name.string)
2095 self.outf.write("SID: %s\n" % local_tdo_info.sid)
2096 self.outf.write("Type: %s\n" % self.trustType_string(local_tdo_info.trust_type))
2097 self.outf.write("Direction: %s\n" % self.trustDirection_string(local_tdo_info.trust_direction))
2098 self.outf.write("Attributes: %s\n" % self.trustAttributes_string(local_tdo_info.trust_attributes))
2099 posix_offset_u32 = ctypes.c_uint32(local_tdo_posix.posix_offset).value
2100 posix_offset_i32 = ctypes.c_int32(local_tdo_posix.posix_offset).value
2101 self.outf.write("PosixOffset: 0x%08X (%d)\n" % (posix_offset_u32, posix_offset_i32))
2102 self.outf.write("kerb_EncTypes: %s\n" % self.kerb_EncTypes_string(local_tdo_enctypes.enc_types))
2104 if local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2105 self.write_forest_trust_info(local_tdo_forest,
2106 tln=local_tdo_info.domain_name.string)
2108 return
2110 class cmd_domain_trust_create(DomainTrustCommand):
2111 """Create a domain or forest trust."""
2113 synopsis = "%prog DOMAIN [options]"
2115 takes_optiongroups = {
2116 "sambaopts": options.SambaOptions,
2117 "versionopts": options.VersionOptions,
2118 "credopts": options.CredentialsOptions,
2119 "localdcopts": LocalDCCredentialsOptions,
2122 takes_options = [
2123 Option("--type", type="choice", metavar="TYPE",
2124 choices=["external", "forest"],
2125 help="The type of the trust: 'external' or 'forest'.",
2126 dest='trust_type',
2127 default="external"),
2128 Option("--direction", type="choice", metavar="DIRECTION",
2129 choices=["incoming", "outgoing", "both"],
2130 help="The trust direction: 'incoming', 'outgoing' or 'both'.",
2131 dest='trust_direction',
2132 default="both"),
2133 Option("--create-location", type="choice", metavar="LOCATION",
2134 choices=["local", "both"],
2135 help="Where to create the trusted domain object: 'local' or 'both'.",
2136 dest='create_location',
2137 default="both"),
2138 Option("--cross-organisation", action="store_true",
2139 help="The related domains does not belong to the same organisation.",
2140 dest='cross_organisation',
2141 default=False),
2142 Option("--quarantined", type="choice", metavar="yes|no",
2143 choices=["yes", "no", None],
2144 help="Special SID filtering rules are applied to the trust. "
2145 "With --type=external the default is yes. "
2146 "With --type=forest the default is no.",
2147 dest='quarantined_arg',
2148 default=None),
2149 Option("--not-transitive", action="store_true",
2150 help="The forest trust is not transitive.",
2151 dest='not_transitive',
2152 default=False),
2153 Option("--treat-as-external", action="store_true",
2154 help="The treat the forest trust as external.",
2155 dest='treat_as_external',
2156 default=False),
2157 Option("--no-aes-keys", action="store_false",
2158 help="The trust uses aes kerberos keys.",
2159 dest='use_aes_keys',
2160 default=True),
2161 Option("--skip-validation", action="store_false",
2162 help="Skip validation of the trust.",
2163 dest='validate',
2164 default=True),
2167 takes_args = ["domain"]
2169 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2170 trust_type=None, trust_direction=None, create_location=None,
2171 cross_organisation=False, quarantined_arg=None,
2172 not_transitive=False, treat_as_external=False,
2173 use_aes_keys=False, validate=True):
2175 lsaString = lsa.String()
2177 quarantined = False
2178 if quarantined_arg is None:
2179 if trust_type == 'external':
2180 quarantined = True
2181 elif quarantined_arg == 'yes':
2182 quarantined = True
2184 if trust_type != 'forest':
2185 if not_transitive:
2186 raise CommandError("--not-transitive requires --type=forest")
2187 if treat_as_external:
2188 raise CommandError("--treat-as-external requires --type=forest")
2190 enc_types = None
2191 if use_aes_keys:
2192 enc_types = lsa.TrustDomainInfoSupportedEncTypes()
2193 enc_types.enc_types = security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96
2194 enc_types.enc_types |= security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96
2196 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2197 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2198 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2200 local_trust_info = lsa.TrustDomainInfoInfoEx()
2201 local_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2202 local_trust_info.trust_direction = 0
2203 if trust_direction == "both":
2204 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2205 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2206 elif trust_direction == "incoming":
2207 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2208 elif trust_direction == "outgoing":
2209 local_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2210 local_trust_info.trust_attributes = 0
2211 if cross_organisation:
2212 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2213 if quarantined:
2214 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2215 if trust_type == "forest":
2216 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2217 if not_transitive:
2218 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2219 if treat_as_external:
2220 local_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2222 def get_password(name):
2223 password = None
2224 while True:
2225 if password is not None and password is not '':
2226 return password
2227 password = getpass("New %s Password: " % name)
2228 passwordverify = getpass("Retype %s Password: " % name)
2229 if not password == passwordverify:
2230 password = None
2231 self.outf.write("Sorry, passwords do not match.\n")
2233 incoming_secret = None
2234 outgoing_secret = None
2235 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2236 if create_location == "local":
2237 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2238 incoming_password = get_password("Incoming Trust")
2239 incoming_secret = string_to_byte_array(incoming_password.encode('utf-16-le'))
2240 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2241 outgoing_password = get_password("Outgoing Trust")
2242 outgoing_secret = string_to_byte_array(outgoing_password.encode('utf-16-le'))
2244 remote_trust_info = None
2245 else:
2246 # We use 240 random bytes.
2247 # Windows uses 28 or 240 random bytes. I guess it's
2248 # based on the trust type external vs. forest.
2250 # The initial trust password can be up to 512 bytes
2251 # while the versioned passwords used for periodic updates
2252 # can only be up to 498 bytes, as netr_ServerPasswordSet2()
2253 # needs to pass the NL_PASSWORD_VERSION structure within the
2254 # 512 bytes and a 2 bytes confounder is required.
2256 def random_trust_secret(length, use_aes_keys=True):
2257 secret = [0] * length
2259 pw1 = samba.generate_random_password(length/2, length/2)
2260 if not use_aes_keys:
2261 # With arcfour-hmac-md5 we have to use valid utf16
2262 # in order to generate the correct pre-auth key
2263 # based on a utf8 password.
2265 # We can remove this once our client libraries
2266 # support using the correct NTHASH.
2267 return string_to_byte_array(pw1.encode('utf-16-le'))
2269 # We mix characters from generate_random_password
2270 # with random numbers from random.randint()
2271 for i in range(len(secret)):
2272 if len(pw1) > i:
2273 secret[i] = ord(pw1[i])
2274 else:
2275 secret[i] = random.randint(0, 255)
2277 return secret
2279 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_INBOUND:
2280 incoming_secret = random_trust_secret(240, use_aes_keys=use_aes_keys)
2281 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2282 outgoing_secret = random_trust_secret(240, use_aes_keys=use_aes_keys)
2284 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2285 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2287 remote_trust_info = lsa.TrustDomainInfoInfoEx()
2288 remote_trust_info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
2289 remote_trust_info.trust_direction = 0
2290 if trust_direction == "both":
2291 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2292 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2293 elif trust_direction == "incoming":
2294 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
2295 elif trust_direction == "outgoing":
2296 remote_trust_info.trust_direction |= lsa.LSA_TRUST_DIRECTION_INBOUND
2297 remote_trust_info.trust_attributes = 0
2298 if cross_organisation:
2299 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2300 if quarantined:
2301 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2302 if trust_type == "forest":
2303 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2304 if not_transitive:
2305 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2306 if treat_as_external:
2307 remote_trust_info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2309 local_server = self.setup_local_server(sambaopts, localdcopts)
2310 try:
2311 local_lsa = self.new_local_lsa_connection()
2312 except RuntimeError as error:
2313 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2315 try:
2316 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2317 except RuntimeError as error:
2318 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2320 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2321 local_lsa_info.name.string,
2322 local_lsa_info.dns_domain.string,
2323 local_lsa_info.sid))
2325 try:
2326 remote_server = self.setup_remote_server(credopts, domain)
2327 except RuntimeError as error:
2328 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2330 try:
2331 remote_lsa = self.new_remote_lsa_connection()
2332 except RuntimeError as error:
2333 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2335 try:
2336 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2337 except RuntimeError as error:
2338 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2340 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2341 remote_lsa_info.name.string,
2342 remote_lsa_info.dns_domain.string,
2343 remote_lsa_info.sid))
2345 local_trust_info.domain_name.string = remote_lsa_info.dns_domain.string
2346 local_trust_info.netbios_name.string = remote_lsa_info.name.string
2347 local_trust_info.sid = remote_lsa_info.sid
2349 if remote_trust_info:
2350 remote_trust_info.domain_name.string = local_lsa_info.dns_domain.string
2351 remote_trust_info.netbios_name.string = local_lsa_info.name.string
2352 remote_trust_info.sid = local_lsa_info.sid
2354 try:
2355 lsaString.string = local_trust_info.domain_name.string
2356 local_old_netbios = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2357 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2358 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2359 except RuntimeError as error:
2360 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2361 raise self.LocalRuntimeError(self, error,
2362 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2363 lsaString.string))
2365 try:
2366 lsaString.string = local_trust_info.netbios_name.string
2367 local_old_dns = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2368 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2369 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2370 except RuntimeError as error:
2371 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2372 raise self.LocalRuntimeError(self, error,
2373 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2374 lsaString.string))
2376 if remote_trust_info:
2377 try:
2378 lsaString.string = remote_trust_info.domain_name.string
2379 remote_old_netbios = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2380 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2381 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2382 except RuntimeError as error:
2383 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2384 raise self.RemoteRuntimeError(self, error,
2385 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2386 lsaString.string))
2388 try:
2389 lsaString.string = remote_trust_info.netbios_name.string
2390 remote_old_dns = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2391 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
2392 raise CommandError("TrustedDomain %s already exist'" % lsaString.string)
2393 except RuntimeError as error:
2394 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2395 raise self.RemoteRuntimeError(self, error,
2396 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2397 lsaString.string))
2399 try:
2400 local_netlogon = self.new_local_netlogon_connection()
2401 except RuntimeError as error:
2402 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2404 try:
2405 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
2406 except RuntimeError as error:
2407 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
2409 if remote_trust_info:
2410 try:
2411 remote_netlogon = self.new_remote_netlogon_connection()
2412 except RuntimeError as error:
2413 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2415 try:
2416 remote_netlogon_info = self.get_netlogon_dc_info(remote_netlogon, remote_server)
2417 except RuntimeError as error:
2418 raise self.RemoteRuntimeError(self, error, "failed to get netlogon dc info")
2420 def generate_AuthInOutBlob(secret, update_time):
2421 if secret is None:
2422 blob = drsblobs.trustAuthInOutBlob()
2423 blob.count = 0
2425 return blob
2427 clear = drsblobs.AuthInfoClear()
2428 clear.size = len(secret)
2429 clear.password = secret
2431 info = drsblobs.AuthenticationInformation()
2432 info.LastUpdateTime = samba.unix2nttime(update_time)
2433 info.AuthType = lsa.TRUST_AUTH_TYPE_CLEAR
2434 info.AuthInfo = clear
2436 array = drsblobs.AuthenticationInformationArray()
2437 array.count = 1
2438 array.array = [info]
2440 blob = drsblobs.trustAuthInOutBlob()
2441 blob.count = 1
2442 blob.current = array
2444 return blob
2446 def generate_AuthInfoInternal(session_key, incoming=None, outgoing=None):
2447 confounder = [0] * 512
2448 for i in range(len(confounder)):
2449 confounder[i] = random.randint(0, 255)
2451 trustpass = drsblobs.trustDomainPasswords()
2453 trustpass.confounder = confounder
2454 trustpass.outgoing = outgoing
2455 trustpass.incoming = incoming
2457 trustpass_blob = ndr_pack(trustpass)
2459 encrypted_trustpass = arcfour_encrypt(session_key, trustpass_blob)
2461 auth_blob = lsa.DATA_BUF2()
2462 auth_blob.size = len(encrypted_trustpass)
2463 auth_blob.data = string_to_byte_array(encrypted_trustpass)
2465 auth_info = lsa.TrustDomainInfoAuthInfoInternal()
2466 auth_info.auth_blob = auth_blob
2468 return auth_info
2470 update_time = samba.current_unix_time()
2471 incoming_blob = generate_AuthInOutBlob(incoming_secret, update_time)
2472 outgoing_blob = generate_AuthInOutBlob(outgoing_secret, update_time)
2474 local_tdo_handle = None
2475 remote_tdo_handle = None
2477 local_auth_info = generate_AuthInfoInternal(local_lsa.session_key,
2478 incoming=incoming_blob,
2479 outgoing=outgoing_blob)
2480 if remote_trust_info:
2481 remote_auth_info = generate_AuthInfoInternal(remote_lsa.session_key,
2482 incoming=outgoing_blob,
2483 outgoing=incoming_blob)
2485 try:
2486 if remote_trust_info:
2487 self.outf.write("Creating remote TDO.\n")
2488 current_request = { "location": "remote", "name": "CreateTrustedDomainEx2"}
2489 remote_tdo_handle = remote_lsa.CreateTrustedDomainEx2(remote_policy,
2490 remote_trust_info,
2491 remote_auth_info,
2492 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2493 self.outf.write("Remote TDO created.\n")
2494 if enc_types:
2495 self.outf.write("Setting supported encryption types on remote TDO.\n")
2496 current_request = { "location": "remote", "name": "SetInformationTrustedDomain"}
2497 remote_lsa.SetInformationTrustedDomain(remote_tdo_handle,
2498 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2499 enc_types)
2501 self.outf.write("Creating local TDO.\n")
2502 current_request = { "location": "local", "name": "CreateTrustedDomainEx2"}
2503 local_tdo_handle = local_lsa.CreateTrustedDomainEx2(local_policy,
2504 local_trust_info,
2505 local_auth_info,
2506 lsa.LSA_TRUSTED_DOMAIN_ALL_ACCESS)
2507 self.outf.write("Local TDO created\n")
2508 if enc_types:
2509 self.outf.write("Setting supported encryption types on local TDO.\n")
2510 current_request = { "location": "local", "name": "SetInformationTrustedDomain"}
2511 local_lsa.SetInformationTrustedDomain(local_tdo_handle,
2512 lsa.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES,
2513 enc_types)
2514 except RuntimeError as error:
2515 self.outf.write("Error: %s failed %sly - cleaning up\n" % (
2516 current_request['name'], current_request['location']))
2517 if remote_tdo_handle:
2518 self.outf.write("Deleting remote TDO.\n")
2519 remote_lsa.DeleteObject(remote_tdo_handle)
2520 remote_tdo_handle = None
2521 if local_tdo_handle:
2522 self.outf.write("Deleting local TDO.\n")
2523 local_lsa.DeleteObject(local_tdo_handle)
2524 local_tdo_handle = None
2525 if current_request['location'] is "remote":
2526 raise self.RemoteRuntimeError(self, error, "%s" % (
2527 current_request['name']))
2528 raise self.LocalRuntimeError(self, error, "%s" % (
2529 current_request['name']))
2531 if validate:
2532 if local_trust_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
2533 self.outf.write("Setup local forest trust information...\n")
2534 try:
2535 # get all information about the remote trust
2536 # this triggers netr_GetForestTrustInformation to the remote domain
2537 # and lsaRSetForestTrustInformation() locally, but new top level
2538 # names are disabled by default.
2539 local_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
2540 remote_lsa_info.dns_domain.string,
2541 netlogon.DS_GFTI_UPDATE_TDO)
2542 except RuntimeError as error:
2543 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2545 try:
2546 # here we try to enable all top level names
2547 local_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
2548 remote_lsa_info.dns_domain,
2549 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2550 local_forest_info,
2552 except RuntimeError as error:
2553 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2555 self.write_forest_trust_info(local_forest_info,
2556 tln=remote_lsa_info.dns_domain.string,
2557 collisions=local_forest_collision)
2559 if remote_trust_info:
2560 self.outf.write("Setup remote forest trust information...\n")
2561 try:
2562 # get all information about the local trust (from the perspective of the remote domain)
2563 # this triggers netr_GetForestTrustInformation to our domain.
2564 # and lsaRSetForestTrustInformation() remotely, but new top level
2565 # names are disabled by default.
2566 remote_forest_info = remote_netlogon.netr_DsRGetForestTrustInformation(remote_netlogon_info.dc_unc,
2567 local_lsa_info.dns_domain.string,
2568 netlogon.DS_GFTI_UPDATE_TDO)
2569 except RuntimeError as error:
2570 raise self.RemoteRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
2572 try:
2573 # here we try to enable all top level names
2574 remote_forest_collision = remote_lsa.lsaRSetForestTrustInformation(remote_policy,
2575 local_lsa_info.dns_domain,
2576 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
2577 remote_forest_info,
2579 except RuntimeError as error:
2580 raise self.RemoteRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
2582 self.write_forest_trust_info(remote_forest_info,
2583 tln=local_lsa_info.dns_domain.string,
2584 collisions=remote_forest_collision)
2586 if local_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2587 self.outf.write("Validating outgoing trust...\n")
2588 try:
2589 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_netlogon_info.dc_unc,
2590 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2592 remote_lsa_info.dns_domain.string)
2593 except RuntimeError as error:
2594 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2596 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2597 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2599 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2600 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2601 local_trust_verify.trusted_dc_name,
2602 local_trust_verify.tc_connection_status[1],
2603 local_trust_verify.pdc_connection_status[1])
2604 else:
2605 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2606 local_trust_verify.trusted_dc_name,
2607 local_trust_verify.tc_connection_status[1],
2608 local_trust_verify.pdc_connection_status[1])
2610 if local_trust_status != self.WERR_OK or local_conn_status != self.WERR_OK:
2611 raise CommandError(local_validation)
2612 else:
2613 self.outf.write("OK: %s\n" % local_validation)
2615 if remote_trust_info:
2616 if remote_trust_info.trust_direction & lsa.LSA_TRUST_DIRECTION_OUTBOUND:
2617 self.outf.write("Validating incoming trust...\n")
2618 try:
2619 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_netlogon_info.dc_unc,
2620 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2622 local_lsa_info.dns_domain.string)
2623 except RuntimeError as error:
2624 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2626 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2627 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2629 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2630 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2631 remote_trust_verify.trusted_dc_name,
2632 remote_trust_verify.tc_connection_status[1],
2633 remote_trust_verify.pdc_connection_status[1])
2634 else:
2635 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2636 remote_trust_verify.trusted_dc_name,
2637 remote_trust_verify.tc_connection_status[1],
2638 remote_trust_verify.pdc_connection_status[1])
2640 if remote_trust_status != self.WERR_OK or remote_conn_status != self.WERR_OK:
2641 raise CommandError(remote_validation)
2642 else:
2643 self.outf.write("OK: %s\n" % remote_validation)
2645 if remote_tdo_handle is not None:
2646 try:
2647 remote_lsa.Close(remote_tdo_handle)
2648 except RuntimeError as error:
2649 pass
2650 remote_tdo_handle = None
2651 if local_tdo_handle is not None:
2652 try:
2653 local_lsa.Close(local_tdo_handle)
2654 except RuntimeError as error:
2655 pass
2656 local_tdo_handle = None
2658 self.outf.write("Success.\n")
2659 return
2661 class cmd_domain_trust_delete(DomainTrustCommand):
2662 """Delete a domain trust."""
2664 synopsis = "%prog DOMAIN [options]"
2666 takes_optiongroups = {
2667 "sambaopts": options.SambaOptions,
2668 "versionopts": options.VersionOptions,
2669 "credopts": options.CredentialsOptions,
2670 "localdcopts": LocalDCCredentialsOptions,
2673 takes_options = [
2674 Option("--delete-location", type="choice", metavar="LOCATION",
2675 choices=["local", "both"],
2676 help="Where to delete the trusted domain object: 'local' or 'both'.",
2677 dest='delete_location',
2678 default="both"),
2681 takes_args = ["domain"]
2683 def run(self, domain, sambaopts=None, localdcopts=None, credopts=None, versionopts=None,
2684 delete_location=None):
2686 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2687 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2688 local_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2690 if delete_location == "local":
2691 remote_policy_access = None
2692 else:
2693 remote_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2694 remote_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
2695 remote_policy_access |= lsa.LSA_POLICY_CREATE_SECRET
2697 local_server = self.setup_local_server(sambaopts, localdcopts)
2698 try:
2699 local_lsa = self.new_local_lsa_connection()
2700 except RuntimeError as error:
2701 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2703 try:
2704 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2705 except RuntimeError as error:
2706 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2708 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2709 local_lsa_info.name.string,
2710 local_lsa_info.dns_domain.string,
2711 local_lsa_info.sid))
2713 local_tdo_info = None
2714 local_tdo_handle = None
2715 remote_tdo_info = None
2716 remote_tdo_handle = None
2718 lsaString = lsa.String()
2719 try:
2720 lsaString.string = domain
2721 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2722 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2723 except RuntimeError as error:
2724 if self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2725 raise CommandError("Failed to find trust for domain '%s'" % domain)
2726 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2729 if remote_policy_access is not None:
2730 try:
2731 remote_server = self.setup_remote_server(credopts, domain)
2732 except RuntimeError as error:
2733 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2735 try:
2736 remote_lsa = self.new_remote_lsa_connection()
2737 except RuntimeError as error:
2738 raise self.RemoteRuntimeError(self, error, "failed to connect lsa server")
2740 try:
2741 (remote_policy, remote_lsa_info) = self.get_lsa_info(remote_lsa, remote_policy_access)
2742 except RuntimeError as error:
2743 raise self.RemoteRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2745 self.outf.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2746 remote_lsa_info.name.string,
2747 remote_lsa_info.dns_domain.string,
2748 remote_lsa_info.sid))
2750 if remote_lsa_info.sid != local_tdo_info.sid or \
2751 remote_lsa_info.name.string != local_tdo_info.netbios_name.string or \
2752 remote_lsa_info.dns_domain.string != local_tdo_info.domain_name.string:
2753 raise CommandError("LocalTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2754 local_tdo_info.netbios_name.string,
2755 local_tdo_info.domain_name.string,
2756 local_tdo_info.sid))
2758 try:
2759 lsaString.string = local_lsa_info.dns_domain.string
2760 remote_tdo_info = remote_lsa.QueryTrustedDomainInfoByName(remote_policy,
2761 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2762 except RuntimeError as error:
2763 if not self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2764 raise self.RemoteRuntimeError(self, error, "QueryTrustedDomainInfoByName(%s)" % (
2765 lsaString.string))
2766 pass
2768 if remote_tdo_info is not None:
2769 if local_lsa_info.sid != remote_tdo_info.sid or \
2770 local_lsa_info.name.string != remote_tdo_info.netbios_name.string or \
2771 local_lsa_info.dns_domain.string != remote_tdo_info.domain_name.string:
2772 raise CommandError("RemoteTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2773 remote_tdo_info.netbios_name.string,
2774 remote_tdo_info.domain_name.string,
2775 remote_tdo_info.sid))
2777 if local_tdo_info is not None:
2778 try:
2779 lsaString.string = local_tdo_info.domain_name.string
2780 local_tdo_handle = local_lsa.OpenTrustedDomainByName(local_policy,
2781 lsaString,
2782 security.SEC_STD_DELETE)
2783 except RuntimeError as error:
2784 raise self.LocalRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2785 lsaString.string))
2787 local_lsa.DeleteObject(local_tdo_handle)
2788 local_tdo_handle = None
2790 if remote_tdo_info is not None:
2791 try:
2792 lsaString.string = remote_tdo_info.domain_name.string
2793 remote_tdo_handle = remote_lsa.OpenTrustedDomainByName(remote_policy,
2794 lsaString,
2795 security.SEC_STD_DELETE)
2796 except RuntimeError as error:
2797 raise self.RemoteRuntimeError(self, error, "OpenTrustedDomainByName(%s)" % (
2798 lsaString.string))
2800 if remote_tdo_handle is not None:
2801 try:
2802 remote_lsa.DeleteObject(remote_tdo_handle)
2803 remote_tdo_handle = None
2804 self.outf.write("RemoteTDO deleted.\n")
2805 except RuntimeError as error:
2806 self.outf.write("%s\n" % self.RemoteRuntimeError(self, error, "DeleteObject() failed"))
2808 if local_tdo_handle is not None:
2809 try:
2810 local_lsa.DeleteObject(local_tdo_handle)
2811 local_tdo_handle = None
2812 self.outf.write("LocalTDO deleted.\n")
2813 except RuntimeError as error:
2814 self.outf.write("%s\n" % self.LocalRuntimeError(self, error, "DeleteObject() failed"))
2816 return
2818 class cmd_domain_trust_validate(DomainTrustCommand):
2819 """Validate a domain trust."""
2821 synopsis = "%prog DOMAIN [options]"
2823 takes_optiongroups = {
2824 "sambaopts": options.SambaOptions,
2825 "versionopts": options.VersionOptions,
2826 "credopts": options.CredentialsOptions,
2827 "localdcopts": LocalDCCredentialsOptions,
2830 takes_options = [
2831 Option("--validate-location", type="choice", metavar="LOCATION",
2832 choices=["local", "both"],
2833 help="Where to validate the trusted domain object: 'local' or 'both'.",
2834 dest='validate_location',
2835 default="both"),
2838 takes_args = ["domain"]
2840 def run(self, domain, sambaopts=None, versionopts=None, credopts=None, localdcopts=None,
2841 validate_location=None):
2843 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
2845 local_server = self.setup_local_server(sambaopts, localdcopts)
2846 try:
2847 local_lsa = self.new_local_lsa_connection()
2848 except RuntimeError as error:
2849 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
2851 try:
2852 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
2853 except RuntimeError as error:
2854 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
2856 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2857 local_lsa_info.name.string,
2858 local_lsa_info.dns_domain.string,
2859 local_lsa_info.sid))
2861 try:
2862 lsaString = lsa.String()
2863 lsaString.string = domain
2864 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
2865 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
2866 except RuntimeError as error:
2867 if self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
2868 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
2870 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
2872 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
2873 local_tdo_info.netbios_name.string,
2874 local_tdo_info.domain_name.string,
2875 local_tdo_info.sid))
2877 try:
2878 local_netlogon = self.new_local_netlogon_connection()
2879 except RuntimeError as error:
2880 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
2882 try:
2883 local_trust_verify = local_netlogon.netr_LogonControl2Ex(local_server,
2884 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2886 local_tdo_info.domain_name.string)
2887 except RuntimeError as error:
2888 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2890 local_trust_status = self._uint32(local_trust_verify.pdc_connection_status[0])
2891 local_conn_status = self._uint32(local_trust_verify.tc_connection_status[0])
2893 if local_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2894 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2895 local_trust_verify.trusted_dc_name,
2896 local_trust_verify.tc_connection_status[1],
2897 local_trust_verify.pdc_connection_status[1])
2898 else:
2899 local_validation = "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2900 local_trust_verify.trusted_dc_name,
2901 local_trust_verify.tc_connection_status[1],
2902 local_trust_verify.pdc_connection_status[1])
2904 if local_trust_status != self.WERR_OK or local_conn_status != self.WERR_OK:
2905 raise CommandError(local_validation)
2906 else:
2907 self.outf.write("OK: %s\n" % local_validation)
2909 try:
2910 server = local_trust_verify.trusted_dc_name.replace('\\', '')
2911 domain_and_server = "%s\\%s" % (local_tdo_info.domain_name.string, server)
2912 local_trust_rediscover = local_netlogon.netr_LogonControl2Ex(local_server,
2913 netlogon.NETLOGON_CONTROL_REDISCOVER,
2915 domain_and_server)
2916 except RuntimeError as error:
2917 raise self.LocalRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
2919 local_conn_status = self._uint32(local_trust_rediscover.tc_connection_status[0])
2920 local_rediscover = "LocalRediscover: DC[%s] CONNECTION[%s]" % (
2921 local_trust_rediscover.trusted_dc_name,
2922 local_trust_rediscover.tc_connection_status[1])
2924 if local_conn_status != self.WERR_OK:
2925 raise CommandError(local_rediscover)
2926 else:
2927 self.outf.write("OK: %s\n" % local_rediscover)
2929 if validate_location != "local":
2930 try:
2931 remote_server = self.setup_remote_server(credopts, domain, require_pdc=False)
2932 except RuntimeError as error:
2933 raise self.RemoteRuntimeError(self, error, "failed to locate remote server")
2935 try:
2936 remote_netlogon = self.new_remote_netlogon_connection()
2937 except RuntimeError as error:
2938 raise self.RemoteRuntimeError(self, error, "failed to connect netlogon server")
2940 try:
2941 remote_trust_verify = remote_netlogon.netr_LogonControl2Ex(remote_server,
2942 netlogon.NETLOGON_CONTROL_TC_VERIFY,
2944 local_lsa_info.dns_domain.string)
2945 except RuntimeError as error:
2946 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_TC_VERIFY failed")
2948 remote_trust_status = self._uint32(remote_trust_verify.pdc_connection_status[0])
2949 remote_conn_status = self._uint32(remote_trust_verify.tc_connection_status[0])
2951 if remote_trust_verify.flags & netlogon.NETLOGON_VERIFY_STATUS_RETURNED:
2952 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2953 remote_trust_verify.trusted_dc_name,
2954 remote_trust_verify.tc_connection_status[1],
2955 remote_trust_verify.pdc_connection_status[1])
2956 else:
2957 remote_validation = "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2958 remote_trust_verify.trusted_dc_name,
2959 remote_trust_verify.tc_connection_status[1],
2960 remote_trust_verify.pdc_connection_status[1])
2962 if remote_trust_status != self.WERR_OK or remote_conn_status != self.WERR_OK:
2963 raise CommandError(remote_validation)
2964 else:
2965 self.outf.write("OK: %s\n" % remote_validation)
2967 try:
2968 server = remote_trust_verify.trusted_dc_name.replace('\\', '')
2969 domain_and_server = "%s\\%s" % (local_lsa_info.dns_domain.string, server)
2970 remote_trust_rediscover = remote_netlogon.netr_LogonControl2Ex(remote_server,
2971 netlogon.NETLOGON_CONTROL_REDISCOVER,
2973 domain_and_server)
2974 except RuntimeError as error:
2975 raise self.RemoteRuntimeError(self, error, "NETLOGON_CONTROL_REDISCOVER failed")
2977 remote_conn_status = self._uint32(remote_trust_rediscover.tc_connection_status[0])
2979 remote_rediscover = "RemoteRediscover: DC[%s] CONNECTION[%s]" % (
2980 remote_trust_rediscover.trusted_dc_name,
2981 remote_trust_rediscover.tc_connection_status[1])
2983 if remote_conn_status != self.WERR_OK:
2984 raise CommandError(remote_rediscover)
2985 else:
2986 self.outf.write("OK: %s\n" % remote_rediscover)
2988 return
2990 class cmd_domain_trust_namespaces(DomainTrustCommand):
2991 """Manage forest trust namespaces."""
2993 synopsis = "%prog [DOMAIN] [options]"
2995 takes_optiongroups = {
2996 "sambaopts": options.SambaOptions,
2997 "versionopts": options.VersionOptions,
2998 "localdcopts": LocalDCCredentialsOptions,
3001 takes_options = [
3002 Option("--refresh", type="choice", metavar="check|store",
3003 choices=["check", "store", None],
3004 help="List and maybe store refreshed forest trust information: 'check' or 'store'.",
3005 dest='refresh',
3006 default=None),
3007 Option("--enable-all", action="store_true",
3008 help="Try to update disabled entries, not allowed with --refresh=check.",
3009 dest='enable_all',
3010 default=False),
3011 Option("--enable-tln", action="append", metavar='DNSDOMAIN',
3012 help="Enable a top level name entry. Can be specified multiple times.",
3013 dest='enable_tln',
3014 default=[]),
3015 Option("--disable-tln", action="append", metavar='DNSDOMAIN',
3016 help="Disable a top level name entry. Can be specified multiple times.",
3017 dest='disable_tln',
3018 default=[]),
3019 Option("--add-tln-ex", action="append", metavar='DNSDOMAIN',
3020 help="Add a top level exclusion entry. Can be specified multiple times.",
3021 dest='add_tln_ex',
3022 default=[]),
3023 Option("--delete-tln-ex", action="append", metavar='DNSDOMAIN',
3024 help="Delete a top level exclusion entry. Can be specified multiple times.",
3025 dest='delete_tln_ex',
3026 default=[]),
3027 Option("--enable-nb", action="append", metavar='NETBIOSDOMAIN',
3028 help="Enable a netbios name in a domain entry. Can be specified multiple times.",
3029 dest='enable_nb',
3030 default=[]),
3031 Option("--disable-nb", action="append", metavar='NETBIOSDOMAIN',
3032 help="Disable a netbios name in a domain entry. Can be specified multiple times.",
3033 dest='disable_nb',
3034 default=[]),
3035 Option("--enable-sid", action="append", metavar='DOMAINSID',
3036 help="Enable a SID in a domain entry. Can be specified multiple times.",
3037 dest='enable_sid_str',
3038 default=[]),
3039 Option("--disable-sid", action="append", metavar='DOMAINSID',
3040 help="Disable a SID in a domain entry. Can be specified multiple times.",
3041 dest='disable_sid_str',
3042 default=[]),
3043 Option("--add-upn-suffix", action="append", metavar='DNSDOMAIN',
3044 help="Add a new uPNSuffixes attribute for the local forest. Can be specified multiple times.",
3045 dest='add_upn',
3046 default=[]),
3047 Option("--delete-upn-suffix", action="append", metavar='DNSDOMAIN',
3048 help="Delete an existing uPNSuffixes attribute of the local forest. Can be specified multiple times.",
3049 dest='delete_upn',
3050 default=[]),
3051 Option("--add-spn-suffix", action="append", metavar='DNSDOMAIN',
3052 help="Add a new msDS-SPNSuffixes attribute for the local forest. Can be specified multiple times.",
3053 dest='add_spn',
3054 default=[]),
3055 Option("--delete-spn-suffix", action="append", metavar='DNSDOMAIN',
3056 help="Delete an existing msDS-SPNSuffixes attribute of the local forest. Can be specified multiple times.",
3057 dest='delete_spn',
3058 default=[]),
3061 takes_args = ["domain?"]
3063 def run(self, domain=None, sambaopts=None, localdcopts=None, versionopts=None,
3064 refresh=None, enable_all=False,
3065 enable_tln=[], disable_tln=[], add_tln_ex=[], delete_tln_ex=[],
3066 enable_sid_str=[], disable_sid_str=[], enable_nb=[], disable_nb=[],
3067 add_upn=[], delete_upn=[], add_spn=[], delete_spn=[]):
3069 require_update = False
3071 if domain is None:
3072 if refresh == "store":
3073 raise CommandError("--refresh=%s not allowed without DOMAIN" % refresh)
3075 if enable_all:
3076 raise CommandError("--enable-all not allowed without DOMAIN")
3078 if len(enable_tln) > 0:
3079 raise CommandError("--enable-tln not allowed without DOMAIN")
3080 if len(disable_tln) > 0:
3081 raise CommandError("--disable-tln not allowed without DOMAIN")
3083 if len(add_tln_ex) > 0:
3084 raise CommandError("--add-tln-ex not allowed without DOMAIN")
3085 if len(delete_tln_ex) > 0:
3086 raise CommandError("--delete-tln-ex not allowed without DOMAIN")
3088 if len(enable_nb) > 0:
3089 raise CommandError("--enable-nb not allowed without DOMAIN")
3090 if len(disable_nb) > 0:
3091 raise CommandError("--disable-nb not allowed without DOMAIN")
3093 if len(enable_sid_str) > 0:
3094 raise CommandError("--enable-sid not allowed without DOMAIN")
3095 if len(disable_sid_str) > 0:
3096 raise CommandError("--disable-sid not allowed without DOMAIN")
3098 if len(add_upn) > 0:
3099 for n in add_upn:
3100 if not n.startswith("*."):
3101 continue
3102 raise CommandError("value[%s] specified for --add-upn-suffix should not include with '*.'" % n)
3103 require_update = True
3104 if len(delete_upn) > 0:
3105 for n in delete_upn:
3106 if not n.startswith("*."):
3107 continue
3108 raise CommandError("value[%s] specified for --delete-upn-suffix should not include with '*.'" % n)
3109 require_update = True
3110 for a in add_upn:
3111 for d in delete_upn:
3112 if a.lower() != d.lower():
3113 continue
3114 raise CommandError("value[%s] specified for --add-upn-suffix and --delete-upn-suffix" % a)
3116 if len(add_spn) > 0:
3117 for n in add_spn:
3118 if not n.startswith("*."):
3119 continue
3120 raise CommandError("value[%s] specified for --add-spn-suffix should not include with '*.'" % n)
3121 require_update = True
3122 if len(delete_spn) > 0:
3123 for n in delete_spn:
3124 if not n.startswith("*."):
3125 continue
3126 raise CommandError("value[%s] specified for --delete-spn-suffix should not include with '*.'" % n)
3127 require_update = True
3128 for a in add_spn:
3129 for d in delete_spn:
3130 if a.lower() != d.lower():
3131 continue
3132 raise CommandError("value[%s] specified for --add-spn-suffix and --delete-spn-suffix" % a)
3133 else:
3134 if len(add_upn) > 0:
3135 raise CommandError("--add-upn-suffix not allowed together with DOMAIN")
3136 if len(delete_upn) > 0:
3137 raise CommandError("--delete-upn-suffix not allowed together with DOMAIN")
3138 if len(add_spn) > 0:
3139 raise CommandError("--add-spn-suffix not allowed together with DOMAIN")
3140 if len(delete_spn) > 0:
3141 raise CommandError("--delete-spn-suffix not allowed together with DOMAIN")
3143 if refresh is not None:
3144 if refresh == "store":
3145 require_update = True
3147 if enable_all and refresh != "store":
3148 raise CommandError("--enable-all not allowed together with --refresh=%s" % refresh)
3150 if len(enable_tln) > 0:
3151 raise CommandError("--enable-tln not allowed together with --refresh")
3152 if len(disable_tln) > 0:
3153 raise CommandError("--disable-tln not allowed together with --refresh")
3155 if len(add_tln_ex) > 0:
3156 raise CommandError("--add-tln-ex not allowed together with --refresh")
3157 if len(delete_tln_ex) > 0:
3158 raise CommandError("--delete-tln-ex not allowed together with --refresh")
3160 if len(enable_nb) > 0:
3161 raise CommandError("--enable-nb not allowed together with --refresh")
3162 if len(disable_nb) > 0:
3163 raise CommandError("--disable-nb not allowed together with --refresh")
3165 if len(enable_sid_str) > 0:
3166 raise CommandError("--enable-sid not allowed together with --refresh")
3167 if len(disable_sid_str) > 0:
3168 raise CommandError("--disable-sid not allowed together with --refresh")
3169 else:
3170 if enable_all:
3171 require_update = True
3173 if len(enable_tln) > 0:
3174 raise CommandError("--enable-tln not allowed together with --enable-all")
3176 if len(enable_nb) > 0:
3177 raise CommandError("--enable-nb not allowed together with --enable-all")
3179 if len(enable_sid) > 0:
3180 raise CommandError("--enable-sid not allowed together with --enable-all")
3182 if len(enable_tln) > 0:
3183 require_update = True
3184 if len(disable_tln) > 0:
3185 require_update = True
3186 for e in enable_tln:
3187 for d in disable_tln:
3188 if e.lower() != d.lower():
3189 continue
3190 raise CommandError("value[%s] specified for --enable-tln and --disable-tln" % e)
3192 if len(add_tln_ex) > 0:
3193 for n in add_tln_ex:
3194 if not n.startswith("*."):
3195 continue
3196 raise CommandError("value[%s] specified for --add-tln-ex should not include with '*.'" % n)
3197 require_update = True
3198 if len(delete_tln_ex) > 0:
3199 for n in delete_tln_ex:
3200 if not n.startswith("*."):
3201 continue
3202 raise CommandError("value[%s] specified for --delete-tln-ex should not include with '*.'" % n)
3203 require_update = True
3204 for a in add_tln_ex:
3205 for d in delete_tln_ex:
3206 if a.lower() != d.lower():
3207 continue
3208 raise CommandError("value[%s] specified for --add-tln-ex and --delete-tln-ex" % a)
3210 if len(enable_nb) > 0:
3211 require_update = True
3212 if len(disable_nb) > 0:
3213 require_update = True
3214 for e in enable_nb:
3215 for d in disable_nb:
3216 if e.upper() != d.upper():
3217 continue
3218 raise CommandError("value[%s] specified for --enable-nb and --disable-nb" % e)
3220 enable_sid = []
3221 for s in enable_sid_str:
3222 try:
3223 sid = security.dom_sid(s)
3224 except TypeError as error:
3225 raise CommandError("value[%s] specified for --enable-sid is not a valid SID" % s)
3226 enable_sid.append(sid)
3227 disable_sid = []
3228 for s in disable_sid_str:
3229 try:
3230 sid = security.dom_sid(s)
3231 except TypeError as error:
3232 raise CommandError("value[%s] specified for --disable-sid is not a valid SID" % s)
3233 disable_sid.append(sid)
3234 if len(enable_sid) > 0:
3235 require_update = True
3236 if len(disable_sid) > 0:
3237 require_update = True
3238 for e in enable_sid:
3239 for d in disable_sid:
3240 if e != d:
3241 continue
3242 raise CommandError("value[%s] specified for --enable-sid and --disable-sid" % e)
3244 local_policy_access = lsa.LSA_POLICY_VIEW_LOCAL_INFORMATION
3245 if require_update:
3246 local_policy_access |= lsa.LSA_POLICY_TRUST_ADMIN
3248 local_server = self.setup_local_server(sambaopts, localdcopts)
3249 try:
3250 local_lsa = self.new_local_lsa_connection()
3251 except RuntimeError as error:
3252 raise self.LocalRuntimeError(self, error, "failed to connect lsa server")
3254 try:
3255 (local_policy, local_lsa_info) = self.get_lsa_info(local_lsa, local_policy_access)
3256 except RuntimeError as error:
3257 raise self.LocalRuntimeError(self, error, "failed to query LSA_POLICY_INFO_DNS")
3259 self.outf.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
3260 local_lsa_info.name.string,
3261 local_lsa_info.dns_domain.string,
3262 local_lsa_info.sid))
3264 if domain is None:
3265 try:
3266 local_netlogon = self.new_local_netlogon_connection()
3267 except RuntimeError as error:
3268 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3270 try:
3271 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3272 except RuntimeError as error:
3273 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3275 if local_netlogon_info.domain_name != local_netlogon_info.forest_name:
3276 raise CommandError("The local domain [%s] is not the forest root [%s]" % (
3277 local_netlogon_info.domain_name,
3278 local_netlogon_info.forest_name))
3280 try:
3281 # get all information about our own forest
3282 own_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3283 None, 0)
3284 except RuntimeError as error:
3285 if self.check_runtime_error(error, self.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE):
3286 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3287 self.local_server))
3289 if self.check_runtime_error(error, self.WERR_INVALID_FUNCTION):
3290 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3291 self.local_server))
3293 if self.check_runtime_error(error, self.WERR_NERR_ACFNOTLOADED):
3294 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3295 self.local_server))
3297 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3299 self.outf.write("Own forest trust information...\n")
3300 self.write_forest_trust_info(own_forest_info,
3301 tln=local_lsa_info.dns_domain.string)
3303 try:
3304 local_samdb = self.new_local_ldap_connection()
3305 except RuntimeError as error:
3306 raise self.LocalRuntimeError(self, error, "failed to connect to SamDB")
3308 local_partitions_dn = "CN=Partitions,%s" % str(local_samdb.get_config_basedn())
3309 attrs = ['uPNSuffixes', 'msDS-SPNSuffixes']
3310 try:
3311 msgs = local_samdb.search(base=local_partitions_dn,
3312 scope=ldb.SCOPE_BASE,
3313 expression="(objectClass=crossRefContainer)",
3314 attrs=attrs)
3315 stored_msg = msgs[0]
3316 except ldb.LdbError as error:
3317 raise self.LocalLdbError(self, error, "failed to search partition dn")
3319 stored_upn_vals = []
3320 if 'uPNSuffixes' in stored_msg:
3321 stored_upn_vals.extend(stored_msg['uPNSuffixes'])
3323 stored_spn_vals = []
3324 if 'msDS-SPNSuffixes' in stored_msg:
3325 stored_spn_vals.extend(stored_msg['msDS-SPNSuffixes'])
3327 self.outf.write("Stored uPNSuffixes attributes[%d]:\n" % len(stored_upn_vals))
3328 for v in stored_upn_vals:
3329 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3330 self.outf.write("Stored msDS-SPNSuffixes attributes[%d]:\n" % len(stored_spn_vals))
3331 for v in stored_spn_vals:
3332 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3334 if not require_update:
3335 return
3337 replace_upn = False
3338 update_upn_vals = []
3339 update_upn_vals.extend(stored_upn_vals)
3341 replace_spn = False
3342 update_spn_vals = []
3343 update_spn_vals.extend(stored_spn_vals)
3345 for upn in add_upn:
3346 idx = None
3347 for i in xrange(0, len(update_upn_vals)):
3348 v = update_upn_vals[i]
3349 if v.lower() != upn.lower():
3350 continue
3351 idx = i
3352 break
3353 if idx is not None:
3354 raise CommandError("Entry already present for value[%s] specified for --add-upn-suffix" % upn)
3355 update_upn_vals.append(upn)
3356 replace_upn = True
3358 for upn in delete_upn:
3359 idx = None
3360 for i in xrange(0, len(update_upn_vals)):
3361 v = update_upn_vals[i]
3362 if v.lower() != upn.lower():
3363 continue
3364 idx = i
3365 break
3366 if idx is None:
3367 raise CommandError("Entry not found for value[%s] specified for --delete-upn-suffix" % upn)
3369 update_upn_vals.pop(idx)
3370 replace_upn = True
3372 for spn in add_spn:
3373 idx = None
3374 for i in xrange(0, len(update_spn_vals)):
3375 v = update_spn_vals[i]
3376 if v.lower() != spn.lower():
3377 continue
3378 idx = i
3379 break
3380 if idx is not None:
3381 raise CommandError("Entry already present for value[%s] specified for --add-spn-suffix" % spn)
3382 update_spn_vals.append(spn)
3383 replace_spn = True
3385 for spn in delete_spn:
3386 idx = None
3387 for i in xrange(0, len(update_spn_vals)):
3388 v = update_spn_vals[i]
3389 if v.lower() != spn.lower():
3390 continue
3391 idx = i
3392 break
3393 if idx is None:
3394 raise CommandError("Entry not found for value[%s] specified for --delete-spn-suffix" % spn)
3396 update_spn_vals.pop(idx)
3397 replace_spn = True
3399 self.outf.write("Update uPNSuffixes attributes[%d]:\n" % len(update_upn_vals))
3400 for v in update_upn_vals:
3401 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3402 self.outf.write("Update msDS-SPNSuffixes attributes[%d]:\n" % len(update_spn_vals))
3403 for v in update_spn_vals:
3404 self.outf.write("TLN: %-32s DNS[*.%s]\n" % ("", v))
3406 update_msg = ldb.Message()
3407 update_msg.dn = stored_msg.dn
3409 if replace_upn:
3410 update_msg['uPNSuffixes'] = ldb.MessageElement(update_upn_vals,
3411 ldb.FLAG_MOD_REPLACE,
3412 'uPNSuffixes')
3413 if replace_spn:
3414 update_msg['msDS-SPNSuffixes'] = ldb.MessageElement(update_spn_vals,
3415 ldb.FLAG_MOD_REPLACE,
3416 'msDS-SPNSuffixes')
3417 try:
3418 local_samdb.modify(update_msg)
3419 except ldb.LdbError as error:
3420 raise self.LocalLdbError(self, error, "failed to update partition dn")
3422 try:
3423 stored_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3424 None, 0)
3425 except RuntimeError as error:
3426 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3428 self.outf.write("Stored forest trust information...\n")
3429 self.write_forest_trust_info(stored_forest_info,
3430 tln=local_lsa_info.dns_domain.string)
3431 return
3433 try:
3434 lsaString = lsa.String()
3435 lsaString.string = domain
3436 local_tdo_info = local_lsa.QueryTrustedDomainInfoByName(local_policy,
3437 lsaString, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
3438 except RuntimeError as error:
3439 if self.check_runtime_error(error, self.NT_STATUS_OBJECT_NAME_NOT_FOUND):
3440 raise CommandError("trusted domain object does not exist for domain [%s]" % domain)
3442 raise self.LocalRuntimeError(self, error, "QueryTrustedDomainInfoByName(INFO_EX) failed")
3444 self.outf.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
3445 local_tdo_info.netbios_name.string,
3446 local_tdo_info.domain_name.string,
3447 local_tdo_info.sid))
3449 if not local_tdo_info.trust_attributes & lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE:
3450 raise CommandError("trusted domain object for domain [%s] is not marked as FOREST_TRANSITIVE." % domain)
3452 if refresh is not None:
3453 try:
3454 local_netlogon = self.new_local_netlogon_connection()
3455 except RuntimeError as error:
3456 raise self.LocalRuntimeError(self, error, "failed to connect netlogon server")
3458 try:
3459 local_netlogon_info = self.get_netlogon_dc_info(local_netlogon, local_server)
3460 except RuntimeError as error:
3461 raise self.LocalRuntimeError(self, error, "failed to get netlogon dc info")
3463 lsa_update_check = 1
3464 if refresh == "store":
3465 netlogon_update_tdo = netlogon.DS_GFTI_UPDATE_TDO
3466 if enable_all:
3467 lsa_update_check = 0
3468 else:
3469 netlogon_update_tdo = 0
3471 try:
3472 # get all information about the remote trust
3473 # this triggers netr_GetForestTrustInformation to the remote domain
3474 # and lsaRSetForestTrustInformation() locally, but new top level
3475 # names are disabled by default.
3476 fresh_forest_info = local_netlogon.netr_DsRGetForestTrustInformation(local_netlogon_info.dc_unc,
3477 local_tdo_info.domain_name.string,
3478 netlogon_update_tdo)
3479 except RuntimeError as error:
3480 raise self.LocalRuntimeError(self, error, "netr_DsRGetForestTrustInformation() failed")
3482 try:
3483 fresh_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3484 local_tdo_info.domain_name,
3485 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3486 fresh_forest_info,
3487 lsa_update_check)
3488 except RuntimeError as error:
3489 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3491 self.outf.write("Fresh forest trust information...\n")
3492 self.write_forest_trust_info(fresh_forest_info,
3493 tln=local_tdo_info.domain_name.string,
3494 collisions=fresh_forest_collision)
3496 if refresh == "store":
3497 try:
3498 lsaString = lsa.String()
3499 lsaString.string = local_tdo_info.domain_name.string
3500 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3501 lsaString,
3502 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3503 except RuntimeError as error:
3504 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3506 self.outf.write("Stored forest trust information...\n")
3507 self.write_forest_trust_info(stored_forest_info,
3508 tln=local_tdo_info.domain_name.string)
3510 return
3513 # The none --refresh path
3516 try:
3517 lsaString = lsa.String()
3518 lsaString.string = local_tdo_info.domain_name.string
3519 local_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3520 lsaString,
3521 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3522 except RuntimeError as error:
3523 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3525 self.outf.write("Local forest trust information...\n")
3526 self.write_forest_trust_info(local_forest_info,
3527 tln=local_tdo_info.domain_name.string)
3529 if not require_update:
3530 return
3532 entries = []
3533 entries.extend(local_forest_info.entries)
3534 update_forest_info = lsa.ForestTrustInformation()
3535 update_forest_info.count = len(entries)
3536 update_forest_info.entries = entries
3538 if enable_all:
3539 for i in xrange(0, len(update_forest_info.entries)):
3540 r = update_forest_info.entries[i]
3541 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3542 continue
3543 if update_forest_info.entries[i].flags == 0:
3544 continue
3545 update_forest_info.entries[i].time = 0
3546 update_forest_info.entries[i].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3547 for i in xrange(0, len(update_forest_info.entries)):
3548 r = update_forest_info.entries[i]
3549 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3550 continue
3551 if update_forest_info.entries[i].flags == 0:
3552 continue
3553 update_forest_info.entries[i].time = 0
3554 update_forest_info.entries[i].flags &= ~lsa.LSA_NB_DISABLED_MASK
3555 update_forest_info.entries[i].flags &= ~lsa.LSA_SID_DISABLED_MASK
3557 for tln in enable_tln:
3558 idx = None
3559 for i in xrange(0, len(update_forest_info.entries)):
3560 r = update_forest_info.entries[i]
3561 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3562 continue
3563 if r.forest_trust_data.string.lower() != tln.lower():
3564 continue
3565 idx = i
3566 break
3567 if idx is None:
3568 raise CommandError("Entry not found for value[%s] specified for --enable-tln" % tln)
3569 if not update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_MASK:
3570 raise CommandError("Entry found for value[%s] specified for --enable-tln is already enabled" % tln)
3571 update_forest_info.entries[idx].time = 0
3572 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3574 for tln in disable_tln:
3575 idx = None
3576 for i in xrange(0, len(update_forest_info.entries)):
3577 r = update_forest_info.entries[i]
3578 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3579 continue
3580 if r.forest_trust_data.string.lower() != tln.lower():
3581 continue
3582 idx = i
3583 break
3584 if idx is None:
3585 raise CommandError("Entry not found for value[%s] specified for --disable-tln" % tln)
3586 if update_forest_info.entries[idx].flags & lsa.LSA_TLN_DISABLED_ADMIN:
3587 raise CommandError("Entry found for value[%s] specified for --disable-tln is already disabled" % tln)
3588 update_forest_info.entries[idx].time = 0
3589 update_forest_info.entries[idx].flags &= ~lsa.LSA_TLN_DISABLED_MASK
3590 update_forest_info.entries[idx].flags |= lsa.LSA_TLN_DISABLED_ADMIN
3592 for tln_ex in add_tln_ex:
3593 idx = None
3594 for i in xrange(0, len(update_forest_info.entries)):
3595 r = update_forest_info.entries[i]
3596 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3597 continue
3598 if r.forest_trust_data.string.lower() != tln_ex.lower():
3599 continue
3600 idx = i
3601 break
3602 if idx is not None:
3603 raise CommandError("Entry already present for value[%s] specified for --add-tln-ex" % tln_ex)
3605 tln_dot = ".%s" % tln_ex.lower()
3606 idx = None
3607 for i in xrange(0, len(update_forest_info.entries)):
3608 r = update_forest_info.entries[i]
3609 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
3610 continue
3611 r_dot = ".%s" % r.forest_trust_data.string.lower()
3612 if tln_dot == r_dot:
3613 raise CommandError("TLN entry present for value[%s] specified for --add-tln-ex" % tln_ex)
3614 if not tln_dot.endswith(r_dot):
3615 continue
3616 idx = i
3617 break
3619 if idx is None:
3620 raise CommandError("No TLN parent present for value[%s] specified for --add-tln-ex" % tln_ex)
3622 r = lsa.ForestTrustRecord()
3623 r.type = lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX
3624 r.flags = 0
3625 r.time = 0
3626 r.forest_trust_data.string = tln_ex
3628 entries = []
3629 entries.extend(update_forest_info.entries)
3630 entries.insert(idx + 1, r)
3631 update_forest_info.count = len(entries)
3632 update_forest_info.entries = entries
3634 for tln_ex in delete_tln_ex:
3635 idx = None
3636 for i in xrange(0, len(update_forest_info.entries)):
3637 r = update_forest_info.entries[i]
3638 if r.type != lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
3639 continue
3640 if r.forest_trust_data.string.lower() != tln_ex.lower():
3641 continue
3642 idx = i
3643 break
3644 if idx is None:
3645 raise CommandError("Entry not found for value[%s] specified for --delete-tln-ex" % tln_ex)
3647 entries = []
3648 entries.extend(update_forest_info.entries)
3649 entries.pop(idx)
3650 update_forest_info.count = len(entries)
3651 update_forest_info.entries = entries
3653 for nb in enable_nb:
3654 idx = None
3655 for i in xrange(0, len(update_forest_info.entries)):
3656 r = update_forest_info.entries[i]
3657 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3658 continue
3659 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3660 continue
3661 idx = i
3662 break
3663 if idx is None:
3664 raise CommandError("Entry not found for value[%s] specified for --enable-nb" % nb)
3665 if not update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_MASK:
3666 raise CommandError("Entry found for value[%s] specified for --enable-nb is already enabled" % nb)
3667 update_forest_info.entries[idx].time = 0
3668 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3670 for nb in disable_nb:
3671 idx = None
3672 for i in xrange(0, len(update_forest_info.entries)):
3673 r = update_forest_info.entries[i]
3674 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3675 continue
3676 if r.forest_trust_data.netbios_domain_name.string.upper() != nb.upper():
3677 continue
3678 idx = i
3679 break
3680 if idx is None:
3681 raise CommandError("Entry not found for value[%s] specified for --delete-nb" % nb)
3682 if update_forest_info.entries[idx].flags & lsa.LSA_NB_DISABLED_ADMIN:
3683 raise CommandError("Entry found for value[%s] specified for --disable-nb is already disabled" % nb)
3684 update_forest_info.entries[idx].time = 0
3685 update_forest_info.entries[idx].flags &= ~lsa.LSA_NB_DISABLED_MASK
3686 update_forest_info.entries[idx].flags |= lsa.LSA_NB_DISABLED_ADMIN
3688 for sid in enable_sid:
3689 idx = None
3690 for i in xrange(0, len(update_forest_info.entries)):
3691 r = update_forest_info.entries[i]
3692 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3693 continue
3694 if r.forest_trust_data.domain_sid != sid:
3695 continue
3696 idx = i
3697 break
3698 if idx is None:
3699 raise CommandError("Entry not found for value[%s] specified for --enable-sid" % sid)
3700 if not update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_MASK:
3701 raise CommandError("Entry found for value[%s] specified for --enable-sid is already enabled" % nb)
3702 update_forest_info.entries[idx].time = 0
3703 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3705 for sid in disable_sid:
3706 idx = None
3707 for i in xrange(0, len(update_forest_info.entries)):
3708 r = update_forest_info.entries[i]
3709 if r.type != lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
3710 continue
3711 if r.forest_trust_data.domain_sid != sid:
3712 continue
3713 idx = i
3714 break
3715 if idx is None:
3716 raise CommandError("Entry not found for value[%s] specified for --delete-sid" % sid)
3717 if update_forest_info.entries[idx].flags & lsa.LSA_SID_DISABLED_ADMIN:
3718 raise CommandError("Entry found for value[%s] specified for --disable-sid is already disabled" % nb)
3719 update_forest_info.entries[idx].time = 0
3720 update_forest_info.entries[idx].flags &= ~lsa.LSA_SID_DISABLED_MASK
3721 update_forest_info.entries[idx].flags |= lsa.LSA_SID_DISABLED_ADMIN
3723 try:
3724 update_forest_collision = local_lsa.lsaRSetForestTrustInformation(local_policy,
3725 local_tdo_info.domain_name,
3726 lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
3727 update_forest_info, 0)
3728 except RuntimeError as error:
3729 raise self.LocalRuntimeError(self, error, "lsaRSetForestTrustInformation() failed")
3731 self.outf.write("Updated forest trust information...\n")
3732 self.write_forest_trust_info(update_forest_info,
3733 tln=local_tdo_info.domain_name.string,
3734 collisions=update_forest_collision)
3736 try:
3737 lsaString = lsa.String()
3738 lsaString.string = local_tdo_info.domain_name.string
3739 stored_forest_info = local_lsa.lsaRQueryForestTrustInformation(local_policy,
3740 lsaString,
3741 lsa.LSA_FOREST_TRUST_DOMAIN_INFO)
3742 except RuntimeError as error:
3743 raise self.LocalRuntimeError(self, error, "lsaRQueryForestTrustInformation() failed")
3745 self.outf.write("Stored forest trust information...\n")
3746 self.write_forest_trust_info(stored_forest_info,
3747 tln=local_tdo_info.domain_name.string)
3748 return
3750 class cmd_domain_trust(SuperCommand):
3751 """Domain and forest trust management."""
3753 subcommands = {}
3754 subcommands["list"] = cmd_domain_trust_list()
3755 subcommands["show"] = cmd_domain_trust_show()
3756 subcommands["create"] = cmd_domain_trust_create()
3757 subcommands["delete"] = cmd_domain_trust_delete()
3758 subcommands["validate"] = cmd_domain_trust_validate()
3759 subcommands["namespaces"] = cmd_domain_trust_namespaces()
3761 class cmd_domain(SuperCommand):
3762 """Domain management."""
3764 subcommands = {}
3765 subcommands["demote"] = cmd_domain_demote()
3766 if cmd_domain_export_keytab is not None:
3767 subcommands["exportkeytab"] = cmd_domain_export_keytab()
3768 subcommands["info"] = cmd_domain_info()
3769 subcommands["provision"] = cmd_domain_provision()
3770 subcommands["join"] = cmd_domain_join()
3771 subcommands["dcpromo"] = cmd_domain_dcpromo()
3772 subcommands["level"] = cmd_domain_level()
3773 subcommands["passwordsettings"] = cmd_domain_passwordsettings()
3774 subcommands["classicupgrade"] = cmd_domain_classicupgrade()
3775 subcommands["samba3upgrade"] = cmd_domain_samba3upgrade()
3776 subcommands["trust"] = cmd_domain_trust()