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