1 # Unix SMB/CIFS implementation.
2 # backend code for provisioning a Samba4 server
4 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2012
5 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
6 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
8 # Based on the original in EJS:
9 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
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 """Functions for setting up a Samba configuration."""
27 __docformat__
= "restructuredText"
29 from base64
import b64encode
44 from samba
.auth
import system_session
, admin_session
46 from samba
.samba3
import smbd
, passdb
47 from samba
.samba3
import param
as s3param
48 from samba
.dsdb
import DS_DOMAIN_FUNCTION_2000
52 check_all_substituted
,
53 is_valid_netbios_char
,
59 from samba
.dcerpc
import security
, misc
60 from samba
.dcerpc
.misc
import (
64 from samba
.dsdb
import (
65 DS_DOMAIN_FUNCTION_2003
,
66 DS_DOMAIN_FUNCTION_2008_R2
,
69 from samba
.idmap
import IDmapDB
70 from samba
.ms_display_specifiers
import read_ms_ldif
71 from samba
.ntacls
import setntacl
, getntacl
, dsacl2fsacl
72 from samba
.ndr
import ndr_pack
, ndr_unpack
73 from samba
.provision
.backend
import (
79 from samba
.provision
.descriptor
import (
81 get_config_descriptor
,
82 get_config_partitions_descriptor
,
83 get_config_sites_descriptor
,
84 get_domain_descriptor
,
85 get_domain_infrastructure_descriptor
,
86 get_domain_builtin_descriptor
,
87 get_domain_computers_descriptor
,
88 get_domain_users_descriptor
,
89 get_domain_controllers_descriptor
,
90 get_dns_partition_descriptor
,
92 from samba
.provision
.common
import (
97 from samba
.provision
.sambadns
import (
99 create_dns_update_list
103 import samba
.registry
104 from samba
.schema
import Schema
105 from samba
.samdb
import SamDB
106 from samba
.dbchecker
import dbcheck
109 DEFAULT_POLICY_GUID
= "31B2F340-016D-11D2-945F-00C04FB984F9"
110 DEFAULT_DC_POLICY_GUID
= "6AC1786C-016F-11D2-945F-00C04fB984F9"
111 DEFAULTSITE
= "Default-First-Site-Name"
112 LAST_PROVISION_USN_ATTRIBUTE
= "lastProvisionUSN"
115 class ProvisionPaths(object):
118 self
.shareconf
= None
129 self
.dns_keytab
= None
132 self
.private_dir
= None
133 self
.state_dir
= None
136 class ProvisionNames(object):
144 self
.dnsforestdn
= None
145 self
.dnsdomaindn
= None
146 self
.ldapmanagerdn
= None
147 self
.dnsdomain
= None
149 self
.netbiosname
= None
156 def find_provision_key_parameters(samdb
, secretsdb
, idmapdb
, paths
, smbconf
,
158 """Get key provision parameters (realm, domain, ...) from a given provision
160 :param samdb: An LDB object connected to the sam.ldb file
161 :param secretsdb: An LDB object connected to the secrets.ldb file
162 :param idmapdb: An LDB object connected to the idmap.ldb file
163 :param paths: A list of path to provision object
164 :param smbconf: Path to the smb.conf file
165 :param lp: A LoadParm object
166 :return: A list of key provision parameters
168 names
= ProvisionNames()
169 names
.adminpass
= None
171 # NT domain, kerberos realm, root dn, domain dn, domain dns name
172 names
.domain
= string
.upper(lp
.get("workgroup"))
173 names
.realm
= lp
.get("realm")
174 names
.dnsdomain
= names
.realm
.lower()
175 basedn
= samba
.dn_from_dns_name(names
.dnsdomain
)
176 names
.realm
= string
.upper(names
.realm
)
178 # Get the netbiosname first (could be obtained from smb.conf in theory)
179 res
= secretsdb
.search(expression
="(flatname=%s)" %
180 names
.domain
,base
="CN=Primary Domains",
181 scope
=ldb
.SCOPE_SUBTREE
, attrs
=["sAMAccountName"])
182 names
.netbiosname
= str(res
[0]["sAMAccountName"]).replace("$","")
184 names
.smbconf
= smbconf
186 # That's a bit simplistic but it's ok as long as we have only 3
188 current
= samdb
.search(expression
="(objectClass=*)",
189 base
="", scope
=ldb
.SCOPE_BASE
,
190 attrs
=["defaultNamingContext", "schemaNamingContext",
191 "configurationNamingContext","rootDomainNamingContext",
194 names
.configdn
= current
[0]["configurationNamingContext"]
195 configdn
= str(names
.configdn
)
196 names
.schemadn
= current
[0]["schemaNamingContext"]
197 if not (ldb
.Dn(samdb
, basedn
) == (ldb
.Dn(samdb
,
198 current
[0]["defaultNamingContext"][0]))):
199 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
200 "is not the same ..." % (paths
.samdb
,
201 str(current
[0]["defaultNamingContext"][0]),
202 paths
.smbconf
, basedn
)))
204 names
.domaindn
=current
[0]["defaultNamingContext"]
205 names
.rootdn
=current
[0]["rootDomainNamingContext"]
206 names
.ncs
=current
[0]["namingContexts"]
207 names
.dnsforestdn
= None
208 names
.dnsdomaindn
= None
210 for i
in range(0, len(names
.ncs
)):
213 dnsforestdn
= "DC=ForestDnsZones,%s" % (str(names
.rootdn
))
214 if nc
== dnsforestdn
:
215 names
.dnsforestdn
= dnsforestdn
218 dnsdomaindn
= "DC=DomainDnsZones,%s" % (str(names
.domaindn
))
219 if nc
== dnsdomaindn
:
220 names
.dnsdomaindn
= dnsdomaindn
224 res3
= samdb
.search(expression
="(objectClass=site)",
225 base
="CN=Sites," + configdn
, scope
=ldb
.SCOPE_ONELEVEL
, attrs
=["cn"])
226 names
.sitename
= str(res3
[0]["cn"])
228 # dns hostname and server dn
229 res4
= samdb
.search(expression
="(CN=%s)" % names
.netbiosname
,
230 base
="OU=Domain Controllers,%s" % basedn
,
231 scope
=ldb
.SCOPE_ONELEVEL
, attrs
=["dNSHostName"])
232 names
.hostname
= str(res4
[0]["dNSHostName"]).replace("." + names
.dnsdomain
, "")
234 server_res
= samdb
.search(expression
="serverReference=%s" % res4
[0].dn
,
235 attrs
=[], base
=configdn
)
236 names
.serverdn
= server_res
[0].dn
238 # invocation id/objectguid
239 res5
= samdb
.search(expression
="(objectClass=*)",
240 base
="CN=NTDS Settings,%s" % str(names
.serverdn
),
241 scope
=ldb
.SCOPE_BASE
,
242 attrs
=["invocationID", "objectGUID"])
243 names
.invocation
= str(ndr_unpack(misc
.GUID
, res5
[0]["invocationId"][0]))
244 names
.ntdsguid
= str(ndr_unpack(misc
.GUID
, res5
[0]["objectGUID"][0]))
247 res6
= samdb
.search(expression
="(objectClass=*)", base
=basedn
,
248 scope
=ldb
.SCOPE_BASE
, attrs
=["objectGUID",
249 "objectSid","msDS-Behavior-Version" ])
250 names
.domainguid
= str(ndr_unpack(misc
.GUID
, res6
[0]["objectGUID"][0]))
251 names
.domainsid
= ndr_unpack( security
.dom_sid
, res6
[0]["objectSid"][0])
252 if res6
[0].get("msDS-Behavior-Version") is None or \
253 int(res6
[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000
:
254 names
.domainlevel
= DS_DOMAIN_FUNCTION_2000
256 names
.domainlevel
= int(res6
[0]["msDS-Behavior-Version"][0])
259 res7
= samdb
.search(expression
="(displayName=Default Domain Policy)",
260 base
="CN=Policies,CN=System," + basedn
,
261 scope
=ldb
.SCOPE_ONELEVEL
, attrs
=["cn","displayName"])
262 names
.policyid
= str(res7
[0]["cn"]).replace("{","").replace("}","")
264 res8
= samdb
.search(expression
="(displayName=Default Domain Controllers"
266 base
="CN=Policies,CN=System," + basedn
,
267 scope
=ldb
.SCOPE_ONELEVEL
,
268 attrs
=["cn","displayName"])
270 names
.policyid_dc
= str(res8
[0]["cn"]).replace("{","").replace("}","")
272 names
.policyid_dc
= None
274 res9
= idmapdb
.search(expression
="(cn=%s-%s)" %
275 (str(names
.domainsid
), security
.DOMAIN_RID_ADMINISTRATOR
),
276 attrs
=["xidNumber", "type"])
278 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid (%s-%s" % (str(names
.domainsid
), security
.DOMAIN_RID_ADMINISTRATOR
))
279 if res9
[0]["type"][0] == "ID_TYPE_BOTH":
280 names
.root_gid
= res9
[0]["xidNumber"][0]
282 names
.root_gid
= pwd
.getpwuid(int(res9
[0]["xidNumber"][0])).pw_gid
286 def update_provision_usn(samdb
, low
, high
, id, replace
=False):
287 """Update the field provisionUSN in sam.ldb
289 This field is used to track range of USN modified by provision and
291 This value is used afterward by next provision to figure out if
292 the field have been modified since last provision.
294 :param samdb: An LDB object connect to sam.ldb
295 :param low: The lowest USN modified by this upgrade
296 :param high: The highest USN modified by this upgrade
297 :param id: The invocation id of the samba's dc
298 :param replace: A boolean indicating if the range should replace any
299 existing one or appended (default)
304 entry
= samdb
.search(base
="@PROVISION",
305 scope
=ldb
.SCOPE_BASE
,
306 attrs
=[LAST_PROVISION_USN_ATTRIBUTE
, "dn"])
307 for e
in entry
[0][LAST_PROVISION_USN_ATTRIBUTE
]:
308 if not re
.search(';', e
):
309 e
= "%s;%s" % (e
, id)
312 tab
.append("%s-%s;%s" % (low
, high
, id))
313 delta
= ldb
.Message()
314 delta
.dn
= ldb
.Dn(samdb
, "@PROVISION")
315 delta
[LAST_PROVISION_USN_ATTRIBUTE
] = ldb
.MessageElement(tab
,
316 ldb
.FLAG_MOD_REPLACE
, LAST_PROVISION_USN_ATTRIBUTE
)
317 entry
= samdb
.search(expression
='provisionnerID=*',
318 base
="@PROVISION", scope
=ldb
.SCOPE_BASE
,
319 attrs
=["provisionnerID"])
320 if len(entry
) == 0 or len(entry
[0]) == 0:
321 delta
["provisionnerID"] = ldb
.MessageElement(id, ldb
.FLAG_MOD_ADD
, "provisionnerID")
325 def set_provision_usn(samdb
, low
, high
, id):
326 """Set the field provisionUSN in sam.ldb
327 This field is used to track range of USN modified by provision and
329 This value is used afterward by next provision to figure out if
330 the field have been modified since last provision.
332 :param samdb: An LDB object connect to sam.ldb
333 :param low: The lowest USN modified by this upgrade
334 :param high: The highest USN modified by this upgrade
335 :param id: The invocationId of the provision"""
338 tab
.append("%s-%s;%s" % (low
, high
, id))
340 delta
= ldb
.Message()
341 delta
.dn
= ldb
.Dn(samdb
, "@PROVISION")
342 delta
[LAST_PROVISION_USN_ATTRIBUTE
] = ldb
.MessageElement(tab
,
343 ldb
.FLAG_MOD_ADD
, LAST_PROVISION_USN_ATTRIBUTE
)
347 def get_max_usn(samdb
,basedn
):
348 """ This function return the biggest USN present in the provision
350 :param samdb: A LDB object pointing to the sam.ldb
351 :param basedn: A string containing the base DN of the provision
353 :return: The biggest USN in the provision"""
355 res
= samdb
.search(expression
="objectClass=*",base
=basedn
,
356 scope
=ldb
.SCOPE_SUBTREE
,attrs
=["uSNChanged"],
357 controls
=["search_options:1:2",
358 "server_sort:1:1:uSNChanged",
359 "paged_results:1:1"])
360 return res
[0]["uSNChanged"]
363 def get_last_provision_usn(sam
):
364 """Get USNs ranges modified by a provision or an upgradeprovision
366 :param sam: An LDB object pointing to the sam.ldb
367 :return: a dictionary which keys are invocation id and values are an array
368 of integer representing the different ranges
371 entry
= sam
.search(expression
="%s=*" % LAST_PROVISION_USN_ATTRIBUTE
,
372 base
="@PROVISION", scope
=ldb
.SCOPE_BASE
,
373 attrs
=[LAST_PROVISION_USN_ATTRIBUTE
, "provisionnerID"])
374 except ldb
.LdbError
, (ecode
, emsg
):
375 if ecode
== ldb
.ERR_NO_SUCH_OBJECT
:
382 if entry
[0].get("provisionnerID"):
383 for e
in entry
[0]["provisionnerID"]:
385 for r
in entry
[0][LAST_PROVISION_USN_ATTRIBUTE
]:
386 tab1
= str(r
).split(';')
391 if (len(myids
) > 0 and id not in myids
):
393 tab2
= p
.split(tab1
[0])
394 if range.get(id) is None:
396 range[id].append(tab2
[0])
397 range[id].append(tab2
[1])
403 class ProvisionResult(object):
404 """Result of a provision.
406 :ivar server_role: The server role
407 :ivar paths: ProvisionPaths instance
408 :ivar domaindn: The domain dn, as string
412 self
.server_role
= None
419 self
.domainsid
= None
420 self
.adminpass_generated
= None
421 self
.adminpass
= None
422 self
.backend_result
= None
424 def report_logger(self
, logger
):
425 """Report this provision result to a logger."""
427 "Once the above files are installed, your Samba4 server will "
429 if self
.adminpass_generated
:
430 logger
.info("Admin password: %s", self
.adminpass
)
431 logger
.info("Server Role: %s", self
.server_role
)
432 logger
.info("Hostname: %s", self
.names
.hostname
)
433 logger
.info("NetBIOS Domain: %s", self
.names
.domain
)
434 logger
.info("DNS Domain: %s", self
.names
.dnsdomain
)
435 logger
.info("DOMAIN SID: %s", self
.domainsid
)
437 if self
.backend_result
:
438 self
.backend_result
.report_logger(logger
)
441 def check_install(lp
, session_info
, credentials
):
442 """Check whether the current install seems ok.
444 :param lp: Loadparm context
445 :param session_info: Session information
446 :param credentials: Credentials
448 if lp
.get("realm") == "":
449 raise Exception("Realm empty")
450 samdb
= Ldb(lp
.samdb_url(), session_info
=session_info
,
451 credentials
=credentials
, lp
=lp
)
452 if len(samdb
.search("(cn=Administrator)")) != 1:
453 raise ProvisioningError("No administrator account found")
456 def findnss(nssfn
, names
):
457 """Find a user or group from a list of possibilities.
459 :param nssfn: NSS Function to try (should raise KeyError if not found)
460 :param names: Names to check.
461 :return: Value return by first names list.
468 raise KeyError("Unable to find user/group in %r" % names
)
471 findnss_uid
= lambda names
: findnss(pwd
.getpwnam
, names
)[2]
472 findnss_gid
= lambda names
: findnss(grp
.getgrnam
, names
)[2]
475 def provision_paths_from_lp(lp
, dnsdomain
):
476 """Set the default paths for provisioning.
478 :param lp: Loadparm context.
479 :param dnsdomain: DNS Domain name
481 paths
= ProvisionPaths()
482 paths
.private_dir
= lp
.get("private dir")
483 paths
.state_dir
= lp
.get("state directory")
485 # This is stored without path prefix for the "privateKeytab" attribute in
486 # "secrets_dns.ldif".
487 paths
.dns_keytab
= "dns.keytab"
488 paths
.keytab
= "secrets.keytab"
490 paths
.shareconf
= os
.path
.join(paths
.private_dir
, "share.ldb")
491 paths
.samdb
= os
.path
.join(paths
.private_dir
, "sam.ldb")
492 paths
.idmapdb
= os
.path
.join(paths
.private_dir
, "idmap.ldb")
493 paths
.secrets
= os
.path
.join(paths
.private_dir
, "secrets.ldb")
494 paths
.privilege
= os
.path
.join(paths
.private_dir
, "privilege.ldb")
495 paths
.dns
= os
.path
.join(paths
.private_dir
, "dns", dnsdomain
+ ".zone")
496 paths
.dns_update_list
= os
.path
.join(paths
.private_dir
, "dns_update_list")
497 paths
.spn_update_list
= os
.path
.join(paths
.private_dir
, "spn_update_list")
498 paths
.namedconf
= os
.path
.join(paths
.private_dir
, "named.conf")
499 paths
.namedconf_update
= os
.path
.join(paths
.private_dir
, "named.conf.update")
500 paths
.namedtxt
= os
.path
.join(paths
.private_dir
, "named.txt")
501 paths
.krb5conf
= os
.path
.join(paths
.private_dir
, "krb5.conf")
502 paths
.winsdb
= os
.path
.join(paths
.private_dir
, "wins.ldb")
503 paths
.s4_ldapi_path
= os
.path
.join(paths
.private_dir
, "ldapi")
504 paths
.hklm
= "hklm.ldb"
505 paths
.hkcr
= "hkcr.ldb"
506 paths
.hkcu
= "hkcu.ldb"
507 paths
.hku
= "hku.ldb"
508 paths
.hkpd
= "hkpd.ldb"
509 paths
.hkpt
= "hkpt.ldb"
510 paths
.sysvol
= lp
.get("path", "sysvol")
511 paths
.netlogon
= lp
.get("path", "netlogon")
512 paths
.smbconf
= lp
.configfile
516 def determine_netbios_name(hostname
):
517 """Determine a netbios name from a hostname."""
518 # remove forbidden chars and force the length to be <16
519 netbiosname
= "".join([x
for x
in hostname
if is_valid_netbios_char(x
)])
520 return netbiosname
[:MAX_NETBIOS_NAME_LEN
].upper()
523 def guess_names(lp
=None, hostname
=None, domain
=None, dnsdomain
=None,
524 serverrole
=None, rootdn
=None, domaindn
=None, configdn
=None,
525 schemadn
=None, serverdn
=None, sitename
=None):
526 """Guess configuration settings to use."""
529 hostname
= socket
.gethostname().split(".")[0]
531 netbiosname
= lp
.get("netbios name")
532 if netbiosname
is None:
533 netbiosname
= determine_netbios_name(hostname
)
534 netbiosname
= netbiosname
.upper()
535 if not valid_netbios_name(netbiosname
):
536 raise InvalidNetbiosName(netbiosname
)
538 if dnsdomain
is None:
539 dnsdomain
= lp
.get("realm")
540 if dnsdomain
is None or dnsdomain
== "":
541 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp
.configfile
)
543 dnsdomain
= dnsdomain
.lower()
545 if serverrole
is None:
546 serverrole
= lp
.get("server role")
547 if serverrole
is None:
548 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp
.configfile
)
550 serverrole
= serverrole
.lower()
552 realm
= dnsdomain
.upper()
554 if lp
.get("realm") == "":
555 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp
.configfile
)
557 if lp
.get("realm").upper() != realm
:
558 raise ProvisioningError("guess_names: 'realm=%s' in %s must match chosen realm '%s'! Please remove the smb.conf file and let provision generate it" % (lp
.get("realm").upper(), realm
, lp
.configfile
))
560 if lp
.get("server role").lower() != serverrole
:
561 raise ProvisioningError("guess_names: 'server role=%s' in %s must match chosen server role '%s'! Please remove the smb.conf file and let provision generate it" % (lp
.get("server role"), lp
.configfile
, serverrole
))
563 if serverrole
== "active directory domain controller":
565 # This will, for better or worse, default to 'WORKGROUP'
566 domain
= lp
.get("workgroup")
567 domain
= domain
.upper()
569 if lp
.get("workgroup").upper() != domain
:
570 raise ProvisioningError("guess_names: Workgroup '%s' in smb.conf must match chosen domain '%s'! Please remove the %s file and let provision generate it" % (lp
.get("workgroup").upper(), domain
, lp
.configfile
))
573 domaindn
= samba
.dn_from_dns_name(dnsdomain
)
575 if domain
== netbiosname
:
576 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain
, netbiosname
))
580 domaindn
= "DC=" + netbiosname
582 if not valid_netbios_name(domain
):
583 raise InvalidNetbiosName(domain
)
585 if hostname
.upper() == realm
:
586 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm
, hostname
))
587 if netbiosname
.upper() == realm
:
588 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm
, netbiosname
))
590 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm
, domain
))
596 configdn
= "CN=Configuration," + rootdn
598 schemadn
= "CN=Schema," + configdn
601 sitename
= DEFAULTSITE
603 names
= ProvisionNames()
604 names
.rootdn
= rootdn
605 names
.domaindn
= domaindn
606 names
.configdn
= configdn
607 names
.schemadn
= schemadn
608 names
.ldapmanagerdn
= "CN=Manager," + rootdn
609 names
.dnsdomain
= dnsdomain
610 names
.domain
= domain
612 names
.netbiosname
= netbiosname
613 names
.hostname
= hostname
614 names
.sitename
= sitename
615 names
.serverdn
= "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
616 netbiosname
, sitename
, configdn
)
621 def make_smbconf(smbconf
, hostname
, domain
, realm
, targetdir
,
622 serverrole
=None, eadb
=False, use_ntvfs
=False, lp
=None,
624 """Create a new smb.conf file based on a couple of basic settings.
626 assert smbconf
is not None
629 hostname
= socket
.gethostname().split(".")[0]
631 netbiosname
= determine_netbios_name(hostname
)
633 if serverrole
is None:
634 serverrole
= "standalone server"
636 assert domain
is not None
637 domain
= domain
.upper()
639 assert realm
is not None
640 realm
= realm
.upper()
643 "netbios name": netbiosname
,
646 "server role": serverrole
,
650 lp
= samba
.param
.LoadParm()
651 #Load non-existent file
652 if os
.path
.exists(smbconf
):
655 if global_param
is not None:
656 for ent
in global_param
:
657 if global_param
[ent
] is not None:
658 global_settings
[ent
] = " ".join(global_param
[ent
])
660 if targetdir
is not None:
661 global_settings
["private dir"] = os
.path
.abspath(os
.path
.join(targetdir
, "private"))
662 global_settings
["lock dir"] = os
.path
.abspath(targetdir
)
663 global_settings
["state directory"] = os
.path
.abspath(os
.path
.join(targetdir
, "state"))
664 global_settings
["cache directory"] = os
.path
.abspath(os
.path
.join(targetdir
, "cache"))
666 lp
.set("lock dir", os
.path
.abspath(targetdir
))
667 lp
.set("state directory", global_settings
["state directory"])
668 lp
.set("cache directory", global_settings
["cache directory"])
671 if use_ntvfs
and not lp
.get("posix:eadb"):
672 if targetdir
is not None:
673 privdir
= os
.path
.join(targetdir
, "private")
675 privdir
= lp
.get("private dir")
676 lp
.set("posix:eadb", os
.path
.abspath(os
.path
.join(privdir
, "eadb.tdb")))
677 elif not use_ntvfs
and not lp
.get("xattr_tdb:file"):
678 if targetdir
is not None:
679 statedir
= os
.path
.join(targetdir
, "state")
681 statedir
= lp
.get("state directory")
682 lp
.set("xattr_tdb:file", os
.path
.abspath(os
.path
.join(statedir
, "xattr.tdb")))
685 if serverrole
== "active directory domain controller":
686 shares
["sysvol"] = os
.path
.join(lp
.get("state directory"), "sysvol")
687 shares
["netlogon"] = os
.path
.join(shares
["sysvol"], realm
.lower(),
690 global_settings
["passdb backend"] = "samba_dsdb"
692 f
= open(smbconf
, 'w')
694 f
.write("[globals]\n")
695 for key
, val
in global_settings
.iteritems():
696 f
.write("\t%s = %s\n" % (key
, val
))
699 for name
, path
in shares
.iteritems():
700 f
.write("[%s]\n" % name
)
701 f
.write("\tpath = %s\n" % path
)
702 f
.write("\tread only = no\n")
706 # reload the smb.conf
709 # and dump it without any values that are the default
710 # this ensures that any smb.conf parameters that were set
711 # on the provision/join command line are set in the resulting smb.conf
712 f
= open(smbconf
, mode
='w')
719 def setup_name_mappings(idmap
, sid
, root_uid
, nobody_uid
,
720 users_gid
, root_gid
):
721 """setup reasonable name mappings for sam names to unix names.
723 :param samdb: SamDB object.
724 :param idmap: IDmap db object.
725 :param sid: The domain sid.
726 :param domaindn: The domain DN.
727 :param root_uid: uid of the UNIX root user.
728 :param nobody_uid: uid of the UNIX nobody user.
729 :param users_gid: gid of the UNIX users group.
730 :param root_gid: gid of the UNIX root group.
732 idmap
.setup_name_mapping("S-1-5-7", idmap
.TYPE_UID
, nobody_uid
)
734 idmap
.setup_name_mapping(sid
+ "-500", idmap
.TYPE_UID
, root_uid
)
735 idmap
.setup_name_mapping(sid
+ "-513", idmap
.TYPE_GID
, users_gid
)
738 def setup_samdb_partitions(samdb_path
, logger
, lp
, session_info
,
739 provision_backend
, names
, schema
, serverrole
,
741 """Setup the partitions for the SAM database.
743 Alternatively, provision() may call this, and then populate the database.
745 :note: This will wipe the Sam Database!
747 :note: This function always removes the local SAM LDB file. The erase
748 parameter controls whether to erase the existing data, which
749 may not be stored locally but in LDAP.
752 assert session_info
is not None
754 # We use options=["modules:"] to stop the modules loading - we
755 # just want to wipe and re-initialise the database, not start it up
758 os
.unlink(samdb_path
)
762 samdb
= Ldb(url
=samdb_path
, session_info
=session_info
,
763 lp
=lp
, options
=["modules:"])
765 ldap_backend_line
= "# No LDAP backend"
766 if provision_backend
.type != "ldb":
767 ldap_backend_line
= "ldapBackend: %s" % provision_backend
.ldap_uri
769 samdb
.transaction_start()
771 logger
.info("Setting up sam.ldb partitions and settings")
772 setup_add_ldif(samdb
, setup_path("provision_partitions.ldif"), {
773 "LDAP_BACKEND_LINE": ldap_backend_line
777 setup_add_ldif(samdb
, setup_path("provision_init.ldif"), {
778 "BACKEND_TYPE": provision_backend
.type,
779 "SERVER_ROLE": serverrole
782 logger
.info("Setting up sam.ldb rootDSE")
783 setup_samdb_rootdse(samdb
, names
)
785 samdb
.transaction_cancel()
788 samdb
.transaction_commit()
791 def secretsdb_self_join(secretsdb
, domain
,
792 netbiosname
, machinepass
, domainsid
=None,
793 realm
=None, dnsdomain
=None,
795 key_version_number
=1,
796 secure_channel_type
=SEC_CHAN_WKSTA
):
797 """Add domain join-specific bits to a secrets database.
799 :param secretsdb: Ldb Handle to the secrets database
800 :param machinepass: Machine password
802 attrs
= ["whenChanged",
809 if realm
is not None:
810 if dnsdomain
is None:
811 dnsdomain
= realm
.lower()
812 dnsname
= '%s.%s' % (netbiosname
.lower(), dnsdomain
.lower())
815 shortname
= netbiosname
.lower()
817 # We don't need to set msg["flatname"] here, because rdn_name will handle
818 # it, and it causes problems for modifies anyway
819 msg
= ldb
.Message(ldb
.Dn(secretsdb
, "flatname=%s,cn=Primary Domains" % domain
))
820 msg
["secureChannelType"] = [str(secure_channel_type
)]
821 msg
["objectClass"] = ["top", "primaryDomain"]
822 if dnsname
is not None:
823 msg
["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
824 msg
["realm"] = [realm
]
825 msg
["saltPrincipal"] = ["host/%s@%s" % (dnsname
, realm
.upper())]
826 msg
["msDS-KeyVersionNumber"] = [str(key_version_number
)]
827 msg
["privateKeytab"] = ["secrets.keytab"]
829 msg
["secret"] = [machinepass
]
830 msg
["samAccountName"] = ["%s$" % netbiosname
]
831 msg
["secureChannelType"] = [str(secure_channel_type
)]
832 if domainsid
is not None:
833 msg
["objectSid"] = [ndr_pack(domainsid
)]
835 # This complex expression tries to ensure that we don't have more
836 # than one record for this SID, realm or netbios domain at a time,
837 # but we don't delete the old record that we are about to modify,
838 # because that would delete the keytab and previous password.
839 res
= secretsdb
.search(base
="cn=Primary Domains", attrs
=attrs
,
840 expression
=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain
, realm
, str(domainsid
), str(msg
.dn
))),
841 scope
=ldb
.SCOPE_ONELEVEL
)
844 secretsdb
.delete(del_msg
.dn
)
846 res
= secretsdb
.search(base
=msg
.dn
, attrs
=attrs
, scope
=ldb
.SCOPE_BASE
)
849 msg
["priorSecret"] = [res
[0]["secret"][0]]
850 msg
["priorWhenChanged"] = [res
[0]["whenChanged"][0]]
853 msg
["privateKeytab"] = [res
[0]["privateKeytab"][0]]
858 msg
["krb5Keytab"] = [res
[0]["krb5Keytab"][0]]
864 msg
[el
].set_flags(ldb
.FLAG_MOD_REPLACE
)
865 secretsdb
.modify(msg
)
866 secretsdb
.rename(res
[0].dn
, msg
.dn
)
868 spn
= [ 'HOST/%s' % shortname
]
869 if secure_channel_type
== SEC_CHAN_BDC
and dnsname
is not None:
870 # we are a domain controller then we add servicePrincipalName
871 # entries for the keytab code to update.
872 spn
.extend([ 'HOST/%s' % dnsname
])
873 msg
["servicePrincipalName"] = spn
878 def setup_secretsdb(paths
, session_info
, backend_credentials
, lp
):
879 """Setup the secrets database.
881 :note: This function does not handle exceptions and transaction on purpose,
882 it's up to the caller to do this job.
884 :param path: Path to the secrets database.
885 :param session_info: Session info.
886 :param credentials: Credentials
887 :param lp: Loadparm context
888 :return: LDB handle for the created secrets database
890 if os
.path
.exists(paths
.secrets
):
891 os
.unlink(paths
.secrets
)
893 keytab_path
= os
.path
.join(paths
.private_dir
, paths
.keytab
)
894 if os
.path
.exists(keytab_path
):
895 os
.unlink(keytab_path
)
897 dns_keytab_path
= os
.path
.join(paths
.private_dir
, paths
.dns_keytab
)
898 if os
.path
.exists(dns_keytab_path
):
899 os
.unlink(dns_keytab_path
)
903 secrets_ldb
= Ldb(path
, session_info
=session_info
, lp
=lp
)
905 secrets_ldb
.load_ldif_file_add(setup_path("secrets_init.ldif"))
906 secrets_ldb
= Ldb(path
, session_info
=session_info
, lp
=lp
)
907 secrets_ldb
.transaction_start()
909 secrets_ldb
.load_ldif_file_add(setup_path("secrets.ldif"))
911 if (backend_credentials
is not None and
912 backend_credentials
.authentication_requested()):
913 if backend_credentials
.get_bind_dn() is not None:
914 setup_add_ldif(secrets_ldb
,
915 setup_path("secrets_simple_ldap.ldif"), {
916 "LDAPMANAGERDN": backend_credentials
.get_bind_dn(),
917 "LDAPMANAGERPASS_B64": b64encode(backend_credentials
.get_password())
920 setup_add_ldif(secrets_ldb
,
921 setup_path("secrets_sasl_ldap.ldif"), {
922 "LDAPADMINUSER": backend_credentials
.get_username(),
923 "LDAPADMINREALM": backend_credentials
.get_realm(),
924 "LDAPADMINPASS_B64": b64encode(backend_credentials
.get_password())
927 secrets_ldb
.transaction_cancel()
932 def setup_privileges(path
, session_info
, lp
):
933 """Setup the privileges database.
935 :param path: Path to the privileges database.
936 :param session_info: Session info.
937 :param credentials: Credentials
938 :param lp: Loadparm context
939 :return: LDB handle for the created secrets database
941 if os
.path
.exists(path
):
943 privilege_ldb
= Ldb(path
, session_info
=session_info
, lp
=lp
)
944 privilege_ldb
.erase()
945 privilege_ldb
.load_ldif_file_add(setup_path("provision_privilege.ldif"))
948 def setup_registry(path
, session_info
, lp
):
949 """Setup the registry.
951 :param path: Path to the registry database
952 :param session_info: Session information
953 :param credentials: Credentials
954 :param lp: Loadparm context
956 reg
= samba
.registry
.Registry()
957 hive
= samba
.registry
.open_ldb(path
, session_info
=session_info
, lp_ctx
=lp
)
958 reg
.mount_hive(hive
, samba
.registry
.HKEY_LOCAL_MACHINE
)
959 provision_reg
= setup_path("provision.reg")
960 assert os
.path
.exists(provision_reg
)
961 reg
.diff_apply(provision_reg
)
964 def setup_idmapdb(path
, session_info
, lp
):
965 """Setup the idmap database.
967 :param path: path to the idmap database
968 :param session_info: Session information
969 :param credentials: Credentials
970 :param lp: Loadparm context
972 if os
.path
.exists(path
):
975 idmap_ldb
= IDmapDB(path
, session_info
=session_info
, lp
=lp
)
977 idmap_ldb
.load_ldif_file_add(setup_path("idmap_init.ldif"))
981 def setup_samdb_rootdse(samdb
, names
):
982 """Setup the SamDB rootdse.
984 :param samdb: Sam Database handle
986 setup_add_ldif(samdb
, setup_path("provision_rootdse_add.ldif"), {
987 "SCHEMADN": names
.schemadn
,
988 "DOMAINDN": names
.domaindn
,
989 "ROOTDN" : names
.rootdn
,
990 "CONFIGDN": names
.configdn
,
991 "SERVERDN": names
.serverdn
,
995 def setup_self_join(samdb
, admin_session_info
, names
, fill
, machinepass
,
996 dns_backend
, dnspass
, domainsid
, next_rid
, invocationid
,
997 policyguid
, policyguid_dc
,
998 domainControllerFunctionality
, ntdsguid
=None, dc_rid
=None):
999 """Join a host to its own domain."""
1000 assert isinstance(invocationid
, str)
1001 if ntdsguid
is not None:
1002 ntdsguid_line
= "objectGUID: %s\n"%ntdsguid
1009 setup_add_ldif(samdb
, setup_path("provision_self_join.ldif"), {
1010 "CONFIGDN": names
.configdn
,
1011 "SCHEMADN": names
.schemadn
,
1012 "DOMAINDN": names
.domaindn
,
1013 "SERVERDN": names
.serverdn
,
1014 "INVOCATIONID": invocationid
,
1015 "NETBIOSNAME": names
.netbiosname
,
1016 "DNSNAME": "%s.%s" % (names
.hostname
, names
.dnsdomain
),
1017 "MACHINEPASS_B64": b64encode(machinepass
.encode('utf-16-le')),
1018 "DOMAINSID": str(domainsid
),
1019 "DCRID": str(dc_rid
),
1020 "SAMBA_VERSION_STRING": version
,
1021 "NTDSGUID": ntdsguid_line
,
1022 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1023 domainControllerFunctionality
),
1024 "RIDALLOCATIONSTART": str(next_rid
+ 100),
1025 "RIDALLOCATIONEND": str(next_rid
+ 100 + 499)})
1027 setup_add_ldif(samdb
, setup_path("provision_group_policy.ldif"), {
1028 "POLICYGUID": policyguid
,
1029 "POLICYGUID_DC": policyguid_dc
,
1030 "DNSDOMAIN": names
.dnsdomain
,
1031 "DOMAINDN": names
.domaindn
})
1033 # If we are setting up a subdomain, then this has been replicated in, so we
1034 # don't need to add it
1035 if fill
== FILL_FULL
:
1036 setup_add_ldif(samdb
, setup_path("provision_self_join_config.ldif"), {
1037 "CONFIGDN": names
.configdn
,
1038 "SCHEMADN": names
.schemadn
,
1039 "DOMAINDN": names
.domaindn
,
1040 "SERVERDN": names
.serverdn
,
1041 "INVOCATIONID": invocationid
,
1042 "NETBIOSNAME": names
.netbiosname
,
1043 "DNSNAME": "%s.%s" % (names
.hostname
, names
.dnsdomain
),
1044 "MACHINEPASS_B64": b64encode(machinepass
.encode('utf-16-le')),
1045 "DOMAINSID": str(domainsid
),
1046 "DCRID": str(dc_rid
),
1047 "SAMBA_VERSION_STRING": version
,
1048 "NTDSGUID": ntdsguid_line
,
1049 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1050 domainControllerFunctionality
)})
1052 # Setup fSMORoleOwner entries to point at the newly created DC entry
1053 setup_modify_ldif(samdb
,
1054 setup_path("provision_self_join_modify_config.ldif"), {
1055 "CONFIGDN": names
.configdn
,
1056 "SCHEMADN": names
.schemadn
,
1057 "DEFAULTSITE": names
.sitename
,
1058 "NETBIOSNAME": names
.netbiosname
,
1059 "SERVERDN": names
.serverdn
,
1062 system_session_info
= system_session()
1063 samdb
.set_session_info(system_session_info
)
1064 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1065 # modify a serverReference under cn=config when we are a subdomain, we must
1066 # be system due to ACLs
1067 setup_modify_ldif(samdb
, setup_path("provision_self_join_modify.ldif"), {
1068 "DOMAINDN": names
.domaindn
,
1069 "SERVERDN": names
.serverdn
,
1070 "NETBIOSNAME": names
.netbiosname
,
1073 samdb
.set_session_info(admin_session_info
)
1075 if dns_backend
!= "SAMBA_INTERNAL":
1076 # This is Samba4 specific and should be replaced by the correct
1077 # DNS AD-style setup
1078 setup_add_ldif(samdb
, setup_path("provision_dns_add_samba.ldif"), {
1079 "DNSDOMAIN": names
.dnsdomain
,
1080 "DOMAINDN": names
.domaindn
,
1081 "DNSPASS_B64": b64encode(dnspass
.encode('utf-16-le')),
1082 "HOSTNAME" : names
.hostname
,
1083 "DNSNAME" : '%s.%s' % (
1084 names
.netbiosname
.lower(), names
.dnsdomain
.lower())
1088 def getpolicypath(sysvolpath
, dnsdomain
, guid
):
1089 """Return the physical path of policy given its guid.
1091 :param sysvolpath: Path to the sysvol folder
1092 :param dnsdomain: DNS name of the AD domain
1093 :param guid: The GUID of the policy
1094 :return: A string with the complete path to the policy folder
1097 guid
= "{%s}" % guid
1098 policy_path
= os
.path
.join(sysvolpath
, dnsdomain
, "Policies", guid
)
1102 def create_gpo_struct(policy_path
):
1103 if not os
.path
.exists(policy_path
):
1104 os
.makedirs(policy_path
, 0775)
1105 f
= open(os
.path
.join(policy_path
, "GPT.INI"), 'w')
1107 f
.write("[General]\r\nVersion=0")
1110 p
= os
.path
.join(policy_path
, "MACHINE")
1111 if not os
.path
.exists(p
):
1112 os
.makedirs(p
, 0775)
1113 p
= os
.path
.join(policy_path
, "USER")
1114 if not os
.path
.exists(p
):
1115 os
.makedirs(p
, 0775)
1118 def create_default_gpo(sysvolpath
, dnsdomain
, policyguid
, policyguid_dc
):
1119 """Create the default GPO for a domain
1121 :param sysvolpath: Physical path for the sysvol folder
1122 :param dnsdomain: DNS domain name of the AD domain
1123 :param policyguid: GUID of the default domain policy
1124 :param policyguid_dc: GUID of the default domain controler policy
1126 policy_path
= getpolicypath(sysvolpath
,dnsdomain
,policyguid
)
1127 create_gpo_struct(policy_path
)
1129 policy_path
= getpolicypath(sysvolpath
,dnsdomain
,policyguid_dc
)
1130 create_gpo_struct(policy_path
)
1133 def setup_samdb(path
, session_info
, provision_backend
, lp
, names
,
1134 logger
, fill
, serverrole
, schema
, am_rodc
=False):
1135 """Setup a complete SAM Database.
1137 :note: This will wipe the main SAM database file!
1140 # Also wipes the database
1141 setup_samdb_partitions(path
, logger
=logger
, lp
=lp
,
1142 provision_backend
=provision_backend
, session_info
=session_info
,
1143 names
=names
, serverrole
=serverrole
, schema
=schema
)
1145 # Load the database, but don's load the global schema and don't connect
1147 samdb
= SamDB(session_info
=session_info
, url
=None, auto_connect
=False,
1148 credentials
=provision_backend
.credentials
, lp
=lp
,
1149 global_schema
=False, am_rodc
=am_rodc
)
1151 logger
.info("Pre-loading the Samba 4 and AD schema")
1153 # Load the schema from the one we computed earlier
1154 samdb
.set_schema(schema
, write_indices_and_attributes
=False)
1156 # Set the NTDS settings DN manually - in order to have it already around
1157 # before the provisioned tree exists and we connect
1158 samdb
.set_ntds_settings_dn("CN=NTDS Settings,%s" % names
.serverdn
)
1160 # And now we can connect to the DB - the schema won't be loaded from the
1164 # But we have to give it one more kick to have it use the schema
1165 # during provision - it needs, now that it is connected, to write
1166 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1167 samdb
.set_schema(schema
, write_indices_and_attributes
=True)
1172 def fill_samdb(samdb
, lp
, names
, logger
, domainsid
, domainguid
, policyguid
,
1173 policyguid_dc
, fill
, adminpass
, krbtgtpass
, machinepass
, dns_backend
,
1174 dnspass
, invocationid
, ntdsguid
, serverrole
, am_rodc
=False,
1175 dom_for_fun_level
=None, schema
=None, next_rid
=None, dc_rid
=None):
1177 if next_rid
is None:
1180 # Provision does not make much sense values larger than 1000000000
1181 # as the upper range of the rIDAvailablePool is 1073741823 and
1182 # we don't want to create a domain that cannot allocate rids.
1183 if next_rid
< 1000 or next_rid
> 1000000000:
1184 error
= "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid
)
1185 error
+= "the valid range is %u-%u. The default is %u." % (
1186 1000, 1000000000, 1000)
1187 raise ProvisioningError(error
)
1189 # ATTENTION: Do NOT change these default values without discussion with the
1190 # team and/or release manager. They have a big impact on the whole program!
1191 domainControllerFunctionality
= DS_DOMAIN_FUNCTION_2008_R2
1193 if dom_for_fun_level
is None:
1194 dom_for_fun_level
= DS_DOMAIN_FUNCTION_2003
1196 if dom_for_fun_level
> domainControllerFunctionality
:
1197 raise ProvisioningError("You want to run SAMBA 4 on a domain and forest function level which itself is higher than its actual DC function level (2008_R2). This won't work!")
1199 domainFunctionality
= dom_for_fun_level
1200 forestFunctionality
= dom_for_fun_level
1202 # Set the NTDS settings DN manually - in order to have it already around
1203 # before the provisioned tree exists and we connect
1204 samdb
.set_ntds_settings_dn("CN=NTDS Settings,%s" % names
.serverdn
)
1206 samdb
.transaction_start()
1208 # Set the domain functionality levels onto the database.
1209 # Various module (the password_hash module in particular) need
1210 # to know what level of AD we are emulating.
1212 # These will be fixed into the database via the database
1213 # modifictions below, but we need them set from the start.
1214 samdb
.set_opaque_integer("domainFunctionality", domainFunctionality
)
1215 samdb
.set_opaque_integer("forestFunctionality", forestFunctionality
)
1216 samdb
.set_opaque_integer("domainControllerFunctionality",
1217 domainControllerFunctionality
)
1219 samdb
.set_domain_sid(str(domainsid
))
1220 samdb
.set_invocation_id(invocationid
)
1222 logger
.info("Adding DomainDN: %s" % names
.domaindn
)
1224 # impersonate domain admin
1225 admin_session_info
= admin_session(lp
, str(domainsid
))
1226 samdb
.set_session_info(admin_session_info
)
1227 if domainguid
is not None:
1228 domainguid_line
= "objectGUID: %s\n-" % domainguid
1230 domainguid_line
= ""
1232 descr
= b64encode(get_domain_descriptor(domainsid
))
1233 setup_add_ldif(samdb
, setup_path("provision_basedn.ldif"), {
1234 "DOMAINDN": names
.domaindn
,
1235 "DOMAINSID": str(domainsid
),
1236 "DESCRIPTOR": descr
,
1237 "DOMAINGUID": domainguid_line
1240 setup_modify_ldif(samdb
, setup_path("provision_basedn_modify.ldif"), {
1241 "DOMAINDN": names
.domaindn
,
1242 "CREATTIME": str(samba
.unix2nttime(int(time
.time()))),
1243 "NEXTRID": str(next_rid
),
1244 "DEFAULTSITE": names
.sitename
,
1245 "CONFIGDN": names
.configdn
,
1246 "POLICYGUID": policyguid
,
1247 "DOMAIN_FUNCTIONALITY": str(domainFunctionality
),
1248 "SAMBA_VERSION_STRING": version
1251 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1252 if fill
== FILL_FULL
:
1253 logger
.info("Adding configuration container")
1254 descr
= b64encode(get_config_descriptor(domainsid
))
1255 setup_add_ldif(samdb
, setup_path("provision_configuration_basedn.ldif"), {
1256 "CONFIGDN": names
.configdn
,
1257 "DESCRIPTOR": descr
,
1260 # The LDIF here was created when the Schema object was constructed
1261 logger
.info("Setting up sam.ldb schema")
1262 samdb
.add_ldif(schema
.schema_dn_add
, controls
=["relax:0"])
1263 samdb
.modify_ldif(schema
.schema_dn_modify
)
1264 samdb
.write_prefixes_from_schema()
1265 samdb
.add_ldif(schema
.schema_data
, controls
=["relax:0"])
1266 setup_add_ldif(samdb
, setup_path("aggregate_schema.ldif"),
1267 {"SCHEMADN": names
.schemadn
})
1269 # Now register this container in the root of the forest
1270 msg
= ldb
.Message(ldb
.Dn(samdb
, names
.domaindn
))
1271 msg
["subRefs"] = ldb
.MessageElement(names
.configdn
, ldb
.FLAG_MOD_ADD
,
1275 samdb
.transaction_cancel()
1278 samdb
.transaction_commit()
1280 samdb
.transaction_start()
1282 samdb
.invocation_id
= invocationid
1284 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1285 if fill
== FILL_FULL
:
1286 logger
.info("Setting up sam.ldb configuration data")
1287 partitions_descr
= b64encode(get_config_partitions_descriptor(domainsid
))
1288 sites_descr
= b64encode(get_config_sites_descriptor(domainsid
))
1289 setup_add_ldif(samdb
, setup_path("provision_configuration.ldif"), {
1290 "CONFIGDN": names
.configdn
,
1291 "NETBIOSNAME": names
.netbiosname
,
1292 "DEFAULTSITE": names
.sitename
,
1293 "DNSDOMAIN": names
.dnsdomain
,
1294 "DOMAIN": names
.domain
,
1295 "SCHEMADN": names
.schemadn
,
1296 "DOMAINDN": names
.domaindn
,
1297 "SERVERDN": names
.serverdn
,
1298 "FOREST_FUNCTIONALITY": str(forestFunctionality
),
1299 "DOMAIN_FUNCTIONALITY": str(domainFunctionality
),
1300 "PARTITIONS_DESCRIPTOR": partitions_descr
,
1301 "SITES_DESCRIPTOR": sites_descr
,
1304 logger
.info("Setting up display specifiers")
1305 display_specifiers_ldif
= read_ms_ldif(
1306 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1307 display_specifiers_ldif
= substitute_var(display_specifiers_ldif
,
1308 {"CONFIGDN": names
.configdn
})
1309 check_all_substituted(display_specifiers_ldif
)
1310 samdb
.add_ldif(display_specifiers_ldif
)
1312 logger
.info("Adding users container")
1313 users_desc
= b64encode(get_domain_users_descriptor(domainsid
))
1314 setup_add_ldif(samdb
, setup_path("provision_users_add.ldif"), {
1315 "DOMAINDN": names
.domaindn
,
1316 "USERS_DESCRIPTOR": users_desc
1318 logger
.info("Modifying users container")
1319 setup_modify_ldif(samdb
, setup_path("provision_users_modify.ldif"), {
1320 "DOMAINDN": names
.domaindn
})
1321 logger
.info("Adding computers container")
1322 computers_desc
= b64encode(get_domain_computers_descriptor(domainsid
))
1323 setup_add_ldif(samdb
, setup_path("provision_computers_add.ldif"), {
1324 "DOMAINDN": names
.domaindn
,
1325 "COMPUTERS_DESCRIPTOR": computers_desc
1327 logger
.info("Modifying computers container")
1328 setup_modify_ldif(samdb
,
1329 setup_path("provision_computers_modify.ldif"), {
1330 "DOMAINDN": names
.domaindn
})
1331 logger
.info("Setting up sam.ldb data")
1332 infrastructure_desc
= b64encode(get_domain_infrastructure_descriptor(domainsid
))
1333 builtin_desc
= b64encode(get_domain_builtin_descriptor(domainsid
))
1334 controllers_desc
= b64encode(get_domain_controllers_descriptor(domainsid
))
1335 setup_add_ldif(samdb
, setup_path("provision.ldif"), {
1336 "CREATTIME": str(samba
.unix2nttime(int(time
.time()))),
1337 "DOMAINDN": names
.domaindn
,
1338 "NETBIOSNAME": names
.netbiosname
,
1339 "DEFAULTSITE": names
.sitename
,
1340 "CONFIGDN": names
.configdn
,
1341 "SERVERDN": names
.serverdn
,
1342 "RIDAVAILABLESTART": str(next_rid
+ 600),
1343 "POLICYGUID_DC": policyguid_dc
,
1344 "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc
,
1345 "BUILTIN_DESCRIPTOR": builtin_desc
,
1346 "DOMAIN_CONTROLLERS_DESCRIPTOR": controllers_desc
,
1349 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1350 if fill
== FILL_FULL
:
1351 setup_modify_ldif(samdb
,
1352 setup_path("provision_configuration_references.ldif"), {
1353 "CONFIGDN": names
.configdn
,
1354 "SCHEMADN": names
.schemadn
})
1356 logger
.info("Setting up well known security principals")
1357 setup_add_ldif(samdb
, setup_path("provision_well_known_sec_princ.ldif"), {
1358 "CONFIGDN": names
.configdn
,
1361 if fill
== FILL_FULL
or fill
== FILL_SUBDOMAIN
:
1362 setup_modify_ldif(samdb
,
1363 setup_path("provision_basedn_references.ldif"),
1364 {"DOMAINDN": names
.domaindn
})
1366 logger
.info("Setting up sam.ldb users and groups")
1367 setup_add_ldif(samdb
, setup_path("provision_users.ldif"), {
1368 "DOMAINDN": names
.domaindn
,
1369 "DOMAINSID": str(domainsid
),
1370 "ADMINPASS_B64": b64encode(adminpass
.encode('utf-16-le')),
1371 "KRBTGTPASS_B64": b64encode(krbtgtpass
.encode('utf-16-le'))
1374 logger
.info("Setting up self join")
1375 setup_self_join(samdb
, admin_session_info
, names
=names
, fill
=fill
,
1376 invocationid
=invocationid
,
1377 dns_backend
=dns_backend
,
1379 machinepass
=machinepass
,
1380 domainsid
=domainsid
,
1383 policyguid
=policyguid
,
1384 policyguid_dc
=policyguid_dc
,
1385 domainControllerFunctionality
=domainControllerFunctionality
,
1388 ntds_dn
= "CN=NTDS Settings,%s" % names
.serverdn
1389 names
.ntdsguid
= samdb
.searchone(basedn
=ntds_dn
,
1390 attribute
="objectGUID", expression
="", scope
=ldb
.SCOPE_BASE
)
1391 assert isinstance(names
.ntdsguid
, str)
1393 samdb
.transaction_cancel()
1396 samdb
.transaction_commit()
1401 FILL_SUBDOMAIN
= "SUBDOMAIN"
1402 FILL_NT4SYNC
= "NT4SYNC"
1404 SYSVOL_ACL
= "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1405 POLICIES_ACL
= "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)(A;OICI;0x001301bf;;;PA)"
1406 SYSVOL_SERVICE
="sysvol"
1408 def set_dir_acl(path
, acl
, lp
, domsid
, use_ntvfs
, passdb
, service
=SYSVOL_SERVICE
):
1409 setntacl(lp
, path
, acl
, domsid
, use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=passdb
, service
=service
)
1410 for root
, dirs
, files
in os
.walk(path
, topdown
=False):
1412 setntacl(lp
, os
.path
.join(root
, name
), acl
, domsid
,
1413 use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=passdb
, service
=service
)
1415 setntacl(lp
, os
.path
.join(root
, name
), acl
, domsid
,
1416 use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=passdb
, service
=service
)
1419 def set_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
, use_ntvfs
, passdb
):
1420 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1423 :param sysvol: Physical path for the sysvol folder
1424 :param dnsdomain: The DNS name of the domain
1425 :param domainsid: The SID of the domain
1426 :param domaindn: The DN of the domain (ie. DC=...)
1427 :param samdb: An LDB object on the SAM db
1428 :param lp: an LP object
1431 # Set ACL for GPO root folder
1432 root_policy_path
= os
.path
.join(sysvol
, dnsdomain
, "Policies")
1433 setntacl(lp
, root_policy_path
, POLICIES_ACL
, str(domainsid
),
1434 use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=passdb
, service
=SYSVOL_SERVICE
)
1436 res
= samdb
.search(base
="CN=Policies,CN=System,%s"%(domaindn),
1437 attrs
=["cn", "nTSecurityDescriptor"],
1438 expression
="", scope
=ldb
.SCOPE_ONELEVEL
)
1441 acl
= ndr_unpack(security
.descriptor
,
1442 str(policy
["nTSecurityDescriptor"])).as_sddl()
1443 policy_path
= getpolicypath(sysvol
, dnsdomain
, str(policy
["cn"]))
1444 set_dir_acl(policy_path
, dsacl2fsacl(acl
, domainsid
), lp
,
1445 str(domainsid
), use_ntvfs
,
1449 def setsysvolacl(samdb
, netlogon
, sysvol
, uid
, gid
, domainsid
, dnsdomain
,
1450 domaindn
, lp
, use_ntvfs
):
1451 """Set the ACL for the sysvol share and the subfolders
1453 :param samdb: An LDB object on the SAM db
1454 :param netlogon: Physical path for the netlogon folder
1455 :param sysvol: Physical path for the sysvol folder
1456 :param uid: The UID of the "Administrator" user
1457 :param gid: The GID of the "Domain adminstrators" group
1458 :param domainsid: The SID of the domain
1459 :param dnsdomain: The DNS name of the domain
1460 :param domaindn: The DN of the domain (ie. DC=...)
1465 # This will ensure that the smbd code we are running when setting ACLs
1466 # is initialised with the smb.conf
1467 s3conf
= s3param
.get_context()
1468 s3conf
.load(lp
.configfile
)
1469 # ensure we are using the right samba_dsdb passdb backend, no matter what
1470 s3conf
.set("passdb backend", "samba_dsdb:%s" % samdb
.url
)
1471 passdb
.reload_static_pdb()
1473 # ensure that we init the samba_dsdb backend, so the domain sid is
1474 # marked in secrets.tdb
1475 s4_passdb
= passdb
.PDB(s3conf
.get("passdb backend"))
1477 # now ensure everything matches correctly, to avoid wierd issues
1478 if passdb
.get_global_sam_sid() != domainsid
:
1479 raise ProvisioningError('SID as seen by smbd [%s] does not match SID as seen by the provision script [%s]!' % (passdb
.get_global_sam_sid(), domainsid
))
1481 domain_info
= s4_passdb
.domain_info()
1482 if domain_info
["dom_sid"] != domainsid
:
1483 raise ProvisioningError('SID as seen by pdb_samba_dsdb [%s] does not match SID as seen by the provision script [%s]!' % (domain_info
["dom_sid"], domainsid
))
1485 if domain_info
["dns_domain"].upper() != dnsdomain
.upper():
1486 raise ProvisioningError('Realm as seen by pdb_samba_dsdb [%s] does not match Realm as seen by the provision script [%s]!' % (domain_info
["dns_domain"].upper(), dnsdomain
.upper()))
1491 os
.chown(sysvol
, -1, gid
)
1497 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1498 setntacl(lp
,sysvol
, SYSVOL_ACL
, str(domainsid
), use_ntvfs
=use_ntvfs
,
1499 skip_invalid_chown
=True, passdb
=s4_passdb
,
1500 service
=SYSVOL_SERVICE
)
1501 for root
, dirs
, files
in os
.walk(sysvol
, topdown
=False):
1503 if use_ntvfs
and canchown
:
1504 os
.chown(os
.path
.join(root
, name
), -1, gid
)
1505 setntacl(lp
, os
.path
.join(root
, name
), SYSVOL_ACL
, str(domainsid
),
1506 use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True,
1507 passdb
=s4_passdb
, service
=SYSVOL_SERVICE
)
1509 if use_ntvfs
and canchown
:
1510 os
.chown(os
.path
.join(root
, name
), -1, gid
)
1511 setntacl(lp
, os
.path
.join(root
, name
), SYSVOL_ACL
, str(domainsid
),
1512 use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True,
1513 passdb
=s4_passdb
, service
=SYSVOL_SERVICE
)
1515 # Set acls on Policy folder and policies folders
1516 set_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
, use_ntvfs
, passdb
=s4_passdb
)
1518 def acl_type(direct_db_access
):
1519 if direct_db_access
:
1524 def check_dir_acl(path
, acl
, lp
, domainsid
, direct_db_access
):
1525 fsacl
= getntacl(lp
, path
, direct_db_access
=direct_db_access
, service
=SYSVOL_SERVICE
)
1526 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1527 if fsacl_sddl
!= acl
:
1528 raise ProvisioningError('%s ACL on GPO directory %s %s does not match expected value %s from GPO object' % (acl_type(direct_db_access
), path
, fsacl_sddl
, acl
))
1530 for root
, dirs
, files
in os
.walk(path
, topdown
=False):
1532 fsacl
= getntacl(lp
, os
.path
.join(root
, name
),
1533 direct_db_access
=direct_db_access
, service
=SYSVOL_SERVICE
)
1535 raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access
), os
.path
.join(root
, name
)))
1536 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1537 if fsacl_sddl
!= acl
:
1538 raise ProvisioningError('%s ACL on GPO file %s %s does not match expected value %s from GPO object' % (acl_type(direct_db_access
), os
.path
.join(root
, name
), fsacl_sddl
, acl
))
1541 fsacl
= getntacl(lp
, os
.path
.join(root
, name
),
1542 direct_db_access
=direct_db_access
, service
=SYSVOL_SERVICE
)
1544 raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access
), os
.path
.join(root
, name
)))
1545 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1546 if fsacl_sddl
!= acl
:
1547 raise ProvisioningError('%s ACL on GPO directory %s %s does not match expected value %s from GPO object' % (acl_type(direct_db_access
), os
.path
.join(root
, name
), fsacl_sddl
, acl
))
1550 def check_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
,
1552 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1555 :param sysvol: Physical path for the sysvol folder
1556 :param dnsdomain: The DNS name of the domain
1557 :param domainsid: The SID of the domain
1558 :param domaindn: The DN of the domain (ie. DC=...)
1559 :param samdb: An LDB object on the SAM db
1560 :param lp: an LP object
1563 # Set ACL for GPO root folder
1564 root_policy_path
= os
.path
.join(sysvol
, dnsdomain
, "Policies")
1565 fsacl
= getntacl(lp
, root_policy_path
,
1566 direct_db_access
=direct_db_access
, service
=SYSVOL_SERVICE
)
1568 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access
), root_policy_path
))
1569 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1570 if fsacl_sddl
!= POLICIES_ACL
:
1571 raise ProvisioningError('%s ACL on policy root %s %s does not match expected value %s from provision' % (acl_type(direct_db_access
), root_policy_path
, fsacl_sddl
, fsacl
))
1572 res
= samdb
.search(base
="CN=Policies,CN=System,%s"%(domaindn),
1573 attrs
=["cn", "nTSecurityDescriptor"],
1574 expression
="", scope
=ldb
.SCOPE_ONELEVEL
)
1577 acl
= ndr_unpack(security
.descriptor
,
1578 str(policy
["nTSecurityDescriptor"])).as_sddl()
1579 policy_path
= getpolicypath(sysvol
, dnsdomain
, str(policy
["cn"]))
1580 check_dir_acl(policy_path
, dsacl2fsacl(acl
, domainsid
), lp
,
1581 domainsid
, direct_db_access
)
1584 def checksysvolacl(samdb
, netlogon
, sysvol
, domainsid
, dnsdomain
, domaindn
,
1586 """Set the ACL for the sysvol share and the subfolders
1588 :param samdb: An LDB object on the SAM db
1589 :param netlogon: Physical path for the netlogon folder
1590 :param sysvol: Physical path for the sysvol folder
1591 :param uid: The UID of the "Administrator" user
1592 :param gid: The GID of the "Domain adminstrators" group
1593 :param domainsid: The SID of the domain
1594 :param dnsdomain: The DNS name of the domain
1595 :param domaindn: The DN of the domain (ie. DC=...)
1598 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1599 s3conf
= s3param
.get_context()
1600 s3conf
.load(lp
.configfile
)
1601 # ensure we are using the right samba_dsdb passdb backend, no matter what
1602 s3conf
.set("passdb backend", "samba_dsdb:%s" % samdb
.url
)
1603 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1604 s4_passdb
= passdb
.PDB(s3conf
.get("passdb backend"))
1606 # now ensure everything matches correctly, to avoid wierd issues
1607 if passdb
.get_global_sam_sid() != domainsid
:
1608 raise ProvisioningError('SID as seen by smbd [%s] does not match SID as seen by the provision script [%s]!' % (passdb
.get_global_sam_sid(), domainsid
))
1610 domain_info
= s4_passdb
.domain_info()
1611 if domain_info
["dom_sid"] != domainsid
:
1612 raise ProvisioningError('SID as seen by pdb_samba_dsdb [%s] does not match SID as seen by the provision script [%s]!' % (domain_info
["dom_sid"], domainsid
))
1614 if domain_info
["dns_domain"].upper() != dnsdomain
.upper():
1615 raise ProvisioningError('Realm as seen by pdb_samba_dsdb [%s] does not match Realm as seen by the provision script [%s]!' % (domain_info
["dns_domain"].upper(), dnsdomain
.upper()))
1617 # Ensure we can read this directly, and via the smbd VFS
1618 for direct_db_access
in [True, False]:
1619 # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1620 for dir_path
in [os
.path
.join(sysvol
, dnsdomain
), netlogon
]:
1621 fsacl
= getntacl(lp
, dir_path
, direct_db_access
=direct_db_access
, service
=SYSVOL_SERVICE
)
1623 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access
), dir_path
))
1624 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1625 if fsacl_sddl
!= SYSVOL_ACL
:
1626 raise ProvisioningError('%s ACL on sysvol directory %s %s does not match expected value %s from provision' % (acl_type(direct_db_access
), dir_path
, fsacl_sddl
, SYSVOL_ACL
))
1628 # Check acls on Policy folder and policies folders
1629 check_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
,
1633 def interface_ips_v4(lp
):
1634 """return only IPv4 IPs"""
1635 ips
= samba
.interface_ips(lp
, False)
1638 if i
.find(':') == -1:
1643 def interface_ips_v6(lp
, linklocal
=False):
1644 """return only IPv6 IPs"""
1645 ips
= samba
.interface_ips(lp
, False)
1648 if i
.find(':') != -1 and (linklocal
or i
.find('%') == -1):
1653 def provision_fill(samdb
, secrets_ldb
, logger
, names
, paths
,
1654 domainsid
, schema
=None,
1655 targetdir
=None, samdb_fill
=FILL_FULL
,
1656 hostip
=None, hostip6
=None,
1657 next_rid
=1000, dc_rid
=None, adminpass
=None, krbtgtpass
=None,
1658 domainguid
=None, policyguid
=None, policyguid_dc
=None,
1659 invocationid
=None, machinepass
=None, ntdsguid
=None,
1660 dns_backend
=None, dnspass
=None,
1661 serverrole
=None, dom_for_fun_level
=None,
1662 am_rodc
=False, lp
=None, use_ntvfs
=False, skip_sysvolacl
=False):
1663 # create/adapt the group policy GUIDs
1664 # Default GUID for default policy are described at
1665 # "How Core Group Policy Works"
1666 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1667 if policyguid
is None:
1668 policyguid
= DEFAULT_POLICY_GUID
1669 policyguid
= policyguid
.upper()
1670 if policyguid_dc
is None:
1671 policyguid_dc
= DEFAULT_DC_POLICY_GUID
1672 policyguid_dc
= policyguid_dc
.upper()
1674 if invocationid
is None:
1675 invocationid
= str(uuid
.uuid4())
1677 if krbtgtpass
is None:
1678 krbtgtpass
= samba
.generate_random_password(128, 255)
1679 if machinepass
is None:
1680 machinepass
= samba
.generate_random_password(128, 255)
1682 dnspass
= samba
.generate_random_password(128, 255)
1684 samdb
= fill_samdb(samdb
, lp
, names
, logger
=logger
,
1685 domainsid
=domainsid
, schema
=schema
, domainguid
=domainguid
,
1686 policyguid
=policyguid
, policyguid_dc
=policyguid_dc
,
1687 fill
=samdb_fill
, adminpass
=adminpass
, krbtgtpass
=krbtgtpass
,
1688 invocationid
=invocationid
, machinepass
=machinepass
,
1689 dns_backend
=dns_backend
, dnspass
=dnspass
,
1690 ntdsguid
=ntdsguid
, serverrole
=serverrole
,
1691 dom_for_fun_level
=dom_for_fun_level
, am_rodc
=am_rodc
,
1692 next_rid
=next_rid
, dc_rid
=dc_rid
)
1694 if serverrole
== "active directory domain controller":
1696 # Set up group policies (domain policy and domain controller
1698 create_default_gpo(paths
.sysvol
, names
.dnsdomain
, policyguid
,
1700 if not skip_sysvolacl
:
1701 setsysvolacl(samdb
, paths
.netlogon
, paths
.sysvol
, paths
.root_uid
,
1702 paths
.root_gid
, domainsid
, names
.dnsdomain
,
1703 names
.domaindn
, lp
, use_ntvfs
)
1705 logger
.info("Setting acl on sysvol skipped")
1707 secretsdb_self_join(secrets_ldb
, domain
=names
.domain
,
1708 realm
=names
.realm
, dnsdomain
=names
.dnsdomain
,
1709 netbiosname
=names
.netbiosname
, domainsid
=domainsid
,
1710 machinepass
=machinepass
, secure_channel_type
=SEC_CHAN_BDC
)
1712 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1713 # In future, this might be determined from some configuration
1714 kerberos_enctypes
= str(ENC_ALL_TYPES
)
1717 msg
= ldb
.Message(ldb
.Dn(samdb
,
1718 samdb
.searchone("distinguishedName",
1719 expression
="samAccountName=%s$" % names
.netbiosname
,
1720 scope
=ldb
.SCOPE_SUBTREE
)))
1721 msg
["msDS-SupportedEncryptionTypes"] = ldb
.MessageElement(
1722 elements
=kerberos_enctypes
, flags
=ldb
.FLAG_MOD_REPLACE
,
1723 name
="msDS-SupportedEncryptionTypes")
1725 except ldb
.LdbError
, (enum
, estr
):
1726 if enum
!= ldb
.ERR_NO_SUCH_ATTRIBUTE
:
1727 # It might be that this attribute does not exist in this schema
1730 setup_ad_dns(samdb
, secrets_ldb
, domainsid
, names
, paths
, lp
, logger
,
1731 hostip
=hostip
, hostip6
=hostip6
, dns_backend
=dns_backend
,
1732 dnspass
=dnspass
, os_level
=dom_for_fun_level
,
1733 targetdir
=targetdir
, site
=DEFAULTSITE
)
1735 domainguid
= samdb
.searchone(basedn
=samdb
.get_default_basedn(),
1736 attribute
="objectGUID")
1737 assert isinstance(domainguid
, str)
1739 lastProvisionUSNs
= get_last_provision_usn(samdb
)
1740 maxUSN
= get_max_usn(samdb
, str(names
.rootdn
))
1741 if lastProvisionUSNs
is not None:
1742 update_provision_usn(samdb
, 0, maxUSN
, invocationid
, 1)
1744 set_provision_usn(samdb
, 0, maxUSN
, invocationid
)
1746 logger
.info("Setting up sam.ldb rootDSE marking as synchronized")
1747 setup_modify_ldif(samdb
, setup_path("provision_rootdse_modify.ldif"),
1748 { 'NTDSGUID' : names
.ntdsguid
})
1750 # fix any dangling GUIDs from the provision
1751 logger
.info("Fixing provision GUIDs")
1752 chk
= dbcheck(samdb
, samdb_schema
=samdb
, verbose
=False, fix
=True, yes
=True,
1754 samdb
.transaction_start()
1756 # a small number of GUIDs are missing because of ordering issues in the
1758 for schema_obj
in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1759 chk
.check_database(DN
="%s,%s" % (schema_obj
, names
.schemadn
),
1760 scope
=ldb
.SCOPE_BASE
,
1761 attrs
=['defaultObjectCategory'])
1762 chk
.check_database(DN
="CN=IP Security,CN=System,%s" % names
.domaindn
,
1763 scope
=ldb
.SCOPE_ONELEVEL
,
1764 attrs
=['ipsecOwnersReference',
1765 'ipsecFilterReference',
1766 'ipsecISAKMPReference',
1767 'ipsecNegotiationPolicyReference',
1768 'ipsecNFAReference'])
1770 samdb
.transaction_cancel()
1773 samdb
.transaction_commit()
1777 "ROLE_STANDALONE": "standalone server",
1778 "ROLE_DOMAIN_MEMBER": "member server",
1779 "ROLE_DOMAIN_BDC": "active directory domain controller",
1780 "ROLE_DOMAIN_PDC": "active directory domain controller",
1781 "dc": "active directory domain controller",
1782 "member": "member server",
1783 "domain controller": "active directory domain controller",
1784 "active directory domain controller": "active directory domain controller",
1785 "member server": "member server",
1786 "standalone": "standalone server",
1787 "standalone server": "standalone server",
1791 def sanitize_server_role(role
):
1792 """Sanitize a server role name.
1794 :param role: Server role
1795 :raise ValueError: If the role can not be interpreted
1796 :return: Sanitized server role (one of "member server",
1797 "active directory domain controller", "standalone server")
1800 return _ROLES_MAP
[role
]
1802 raise ValueError(role
)
1805 def provision_fake_ypserver(logger
, samdb
, domaindn
, netbiosname
, nisdomain
,
1807 """Create AD entries for the fake ypserver.
1809 This is needed for being able to manipulate posix attrs via ADUC.
1811 samdb
.transaction_start()
1813 logger
.info("Setting up fake yp server settings")
1814 setup_add_ldif(samdb
, setup_path("ypServ30.ldif"), {
1815 "DOMAINDN": domaindn
,
1816 "NETBIOSNAME": netbiosname
,
1817 "NISDOMAIN": nisdomain
,
1820 samdb
.transaction_cancel()
1823 samdb
.transaction_commit()
1826 def provision(logger
, session_info
, credentials
, smbconf
=None,
1827 targetdir
=None, samdb_fill
=FILL_FULL
, realm
=None, rootdn
=None,
1828 domaindn
=None, schemadn
=None, configdn
=None, serverdn
=None,
1829 domain
=None, hostname
=None, hostip
=None, hostip6
=None, domainsid
=None,
1830 next_rid
=1000, dc_rid
=None, adminpass
=None, ldapadminpass
=None,
1831 krbtgtpass
=None, domainguid
=None, policyguid
=None, policyguid_dc
=None,
1832 dns_backend
=None, dns_forwarder
=None, dnspass
=None,
1833 invocationid
=None, machinepass
=None, ntdsguid
=None,
1834 root
=None, nobody
=None, users
=None, backup
=None, aci
=None,
1835 serverrole
=None, dom_for_fun_level
=None, backend_type
=None,
1836 sitename
=None, ol_mmr_urls
=None, ol_olc
=None, slapd_path
="/bin/false",
1837 useeadb
=False, am_rodc
=False, lp
=None, use_ntvfs
=False,
1838 use_rfc2307
=False, maxuid
=None, maxgid
=None, skip_sysvolacl
=True):
1841 :note: caution, this wipes all existing data!
1845 serverrole
= sanitize_server_role(serverrole
)
1847 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole
)
1849 if ldapadminpass
is None:
1850 # Make a new, random password between Samba and it's LDAP server
1851 ldapadminpass
= samba
.generate_random_password(128, 255)
1853 if backend_type
is None:
1854 backend_type
= "ldb"
1856 if domainsid
is None:
1857 domainsid
= security
.random_sid()
1859 domainsid
= security
.dom_sid(domainsid
)
1861 root_uid
= findnss_uid([root
or "root"])
1862 nobody_uid
= findnss_uid([nobody
or "nobody"])
1863 users_gid
= findnss_gid([users
or "users", 'users', 'other', 'staff'])
1864 root_gid
= pwd
.getpwuid(root_uid
).pw_gid
1867 bind_gid
= findnss_gid(["bind", "named"])
1871 if targetdir
is not None:
1872 smbconf
= os
.path
.join(targetdir
, "etc", "smb.conf")
1873 elif smbconf
is None:
1874 smbconf
= samba
.param
.default_path()
1875 if not os
.path
.exists(os
.path
.dirname(smbconf
)):
1876 os
.makedirs(os
.path
.dirname(smbconf
))
1878 server_services
= []
1881 global_param
["idmap_ldb:use rfc2307"] = ["yes"]
1883 if dns_backend
!= "SAMBA_INTERNAL":
1884 server_services
.append("-dns")
1886 if dns_forwarder
is not None:
1887 global_param
["dns forwarder"] = [dns_forwarder
]
1890 server_services
.append("+smb")
1891 server_services
.append("-s3fs")
1892 global_param
["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
1894 if len(server_services
) > 0:
1895 global_param
["server services"] = server_services
1897 # only install a new smb.conf if there isn't one there already
1898 if os
.path
.exists(smbconf
):
1899 # if Samba Team members can't figure out the weird errors
1900 # loading an empty smb.conf gives, then we need to be smarter.
1901 # Pretend it just didn't exist --abartlet
1902 f
= open(smbconf
, 'r')
1904 data
= f
.read().lstrip()
1907 if data
is None or data
== "":
1908 make_smbconf(smbconf
, hostname
, domain
, realm
,
1909 targetdir
, serverrole
=serverrole
,
1910 eadb
=useeadb
, use_ntvfs
=use_ntvfs
,
1911 lp
=lp
, global_param
=global_param
)
1913 make_smbconf(smbconf
, hostname
, domain
, realm
, targetdir
,
1914 serverrole
=serverrole
,
1915 eadb
=useeadb
, use_ntvfs
=use_ntvfs
, lp
=lp
, global_param
=global_param
)
1918 lp
= samba
.param
.LoadParm()
1920 names
= guess_names(lp
=lp
, hostname
=hostname
, domain
=domain
,
1921 dnsdomain
=realm
, serverrole
=serverrole
, domaindn
=domaindn
,
1922 configdn
=configdn
, schemadn
=schemadn
, serverdn
=serverdn
,
1923 sitename
=sitename
, rootdn
=rootdn
)
1924 paths
= provision_paths_from_lp(lp
, names
.dnsdomain
)
1926 paths
.bind_gid
= bind_gid
1927 paths
.root_uid
= root_uid
;
1928 paths
.root_gid
= root_gid
1931 logger
.info("Looking up IPv4 addresses")
1932 hostips
= interface_ips_v4(lp
)
1933 if len(hostips
) > 0:
1935 if len(hostips
) > 1:
1936 logger
.warning("More than one IPv4 address found. Using %s",
1938 if hostip
== "127.0.0.1":
1941 logger
.warning("No IPv4 address will be assigned")
1944 logger
.info("Looking up IPv6 addresses")
1945 hostips
= interface_ips_v6(lp
, linklocal
=False)
1947 hostip6
= hostips
[0]
1948 if len(hostips
) > 1:
1949 logger
.warning("More than one IPv6 address found. Using %s", hostip6
)
1951 logger
.warning("No IPv6 address will be assigned")
1953 names
.hostip
= hostip
1954 names
.hostip6
= hostip6
1956 if serverrole
is None:
1957 serverrole
= lp
.get("server role")
1959 if not os
.path
.exists(paths
.private_dir
):
1960 os
.mkdir(paths
.private_dir
)
1961 if not os
.path
.exists(os
.path
.join(paths
.private_dir
, "tls")):
1962 os
.mkdir(os
.path
.join(paths
.private_dir
, "tls"))
1963 if not os
.path
.exists(paths
.state_dir
):
1964 os
.mkdir(paths
.state_dir
)
1966 if paths
.sysvol
and not os
.path
.exists(paths
.sysvol
):
1967 os
.makedirs(paths
.sysvol
, 0775)
1969 if not use_ntvfs
and serverrole
== "active directory domain controller":
1970 s3conf
= s3param
.get_context()
1971 s3conf
.load(lp
.configfile
)
1973 if paths
.sysvol
is None:
1974 raise MissingShareError("sysvol", paths
.smbconf
)
1976 file = tempfile
.NamedTemporaryFile(dir=os
.path
.abspath(paths
.sysvol
))
1979 smbd
.set_simple_acl(file.name
, 0755, root_gid
)
1981 if not smbd
.have_posix_acls():
1982 # This clue is only strictly correct for RPM and
1983 # Debian-like Linux systems, but hopefully other users
1984 # will get enough clue from it.
1985 raise ProvisioningError("Samba was compiled without the posix ACL support that s3fs requires. Try installing libacl1-dev or libacl-devel, then re-run configure and make.")
1987 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. Try the mounting the filesystem with the 'acl' option.")
1989 smbd
.chown(file.name
, root_uid
, root_gid
)
1991 raise ProvisioningError("Unable to chown a file on your filesystem. You may not be running provision as root.")
1995 ldapi_url
= "ldapi://%s" % urllib
.quote(paths
.s4_ldapi_path
, safe
="")
1997 schema
= Schema(domainsid
, invocationid
=invocationid
,
1998 schemadn
=names
.schemadn
)
2000 if backend_type
== "ldb":
2001 provision_backend
= LDBBackend(backend_type
, paths
=paths
,
2002 lp
=lp
, credentials
=credentials
,
2003 names
=names
, logger
=logger
)
2004 elif backend_type
== "existing":
2005 # If support for this is ever added back, then the URI will need to be
2007 provision_backend
= ExistingBackend(backend_type
, paths
=paths
,
2008 lp
=lp
, credentials
=credentials
,
2009 names
=names
, logger
=logger
,
2010 ldap_backend_forced_uri
=None)
2011 elif backend_type
== "fedora-ds":
2012 provision_backend
= FDSBackend(backend_type
, paths
=paths
,
2013 lp
=lp
, credentials
=credentials
,
2014 names
=names
, logger
=logger
, domainsid
=domainsid
,
2015 schema
=schema
, hostname
=hostname
, ldapadminpass
=ldapadminpass
,
2016 slapd_path
=slapd_path
,
2018 elif backend_type
== "openldap":
2019 provision_backend
= OpenLDAPBackend(backend_type
, paths
=paths
,
2020 lp
=lp
, credentials
=credentials
,
2021 names
=names
, logger
=logger
, domainsid
=domainsid
,
2022 schema
=schema
, hostname
=hostname
, ldapadminpass
=ldapadminpass
,
2023 slapd_path
=slapd_path
, ol_mmr_urls
=ol_mmr_urls
)
2025 raise ValueError("Unknown LDAP backend type selected")
2027 provision_backend
.init()
2028 provision_backend
.start()
2030 # only install a new shares config db if there is none
2031 if not os
.path
.exists(paths
.shareconf
):
2032 logger
.info("Setting up share.ldb")
2033 share_ldb
= Ldb(paths
.shareconf
, session_info
=session_info
, lp
=lp
)
2034 share_ldb
.load_ldif_file_add(setup_path("share.ldif"))
2036 logger
.info("Setting up secrets.ldb")
2037 secrets_ldb
= setup_secretsdb(paths
,
2038 session_info
=session_info
,
2039 backend_credentials
=provision_backend
.secrets_credentials
, lp
=lp
)
2042 logger
.info("Setting up the registry")
2043 setup_registry(paths
.hklm
, session_info
, lp
=lp
)
2045 logger
.info("Setting up the privileges database")
2046 setup_privileges(paths
.privilege
, session_info
, lp
=lp
)
2048 logger
.info("Setting up idmap db")
2049 idmap
= setup_idmapdb(paths
.idmapdb
, session_info
=session_info
, lp
=lp
)
2051 setup_name_mappings(idmap
, sid
=str(domainsid
),
2052 root_uid
=root_uid
, nobody_uid
=nobody_uid
,
2053 users_gid
=users_gid
, root_gid
=root_gid
)
2055 logger
.info("Setting up SAM db")
2056 samdb
= setup_samdb(paths
.samdb
, session_info
,
2057 provision_backend
, lp
, names
, logger
=logger
,
2058 serverrole
=serverrole
,
2059 schema
=schema
, fill
=samdb_fill
, am_rodc
=am_rodc
)
2061 if serverrole
== "active directory domain controller":
2062 if paths
.netlogon
is None:
2063 raise MissingShareError("netlogon", paths
.smbconf
)
2065 if paths
.sysvol
is None:
2066 raise MissingShareError("sysvol", paths
.smbconf
)
2068 if not os
.path
.isdir(paths
.netlogon
):
2069 os
.makedirs(paths
.netlogon
, 0755)
2071 if adminpass
is None:
2072 adminpass
= samba
.generate_random_password(12, 32)
2073 adminpass_generated
= True
2075 adminpass_generated
= False
2077 if samdb_fill
== FILL_FULL
:
2078 provision_fill(samdb
, secrets_ldb
, logger
, names
, paths
,
2079 schema
=schema
, targetdir
=targetdir
, samdb_fill
=samdb_fill
,
2080 hostip
=hostip
, hostip6
=hostip6
, domainsid
=domainsid
,
2081 next_rid
=next_rid
, dc_rid
=dc_rid
, adminpass
=adminpass
,
2082 krbtgtpass
=krbtgtpass
, domainguid
=domainguid
,
2083 policyguid
=policyguid
, policyguid_dc
=policyguid_dc
,
2084 invocationid
=invocationid
, machinepass
=machinepass
,
2085 ntdsguid
=ntdsguid
, dns_backend
=dns_backend
,
2086 dnspass
=dnspass
, serverrole
=serverrole
,
2087 dom_for_fun_level
=dom_for_fun_level
, am_rodc
=am_rodc
,
2088 lp
=lp
, use_ntvfs
=use_ntvfs
,
2089 skip_sysvolacl
=skip_sysvolacl
)
2091 create_krb5_conf(paths
.krb5conf
,
2092 dnsdomain
=names
.dnsdomain
, hostname
=names
.hostname
,
2094 logger
.info("A Kerberos configuration suitable for Samba 4 has been "
2095 "generated at %s", paths
.krb5conf
)
2097 if serverrole
== "active directory domain controller":
2098 create_dns_update_list(lp
, logger
, paths
)
2100 backend_result
= provision_backend
.post_setup()
2101 provision_backend
.shutdown()
2104 secrets_ldb
.transaction_cancel()
2107 # Now commit the secrets.ldb to disk
2108 secrets_ldb
.transaction_commit()
2110 # the commit creates the dns.keytab, now chown it
2111 dns_keytab_path
= os
.path
.join(paths
.private_dir
, paths
.dns_keytab
)
2112 if os
.path
.isfile(dns_keytab_path
) and paths
.bind_gid
is not None:
2114 os
.chmod(dns_keytab_path
, 0640)
2115 os
.chown(dns_keytab_path
, -1, paths
.bind_gid
)
2117 if not os
.environ
.has_key('SAMBA_SELFTEST'):
2118 logger
.info("Failed to chown %s to bind gid %u",
2119 dns_keytab_path
, paths
.bind_gid
)
2121 result
= ProvisionResult()
2122 result
.server_role
= serverrole
2123 result
.domaindn
= domaindn
2124 result
.paths
= paths
2125 result
.names
= names
2127 result
.samdb
= samdb
2128 result
.idmap
= idmap
2129 result
.domainsid
= str(domainsid
)
2131 if samdb_fill
== FILL_FULL
:
2132 result
.adminpass_generated
= adminpass_generated
2133 result
.adminpass
= adminpass
2135 result
.adminpass_generated
= False
2136 result
.adminpass
= None
2138 result
.backend_result
= backend_result
2141 provision_fake_ypserver(logger
=logger
, samdb
=samdb
,
2142 domaindn
=names
.domaindn
, netbiosname
=names
.netbiosname
,
2143 nisdomain
=names
.domain
.lower(), maxuid
=maxuid
, maxgid
=maxgid
)
2148 def provision_become_dc(smbconf
=None, targetdir
=None,
2149 realm
=None, rootdn
=None, domaindn
=None, schemadn
=None, configdn
=None,
2150 serverdn
=None, domain
=None, hostname
=None, domainsid
=None,
2151 adminpass
=None, krbtgtpass
=None, domainguid
=None, policyguid
=None,
2152 policyguid_dc
=None, invocationid
=None, machinepass
=None, dnspass
=None,
2153 dns_backend
=None, root
=None, nobody
=None, users
=None,
2154 backup
=None, serverrole
=None, ldap_backend
=None,
2155 ldap_backend_type
=None, sitename
=None, debuglevel
=1, use_ntvfs
=False):
2157 logger
= logging
.getLogger("provision")
2158 samba
.set_debug_level(debuglevel
)
2160 res
= provision(logger
, system_session(), None,
2161 smbconf
=smbconf
, targetdir
=targetdir
, samdb_fill
=FILL_DRS
,
2162 realm
=realm
, rootdn
=rootdn
, domaindn
=domaindn
, schemadn
=schemadn
,
2163 configdn
=configdn
, serverdn
=serverdn
, domain
=domain
,
2164 hostname
=hostname
, hostip
=None, domainsid
=domainsid
,
2165 machinepass
=machinepass
,
2166 serverrole
="active directory domain controller",
2167 sitename
=sitename
, dns_backend
=dns_backend
, dnspass
=dnspass
,
2168 use_ntvfs
=use_ntvfs
)
2169 res
.lp
.set("debuglevel", str(debuglevel
))
2173 def create_krb5_conf(path
, dnsdomain
, hostname
, realm
):
2174 """Write out a file containing zone statements suitable for inclusion in a
2175 named.conf file (including GSS-TSIG configuration).
2177 :param path: Path of the new named.conf file.
2178 :param dnsdomain: DNS Domain name
2179 :param hostname: Local hostname
2180 :param realm: Realm name
2182 setup_file(setup_path("krb5.conf"), path
, {
2183 "DNSDOMAIN": dnsdomain
,
2184 "HOSTNAME": hostname
,
2189 class ProvisioningError(Exception):
2190 """A generic provision error."""
2192 def __init__(self
, value
):
2196 return "ProvisioningError: " + self
.value
2199 class InvalidNetbiosName(Exception):
2200 """A specified name was not a valid NetBIOS name."""
2202 def __init__(self
, name
):
2203 super(InvalidNetbiosName
, self
).__init
__(
2204 "The name '%r' is not a valid NetBIOS name" % name
)
2207 class MissingShareError(ProvisioningError
):
2209 def __init__(self
, name
, smbconf
):
2210 super(MissingShareError
, self
).__init
__(
2211 "Existing smb.conf does not have a [%s] share, but you are "
2212 "configuring a DC. Please remove %s or add the share manually." %