3 # Copyright Matthias Dieter Wallnoefer 2009
4 # Copyright Andrew Kroeger 2009
5 # Copyright Jelmer Vernooij 2007-2009
6 # Copyright Giampaolo Lauria 2011
7 # Copyright Matthieu Patou <mat@matws.net> 2011
8 # Copyright Andrew Bartlett 2008
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/>.
27 import samba
.getopt
as options
34 from samba
.net
import Net
, LIBNET_JOIN_AUTOMATIC
36 from samba
.join
import join_RODC
, join_DC
, join_subdomain
37 from samba
.auth
import system_session
38 from samba
.samdb
import SamDB
39 from samba
.dcerpc
import drsuapi
40 from samba
.dcerpc
.samr
import DOMAIN_PASSWORD_COMPLEX
, DOMAIN_PASSWORD_STORE_CLEARTEXT
41 from samba
.netcmd
import (
47 from samba
.netcmd
.common
import netcmd_get_domain_infos_via_cldap
48 from samba
.samba3
import Samba3
49 from samba
.samba3
import param
as s3param
50 from samba
.upgrade
import upgrade_from_samba3
51 from samba
.drs_utils
import (
52 sendDsReplicaSync
, drsuapi_connect
, drsException
,
56 from samba
.dsdb
import (
57 DS_DOMAIN_FUNCTION_2000
,
58 DS_DOMAIN_FUNCTION_2003
,
59 DS_DOMAIN_FUNCTION_2003_MIXED
,
60 DS_DOMAIN_FUNCTION_2008
,
61 DS_DOMAIN_FUNCTION_2008_R2
,
62 DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL
,
63 DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
,
64 UF_WORKSTATION_TRUST_ACCOUNT
,
65 UF_SERVER_TRUST_ACCOUNT
,
66 UF_TRUSTED_FOR_DELEGATION
69 from samba
.credentials
import DONT_USE_KERBEROS
70 from samba
.provision
import (
78 def get_testparm_var(testparm
, smbconf
, varname
):
79 cmd
= "%s -s -l --parameter-name='%s' %s 2>/dev/null" % (testparm
, varname
, smbconf
)
80 output
= os
.popen(cmd
, 'r').readline()
85 class cmd_domain_export_keytab(Command
):
86 """Dumps kerberos keys of the domain into a keytab"""
88 synopsis
= "%prog <keytab> [options]"
90 takes_optiongroups
= {
91 "sambaopts": options
.SambaOptions
,
92 "credopts": options
.CredentialsOptions
,
93 "versionopts": options
.VersionOptions
,
97 Option("--principal", help="extract only this principal", type=str),
100 takes_args
= ["keytab"]
102 def run(self
, keytab
, credopts
=None, sambaopts
=None, versionopts
=None, principal
=None):
103 lp
= sambaopts
.get_loadparm()
105 net
.export_keytab(keytab
=keytab
, principal
=principal
)
107 cmd_domain_export_keytab
= None
110 class cmd_domain_info(Command
):
111 """Print basic info about a domain and the DC passed as parameter"""
113 synopsis
= "%prog <ip_address> [options]"
118 takes_optiongroups
= {
119 "sambaopts": options
.SambaOptions
,
120 "credopts": options
.CredentialsOptions
,
121 "versionopts": options
.VersionOptions
,
124 takes_args
= ["address"]
126 def run(self
, address
, credopts
=None, sambaopts
=None, versionopts
=None):
127 lp
= sambaopts
.get_loadparm()
129 res
= netcmd_get_domain_infos_via_cldap(lp
, None, address
)
130 print "Forest : %s" % res
.forest
131 print "Domain : %s" % res
.dns_domain
132 print "Netbios domain : %s" % res
.domain_name
133 print "DC name : %s" % res
.pdc_dns_name
134 print "DC netbios name : %s" % res
.pdc_name
135 print "Server site : %s" % res
.server_site
136 print "Client site : %s" % res
.client_site
138 raise CommandError("Invalid IP address '" + address
+ "'!")
140 class cmd_domain_provision(Command
):
141 """Promotes an existing domain member or NT4 PDC to an AD DC"""
143 synopsis
= "%prog <dnsdomain> [DC|RODC] [options]"
145 takes_optiongroups
= {
146 "sambaopts": options
.SambaOptions
,
147 "versionopts": options
.VersionOptions
,
148 "credopts": options
.CredentialsOptions
,
152 Option("--interactive", help="Ask for names", action
="store_true"),
153 Option("--domain", type="string", metavar
="DOMAIN",
155 Option("--domain-guid", type="string", metavar
="GUID",
156 help="set domainguid (otherwise random)"),
157 Option("--domain-sid", type="string", metavar
="SID",
158 help="set domainsid (otherwise random)"),
159 Option("--ntds-guid", type="string", metavar
="GUID",
160 help="set NTDS object GUID (otherwise random)"),
161 Option("--invocationid", type="string", metavar
="GUID",
162 help="set invocationid (otherwise random)"),
163 Option("--host-name", type="string", metavar
="HOSTNAME",
164 help="set hostname"),
165 Option("--host-ip", type="string", metavar
="IPADDRESS",
166 help="set IPv4 ipaddress"),
167 Option("--host-ip6", type="string", metavar
="IP6ADDRESS",
168 help="set IPv6 ipaddress"),
169 Option("--adminpass", type="string", metavar
="PASSWORD",
170 help="choose admin password (otherwise random)"),
171 Option("--krbtgtpass", type="string", metavar
="PASSWORD",
172 help="choose krbtgt password (otherwise random)"),
173 Option("--machinepass", type="string", metavar
="PASSWORD",
174 help="choose machine password (otherwise random)"),
175 Option("--dns-backend", type="choice", metavar
="NAMESERVER-BACKEND",
176 choices
=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
177 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), " \
178 "BIND9_FLATFILE uses bind9 text database to store zone information, " \
179 "BIND9_DLZ uses samba4 AD to store zone information, " \
180 "NONE skips the DNS setup entirely (not recommended)",
181 default
="SAMBA_INTERNAL"),
182 Option("--dnspass", type="string", metavar
="PASSWORD",
183 help="choose dns password (otherwise random)"),
184 Option("--ldapadminpass", type="string", metavar
="PASSWORD",
185 help="choose password to set between Samba and it's LDAP backend (otherwise random)"),
186 Option("--root", type="string", metavar
="USERNAME",
187 help="choose 'root' unix username"),
188 Option("--nobody", type="string", metavar
="USERNAME",
189 help="choose 'nobody' user"),
190 Option("--wheel", type="string", metavar
="GROUPNAME",
191 help="choose 'wheel' privileged group"),
192 Option("--users", type="string", metavar
="GROUPNAME",
193 help="choose 'users' group"),
194 Option("--quiet", help="Be quiet", action
="store_true"),
195 Option("--blank", action
="store_true",
196 help="do not add users or groups, just the structure"),
197 Option("--ldap-backend-type", type="choice", metavar
="LDAP-BACKEND-TYPE",
198 help="Test initialisation support for unsupported LDAP backend type (fedora-ds or openldap) DO NOT USE",
199 choices
=["fedora-ds", "openldap"]),
200 Option("--server-role", type="choice", metavar
="ROLE",
201 choices
=["domain controller", "dc", "member server", "member", "standalone"],
202 help="The server role (domain controller | dc | member server | member | standalone). Default is dc.",
203 default
="domain controller"),
204 Option("--function-level", type="choice", metavar
="FOR-FUN-LEVEL",
205 choices
=["2000", "2003", "2008", "2008_R2"],
206 help="The domain and forest function level (2000 | 2003 | 2008 | 2008_R2 - always native). Default is (Windows) 2003 Native.",
208 Option("--next-rid", type="int", metavar
="NEXTRID", default
=1000,
209 help="The initial nextRid value (only needed for upgrades). Default is 1000."),
210 Option("--partitions-only",
211 help="Configure Samba's partitions, but do not modify them (ie, join a BDC)", action
="store_true"),
212 Option("--targetdir", type="string", metavar
="DIR",
213 help="Set target directory"),
214 Option("--ol-mmr-urls", type="string", metavar
="LDAPSERVER",
215 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\""),
216 Option("--use-xattrs", type="choice", choices
=["yes", "no", "auto"], help="Define if we should use the native fs capabilities or a tdb file for storing attributes likes ntacl, auto tries to make an inteligent guess based on the user rights and system capabilities", default
="auto"),
217 Option("--use-ntvfs", action
="store_true", help="Use NTVFS for the fileserver (default = no)"),
218 Option("--use-rfc2307", action
="store_true", help="Use AD to store posix attributes (default = no)"),
222 def run(self
, sambaopts
=None, credopts
=None, versionopts
=None,
237 ldapadminpass
= None,
244 ldap_backend_type
= None,
246 function_level
= None,
248 partitions_only
= None,
255 logger
= self
.get_logger("provision")
257 logger
.setLevel(logging
.WARNING
)
259 logger
.setLevel(logging
.INFO
)
261 lp
= sambaopts
.get_loadparm()
262 smbconf
= lp
.configfile
264 creds
= credopts
.get_credentials(lp
)
266 creds
.set_kerberos_state(DONT_USE_KERBEROS
)
268 if len(self
.raw_argv
) == 1:
272 from getpass
import getpass
275 def ask(prompt
, default
=None):
276 if default
is not None:
277 print "%s [%s]: " % (prompt
, default
),
279 print "%s: " % (prompt
,),
280 return sys
.stdin
.readline().rstrip("\n") or default
283 default
= socket
.getfqdn().split(".", 1)[1].upper()
286 realm
= ask("Realm", default
)
287 if realm
in (None, ""):
288 raise CommandError("No realm set!")
291 default
= realm
.split(".")[0]
294 domain
= ask("Domain", default
)
296 raise CommandError("No domain set!")
298 server_role
= ask("Server Role (dc, member, standalone)", "dc")
300 dns_backend
= ask("DNS backend (SAMBA_INTERNAL, BIND9_FLATFILE, BIND9_DLZ, NONE)", "SAMBA_INTERNAL")
301 if dns_backend
in (None, ''):
302 raise CommandError("No DNS backend set!")
305 adminpassplain
= getpass("Administrator password: ")
306 if not adminpassplain
:
307 print >>sys
.stderr
, "Invalid administrator password."
309 adminpassverify
= getpass("Retype password: ")
310 if not adminpassplain
== adminpassverify
:
311 print >>sys
.stderr
, "Sorry, passwords do not match."
313 adminpass
= adminpassplain
317 realm
= sambaopts
._lp
.get('realm')
319 raise CommandError("No realm set!")
321 raise CommandError("No domain set!")
324 logger
.info("Administrator password will be set randomly!")
326 if function_level
== "2000":
327 dom_for_fun_level
= DS_DOMAIN_FUNCTION_2000
328 elif function_level
== "2003":
329 dom_for_fun_level
= DS_DOMAIN_FUNCTION_2003
330 elif function_level
== "2008":
331 dom_for_fun_level
= DS_DOMAIN_FUNCTION_2008
332 elif function_level
== "2008_R2":
333 dom_for_fun_level
= DS_DOMAIN_FUNCTION_2008_R2
335 samdb_fill
= FILL_FULL
337 samdb_fill
= FILL_NT4SYNC
338 elif partitions_only
:
339 samdb_fill
= FILL_DRS
341 if targetdir
is not None:
342 if not os
.path
.isdir(targetdir
):
347 if use_xattrs
== "yes":
349 elif use_xattrs
== "auto" and not lp
.get("posix:eadb"):
351 file = tempfile
.NamedTemporaryFile(dir=os
.path
.abspath(targetdir
))
353 file = tempfile
.NamedTemporaryFile(dir=os
.path
.abspath(os
.path
.dirname(lp
.get("private dir"))))
356 samba
.ntacls
.setntacl(lp
, file.name
,
357 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
360 logger
.info("You are not root or your system do not support xattr, using tdb backend for attributes. ")
365 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.")
367 session
= system_session()
369 result
= provision(logger
,
370 session
, creds
, smbconf
=smbconf
, targetdir
=targetdir
,
371 samdb_fill
=samdb_fill
, realm
=realm
, domain
=domain
,
372 domainguid
=domain_guid
, domainsid
=domain_sid
,
374 hostip
=host_ip
, hostip6
=host_ip6
,
376 invocationid
=invocationid
, adminpass
=adminpass
,
377 krbtgtpass
=krbtgtpass
, machinepass
=machinepass
,
378 dns_backend
=dns_backend
,
379 dnspass
=dnspass
, root
=root
, nobody
=nobody
,
380 wheel
=wheel
, users
=users
,
381 serverrole
=server_role
, dom_for_fun_level
=dom_for_fun_level
,
382 backend_type
=ldap_backend_type
,
383 ldapadminpass
=ldapadminpass
, ol_mmr_urls
=ol_mmr_urls
,
384 useeadb
=eadb
, next_rid
=next_rid
, lp
=lp
, use_ntvfs
=(use_ntvfs
),
385 use_rfc2307
=(use_rfc2307
))
386 except ProvisioningError
, e
:
387 raise CommandError("Provision failed", e
)
389 result
.report_logger(logger
)
391 class cmd_domain_dcpromo(Command
):
392 """Promotes an existing domain member or NT4 PDC to an AD DC"""
394 synopsis
= "%prog <dnsdomain> [DC|RODC] [options]"
396 takes_optiongroups
= {
397 "sambaopts": options
.SambaOptions
,
398 "versionopts": options
.VersionOptions
,
399 "credopts": options
.CredentialsOptions
,
403 Option("--server", help="DC to join", type=str),
404 Option("--site", help="site to join", type=str),
405 Option("--targetdir", help="where to store provision", type=str),
406 Option("--domain-critical-only",
407 help="only replicate critical domain objects",
408 action
="store_true"),
409 Option("--machinepass", type=str, metavar
="PASSWORD",
410 help="choose machine password (otherwise random)"),
411 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
412 action
="store_true"),
413 Option("--dns-backend", type="choice", metavar
="NAMESERVER-BACKEND",
414 choices
=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
415 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), " \
416 "BIND9_DLZ uses samba4 AD to store zone information, " \
417 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
418 default
="SAMBA_INTERNAL")
421 takes_args
= ["domain", "role?"]
423 def run(self
, domain
, role
=None, sambaopts
=None, credopts
=None,
424 versionopts
=None, server
=None, site
=None, targetdir
=None,
425 domain_critical_only
=False, parent_domain
=None, machinepass
=None,
426 use_ntvfs
=False, dns_backend
=None):
427 lp
= sambaopts
.get_loadparm()
428 creds
= credopts
.get_credentials(lp
)
429 net
= Net(creds
, lp
, server
=credopts
.ipaddress
)
432 site
= "Default-First-Site-Name"
434 netbios_name
= lp
.get("netbios name")
440 join_DC(server
=server
, creds
=creds
, lp
=lp
, domain
=domain
,
441 site
=site
, netbios_name
=netbios_name
, targetdir
=targetdir
,
442 domain_critical_only
=domain_critical_only
,
443 machinepass
=machinepass
, use_ntvfs
=use_ntvfs
, dns_backend
=dns_backend
,
444 promote_existing
=True)
447 join_RODC(server
=server
, creds
=creds
, lp
=lp
, domain
=domain
,
448 site
=site
, netbios_name
=netbios_name
, targetdir
=targetdir
,
449 domain_critical_only
=domain_critical_only
,
450 machinepass
=machinepass
, use_ntvfs
=use_ntvfs
, dns_backend
=dns_backend
,
451 promote_existing
=True)
454 raise CommandError("Invalid role '%s' (possible values: DC, RODC)" % role
)
457 class cmd_domain_join(Command
):
458 """Joins domain as either member or backup domain controller"""
460 synopsis
= "%prog <dnsdomain> [DC|RODC|MEMBER|SUBDOMAIN] [options]"
462 takes_optiongroups
= {
463 "sambaopts": options
.SambaOptions
,
464 "versionopts": options
.VersionOptions
,
465 "credopts": options
.CredentialsOptions
,
469 Option("--server", help="DC to join", type=str),
470 Option("--site", help="site to join", type=str),
471 Option("--targetdir", help="where to store provision", type=str),
472 Option("--parent-domain", help="parent domain to create subdomain under", type=str),
473 Option("--domain-critical-only",
474 help="only replicate critical domain objects",
475 action
="store_true"),
476 Option("--machinepass", type=str, metavar
="PASSWORD",
477 help="choose machine password (otherwise random)"),
478 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
479 action
="store_true"),
480 Option("--dns-backend", type="choice", metavar
="NAMESERVER-BACKEND",
481 choices
=["SAMBA_INTERNAL", "BIND9_DLZ", "NONE"],
482 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), " \
483 "BIND9_DLZ uses samba4 AD to store zone information, " \
484 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
485 default
="SAMBA_INTERNAL")
488 takes_args
= ["domain", "role?"]
490 def run(self
, domain
, role
=None, sambaopts
=None, credopts
=None,
491 versionopts
=None, server
=None, site
=None, targetdir
=None,
492 domain_critical_only
=False, parent_domain
=None, machinepass
=None,
493 use_ntvfs
=False, dns_backend
=None):
494 lp
= sambaopts
.get_loadparm()
495 creds
= credopts
.get_credentials(lp
)
496 net
= Net(creds
, lp
, server
=credopts
.ipaddress
)
499 site
= "Default-First-Site-Name"
501 netbios_name
= lp
.get("netbios name")
506 if role
is None or role
== "MEMBER":
507 (join_password
, sid
, domain_name
) = net
.join_member(domain
,
509 LIBNET_JOIN_AUTOMATIC
,
510 machinepass
=machinepass
)
512 self
.outf
.write("Joined domain %s (%s)\n" % (domain_name
, sid
))
515 join_DC(server
=server
, creds
=creds
, lp
=lp
, domain
=domain
,
516 site
=site
, netbios_name
=netbios_name
, targetdir
=targetdir
,
517 domain_critical_only
=domain_critical_only
,
518 machinepass
=machinepass
, use_ntvfs
=use_ntvfs
, dns_backend
=dns_backend
)
521 join_RODC(server
=server
, creds
=creds
, lp
=lp
, domain
=domain
,
522 site
=site
, netbios_name
=netbios_name
, targetdir
=targetdir
,
523 domain_critical_only
=domain_critical_only
,
524 machinepass
=machinepass
, use_ntvfs
=use_ntvfs
, dns_backend
=dns_backend
)
526 elif role
== "SUBDOMAIN":
527 netbios_domain
= lp
.get("workgroup")
528 if parent_domain
is None:
529 parent_domain
= ".".join(domain
.split(".")[1:])
530 join_subdomain(server
=server
, creds
=creds
, lp
=lp
, dnsdomain
=domain
, parent_domain
=parent_domain
,
531 site
=site
, netbios_name
=netbios_name
, netbios_domain
=netbios_domain
, targetdir
=targetdir
,
532 machinepass
=machinepass
, use_ntvfs
=use_ntvfs
, dns_backend
=dns_backend
)
535 raise CommandError("Invalid role '%s' (possible values: MEMBER, DC, RODC, SUBDOMAIN)" % role
)
539 class cmd_domain_demote(Command
):
540 """Demote ourselves from the role of Domain Controller"""
542 synopsis
= "%prog [options]"
545 Option("--server", help="DC to force replication before demote", type=str),
546 Option("--targetdir", help="where provision is stored", type=str),
549 takes_optiongroups
= {
550 "sambaopts": options
.SambaOptions
,
551 "credopts": options
.CredentialsOptions
,
552 "versionopts": options
.VersionOptions
,
555 def run(self
, sambaopts
=None, credopts
=None,
556 versionopts
=None, server
=None, targetdir
=None):
557 lp
= sambaopts
.get_loadparm()
558 creds
= credopts
.get_credentials(lp
)
559 net
= Net(creds
, lp
, server
=credopts
.ipaddress
)
561 netbios_name
= lp
.get("netbios name")
562 samdb
= SamDB(session_info
=system_session(), credentials
=creds
, lp
=lp
)
564 res
= samdb
.search(expression
='(&(objectClass=computer)(serverReferenceBL=*))', attrs
=["dnsHostName", "name"])
566 raise CommandError("Unable to search for servers")
569 raise CommandError("You are the latest server in the domain")
573 if str(e
["name"]).lower() != netbios_name
.lower():
574 server
= e
["dnsHostName"]
577 ntds_guid
= samdb
.get_ntds_GUID()
578 msg
= samdb
.search(base
=str(samdb
.get_config_basedn()), scope
=ldb
.SCOPE_SUBTREE
,
579 expression
="(objectGUID=%s)" % ntds_guid
,
581 if len(msg
) == 0 or "options" not in msg
[0]:
582 raise CommandError("Failed to find options on %s" % ntds_guid
)
585 dsa_options
= int(str(msg
[0]['options']))
587 res
= samdb
.search(expression
="(fSMORoleOwner=%s)" % str(ntds_dn
),
588 controls
=["search_options:1:2"])
591 raise CommandError("Current DC is still the owner of %d role(s), use the role command to transfer roles to another DC" % len(res
))
593 print "Using %s as partner server for the demotion" % server
594 (drsuapiBind
, drsuapi_handle
, supportedExtensions
) = drsuapi_connect(server
, lp
, creds
)
596 print "Desactivating inbound replication"
601 dsa_options |
= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
602 nmsg
["options"] = ldb
.MessageElement(str(dsa_options
), ldb
.FLAG_MOD_REPLACE
, "options")
605 if not (dsa_options
& DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL
) and not samdb
.am_rodc():
607 print "Asking partner server %s to synchronize from us" % server
608 for part
in (samdb
.get_schema_basedn(),
609 samdb
.get_config_basedn(),
610 samdb
.get_root_basedn()):
612 sendDsReplicaSync(drsuapiBind
, drsuapi_handle
, ntds_guid
, str(part
), drsuapi
.DRSUAPI_DRS_WRIT_REP
)
613 except drsException
, e
:
614 print "Error while demoting, re-enabling inbound replication"
615 dsa_options ^
= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
616 nmsg
["options"] = ldb
.MessageElement(str(dsa_options
), ldb
.FLAG_MOD_REPLACE
, "options")
618 raise CommandError("Error while sending a DsReplicaSync for partion %s" % str(part
), e
)
620 remote_samdb
= SamDB(url
="ldap://%s" % server
,
621 session_info
=system_session(),
622 credentials
=creds
, lp
=lp
)
624 print "Changing userControl and container"
625 res
= remote_samdb
.search(base
=str(remote_samdb
.get_root_basedn()),
626 expression
="(&(objectClass=user)(sAMAccountName=%s$))" %
627 netbios_name
.upper(),
628 attrs
=["userAccountControl"])
630 uac
= int(str(res
[0]["userAccountControl"]))
633 print "Error while demoting, re-enabling inbound replication"
634 dsa_options ^
= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
635 nmsg
["options"] = ldb
.MessageElement(str(dsa_options
), ldb
.FLAG_MOD_REPLACE
, "options")
637 raise CommandError("Error while changing account control", e
)
640 print "Error while demoting, re-enabling inbound replication"
641 dsa_options ^
= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
642 nmsg
["options"] = ldb
.MessageElement(str(dsa_options
), ldb
.FLAG_MOD_REPLACE
, "options")
644 raise CommandError("Unable to find object with samaccountName = %s$"
645 " in the remote dc" % netbios_name
.upper())
649 uac ^
= (UF_SERVER_TRUST_ACCOUNT|UF_TRUSTED_FOR_DELEGATION
)
650 uac |
= UF_WORKSTATION_TRUST_ACCOUNT
655 msg
["userAccountControl"] = ldb
.MessageElement("%d" % uac
,
656 ldb
.FLAG_MOD_REPLACE
,
657 "userAccountControl")
659 remote_samdb
.modify(msg
)
661 print "Error while demoting, re-enabling inbound replication"
662 dsa_options ^
= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
663 nmsg
["options"] = ldb
.MessageElement(str(dsa_options
), ldb
.FLAG_MOD_REPLACE
, "options")
666 raise CommandError("Error while changing account control", e
)
668 parent
= msg
.dn
.parent()
670 rdn
= string
.replace(rdn
, ",%s" % str(parent
), "")
671 # Let's move to the Computer container
675 computer_dn
= ldb
.Dn(remote_samdb
, "CN=Computers,%s" % str(remote_samdb
.get_root_basedn()))
676 res
= remote_samdb
.search(base
=computer_dn
, expression
=rdn
, scope
=ldb
.SCOPE_ONELEVEL
)
679 res
= remote_samdb
.search(base
=computer_dn
, expression
="%s-%d" % (rdn
, i
),
680 scope
=ldb
.SCOPE_ONELEVEL
)
681 while(len(res
) != 0 and i
< 100):
683 res
= remote_samdb
.search(base
=computer_dn
, expression
="%s-%d" % (rdn
, i
),
684 scope
=ldb
.SCOPE_ONELEVEL
)
687 print "Error while demoting, re-enabling inbound replication"
688 dsa_options ^
= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
689 nmsg
["options"] = ldb
.MessageElement(str(dsa_options
), ldb
.FLAG_MOD_REPLACE
, "options")
695 msg
["userAccountControl"] = ldb
.MessageElement("%d" % uac
,
696 ldb
.FLAG_MOD_REPLACE
,
697 "userAccountControl")
699 remote_samdb
.modify(msg
)
701 raise CommandError("Unable to find a slot for renaming %s,"
702 " all names from %s-1 to %s-%d seemed used" %
703 (str(dc_dn
), rdn
, rdn
, i
- 9))
705 newrdn
= "%s-%d" % (rdn
, i
)
708 newdn
= ldb
.Dn(remote_samdb
, "%s,%s" % (newrdn
, str(computer_dn
)))
709 remote_samdb
.rename(dc_dn
, newdn
)
711 print "Error while demoting, re-enabling inbound replication"
712 dsa_options ^
= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
713 nmsg
["options"] = ldb
.MessageElement(str(dsa_options
), ldb
.FLAG_MOD_REPLACE
, "options")
719 msg
["userAccountControl"] = ldb
.MessageElement("%d" % uac
,
720 ldb
.FLAG_MOD_REPLACE
,
721 "userAccountControl")
723 remote_samdb
.modify(msg
)
724 raise CommandError("Error while renaming %s to %s" % (str(dc_dn
), str(newdn
)), e
)
727 server_dsa_dn
= samdb
.get_serverName()
728 domain
= remote_samdb
.get_root_basedn()
731 sendRemoveDsServer(drsuapiBind
, drsuapi_handle
, server_dsa_dn
, domain
)
732 except drsException
, e
:
733 print "Error while demoting, re-enabling inbound replication"
734 dsa_options ^
= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
735 nmsg
["options"] = ldb
.MessageElement(str(dsa_options
), ldb
.FLAG_MOD_REPLACE
, "options")
741 msg
["userAccountControl"] = ldb
.MessageElement("%d" % uac
,
742 ldb
.FLAG_MOD_REPLACE
,
743 "userAccountControl")
745 remote_samdb
.modify(msg
)
746 remote_samdb
.rename(newdn
, dc_dn
)
747 raise CommandError("Error while sending a removeDsServer", e
)
749 for s
in ("CN=Entreprise,CN=Microsoft System Volumes,CN=System,CN=Configuration",
750 "CN=%s,CN=Microsoft System Volumes,CN=System,CN=Configuration" % lp
.get("realm"),
751 "CN=Domain System Volumes (SYSVOL share),CN=File Replication Service,CN=System"):
753 remote_samdb
.delete(ldb
.Dn(remote_samdb
,
754 "%s,%s,%s" % (str(rdn
), s
, str(remote_samdb
.get_root_basedn()))))
755 except ldb
.LdbError
, l
:
758 for s
in ("CN=Entreprise,CN=NTFRS Subscriptions",
759 "CN=%s, CN=NTFRS Subscriptions" % lp
.get("realm"),
760 "CN=Domain system Volumes (SYSVOL Share), CN=NTFRS Subscriptions",
761 "CN=NTFRS Subscriptions"):
763 remote_samdb
.delete(ldb
.Dn(remote_samdb
,
764 "%s,%s" % (s
, str(newdn
))))
765 except ldb
.LdbError
, l
:
768 self
.outf
.write("Demote successfull\n")
771 class cmd_domain_level(Command
):
772 """Raises domain and forest function levels"""
774 synopsis
= "%prog (show|raise <options>) [options]"
776 takes_optiongroups
= {
777 "sambaopts": options
.SambaOptions
,
778 "credopts": options
.CredentialsOptions
,
779 "versionopts": options
.VersionOptions
,
783 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
784 metavar
="URL", dest
="H"),
785 Option("--quiet", help="Be quiet", action
="store_true"),
786 Option("--forest-level", type="choice", choices
=["2003", "2008", "2008_R2"],
787 help="The forest function level (2003 | 2008 | 2008_R2)"),
788 Option("--domain-level", type="choice", choices
=["2003", "2008", "2008_R2"],
789 help="The domain function level (2003 | 2008 | 2008_R2)")
792 takes_args
= ["subcommand"]
794 def run(self
, subcommand
, H
=None, forest_level
=None, domain_level
=None,
795 quiet
=False, credopts
=None, sambaopts
=None, versionopts
=None):
796 lp
= sambaopts
.get_loadparm()
797 creds
= credopts
.get_credentials(lp
, fallback_machine
=True)
799 samdb
= SamDB(url
=H
, session_info
=system_session(),
800 credentials
=creds
, lp
=lp
)
802 domain_dn
= samdb
.domain_dn()
804 res_forest
= samdb
.search("CN=Partitions,%s" % samdb
.get_config_basedn(),
805 scope
=ldb
.SCOPE_BASE
, attrs
=["msDS-Behavior-Version"])
806 assert len(res_forest
) == 1
808 res_domain
= samdb
.search(domain_dn
, scope
=ldb
.SCOPE_BASE
,
809 attrs
=["msDS-Behavior-Version", "nTMixedDomain"])
810 assert len(res_domain
) == 1
812 res_dc_s
= samdb
.search("CN=Sites,%s" % samdb
.get_config_basedn(),
813 scope
=ldb
.SCOPE_SUBTREE
, expression
="(objectClass=nTDSDSA)",
814 attrs
=["msDS-Behavior-Version"])
815 assert len(res_dc_s
) >= 1
818 level_forest
= int(res_forest
[0]["msDS-Behavior-Version"][0])
819 level_domain
= int(res_domain
[0]["msDS-Behavior-Version"][0])
820 level_domain_mixed
= int(res_domain
[0]["nTMixedDomain"][0])
822 min_level_dc
= int(res_dc_s
[0]["msDS-Behavior-Version"][0]) # Init value
824 if int(msg
["msDS-Behavior-Version"][0]) < min_level_dc
:
825 min_level_dc
= int(msg
["msDS-Behavior-Version"][0])
827 if level_forest
< 0 or level_domain
< 0:
828 raise CommandError("Domain and/or forest function level(s) is/are invalid. Correct them or reprovision!")
830 raise CommandError("Lowest function level of a DC is invalid. Correct this or reprovision!")
831 if level_forest
> level_domain
:
832 raise CommandError("Forest function level is higher than the domain level(s). Correct this or reprovision!")
833 if level_domain
> min_level_dc
:
834 raise CommandError("Domain function level is higher than the lowest function level of a DC. Correct this or reprovision!")
837 raise CommandError("Could not retrieve the actual domain, forest level and/or lowest DC function level!")
839 if subcommand
== "show":
840 self
.message("Domain and forest function level for domain '%s'" % domain_dn
)
841 if level_forest
== DS_DOMAIN_FUNCTION_2000
and level_domain_mixed
!= 0:
842 self
.message("\nATTENTION: You run SAMBA 4 on a forest function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
843 if level_domain
== DS_DOMAIN_FUNCTION_2000
and level_domain_mixed
!= 0:
844 self
.message("\nATTENTION: You run SAMBA 4 on a domain function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
845 if min_level_dc
== DS_DOMAIN_FUNCTION_2000
and level_domain_mixed
!= 0:
846 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)!")
850 if level_forest
== DS_DOMAIN_FUNCTION_2000
:
852 elif level_forest
== DS_DOMAIN_FUNCTION_2003_MIXED
:
853 outstr
= "2003 with mixed domains/interim (NT4 DC support)"
854 elif level_forest
== DS_DOMAIN_FUNCTION_2003
:
856 elif level_forest
== DS_DOMAIN_FUNCTION_2008
:
858 elif level_forest
== DS_DOMAIN_FUNCTION_2008_R2
:
861 outstr
= "higher than 2008 R2"
862 self
.message("Forest function level: (Windows) " + outstr
)
864 if level_domain
== DS_DOMAIN_FUNCTION_2000
and level_domain_mixed
!= 0:
865 outstr
= "2000 mixed (NT4 DC support)"
866 elif level_domain
== DS_DOMAIN_FUNCTION_2000
and level_domain_mixed
== 0:
868 elif level_domain
== DS_DOMAIN_FUNCTION_2003_MIXED
:
869 outstr
= "2003 with mixed domains/interim (NT4 DC support)"
870 elif level_domain
== DS_DOMAIN_FUNCTION_2003
:
872 elif level_domain
== DS_DOMAIN_FUNCTION_2008
:
874 elif level_domain
== DS_DOMAIN_FUNCTION_2008_R2
:
877 outstr
= "higher than 2008 R2"
878 self
.message("Domain function level: (Windows) " + outstr
)
880 if min_level_dc
== DS_DOMAIN_FUNCTION_2000
:
882 elif min_level_dc
== DS_DOMAIN_FUNCTION_2003
:
884 elif min_level_dc
== DS_DOMAIN_FUNCTION_2008
:
886 elif min_level_dc
== DS_DOMAIN_FUNCTION_2008_R2
:
889 outstr
= "higher than 2008 R2"
890 self
.message("Lowest function level of a DC: (Windows) " + outstr
)
892 elif subcommand
== "raise":
895 if domain_level
is not None:
896 if domain_level
== "2003":
897 new_level_domain
= DS_DOMAIN_FUNCTION_2003
898 elif domain_level
== "2008":
899 new_level_domain
= DS_DOMAIN_FUNCTION_2008
900 elif domain_level
== "2008_R2":
901 new_level_domain
= DS_DOMAIN_FUNCTION_2008_R2
903 if new_level_domain
<= level_domain
and level_domain_mixed
== 0:
904 raise CommandError("Domain function level can't be smaller than or equal to the actual one!")
906 if new_level_domain
> min_level_dc
:
907 raise CommandError("Domain function level can't be higher than the lowest function level of a DC!")
909 # Deactivate mixed/interim domain support
910 if level_domain_mixed
!= 0:
911 # Directly on the base DN
913 m
.dn
= ldb
.Dn(samdb
, domain_dn
)
914 m
["nTMixedDomain"] = ldb
.MessageElement("0",
915 ldb
.FLAG_MOD_REPLACE
, "nTMixedDomain")
919 m
.dn
= ldb
.Dn(samdb
, "CN=" + lp
.get("workgroup") + ",CN=Partitions,%s" % samdb
.get_config_basedn())
920 m
["nTMixedDomain"] = ldb
.MessageElement("0",
921 ldb
.FLAG_MOD_REPLACE
, "nTMixedDomain")
924 except ldb
.LdbError
, (enum
, emsg
):
925 if enum
!= ldb
.ERR_UNWILLING_TO_PERFORM
:
928 # Directly on the base DN
930 m
.dn
= ldb
.Dn(samdb
, domain_dn
)
931 m
["msDS-Behavior-Version"]= ldb
.MessageElement(
932 str(new_level_domain
), ldb
.FLAG_MOD_REPLACE
,
933 "msDS-Behavior-Version")
937 m
.dn
= ldb
.Dn(samdb
, "CN=" + lp
.get("workgroup")
938 + ",CN=Partitions,%s" % samdb
.get_config_basedn())
939 m
["msDS-Behavior-Version"]= ldb
.MessageElement(
940 str(new_level_domain
), ldb
.FLAG_MOD_REPLACE
,
941 "msDS-Behavior-Version")
944 except ldb
.LdbError
, (enum
, emsg
):
945 if enum
!= ldb
.ERR_UNWILLING_TO_PERFORM
:
948 level_domain
= new_level_domain
949 msgs
.append("Domain function level changed!")
951 if forest_level
is not None:
952 if forest_level
== "2003":
953 new_level_forest
= DS_DOMAIN_FUNCTION_2003
954 elif forest_level
== "2008":
955 new_level_forest
= DS_DOMAIN_FUNCTION_2008
956 elif forest_level
== "2008_R2":
957 new_level_forest
= DS_DOMAIN_FUNCTION_2008_R2
958 if new_level_forest
<= level_forest
:
959 raise CommandError("Forest function level can't be smaller than or equal to the actual one!")
960 if new_level_forest
> level_domain
:
961 raise CommandError("Forest function level can't be higher than the domain function level(s). Please raise it/them first!")
963 m
.dn
= ldb
.Dn(samdb
, "CN=Partitions,%s" % samdb
.get_config_basedn())
964 m
["msDS-Behavior-Version"]= ldb
.MessageElement(
965 str(new_level_forest
), ldb
.FLAG_MOD_REPLACE
,
966 "msDS-Behavior-Version")
968 msgs
.append("Forest function level changed!")
969 msgs
.append("All changes applied successfully!")
970 self
.message("\n".join(msgs
))
972 raise CommandError("invalid argument: '%s' (choose from 'show', 'raise')" % subcommand
)
975 class cmd_domain_passwordsettings(Command
):
976 """Sets password settings
978 Password complexity, history length, minimum password length, the minimum
979 and maximum password age) on a Samba4 server.
982 synopsis
= "%prog (show|set <options>) [options]"
984 takes_optiongroups
= {
985 "sambaopts": options
.SambaOptions
,
986 "versionopts": options
.VersionOptions
,
987 "credopts": options
.CredentialsOptions
,
991 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
992 metavar
="URL", dest
="H"),
993 Option("--quiet", help="Be quiet", action
="store_true"),
994 Option("--complexity", type="choice", choices
=["on","off","default"],
995 help="The password complexity (on | off | default). Default is 'on'"),
996 Option("--store-plaintext", type="choice", choices
=["on","off","default"],
997 help="Store plaintext passwords where account have 'store passwords with reversible encryption' set (on | off | default). Default is 'off'"),
998 Option("--history-length",
999 help="The password history length (<integer> | default). Default is 24.", type=str),
1000 Option("--min-pwd-length",
1001 help="The minimum password length (<integer> | default). Default is 7.", type=str),
1002 Option("--min-pwd-age",
1003 help="The minimum password age (<integer in days> | default). Default is 1.", type=str),
1004 Option("--max-pwd-age",
1005 help="The maximum password age (<integer in days> | default). Default is 43.", type=str),
1008 takes_args
= ["subcommand"]
1010 def run(self
, subcommand
, H
=None, min_pwd_age
=None, max_pwd_age
=None,
1011 quiet
=False, complexity
=None, store_plaintext
=None, history_length
=None,
1012 min_pwd_length
=None, credopts
=None, sambaopts
=None,
1014 lp
= sambaopts
.get_loadparm()
1015 creds
= credopts
.get_credentials(lp
)
1017 samdb
= SamDB(url
=H
, session_info
=system_session(),
1018 credentials
=creds
, lp
=lp
)
1020 domain_dn
= samdb
.domain_dn()
1021 res
= samdb
.search(domain_dn
, scope
=ldb
.SCOPE_BASE
,
1022 attrs
=["pwdProperties", "pwdHistoryLength", "minPwdLength",
1023 "minPwdAge", "maxPwdAge"])
1024 assert(len(res
) == 1)
1026 pwd_props
= int(res
[0]["pwdProperties"][0])
1027 pwd_hist_len
= int(res
[0]["pwdHistoryLength"][0])
1028 cur_min_pwd_len
= int(res
[0]["minPwdLength"][0])
1030 cur_min_pwd_age
= int(abs(int(res
[0]["minPwdAge"][0])) / (1e7
* 60 * 60 * 24))
1031 if int(res
[0]["maxPwdAge"][0]) == -0x8000000000000000:
1034 cur_max_pwd_age
= int(abs(int(res
[0]["maxPwdAge"][0])) / (1e7
* 60 * 60 * 24))
1035 except Exception, e
:
1036 raise CommandError("Could not retrieve password properties!", e
)
1038 if subcommand
== "show":
1039 self
.message("Password informations for domain '%s'" % domain_dn
)
1041 if pwd_props
& DOMAIN_PASSWORD_COMPLEX
!= 0:
1042 self
.message("Password complexity: on")
1044 self
.message("Password complexity: off")
1045 if pwd_props
& DOMAIN_PASSWORD_STORE_CLEARTEXT
!= 0:
1046 self
.message("Store plaintext passwords: on")
1048 self
.message("Store plaintext passwords: off")
1049 self
.message("Password history length: %d" % pwd_hist_len
)
1050 self
.message("Minimum password length: %d" % cur_min_pwd_len
)
1051 self
.message("Minimum password age (days): %d" % cur_min_pwd_age
)
1052 self
.message("Maximum password age (days): %d" % cur_max_pwd_age
)
1053 elif subcommand
== "set":
1056 m
.dn
= ldb
.Dn(samdb
, domain_dn
)
1058 if complexity
is not None:
1059 if complexity
== "on" or complexity
== "default":
1060 pwd_props
= pwd_props | DOMAIN_PASSWORD_COMPLEX
1061 msgs
.append("Password complexity activated!")
1062 elif complexity
== "off":
1063 pwd_props
= pwd_props
& (~DOMAIN_PASSWORD_COMPLEX
)
1064 msgs
.append("Password complexity deactivated!")
1066 if store_plaintext
is not None:
1067 if store_plaintext
== "on" or store_plaintext
== "default":
1068 pwd_props
= pwd_props | DOMAIN_PASSWORD_STORE_CLEARTEXT
1069 msgs
.append("Plaintext password storage for changed passwords activated!")
1070 elif store_plaintext
== "off":
1071 pwd_props
= pwd_props
& (~DOMAIN_PASSWORD_STORE_CLEARTEXT
)
1072 msgs
.append("Plaintext password storage for changed passwords deactivated!")
1074 if complexity
is not None or store_plaintext
is not None:
1075 m
["pwdProperties"] = ldb
.MessageElement(str(pwd_props
),
1076 ldb
.FLAG_MOD_REPLACE
, "pwdProperties")
1078 if history_length
is not None:
1079 if history_length
== "default":
1082 pwd_hist_len
= int(history_length
)
1084 if pwd_hist_len
< 0 or pwd_hist_len
> 24:
1085 raise CommandError("Password history length must be in the range of 0 to 24!")
1087 m
["pwdHistoryLength"] = ldb
.MessageElement(str(pwd_hist_len
),
1088 ldb
.FLAG_MOD_REPLACE
, "pwdHistoryLength")
1089 msgs
.append("Password history length changed!")
1091 if min_pwd_length
is not None:
1092 if min_pwd_length
== "default":
1095 min_pwd_len
= int(min_pwd_length
)
1097 if min_pwd_len
< 0 or min_pwd_len
> 14:
1098 raise CommandError("Minimum password length must be in the range of 0 to 14!")
1100 m
["minPwdLength"] = ldb
.MessageElement(str(min_pwd_len
),
1101 ldb
.FLAG_MOD_REPLACE
, "minPwdLength")
1102 msgs
.append("Minimum password length changed!")
1104 if min_pwd_age
is not None:
1105 if min_pwd_age
== "default":
1108 min_pwd_age
= int(min_pwd_age
)
1110 if min_pwd_age
< 0 or min_pwd_age
> 998:
1111 raise CommandError("Minimum password age must be in the range of 0 to 998!")
1114 min_pwd_age_ticks
= -int(min_pwd_age
* (24 * 60 * 60 * 1e7
))
1116 m
["minPwdAge"] = ldb
.MessageElement(str(min_pwd_age_ticks
),
1117 ldb
.FLAG_MOD_REPLACE
, "minPwdAge")
1118 msgs
.append("Minimum password age changed!")
1120 if max_pwd_age
is not None:
1121 if max_pwd_age
== "default":
1124 max_pwd_age
= int(max_pwd_age
)
1126 if max_pwd_age
< 0 or max_pwd_age
> 999:
1127 raise CommandError("Maximum password age must be in the range of 0 to 999!")
1130 if max_pwd_age
== 0:
1131 max_pwd_age_ticks
= -0x8000000000000000
1133 max_pwd_age_ticks
= -int(max_pwd_age
* (24 * 60 * 60 * 1e7
))
1135 m
["maxPwdAge"] = ldb
.MessageElement(str(max_pwd_age_ticks
),
1136 ldb
.FLAG_MOD_REPLACE
, "maxPwdAge")
1137 msgs
.append("Maximum password age changed!")
1139 if max_pwd_age
> 0 and min_pwd_age
>= max_pwd_age
:
1140 raise CommandError("Maximum password age (%d) must be greater than minimum password age (%d)!" % (max_pwd_age
, min_pwd_age
))
1143 raise CommandError("You must specify at least one option to set. Try --help")
1145 msgs
.append("All changes applied successfully!")
1146 self
.message("\n".join(msgs
))
1148 raise CommandError("Wrong argument '%s'!" % subcommand
)
1151 class cmd_domain_classicupgrade(Command
):
1152 """Upgrade from Samba classic (NT4-like) database to Samba AD DC database.
1154 Specify either a directory with all Samba classic DC databases and state files (with --dbdir) or
1155 the testparm utility from your classic installation (with --testparm).
1158 synopsis
= "%prog [options] <classic_smb_conf>"
1160 takes_optiongroups
= {
1161 "sambaopts": options
.SambaOptions
,
1162 "versionopts": options
.VersionOptions
1166 Option("--dbdir", type="string", metavar
="DIR",
1167 help="Path to samba classic DC database directory"),
1168 Option("--testparm", type="string", metavar
="PATH",
1169 help="Path to samba classic DC testparm utility from the previous installation. This allows the default paths of the previous installation to be followed"),
1170 Option("--targetdir", type="string", metavar
="DIR",
1171 help="Path prefix where the new Samba 4.0 AD domain should be initialised"),
1172 Option("--quiet", help="Be quiet", action
="store_true"),
1173 Option("--verbose", help="Be verbose", action
="store_true"),
1174 Option("--use-xattrs", type="choice", choices
=["yes","no","auto"], metavar
="[yes|no|auto]",
1175 help="Define if we should use the native fs capabilities or a tdb file for storing attributes likes ntacl, auto tries to make an inteligent guess based on the user rights and system capabilities", default
="auto"),
1176 Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)",
1177 action
="store_true"),
1178 Option("--dns-backend", type="choice", metavar
="NAMESERVER-BACKEND",
1179 choices
=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
1180 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), " \
1181 "BIND9_FLATFILE uses bind9 text database to store zone information, " \
1182 "BIND9_DLZ uses samba4 AD to store zone information, " \
1183 "NONE skips the DNS setup entirely (this DC will not be a DNS server)",
1184 default
="SAMBA_INTERNAL")
1187 takes_args
= ["smbconf"]
1189 def run(self
, smbconf
=None, targetdir
=None, dbdir
=None, testparm
=None,
1190 quiet
=False, verbose
=False, use_xattrs
=None, sambaopts
=None, versionopts
=None,
1191 dns_backend
=None, use_ntvfs
=False):
1193 if not os
.path
.exists(smbconf
):
1194 raise CommandError("File %s does not exist" % smbconf
)
1196 if testparm
and not os
.path
.exists(testparm
):
1197 raise CommandError("Testparm utility %s does not exist" % testparm
)
1199 if dbdir
and not os
.path
.exists(dbdir
):
1200 raise CommandError("Directory %s does not exist" % dbdir
)
1202 if not dbdir
and not testparm
:
1203 raise CommandError("Please specify either dbdir or testparm")
1205 logger
= self
.get_logger()
1207 logger
.setLevel(logging
.DEBUG
)
1209 logger
.setLevel(logging
.WARNING
)
1211 logger
.setLevel(logging
.INFO
)
1213 if dbdir
and testparm
:
1214 logger
.warning("both dbdir and testparm specified, ignoring dbdir.")
1217 lp
= sambaopts
.get_loadparm()
1219 s3conf
= s3param
.get_context()
1222 s3conf
.set("realm", sambaopts
.realm
)
1224 if targetdir
is not None:
1225 if not os
.path
.isdir(targetdir
):
1229 if use_xattrs
== "yes":
1231 elif use_xattrs
== "auto" and not s3conf
.get("posix:eadb"):
1233 tmpfile
= tempfile
.NamedTemporaryFile(dir=os
.path
.abspath(targetdir
))
1235 tmpfile
= tempfile
.NamedTemporaryFile(dir=os
.path
.abspath(os
.path
.dirname(lp
.get("private dir"))))
1238 samba
.ntacls
.setntacl(lp
, tmpfile
.name
,
1239 "O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
1242 # FIXME: Don't catch all exceptions here
1243 logger
.info("You are not root or your system do not support xattr, using tdb backend for attributes. "
1244 "If you intend to use this provision in production, rerun the script as root on a system supporting xattrs.")
1248 # Set correct default values from dbdir or testparm
1251 paths
["state directory"] = dbdir
1252 paths
["private dir"] = dbdir
1253 paths
["lock directory"] = dbdir
1254 paths
["smb passwd file"] = dbdir
+ "/smbpasswd"
1256 paths
["state directory"] = get_testparm_var(testparm
, smbconf
, "state directory")
1257 paths
["private dir"] = get_testparm_var(testparm
, smbconf
, "private dir")
1258 paths
["smb passwd file"] = get_testparm_var(testparm
, smbconf
, "smb passwd file")
1259 paths
["lock directory"] = get_testparm_var(testparm
, smbconf
, "lock directory")
1260 # "testparm" from Samba 3 < 3.4.x is not aware of the parameter
1261 # "state directory", instead make use of "lock directory"
1262 if len(paths
["state directory"]) == 0:
1263 paths
["state directory"] = paths
["lock directory"]
1266 s3conf
.set(p
, paths
[p
])
1268 # load smb.conf parameters
1269 logger
.info("Reading smb.conf")
1270 s3conf
.load(smbconf
)
1271 samba3
= Samba3(smbconf
, s3conf
)
1273 logger
.info("Provisioning")
1274 upgrade_from_samba3(samba3
, logger
, targetdir
, session_info
=system_session(),
1275 useeadb
=eadb
, dns_backend
=dns_backend
, use_ntvfs
=use_ntvfs
)
1277 class cmd_domain(SuperCommand
):
1278 """Domain management"""
1281 subcommands
["demote"] = cmd_domain_demote()
1282 if type(cmd_domain_export_keytab
).__name
__ != 'NoneType':
1283 subcommands
["exportkeytab"] = cmd_domain_export_keytab()
1284 subcommands
["info"] = cmd_domain_info()
1285 subcommands
["provision"] = cmd_domain_provision()
1286 subcommands
["join"] = cmd_domain_join()
1287 subcommands
["dcpromo"] = cmd_domain_dcpromo()
1288 subcommands
["level"] = cmd_domain_level()
1289 subcommands
["passwordsettings"] = cmd_domain_passwordsettings()
1290 subcommands
["classicupgrade"] = cmd_domain_classicupgrade()
1291 subcommands
["samba3upgrade"] = cmd_domain_classicupgrade()