3 # Copyright Matthias Dieter Wallnoefer 2009
4 # Copyright Andrew Kroeger 2009
5 # Copyright Jelmer Vernooij 2007-2012
6 # Copyright Giampaolo Lauria 2011
7 # Copyright Matthieu Patou <mat@matws.net> 2011
8 # Copyright Andrew Bartlett 2008-2015
9 # Copyright Stefan Metzmacher 2012
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 3 of the License, or
14 # (at your option) any later version.
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 import samba
.getopt
as options
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
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 (
61 from samba
.netcmd
.common
import netcmd_get_domain_infos_via_cldap
62 from samba
.samba3
import Samba3
63 from samba
.samba3
import param
as s3param
64 from samba
.upgrade
import upgrade_from_samba3
65 from samba
.drs_utils
import (
66 sendDsReplicaSync
, drsuapi_connect
, drsException
,
68 from samba
import remove_dc
, arcfour_encrypt
, string_to_byte_array
70 from samba
.dsdb
import (
71 DS_DOMAIN_FUNCTION_2000
,
72 DS_DOMAIN_FUNCTION_2003
,
73 DS_DOMAIN_FUNCTION_2003_MIXED
,
74 DS_DOMAIN_FUNCTION_2008
,
75 DS_DOMAIN_FUNCTION_2008_R2
,
76 DS_DOMAIN_FUNCTION_2012
,
77 DS_DOMAIN_FUNCTION_2012_R2
,
78 DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL
,
79 DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
,
80 UF_WORKSTATION_TRUST_ACCOUNT
,
81 UF_SERVER_TRUST_ACCOUNT
,
82 UF_TRUSTED_FOR_DELEGATION
,
83 UF_PARTIAL_SECRETS_ACCOUNT
86 from samba
.provision
import (
89 DEFAULT_MIN_PWD_LENGTH
,
93 from samba
.provision
.common
import (
99 def get_testparm_var(testparm
, smbconf
, varname
):
100 errfile
= open(os
.devnull
, 'w')
101 p
= subprocess
.Popen([testparm
, '-s', '-l',
102 '--parameter-name=%s' % varname
, smbconf
],
103 stdout
=subprocess
.PIPE
, stderr
=errfile
)
104 (out
,err
) = p
.communicate()
106 lines
= out
.split('\n')
108 return lines
[0].strip()
112 import samba
.dckeytab
114 cmd_domain_export_keytab
= None
116 class cmd_domain_export_keytab(Command
):
117 """Dump Kerberos keys of the domain into a keytab."""
119 synopsis
= "%prog <keytab> [options]"
121 takes_optiongroups
= {
122 "sambaopts": options
.SambaOptions
,
123 "credopts": options
.CredentialsOptions
,
124 "versionopts": options
.VersionOptions
,
128 Option("--principal", help="extract only this principal", type=str),
131 takes_args
= ["keytab"]
133 def run(self
, keytab
, credopts
=None, sambaopts
=None, versionopts
=None, principal
=None):
134 lp
= sambaopts
.get_loadparm()
136 net
.export_keytab(keytab
=keytab
, principal
=principal
)
139 class cmd_domain_info(Command
):
140 """Print basic info about a domain and the DC passed as parameter."""
142 synopsis
= "%prog <ip_address> [options]"
147 takes_optiongroups
= {
148 "sambaopts": options
.SambaOptions
,
149 "credopts": options
.CredentialsOptions
,
150 "versionopts": options
.VersionOptions
,
153 takes_args
= ["address"]
155 def run(self
, address
, credopts
=None, sambaopts
=None, versionopts
=None):
156 lp
= sambaopts
.get_loadparm()
158 res
= netcmd_get_domain_infos_via_cldap(lp
, None, address
)
160 raise CommandError("Invalid IP address '" + address
+ "'!")
161 self
.outf
.write("Forest : %s\n" % res
.forest
)
162 self
.outf
.write("Domain : %s\n" % res
.dns_domain
)
163 self
.outf
.write("Netbios domain : %s\n" % res
.domain_name
)
164 self
.outf
.write("DC name : %s\n" % res
.pdc_dns_name
)
165 self
.outf
.write("DC netbios name : %s\n" % res
.pdc_name
)
166 self
.outf
.write("Server site : %s\n" % res
.server_site
)
167 self
.outf
.write("Client site : %s\n" % res
.client_site
)
170 class cmd_domain_provision(Command
):
171 """Provision a domain."""
173 synopsis
= "%prog [options]"
175 takes_optiongroups
= {
176 "sambaopts": options
.SambaOptions
,
177 "versionopts": options
.VersionOptions
,
181 Option("--interactive", help="Ask for names", action
="store_true"),
182 Option("--domain", type="string", metavar
="DOMAIN",
183 help="NetBIOS domain name to use"),
184 Option("--domain-guid", type="string", metavar
="GUID",
185 help="set domainguid (otherwise random)"),
186 Option("--domain-sid", type="string", metavar
="SID",
187 help="set domainsid (otherwise random)"),
188 Option("--ntds-guid", type="string", metavar
="GUID",
189 help="set NTDS object GUID (otherwise random)"),
190 Option("--invocationid", type="string", metavar
="GUID",
191 help="set invocationid (otherwise random)"),
192 Option("--host-name", type="string", metavar
="HOSTNAME",
193 help="set hostname"),
194 Option("--host-ip", type="string", metavar
="IPADDRESS",
195 help="set IPv4 ipaddress"),
196 Option("--host-ip6", type="string", metavar
="IP6ADDRESS",
197 help="set IPv6 ipaddress"),
198 Option("--site", type="string", metavar
="SITENAME",
199 help="set site name"),
200 Option("--adminpass", type="string", metavar
="PASSWORD",
201 help="choose admin password (otherwise random)"),
202 Option("--krbtgtpass", type="string", metavar
="PASSWORD",
203 help="choose krbtgt password (otherwise random)"),
204 Option("--machinepass", type="string", metavar
="PASSWORD",
205 help="choose machine password (otherwise random)"),
206 Option("--dns-backend", type="choice", metavar
="NAMESERVER-BACKEND",
207 choices
=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
208 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
209 "BIND9_FLATFILE uses bind9 text database to store zone information, "
210 "BIND9_DLZ uses samba4 AD to store zone information, "
211 "NONE skips the DNS setup entirely (not recommended)",
212 default
="SAMBA_INTERNAL"),
213 Option("--dnspass", type="string", metavar
="PASSWORD",
214 help="choose dns password (otherwise random)"),
215 Option("--ldapadminpass", type="string", metavar
="PASSWORD",
216 help="choose password to set between Samba and its LDAP backend (otherwise random)"),
217 Option("--root", type="string", metavar
="USERNAME",
218 help="choose 'root' unix username"),
219 Option("--nobody", type="string", metavar
="USERNAME",
220 help="choose 'nobody' user"),
221 Option("--users", type="string", metavar
="GROUPNAME",
222 help="choose 'users' group"),
223 Option("--quiet", help="Be quiet", action
="store_true"),
224 Option("--blank", action
="store_true",
225 help="do not add users or groups, just the structure"),
226 Option("--ldap-backend-type", type="choice", metavar
="LDAP-BACKEND-TYPE",
227 help="Test initialisation support for unsupported LDAP backend type (fedora-ds or openldap) DO NOT USE",
228 choices
=["fedora-ds", "openldap"]),
229 Option("--server-role", type="choice", metavar
="ROLE",
230 choices
=["domain controller", "dc", "member server", "member", "standalone"],
231 help="The server role (domain controller | dc | member server | member | standalone). Default is dc.",
232 default
="domain controller"),
233 Option("--function-level", type="choice", metavar
="FOR-FUN-LEVEL",
234 choices
=["2000", "2003", "2008", "2008_R2"],
235 help="The domain and forest function level (2000 | 2003 | 2008 | 2008_R2 - always native). Default is (Windows) 2008_R2 Native.",
237 Option("--base-schema", type="choice", metavar
="BASE-SCHEMA",
238 choices
=["2008_R2", "2008_R2_old", "2012", "2012_R2"],
239 help="The base schema files to use. Default is (Windows) 2008_R2.",
241 Option("--next-rid", type="int", metavar
="NEXTRID", default
=1000,
242 help="The initial nextRid value (only needed for upgrades). Default is 1000."),
243 Option("--partitions-only",
244 help="Configure Samba's partitions, but do not modify them (ie, join a BDC)", action
="store_true"),
245 Option("--targetdir", type="string", metavar
="DIR",
246 help="Set target directory"),
247 Option("--ol-mmr-urls", type="string", metavar
="LDAPSERVER",
248 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\""),
249 Option("--use-rfc2307", action
="store_true", help="Use AD to store posix attributes (default = no)"),
253 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",
254 action
="store_true"),
255 Option("--slapd-path", type="string", metavar
="SLAPD-PATH",
256 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."),
257 Option("--ldap-backend-extra-port", type="int", metavar
="LDAP-BACKEND-EXTRA-PORT", help="Additional TCP port for LDAP backend server (to use for replication)"),
258 Option("--ldap-backend-forced-uri", type="string", metavar
="LDAP-BACKEND-FORCED-URI",
259 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"),
260 Option("--ldap-backend-nosync", help="Configure LDAP backend not to call fsync() (for performance in test environments)", action
="store_true"),
264 Option("--use-ntvfs", action
="store_true", help="Use NTVFS for the fileserver (default = no)"),
265 Option("--use-xattrs", type="choice", choices
=["yes","no","auto"],
266 metavar
="[yes|no|auto]",
267 help="Define if we should use the native fs capabilities or a tdb file for "
268 "storing attributes likes ntacl when --use-ntvfs is set. "
269 "auto tries to make an inteligent guess based on the user rights and system capabilities",
273 if os
.getenv('TEST_LDAP', "no") == "yes":
274 takes_options
.extend(openldap_options
)
276 if samba
.is_ntvfs_fileserver_built():
277 takes_options
.extend(ntvfs_options
)
281 def run(self
, sambaopts
=None, versionopts
=None,
304 ldap_backend_type
=None,
308 partitions_only
=None,
315 ldap_backend_nosync
=None,
316 ldap_backend_extra_port
=None,
317 ldap_backend_forced_uri
=None,
318 ldap_dryrun_mode
=None,
321 self
.logger
= self
.get_logger("provision")
323 self
.logger
.setLevel(logging
.WARNING
)
325 self
.logger
.setLevel(logging
.INFO
)
327 lp
= sambaopts
.get_loadparm()
328 smbconf
= lp
.configfile
330 if dns_forwarder
is not None:
331 suggested_forwarder
= dns_forwarder
333 suggested_forwarder
= self
._get
_nameserver
_ip
()
334 if suggested_forwarder
is None:
335 suggested_forwarder
= "none"
337 if len(self
.raw_argv
) == 1:
341 from getpass
import getpass
344 def ask(prompt
, default
=None):
345 if default
is not None:
346 print "%s [%s]: " % (prompt
, default
),
348 print "%s: " % (prompt
,),
349 return sys
.stdin
.readline().rstrip("\n") or default
352 default
= socket
.getfqdn().split(".", 1)[1].upper()
355 realm
= ask("Realm", default
)
356 if realm
in (None, ""):
357 raise CommandError("No realm set!")
360 default
= realm
.split(".")[0]
363 domain
= ask("Domain", default
)
365 raise CommandError("No domain set!")
367 server_role
= ask("Server Role (dc, member, standalone)", "dc")
369 dns_backend
= ask("DNS backend (SAMBA_INTERNAL, BIND9_FLATFILE, BIND9_DLZ, NONE)", "SAMBA_INTERNAL")
370 if dns_backend
in (None, ''):
371 raise CommandError("No DNS backend set!")
373 if dns_backend
== "SAMBA_INTERNAL":
374 dns_forwarder
= ask("DNS forwarder IP address (write 'none' to disable forwarding)", suggested_forwarder
)
375 if dns_forwarder
.lower() in (None, 'none'):
376 suggested_forwarder
= None
380 adminpassplain
= getpass("Administrator password: ")
381 issue
= self
._adminpass
_issue
(adminpassplain
)
383 self
.errf
.write("%s.\n" % issue
)
385 adminpassverify
= getpass("Retype password: ")
386 if not adminpassplain
== adminpassverify
:
387 self
.errf
.write("Sorry, passwords do not match.\n")
389 adminpass
= adminpassplain
393 realm
= sambaopts
._lp
.get('realm')
395 raise CommandError("No realm set!")
397 raise CommandError("No domain set!")
400 issue
= self
._adminpass
_issue
(adminpass
)
402 raise CommandError(issue
)
404 self
.logger
.info("Administrator password will be set randomly!")
406 if function_level
== "2000":
407 dom_for_fun_level
= DS_DOMAIN_FUNCTION_2000
408 elif function_level
== "2003":
409 dom_for_fun_level
= DS_DOMAIN_FUNCTION_2003
410 elif function_level
== "2008":
411 dom_for_fun_level
= DS_DOMAIN_FUNCTION_2008
412 elif function_level
== "2008_R2":
413 dom_for_fun_level
= DS_DOMAIN_FUNCTION_2008_R2
415 if dns_backend
== "SAMBA_INTERNAL" and dns_forwarder
is None:
416 dns_forwarder
= suggested_forwarder
418 samdb_fill
= FILL_FULL
420 samdb_fill
= FILL_NT4SYNC
421 elif partitions_only
:
422 samdb_fill
= FILL_DRS
424 if targetdir
is not None:
425 if not os
.path
.isdir(targetdir
):
430 if use_xattrs
== "yes":
432 elif use_xattrs
== "auto" and use_ntvfs
== False:
434 elif use_ntvfs
== False:
435 raise CommandError("--use-xattrs=no requires --use-ntvfs (not supported for production use). "
436 "Please re-run with --use-xattrs omitted.")
437 elif use_xattrs
== "auto" and not lp
.get("posix:eadb"):
439 file = tempfile
.NamedTemporaryFile(dir=os
.path
.abspath(targetdir
))
441 file = tempfile
.NamedTemporaryFile(dir=os
.path
.abspath(os
.path
.dirname(lp
.get("private dir"))))
444 samba
.ntacls
.setntacl(lp
, file.name
,
445 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
448 self
.logger
.info("You are not root or your system does not support xattr, using tdb backend for attributes. ")
453 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.")
454 if ldap_backend_type
== "existing":
455 if ldap_backend_forced_uri
is not None:
456 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
)
458 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")
460 if ldap_backend_forced_uri
is not None:
461 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")
463 if domain_sid
is not None:
464 domain_sid
= security
.dom_sid(domain_sid
)
466 session
= system_session()
468 result
= provision(self
.logger
,
469 session
, smbconf
=smbconf
, targetdir
=targetdir
,
470 samdb_fill
=samdb_fill
, realm
=realm
, domain
=domain
,
471 domainguid
=domain_guid
, domainsid
=domain_sid
,
473 hostip
=host_ip
, hostip6
=host_ip6
,
474 sitename
=site
, ntdsguid
=ntds_guid
,
475 invocationid
=invocationid
, adminpass
=adminpass
,
476 krbtgtpass
=krbtgtpass
, machinepass
=machinepass
,
477 dns_backend
=dns_backend
, dns_forwarder
=dns_forwarder
,
478 dnspass
=dnspass
, root
=root
, nobody
=nobody
,
480 serverrole
=server_role
, dom_for_fun_level
=dom_for_fun_level
,
481 backend_type
=ldap_backend_type
,
482 ldapadminpass
=ldapadminpass
, ol_mmr_urls
=ol_mmr_urls
, slapd_path
=slapd_path
,
483 useeadb
=eadb
, next_rid
=next_rid
, lp
=lp
, use_ntvfs
=use_ntvfs
,
484 use_rfc2307
=use_rfc2307
, skip_sysvolacl
=False,
485 ldap_backend_extra_port
=ldap_backend_extra_port
,
486 ldap_backend_forced_uri
=ldap_backend_forced_uri
,
487 nosync
=ldap_backend_nosync
, ldap_dryrun_mode
=ldap_dryrun_mode
,
488 base_schema
=base_schema
)
490 except ProvisioningError
, e
:
491 raise CommandError("Provision failed", e
)
493 result
.report_logger(self
.logger
)
495 def _get_nameserver_ip(self
):
496 """Grab the nameserver IP address from /etc/resolv.conf."""
498 RESOLV_CONF
="/etc/resolv.conf"
500 if not path
.isfile(RESOLV_CONF
):
501 self
.logger
.warning("Failed to locate %s" % RESOLV_CONF
)
506 handle
= open(RESOLV_CONF
, 'r')
508 if not line
.startswith('nameserver'):
510 # we want the last non-space continuous string of the line
511 return line
.strip().split()[-1]
513 if handle
is not None:
516 self
.logger
.warning("No nameserver found in %s" % RESOLV_CONF
)
518 def _adminpass_issue(self
, adminpass
):
519 """Returns error string for a bad administrator password,
520 or None if acceptable"""
522 if len(adminpass
.decode('utf-8')) < DEFAULT_MIN_PWD_LENGTH
:
523 return "Administrator password does not meet the default minimum" \
524 " password length requirement (%d characters)" \
525 % DEFAULT_MIN_PWD_LENGTH
526 elif not samba
.check_password_quality(adminpass
):
527 return "Administrator password does not meet the default" \
533 class cmd_domain_dcpromo(Command
):
534 """Promote an existing domain member or NT4 PDC to an AD DC."""
536 synopsis
= "%prog <dnsdomain> [DC|RODC] [options]"
538 takes_optiongroups
= {
539 "sambaopts": options
.SambaOptions
,
540 "versionopts": options
.VersionOptions
,
541 "credopts": options
.CredentialsOptions
,
545 Option("--server", help="DC to join", type=str),
546 Option("--site", help="site to join", type=str),
547 Option("--targetdir", help="where to store provision", type=str),
548 Option("--domain-critical-only",
549 help="only replicate critical domain objects",
550 action
="store_true"),
551 Option("--machinepass", type=str, metavar
="PASSWORD",
552 help="choose machine password (otherwise random)"),
553 Option("--dns-backend", type="choice", metavar
="NAMESERVER-BACKEND",
554 choices
=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
555 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
556 "BIND9_DLZ uses samba4 AD to store zone information, "
557 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
558 default
="SAMBA_INTERNAL"),
559 Option("--quiet", help="Be quiet", action
="store_true"),
560 Option("--verbose", help="Be verbose", action
="store_true")
564 Option("--use-ntvfs", action
="store_true", help="Use NTVFS for the fileserver (default = no)"),
567 if samba
.is_ntvfs_fileserver_built():
568 takes_options
.extend(ntvfs_options
)
571 takes_args
= ["domain", "role?"]
573 def run(self
, domain
, role
=None, sambaopts
=None, credopts
=None,
574 versionopts
=None, server
=None, site
=None, targetdir
=None,
575 domain_critical_only
=False, parent_domain
=None, machinepass
=None,
576 use_ntvfs
=False, dns_backend
=None,
577 quiet
=False, verbose
=False):
578 lp
= sambaopts
.get_loadparm()
579 creds
= credopts
.get_credentials(lp
)
580 net
= Net(creds
, lp
, server
=credopts
.ipaddress
)
582 logger
= self
.get_logger()
584 logger
.setLevel(logging
.DEBUG
)
586 logger
.setLevel(logging
.WARNING
)
588 logger
.setLevel(logging
.INFO
)
590 netbios_name
= lp
.get("netbios name")
596 join_DC(logger
=logger
, server
=server
, creds
=creds
, lp
=lp
, domain
=domain
,
597 site
=site
, netbios_name
=netbios_name
, targetdir
=targetdir
,
598 domain_critical_only
=domain_critical_only
,
599 machinepass
=machinepass
, use_ntvfs
=use_ntvfs
,
600 dns_backend
=dns_backend
,
601 promote_existing
=True)
603 join_RODC(logger
=logger
, server
=server
, creds
=creds
, lp
=lp
, domain
=domain
,
604 site
=site
, netbios_name
=netbios_name
, targetdir
=targetdir
,
605 domain_critical_only
=domain_critical_only
,
606 machinepass
=machinepass
, use_ntvfs
=use_ntvfs
, dns_backend
=dns_backend
,
607 promote_existing
=True)
609 raise CommandError("Invalid role '%s' (possible values: DC, RODC)" % role
)
612 class cmd_domain_join(Command
):
613 """Join domain as either member or backup domain controller."""
615 synopsis
= "%prog <dnsdomain> [DC|RODC|MEMBER|SUBDOMAIN] [options]"
617 takes_optiongroups
= {
618 "sambaopts": options
.SambaOptions
,
619 "versionopts": options
.VersionOptions
,
620 "credopts": options
.CredentialsOptions
,
624 Option("--server", help="DC to join", type=str),
625 Option("--site", help="site to join", type=str),
626 Option("--targetdir", help="where to store provision", type=str),
627 Option("--parent-domain", help="parent domain to create subdomain under", type=str),
628 Option("--domain-critical-only",
629 help="only replicate critical domain objects",
630 action
="store_true"),
631 Option("--machinepass", type=str, metavar
="PASSWORD",
632 help="choose machine password (otherwise random)"),
633 Option("--adminpass", type="string", metavar
="PASSWORD",
634 help="choose adminstrator password when joining as a subdomain (otherwise random)"),
635 Option("--dns-backend", type="choice", metavar
="NAMESERVER-BACKEND",
636 choices
=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
637 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
638 "BIND9_DLZ uses samba4 AD to store zone information, "
639 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
640 default
="SAMBA_INTERNAL"),
641 Option("--quiet", help="Be quiet", action
="store_true"),
642 Option("--verbose", help="Be verbose", action
="store_true")
646 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
649 if samba
.is_ntvfs_fileserver_built():
650 takes_options
.extend(ntvfs_options
)
652 takes_args
= ["domain", "role?"]
654 def run(self
, domain
, role
=None, sambaopts
=None, credopts
=None,
655 versionopts
=None, server
=None, site
=None, targetdir
=None,
656 domain_critical_only
=False, parent_domain
=None, machinepass
=None,
657 use_ntvfs
=False, dns_backend
=None, adminpass
=None,
658 quiet
=False, verbose
=False):
659 lp
= sambaopts
.get_loadparm()
660 creds
= credopts
.get_credentials(lp
)
661 net
= Net(creds
, lp
, server
=credopts
.ipaddress
)
664 site
= "Default-First-Site-Name"
666 logger
= self
.get_logger()
668 logger
.setLevel(logging
.DEBUG
)
670 logger
.setLevel(logging
.WARNING
)
672 logger
.setLevel(logging
.INFO
)
674 netbios_name
= lp
.get("netbios name")
679 if role
is None or role
== "MEMBER":
680 (join_password
, sid
, domain_name
) = net
.join_member(
681 domain
, netbios_name
, LIBNET_JOIN_AUTOMATIC
,
682 machinepass
=machinepass
)
684 self
.errf
.write("Joined domain %s (%s)\n" % (domain_name
, sid
))
686 join_DC(logger
=logger
, server
=server
, creds
=creds
, lp
=lp
, domain
=domain
,
687 site
=site
, netbios_name
=netbios_name
, targetdir
=targetdir
,
688 domain_critical_only
=domain_critical_only
,
689 machinepass
=machinepass
, use_ntvfs
=use_ntvfs
, dns_backend
=dns_backend
)
691 join_RODC(logger
=logger
, server
=server
, creds
=creds
, lp
=lp
, domain
=domain
,
692 site
=site
, netbios_name
=netbios_name
, targetdir
=targetdir
,
693 domain_critical_only
=domain_critical_only
,
694 machinepass
=machinepass
, use_ntvfs
=use_ntvfs
,
695 dns_backend
=dns_backend
)
696 elif role
== "SUBDOMAIN":
698 logger
.info("Administrator password will be set randomly!")
700 netbios_domain
= lp
.get("workgroup")
701 if parent_domain
is None:
702 parent_domain
= ".".join(domain
.split(".")[1:])
703 join_subdomain(logger
=logger
, server
=server
, creds
=creds
, lp
=lp
, dnsdomain
=domain
,
704 parent_domain
=parent_domain
, site
=site
,
705 netbios_name
=netbios_name
, netbios_domain
=netbios_domain
,
706 targetdir
=targetdir
, machinepass
=machinepass
,
707 use_ntvfs
=use_ntvfs
, dns_backend
=dns_backend
,
710 raise CommandError("Invalid role '%s' (possible values: MEMBER, DC, RODC, SUBDOMAIN)" % role
)
713 class cmd_domain_demote(Command
):
714 """Demote ourselves from the role of Domain Controller."""
716 synopsis
= "%prog [options]"
719 Option("--server", help="writable DC to write demotion changes on", type=str),
720 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
721 metavar
="URL", dest
="H"),
722 Option("--remove-other-dead-server", help="Dead DC (name or NTDS GUID) "
723 "to remove ALL references to (rather than this DC)", type=str),
724 Option("--quiet", help="Be quiet", action
="store_true"),
725 Option("--verbose", help="Be verbose", action
="store_true"),
728 takes_optiongroups
= {
729 "sambaopts": options
.SambaOptions
,
730 "credopts": options
.CredentialsOptions
,
731 "versionopts": options
.VersionOptions
,
734 def run(self
, sambaopts
=None, credopts
=None,
735 versionopts
=None, server
=None,
736 remove_other_dead_server
=None, H
=None,
737 verbose
=False, quiet
=False):
738 lp
= sambaopts
.get_loadparm()
739 creds
= credopts
.get_credentials(lp
)
740 net
= Net(creds
, lp
, server
=credopts
.ipaddress
)
742 logger
= self
.get_logger()
744 logger
.setLevel(logging
.DEBUG
)
746 logger
.setLevel(logging
.WARNING
)
748 logger
.setLevel(logging
.INFO
)
750 if remove_other_dead_server
is not None:
751 if server
is not None:
752 samdb
= SamDB(url
="ldap://%s" % server
,
753 session_info
=system_session(),
754 credentials
=creds
, lp
=lp
)
756 samdb
= SamDB(url
=H
, session_info
=system_session(), credentials
=creds
, lp
=lp
)
758 remove_dc
.remove_dc(samdb
, logger
, remove_other_dead_server
)
759 except remove_dc
.DemoteException
as err
:
760 raise CommandError("Demote failed: %s" % err
)
763 netbios_name
= lp
.get("netbios name")
764 samdb
= SamDB(url
=H
, session_info
=system_session(), credentials
=creds
, lp
=lp
)
766 res
= samdb
.search(expression
='(&(objectClass=computer)(serverReferenceBL=*))', attrs
=["dnsHostName", "name"])
768 raise CommandError("Unable to search for servers")
771 raise CommandError("You are the latest server in the domain")
775 if str(e
["name"]).lower() != netbios_name
.lower():
776 server
= e
["dnsHostName"]
779 ntds_guid
= samdb
.get_ntds_GUID()
780 msg
= samdb
.search(base
=str(samdb
.get_config_basedn()),
781 scope
=ldb
.SCOPE_SUBTREE
, expression
="(objectGUID=%s)" % ntds_guid
,
783 if len(msg
) == 0 or "options" not in msg
[0]:
784 raise CommandError("Failed to find options on %s" % ntds_guid
)
787 dsa_options
= int(str(msg
[0]['options']))
789 res
= samdb
.search(expression
="(fSMORoleOwner=%s)" % str(ntds_dn
),
790 controls
=["search_options:1:2"])
793 raise CommandError("Current DC is still the owner of %d role(s), use the role command to transfer roles to another DC" % len(res
))
795 self
.errf
.write("Using %s as partner server for the demotion\n" %
797 (drsuapiBind
, drsuapi_handle
, supportedExtensions
) = drsuapi_connect(server
, lp
, creds
)
799 self
.errf
.write("Deactivating inbound replication\n")
804 if not (dsa_options
& DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL
) and not samdb
.am_rodc():
805 dsa_options |
= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
806 nmsg
["options"] = ldb
.MessageElement(str(dsa_options
), ldb
.FLAG_MOD_REPLACE
, "options")
810 self
.errf
.write("Asking partner server %s to synchronize from us\n"
812 for part
in (samdb
.get_schema_basedn(),
813 samdb
.get_config_basedn(),
814 samdb
.get_root_basedn()):
815 nc
= drsuapi
.DsReplicaObjectIdentifier()
818 req1
= drsuapi
.DsReplicaSyncRequest1()
819 req1
.naming_context
= nc
;
820 req1
.options
= drsuapi
.DRSUAPI_DRS_WRIT_REP
821 req1
.source_dsa_guid
= misc
.GUID(ntds_guid
)
824 drsuapiBind
.DsReplicaSync(drsuapi_handle
, 1, req1
)
825 except RuntimeError as (werr
, string
):
826 if werr
== werror
.WERR_DS_DRA_NO_REPLICA
:
830 "Error while replicating out last local changes from '%s' for demotion, "
831 "re-enabling inbound replication\n" % part
)
832 dsa_options ^
= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
833 nmsg
["options"] = ldb
.MessageElement(str(dsa_options
), ldb
.FLAG_MOD_REPLACE
, "options")
835 raise CommandError("Error while sending a DsReplicaSync for partition '%s'" % str(part
), string
)
837 remote_samdb
= SamDB(url
="ldap://%s" % server
,
838 session_info
=system_session(),
839 credentials
=creds
, lp
=lp
)
841 self
.errf
.write("Changing userControl and container\n")
842 res
= remote_samdb
.search(base
=str(remote_samdb
.domain_dn()),
843 expression
="(&(objectClass=user)(sAMAccountName=%s$))" %
844 netbios_name
.upper(),
845 attrs
=["userAccountControl"])
847 uac
= int(str(res
[0]["userAccountControl"]))
850 if not (dsa_options
& DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL
) and not samdb
.am_rodc():
852 "Error while demoting, re-enabling inbound replication\n")
853 dsa_options ^
= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
854 nmsg
["options"] = ldb
.MessageElement(str(dsa_options
), ldb
.FLAG_MOD_REPLACE
, "options")
856 raise CommandError("Error while changing account control", e
)
859 if not (dsa_options
& DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL
) and not samdb
.am_rodc():
861 "Error while demoting, re-enabling inbound replication")
862 dsa_options ^
= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
863 nmsg
["options"] = ldb
.MessageElement(str(dsa_options
), ldb
.FLAG_MOD_REPLACE
, "options")
865 raise CommandError("Unable to find object with samaccountName = %s$"
866 " in the remote dc" % netbios_name
.upper())
870 uac
&= ~
(UF_SERVER_TRUST_ACCOUNT|UF_TRUSTED_FOR_DELEGATION|UF_PARTIAL_SECRETS_ACCOUNT
)
871 uac |
= UF_WORKSTATION_TRUST_ACCOUNT
876 msg
["userAccountControl"] = ldb
.MessageElement("%d" % uac
,
877 ldb
.FLAG_MOD_REPLACE
,
878 "userAccountControl")
880 remote_samdb
.modify(msg
)
882 if not (dsa_options
& DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL
) and not samdb
.am_rodc():
884 "Error while demoting, re-enabling inbound replication")
885 dsa_options ^
= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
886 nmsg
["options"] = ldb
.MessageElement(str(dsa_options
), ldb
.FLAG_MOD_REPLACE
, "options")
889 raise CommandError("Error while changing account control", e
)
891 parent
= msg
.dn
.parent()
892 dc_name
= res
[0].dn
.get_rdn_value()
893 rdn
= "CN=%s" % dc_name
895 # Let's move to the Computer container
899 computer_dn
= ldb
.Dn(remote_samdb
, "CN=Computers,%s" % str(remote_samdb
.domain_dn()))
900 res
= remote_samdb
.search(base
=computer_dn
, expression
=rdn
, scope
=ldb
.SCOPE_ONELEVEL
)
903 res
= remote_samdb
.search(base
=computer_dn
, expression
="%s-%d" % (rdn
, i
),
904 scope
=ldb
.SCOPE_ONELEVEL
)
905 while(len(res
) != 0 and i
< 100):
907 res
= remote_samdb
.search(base
=computer_dn
, expression
="%s-%d" % (rdn
, i
),
908 scope
=ldb
.SCOPE_ONELEVEL
)
911 if not (dsa_options
& DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL
) and not samdb
.am_rodc():
913 "Error while demoting, re-enabling inbound replication\n")
914 dsa_options ^
= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
915 nmsg
["options"] = ldb
.MessageElement(str(dsa_options
), ldb
.FLAG_MOD_REPLACE
, "options")
921 msg
["userAccountControl"] = ldb
.MessageElement("%d" % uac
,
922 ldb
.FLAG_MOD_REPLACE
,
923 "userAccountControl")
925 remote_samdb
.modify(msg
)
927 raise CommandError("Unable to find a slot for renaming %s,"
928 " all names from %s-1 to %s-%d seemed used" %
929 (str(dc_dn
), rdn
, rdn
, i
- 9))
931 newrdn
= "%s-%d" % (rdn
, i
)
934 newdn
= ldb
.Dn(remote_samdb
, "%s,%s" % (newrdn
, str(computer_dn
)))
935 remote_samdb
.rename(dc_dn
, newdn
)
937 if not (dsa_options
& DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL
) and not samdb
.am_rodc():
939 "Error while demoting, re-enabling inbound replication\n")
940 dsa_options ^
= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
941 nmsg
["options"] = ldb
.MessageElement(str(dsa_options
), ldb
.FLAG_MOD_REPLACE
, "options")
947 msg
["userAccountControl"] = ldb
.MessageElement("%d" % uac
,
948 ldb
.FLAG_MOD_REPLACE
,
949 "userAccountControl")
951 remote_samdb
.modify(msg
)
952 raise CommandError("Error while renaming %s to %s" % (str(dc_dn
), str(newdn
)), e
)
955 server_dsa_dn
= samdb
.get_serverName()
956 domain
= remote_samdb
.get_root_basedn()
959 req1
= drsuapi
.DsRemoveDSServerRequest1()
960 req1
.server_dn
= str(server_dsa_dn
)
961 req1
.domain_dn
= str(domain
)
964 drsuapiBind
.DsRemoveDSServer(drsuapi_handle
, 1, req1
)
965 except RuntimeError as (werr
, string
):
966 if not (dsa_options
& DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL
) and not samdb
.am_rodc():
968 "Error while demoting, re-enabling inbound replication\n")
969 dsa_options ^
= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
970 nmsg
["options"] = ldb
.MessageElement(str(dsa_options
), ldb
.FLAG_MOD_REPLACE
, "options")
976 msg
["userAccountControl"] = ldb
.MessageElement("%d" % uac
,
977 ldb
.FLAG_MOD_REPLACE
,
978 "userAccountControl")
979 remote_samdb
.modify(msg
)
980 remote_samdb
.rename(newdn
, dc_dn
)
981 if werr
== werror
.WERR_DS_DRA_NO_REPLICA
:
982 raise CommandError("The DC %s is not present on (already removed from) the remote server: " % server_dsa_dn
, e
)
984 raise CommandError("Error while sending a removeDsServer of %s: " % server_dsa_dn
, e
)
986 remove_dc
.remove_sysvol_references(remote_samdb
, logger
, dc_name
)
988 # These are objects under the computer account that should be deleted
989 for s
in ("CN=Enterprise,CN=NTFRS Subscriptions",
990 "CN=%s, CN=NTFRS Subscriptions" % lp
.get("realm"),
991 "CN=Domain system Volumes (SYSVOL Share), CN=NTFRS Subscriptions",
992 "CN=NTFRS Subscriptions"):
994 remote_samdb
.delete(ldb
.Dn(remote_samdb
,
995 "%s,%s" % (s
, str(newdn
))))
996 except ldb
.LdbError
, l
:
999 self
.errf
.write("Demote successful\n")
1002 class cmd_domain_level(Command
):
1003 """Raise domain and forest function levels."""
1005 synopsis
= "%prog (show|raise <options>) [options]"
1007 takes_optiongroups
= {
1008 "sambaopts": options
.SambaOptions
,
1009 "credopts": options
.CredentialsOptions
,
1010 "versionopts": options
.VersionOptions
,
1014 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1015 metavar
="URL", dest
="H"),
1016 Option("--quiet", help="Be quiet", action
="store_true"),
1017 Option("--forest-level", type="choice", choices
=["2003", "2008", "2008_R2", "2012", "2012_R2"],
1018 help="The forest function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)"),
1019 Option("--domain-level", type="choice", choices
=["2003", "2008", "2008_R2", "2012", "2012_R2"],
1020 help="The domain function level (2003 | 2008 | 2008_R2 | 2012 | 2012_R2)")
1023 takes_args
= ["subcommand"]
1025 def run(self
, subcommand
, H
=None, forest_level
=None, domain_level
=None,
1026 quiet
=False, credopts
=None, sambaopts
=None, versionopts
=None):
1027 lp
= sambaopts
.get_loadparm()
1028 creds
= credopts
.get_credentials(lp
, fallback_machine
=True)
1030 samdb
= SamDB(url
=H
, session_info
=system_session(),
1031 credentials
=creds
, lp
=lp
)
1033 domain_dn
= samdb
.domain_dn()
1035 res_forest
= samdb
.search("CN=Partitions,%s" % samdb
.get_config_basedn(),
1036 scope
=ldb
.SCOPE_BASE
, attrs
=["msDS-Behavior-Version"])
1037 assert len(res_forest
) == 1
1039 res_domain
= samdb
.search(domain_dn
, scope
=ldb
.SCOPE_BASE
,
1040 attrs
=["msDS-Behavior-Version", "nTMixedDomain"])
1041 assert len(res_domain
) == 1
1043 res_dc_s
= samdb
.search("CN=Sites,%s" % samdb
.get_config_basedn(),
1044 scope
=ldb
.SCOPE_SUBTREE
, expression
="(objectClass=nTDSDSA)",
1045 attrs
=["msDS-Behavior-Version"])
1046 assert len(res_dc_s
) >= 1
1048 # default values, since "msDS-Behavior-Version" does not exist on Windows 2000 AD
1049 level_forest
= DS_DOMAIN_FUNCTION_2000
1050 level_domain
= DS_DOMAIN_FUNCTION_2000
1052 if "msDS-Behavior-Version" in res_forest
[0]:
1053 level_forest
= int(res_forest
[0]["msDS-Behavior-Version"][0])
1054 if "msDS-Behavior-Version" in res_domain
[0]:
1055 level_domain
= int(res_domain
[0]["msDS-Behavior-Version"][0])
1056 level_domain_mixed
= int(res_domain
[0]["nTMixedDomain"][0])
1059 for msg
in res_dc_s
:
1060 if "msDS-Behavior-Version" in msg
:
1061 if min_level_dc
is None or int(msg
["msDS-Behavior-Version"][0]) < min_level_dc
:
1062 min_level_dc
= int(msg
["msDS-Behavior-Version"][0])
1064 min_level_dc
= DS_DOMAIN_FUNCTION_2000
1065 # well, this is the least
1068 if level_forest
< DS_DOMAIN_FUNCTION_2000
or level_domain
< DS_DOMAIN_FUNCTION_2000
:
1069 raise CommandError("Domain and/or forest function level(s) is/are invalid. Correct them or reprovision!")
1070 if min_level_dc
< DS_DOMAIN_FUNCTION_2000
:
1071 raise CommandError("Lowest function level of a DC is invalid. Correct this or reprovision!")
1072 if level_forest
> level_domain
:
1073 raise CommandError("Forest function level is higher than the domain level(s). Correct this or reprovision!")
1074 if level_domain
> min_level_dc
:
1075 raise CommandError("Domain function level is higher than the lowest function level of a DC. Correct this or reprovision!")
1077 if subcommand
== "show":
1078 self
.message("Domain and forest function level for domain '%s'" % domain_dn
)
1079 if level_forest
== DS_DOMAIN_FUNCTION_2000
and level_domain_mixed
!= 0:
1080 self
.message("\nATTENTION: You run SAMBA 4 on a forest function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1081 if level_domain
== DS_DOMAIN_FUNCTION_2000
and level_domain_mixed
!= 0:
1082 self
.message("\nATTENTION: You run SAMBA 4 on a domain function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
1083 if min_level_dc
== DS_DOMAIN_FUNCTION_2000
and level_domain_mixed
!= 0:
1084 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)!")
1088 if level_forest
== DS_DOMAIN_FUNCTION_2000
:
1090 elif level_forest
== DS_DOMAIN_FUNCTION_2003_MIXED
:
1091 outstr
= "2003 with mixed domains/interim (NT4 DC support)"
1092 elif level_forest
== DS_DOMAIN_FUNCTION_2003
:
1094 elif level_forest
== DS_DOMAIN_FUNCTION_2008
:
1096 elif level_forest
== DS_DOMAIN_FUNCTION_2008_R2
:
1098 elif level_forest
== DS_DOMAIN_FUNCTION_2012
:
1100 elif level_forest
== DS_DOMAIN_FUNCTION_2012_R2
:
1103 outstr
= "higher than 2012 R2"
1104 self
.message("Forest function level: (Windows) " + outstr
)
1106 if level_domain
== DS_DOMAIN_FUNCTION_2000
and level_domain_mixed
!= 0:
1107 outstr
= "2000 mixed (NT4 DC support)"
1108 elif level_domain
== DS_DOMAIN_FUNCTION_2000
and level_domain_mixed
== 0:
1110 elif level_domain
== DS_DOMAIN_FUNCTION_2003_MIXED
:
1111 outstr
= "2003 with mixed domains/interim (NT4 DC support)"
1112 elif level_domain
== DS_DOMAIN_FUNCTION_2003
:
1114 elif level_domain
== DS_DOMAIN_FUNCTION_2008
:
1116 elif level_domain
== DS_DOMAIN_FUNCTION_2008_R2
:
1118 elif level_domain
== DS_DOMAIN_FUNCTION_2012
:
1120 elif level_domain
== DS_DOMAIN_FUNCTION_2012_R2
:
1123 outstr
= "higher than 2012 R2"
1124 self
.message("Domain function level: (Windows) " + outstr
)
1126 if min_level_dc
== DS_DOMAIN_FUNCTION_2000
:
1128 elif min_level_dc
== DS_DOMAIN_FUNCTION_2003
:
1130 elif min_level_dc
== DS_DOMAIN_FUNCTION_2008
:
1132 elif min_level_dc
== DS_DOMAIN_FUNCTION_2008_R2
:
1134 elif min_level_dc
== DS_DOMAIN_FUNCTION_2012
:
1136 elif min_level_dc
== DS_DOMAIN_FUNCTION_2012_R2
:
1139 outstr
= "higher than 2012 R2"
1140 self
.message("Lowest function level of a DC: (Windows) " + outstr
)
1142 elif subcommand
== "raise":
1145 if domain_level
is not None:
1146 if domain_level
== "2003":
1147 new_level_domain
= DS_DOMAIN_FUNCTION_2003
1148 elif domain_level
== "2008":
1149 new_level_domain
= DS_DOMAIN_FUNCTION_2008
1150 elif domain_level
== "2008_R2":
1151 new_level_domain
= DS_DOMAIN_FUNCTION_2008_R2
1152 elif domain_level
== "2012":
1153 new_level_domain
= DS_DOMAIN_FUNCTION_2012
1154 elif domain_level
== "2012_R2":
1155 new_level_domain
= DS_DOMAIN_FUNCTION_2012_R2
1157 if new_level_domain
<= level_domain
and level_domain_mixed
== 0:
1158 raise CommandError("Domain function level can't be smaller than or equal to the actual one!")
1159 if new_level_domain
> min_level_dc
:
1160 raise CommandError("Domain function level can't be higher than the lowest function level of a DC!")
1162 # Deactivate mixed/interim domain support
1163 if level_domain_mixed
!= 0:
1164 # Directly on the base DN
1166 m
.dn
= ldb
.Dn(samdb
, domain_dn
)
1167 m
["nTMixedDomain"] = ldb
.MessageElement("0",
1168 ldb
.FLAG_MOD_REPLACE
, "nTMixedDomain")
1172 m
.dn
= ldb
.Dn(samdb
, "CN=" + lp
.get("workgroup") + ",CN=Partitions,%s" % samdb
.get_config_basedn())
1173 m
["nTMixedDomain"] = ldb
.MessageElement("0",
1174 ldb
.FLAG_MOD_REPLACE
, "nTMixedDomain")
1177 except ldb
.LdbError
, (enum
, emsg
):
1178 if enum
!= ldb
.ERR_UNWILLING_TO_PERFORM
:
1181 # Directly on the base DN
1183 m
.dn
= ldb
.Dn(samdb
, domain_dn
)
1184 m
["msDS-Behavior-Version"]= ldb
.MessageElement(
1185 str(new_level_domain
), ldb
.FLAG_MOD_REPLACE
,
1186 "msDS-Behavior-Version")
1190 m
.dn
= ldb
.Dn(samdb
, "CN=" + lp
.get("workgroup")
1191 + ",CN=Partitions,%s" % samdb
.get_config_basedn())
1192 m
["msDS-Behavior-Version"]= ldb
.MessageElement(
1193 str(new_level_domain
), ldb
.FLAG_MOD_REPLACE
,
1194 "msDS-Behavior-Version")
1197 except ldb
.LdbError
, (enum
, emsg
):
1198 if enum
!= ldb
.ERR_UNWILLING_TO_PERFORM
:
1201 level_domain
= new_level_domain
1202 msgs
.append("Domain function level changed!")
1204 if forest_level
is not None:
1205 if forest_level
== "2003":
1206 new_level_forest
= DS_DOMAIN_FUNCTION_2003
1207 elif forest_level
== "2008":
1208 new_level_forest
= DS_DOMAIN_FUNCTION_2008
1209 elif forest_level
== "2008_R2":
1210 new_level_forest
= DS_DOMAIN_FUNCTION_2008_R2
1211 elif forest_level
== "2012":
1212 new_level_forest
= DS_DOMAIN_FUNCTION_2012
1213 elif forest_level
== "2012_R2":
1214 new_level_forest
= DS_DOMAIN_FUNCTION_2012_R2
1216 if new_level_forest
<= level_forest
:
1217 raise CommandError("Forest function level can't be smaller than or equal to the actual one!")
1218 if new_level_forest
> level_domain
:
1219 raise CommandError("Forest function level can't be higher than the domain function level(s). Please raise it/them first!")
1222 m
.dn
= ldb
.Dn(samdb
, "CN=Partitions,%s" % samdb
.get_config_basedn())
1223 m
["msDS-Behavior-Version"]= ldb
.MessageElement(
1224 str(new_level_forest
), ldb
.FLAG_MOD_REPLACE
,
1225 "msDS-Behavior-Version")
1227 msgs
.append("Forest function level changed!")
1228 msgs
.append("All changes applied successfully!")
1229 self
.message("\n".join(msgs
))
1231 raise CommandError("invalid argument: '%s' (choose from 'show', 'raise')" % subcommand
)
1234 class cmd_domain_passwordsettings(Command
):
1235 """Set password settings.
1237 Password complexity, password lockout policy, history length,
1238 minimum password length, the minimum and maximum password age) on
1239 a Samba AD DC server.
1241 Use against a Windows DC is possible, but group policy will override it.
1244 synopsis
= "%prog (show|set <options>) [options]"
1246 takes_optiongroups
= {
1247 "sambaopts": options
.SambaOptions
,
1248 "versionopts": options
.VersionOptions
,
1249 "credopts": options
.CredentialsOptions
,
1253 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1254 metavar
="URL", dest
="H"),
1255 Option("--quiet", help="Be quiet", action
="store_true"),
1256 Option("--complexity", type="choice", choices
=["on","off","default"],
1257 help="The password complexity (on | off | default). Default is 'on'"),
1258 Option("--store-plaintext", type="choice", choices
=["on","off","default"],
1259 help="Store plaintext passwords where account have 'store passwords with reversible encryption' set (on | off | default). Default is 'off'"),
1260 Option("--history-length",
1261 help="The password history length (<integer> | default). Default is 24.", type=str),
1262 Option("--min-pwd-length",
1263 help="The minimum password length (<integer> | default). Default is 7.", type=str),
1264 Option("--min-pwd-age",
1265 help="The minimum password age (<integer in days> | default). Default is 1.", type=str),
1266 Option("--max-pwd-age",
1267 help="The maximum password age (<integer in days> | default). Default is 43.", type=str),
1268 Option("--account-lockout-duration",
1269 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),
1270 Option("--account-lockout-threshold",
1271 help="The number of bad password attempts allowed before locking out the account (<integer> | default). Default is 0 (never lock out).", type=str),
1272 Option("--reset-account-lockout-after",
1273 help="After this time is elapsed, the recorded number of attempts restarts from zero (<integer> | default). Default is 30.", type=str),
1276 takes_args
= ["subcommand"]
1278 def run(self
, subcommand
, H
=None, min_pwd_age
=None, max_pwd_age
=None,
1279 quiet
=False, complexity
=None, store_plaintext
=None, history_length
=None,
1280 min_pwd_length
=None, account_lockout_duration
=None, account_lockout_threshold
=None,
1281 reset_account_lockout_after
=None, credopts
=None, sambaopts
=None,
1283 lp
= sambaopts
.get_loadparm()
1284 creds
= credopts
.get_credentials(lp
)
1286 samdb
= SamDB(url
=H
, session_info
=system_session(),
1287 credentials
=creds
, lp
=lp
)
1289 domain_dn
= samdb
.domain_dn()
1290 res
= samdb
.search(domain_dn
, scope
=ldb
.SCOPE_BASE
,
1291 attrs
=["pwdProperties", "pwdHistoryLength", "minPwdLength",
1292 "minPwdAge", "maxPwdAge", "lockoutDuration", "lockoutThreshold",
1293 "lockOutObservationWindow"])
1294 assert(len(res
) == 1)
1296 pwd_props
= int(res
[0]["pwdProperties"][0])
1297 pwd_hist_len
= int(res
[0]["pwdHistoryLength"][0])
1298 cur_min_pwd_len
= int(res
[0]["minPwdLength"][0])
1300 cur_min_pwd_age
= int(abs(int(res
[0]["minPwdAge"][0])) / (1e7
* 60 * 60 * 24))
1301 if int(res
[0]["maxPwdAge"][0]) == -0x8000000000000000:
1304 cur_max_pwd_age
= int(abs(int(res
[0]["maxPwdAge"][0])) / (1e7
* 60 * 60 * 24))
1305 cur_account_lockout_threshold
= int(res
[0]["lockoutThreshold"][0])
1307 if int(res
[0]["lockoutDuration"][0]) == -0x8000000000000000:
1308 cur_account_lockout_duration
= 0
1310 cur_account_lockout_duration
= abs(int(res
[0]["lockoutDuration"][0])) / (1e7
* 60)
1311 cur_reset_account_lockout_after
= abs(int(res
[0]["lockOutObservationWindow"][0])) / (1e7
* 60)
1312 except Exception, e
:
1313 raise CommandError("Could not retrieve password properties!", e
)
1315 if subcommand
== "show":
1316 self
.message("Password informations for domain '%s'" % domain_dn
)
1318 if pwd_props
& DOMAIN_PASSWORD_COMPLEX
!= 0:
1319 self
.message("Password complexity: on")
1321 self
.message("Password complexity: off")
1322 if pwd_props
& DOMAIN_PASSWORD_STORE_CLEARTEXT
!= 0:
1323 self
.message("Store plaintext passwords: on")
1325 self
.message("Store plaintext passwords: off")
1326 self
.message("Password history length: %d" % pwd_hist_len
)
1327 self
.message("Minimum password length: %d" % cur_min_pwd_len
)
1328 self
.message("Minimum password age (days): %d" % cur_min_pwd_age
)
1329 self
.message("Maximum password age (days): %d" % cur_max_pwd_age
)
1330 self
.message("Account lockout duration (mins): %d" % cur_account_lockout_duration
)
1331 self
.message("Account lockout threshold (attempts): %d" % cur_account_lockout_threshold
)
1332 self
.message("Reset account lockout after (mins): %d" % cur_reset_account_lockout_after
)
1333 elif subcommand
== "set":
1336 m
.dn
= ldb
.Dn(samdb
, domain_dn
)
1338 if complexity
is not None:
1339 if complexity
== "on" or complexity
== "default":
1340 pwd_props
= pwd_props | DOMAIN_PASSWORD_COMPLEX
1341 msgs
.append("Password complexity activated!")
1342 elif complexity
== "off":
1343 pwd_props
= pwd_props
& (~DOMAIN_PASSWORD_COMPLEX
)
1344 msgs
.append("Password complexity deactivated!")
1346 if store_plaintext
is not None:
1347 if store_plaintext
== "on" or store_plaintext
== "default":
1348 pwd_props
= pwd_props | DOMAIN_PASSWORD_STORE_CLEARTEXT
1349 msgs
.append("Plaintext password storage for changed passwords activated!")
1350 elif store_plaintext
== "off":
1351 pwd_props
= pwd_props
& (~DOMAIN_PASSWORD_STORE_CLEARTEXT
)
1352 msgs
.append("Plaintext password storage for changed passwords deactivated!")
1354 if complexity
is not None or store_plaintext
is not None:
1355 m
["pwdProperties"] = ldb
.MessageElement(str(pwd_props
),
1356 ldb
.FLAG_MOD_REPLACE
, "pwdProperties")
1358 if history_length
is not None:
1359 if history_length
== "default":
1362 pwd_hist_len
= int(history_length
)
1364 if pwd_hist_len
< 0 or pwd_hist_len
> 24:
1365 raise CommandError("Password history length must be in the range of 0 to 24!")
1367 m
["pwdHistoryLength"] = ldb
.MessageElement(str(pwd_hist_len
),
1368 ldb
.FLAG_MOD_REPLACE
, "pwdHistoryLength")
1369 msgs
.append("Password history length changed!")
1371 if min_pwd_length
is not None:
1372 if min_pwd_length
== "default":
1375 min_pwd_len
= int(min_pwd_length
)
1377 if min_pwd_len
< 0 or min_pwd_len
> 14:
1378 raise CommandError("Minimum password length must be in the range of 0 to 14!")
1380 m
["minPwdLength"] = ldb
.MessageElement(str(min_pwd_len
),
1381 ldb
.FLAG_MOD_REPLACE
, "minPwdLength")
1382 msgs
.append("Minimum password length changed!")
1384 if min_pwd_age
is not None:
1385 if min_pwd_age
== "default":
1388 min_pwd_age
= int(min_pwd_age
)
1390 if min_pwd_age
< 0 or min_pwd_age
> 998:
1391 raise CommandError("Minimum password age must be in the range of 0 to 998!")
1394 min_pwd_age_ticks
= -int(min_pwd_age
* (24 * 60 * 60 * 1e7
))
1396 m
["minPwdAge"] = ldb
.MessageElement(str(min_pwd_age_ticks
),
1397 ldb
.FLAG_MOD_REPLACE
, "minPwdAge")
1398 msgs
.append("Minimum password age changed!")
1400 if max_pwd_age
is not None:
1401 if max_pwd_age
== "default":
1404 max_pwd_age
= int(max_pwd_age
)
1406 if max_pwd_age
< 0 or max_pwd_age
> 999:
1407 raise CommandError("Maximum password age must be in the range of 0 to 999!")
1410 if max_pwd_age
== 0:
1411 max_pwd_age_ticks
= -0x8000000000000000
1413 max_pwd_age_ticks
= -int(max_pwd_age
* (24 * 60 * 60 * 1e7
))
1415 m
["maxPwdAge"] = ldb
.MessageElement(str(max_pwd_age_ticks
),
1416 ldb
.FLAG_MOD_REPLACE
, "maxPwdAge")
1417 msgs
.append("Maximum password age changed!")
1419 if account_lockout_duration
is not None:
1420 if account_lockout_duration
== "default":
1421 account_lockout_duration
= 30
1423 account_lockout_duration
= int(account_lockout_duration
)
1425 if account_lockout_duration
< 0 or account_lockout_duration
> 99999:
1426 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1429 if account_lockout_duration
== 0:
1430 account_lockout_duration_ticks
= -0x8000000000000000
1432 account_lockout_duration_ticks
= -int(account_lockout_duration
* (60 * 1e7
))
1434 m
["lockoutDuration"] = ldb
.MessageElement(str(account_lockout_duration_ticks
),
1435 ldb
.FLAG_MOD_REPLACE
, "lockoutDuration")
1436 msgs
.append("Account lockout duration changed!")
1438 if account_lockout_threshold
is not None:
1439 if account_lockout_threshold
== "default":
1440 account_lockout_threshold
= 0
1442 account_lockout_threshold
= int(account_lockout_threshold
)
1444 m
["lockoutThreshold"] = ldb
.MessageElement(str(account_lockout_threshold
),
1445 ldb
.FLAG_MOD_REPLACE
, "lockoutThreshold")
1446 msgs
.append("Account lockout threshold changed!")
1448 if reset_account_lockout_after
is not None:
1449 if reset_account_lockout_after
== "default":
1450 reset_account_lockout_after
= 30
1452 reset_account_lockout_after
= int(reset_account_lockout_after
)
1454 if reset_account_lockout_after
< 0 or reset_account_lockout_after
> 99999:
1455 raise CommandError("Maximum password age must be in the range of 0 to 99999!")
1458 if reset_account_lockout_after
== 0:
1459 reset_account_lockout_after_ticks
= -0x8000000000000000
1461 reset_account_lockout_after_ticks
= -int(reset_account_lockout_after
* (60 * 1e7
))
1463 m
["lockOutObservationWindow"] = ldb
.MessageElement(str(reset_account_lockout_after_ticks
),
1464 ldb
.FLAG_MOD_REPLACE
, "lockOutObservationWindow")
1465 msgs
.append("Duration to reset account lockout after changed!")
1467 if max_pwd_age
> 0 and min_pwd_age
>= max_pwd_age
:
1468 raise CommandError("Maximum password age (%d) must be greater than minimum password age (%d)!" % (max_pwd_age
, min_pwd_age
))
1471 raise CommandError("You must specify at least one option to set. Try --help")
1473 msgs
.append("All changes applied successfully!")
1474 self
.message("\n".join(msgs
))
1476 raise CommandError("Wrong argument '%s'!" % subcommand
)
1479 class cmd_domain_classicupgrade(Command
):
1480 """Upgrade from Samba classic (NT4-like) database to Samba AD DC database.
1482 Specify either a directory with all Samba classic DC databases and state files (with --dbdir) or
1483 the testparm utility from your classic installation (with --testparm).
1486 synopsis
= "%prog [options] <classic_smb_conf>"
1488 takes_optiongroups
= {
1489 "sambaopts": options
.SambaOptions
,
1490 "versionopts": options
.VersionOptions
1494 Option("--dbdir", type="string", metavar
="DIR",
1495 help="Path to samba classic DC database directory"),
1496 Option("--testparm", type="string", metavar
="PATH",
1497 help="Path to samba classic DC testparm utility from the previous installation. This allows the default paths of the previous installation to be followed"),
1498 Option("--targetdir", type="string", metavar
="DIR",
1499 help="Path prefix where the new Samba 4.0 AD domain should be initialised"),
1500 Option("--quiet", help="Be quiet", action
="store_true"),
1501 Option("--verbose", help="Be verbose", action
="store_true"),
1502 Option("--dns-backend", type="choice", metavar
="NAMESERVER-BACKEND",
1503 choices
=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
1504 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
1505 "BIND9_FLATFILE uses bind9 text database to store zone information, "
1506 "BIND9_DLZ uses samba4 AD to store zone information, "
1507 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
1508 default
="SAMBA_INTERNAL")
1512 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
1513 action
="store_true"),
1514 Option("--use-xattrs", type="choice", choices
=["yes","no","auto"],
1515 metavar
="[yes|no|auto]",
1516 help="Define if we should use the native fs capabilities or a tdb file for "
1517 "storing attributes likes ntacl when --use-ntvfs is set. "
1518 "auto tries to make an inteligent guess based on the user rights and system capabilities",
1521 if samba
.is_ntvfs_fileserver_built():
1522 takes_options
.extend(ntvfs_options
)
1524 takes_args
= ["smbconf"]
1526 def run(self
, smbconf
=None, targetdir
=None, dbdir
=None, testparm
=None,
1527 quiet
=False, verbose
=False, use_xattrs
="auto", sambaopts
=None, versionopts
=None,
1528 dns_backend
=None, use_ntvfs
=False):
1530 if not os
.path
.exists(smbconf
):
1531 raise CommandError("File %s does not exist" % smbconf
)
1533 if testparm
and not os
.path
.exists(testparm
):
1534 raise CommandError("Testparm utility %s does not exist" % testparm
)
1536 if dbdir
and not os
.path
.exists(dbdir
):
1537 raise CommandError("Directory %s does not exist" % dbdir
)
1539 if not dbdir
and not testparm
:
1540 raise CommandError("Please specify either dbdir or testparm")
1542 logger
= self
.get_logger()
1544 logger
.setLevel(logging
.DEBUG
)
1546 logger
.setLevel(logging
.WARNING
)
1548 logger
.setLevel(logging
.INFO
)
1550 if dbdir
and testparm
:
1551 logger
.warning("both dbdir and testparm specified, ignoring dbdir.")
1554 lp
= sambaopts
.get_loadparm()
1556 s3conf
= s3param
.get_context()
1559 s3conf
.set("realm", sambaopts
.realm
)
1561 if targetdir
is not None:
1562 if not os
.path
.isdir(targetdir
):
1566 if use_xattrs
== "yes":
1568 elif use_xattrs
== "auto" and use_ntvfs
== False:
1570 elif use_ntvfs
== False:
1571 raise CommandError("--use-xattrs=no requires --use-ntvfs (not supported for production use). "
1572 "Please re-run with --use-xattrs omitted.")
1573 elif use_xattrs
== "auto" and not s3conf
.get("posix:eadb"):
1575 tmpfile
= tempfile
.NamedTemporaryFile(dir=os
.path
.abspath(targetdir
))
1577 tmpfile
= tempfile
.NamedTemporaryFile(dir=os
.path
.abspath(os
.path
.dirname(lp
.get("private dir"))))
1580 samba
.ntacls
.setntacl(lp
, tmpfile
.name
,
1581 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
1584 # FIXME: Don't catch all exceptions here
1585 logger
.info("You are not root or your system does not support xattr, using tdb backend for attributes. "
1586 "If you intend to use this provision in production, rerun the script as root on a system supporting xattrs.")
1590 # Set correct default values from dbdir or testparm
1593 paths
["state directory"] = dbdir
1594 paths
["private dir"] = dbdir
1595 paths
["lock directory"] = dbdir
1596 paths
["smb passwd file"] = dbdir
+ "/smbpasswd"
1598 paths
["state directory"] = get_testparm_var(testparm
, smbconf
, "state directory")
1599 paths
["private dir"] = get_testparm_var(testparm
, smbconf
, "private dir")
1600 paths
["smb passwd file"] = get_testparm_var(testparm
, smbconf
, "smb passwd file")
1601 paths
["lock directory"] = get_testparm_var(testparm
, smbconf
, "lock directory")
1602 # "testparm" from Samba 3 < 3.4.x is not aware of the parameter
1603 # "state directory", instead make use of "lock directory"
1604 if len(paths
["state directory"]) == 0:
1605 paths
["state directory"] = paths
["lock directory"]
1608 s3conf
.set(p
, paths
[p
])
1610 # load smb.conf parameters
1611 logger
.info("Reading smb.conf")
1612 s3conf
.load(smbconf
)
1613 samba3
= Samba3(smbconf
, s3conf
)
1615 logger
.info("Provisioning")
1616 upgrade_from_samba3(samba3
, logger
, targetdir
, session_info
=system_session(),
1617 useeadb
=eadb
, dns_backend
=dns_backend
, use_ntvfs
=use_ntvfs
)
1620 class cmd_domain_samba3upgrade(cmd_domain_classicupgrade
):
1621 __doc__
= cmd_domain_classicupgrade
.__doc
__
1623 # This command is present for backwards compatibility only,
1624 # and should not be shown.
1628 class LocalDCCredentialsOptions(options
.CredentialsOptions
):
1629 def __init__(self
, parser
):
1630 options
.CredentialsOptions
.__init
__(self
, parser
, special_name
="local-dc")
1632 class DomainTrustCommand(Command
):
1633 """List domain trusts."""
1636 Command
.__init
__(self
)
1637 self
.local_lp
= None
1639 self
.local_server
= None
1640 self
.local_binding_string
= None
1641 self
.local_creds
= None
1643 self
.remote_server
= None
1644 self
.remote_binding_string
= None
1645 self
.remote_creds
= None
1647 def _uint32(self
, v
):
1648 return ctypes
.c_uint32(v
).value
1650 def check_runtime_error(self
, runtime
, val
):
1654 err32
= self
._uint
32(runtime
[0])
1660 class LocalRuntimeError(CommandError
):
1661 def __init__(exception_self
, self
, runtime
, message
):
1662 err32
= self
._uint
32(runtime
[0])
1664 msg
= "LOCAL_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1665 self
.local_server
, message
, err32
, errstr
)
1666 CommandError
.__init
__(exception_self
, msg
)
1668 class RemoteRuntimeError(CommandError
):
1669 def __init__(exception_self
, self
, runtime
, message
):
1670 err32
= self
._uint
32(runtime
[0])
1672 msg
= "REMOTE_DC[%s]: %s - ERROR(0x%08X) - %s" % (
1673 self
.remote_server
, message
, err32
, errstr
)
1674 CommandError
.__init
__(exception_self
, msg
)
1676 class LocalLdbError(CommandError
):
1677 def __init__(exception_self
, self
, ldb_error
, message
):
1678 errval
= ldb_error
[0]
1679 errstr
= ldb_error
[1]
1680 msg
= "LOCAL_DC[%s]: %s - ERROR(%d) - %s" % (
1681 self
.local_server
, message
, errval
, errstr
)
1682 CommandError
.__init
__(exception_self
, msg
)
1684 def setup_local_server(self
, sambaopts
, localdcopts
):
1685 if self
.local_server
is not None:
1686 return self
.local_server
1688 lp
= sambaopts
.get_loadparm()
1690 local_server
= localdcopts
.ipaddress
1691 if local_server
is None:
1692 server_role
= lp
.server_role()
1693 if server_role
!= "ROLE_ACTIVE_DIRECTORY_DC":
1694 raise CommandError("Invalid server_role %s" % (server_role
))
1695 local_server
= lp
.get('netbios name')
1696 local_transport
= "ncalrpc"
1697 local_binding_options
= ""
1698 local_binding_options
+= ",auth_type=ncalrpc_as_system"
1699 local_ldap_url
= None
1702 local_transport
= "ncacn_np"
1703 local_binding_options
= ""
1704 local_ldap_url
= "ldap://%s" % local_server
1705 local_creds
= localdcopts
.get_credentials(lp
)
1709 self
.local_server
= local_server
1710 self
.local_binding_string
= "%s:%s[%s]" % (local_transport
, local_server
, local_binding_options
)
1711 self
.local_ldap_url
= local_ldap_url
1712 self
.local_creds
= local_creds
1713 return self
.local_server
1715 def new_local_lsa_connection(self
):
1716 return lsa
.lsarpc(self
.local_binding_string
, self
.local_lp
, self
.local_creds
)
1718 def new_local_netlogon_connection(self
):
1719 return netlogon
.netlogon(self
.local_binding_string
, self
.local_lp
, self
.local_creds
)
1721 def new_local_ldap_connection(self
):
1722 return SamDB(url
=self
.local_ldap_url
,
1723 session_info
=system_session(),
1724 credentials
=self
.local_creds
,
1727 def setup_remote_server(self
, credopts
, domain
,
1729 require_writable
=True):
1732 assert require_writable
1734 if self
.remote_server
is not None:
1735 return self
.remote_server
1737 self
.remote_server
= "__unknown__remote_server__.%s" % domain
1738 assert self
.local_server
is not None
1740 remote_creds
= credopts
.get_credentials(self
.local_lp
)
1741 remote_server
= credopts
.ipaddress
1742 remote_binding_options
= ""
1744 # TODO: we should also support NT4 domains
1745 # we could use local_netlogon.netr_DsRGetDCNameEx2() with the remote domain name
1746 # and delegate NBT or CLDAP to the local netlogon server
1748 remote_net
= Net(remote_creds
, self
.local_lp
, server
=remote_server
)
1749 remote_flags
= nbt
.NBT_SERVER_LDAP | nbt
.NBT_SERVER_DS
1750 if require_writable
:
1751 remote_flags |
= nbt
.NBT_SERVER_WRITABLE
1753 remote_flags |
= nbt
.NBT_SERVER_PDC
1754 remote_info
= remote_net
.finddc(flags
=remote_flags
, domain
=domain
, address
=remote_server
)
1756 raise CommandError("Failed to find a writeable DC for domain '%s'" % domain
)
1758 nbt
.NBT_SERVER_PDC
: "PDC",
1759 nbt
.NBT_SERVER_GC
: "GC",
1760 nbt
.NBT_SERVER_LDAP
: "LDAP",
1761 nbt
.NBT_SERVER_DS
: "DS",
1762 nbt
.NBT_SERVER_KDC
: "KDC",
1763 nbt
.NBT_SERVER_TIMESERV
: "TIMESERV",
1764 nbt
.NBT_SERVER_CLOSEST
: "CLOSEST",
1765 nbt
.NBT_SERVER_WRITABLE
: "WRITABLE",
1766 nbt
.NBT_SERVER_GOOD_TIMESERV
: "GOOD_TIMESERV",
1767 nbt
.NBT_SERVER_NDNC
: "NDNC",
1768 nbt
.NBT_SERVER_SELECT_SECRET_DOMAIN_6
: "SELECT_SECRET_DOMAIN_6",
1769 nbt
.NBT_SERVER_FULL_SECRET_DOMAIN_6
: "FULL_SECRET_DOMAIN_6",
1770 nbt
.NBT_SERVER_ADS_WEB_SERVICE
: "ADS_WEB_SERVICE",
1771 nbt
.NBT_SERVER_DS_8
: "DS_8",
1772 nbt
.NBT_SERVER_HAS_DNS_NAME
: "HAS_DNS_NAME",
1773 nbt
.NBT_SERVER_IS_DEFAULT_NC
: "IS_DEFAULT_NC",
1774 nbt
.NBT_SERVER_FOREST_ROOT
: "FOREST_ROOT",
1776 server_type_string
= self
.generic_bitmap_to_string(flag_map
,
1777 remote_info
.server_type
, names_only
=True)
1778 self
.outf
.write("RemoteDC Netbios[%s] DNS[%s] ServerType[%s]\n" % (
1779 remote_info
.pdc_name
,
1780 remote_info
.pdc_dns_name
,
1781 server_type_string
))
1783 self
.remote_server
= remote_info
.pdc_dns_name
1784 self
.remote_binding_string
="ncacn_np:%s[%s]" % (self
.remote_server
, remote_binding_options
)
1785 self
.remote_creds
= remote_creds
1786 return self
.remote_server
1788 def new_remote_lsa_connection(self
):
1789 return lsa
.lsarpc(self
.remote_binding_string
, self
.local_lp
, self
.remote_creds
)
1791 def new_remote_netlogon_connection(self
):
1792 return netlogon
.netlogon(self
.remote_binding_string
, self
.local_lp
, self
.remote_creds
)
1794 def get_lsa_info(self
, conn
, policy_access
):
1795 objectAttr
= lsa
.ObjectAttribute()
1796 objectAttr
.sec_qos
= lsa
.QosInfo()
1798 policy
= conn
.OpenPolicy2(''.decode('utf-8'),
1799 objectAttr
, policy_access
)
1801 info
= conn
.QueryInfoPolicy2(policy
, lsa
.LSA_POLICY_INFO_DNS
)
1803 return (policy
, info
)
1805 def get_netlogon_dc_info(self
, conn
, server
):
1806 info
= conn
.netr_DsRGetDCNameEx2(server
,
1807 None, 0, None, None, None,
1808 netlogon
.DS_RETURN_DNS_NAME
)
1811 def netr_DomainTrust_to_name(self
, t
):
1812 if t
.trust_type
== lsa
.LSA_TRUST_TYPE_DOWNLEVEL
:
1813 return t
.netbios_name
1817 def netr_DomainTrust_to_type(self
, a
, t
):
1819 primary_parent
= None
1821 if _t
.trust_flags
& netlogon
.NETR_TRUST_FLAG_PRIMARY
:
1823 if not _t
.trust_flags
& netlogon
.NETR_TRUST_FLAG_TREEROOT
:
1824 primary_parent
= a
[_t
.parent_index
]
1827 if t
.trust_flags
& netlogon
.NETR_TRUST_FLAG_IN_FOREST
:
1828 if t
is primary_parent
:
1831 if t
.trust_flags
& netlogon
.NETR_TRUST_FLAG_TREEROOT
:
1834 parent
= a
[t
.parent_index
]
1835 if parent
is primary
:
1840 if t
.trust_attributes
& lsa
.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
:
1845 def netr_DomainTrust_to_transitive(self
, t
):
1846 if t
.trust_flags
& netlogon
.NETR_TRUST_FLAG_IN_FOREST
:
1849 if t
.trust_attributes
& lsa
.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
:
1852 if t
.trust_attributes
& lsa
.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
:
1857 def netr_DomainTrust_to_direction(self
, t
):
1858 if t
.trust_flags
& netlogon
.NETR_TRUST_FLAG_INBOUND
and \
1859 t
.trust_flags
& netlogon
.NETR_TRUST_FLAG_OUTBOUND
:
1862 if t
.trust_flags
& netlogon
.NETR_TRUST_FLAG_INBOUND
:
1865 if t
.trust_flags
& netlogon
.NETR_TRUST_FLAG_OUTBOUND
:
1870 def generic_enum_to_string(self
, e_dict
, v
, names_only
=False):
1874 v32
= self
._uint
32(v
)
1875 w
= "__unknown__%08X__" % v32
1877 r
= "0x%x (%s)" % (v
, w
)
1880 def generic_bitmap_to_string(self
, b_dict
, v
, names_only
=False):
1885 for b
in sorted(b_dict
.keys()):
1892 c32
= self
._uint
32(c
)
1893 s
+= ["__unknown_%08X__" % c32
]
1898 r
= "0x%x (%s)" % (v
, w
)
1901 def trustType_string(self
, v
):
1903 lsa
.LSA_TRUST_TYPE_DOWNLEVEL
: "DOWNLEVEL",
1904 lsa
.LSA_TRUST_TYPE_UPLEVEL
: "UPLEVEL",
1905 lsa
.LSA_TRUST_TYPE_MIT
: "MIT",
1906 lsa
.LSA_TRUST_TYPE_DCE
: "DCE",
1908 return self
.generic_enum_to_string(types
, v
)
1910 def trustDirection_string(self
, v
):
1912 lsa
.LSA_TRUST_DIRECTION_INBOUND |
1913 lsa
.LSA_TRUST_DIRECTION_OUTBOUND
: "BOTH",
1914 lsa
.LSA_TRUST_DIRECTION_INBOUND
: "INBOUND",
1915 lsa
.LSA_TRUST_DIRECTION_OUTBOUND
: "OUTBOUND",
1917 return self
.generic_enum_to_string(directions
, v
)
1919 def trustAttributes_string(self
, v
):
1921 lsa
.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
: "NON_TRANSITIVE",
1922 lsa
.LSA_TRUST_ATTRIBUTE_UPLEVEL_ONLY
: "UPLEVEL_ONLY",
1923 lsa
.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
: "QUARANTINED_DOMAIN",
1924 lsa
.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
: "FOREST_TRANSITIVE",
1925 lsa
.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
: "CROSS_ORGANIZATION",
1926 lsa
.LSA_TRUST_ATTRIBUTE_WITHIN_FOREST
: "WITHIN_FOREST",
1927 lsa
.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
: "TREAT_AS_EXTERNAL",
1928 lsa
.LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION
: "USES_RC4_ENCRYPTION",
1930 return self
.generic_bitmap_to_string(attributes
, v
)
1932 def kerb_EncTypes_string(self
, v
):
1934 security
.KERB_ENCTYPE_DES_CBC_CRC
: "DES_CBC_CRC",
1935 security
.KERB_ENCTYPE_DES_CBC_MD5
: "DES_CBC_MD5",
1936 security
.KERB_ENCTYPE_RC4_HMAC_MD5
: "RC4_HMAC_MD5",
1937 security
.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96
: "AES128_CTS_HMAC_SHA1_96",
1938 security
.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96
: "AES256_CTS_HMAC_SHA1_96",
1939 security
.KERB_ENCTYPE_FAST_SUPPORTED
: "FAST_SUPPORTED",
1940 security
.KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED
: "COMPOUND_IDENTITY_SUPPORTED",
1941 security
.KERB_ENCTYPE_CLAIMS_SUPPORTED
: "CLAIMS_SUPPORTED",
1942 security
.KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED
: "RESOURCE_SID_COMPRESSION_DISABLED",
1944 return self
.generic_bitmap_to_string(enctypes
, v
)
1946 def entry_tln_status(self
, e_flags
, ):
1948 return "Status[Enabled]"
1951 lsa
.LSA_TLN_DISABLED_NEW
: "Disabled-New",
1952 lsa
.LSA_TLN_DISABLED_ADMIN
: "Disabled",
1953 lsa
.LSA_TLN_DISABLED_CONFLICT
: "Disabled-Conflicting",
1955 return "Status[%s]" % self
.generic_bitmap_to_string(flags
, e_flags
, names_only
=True)
1957 def entry_dom_status(self
, e_flags
):
1959 return "Status[Enabled]"
1962 lsa
.LSA_SID_DISABLED_ADMIN
: "Disabled-SID",
1963 lsa
.LSA_SID_DISABLED_CONFLICT
: "Disabled-SID-Conflicting",
1964 lsa
.LSA_NB_DISABLED_ADMIN
: "Disabled-NB",
1965 lsa
.LSA_NB_DISABLED_CONFLICT
: "Disabled-NB-Conflicting",
1967 return "Status[%s]" % self
.generic_bitmap_to_string(flags
, e_flags
, names_only
=True)
1969 def write_forest_trust_info(self
, fti
, tln
=None, collisions
=None):
1971 tln_string
= " TDO[%s]" % tln
1975 self
.outf
.write("Namespaces[%d]%s:\n" % (
1976 len(fti
.entries
), tln_string
))
1978 for i
in xrange(0, len(fti
.entries
)):
1982 collision_string
= ""
1984 if collisions
is not None:
1985 for c
in collisions
.entries
:
1989 collision_string
= " Collision[%s]" % (c
.name
.string
)
1991 d
= e
.forest_trust_data
1992 if e
.type == lsa
.LSA_FOREST_TRUST_TOP_LEVEL_NAME
:
1993 self
.outf
.write("TLN: %-32s DNS[*.%s]%s\n" % (
1994 self
.entry_tln_status(flags
),
1995 d
.string
, collision_string
))
1996 elif e
.type == lsa
.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX
:
1997 self
.outf
.write("TLN_EX: %-29s DNS[*.%s]\n" % (
1999 elif e
.type == lsa
.LSA_FOREST_TRUST_DOMAIN_INFO
:
2000 self
.outf
.write("DOM: %-32s DNS[%s] Netbios[%s] SID[%s]%s\n" % (
2001 self
.entry_dom_status(flags
),
2002 d
.dns_domain_name
.string
,
2003 d
.netbios_domain_name
.string
,
2004 d
.domain_sid
, collision_string
))
2007 class cmd_domain_trust_list(DomainTrustCommand
):
2008 """List domain trusts."""
2010 synopsis
= "%prog [options]"
2012 takes_optiongroups
= {
2013 "sambaopts": options
.SambaOptions
,
2014 "versionopts": options
.VersionOptions
,
2015 "localdcopts": LocalDCCredentialsOptions
,
2021 def run(self
, sambaopts
=None, versionopts
=None, localdcopts
=None):
2023 local_server
= self
.setup_local_server(sambaopts
, localdcopts
)
2025 local_netlogon
= self
.new_local_netlogon_connection()
2026 except RuntimeError as error
:
2027 raise self
.LocalRuntimeError(self
, error
, "failed to connect netlogon server")
2030 local_netlogon_trusts
= local_netlogon
.netr_DsrEnumerateDomainTrusts(local_server
,
2031 netlogon
.NETR_TRUST_FLAG_IN_FOREST |
2032 netlogon
.NETR_TRUST_FLAG_OUTBOUND |
2033 netlogon
.NETR_TRUST_FLAG_INBOUND
)
2034 except RuntimeError as error
:
2035 if self
.check_runtime_error(error
, werror
.WERR_RPC_S_PROCNUM_OUT_OF_RANGE
):
2036 # TODO: we could implement a fallback to lsa.EnumTrustDom()
2037 raise CommandError("LOCAL_DC[%s]: netr_DsrEnumerateDomainTrusts not supported." % (
2039 raise self
.LocalRuntimeError(self
, error
, "netr_DsrEnumerateDomainTrusts failed")
2041 a
= local_netlogon_trusts
.array
2043 if t
.trust_flags
& netlogon
.NETR_TRUST_FLAG_PRIMARY
:
2045 self
.outf
.write("%-14s %-15s %-19s %s\n" % (
2046 "Type[%s]" % self
.netr_DomainTrust_to_type(a
, t
),
2047 "Transitive[%s]" % self
.netr_DomainTrust_to_transitive(t
),
2048 "Direction[%s]" % self
.netr_DomainTrust_to_direction(t
),
2049 "Name[%s]" % self
.netr_DomainTrust_to_name(t
)))
2052 class cmd_domain_trust_show(DomainTrustCommand
):
2053 """Show trusted domain details."""
2055 synopsis
= "%prog NAME [options]"
2057 takes_optiongroups
= {
2058 "sambaopts": options
.SambaOptions
,
2059 "versionopts": options
.VersionOptions
,
2060 "localdcopts": LocalDCCredentialsOptions
,
2066 takes_args
= ["domain"]
2068 def run(self
, domain
, sambaopts
=None, versionopts
=None, localdcopts
=None):
2070 local_server
= self
.setup_local_server(sambaopts
, localdcopts
)
2072 local_lsa
= self
.new_local_lsa_connection()
2073 except RuntimeError as error
:
2074 raise self
.LocalRuntimeError(self
, error
, "failed to connect lsa server")
2077 local_policy_access
= lsa
.LSA_POLICY_VIEW_LOCAL_INFORMATION
2078 (local_policy
, local_lsa_info
) = self
.get_lsa_info(local_lsa
, local_policy_access
)
2079 except RuntimeError as error
:
2080 raise self
.LocalRuntimeError(self
, error
, "failed to query LSA_POLICY_INFO_DNS")
2082 self
.outf
.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2083 local_lsa_info
.name
.string
,
2084 local_lsa_info
.dns_domain
.string
,
2085 local_lsa_info
.sid
))
2087 lsaString
= lsa
.String()
2088 lsaString
.string
= domain
2090 local_tdo_full
= local_lsa
.QueryTrustedDomainInfoByName(local_policy
,
2091 lsaString
, lsa
.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO
)
2092 local_tdo_info
= local_tdo_full
.info_ex
2093 local_tdo_posix
= local_tdo_full
.posix_offset
2094 except NTSTATUSError
as error
:
2095 if self
.check_runtime_error(error
, ntstatus
.NT_STATUS_OBJECT_NAME_NOT_FOUND
):
2096 raise CommandError("trusted domain object does not exist for domain [%s]" % domain
)
2098 raise self
.LocalRuntimeError(self
, error
, "QueryTrustedDomainInfoByName(FULL_INFO) failed")
2101 local_tdo_enctypes
= local_lsa
.QueryTrustedDomainInfoByName(local_policy
,
2102 lsaString
, lsa
.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES
)
2103 except NTSTATUSError
as error
:
2104 if self
.check_runtime_error(error
, ntstatus
.NT_STATUS_INVALID_PARAMETER
):
2106 if self
.check_runtime_error(error
, ntstatus
.NT_STATUS_INVALID_INFO_CLASS
):
2109 if error
is not None:
2110 raise self
.LocalRuntimeError(self
, error
,
2111 "QueryTrustedDomainInfoByName(SUPPORTED_ENCRYPTION_TYPES) failed")
2113 local_tdo_enctypes
= lsa
.TrustDomainInfoSupportedEncTypes()
2114 local_tdo_enctypes
.enc_types
= 0
2117 local_tdo_forest
= None
2118 if local_tdo_info
.trust_attributes
& lsa
.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
:
2119 local_tdo_forest
= local_lsa
.lsaRQueryForestTrustInformation(local_policy
,
2120 lsaString
, lsa
.LSA_FOREST_TRUST_DOMAIN_INFO
)
2121 except RuntimeError as error
:
2122 if self
.check_runtime_error(error
, ntstatus
.NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE
):
2124 if self
.check_runtime_error(error
, ntstatus
.NT_STATUS_NOT_FOUND
):
2126 if error
is not None:
2127 raise self
.LocalRuntimeError(self
, error
, "lsaRQueryForestTrustInformation failed")
2129 local_tdo_forest
= lsa
.ForestTrustInformation()
2130 local_tdo_forest
.count
= 0
2131 local_tdo_forest
.entries
= []
2133 self
.outf
.write("TrusteDomain:\n\n");
2134 self
.outf
.write("NetbiosName: %s\n" % local_tdo_info
.netbios_name
.string
)
2135 if local_tdo_info
.netbios_name
.string
!= local_tdo_info
.domain_name
.string
:
2136 self
.outf
.write("DnsName: %s\n" % local_tdo_info
.domain_name
.string
)
2137 self
.outf
.write("SID: %s\n" % local_tdo_info
.sid
)
2138 self
.outf
.write("Type: %s\n" % self
.trustType_string(local_tdo_info
.trust_type
))
2139 self
.outf
.write("Direction: %s\n" % self
.trustDirection_string(local_tdo_info
.trust_direction
))
2140 self
.outf
.write("Attributes: %s\n" % self
.trustAttributes_string(local_tdo_info
.trust_attributes
))
2141 posix_offset_u32
= ctypes
.c_uint32(local_tdo_posix
.posix_offset
).value
2142 posix_offset_i32
= ctypes
.c_int32(local_tdo_posix
.posix_offset
).value
2143 self
.outf
.write("PosixOffset: 0x%08X (%d)\n" % (posix_offset_u32
, posix_offset_i32
))
2144 self
.outf
.write("kerb_EncTypes: %s\n" % self
.kerb_EncTypes_string(local_tdo_enctypes
.enc_types
))
2146 if local_tdo_info
.trust_attributes
& lsa
.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
:
2147 self
.write_forest_trust_info(local_tdo_forest
,
2148 tln
=local_tdo_info
.domain_name
.string
)
2152 class cmd_domain_trust_create(DomainTrustCommand
):
2153 """Create a domain or forest trust."""
2155 synopsis
= "%prog DOMAIN [options]"
2157 takes_optiongroups
= {
2158 "sambaopts": options
.SambaOptions
,
2159 "versionopts": options
.VersionOptions
,
2160 "credopts": options
.CredentialsOptions
,
2161 "localdcopts": LocalDCCredentialsOptions
,
2165 Option("--type", type="choice", metavar
="TYPE",
2166 choices
=["external", "forest"],
2167 help="The type of the trust: 'external' or 'forest'.",
2169 default
="external"),
2170 Option("--direction", type="choice", metavar
="DIRECTION",
2171 choices
=["incoming", "outgoing", "both"],
2172 help="The trust direction: 'incoming', 'outgoing' or 'both'.",
2173 dest
='trust_direction',
2175 Option("--create-location", type="choice", metavar
="LOCATION",
2176 choices
=["local", "both"],
2177 help="Where to create the trusted domain object: 'local' or 'both'.",
2178 dest
='create_location',
2180 Option("--cross-organisation", action
="store_true",
2181 help="The related domains does not belong to the same organisation.",
2182 dest
='cross_organisation',
2184 Option("--quarantined", type="choice", metavar
="yes|no",
2185 choices
=["yes", "no", None],
2186 help="Special SID filtering rules are applied to the trust. "
2187 "With --type=external the default is yes. "
2188 "With --type=forest the default is no.",
2189 dest
='quarantined_arg',
2191 Option("--not-transitive", action
="store_true",
2192 help="The forest trust is not transitive.",
2193 dest
='not_transitive',
2195 Option("--treat-as-external", action
="store_true",
2196 help="The treat the forest trust as external.",
2197 dest
='treat_as_external',
2199 Option("--no-aes-keys", action
="store_false",
2200 help="The trust uses aes kerberos keys.",
2201 dest
='use_aes_keys',
2203 Option("--skip-validation", action
="store_false",
2204 help="Skip validation of the trust.",
2209 takes_args
= ["domain"]
2211 def run(self
, domain
, sambaopts
=None, localdcopts
=None, credopts
=None, versionopts
=None,
2212 trust_type
=None, trust_direction
=None, create_location
=None,
2213 cross_organisation
=False, quarantined_arg
=None,
2214 not_transitive
=False, treat_as_external
=False,
2215 use_aes_keys
=False, validate
=True):
2217 lsaString
= lsa
.String()
2220 if quarantined_arg
is None:
2221 if trust_type
== 'external':
2223 elif quarantined_arg
== 'yes':
2226 if trust_type
!= 'forest':
2228 raise CommandError("--not-transitive requires --type=forest")
2229 if treat_as_external
:
2230 raise CommandError("--treat-as-external requires --type=forest")
2234 enc_types
= lsa
.TrustDomainInfoSupportedEncTypes()
2235 enc_types
.enc_types
= security
.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96
2236 enc_types
.enc_types |
= security
.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96
2238 local_policy_access
= lsa
.LSA_POLICY_VIEW_LOCAL_INFORMATION
2239 local_policy_access |
= lsa
.LSA_POLICY_TRUST_ADMIN
2240 local_policy_access |
= lsa
.LSA_POLICY_CREATE_SECRET
2242 local_trust_info
= lsa
.TrustDomainInfoInfoEx()
2243 local_trust_info
.trust_type
= lsa
.LSA_TRUST_TYPE_UPLEVEL
2244 local_trust_info
.trust_direction
= 0
2245 if trust_direction
== "both":
2246 local_trust_info
.trust_direction |
= lsa
.LSA_TRUST_DIRECTION_INBOUND
2247 local_trust_info
.trust_direction |
= lsa
.LSA_TRUST_DIRECTION_OUTBOUND
2248 elif trust_direction
== "incoming":
2249 local_trust_info
.trust_direction |
= lsa
.LSA_TRUST_DIRECTION_INBOUND
2250 elif trust_direction
== "outgoing":
2251 local_trust_info
.trust_direction |
= lsa
.LSA_TRUST_DIRECTION_OUTBOUND
2252 local_trust_info
.trust_attributes
= 0
2253 if cross_organisation
:
2254 local_trust_info
.trust_attributes |
= lsa
.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2256 local_trust_info
.trust_attributes |
= lsa
.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2257 if trust_type
== "forest":
2258 local_trust_info
.trust_attributes |
= lsa
.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2260 local_trust_info
.trust_attributes |
= lsa
.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2261 if treat_as_external
:
2262 local_trust_info
.trust_attributes |
= lsa
.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2264 def get_password(name
):
2267 if password
is not None and password
is not '':
2269 password
= getpass("New %s Password: " % name
)
2270 passwordverify
= getpass("Retype %s Password: " % name
)
2271 if not password
== passwordverify
:
2273 self
.outf
.write("Sorry, passwords do not match.\n")
2275 incoming_secret
= None
2276 outgoing_secret
= None
2277 remote_policy_access
= lsa
.LSA_POLICY_VIEW_LOCAL_INFORMATION
2278 if create_location
== "local":
2279 if local_trust_info
.trust_direction
& lsa
.LSA_TRUST_DIRECTION_INBOUND
:
2280 incoming_password
= get_password("Incoming Trust")
2281 incoming_secret
= string_to_byte_array(incoming_password
.encode('utf-16-le'))
2282 if local_trust_info
.trust_direction
& lsa
.LSA_TRUST_DIRECTION_OUTBOUND
:
2283 outgoing_password
= get_password("Outgoing Trust")
2284 outgoing_secret
= string_to_byte_array(outgoing_password
.encode('utf-16-le'))
2286 remote_trust_info
= None
2288 # We use 240 random bytes.
2289 # Windows uses 28 or 240 random bytes. I guess it's
2290 # based on the trust type external vs. forest.
2292 # The initial trust password can be up to 512 bytes
2293 # while the versioned passwords used for periodic updates
2294 # can only be up to 498 bytes, as netr_ServerPasswordSet2()
2295 # needs to pass the NL_PASSWORD_VERSION structure within the
2296 # 512 bytes and a 2 bytes confounder is required.
2298 def random_trust_secret(length
):
2299 pw
= samba
.generate_random_machine_password(length
/2, length
/2)
2300 return string_to_byte_array(pw
.encode('utf-16-le'))
2302 if local_trust_info
.trust_direction
& lsa
.LSA_TRUST_DIRECTION_INBOUND
:
2303 incoming_secret
= random_trust_secret(240)
2304 if local_trust_info
.trust_direction
& lsa
.LSA_TRUST_DIRECTION_OUTBOUND
:
2305 outgoing_secret
= random_trust_secret(240)
2307 remote_policy_access |
= lsa
.LSA_POLICY_TRUST_ADMIN
2308 remote_policy_access |
= lsa
.LSA_POLICY_CREATE_SECRET
2310 remote_trust_info
= lsa
.TrustDomainInfoInfoEx()
2311 remote_trust_info
.trust_type
= lsa
.LSA_TRUST_TYPE_UPLEVEL
2312 remote_trust_info
.trust_direction
= 0
2313 if trust_direction
== "both":
2314 remote_trust_info
.trust_direction |
= lsa
.LSA_TRUST_DIRECTION_INBOUND
2315 remote_trust_info
.trust_direction |
= lsa
.LSA_TRUST_DIRECTION_OUTBOUND
2316 elif trust_direction
== "incoming":
2317 remote_trust_info
.trust_direction |
= lsa
.LSA_TRUST_DIRECTION_OUTBOUND
2318 elif trust_direction
== "outgoing":
2319 remote_trust_info
.trust_direction |
= lsa
.LSA_TRUST_DIRECTION_INBOUND
2320 remote_trust_info
.trust_attributes
= 0
2321 if cross_organisation
:
2322 remote_trust_info
.trust_attributes |
= lsa
.LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION
2324 remote_trust_info
.trust_attributes |
= lsa
.LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN
2325 if trust_type
== "forest":
2326 remote_trust_info
.trust_attributes |
= lsa
.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
2328 remote_trust_info
.trust_attributes |
= lsa
.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
2329 if treat_as_external
:
2330 remote_trust_info
.trust_attributes |
= lsa
.LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL
2332 local_server
= self
.setup_local_server(sambaopts
, localdcopts
)
2334 local_lsa
= self
.new_local_lsa_connection()
2335 except RuntimeError as error
:
2336 raise self
.LocalRuntimeError(self
, error
, "failed to connect lsa server")
2339 (local_policy
, local_lsa_info
) = self
.get_lsa_info(local_lsa
, local_policy_access
)
2340 except RuntimeError as error
:
2341 raise self
.LocalRuntimeError(self
, error
, "failed to query LSA_POLICY_INFO_DNS")
2343 self
.outf
.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2344 local_lsa_info
.name
.string
,
2345 local_lsa_info
.dns_domain
.string
,
2346 local_lsa_info
.sid
))
2349 remote_server
= self
.setup_remote_server(credopts
, domain
)
2350 except RuntimeError as error
:
2351 raise self
.RemoteRuntimeError(self
, error
, "failed to locate remote server")
2354 remote_lsa
= self
.new_remote_lsa_connection()
2355 except RuntimeError as error
:
2356 raise self
.RemoteRuntimeError(self
, error
, "failed to connect lsa server")
2359 (remote_policy
, remote_lsa_info
) = self
.get_lsa_info(remote_lsa
, remote_policy_access
)
2360 except RuntimeError as error
:
2361 raise self
.RemoteRuntimeError(self
, error
, "failed to query LSA_POLICY_INFO_DNS")
2363 self
.outf
.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2364 remote_lsa_info
.name
.string
,
2365 remote_lsa_info
.dns_domain
.string
,
2366 remote_lsa_info
.sid
))
2368 local_trust_info
.domain_name
.string
= remote_lsa_info
.dns_domain
.string
2369 local_trust_info
.netbios_name
.string
= remote_lsa_info
.name
.string
2370 local_trust_info
.sid
= remote_lsa_info
.sid
2372 if remote_trust_info
:
2373 remote_trust_info
.domain_name
.string
= local_lsa_info
.dns_domain
.string
2374 remote_trust_info
.netbios_name
.string
= local_lsa_info
.name
.string
2375 remote_trust_info
.sid
= local_lsa_info
.sid
2378 lsaString
.string
= local_trust_info
.domain_name
.string
2379 local_old_netbios
= local_lsa
.QueryTrustedDomainInfoByName(local_policy
,
2380 lsaString
, lsa
.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO
)
2381 raise CommandError("TrustedDomain %s already exist'" % lsaString
.string
)
2382 except NTSTATUSError
as error
:
2383 if not self
.check_runtime_error(error
, ntstatus
.NT_STATUS_OBJECT_NAME_NOT_FOUND
):
2384 raise self
.LocalRuntimeError(self
, error
,
2385 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2389 lsaString
.string
= local_trust_info
.netbios_name
.string
2390 local_old_dns
= local_lsa
.QueryTrustedDomainInfoByName(local_policy
,
2391 lsaString
, lsa
.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO
)
2392 raise CommandError("TrustedDomain %s already exist'" % lsaString
.string
)
2393 except NTSTATUSError
as error
:
2394 if not self
.check_runtime_error(error
, ntstatus
.NT_STATUS_OBJECT_NAME_NOT_FOUND
):
2395 raise self
.LocalRuntimeError(self
, error
,
2396 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2399 if remote_trust_info
:
2401 lsaString
.string
= remote_trust_info
.domain_name
.string
2402 remote_old_netbios
= remote_lsa
.QueryTrustedDomainInfoByName(remote_policy
,
2403 lsaString
, lsa
.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO
)
2404 raise CommandError("TrustedDomain %s already exist'" % lsaString
.string
)
2405 except NTSTATUSError
as error
:
2406 if not self
.check_runtime_error(error
, ntstatus
.NT_STATUS_OBJECT_NAME_NOT_FOUND
):
2407 raise self
.RemoteRuntimeError(self
, error
,
2408 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2412 lsaString
.string
= remote_trust_info
.netbios_name
.string
2413 remote_old_dns
= remote_lsa
.QueryTrustedDomainInfoByName(remote_policy
,
2414 lsaString
, lsa
.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO
)
2415 raise CommandError("TrustedDomain %s already exist'" % lsaString
.string
)
2416 except NTSTATUSError
as error
:
2417 if not self
.check_runtime_error(error
, ntstatus
.NT_STATUS_OBJECT_NAME_NOT_FOUND
):
2418 raise self
.RemoteRuntimeError(self
, error
,
2419 "QueryTrustedDomainInfoByName(%s, FULL_INFO) failed" % (
2423 local_netlogon
= self
.new_local_netlogon_connection()
2424 except RuntimeError as error
:
2425 raise self
.LocalRuntimeError(self
, error
, "failed to connect netlogon server")
2428 local_netlogon_info
= self
.get_netlogon_dc_info(local_netlogon
, local_server
)
2429 except RuntimeError as error
:
2430 raise self
.LocalRuntimeError(self
, error
, "failed to get netlogon dc info")
2432 if remote_trust_info
:
2434 remote_netlogon
= self
.new_remote_netlogon_connection()
2435 except RuntimeError as error
:
2436 raise self
.RemoteRuntimeError(self
, error
, "failed to connect netlogon server")
2439 remote_netlogon_info
= self
.get_netlogon_dc_info(remote_netlogon
, remote_server
)
2440 except RuntimeError as error
:
2441 raise self
.RemoteRuntimeError(self
, error
, "failed to get netlogon dc info")
2443 def generate_AuthInOutBlob(secret
, update_time
):
2445 blob
= drsblobs
.trustAuthInOutBlob()
2450 clear
= drsblobs
.AuthInfoClear()
2451 clear
.size
= len(secret
)
2452 clear
.password
= secret
2454 info
= drsblobs
.AuthenticationInformation()
2455 info
.LastUpdateTime
= samba
.unix2nttime(update_time
)
2456 info
.AuthType
= lsa
.TRUST_AUTH_TYPE_CLEAR
2457 info
.AuthInfo
= clear
2459 array
= drsblobs
.AuthenticationInformationArray()
2461 array
.array
= [info
]
2463 blob
= drsblobs
.trustAuthInOutBlob()
2465 blob
.current
= array
2469 def generate_AuthInfoInternal(session_key
, incoming
=None, outgoing
=None):
2470 confounder
= [0] * 512
2471 for i
in range(len(confounder
)):
2472 confounder
[i
] = random
.randint(0, 255)
2474 trustpass
= drsblobs
.trustDomainPasswords()
2476 trustpass
.confounder
= confounder
2477 trustpass
.outgoing
= outgoing
2478 trustpass
.incoming
= incoming
2480 trustpass_blob
= ndr_pack(trustpass
)
2482 encrypted_trustpass
= arcfour_encrypt(session_key
, trustpass_blob
)
2484 auth_blob
= lsa
.DATA_BUF2()
2485 auth_blob
.size
= len(encrypted_trustpass
)
2486 auth_blob
.data
= string_to_byte_array(encrypted_trustpass
)
2488 auth_info
= lsa
.TrustDomainInfoAuthInfoInternal()
2489 auth_info
.auth_blob
= auth_blob
2493 update_time
= samba
.current_unix_time()
2494 incoming_blob
= generate_AuthInOutBlob(incoming_secret
, update_time
)
2495 outgoing_blob
= generate_AuthInOutBlob(outgoing_secret
, update_time
)
2497 local_tdo_handle
= None
2498 remote_tdo_handle
= None
2500 local_auth_info
= generate_AuthInfoInternal(local_lsa
.session_key
,
2501 incoming
=incoming_blob
,
2502 outgoing
=outgoing_blob
)
2503 if remote_trust_info
:
2504 remote_auth_info
= generate_AuthInfoInternal(remote_lsa
.session_key
,
2505 incoming
=outgoing_blob
,
2506 outgoing
=incoming_blob
)
2509 if remote_trust_info
:
2510 self
.outf
.write("Creating remote TDO.\n")
2511 current_request
= { "location": "remote", "name": "CreateTrustedDomainEx2"}
2512 remote_tdo_handle
= remote_lsa
.CreateTrustedDomainEx2(remote_policy
,
2515 lsa
.LSA_TRUSTED_DOMAIN_ALL_ACCESS
)
2516 self
.outf
.write("Remote TDO created.\n")
2518 self
.outf
.write("Setting supported encryption types on remote TDO.\n")
2519 current_request
= { "location": "remote", "name": "SetInformationTrustedDomain"}
2520 remote_lsa
.SetInformationTrustedDomain(remote_tdo_handle
,
2521 lsa
.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES
,
2524 self
.outf
.write("Creating local TDO.\n")
2525 current_request
= { "location": "local", "name": "CreateTrustedDomainEx2"}
2526 local_tdo_handle
= local_lsa
.CreateTrustedDomainEx2(local_policy
,
2529 lsa
.LSA_TRUSTED_DOMAIN_ALL_ACCESS
)
2530 self
.outf
.write("Local TDO created\n")
2532 self
.outf
.write("Setting supported encryption types on local TDO.\n")
2533 current_request
= { "location": "local", "name": "SetInformationTrustedDomain"}
2534 local_lsa
.SetInformationTrustedDomain(local_tdo_handle
,
2535 lsa
.LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES
,
2537 except RuntimeError as error
:
2538 self
.outf
.write("Error: %s failed %sly - cleaning up\n" % (
2539 current_request
['name'], current_request
['location']))
2540 if remote_tdo_handle
:
2541 self
.outf
.write("Deleting remote TDO.\n")
2542 remote_lsa
.DeleteObject(remote_tdo_handle
)
2543 remote_tdo_handle
= None
2544 if local_tdo_handle
:
2545 self
.outf
.write("Deleting local TDO.\n")
2546 local_lsa
.DeleteObject(local_tdo_handle
)
2547 local_tdo_handle
= None
2548 if current_request
['location'] is "remote":
2549 raise self
.RemoteRuntimeError(self
, error
, "%s" % (
2550 current_request
['name']))
2551 raise self
.LocalRuntimeError(self
, error
, "%s" % (
2552 current_request
['name']))
2555 if local_trust_info
.trust_attributes
& lsa
.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
:
2556 self
.outf
.write("Setup local forest trust information...\n")
2558 # get all information about the remote trust
2559 # this triggers netr_GetForestTrustInformation to the remote domain
2560 # and lsaRSetForestTrustInformation() locally, but new top level
2561 # names are disabled by default.
2562 local_forest_info
= local_netlogon
.netr_DsRGetForestTrustInformation(local_netlogon_info
.dc_unc
,
2563 remote_lsa_info
.dns_domain
.string
,
2564 netlogon
.DS_GFTI_UPDATE_TDO
)
2565 except RuntimeError as error
:
2566 raise self
.LocalRuntimeError(self
, error
, "netr_DsRGetForestTrustInformation() failed")
2569 # here we try to enable all top level names
2570 local_forest_collision
= local_lsa
.lsaRSetForestTrustInformation(local_policy
,
2571 remote_lsa_info
.dns_domain
,
2572 lsa
.LSA_FOREST_TRUST_DOMAIN_INFO
,
2575 except RuntimeError as error
:
2576 raise self
.LocalRuntimeError(self
, error
, "lsaRSetForestTrustInformation() failed")
2578 self
.write_forest_trust_info(local_forest_info
,
2579 tln
=remote_lsa_info
.dns_domain
.string
,
2580 collisions
=local_forest_collision
)
2582 if remote_trust_info
:
2583 self
.outf
.write("Setup remote forest trust information...\n")
2585 # get all information about the local trust (from the perspective of the remote domain)
2586 # this triggers netr_GetForestTrustInformation to our domain.
2587 # and lsaRSetForestTrustInformation() remotely, but new top level
2588 # names are disabled by default.
2589 remote_forest_info
= remote_netlogon
.netr_DsRGetForestTrustInformation(remote_netlogon_info
.dc_unc
,
2590 local_lsa_info
.dns_domain
.string
,
2591 netlogon
.DS_GFTI_UPDATE_TDO
)
2592 except RuntimeError as error
:
2593 raise self
.RemoteRuntimeError(self
, error
, "netr_DsRGetForestTrustInformation() failed")
2596 # here we try to enable all top level names
2597 remote_forest_collision
= remote_lsa
.lsaRSetForestTrustInformation(remote_policy
,
2598 local_lsa_info
.dns_domain
,
2599 lsa
.LSA_FOREST_TRUST_DOMAIN_INFO
,
2602 except RuntimeError as error
:
2603 raise self
.RemoteRuntimeError(self
, error
, "lsaRSetForestTrustInformation() failed")
2605 self
.write_forest_trust_info(remote_forest_info
,
2606 tln
=local_lsa_info
.dns_domain
.string
,
2607 collisions
=remote_forest_collision
)
2609 if local_trust_info
.trust_direction
& lsa
.LSA_TRUST_DIRECTION_OUTBOUND
:
2610 self
.outf
.write("Validating outgoing trust...\n")
2612 local_trust_verify
= local_netlogon
.netr_LogonControl2Ex(local_netlogon_info
.dc_unc
,
2613 netlogon
.NETLOGON_CONTROL_TC_VERIFY
,
2615 remote_lsa_info
.dns_domain
.string
)
2616 except RuntimeError as error
:
2617 raise self
.LocalRuntimeError(self
, error
, "NETLOGON_CONTROL_TC_VERIFY failed")
2619 local_trust_status
= self
._uint
32(local_trust_verify
.pdc_connection_status
[0])
2620 local_conn_status
= self
._uint
32(local_trust_verify
.tc_connection_status
[0])
2622 if local_trust_verify
.flags
& netlogon
.NETLOGON_VERIFY_STATUS_RETURNED
:
2623 local_validation
= "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2624 local_trust_verify
.trusted_dc_name
,
2625 local_trust_verify
.tc_connection_status
[1],
2626 local_trust_verify
.pdc_connection_status
[1])
2628 local_validation
= "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2629 local_trust_verify
.trusted_dc_name
,
2630 local_trust_verify
.tc_connection_status
[1],
2631 local_trust_verify
.pdc_connection_status
[1])
2633 if local_trust_status
!= werror
.WERR_SUCCESS
or local_conn_status
!= werror
.WERR_SUCCESS
:
2634 raise CommandError(local_validation
)
2636 self
.outf
.write("OK: %s\n" % local_validation
)
2638 if remote_trust_info
:
2639 if remote_trust_info
.trust_direction
& lsa
.LSA_TRUST_DIRECTION_OUTBOUND
:
2640 self
.outf
.write("Validating incoming trust...\n")
2642 remote_trust_verify
= remote_netlogon
.netr_LogonControl2Ex(remote_netlogon_info
.dc_unc
,
2643 netlogon
.NETLOGON_CONTROL_TC_VERIFY
,
2645 local_lsa_info
.dns_domain
.string
)
2646 except RuntimeError as error
:
2647 raise self
.RemoteRuntimeError(self
, error
, "NETLOGON_CONTROL_TC_VERIFY failed")
2649 remote_trust_status
= self
._uint
32(remote_trust_verify
.pdc_connection_status
[0])
2650 remote_conn_status
= self
._uint
32(remote_trust_verify
.tc_connection_status
[0])
2652 if remote_trust_verify
.flags
& netlogon
.NETLOGON_VERIFY_STATUS_RETURNED
:
2653 remote_validation
= "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2654 remote_trust_verify
.trusted_dc_name
,
2655 remote_trust_verify
.tc_connection_status
[1],
2656 remote_trust_verify
.pdc_connection_status
[1])
2658 remote_validation
= "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2659 remote_trust_verify
.trusted_dc_name
,
2660 remote_trust_verify
.tc_connection_status
[1],
2661 remote_trust_verify
.pdc_connection_status
[1])
2663 if remote_trust_status
!= werror
.WERR_SUCCESS
or remote_conn_status
!= werror
.WERR_SUCCESS
:
2664 raise CommandError(remote_validation
)
2666 self
.outf
.write("OK: %s\n" % remote_validation
)
2668 if remote_tdo_handle
is not None:
2670 remote_lsa
.Close(remote_tdo_handle
)
2671 except RuntimeError as error
:
2673 remote_tdo_handle
= None
2674 if local_tdo_handle
is not None:
2676 local_lsa
.Close(local_tdo_handle
)
2677 except RuntimeError as error
:
2679 local_tdo_handle
= None
2681 self
.outf
.write("Success.\n")
2684 class cmd_domain_trust_delete(DomainTrustCommand
):
2685 """Delete a domain trust."""
2687 synopsis
= "%prog DOMAIN [options]"
2689 takes_optiongroups
= {
2690 "sambaopts": options
.SambaOptions
,
2691 "versionopts": options
.VersionOptions
,
2692 "credopts": options
.CredentialsOptions
,
2693 "localdcopts": LocalDCCredentialsOptions
,
2697 Option("--delete-location", type="choice", metavar
="LOCATION",
2698 choices
=["local", "both"],
2699 help="Where to delete the trusted domain object: 'local' or 'both'.",
2700 dest
='delete_location',
2704 takes_args
= ["domain"]
2706 def run(self
, domain
, sambaopts
=None, localdcopts
=None, credopts
=None, versionopts
=None,
2707 delete_location
=None):
2709 local_policy_access
= lsa
.LSA_POLICY_VIEW_LOCAL_INFORMATION
2710 local_policy_access |
= lsa
.LSA_POLICY_TRUST_ADMIN
2711 local_policy_access |
= lsa
.LSA_POLICY_CREATE_SECRET
2713 if delete_location
== "local":
2714 remote_policy_access
= None
2716 remote_policy_access
= lsa
.LSA_POLICY_VIEW_LOCAL_INFORMATION
2717 remote_policy_access |
= lsa
.LSA_POLICY_TRUST_ADMIN
2718 remote_policy_access |
= lsa
.LSA_POLICY_CREATE_SECRET
2720 local_server
= self
.setup_local_server(sambaopts
, localdcopts
)
2722 local_lsa
= self
.new_local_lsa_connection()
2723 except RuntimeError as error
:
2724 raise self
.LocalRuntimeError(self
, error
, "failed to connect lsa server")
2727 (local_policy
, local_lsa_info
) = self
.get_lsa_info(local_lsa
, local_policy_access
)
2728 except RuntimeError as error
:
2729 raise self
.LocalRuntimeError(self
, error
, "failed to query LSA_POLICY_INFO_DNS")
2731 self
.outf
.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2732 local_lsa_info
.name
.string
,
2733 local_lsa_info
.dns_domain
.string
,
2734 local_lsa_info
.sid
))
2736 local_tdo_info
= None
2737 local_tdo_handle
= None
2738 remote_tdo_info
= None
2739 remote_tdo_handle
= None
2741 lsaString
= lsa
.String()
2743 lsaString
.string
= domain
2744 local_tdo_info
= local_lsa
.QueryTrustedDomainInfoByName(local_policy
,
2745 lsaString
, lsa
.LSA_TRUSTED_DOMAIN_INFO_INFO_EX
)
2746 except NTSTATUSError
as error
:
2747 if self
.check_runtime_error(error
, ntstatus
.NT_STATUS_OBJECT_NAME_NOT_FOUND
):
2748 raise CommandError("Failed to find trust for domain '%s'" % domain
)
2749 raise self
.RemoteRuntimeError(self
, error
, "failed to locate remote server")
2752 if remote_policy_access
is not None:
2754 remote_server
= self
.setup_remote_server(credopts
, domain
)
2755 except RuntimeError as error
:
2756 raise self
.RemoteRuntimeError(self
, error
, "failed to locate remote server")
2759 remote_lsa
= self
.new_remote_lsa_connection()
2760 except RuntimeError as error
:
2761 raise self
.RemoteRuntimeError(self
, error
, "failed to connect lsa server")
2764 (remote_policy
, remote_lsa_info
) = self
.get_lsa_info(remote_lsa
, remote_policy_access
)
2765 except RuntimeError as error
:
2766 raise self
.RemoteRuntimeError(self
, error
, "failed to query LSA_POLICY_INFO_DNS")
2768 self
.outf
.write("RemoteDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2769 remote_lsa_info
.name
.string
,
2770 remote_lsa_info
.dns_domain
.string
,
2771 remote_lsa_info
.sid
))
2773 if remote_lsa_info
.sid
!= local_tdo_info
.sid
or \
2774 remote_lsa_info
.name
.string
!= local_tdo_info
.netbios_name
.string
or \
2775 remote_lsa_info
.dns_domain
.string
!= local_tdo_info
.domain_name
.string
:
2776 raise CommandError("LocalTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2777 local_tdo_info
.netbios_name
.string
,
2778 local_tdo_info
.domain_name
.string
,
2779 local_tdo_info
.sid
))
2782 lsaString
.string
= local_lsa_info
.dns_domain
.string
2783 remote_tdo_info
= remote_lsa
.QueryTrustedDomainInfoByName(remote_policy
,
2784 lsaString
, lsa
.LSA_TRUSTED_DOMAIN_INFO_INFO_EX
)
2785 except NTSTATUSError
as error
:
2786 if not self
.check_runtime_error(error
, ntstatus
.NT_STATUS_OBJECT_NAME_NOT_FOUND
):
2787 raise self
.RemoteRuntimeError(self
, error
, "QueryTrustedDomainInfoByName(%s)" % (
2791 if remote_tdo_info
is not None:
2792 if local_lsa_info
.sid
!= remote_tdo_info
.sid
or \
2793 local_lsa_info
.name
.string
!= remote_tdo_info
.netbios_name
.string
or \
2794 local_lsa_info
.dns_domain
.string
!= remote_tdo_info
.domain_name
.string
:
2795 raise CommandError("RemoteTDO inconsistend: Netbios[%s] DNS[%s] SID[%s]" % (
2796 remote_tdo_info
.netbios_name
.string
,
2797 remote_tdo_info
.domain_name
.string
,
2798 remote_tdo_info
.sid
))
2800 if local_tdo_info
is not None:
2802 lsaString
.string
= local_tdo_info
.domain_name
.string
2803 local_tdo_handle
= local_lsa
.OpenTrustedDomainByName(local_policy
,
2805 security
.SEC_STD_DELETE
)
2806 except RuntimeError as error
:
2807 raise self
.LocalRuntimeError(self
, error
, "OpenTrustedDomainByName(%s)" % (
2810 local_lsa
.DeleteObject(local_tdo_handle
)
2811 local_tdo_handle
= None
2813 if remote_tdo_info
is not None:
2815 lsaString
.string
= remote_tdo_info
.domain_name
.string
2816 remote_tdo_handle
= remote_lsa
.OpenTrustedDomainByName(remote_policy
,
2818 security
.SEC_STD_DELETE
)
2819 except RuntimeError as error
:
2820 raise self
.RemoteRuntimeError(self
, error
, "OpenTrustedDomainByName(%s)" % (
2823 if remote_tdo_handle
is not None:
2825 remote_lsa
.DeleteObject(remote_tdo_handle
)
2826 remote_tdo_handle
= None
2827 self
.outf
.write("RemoteTDO deleted.\n")
2828 except RuntimeError as error
:
2829 self
.outf
.write("%s\n" % self
.RemoteRuntimeError(self
, error
, "DeleteObject() failed"))
2831 if local_tdo_handle
is not None:
2833 local_lsa
.DeleteObject(local_tdo_handle
)
2834 local_tdo_handle
= None
2835 self
.outf
.write("LocalTDO deleted.\n")
2836 except RuntimeError as error
:
2837 self
.outf
.write("%s\n" % self
.LocalRuntimeError(self
, error
, "DeleteObject() failed"))
2841 class cmd_domain_trust_validate(DomainTrustCommand
):
2842 """Validate a domain trust."""
2844 synopsis
= "%prog DOMAIN [options]"
2846 takes_optiongroups
= {
2847 "sambaopts": options
.SambaOptions
,
2848 "versionopts": options
.VersionOptions
,
2849 "credopts": options
.CredentialsOptions
,
2850 "localdcopts": LocalDCCredentialsOptions
,
2854 Option("--validate-location", type="choice", metavar
="LOCATION",
2855 choices
=["local", "both"],
2856 help="Where to validate the trusted domain object: 'local' or 'both'.",
2857 dest
='validate_location',
2861 takes_args
= ["domain"]
2863 def run(self
, domain
, sambaopts
=None, versionopts
=None, credopts
=None, localdcopts
=None,
2864 validate_location
=None):
2866 local_policy_access
= lsa
.LSA_POLICY_VIEW_LOCAL_INFORMATION
2868 local_server
= self
.setup_local_server(sambaopts
, localdcopts
)
2870 local_lsa
= self
.new_local_lsa_connection()
2871 except RuntimeError as error
:
2872 raise self
.LocalRuntimeError(self
, error
, "failed to connect lsa server")
2875 (local_policy
, local_lsa_info
) = self
.get_lsa_info(local_lsa
, local_policy_access
)
2876 except RuntimeError as error
:
2877 raise self
.LocalRuntimeError(self
, error
, "failed to query LSA_POLICY_INFO_DNS")
2879 self
.outf
.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
2880 local_lsa_info
.name
.string
,
2881 local_lsa_info
.dns_domain
.string
,
2882 local_lsa_info
.sid
))
2885 lsaString
= lsa
.String()
2886 lsaString
.string
= domain
2887 local_tdo_info
= local_lsa
.QueryTrustedDomainInfoByName(local_policy
,
2888 lsaString
, lsa
.LSA_TRUSTED_DOMAIN_INFO_INFO_EX
)
2889 except NTSTATUSError
as error
:
2890 if self
.check_runtime_error(error
, ntstatus
.NT_STATUS_OBJECT_NAME_NOT_FOUND
):
2891 raise CommandError("trusted domain object does not exist for domain [%s]" % domain
)
2893 raise self
.LocalRuntimeError(self
, error
, "QueryTrustedDomainInfoByName(INFO_EX) failed")
2895 self
.outf
.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
2896 local_tdo_info
.netbios_name
.string
,
2897 local_tdo_info
.domain_name
.string
,
2898 local_tdo_info
.sid
))
2901 local_netlogon
= self
.new_local_netlogon_connection()
2902 except RuntimeError as error
:
2903 raise self
.LocalRuntimeError(self
, error
, "failed to connect netlogon server")
2906 local_trust_verify
= local_netlogon
.netr_LogonControl2Ex(local_server
,
2907 netlogon
.NETLOGON_CONTROL_TC_VERIFY
,
2909 local_tdo_info
.domain_name
.string
)
2910 except RuntimeError as error
:
2911 raise self
.LocalRuntimeError(self
, error
, "NETLOGON_CONTROL_TC_VERIFY failed")
2913 local_trust_status
= self
._uint
32(local_trust_verify
.pdc_connection_status
[0])
2914 local_conn_status
= self
._uint
32(local_trust_verify
.tc_connection_status
[0])
2916 if local_trust_verify
.flags
& netlogon
.NETLOGON_VERIFY_STATUS_RETURNED
:
2917 local_validation
= "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2918 local_trust_verify
.trusted_dc_name
,
2919 local_trust_verify
.tc_connection_status
[1],
2920 local_trust_verify
.pdc_connection_status
[1])
2922 local_validation
= "LocalValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2923 local_trust_verify
.trusted_dc_name
,
2924 local_trust_verify
.tc_connection_status
[1],
2925 local_trust_verify
.pdc_connection_status
[1])
2927 if local_trust_status
!= werror
.WERR_SUCCESS
or local_conn_status
!= werror
.WERR_SUCCESS
:
2928 raise CommandError(local_validation
)
2930 self
.outf
.write("OK: %s\n" % local_validation
)
2933 server
= local_trust_verify
.trusted_dc_name
.replace('\\', '')
2934 domain_and_server
= "%s\\%s" % (local_tdo_info
.domain_name
.string
, server
)
2935 local_trust_rediscover
= local_netlogon
.netr_LogonControl2Ex(local_server
,
2936 netlogon
.NETLOGON_CONTROL_REDISCOVER
,
2939 except RuntimeError as error
:
2940 raise self
.LocalRuntimeError(self
, error
, "NETLOGON_CONTROL_REDISCOVER failed")
2942 local_conn_status
= self
._uint
32(local_trust_rediscover
.tc_connection_status
[0])
2943 local_rediscover
= "LocalRediscover: DC[%s] CONNECTION[%s]" % (
2944 local_trust_rediscover
.trusted_dc_name
,
2945 local_trust_rediscover
.tc_connection_status
[1])
2947 if local_conn_status
!= werror
.WERR_SUCCESS
:
2948 raise CommandError(local_rediscover
)
2950 self
.outf
.write("OK: %s\n" % local_rediscover
)
2952 if validate_location
!= "local":
2954 remote_server
= self
.setup_remote_server(credopts
, domain
, require_pdc
=False)
2955 except RuntimeError as error
:
2956 raise self
.RemoteRuntimeError(self
, error
, "failed to locate remote server")
2959 remote_netlogon
= self
.new_remote_netlogon_connection()
2960 except RuntimeError as error
:
2961 raise self
.RemoteRuntimeError(self
, error
, "failed to connect netlogon server")
2964 remote_trust_verify
= remote_netlogon
.netr_LogonControl2Ex(remote_server
,
2965 netlogon
.NETLOGON_CONTROL_TC_VERIFY
,
2967 local_lsa_info
.dns_domain
.string
)
2968 except RuntimeError as error
:
2969 raise self
.RemoteRuntimeError(self
, error
, "NETLOGON_CONTROL_TC_VERIFY failed")
2971 remote_trust_status
= self
._uint
32(remote_trust_verify
.pdc_connection_status
[0])
2972 remote_conn_status
= self
._uint
32(remote_trust_verify
.tc_connection_status
[0])
2974 if remote_trust_verify
.flags
& netlogon
.NETLOGON_VERIFY_STATUS_RETURNED
:
2975 remote_validation
= "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s] VERIFY_STATUS_RETURNED" % (
2976 remote_trust_verify
.trusted_dc_name
,
2977 remote_trust_verify
.tc_connection_status
[1],
2978 remote_trust_verify
.pdc_connection_status
[1])
2980 remote_validation
= "RemoteValidation: DC[%s] CONNECTION[%s] TRUST[%s]" % (
2981 remote_trust_verify
.trusted_dc_name
,
2982 remote_trust_verify
.tc_connection_status
[1],
2983 remote_trust_verify
.pdc_connection_status
[1])
2985 if remote_trust_status
!= werror
.WERR_SUCCESS
or remote_conn_status
!= werror
.WERR_SUCCESS
:
2986 raise CommandError(remote_validation
)
2988 self
.outf
.write("OK: %s\n" % remote_validation
)
2991 server
= remote_trust_verify
.trusted_dc_name
.replace('\\', '')
2992 domain_and_server
= "%s\\%s" % (local_lsa_info
.dns_domain
.string
, server
)
2993 remote_trust_rediscover
= remote_netlogon
.netr_LogonControl2Ex(remote_server
,
2994 netlogon
.NETLOGON_CONTROL_REDISCOVER
,
2997 except RuntimeError as error
:
2998 raise self
.RemoteRuntimeError(self
, error
, "NETLOGON_CONTROL_REDISCOVER failed")
3000 remote_conn_status
= self
._uint
32(remote_trust_rediscover
.tc_connection_status
[0])
3002 remote_rediscover
= "RemoteRediscover: DC[%s] CONNECTION[%s]" % (
3003 remote_trust_rediscover
.trusted_dc_name
,
3004 remote_trust_rediscover
.tc_connection_status
[1])
3006 if remote_conn_status
!= werror
.WERR_SUCCESS
:
3007 raise CommandError(remote_rediscover
)
3009 self
.outf
.write("OK: %s\n" % remote_rediscover
)
3013 class cmd_domain_trust_namespaces(DomainTrustCommand
):
3014 """Manage forest trust namespaces."""
3016 synopsis
= "%prog [DOMAIN] [options]"
3018 takes_optiongroups
= {
3019 "sambaopts": options
.SambaOptions
,
3020 "versionopts": options
.VersionOptions
,
3021 "localdcopts": LocalDCCredentialsOptions
,
3025 Option("--refresh", type="choice", metavar
="check|store",
3026 choices
=["check", "store", None],
3027 help="List and maybe store refreshed forest trust information: 'check' or 'store'.",
3030 Option("--enable-all", action
="store_true",
3031 help="Try to update disabled entries, not allowed with --refresh=check.",
3034 Option("--enable-tln", action
="append", metavar
='DNSDOMAIN',
3035 help="Enable a top level name entry. Can be specified multiple times.",
3038 Option("--disable-tln", action
="append", metavar
='DNSDOMAIN',
3039 help="Disable a top level name entry. Can be specified multiple times.",
3042 Option("--add-tln-ex", action
="append", metavar
='DNSDOMAIN',
3043 help="Add a top level exclusion entry. Can be specified multiple times.",
3046 Option("--delete-tln-ex", action
="append", metavar
='DNSDOMAIN',
3047 help="Delete a top level exclusion entry. Can be specified multiple times.",
3048 dest
='delete_tln_ex',
3050 Option("--enable-nb", action
="append", metavar
='NETBIOSDOMAIN',
3051 help="Enable a netbios name in a domain entry. Can be specified multiple times.",
3054 Option("--disable-nb", action
="append", metavar
='NETBIOSDOMAIN',
3055 help="Disable a netbios name in a domain entry. Can be specified multiple times.",
3058 Option("--enable-sid", action
="append", metavar
='DOMAINSID',
3059 help="Enable a SID in a domain entry. Can be specified multiple times.",
3060 dest
='enable_sid_str',
3062 Option("--disable-sid", action
="append", metavar
='DOMAINSID',
3063 help="Disable a SID in a domain entry. Can be specified multiple times.",
3064 dest
='disable_sid_str',
3066 Option("--add-upn-suffix", action
="append", metavar
='DNSDOMAIN',
3067 help="Add a new uPNSuffixes attribute for the local forest. Can be specified multiple times.",
3070 Option("--delete-upn-suffix", action
="append", metavar
='DNSDOMAIN',
3071 help="Delete an existing uPNSuffixes attribute of the local forest. Can be specified multiple times.",
3074 Option("--add-spn-suffix", action
="append", metavar
='DNSDOMAIN',
3075 help="Add a new msDS-SPNSuffixes attribute for the local forest. Can be specified multiple times.",
3078 Option("--delete-spn-suffix", action
="append", metavar
='DNSDOMAIN',
3079 help="Delete an existing msDS-SPNSuffixes attribute of the local forest. Can be specified multiple times.",
3084 takes_args
= ["domain?"]
3086 def run(self
, domain
=None, sambaopts
=None, localdcopts
=None, versionopts
=None,
3087 refresh
=None, enable_all
=False,
3088 enable_tln
=[], disable_tln
=[], add_tln_ex
=[], delete_tln_ex
=[],
3089 enable_sid_str
=[], disable_sid_str
=[], enable_nb
=[], disable_nb
=[],
3090 add_upn
=[], delete_upn
=[], add_spn
=[], delete_spn
=[]):
3092 require_update
= False
3095 if refresh
== "store":
3096 raise CommandError("--refresh=%s not allowed without DOMAIN" % refresh
)
3099 raise CommandError("--enable-all not allowed without DOMAIN")
3101 if len(enable_tln
) > 0:
3102 raise CommandError("--enable-tln not allowed without DOMAIN")
3103 if len(disable_tln
) > 0:
3104 raise CommandError("--disable-tln not allowed without DOMAIN")
3106 if len(add_tln_ex
) > 0:
3107 raise CommandError("--add-tln-ex not allowed without DOMAIN")
3108 if len(delete_tln_ex
) > 0:
3109 raise CommandError("--delete-tln-ex not allowed without DOMAIN")
3111 if len(enable_nb
) > 0:
3112 raise CommandError("--enable-nb not allowed without DOMAIN")
3113 if len(disable_nb
) > 0:
3114 raise CommandError("--disable-nb not allowed without DOMAIN")
3116 if len(enable_sid_str
) > 0:
3117 raise CommandError("--enable-sid not allowed without DOMAIN")
3118 if len(disable_sid_str
) > 0:
3119 raise CommandError("--disable-sid not allowed without DOMAIN")
3121 if len(add_upn
) > 0:
3123 if not n
.startswith("*."):
3125 raise CommandError("value[%s] specified for --add-upn-suffix should not include with '*.'" % n
)
3126 require_update
= True
3127 if len(delete_upn
) > 0:
3128 for n
in delete_upn
:
3129 if not n
.startswith("*."):
3131 raise CommandError("value[%s] specified for --delete-upn-suffix should not include with '*.'" % n
)
3132 require_update
= True
3134 for d
in delete_upn
:
3135 if a
.lower() != d
.lower():
3137 raise CommandError("value[%s] specified for --add-upn-suffix and --delete-upn-suffix" % a
)
3139 if len(add_spn
) > 0:
3141 if not n
.startswith("*."):
3143 raise CommandError("value[%s] specified for --add-spn-suffix should not include with '*.'" % n
)
3144 require_update
= True
3145 if len(delete_spn
) > 0:
3146 for n
in delete_spn
:
3147 if not n
.startswith("*."):
3149 raise CommandError("value[%s] specified for --delete-spn-suffix should not include with '*.'" % n
)
3150 require_update
= True
3152 for d
in delete_spn
:
3153 if a
.lower() != d
.lower():
3155 raise CommandError("value[%s] specified for --add-spn-suffix and --delete-spn-suffix" % a
)
3157 if len(add_upn
) > 0:
3158 raise CommandError("--add-upn-suffix not allowed together with DOMAIN")
3159 if len(delete_upn
) > 0:
3160 raise CommandError("--delete-upn-suffix not allowed together with DOMAIN")
3161 if len(add_spn
) > 0:
3162 raise CommandError("--add-spn-suffix not allowed together with DOMAIN")
3163 if len(delete_spn
) > 0:
3164 raise CommandError("--delete-spn-suffix not allowed together with DOMAIN")
3166 if refresh
is not None:
3167 if refresh
== "store":
3168 require_update
= True
3170 if enable_all
and refresh
!= "store":
3171 raise CommandError("--enable-all not allowed together with --refresh=%s" % refresh
)
3173 if len(enable_tln
) > 0:
3174 raise CommandError("--enable-tln not allowed together with --refresh")
3175 if len(disable_tln
) > 0:
3176 raise CommandError("--disable-tln not allowed together with --refresh")
3178 if len(add_tln_ex
) > 0:
3179 raise CommandError("--add-tln-ex not allowed together with --refresh")
3180 if len(delete_tln_ex
) > 0:
3181 raise CommandError("--delete-tln-ex not allowed together with --refresh")
3183 if len(enable_nb
) > 0:
3184 raise CommandError("--enable-nb not allowed together with --refresh")
3185 if len(disable_nb
) > 0:
3186 raise CommandError("--disable-nb not allowed together with --refresh")
3188 if len(enable_sid_str
) > 0:
3189 raise CommandError("--enable-sid not allowed together with --refresh")
3190 if len(disable_sid_str
) > 0:
3191 raise CommandError("--disable-sid not allowed together with --refresh")
3194 require_update
= True
3196 if len(enable_tln
) > 0:
3197 raise CommandError("--enable-tln not allowed together with --enable-all")
3199 if len(enable_nb
) > 0:
3200 raise CommandError("--enable-nb not allowed together with --enable-all")
3202 if len(enable_sid_str
) > 0:
3203 raise CommandError("--enable-sid not allowed together with --enable-all")
3205 if len(enable_tln
) > 0:
3206 require_update
= True
3207 if len(disable_tln
) > 0:
3208 require_update
= True
3209 for e
in enable_tln
:
3210 for d
in disable_tln
:
3211 if e
.lower() != d
.lower():
3213 raise CommandError("value[%s] specified for --enable-tln and --disable-tln" % e
)
3215 if len(add_tln_ex
) > 0:
3216 for n
in add_tln_ex
:
3217 if not n
.startswith("*."):
3219 raise CommandError("value[%s] specified for --add-tln-ex should not include with '*.'" % n
)
3220 require_update
= True
3221 if len(delete_tln_ex
) > 0:
3222 for n
in delete_tln_ex
:
3223 if not n
.startswith("*."):
3225 raise CommandError("value[%s] specified for --delete-tln-ex should not include with '*.'" % n
)
3226 require_update
= True
3227 for a
in add_tln_ex
:
3228 for d
in delete_tln_ex
:
3229 if a
.lower() != d
.lower():
3231 raise CommandError("value[%s] specified for --add-tln-ex and --delete-tln-ex" % a
)
3233 if len(enable_nb
) > 0:
3234 require_update
= True
3235 if len(disable_nb
) > 0:
3236 require_update
= True
3238 for d
in disable_nb
:
3239 if e
.upper() != d
.upper():
3241 raise CommandError("value[%s] specified for --enable-nb and --disable-nb" % e
)
3244 for s
in enable_sid_str
:
3246 sid
= security
.dom_sid(s
)
3247 except TypeError as error
:
3248 raise CommandError("value[%s] specified for --enable-sid is not a valid SID" % s
)
3249 enable_sid
.append(sid
)
3251 for s
in disable_sid_str
:
3253 sid
= security
.dom_sid(s
)
3254 except TypeError as error
:
3255 raise CommandError("value[%s] specified for --disable-sid is not a valid SID" % s
)
3256 disable_sid
.append(sid
)
3257 if len(enable_sid
) > 0:
3258 require_update
= True
3259 if len(disable_sid
) > 0:
3260 require_update
= True
3261 for e
in enable_sid
:
3262 for d
in disable_sid
:
3265 raise CommandError("value[%s] specified for --enable-sid and --disable-sid" % e
)
3267 local_policy_access
= lsa
.LSA_POLICY_VIEW_LOCAL_INFORMATION
3269 local_policy_access |
= lsa
.LSA_POLICY_TRUST_ADMIN
3271 local_server
= self
.setup_local_server(sambaopts
, localdcopts
)
3273 local_lsa
= self
.new_local_lsa_connection()
3274 except RuntimeError as error
:
3275 raise self
.LocalRuntimeError(self
, error
, "failed to connect lsa server")
3278 (local_policy
, local_lsa_info
) = self
.get_lsa_info(local_lsa
, local_policy_access
)
3279 except RuntimeError as error
:
3280 raise self
.LocalRuntimeError(self
, error
, "failed to query LSA_POLICY_INFO_DNS")
3282 self
.outf
.write("LocalDomain Netbios[%s] DNS[%s] SID[%s]\n" % (
3283 local_lsa_info
.name
.string
,
3284 local_lsa_info
.dns_domain
.string
,
3285 local_lsa_info
.sid
))
3289 local_netlogon
= self
.new_local_netlogon_connection()
3290 except RuntimeError as error
:
3291 raise self
.LocalRuntimeError(self
, error
, "failed to connect netlogon server")
3294 local_netlogon_info
= self
.get_netlogon_dc_info(local_netlogon
, local_server
)
3295 except RuntimeError as error
:
3296 raise self
.LocalRuntimeError(self
, error
, "failed to get netlogon dc info")
3298 if local_netlogon_info
.domain_name
!= local_netlogon_info
.forest_name
:
3299 raise CommandError("The local domain [%s] is not the forest root [%s]" % (
3300 local_netlogon_info
.domain_name
,
3301 local_netlogon_info
.forest_name
))
3304 # get all information about our own forest
3305 own_forest_info
= local_netlogon
.netr_DsRGetForestTrustInformation(local_netlogon_info
.dc_unc
,
3307 except RuntimeError as error
:
3308 if self
.check_runtime_error(error
, werror
.WERR_RPC_S_PROCNUM_OUT_OF_RANGE
):
3309 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3312 if self
.check_runtime_error(error
, werror
.WERR_INVALID_FUNCTION
):
3313 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3316 if self
.check_runtime_error(error
, werror
.WERR_NERR_ACFNOTLOADED
):
3317 raise CommandError("LOCAL_DC[%s]: netr_DsRGetForestTrustInformation() not supported." % (
3320 raise self
.LocalRuntimeError(self
, error
, "netr_DsRGetForestTrustInformation() failed")
3322 self
.outf
.write("Own forest trust information...\n")
3323 self
.write_forest_trust_info(own_forest_info
,
3324 tln
=local_lsa_info
.dns_domain
.string
)
3327 local_samdb
= self
.new_local_ldap_connection()
3328 except RuntimeError as error
:
3329 raise self
.LocalRuntimeError(self
, error
, "failed to connect to SamDB")
3331 local_partitions_dn
= "CN=Partitions,%s" % str(local_samdb
.get_config_basedn())
3332 attrs
= ['uPNSuffixes', 'msDS-SPNSuffixes']
3334 msgs
= local_samdb
.search(base
=local_partitions_dn
,
3335 scope
=ldb
.SCOPE_BASE
,
3336 expression
="(objectClass=crossRefContainer)",
3338 stored_msg
= msgs
[0]
3339 except ldb
.LdbError
as error
:
3340 raise self
.LocalLdbError(self
, error
, "failed to search partition dn")
3342 stored_upn_vals
= []
3343 if 'uPNSuffixes' in stored_msg
:
3344 stored_upn_vals
.extend(stored_msg
['uPNSuffixes'])
3346 stored_spn_vals
= []
3347 if 'msDS-SPNSuffixes' in stored_msg
:
3348 stored_spn_vals
.extend(stored_msg
['msDS-SPNSuffixes'])
3350 self
.outf
.write("Stored uPNSuffixes attributes[%d]:\n" % len(stored_upn_vals
))
3351 for v
in stored_upn_vals
:
3352 self
.outf
.write("TLN: %-32s DNS[*.%s]\n" % ("", v
))
3353 self
.outf
.write("Stored msDS-SPNSuffixes attributes[%d]:\n" % len(stored_spn_vals
))
3354 for v
in stored_spn_vals
:
3355 self
.outf
.write("TLN: %-32s DNS[*.%s]\n" % ("", v
))
3357 if not require_update
:
3361 update_upn_vals
= []
3362 update_upn_vals
.extend(stored_upn_vals
)
3365 update_spn_vals
= []
3366 update_spn_vals
.extend(stored_spn_vals
)
3370 for i
in xrange(0, len(update_upn_vals
)):
3371 v
= update_upn_vals
[i
]
3372 if v
.lower() != upn
.lower():
3377 raise CommandError("Entry already present for value[%s] specified for --add-upn-suffix" % upn
)
3378 update_upn_vals
.append(upn
)
3381 for upn
in delete_upn
:
3383 for i
in xrange(0, len(update_upn_vals
)):
3384 v
= update_upn_vals
[i
]
3385 if v
.lower() != upn
.lower():
3390 raise CommandError("Entry not found for value[%s] specified for --delete-upn-suffix" % upn
)
3392 update_upn_vals
.pop(idx
)
3397 for i
in xrange(0, len(update_spn_vals
)):
3398 v
= update_spn_vals
[i
]
3399 if v
.lower() != spn
.lower():
3404 raise CommandError("Entry already present for value[%s] specified for --add-spn-suffix" % spn
)
3405 update_spn_vals
.append(spn
)
3408 for spn
in delete_spn
:
3410 for i
in xrange(0, len(update_spn_vals
)):
3411 v
= update_spn_vals
[i
]
3412 if v
.lower() != spn
.lower():
3417 raise CommandError("Entry not found for value[%s] specified for --delete-spn-suffix" % spn
)
3419 update_spn_vals
.pop(idx
)
3422 self
.outf
.write("Update uPNSuffixes attributes[%d]:\n" % len(update_upn_vals
))
3423 for v
in update_upn_vals
:
3424 self
.outf
.write("TLN: %-32s DNS[*.%s]\n" % ("", v
))
3425 self
.outf
.write("Update msDS-SPNSuffixes attributes[%d]:\n" % len(update_spn_vals
))
3426 for v
in update_spn_vals
:
3427 self
.outf
.write("TLN: %-32s DNS[*.%s]\n" % ("", v
))
3429 update_msg
= ldb
.Message()
3430 update_msg
.dn
= stored_msg
.dn
3433 update_msg
['uPNSuffixes'] = ldb
.MessageElement(update_upn_vals
,
3434 ldb
.FLAG_MOD_REPLACE
,
3437 update_msg
['msDS-SPNSuffixes'] = ldb
.MessageElement(update_spn_vals
,
3438 ldb
.FLAG_MOD_REPLACE
,
3441 local_samdb
.modify(update_msg
)
3442 except ldb
.LdbError
as error
:
3443 raise self
.LocalLdbError(self
, error
, "failed to update partition dn")
3446 stored_forest_info
= local_netlogon
.netr_DsRGetForestTrustInformation(local_netlogon_info
.dc_unc
,
3448 except RuntimeError as error
:
3449 raise self
.LocalRuntimeError(self
, error
, "netr_DsRGetForestTrustInformation() failed")
3451 self
.outf
.write("Stored forest trust information...\n")
3452 self
.write_forest_trust_info(stored_forest_info
,
3453 tln
=local_lsa_info
.dns_domain
.string
)
3457 lsaString
= lsa
.String()
3458 lsaString
.string
= domain
3459 local_tdo_info
= local_lsa
.QueryTrustedDomainInfoByName(local_policy
,
3460 lsaString
, lsa
.LSA_TRUSTED_DOMAIN_INFO_INFO_EX
)
3461 except NTSTATUSError
as error
:
3462 if self
.check_runtime_error(error
, ntstatus
.NT_STATUS_OBJECT_NAME_NOT_FOUND
):
3463 raise CommandError("trusted domain object does not exist for domain [%s]" % domain
)
3465 raise self
.LocalRuntimeError(self
, error
, "QueryTrustedDomainInfoByName(INFO_EX) failed")
3467 self
.outf
.write("LocalTDO Netbios[%s] DNS[%s] SID[%s]\n" % (
3468 local_tdo_info
.netbios_name
.string
,
3469 local_tdo_info
.domain_name
.string
,
3470 local_tdo_info
.sid
))
3472 if not local_tdo_info
.trust_attributes
& lsa
.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
:
3473 raise CommandError("trusted domain object for domain [%s] is not marked as FOREST_TRANSITIVE." % domain
)
3475 if refresh
is not None:
3477 local_netlogon
= self
.new_local_netlogon_connection()
3478 except RuntimeError as error
:
3479 raise self
.LocalRuntimeError(self
, error
, "failed to connect netlogon server")
3482 local_netlogon_info
= self
.get_netlogon_dc_info(local_netlogon
, local_server
)
3483 except RuntimeError as error
:
3484 raise self
.LocalRuntimeError(self
, error
, "failed to get netlogon dc info")
3486 lsa_update_check
= 1
3487 if refresh
== "store":
3488 netlogon_update_tdo
= netlogon
.DS_GFTI_UPDATE_TDO
3490 lsa_update_check
= 0
3492 netlogon_update_tdo
= 0
3495 # get all information about the remote trust
3496 # this triggers netr_GetForestTrustInformation to the remote domain
3497 # and lsaRSetForestTrustInformation() locally, but new top level
3498 # names are disabled by default.
3499 fresh_forest_info
= local_netlogon
.netr_DsRGetForestTrustInformation(local_netlogon_info
.dc_unc
,
3500 local_tdo_info
.domain_name
.string
,
3501 netlogon_update_tdo
)
3502 except RuntimeError as error
:
3503 raise self
.LocalRuntimeError(self
, error
, "netr_DsRGetForestTrustInformation() failed")
3506 fresh_forest_collision
= local_lsa
.lsaRSetForestTrustInformation(local_policy
,
3507 local_tdo_info
.domain_name
,
3508 lsa
.LSA_FOREST_TRUST_DOMAIN_INFO
,
3511 except RuntimeError as error
:
3512 raise self
.LocalRuntimeError(self
, error
, "lsaRSetForestTrustInformation() failed")
3514 self
.outf
.write("Fresh forest trust information...\n")
3515 self
.write_forest_trust_info(fresh_forest_info
,
3516 tln
=local_tdo_info
.domain_name
.string
,
3517 collisions
=fresh_forest_collision
)
3519 if refresh
== "store":
3521 lsaString
= lsa
.String()
3522 lsaString
.string
= local_tdo_info
.domain_name
.string
3523 stored_forest_info
= local_lsa
.lsaRQueryForestTrustInformation(local_policy
,
3525 lsa
.LSA_FOREST_TRUST_DOMAIN_INFO
)
3526 except RuntimeError as error
:
3527 raise self
.LocalRuntimeError(self
, error
, "lsaRQueryForestTrustInformation() failed")
3529 self
.outf
.write("Stored forest trust information...\n")
3530 self
.write_forest_trust_info(stored_forest_info
,
3531 tln
=local_tdo_info
.domain_name
.string
)
3536 # The none --refresh path
3540 lsaString
= lsa
.String()
3541 lsaString
.string
= local_tdo_info
.domain_name
.string
3542 local_forest_info
= local_lsa
.lsaRQueryForestTrustInformation(local_policy
,
3544 lsa
.LSA_FOREST_TRUST_DOMAIN_INFO
)
3545 except RuntimeError as error
:
3546 raise self
.LocalRuntimeError(self
, error
, "lsaRQueryForestTrustInformation() failed")
3548 self
.outf
.write("Local forest trust information...\n")
3549 self
.write_forest_trust_info(local_forest_info
,
3550 tln
=local_tdo_info
.domain_name
.string
)
3552 if not require_update
:
3556 entries
.extend(local_forest_info
.entries
)
3557 update_forest_info
= lsa
.ForestTrustInformation()
3558 update_forest_info
.count
= len(entries
)
3559 update_forest_info
.entries
= entries
3562 for i
in xrange(0, len(update_forest_info
.entries
)):
3563 r
= update_forest_info
.entries
[i
]
3564 if r
.type != lsa
.LSA_FOREST_TRUST_TOP_LEVEL_NAME
:
3566 if update_forest_info
.entries
[i
].flags
== 0:
3568 update_forest_info
.entries
[i
].time
= 0
3569 update_forest_info
.entries
[i
].flags
&= ~lsa
.LSA_TLN_DISABLED_MASK
3570 for i
in xrange(0, len(update_forest_info
.entries
)):
3571 r
= update_forest_info
.entries
[i
]
3572 if r
.type != lsa
.LSA_FOREST_TRUST_DOMAIN_INFO
:
3574 if update_forest_info
.entries
[i
].flags
== 0:
3576 update_forest_info
.entries
[i
].time
= 0
3577 update_forest_info
.entries
[i
].flags
&= ~lsa
.LSA_NB_DISABLED_MASK
3578 update_forest_info
.entries
[i
].flags
&= ~lsa
.LSA_SID_DISABLED_MASK
3580 for tln
in enable_tln
:
3582 for i
in xrange(0, len(update_forest_info
.entries
)):
3583 r
= update_forest_info
.entries
[i
]
3584 if r
.type != lsa
.LSA_FOREST_TRUST_TOP_LEVEL_NAME
:
3586 if r
.forest_trust_data
.string
.lower() != tln
.lower():
3591 raise CommandError("Entry not found for value[%s] specified for --enable-tln" % tln
)
3592 if not update_forest_info
.entries
[idx
].flags
& lsa
.LSA_TLN_DISABLED_MASK
:
3593 raise CommandError("Entry found for value[%s] specified for --enable-tln is already enabled" % tln
)
3594 update_forest_info
.entries
[idx
].time
= 0
3595 update_forest_info
.entries
[idx
].flags
&= ~lsa
.LSA_TLN_DISABLED_MASK
3597 for tln
in disable_tln
:
3599 for i
in xrange(0, len(update_forest_info
.entries
)):
3600 r
= update_forest_info
.entries
[i
]
3601 if r
.type != lsa
.LSA_FOREST_TRUST_TOP_LEVEL_NAME
:
3603 if r
.forest_trust_data
.string
.lower() != tln
.lower():
3608 raise CommandError("Entry not found for value[%s] specified for --disable-tln" % tln
)
3609 if update_forest_info
.entries
[idx
].flags
& lsa
.LSA_TLN_DISABLED_ADMIN
:
3610 raise CommandError("Entry found for value[%s] specified for --disable-tln is already disabled" % tln
)
3611 update_forest_info
.entries
[idx
].time
= 0
3612 update_forest_info
.entries
[idx
].flags
&= ~lsa
.LSA_TLN_DISABLED_MASK
3613 update_forest_info
.entries
[idx
].flags |
= lsa
.LSA_TLN_DISABLED_ADMIN
3615 for tln_ex
in add_tln_ex
:
3617 for i
in xrange(0, len(update_forest_info
.entries
)):
3618 r
= update_forest_info
.entries
[i
]
3619 if r
.type != lsa
.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX
:
3621 if r
.forest_trust_data
.string
.lower() != tln_ex
.lower():
3626 raise CommandError("Entry already present for value[%s] specified for --add-tln-ex" % tln_ex
)
3628 tln_dot
= ".%s" % tln_ex
.lower()
3630 for i
in xrange(0, len(update_forest_info
.entries
)):
3631 r
= update_forest_info
.entries
[i
]
3632 if r
.type != lsa
.LSA_FOREST_TRUST_TOP_LEVEL_NAME
:
3634 r_dot
= ".%s" % r
.forest_trust_data
.string
.lower()
3635 if tln_dot
== r_dot
:
3636 raise CommandError("TLN entry present for value[%s] specified for --add-tln-ex" % tln_ex
)
3637 if not tln_dot
.endswith(r_dot
):
3643 raise CommandError("No TLN parent present for value[%s] specified for --add-tln-ex" % tln_ex
)
3645 r
= lsa
.ForestTrustRecord()
3646 r
.type = lsa
.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX
3649 r
.forest_trust_data
.string
= tln_ex
3652 entries
.extend(update_forest_info
.entries
)
3653 entries
.insert(idx
+ 1, r
)
3654 update_forest_info
.count
= len(entries
)
3655 update_forest_info
.entries
= entries
3657 for tln_ex
in delete_tln_ex
:
3659 for i
in xrange(0, len(update_forest_info
.entries
)):
3660 r
= update_forest_info
.entries
[i
]
3661 if r
.type != lsa
.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX
:
3663 if r
.forest_trust_data
.string
.lower() != tln_ex
.lower():
3668 raise CommandError("Entry not found for value[%s] specified for --delete-tln-ex" % tln_ex
)
3671 entries
.extend(update_forest_info
.entries
)
3673 update_forest_info
.count
= len(entries
)
3674 update_forest_info
.entries
= entries
3676 for nb
in enable_nb
:
3678 for i
in xrange(0, len(update_forest_info
.entries
)):
3679 r
= update_forest_info
.entries
[i
]
3680 if r
.type != lsa
.LSA_FOREST_TRUST_DOMAIN_INFO
:
3682 if r
.forest_trust_data
.netbios_domain_name
.string
.upper() != nb
.upper():
3687 raise CommandError("Entry not found for value[%s] specified for --enable-nb" % nb
)
3688 if not update_forest_info
.entries
[idx
].flags
& lsa
.LSA_NB_DISABLED_MASK
:
3689 raise CommandError("Entry found for value[%s] specified for --enable-nb is already enabled" % nb
)
3690 update_forest_info
.entries
[idx
].time
= 0
3691 update_forest_info
.entries
[idx
].flags
&= ~lsa
.LSA_NB_DISABLED_MASK
3693 for nb
in disable_nb
:
3695 for i
in xrange(0, len(update_forest_info
.entries
)):
3696 r
= update_forest_info
.entries
[i
]
3697 if r
.type != lsa
.LSA_FOREST_TRUST_DOMAIN_INFO
:
3699 if r
.forest_trust_data
.netbios_domain_name
.string
.upper() != nb
.upper():
3704 raise CommandError("Entry not found for value[%s] specified for --delete-nb" % nb
)
3705 if update_forest_info
.entries
[idx
].flags
& lsa
.LSA_NB_DISABLED_ADMIN
:
3706 raise CommandError("Entry found for value[%s] specified for --disable-nb is already disabled" % nb
)
3707 update_forest_info
.entries
[idx
].time
= 0
3708 update_forest_info
.entries
[idx
].flags
&= ~lsa
.LSA_NB_DISABLED_MASK
3709 update_forest_info
.entries
[idx
].flags |
= lsa
.LSA_NB_DISABLED_ADMIN
3711 for sid
in enable_sid
:
3713 for i
in xrange(0, len(update_forest_info
.entries
)):
3714 r
= update_forest_info
.entries
[i
]
3715 if r
.type != lsa
.LSA_FOREST_TRUST_DOMAIN_INFO
:
3717 if r
.forest_trust_data
.domain_sid
!= sid
:
3722 raise CommandError("Entry not found for value[%s] specified for --enable-sid" % sid
)
3723 if not update_forest_info
.entries
[idx
].flags
& lsa
.LSA_SID_DISABLED_MASK
:
3724 raise CommandError("Entry found for value[%s] specified for --enable-sid is already enabled" % nb
)
3725 update_forest_info
.entries
[idx
].time
= 0
3726 update_forest_info
.entries
[idx
].flags
&= ~lsa
.LSA_SID_DISABLED_MASK
3728 for sid
in disable_sid
:
3730 for i
in xrange(0, len(update_forest_info
.entries
)):
3731 r
= update_forest_info
.entries
[i
]
3732 if r
.type != lsa
.LSA_FOREST_TRUST_DOMAIN_INFO
:
3734 if r
.forest_trust_data
.domain_sid
!= sid
:
3739 raise CommandError("Entry not found for value[%s] specified for --delete-sid" % sid
)
3740 if update_forest_info
.entries
[idx
].flags
& lsa
.LSA_SID_DISABLED_ADMIN
:
3741 raise CommandError("Entry found for value[%s] specified for --disable-sid is already disabled" % nb
)
3742 update_forest_info
.entries
[idx
].time
= 0
3743 update_forest_info
.entries
[idx
].flags
&= ~lsa
.LSA_SID_DISABLED_MASK
3744 update_forest_info
.entries
[idx
].flags |
= lsa
.LSA_SID_DISABLED_ADMIN
3747 update_forest_collision
= local_lsa
.lsaRSetForestTrustInformation(local_policy
,
3748 local_tdo_info
.domain_name
,
3749 lsa
.LSA_FOREST_TRUST_DOMAIN_INFO
,
3750 update_forest_info
, 0)
3751 except RuntimeError as error
:
3752 raise self
.LocalRuntimeError(self
, error
, "lsaRSetForestTrustInformation() failed")
3754 self
.outf
.write("Updated forest trust information...\n")
3755 self
.write_forest_trust_info(update_forest_info
,
3756 tln
=local_tdo_info
.domain_name
.string
,
3757 collisions
=update_forest_collision
)
3760 lsaString
= lsa
.String()
3761 lsaString
.string
= local_tdo_info
.domain_name
.string
3762 stored_forest_info
= local_lsa
.lsaRQueryForestTrustInformation(local_policy
,
3764 lsa
.LSA_FOREST_TRUST_DOMAIN_INFO
)
3765 except RuntimeError as error
:
3766 raise self
.LocalRuntimeError(self
, error
, "lsaRQueryForestTrustInformation() failed")
3768 self
.outf
.write("Stored forest trust information...\n")
3769 self
.write_forest_trust_info(stored_forest_info
,
3770 tln
=local_tdo_info
.domain_name
.string
)
3773 class cmd_domain_tombstones_expunge(Command
):
3774 """Expunge tombstones from the database.
3776 This command expunges tombstones from the database."""
3777 synopsis
= "%prog NC [NC [...]] [options]"
3780 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
3781 metavar
="URL", dest
="H"),
3782 Option("--current-time",
3783 help="The current time to evaluate the tombstone lifetime from, expressed as YYYY-MM-DD",
3785 Option("--tombstone-lifetime", help="Number of days a tombstone should be preserved for", type=int),
3788 takes_args
= ["nc*"]
3790 takes_optiongroups
= {
3791 "sambaopts": options
.SambaOptions
,
3792 "credopts": options
.CredentialsOptions
,
3793 "versionopts": options
.VersionOptions
,
3796 def run(self
, *ncs
, **kwargs
):
3797 sambaopts
= kwargs
.get("sambaopts")
3798 credopts
= kwargs
.get("credopts")
3799 versionpts
= kwargs
.get("versionopts")
3801 current_time_string
= kwargs
.get("current_time")
3802 tombstone_lifetime
= kwargs
.get("tombstone_lifetime")
3803 lp
= sambaopts
.get_loadparm()
3804 creds
= credopts
.get_credentials(lp
)
3805 samdb
= SamDB(url
=H
, session_info
=system_session(),
3806 credentials
=creds
, lp
=lp
)
3808 if current_time_string
is not None:
3809 current_time_obj
= time
.strptime(current_time_string
, "%Y-%m-%d")
3810 current_time
= long(time
.mktime(current_time_obj
))
3813 current_time
= long(time
.time())
3816 res
= samdb
.search(expression
="", base
="", scope
=ldb
.SCOPE_BASE
,
3817 attrs
=["namingContexts"])
3820 for nc
in res
[0]["namingContexts"]:
3825 started_transaction
= False
3827 samdb
.transaction_start()
3828 started_transaction
= True
3830 removed_links
) = samdb
.garbage_collect_tombstones(ncs
,
3831 current_time
=current_time
,
3832 tombstone_lifetime
=tombstone_lifetime
)
3834 except Exception, err
:
3835 if started_transaction
:
3836 samdb
.transaction_cancel()
3837 raise CommandError("Failed to expunge / garbage collect tombstones", err
)
3839 samdb
.transaction_commit()
3841 self
.outf
.write("Removed %d objects and %d links successfully\n"
3842 % (removed_objects
, removed_links
))
3846 class cmd_domain_trust(SuperCommand
):
3847 """Domain and forest trust management."""
3850 subcommands
["list"] = cmd_domain_trust_list()
3851 subcommands
["show"] = cmd_domain_trust_show()
3852 subcommands
["create"] = cmd_domain_trust_create()
3853 subcommands
["delete"] = cmd_domain_trust_delete()
3854 subcommands
["validate"] = cmd_domain_trust_validate()
3855 subcommands
["namespaces"] = cmd_domain_trust_namespaces()
3857 class cmd_domain_tombstones(SuperCommand
):
3858 """Domain tombstone and recycled object management."""
3861 subcommands
["expunge"] = cmd_domain_tombstones_expunge()
3863 class ldif_schema_update
:
3864 """Helper class for applying LDIF schema updates"""
3867 self
.is_defunct
= False
3868 self
.unknown_oid
= None
3872 def _ldap_schemaUpdateNow(self
, samdb
):
3876 add: schemaUpdateNow
3879 samdb
.modify_ldif(ldif
)
3881 def can_ignore_failure(self
, error
):
3882 """Checks if we can safely ignore failure to apply an LDIF update"""
3883 (num
, errstr
) = error
.args
3885 # Microsoft has marked objects as defunct that Samba doesn't know about
3886 if num
== ldb
.ERR_NO_SUCH_OBJECT
and self
.is_defunct
:
3887 print("Defunct object %s doesn't exist, skipping" % self
.dn
)
3889 elif self
.unknown_oid
is not None:
3890 print("Skipping unknown OID %s for object %s" %(self
.unknown_oid
, self
.dn
))
3895 def apply(self
, samdb
):
3896 """Applies a single LDIF update to the schema"""
3899 samdb
.modify_ldif(self
.ldif
, controls
=['relax:0'])
3900 except ldb
.LdbError
as e
:
3901 if self
.can_ignore_failure(e
):
3904 print("Exception: %s" % e
)
3905 print("Encountered while trying to apply the following LDIF")
3906 print("----------------------------------------------------")
3907 print("%s" % self
.ldif
)
3911 # REFRESH AFTER EVERY CHANGE
3912 # Otherwise the OID-to-attribute mapping in _apply_updates_in_file()
3913 # won't work, because it can't lookup the new OID in the schema
3914 self
._ldap
_schemaUpdateNow
(samdb
)
3918 class cmd_domain_schema_upgrade(Command
):
3919 """Domain schema upgrading"""
3921 synopsis
= "%prog [options]"
3923 takes_optiongroups
= {
3924 "sambaopts": options
.SambaOptions
,
3925 "versionopts": options
.VersionOptions
,
3926 "credopts": options
.CredentialsOptions
,
3930 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
3931 metavar
="URL", dest
="H"),
3932 Option("--quiet", help="Be quiet", action
="store_true"),
3933 Option("--verbose", help="Be verbose", action
="store_true"),
3934 Option("--schema", type="choice", metavar
="SCHEMA",
3935 choices
=["2012", "2012_R2"],
3936 help="The schema file to upgrade to. Default is (Windows) 2012_R2.",
3938 Option("--ldf-file", type=str, default
=None,
3939 help="Just apply the schema updates in the adprep/.LDF file(s) specified"),
3940 Option("--base-dir", type=str, default
=None,
3941 help="Location of ldf files Default is ${SETUPDIR}/adprep.")
3944 def _apply_updates_in_file(self
, samdb
, ldif_file
):
3946 Applies a series of updates specified in an .LDIF file. The .LDIF file
3947 is based on the adprep Schema updates provided by Microsoft.
3950 ldif_op
= ldif_schema_update()
3952 # parse the file line by line and work out each update operation to apply
3953 for line
in ldif_file
:
3955 line
= line
.rstrip()
3957 # the operations in the .LDIF file are separated by blank lines. If
3958 # we hit a blank line, try to apply the update we've parsed so far
3961 # keep going if we haven't parsed anything yet
3962 if ldif_op
.ldif
== '':
3965 # Apply the individual change
3966 count
+= ldif_op
.apply(samdb
)
3968 # start storing the next operation from scratch again
3969 ldif_op
= ldif_schema_update()
3972 # replace the placeholder domain name in the .ldif file with the real domain
3973 if line
.upper().endswith('DC=X'):
3974 line
= line
[:-len('DC=X')] + str(samdb
.get_default_basedn())
3975 elif line
.upper().endswith('CN=X'):
3976 line
= line
[:-len('CN=X')] + str(samdb
.get_default_basedn())
3978 values
= line
.split(':')
3980 if values
[0].lower() == 'dn':
3981 ldif_op
.dn
= values
[1].strip()
3983 # replace the Windows-specific operation with the Samba one
3984 if values
[0].lower() == 'changetype':
3985 line
= line
.lower().replace(': ntdsschemaadd',
3987 line
= line
.lower().replace(': ntdsschemamodify',
3990 if values
[0].lower() in ['rdnattid', 'subclassof',
3991 'systemposssuperiors',
3993 'systemauxiliaryclass']:
3996 # The Microsoft updates contain some OIDs we don't recognize.
3997 # Query the DB to see if we can work out the OID this update is
3998 # referring to. If we find a match, then replace the OID with
3999 # the ldapDisplayname
4001 res
= samdb
.search(base
=samdb
.get_schema_basedn(),
4002 expression
="(|(attributeId=%s)(governsId=%s))" %
4004 attrs
=['ldapDisplayName'])
4007 ldif_op
.unknown_oid
= value
4009 display_name
= res
[0]['ldapDisplayName'][0]
4010 line
= line
.replace(value
, ' ' + display_name
)
4012 # Microsoft has marked objects as defunct that Samba doesn't know about
4013 if values
[0].lower() == 'isdefunct' and values
[1].strip().lower() == 'true':
4014 ldif_op
.is_defunct
= True
4016 # Samba has added the showInAdvancedViewOnly attribute to all objects,
4017 # so rather than doing an add, we need to do a replace
4018 if values
[0].lower() == 'add' and values
[1].strip().lower() == 'showinadvancedviewonly':
4019 line
= 'replace: showInAdvancedViewOnly'
4021 # Add the line to the current LDIF operation (including the newline
4022 # we stripped off at the start of the loop)
4023 ldif_op
.ldif
+= line
+ '\n'
4028 def _apply_update(self
, samdb
, update_file
, base_dir
):
4029 """Wrapper function for parsing an LDIF file and applying the updates"""
4031 print("Applying %s updates..." % update_file
)
4035 ldif_file
= open(os
.path
.join(base_dir
, update_file
))
4037 count
= self
._apply
_updates
_in
_file
(samdb
, ldif_file
)
4043 print("%u changes applied" % count
)
4047 def run(self
, **kwargs
):
4048 from samba
.ms_schema_markdown
import read_ms_markdown
4049 from samba
.schema
import Schema
4051 updates_allowed_overriden
= False
4052 sambaopts
= kwargs
.get("sambaopts")
4053 credopts
= kwargs
.get("credopts")
4054 versionpts
= kwargs
.get("versionopts")
4055 lp
= sambaopts
.get_loadparm()
4056 creds
= credopts
.get_credentials(lp
)
4058 target_schema
= kwargs
.get("schema")
4059 ldf_files
= kwargs
.get("ldf_file")
4060 base_dir
= kwargs
.get("base_dir")
4064 samdb
= SamDB(url
=H
, session_info
=system_session(), credentials
=creds
, lp
=lp
)
4066 # we're not going to get far if the config doesn't allow schema updates
4067 if lp
.get("dsdb:schema update allowed") is None:
4068 lp
.set("dsdb:schema update allowed", "yes")
4069 print("Temporarily overriding 'dsdb:schema update allowed' setting")
4070 updates_allowed_overriden
= True
4072 # if specific LDIF files were specified, just apply them
4074 schema_updates
= ldf_files
.split(",")
4078 # work out the version of the target schema we're upgrading to
4079 end
= Schema
.get_version(target_schema
)
4081 # work out the version of the schema we're currently using
4082 res
= samdb
.search(base
=samdb
.get_schema_basedn(),
4083 scope
=ldb
.SCOPE_BASE
, attrs
=['objectVersion'])
4086 raise CommandError('Could not determine current schema version')
4087 start
= int(res
[0]['objectVersion'][0]) + 1
4089 diff_dir
= setup_path("adprep/WindowsServerDocs")
4090 if base_dir
is None:
4091 # Read from the Schema-Updates.md file
4092 temp_folder
= tempfile
.mkdtemp()
4094 update_file
= setup_path("adprep/WindowsServerDocs/Schema-Updates.md")
4097 read_ms_markdown(update_file
, temp_folder
)
4098 except Exception as e
:
4099 print("Exception in markdown parsing: %s" % e
)
4100 shutil
.rmtree(temp_folder
)
4101 raise CommandError('Failed to upgrade schema')
4103 base_dir
= temp_folder
4105 for version
in range(start
, end
+ 1):
4106 update
= 'Sch%d.ldf' % version
4107 schema_updates
.append(update
)
4109 # Apply patches if we parsed the Schema-Updates.md file
4110 diff
= os
.path
.abspath(os
.path
.join(diff_dir
, update
+ '.diff'))
4111 if temp_folder
and os
.path
.exists(diff
):
4112 p
= subprocess
.Popen(['patch', update
, '-i', diff
],
4113 stdout
=subprocess
.PIPE
,
4114 stderr
=subprocess
.PIPE
, cwd
=temp_folder
)
4115 stdout
, stderr
= p
.communicate()
4118 print("Exception in patch: %s\n%s" % (stdout
, stderr
))
4119 shutil
.rmtree(temp_folder
)
4120 raise CommandError('Failed to upgrade schema')
4122 print("Patched %s using %s" % (update
, diff
))
4124 if base_dir
is None:
4125 base_dir
= setup_path("adprep")
4127 samdb
.transaction_start()
4129 error_encountered
= False
4132 # Apply the schema updates needed to move to the new schema version
4133 for ldif_file
in schema_updates
:
4134 count
+= self
._apply
_update
(samdb
, ldif_file
, base_dir
)
4137 samdb
.transaction_commit()
4138 print("Schema successfully updated")
4140 print("No changes applied to schema")
4141 samdb
.transaction_cancel()
4142 except Exception as e
:
4143 print("Exception: %s" % e
)
4144 print("Error encountered, aborting schema upgrade")
4145 samdb
.transaction_cancel()
4146 error_encountered
= True
4148 if updates_allowed_overriden
:
4149 lp
.set("dsdb:schema update allowed", "no")
4152 shutil
.rmtree(temp_folder
)
4154 if error_encountered
:
4155 raise CommandError('Failed to upgrade schema')
4157 class cmd_domain(SuperCommand
):
4158 """Domain management."""
4161 subcommands
["demote"] = cmd_domain_demote()
4162 if cmd_domain_export_keytab
is not None:
4163 subcommands
["exportkeytab"] = cmd_domain_export_keytab()
4164 subcommands
["info"] = cmd_domain_info()
4165 subcommands
["provision"] = cmd_domain_provision()
4166 subcommands
["join"] = cmd_domain_join()
4167 subcommands
["dcpromo"] = cmd_domain_dcpromo()
4168 subcommands
["level"] = cmd_domain_level()
4169 subcommands
["passwordsettings"] = cmd_domain_passwordsettings()
4170 subcommands
["classicupgrade"] = cmd_domain_classicupgrade()
4171 subcommands
["samba3upgrade"] = cmd_domain_samba3upgrade()
4172 subcommands
["trust"] = cmd_domain_trust()
4173 subcommands
["tombstones"] = cmd_domain_tombstones()
4174 subcommands
["schemaupgrade"] = cmd_domain_schema_upgrade()