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