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