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