2 # Unix SMB/CIFS implementation.
3 # backend code for provisioning a Samba4 server
5 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2010
6 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
7 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
9 # Based on the original in EJS:
10 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
12 # This program is free software; you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
14 # the Free Software Foundation; either version 3 of the License, or
15 # (at your option) any later version.
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
22 # You should have received a copy of the GNU General Public License
23 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 """Functions for setting up a Samba configuration."""
28 __docformat__
= "restructuredText"
30 from base64
import b64encode
45 from samba
.auth
import system_session
, admin_session
47 from samba
.dsdb
import DS_DOMAIN_FUNCTION_2000
50 check_all_substituted
,
57 from samba
.dcerpc
import security
, misc
58 from samba
.dcerpc
.misc
import (
62 from samba
.dsdb
import (
63 DS_DOMAIN_FUNCTION_2003
,
64 DS_DOMAIN_FUNCTION_2008_R2
,
67 from samba
.idmap
import IDmapDB
68 from samba
.ms_display_specifiers
import read_ms_ldif
69 from samba
.ntacls
import setntacl
, dsacl2fsacl
70 from samba
.ndr
import ndr_pack
, ndr_unpack
71 from samba
.provision
.backend
import (
79 from samba
.schema
import Schema
80 from samba
.samdb
import SamDB
82 VALID_NETBIOS_CHARS
= " !#$%&'()-.@^_{}~"
83 DEFAULT_POLICY_GUID
= "31B2F340-016D-11D2-945F-00C04FB984F9"
84 DEFAULT_DC_POLICY_GUID
= "6AC1786C-016F-11D2-945F-00C04fB984F9"
85 DEFAULTSITE
= "Default-First-Site-Name"
86 LAST_PROVISION_USN_ATTRIBUTE
= "lastProvisionUSN"
90 """Return an absolute path to the provision tempate file specified by file"""
91 return os
.path
.join(samba
.param
.setup_dir(), file)
93 # Descriptors of naming contexts and other important objects
95 # "get_schema_descriptor" is located in "schema.py"
97 def get_config_descriptor(domain_sid
):
98 sddl
= "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
99 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
100 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
101 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
102 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
103 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
104 "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
105 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
106 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
107 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
108 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
109 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
110 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
111 "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
112 "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
113 sec
= security
.descriptor
.from_sddl(sddl
, domain_sid
)
117 def get_domain_descriptor(domain_sid
):
118 sddl
= "O:BAG:BAD:AI(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
119 "(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
120 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
121 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
122 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
123 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
124 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
125 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
126 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
127 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
128 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
129 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)" \
130 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)" \
131 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)" \
132 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)" \
133 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
134 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
135 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
136 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
137 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
138 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
139 "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;IF)" \
140 "(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)" \
141 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)" \
142 "(OA;CIIO;RPLCLORC;;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
143 "(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)" \
144 "(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
145 "(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)" \
146 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
147 "(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)" \
148 "(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)" \
149 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
150 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
151 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
152 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
153 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)" \
154 "(OA;CIIO;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \
155 "(A;;RPWPCRCCLCLORCWOWDSW;;;DA)" \
156 "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
159 "(A;CI;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
161 "(A;;RPLCLORC;;;ED)" \
162 "(A;;RPLCLORC;;;AU)" \
163 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
164 "S:AI(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
165 "(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
166 "(AU;SA;CR;;;DU)(AU;SA;CR;;;BA)(AU;SA;WPWOWD;;;WD)"
167 sec
= security
.descriptor
.from_sddl(sddl
, domain_sid
)
171 class ProvisionPaths(object):
174 self
.shareconf
= None
185 self
.dns_keytab
= None
188 self
.private_dir
= None
191 class ProvisionNames(object):
198 self
.ldapmanagerdn
= None
199 self
.dnsdomain
= None
201 self
.netbiosname
= None
207 def find_provision_key_parameters(samdb
, secretsdb
, idmapdb
, paths
, smbconf
, lp
):
208 """Get key provision parameters (realm, domain, ...) from a given provision
210 :param samdb: An LDB object connected to the sam.ldb file
211 :param secretsdb: An LDB object connected to the secrets.ldb file
212 :param idmapdb: An LDB object connected to the idmap.ldb file
213 :param paths: A list of path to provision object
214 :param smbconf: Path to the smb.conf file
215 :param lp: A LoadParm object
216 :return: A list of key provision parameters
218 names
= ProvisionNames()
219 names
.adminpass
= None
221 # NT domain, kerberos realm, root dn, domain dn, domain dns name
222 names
.domain
= string
.upper(lp
.get("workgroup"))
223 names
.realm
= lp
.get("realm")
224 basedn
= "DC=" + names
.realm
.replace(".",",DC=")
225 names
.dnsdomain
= names
.realm
.lower()
226 names
.realm
= string
.upper(names
.realm
)
228 # Get the netbiosname first (could be obtained from smb.conf in theory)
229 res
= secretsdb
.search(expression
="(flatname=%s)" %
230 names
.domain
,base
="CN=Primary Domains",
231 scope
=ldb
.SCOPE_SUBTREE
, attrs
=["sAMAccountName"])
232 names
.netbiosname
= str(res
[0]["sAMAccountName"]).replace("$","")
234 names
.smbconf
= smbconf
236 # That's a bit simplistic but it's ok as long as we have only 3
238 current
= samdb
.search(expression
="(objectClass=*)",
239 base
="", scope
=ldb
.SCOPE_BASE
,
240 attrs
=["defaultNamingContext", "schemaNamingContext",
241 "configurationNamingContext","rootDomainNamingContext"])
243 names
.configdn
= current
[0]["configurationNamingContext"]
244 configdn
= str(names
.configdn
)
245 names
.schemadn
= current
[0]["schemaNamingContext"]
246 if not (ldb
.Dn(samdb
, basedn
) == (ldb
.Dn(samdb
,
247 current
[0]["defaultNamingContext"][0]))):
248 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
249 "is not the same ..." % (paths
.samdb
,
250 str(current
[0]["defaultNamingContext"][0]),
251 paths
.smbconf
, basedn
)))
253 names
.domaindn
=current
[0]["defaultNamingContext"]
254 names
.rootdn
=current
[0]["rootDomainNamingContext"]
256 res3
= samdb
.search(expression
="(objectClass=site)",
257 base
="CN=Sites," + configdn
, scope
=ldb
.SCOPE_ONELEVEL
, attrs
=["cn"])
258 names
.sitename
= str(res3
[0]["cn"])
260 # dns hostname and server dn
261 res4
= samdb
.search(expression
="(CN=%s)" % names
.netbiosname
,
262 base
="OU=Domain Controllers,%s" % basedn
,
263 scope
=ldb
.SCOPE_ONELEVEL
, attrs
=["dNSHostName"])
264 names
.hostname
= str(res4
[0]["dNSHostName"]).replace("." + names
.dnsdomain
,"")
266 server_res
= samdb
.search(expression
="serverReference=%s" % res4
[0].dn
,
267 attrs
=[], base
=configdn
)
268 names
.serverdn
= server_res
[0].dn
270 # invocation id/objectguid
271 res5
= samdb
.search(expression
="(objectClass=*)",
272 base
="CN=NTDS Settings,%s" % str(names
.serverdn
), scope
=ldb
.SCOPE_BASE
,
273 attrs
=["invocationID", "objectGUID"])
274 names
.invocation
= str(ndr_unpack(misc
.GUID
, res5
[0]["invocationId"][0]))
275 names
.ntdsguid
= str(ndr_unpack(misc
.GUID
, res5
[0]["objectGUID"][0]))
278 res6
= samdb
.search(expression
="(objectClass=*)", base
=basedn
,
279 scope
=ldb
.SCOPE_BASE
, attrs
=["objectGUID",
280 "objectSid","msDS-Behavior-Version" ])
281 names
.domainguid
= str(ndr_unpack(misc
.GUID
, res6
[0]["objectGUID"][0]))
282 names
.domainsid
= ndr_unpack( security
.dom_sid
, res6
[0]["objectSid"][0])
283 if res6
[0].get("msDS-Behavior-Version") is None or \
284 int(res6
[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000
:
285 names
.domainlevel
= DS_DOMAIN_FUNCTION_2000
287 names
.domainlevel
= int(res6
[0]["msDS-Behavior-Version"][0])
290 res7
= samdb
.search(expression
="(displayName=Default Domain Policy)",
291 base
="CN=Policies,CN=System," + basedn
,
292 scope
=ldb
.SCOPE_ONELEVEL
, attrs
=["cn","displayName"])
293 names
.policyid
= str(res7
[0]["cn"]).replace("{","").replace("}","")
295 res8
= samdb
.search(expression
="(displayName=Default Domain Controllers"
297 base
="CN=Policies,CN=System," + basedn
,
298 scope
=ldb
.SCOPE_ONELEVEL
, attrs
=["cn","displayName"])
300 names
.policyid_dc
= str(res8
[0]["cn"]).replace("{","").replace("}","")
302 names
.policyid_dc
= None
303 res9
= idmapdb
.search(expression
="(cn=%s)" %
304 (security
.SID_BUILTIN_ADMINISTRATORS
),
307 names
.wheel_gid
= res9
[0]["xidNumber"]
309 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid")
312 def update_provision_usn(samdb
, low
, high
, id, replace
=False):
313 """Update the field provisionUSN in sam.ldb
315 This field is used to track range of USN modified by provision and
317 This value is used afterward by next provision to figure out if
318 the field have been modified since last provision.
320 :param samdb: An LDB object connect to sam.ldb
321 :param low: The lowest USN modified by this upgrade
322 :param high: The highest USN modified by this upgrade
323 :param id: The invocation id of the samba's dc
324 :param replace: A boolean indicating if the range should replace any
325 existing one or appended (default)
330 entry
= samdb
.search(expression
="(&(dn=@PROVISION)(%s=*))" %
331 LAST_PROVISION_USN_ATTRIBUTE
, base
="",
332 scope
=ldb
.SCOPE_SUBTREE
,
333 attrs
=[LAST_PROVISION_USN_ATTRIBUTE
, "dn"])
334 for e
in entry
[0][LAST_PROVISION_USN_ATTRIBUTE
]:
335 if not re
.search(';', e
):
336 e
= "%s;%s" % (e
, id)
339 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_REPLACE
, LAST_PROVISION_USN_ATTRIBUTE
)
344 entry
= samdb
.search(expression
="(&(dn=@PROVISION)(provisionnerID=*))",
345 base
="", scope
=ldb
.SCOPE_SUBTREE
,
346 attrs
=["provisionnerID"])
347 if len(entry
) == 0 or len(entry
[0]) == 0:
348 delta
["provisionnerID"] = ldb
.MessageElement(id, ldb
.FLAG_MOD_ADD
, "provisionnerID")
352 def set_provision_usn(samdb
, low
, high
, id):
353 """Set the field provisionUSN in sam.ldb
354 This field is used to track range of USN modified by provision and
356 This value is used afterward by next provision to figure out if
357 the field have been modified since last provision.
359 :param samdb: An LDB object connect to sam.ldb
360 :param low: The lowest USN modified by this upgrade
361 :param high: The highest USN modified by this upgrade
362 :param id: The invocationId of the provision"""
365 tab
.append("%s-%s;%s" % (low
, high
, id))
367 delta
= ldb
.Message()
368 delta
.dn
= ldb
.Dn(samdb
, "@PROVISION")
369 delta
[LAST_PROVISION_USN_ATTRIBUTE
] = ldb
.MessageElement(tab
,
370 ldb
.FLAG_MOD_ADD
, LAST_PROVISION_USN_ATTRIBUTE
)
374 def get_max_usn(samdb
,basedn
):
375 """ This function return the biggest USN present in the provision
377 :param samdb: A LDB object pointing to the sam.ldb
378 :param basedn: A string containing the base DN of the provision
380 :return: The biggest USN in the provision"""
382 res
= samdb
.search(expression
="objectClass=*",base
=basedn
,
383 scope
=ldb
.SCOPE_SUBTREE
,attrs
=["uSNChanged"],
384 controls
=["search_options:1:2",
385 "server_sort:1:1:uSNChanged",
386 "paged_results:1:1"])
387 return res
[0]["uSNChanged"]
390 def get_last_provision_usn(sam
):
391 """Get USNs ranges modified by a provision or an upgradeprovision
393 :param sam: An LDB object pointing to the sam.ldb
394 :return: a dictionnary which keys are invocation id and values are an array
395 of integer representing the different ranges
397 entry
= sam
.search(expression
="(&(dn=@PROVISION)(%s=*))" %
398 LAST_PROVISION_USN_ATTRIBUTE
,
399 base
="", scope
=ldb
.SCOPE_SUBTREE
,
400 attrs
=[LAST_PROVISION_USN_ATTRIBUTE
, "provisionnerID"])
405 if entry
[0].get("provisionnerID"):
406 for e
in entry
[0]["provisionnerID"]:
408 for r
in entry
[0][LAST_PROVISION_USN_ATTRIBUTE
]:
409 tab1
= str(r
).split(';')
414 if (len(myids
) > 0 and id not in myids
):
416 tab2
= p
.split(tab1
[0])
417 if range.get(id) == None:
419 range[id].append(tab2
[0])
420 range[id].append(tab2
[1])
426 class ProvisionResult(object):
435 def check_install(lp
, session_info
, credentials
):
436 """Check whether the current install seems ok.
438 :param lp: Loadparm context
439 :param session_info: Session information
440 :param credentials: Credentials
442 if lp
.get("realm") == "":
443 raise Exception("Realm empty")
444 samdb
= Ldb(lp
.samdb_url(), session_info
=session_info
,
445 credentials
=credentials
, lp
=lp
)
446 if len(samdb
.search("(cn=Administrator)")) != 1:
447 raise ProvisioningError("No administrator account found")
450 def findnss(nssfn
, names
):
451 """Find a user or group from a list of possibilities.
453 :param nssfn: NSS Function to try (should raise KeyError if not found)
454 :param names: Names to check.
455 :return: Value return by first names list.
462 raise KeyError("Unable to find user/group in %r" % names
)
465 findnss_uid
= lambda names
: findnss(pwd
.getpwnam
, names
)[2]
466 findnss_gid
= lambda names
: findnss(grp
.getgrnam
, names
)[2]
469 def setup_add_ldif(ldb
, ldif_path
, subst_vars
=None,controls
=["relax:0"]):
470 """Setup a ldb in the private dir.
472 :param ldb: LDB file to import data into
473 :param ldif_path: Path of the LDIF file to load
474 :param subst_vars: Optional variables to subsitute in LDIF.
475 :param nocontrols: Optional list of controls, can be None for no controls
477 assert isinstance(ldif_path
, str)
478 data
= read_and_sub_file(ldif_path
, subst_vars
)
479 ldb
.add_ldif(data
, controls
)
482 def setup_modify_ldif(ldb
, ldif_path
, subst_vars
=None,controls
=["relax:0"]):
483 """Modify a ldb in the private dir.
485 :param ldb: LDB object.
486 :param ldif_path: LDIF file path.
487 :param subst_vars: Optional dictionary with substitution variables.
489 data
= read_and_sub_file(ldif_path
, subst_vars
)
490 ldb
.modify_ldif(data
, controls
)
493 def setup_ldb(ldb
, ldif_path
, subst_vars
):
494 """Import a LDIF a file into a LDB handle, optionally substituting
497 :note: Either all LDIF data will be added or none (using transactions).
499 :param ldb: LDB file to import into.
500 :param ldif_path: Path to the LDIF file.
501 :param subst_vars: Dictionary with substitution variables.
503 assert ldb
is not None
504 ldb
.transaction_start()
506 setup_add_ldif(ldb
, ldif_path
, subst_vars
)
508 ldb
.transaction_cancel()
511 ldb
.transaction_commit()
514 def provision_paths_from_lp(lp
, dnsdomain
):
515 """Set the default paths for provisioning.
517 :param lp: Loadparm context.
518 :param dnsdomain: DNS Domain name
520 paths
= ProvisionPaths()
521 paths
.private_dir
= lp
.get("private dir")
523 # This is stored without path prefix for the "privateKeytab" attribute in
524 # "secrets_dns.ldif".
525 paths
.dns_keytab
= "dns.keytab"
526 paths
.keytab
= "secrets.keytab"
528 paths
.shareconf
= os
.path
.join(paths
.private_dir
, "share.ldb")
529 paths
.samdb
= os
.path
.join(paths
.private_dir
, "sam.ldb")
530 paths
.idmapdb
= os
.path
.join(paths
.private_dir
, "idmap.ldb")
531 paths
.secrets
= os
.path
.join(paths
.private_dir
, "secrets.ldb")
532 paths
.privilege
= os
.path
.join(paths
.private_dir
, "privilege.ldb")
533 paths
.dns
= os
.path
.join(paths
.private_dir
, "dns", dnsdomain
+ ".zone")
534 paths
.dns_update_list
= os
.path
.join(paths
.private_dir
, "dns_update_list")
535 paths
.spn_update_list
= os
.path
.join(paths
.private_dir
, "spn_update_list")
536 paths
.namedconf
= os
.path
.join(paths
.private_dir
, "named.conf")
537 paths
.namedconf_update
= os
.path
.join(paths
.private_dir
, "named.conf.update")
538 paths
.namedtxt
= os
.path
.join(paths
.private_dir
, "named.txt")
539 paths
.krb5conf
= os
.path
.join(paths
.private_dir
, "krb5.conf")
540 paths
.winsdb
= os
.path
.join(paths
.private_dir
, "wins.ldb")
541 paths
.s4_ldapi_path
= os
.path
.join(paths
.private_dir
, "ldapi")
542 paths
.phpldapadminconfig
= os
.path
.join(paths
.private_dir
,
543 "phpldapadmin-config.php")
544 paths
.hklm
= "hklm.ldb"
545 paths
.hkcr
= "hkcr.ldb"
546 paths
.hkcu
= "hkcu.ldb"
547 paths
.hku
= "hku.ldb"
548 paths
.hkpd
= "hkpd.ldb"
549 paths
.hkpt
= "hkpt.ldb"
550 paths
.sysvol
= lp
.get("path", "sysvol")
551 paths
.netlogon
= lp
.get("path", "netlogon")
552 paths
.smbconf
= lp
.configfile
556 def guess_names(lp
=None, hostname
=None, domain
=None, dnsdomain
=None,
557 serverrole
=None, rootdn
=None, domaindn
=None, configdn
=None,
558 schemadn
=None, serverdn
=None, sitename
=None):
559 """Guess configuration settings to use."""
562 hostname
= socket
.gethostname().split(".")[0]
564 netbiosname
= lp
.get("netbios name")
565 if netbiosname
is None:
566 netbiosname
= hostname
567 # remove forbidden chars
569 for x
in netbiosname
:
570 if x
.isalnum() or x
in VALID_NETBIOS_CHARS
:
571 newnbname
= "%s%c" % (newnbname
, x
)
572 # force the length to be <16
573 netbiosname
= newnbname
[0:15]
574 assert netbiosname
is not None
575 netbiosname
= netbiosname
.upper()
576 if not valid_netbios_name(netbiosname
):
577 raise InvalidNetbiosName(netbiosname
)
579 if dnsdomain
is None:
580 dnsdomain
= lp
.get("realm")
581 if dnsdomain
is None or dnsdomain
== "":
582 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp
.configfile
)
584 dnsdomain
= dnsdomain
.lower()
586 if serverrole
is None:
587 serverrole
= lp
.get("server role")
588 if serverrole
is None:
589 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp
.configfile
)
591 serverrole
= serverrole
.lower()
593 realm
= dnsdomain
.upper()
595 if lp
.get("realm") == "":
596 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp
.configfile
)
598 if lp
.get("realm").upper() != realm
:
599 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
))
601 if lp
.get("server role").lower() != serverrole
:
602 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"), serverrole
, lp
.configfile
))
604 if serverrole
== "domain controller":
606 # This will, for better or worse, default to 'WORKGROUP'
607 domain
= lp
.get("workgroup")
608 domain
= domain
.upper()
610 if lp
.get("workgroup").upper() != domain
:
611 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
))
614 domaindn
= "DC=" + dnsdomain
.replace(".", ",DC=")
616 if domain
== netbiosname
:
617 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain
, netbiosname
))
621 domaindn
= "DC=" + netbiosname
623 if not valid_netbios_name(domain
):
624 raise InvalidNetbiosName(domain
)
626 if hostname
.upper() == realm
:
627 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm
, hostname
))
628 if netbiosname
.upper() == realm
:
629 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm
, netbiosname
))
631 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm
, domain
))
637 configdn
= "CN=Configuration," + rootdn
639 schemadn
= "CN=Schema," + configdn
644 names
= ProvisionNames()
645 names
.rootdn
= rootdn
646 names
.domaindn
= domaindn
647 names
.configdn
= configdn
648 names
.schemadn
= schemadn
649 names
.ldapmanagerdn
= "CN=Manager," + rootdn
650 names
.dnsdomain
= dnsdomain
651 names
.domain
= domain
653 names
.netbiosname
= netbiosname
654 names
.hostname
= hostname
655 names
.sitename
= sitename
656 names
.serverdn
= "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
657 netbiosname
, sitename
, configdn
)
662 def make_smbconf(smbconf
, hostname
, domain
, realm
, serverrole
,
663 targetdir
, sid_generator
="internal", eadb
=False, lp
=None):
664 """Create a new smb.conf file based on a couple of basic settings.
666 assert smbconf
is not None
668 hostname
= socket
.gethostname().split(".")[0]
669 netbiosname
= hostname
.upper()
670 # remove forbidden chars
672 for x
in netbiosname
:
673 if x
.isalnum() or x
in VALID_NETBIOS_CHARS
:
674 newnbname
= "%s%c" % (newnbname
, x
)
675 #force the length to be <16
676 netbiosname
= newnbname
[0:15]
678 netbiosname
= hostname
.upper()
680 if serverrole
is None:
681 serverrole
= "standalone"
683 assert serverrole
in ("domain controller", "member server", "standalone")
684 if serverrole
== "domain controller":
686 elif serverrole
== "member server":
687 smbconfsuffix
= "member"
688 elif serverrole
== "standalone":
689 smbconfsuffix
= "standalone"
691 if sid_generator
is None:
692 sid_generator
= "internal"
694 assert domain
is not None
695 domain
= domain
.upper()
697 assert realm
is not None
698 realm
= realm
.upper()
701 lp
= samba
.param
.LoadParm()
702 #Load non-existant file
703 if os
.path
.exists(smbconf
):
705 if eadb
and not lp
.get("posix:eadb"):
706 if targetdir
is not None:
707 privdir
= os
.path
.join(targetdir
, "private")
709 privdir
= lp
.get("private dir")
710 lp
.set("posix:eadb", os
.path
.abspath(os
.path
.join(privdir
, "eadb.tdb")))
712 if targetdir
is not None:
713 privatedir_line
= "private dir = " + os
.path
.abspath(os
.path
.join(targetdir
, "private"))
714 lockdir_line
= "lock dir = " + os
.path
.abspath(targetdir
)
716 lp
.set("lock dir", os
.path
.abspath(targetdir
))
721 sysvol
= os
.path
.join(lp
.get("lock dir"), "sysvol")
722 netlogon
= os
.path
.join(sysvol
, realm
.lower(), "scripts")
724 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix
),
726 "NETBIOS_NAME": netbiosname
,
729 "SERVERROLE": serverrole
,
730 "NETLOGONPATH": netlogon
,
731 "SYSVOLPATH": sysvol
,
732 "PRIVATEDIR_LINE": privatedir_line
,
733 "LOCKDIR_LINE": lockdir_line
736 # reload the smb.conf
739 # and dump it without any values that are the default
740 # this ensures that any smb.conf parameters that were set
741 # on the provision/join command line are set in the resulting smb.conf
742 f
= open(smbconf
, mode
='w')
748 def setup_name_mappings(samdb
, idmap
, sid
, domaindn
, root_uid
, nobody_uid
,
749 users_gid
, wheel_gid
):
750 """setup reasonable name mappings for sam names to unix names.
752 :param samdb: SamDB object.
753 :param idmap: IDmap db object.
754 :param sid: The domain sid.
755 :param domaindn: The domain DN.
756 :param root_uid: uid of the UNIX root user.
757 :param nobody_uid: uid of the UNIX nobody user.
758 :param users_gid: gid of the UNIX users group.
759 :param wheel_gid: gid of the UNIX wheel group.
761 idmap
.setup_name_mapping("S-1-5-7", idmap
.TYPE_UID
, nobody_uid
)
762 idmap
.setup_name_mapping("S-1-5-32-544", idmap
.TYPE_GID
, wheel_gid
)
764 idmap
.setup_name_mapping(sid
+ "-500", idmap
.TYPE_UID
, root_uid
)
765 idmap
.setup_name_mapping(sid
+ "-513", idmap
.TYPE_GID
, users_gid
)
768 def setup_samdb_partitions(samdb_path
, logger
, lp
, session_info
,
769 provision_backend
, names
, schema
, serverrole
,
771 """Setup the partitions for the SAM database.
773 Alternatively, provision() may call this, and then populate the database.
775 :note: This will wipe the Sam Database!
777 :note: This function always removes the local SAM LDB file. The erase
778 parameter controls whether to erase the existing data, which
779 may not be stored locally but in LDAP.
782 assert session_info
is not None
784 # We use options=["modules:"] to stop the modules loading - we
785 # just want to wipe and re-initialise the database, not start it up
788 os
.unlink(samdb_path
)
792 samdb
= Ldb(url
=samdb_path
, session_info
=session_info
,
793 lp
=lp
, options
=["modules:"])
795 ldap_backend_line
= "# No LDAP backend"
796 if provision_backend
.type is not "ldb":
797 ldap_backend_line
= "ldapBackend: %s" % provision_backend
.ldap_uri
799 samdb
.transaction_start()
801 logger
.info("Setting up sam.ldb partitions and settings")
802 setup_add_ldif(samdb
, setup_path("provision_partitions.ldif"), {
803 "SCHEMADN": ldb
.Dn(schema
.ldb
, names
.schemadn
).get_casefold(),
804 "CONFIGDN": ldb
.Dn(schema
.ldb
, names
.configdn
).get_casefold(),
805 "DOMAINDN": ldb
.Dn(schema
.ldb
, names
.domaindn
).get_casefold(),
806 "LDAP_BACKEND_LINE": ldap_backend_line
,
810 setup_add_ldif(samdb
, setup_path("provision_init.ldif"), {
811 "BACKEND_TYPE": provision_backend
.type,
812 "SERVER_ROLE": serverrole
815 logger
.info("Setting up sam.ldb rootDSE")
816 setup_samdb_rootdse(samdb
, names
)
818 samdb
.transaction_cancel()
821 samdb
.transaction_commit()
824 def secretsdb_self_join(secretsdb
, domain
,
825 netbiosname
, machinepass
, domainsid
=None,
826 realm
=None, dnsdomain
=None,
828 key_version_number
=1,
829 secure_channel_type
=SEC_CHAN_WKSTA
):
830 """Add domain join-specific bits to a secrets database.
832 :param secretsdb: Ldb Handle to the secrets database
833 :param machinepass: Machine password
835 attrs
= ["whenChanged",
842 if realm
is not None:
843 if dnsdomain
is None:
844 dnsdomain
= realm
.lower()
845 dnsname
= '%s.%s' % (netbiosname
.lower(), dnsdomain
.lower())
848 shortname
= netbiosname
.lower()
850 # We don't need to set msg["flatname"] here, because rdn_name will handle
851 # it, and it causes problems for modifies anyway
852 msg
= ldb
.Message(ldb
.Dn(secretsdb
, "flatname=%s,cn=Primary Domains" % domain
))
853 msg
["secureChannelType"] = [str(secure_channel_type
)]
854 msg
["objectClass"] = ["top", "primaryDomain"]
855 if dnsname
is not None:
856 msg
["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
857 msg
["realm"] = [realm
]
858 msg
["saltPrincipal"] = ["host/%s@%s" % (dnsname
, realm
.upper())]
859 msg
["msDS-KeyVersionNumber"] = [str(key_version_number
)]
860 msg
["privateKeytab"] = ["secrets.keytab"]
862 msg
["secret"] = [machinepass
]
863 msg
["samAccountName"] = ["%s$" % netbiosname
]
864 msg
["secureChannelType"] = [str(secure_channel_type
)]
865 if domainsid
is not None:
866 msg
["objectSid"] = [ndr_pack(domainsid
)]
868 # This complex expression tries to ensure that we don't have more
869 # than one record for this SID, realm or netbios domain at a time,
870 # but we don't delete the old record that we are about to modify,
871 # because that would delete the keytab and previous password.
872 res
= secretsdb
.search(base
="cn=Primary Domains", attrs
=attrs
,
873 expression
=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(dn=%s)))" % (domain
, realm
, str(domainsid
), str(msg
.dn
))),
874 scope
=ldb
.SCOPE_ONELEVEL
)
877 secretsdb
.delete(del_msg
.dn
)
879 res
= secretsdb
.search(base
=msg
.dn
, attrs
=attrs
, scope
=ldb
.SCOPE_BASE
)
882 msg
["priorSecret"] = [res
[0]["secret"][0]]
883 msg
["priorWhenChanged"] = [res
[0]["whenChanged"][0]]
886 msg
["privateKeytab"] = [res
[0]["privateKeytab"][0]]
891 msg
["krb5Keytab"] = [res
[0]["krb5Keytab"][0]]
897 msg
[el
].set_flags(ldb
.FLAG_MOD_REPLACE
)
898 secretsdb
.modify(msg
)
899 secretsdb
.rename(res
[0].dn
, msg
.dn
)
901 spn
= [ 'HOST/%s' % shortname
]
902 if secure_channel_type
== SEC_CHAN_BDC
and dnsname
is not None:
903 # we are a domain controller then we add servicePrincipalName
904 # entries for the keytab code to update.
905 spn
.extend([ 'HOST/%s' % dnsname
])
906 msg
["servicePrincipalName"] = spn
911 def secretsdb_setup_dns(secretsdb
, names
, private_dir
, realm
,
912 dnsdomain
, dns_keytab_path
, dnspass
):
913 """Add DNS specific bits to a secrets database.
915 :param secretsdb: Ldb Handle to the secrets database
916 :param machinepass: Machine password
919 os
.unlink(os
.path
.join(private_dir
, dns_keytab_path
))
923 setup_ldb(secretsdb
, setup_path("secrets_dns.ldif"), {
925 "DNSDOMAIN": dnsdomain
,
926 "DNS_KEYTAB": dns_keytab_path
,
927 "DNSPASS_B64": b64encode(dnspass
),
928 "HOSTNAME": names
.hostname
,
929 "DNSNAME" : '%s.%s' % (
930 names
.netbiosname
.lower(), names
.dnsdomain
.lower())
934 def setup_secretsdb(paths
, session_info
, backend_credentials
, lp
):
935 """Setup the secrets database.
937 :note: This function does not handle exceptions and transaction on purpose,
938 it's up to the caller to do this job.
940 :param path: Path to the secrets database.
941 :param session_info: Session info.
942 :param credentials: Credentials
943 :param lp: Loadparm context
944 :return: LDB handle for the created secrets database
946 if os
.path
.exists(paths
.secrets
):
947 os
.unlink(paths
.secrets
)
949 keytab_path
= os
.path
.join(paths
.private_dir
, paths
.keytab
)
950 if os
.path
.exists(keytab_path
):
951 os
.unlink(keytab_path
)
953 dns_keytab_path
= os
.path
.join(paths
.private_dir
, paths
.dns_keytab
)
954 if os
.path
.exists(dns_keytab_path
):
955 os
.unlink(dns_keytab_path
)
959 secrets_ldb
= Ldb(path
, session_info
=session_info
,
962 secrets_ldb
.load_ldif_file_add(setup_path("secrets_init.ldif"))
963 secrets_ldb
= Ldb(path
, session_info
=session_info
,
965 secrets_ldb
.transaction_start()
967 secrets_ldb
.load_ldif_file_add(setup_path("secrets.ldif"))
969 if (backend_credentials
is not None and
970 backend_credentials
.authentication_requested()):
971 if backend_credentials
.get_bind_dn() is not None:
972 setup_add_ldif(secrets_ldb
,
973 setup_path("secrets_simple_ldap.ldif"), {
974 "LDAPMANAGERDN": backend_credentials
.get_bind_dn(),
975 "LDAPMANAGERPASS_B64": b64encode(backend_credentials
.get_password())
978 setup_add_ldif(secrets_ldb
,
979 setup_path("secrets_sasl_ldap.ldif"), {
980 "LDAPADMINUSER": backend_credentials
.get_username(),
981 "LDAPADMINREALM": backend_credentials
.get_realm(),
982 "LDAPADMINPASS_B64": b64encode(backend_credentials
.get_password())
987 secrets_ldb
.transaction_cancel()
991 def setup_privileges(path
, session_info
, lp
):
992 """Setup the privileges database.
994 :param path: Path to the privileges database.
995 :param session_info: Session info.
996 :param credentials: Credentials
997 :param lp: Loadparm context
998 :return: LDB handle for the created secrets database
1000 if os
.path
.exists(path
):
1002 privilege_ldb
= Ldb(path
, session_info
=session_info
, lp
=lp
)
1003 privilege_ldb
.erase()
1004 privilege_ldb
.load_ldif_file_add(setup_path("provision_privilege.ldif"))
1007 def setup_registry(path
, session_info
, lp
):
1008 """Setup the registry.
1010 :param path: Path to the registry database
1011 :param session_info: Session information
1012 :param credentials: Credentials
1013 :param lp: Loadparm context
1015 reg
= samba
.registry
.Registry()
1016 hive
= samba
.registry
.open_ldb(path
, session_info
=session_info
, lp_ctx
=lp
)
1017 reg
.mount_hive(hive
, samba
.registry
.HKEY_LOCAL_MACHINE
)
1018 provision_reg
= setup_path("provision.reg")
1019 assert os
.path
.exists(provision_reg
)
1020 reg
.diff_apply(provision_reg
)
1023 def setup_idmapdb(path
, session_info
, lp
):
1024 """Setup the idmap database.
1026 :param path: path to the idmap database
1027 :param session_info: Session information
1028 :param credentials: Credentials
1029 :param lp: Loadparm context
1031 if os
.path
.exists(path
):
1034 idmap_ldb
= IDmapDB(path
, session_info
=session_info
, lp
=lp
)
1036 idmap_ldb
.load_ldif_file_add(setup_path("idmap_init.ldif"))
1040 def setup_samdb_rootdse(samdb
, names
):
1041 """Setup the SamDB rootdse.
1043 :param samdb: Sam Database handle
1045 setup_add_ldif(samdb
, setup_path("provision_rootdse_add.ldif"), {
1046 "SCHEMADN": names
.schemadn
,
1047 "DOMAINDN": names
.domaindn
,
1048 "ROOTDN": names
.rootdn
,
1049 "CONFIGDN": names
.configdn
,
1050 "SERVERDN": names
.serverdn
,
1054 def setup_self_join(samdb
, names
, machinepass
, dnspass
,
1055 domainsid
, next_rid
, invocationid
,
1056 policyguid
, policyguid_dc
, domainControllerFunctionality
,
1058 """Join a host to its own domain."""
1059 assert isinstance(invocationid
, str)
1060 if ntdsguid
is not None:
1061 ntdsguid_line
= "objectGUID: %s\n"%ntdsguid
1064 setup_add_ldif(samdb
, setup_path("provision_self_join.ldif"), {
1065 "CONFIGDN": names
.configdn
,
1066 "SCHEMADN": names
.schemadn
,
1067 "DOMAINDN": names
.domaindn
,
1068 "SERVERDN": names
.serverdn
,
1069 "INVOCATIONID": invocationid
,
1070 "NETBIOSNAME": names
.netbiosname
,
1071 "DNSNAME": "%s.%s" % (names
.hostname
, names
.dnsdomain
),
1072 "MACHINEPASS_B64": b64encode(machinepass
.encode('utf-16-le')),
1073 "DOMAINSID": str(domainsid
),
1074 "DCRID": str(next_rid
),
1075 "SAMBA_VERSION_STRING": version
,
1076 "NTDSGUID": ntdsguid_line
,
1077 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1078 domainControllerFunctionality
)})
1080 setup_add_ldif(samdb
, setup_path("provision_group_policy.ldif"), {
1081 "POLICYGUID": policyguid
,
1082 "POLICYGUID_DC": policyguid_dc
,
1083 "DNSDOMAIN": names
.dnsdomain
,
1084 "DOMAINDN": names
.domaindn
})
1086 # add the NTDSGUID based SPNs
1087 ntds_dn
= "CN=NTDS Settings,%s" % names
.serverdn
1088 names
.ntdsguid
= samdb
.searchone(basedn
=ntds_dn
, attribute
="objectGUID",
1089 expression
="", scope
=ldb
.SCOPE_BASE
)
1090 assert isinstance(names
.ntdsguid
, str)
1092 # Setup fSMORoleOwner entries to point at the newly created DC entry
1093 setup_modify_ldif(samdb
, setup_path("provision_self_join_modify.ldif"), {
1094 "DOMAINDN": names
.domaindn
,
1095 "CONFIGDN": names
.configdn
,
1096 "SCHEMADN": names
.schemadn
,
1097 "DEFAULTSITE": names
.sitename
,
1098 "SERVERDN": names
.serverdn
,
1099 "NETBIOSNAME": names
.netbiosname
,
1100 "RIDALLOCATIONSTART": str(next_rid
+ 100),
1101 "RIDALLOCATIONEND": str(next_rid
+ 100 + 499),
1104 # This is partially Samba4 specific and should be replaced by the correct
1105 # DNS AD-style setup
1106 setup_add_ldif(samdb
, setup_path("provision_dns_add.ldif"), {
1107 "DNSDOMAIN": names
.dnsdomain
,
1108 "DOMAINDN": names
.domaindn
,
1109 "DNSPASS_B64": b64encode(dnspass
.encode('utf-16-le')),
1110 "HOSTNAME" : names
.hostname
,
1111 "DNSNAME" : '%s.%s' % (
1112 names
.netbiosname
.lower(), names
.dnsdomain
.lower())
1116 def getpolicypath(sysvolpath
, dnsdomain
, guid
):
1117 """Return the physical path of policy given its guid.
1119 :param sysvolpath: Path to the sysvol folder
1120 :param dnsdomain: DNS name of the AD domain
1121 :param guid: The GUID of the policy
1122 :return: A string with the complete path to the policy folder
1126 guid
= "{%s}" % guid
1127 policy_path
= os
.path
.join(sysvolpath
, dnsdomain
, "Policies", guid
)
1131 def create_gpo_struct(policy_path
):
1132 if not os
.path
.exists(policy_path
):
1133 os
.makedirs(policy_path
, 0775)
1134 open(os
.path
.join(policy_path
, "GPT.INI"), 'w').write(
1135 "[General]\r\nVersion=0")
1136 p
= os
.path
.join(policy_path
, "MACHINE")
1137 if not os
.path
.exists(p
):
1138 os
.makedirs(p
, 0775)
1139 p
= os
.path
.join(policy_path
, "USER")
1140 if not os
.path
.exists(p
):
1141 os
.makedirs(p
, 0775)
1144 def create_default_gpo(sysvolpath
, dnsdomain
, policyguid
, policyguid_dc
):
1145 """Create the default GPO for a domain
1147 :param sysvolpath: Physical path for the sysvol folder
1148 :param dnsdomain: DNS domain name of the AD domain
1149 :param policyguid: GUID of the default domain policy
1150 :param policyguid_dc: GUID of the default domain controler policy
1152 policy_path
= getpolicypath(sysvolpath
,dnsdomain
,policyguid
)
1153 create_gpo_struct(policy_path
)
1155 policy_path
= getpolicypath(sysvolpath
,dnsdomain
,policyguid_dc
)
1156 create_gpo_struct(policy_path
)
1159 def setup_samdb(path
, session_info
, provision_backend
, lp
, names
,
1160 logger
, domainsid
, domainguid
, policyguid
, policyguid_dc
, fill
,
1161 adminpass
, krbtgtpass
, machinepass
, invocationid
, dnspass
, ntdsguid
,
1162 serverrole
, am_rodc
=False, dom_for_fun_level
=None, schema
=None,
1164 """Setup a complete SAM Database.
1166 :note: This will wipe the main SAM database file!
1169 # Provision does not make much sense values larger than 1000000000
1170 # as the upper range of the rIDAvailablePool is 1073741823 and
1171 # we don't want to create a domain that cannot allocate rids.
1172 if next_rid
< 1000 or next_rid
> 1000000000:
1173 error
= "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid
)
1174 error
+= "the valid range is %u-%u. The default is %u." % (
1175 1000, 1000000000, 1000)
1176 raise ProvisioningError(error
)
1178 # ATTENTION: Do NOT change these default values without discussion with the
1179 # team and/or release manager. They have a big impact on the whole program!
1180 domainControllerFunctionality
= DS_DOMAIN_FUNCTION_2008_R2
1182 if dom_for_fun_level
is None:
1183 dom_for_fun_level
= DS_DOMAIN_FUNCTION_2003
1185 if dom_for_fun_level
> domainControllerFunctionality
:
1186 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!")
1188 domainFunctionality
= dom_for_fun_level
1189 forestFunctionality
= dom_for_fun_level
1191 # Also wipes the database
1192 setup_samdb_partitions(path
, logger
=logger
, lp
=lp
,
1193 provision_backend
=provision_backend
, session_info
=session_info
,
1194 names
=names
, serverrole
=serverrole
, schema
=schema
)
1197 schema
= Schema(domainsid
, schemadn
=names
.schemadn
)
1199 # Load the database, but don's load the global schema and don't connect
1201 samdb
= SamDB(session_info
=session_info
, url
=None, auto_connect
=False,
1202 credentials
=provision_backend
.credentials
, lp
=lp
,
1203 global_schema
=False, am_rodc
=am_rodc
)
1205 logger
.info("Pre-loading the Samba 4 and AD schema")
1207 # Load the schema from the one we computed earlier
1208 samdb
.set_schema(schema
)
1210 # Set the NTDS settings DN manually - in order to have it already around
1211 # before the provisioned tree exists and we connect
1212 samdb
.set_ntds_settings_dn("CN=NTDS Settings,%s" % names
.serverdn
)
1214 # And now we can connect to the DB - the schema won't be loaded from the
1218 if fill
== FILL_DRS
:
1221 samdb
.transaction_start()
1223 # Set the domain functionality levels onto the database.
1224 # Various module (the password_hash module in particular) need
1225 # to know what level of AD we are emulating.
1227 # These will be fixed into the database via the database
1228 # modifictions below, but we need them set from the start.
1229 samdb
.set_opaque_integer("domainFunctionality", domainFunctionality
)
1230 samdb
.set_opaque_integer("forestFunctionality", forestFunctionality
)
1231 samdb
.set_opaque_integer("domainControllerFunctionality",
1232 domainControllerFunctionality
)
1234 samdb
.set_domain_sid(str(domainsid
))
1235 samdb
.set_invocation_id(invocationid
)
1237 logger
.info("Adding DomainDN: %s" % names
.domaindn
)
1239 # impersonate domain admin
1240 admin_session_info
= admin_session(lp
, str(domainsid
))
1241 samdb
.set_session_info(admin_session_info
)
1242 if domainguid
is not None:
1243 domainguid_line
= "objectGUID: %s\n-" % domainguid
1245 domainguid_line
= ""
1247 descr
= b64encode(get_domain_descriptor(domainsid
))
1248 setup_add_ldif(samdb
, setup_path("provision_basedn.ldif"), {
1249 "DOMAINDN": names
.domaindn
,
1250 "DOMAINSID": str(domainsid
),
1251 "DESCRIPTOR": descr
,
1252 "DOMAINGUID": domainguid_line
1255 setup_modify_ldif(samdb
, setup_path("provision_basedn_modify.ldif"), {
1256 "DOMAINDN": names
.domaindn
,
1257 "CREATTIME": str(int(time
.time() * 1e7
)), # seconds -> ticks
1258 "NEXTRID": str(next_rid
),
1259 "DEFAULTSITE": names
.sitename
,
1260 "CONFIGDN": names
.configdn
,
1261 "POLICYGUID": policyguid
,
1262 "DOMAIN_FUNCTIONALITY": str(domainFunctionality
),
1263 "SAMBA_VERSION_STRING": version
1266 logger
.info("Adding configuration container")
1267 descr
= b64encode(get_config_descriptor(domainsid
))
1268 setup_add_ldif(samdb
, setup_path("provision_configuration_basedn.ldif"), {
1269 "CONFIGDN": names
.configdn
,
1270 "DESCRIPTOR": descr
,
1273 # Now register this container in the root of the forest
1274 msg
= ldb
.Message(ldb
.Dn(samdb
, names
.domaindn
))
1275 msg
["subRefs"] = ldb
.MessageElement(names
.configdn
, ldb
.FLAG_MOD_ADD
,
1278 # The LDIF here was created when the Schema object was constructed
1279 logger
.info("Setting up sam.ldb schema")
1280 samdb
.add_ldif(schema
.schema_dn_add
, controls
=["relax:0"])
1281 samdb
.modify_ldif(schema
.schema_dn_modify
)
1282 samdb
.write_prefixes_from_schema()
1283 samdb
.add_ldif(schema
.schema_data
, controls
=["relax:0"])
1284 setup_add_ldif(samdb
, setup_path("aggregate_schema.ldif"),
1285 {"SCHEMADN": names
.schemadn
})
1287 logger
.info("Reopening sam.ldb with new schema")
1289 samdb
.transaction_cancel()
1292 samdb
.transaction_commit()
1294 samdb
= SamDB(session_info
=admin_session_info
, auto_connect
=False,
1295 credentials
=provision_backend
.credentials
, lp
=lp
,
1296 global_schema
=False, am_rodc
=am_rodc
)
1298 # Set the NTDS settings DN manually - in order to have it already around
1299 # before the provisioned tree exists and we connect
1300 samdb
.set_ntds_settings_dn("CN=NTDS Settings,%s" % names
.serverdn
)
1303 samdb
.transaction_start()
1305 samdb
.invocation_id
= invocationid
1307 logger
.info("Setting up sam.ldb configuration data")
1308 setup_add_ldif(samdb
, setup_path("provision_configuration.ldif"), {
1309 "CONFIGDN": names
.configdn
,
1310 "NETBIOSNAME": names
.netbiosname
,
1311 "DEFAULTSITE": names
.sitename
,
1312 "DNSDOMAIN": names
.dnsdomain
,
1313 "DOMAIN": names
.domain
,
1314 "SCHEMADN": names
.schemadn
,
1315 "DOMAINDN": names
.domaindn
,
1316 "SERVERDN": names
.serverdn
,
1317 "FOREST_FUNCTIONALITY": str(forestFunctionality
),
1318 "DOMAIN_FUNCTIONALITY": str(domainFunctionality
),
1321 logger
.info("Setting up display specifiers")
1322 display_specifiers_ldif
= read_ms_ldif(
1323 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1324 display_specifiers_ldif
= substitute_var(display_specifiers_ldif
,
1325 {"CONFIGDN": names
.configdn
})
1326 check_all_substituted(display_specifiers_ldif
)
1327 samdb
.add_ldif(display_specifiers_ldif
)
1329 logger
.info("Adding users container")
1330 setup_add_ldif(samdb
, setup_path("provision_users_add.ldif"), {
1331 "DOMAINDN": names
.domaindn
})
1332 logger
.info("Modifying users container")
1333 setup_modify_ldif(samdb
, setup_path("provision_users_modify.ldif"), {
1334 "DOMAINDN": names
.domaindn
})
1335 logger
.info("Adding computers container")
1336 setup_add_ldif(samdb
, setup_path("provision_computers_add.ldif"), {
1337 "DOMAINDN": names
.domaindn
})
1338 logger
.info("Modifying computers container")
1339 setup_modify_ldif(samdb
,
1340 setup_path("provision_computers_modify.ldif"), {
1341 "DOMAINDN": names
.domaindn
})
1342 logger
.info("Setting up sam.ldb data")
1343 setup_add_ldif(samdb
, setup_path("provision.ldif"), {
1344 "CREATTIME": str(int(time
.time() * 1e7
)), # seconds -> ticks
1345 "DOMAINDN": names
.domaindn
,
1346 "NETBIOSNAME": names
.netbiosname
,
1347 "DEFAULTSITE": names
.sitename
,
1348 "CONFIGDN": names
.configdn
,
1349 "SERVERDN": names
.serverdn
,
1350 "RIDAVAILABLESTART": str(next_rid
+ 600),
1351 "POLICYGUID_DC": policyguid_dc
1354 setup_modify_ldif(samdb
,
1355 setup_path("provision_basedn_references.ldif"), {
1356 "DOMAINDN": names
.domaindn
})
1358 setup_modify_ldif(samdb
,
1359 setup_path("provision_configuration_references.ldif"), {
1360 "CONFIGDN": names
.configdn
,
1361 "SCHEMADN": names
.schemadn
})
1362 if fill
== FILL_FULL
:
1363 logger
.info("Setting up sam.ldb users and groups")
1364 setup_add_ldif(samdb
, setup_path("provision_users.ldif"), {
1365 "DOMAINDN": names
.domaindn
,
1366 "DOMAINSID": str(domainsid
),
1367 "CONFIGDN": names
.configdn
,
1368 "ADMINPASS_B64": b64encode(adminpass
.encode('utf-16-le')),
1369 "KRBTGTPASS_B64": b64encode(krbtgtpass
.encode('utf-16-le'))
1372 logger
.info("Setting up self join")
1373 setup_self_join(samdb
, names
=names
, invocationid
=invocationid
,
1375 machinepass
=machinepass
,
1376 domainsid
=domainsid
,
1378 policyguid
=policyguid
,
1379 policyguid_dc
=policyguid_dc
,
1380 domainControllerFunctionality
=domainControllerFunctionality
,
1383 ntds_dn
= "CN=NTDS Settings,%s" % names
.serverdn
1384 names
.ntdsguid
= samdb
.searchone(basedn
=ntds_dn
,
1385 attribute
="objectGUID", expression
="", scope
=ldb
.SCOPE_BASE
)
1386 assert isinstance(names
.ntdsguid
, str)
1388 samdb
.transaction_cancel()
1391 samdb
.transaction_commit()
1396 FILL_NT4SYNC
= "NT4SYNC"
1398 SYSVOL_ACL
= "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1399 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)"
1402 def set_dir_acl(path
, acl
, lp
, domsid
):
1403 setntacl(lp
, path
, acl
, domsid
)
1404 for root
, dirs
, files
in os
.walk(path
, topdown
=False):
1406 setntacl(lp
, os
.path
.join(root
, name
), acl
, domsid
)
1408 setntacl(lp
, os
.path
.join(root
, name
), acl
, domsid
)
1411 def set_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
):
1412 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1415 :param sysvol: Physical path for the sysvol folder
1416 :param dnsdomain: The DNS name of the domain
1417 :param domainsid: The SID of the domain
1418 :param domaindn: The DN of the domain (ie. DC=...)
1419 :param samdb: An LDB object on the SAM db
1420 :param lp: an LP object
1423 # Set ACL for GPO root folder
1424 root_policy_path
= os
.path
.join(sysvol
, dnsdomain
, "Policies")
1425 setntacl(lp
, root_policy_path
, POLICIES_ACL
, str(domainsid
))
1427 res
= samdb
.search(base
="CN=Policies,CN=System,%s"%(domaindn),
1428 attrs
=["cn", "nTSecurityDescriptor"],
1429 expression
="", scope
=ldb
.SCOPE_ONELEVEL
)
1432 acl
= ndr_unpack(security
.descriptor
,
1433 str(policy
["nTSecurityDescriptor"])).as_sddl()
1434 policy_path
= getpolicypath(sysvol
, dnsdomain
, str(policy
["cn"]))
1435 set_dir_acl(policy_path
, dsacl2fsacl(acl
, str(domainsid
)), lp
,
1439 def setsysvolacl(samdb
, netlogon
, sysvol
, gid
, domainsid
, dnsdomain
, domaindn
,
1441 """Set the ACL for the sysvol share and the subfolders
1443 :param samdb: An LDB object on the SAM db
1444 :param netlogon: Physical path for the netlogon folder
1445 :param sysvol: Physical path for the sysvol folder
1446 :param gid: The GID of the "Domain adminstrators" group
1447 :param domainsid: The SID of the domain
1448 :param dnsdomain: The DNS name of the domain
1449 :param domaindn: The DN of the domain (ie. DC=...)
1453 os
.chown(sysvol
, -1, gid
)
1459 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1460 setntacl(lp
,sysvol
, SYSVOL_ACL
, str(domainsid
))
1461 for root
, dirs
, files
in os
.walk(sysvol
, topdown
=False):
1464 os
.chown(os
.path
.join(root
, name
), -1, gid
)
1465 setntacl(lp
, os
.path
.join(root
, name
), SYSVOL_ACL
, str(domainsid
))
1468 os
.chown(os
.path
.join(root
, name
), -1, gid
)
1469 setntacl(lp
, os
.path
.join(root
, name
), SYSVOL_ACL
, str(domainsid
))
1471 # Set acls on Policy folder and policies folders
1472 set_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
)
1475 def interface_ips_v4(lp
):
1476 '''return only IPv4 IPs'''
1477 ips
= samba
.interface_ips(lp
, False)
1480 if i
.find(':') == -1:
1484 def interface_ips_v6(lp
, linklocal
=False):
1485 '''return only IPv6 IPs'''
1486 ips
= samba
.interface_ips(lp
, False)
1489 if i
.find(':') != -1 and (linklocal
or i
.find('%') == -1):
1494 def provision(logger
, session_info
, credentials
, smbconf
=None,
1495 targetdir
=None, samdb_fill
=FILL_FULL
, realm
=None, rootdn
=None,
1496 domaindn
=None, schemadn
=None, configdn
=None, serverdn
=None,
1497 domain
=None, hostname
=None, hostip
=None, hostip6
=None, domainsid
=None,
1498 next_rid
=1000, adminpass
=None, ldapadminpass
=None, krbtgtpass
=None,
1499 domainguid
=None, policyguid
=None, policyguid_dc
=None,
1500 invocationid
=None, machinepass
=None, ntdsguid
=None, dnspass
=None,
1501 root
=None, nobody
=None, users
=None, wheel
=None, backup
=None, aci
=None,
1502 serverrole
=None, dom_for_fun_level
=None, ldap_backend_extra_port
=None,
1503 ldap_backend_forced_uri
=None, backend_type
=None, sitename
=None,
1504 ol_mmr_urls
=None, ol_olc
=None, setup_ds_path
=None, slapd_path
=None,
1505 nosync
=False, ldap_dryrun_mode
=False, useeadb
=False, am_rodc
=False,
1509 :note: caution, this wipes all existing data!
1512 if domainsid
is None:
1513 domainsid
= security
.random_sid()
1515 domainsid
= security
.dom_sid(domainsid
)
1517 # create/adapt the group policy GUIDs
1518 # Default GUID for default policy are described at
1519 # "How Core Group Policy Works"
1520 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1521 if policyguid
is None:
1522 policyguid
= DEFAULT_POLICY_GUID
1523 policyguid
= policyguid
.upper()
1524 if policyguid_dc
is None:
1525 policyguid_dc
= DEFAULT_DC_POLICY_GUID
1526 policyguid_dc
= policyguid_dc
.upper()
1528 if adminpass
is None:
1529 adminpass
= samba
.generate_random_password(12, 32)
1530 if krbtgtpass
is None:
1531 krbtgtpass
= samba
.generate_random_password(128, 255)
1532 if machinepass
is None:
1533 machinepass
= samba
.generate_random_password(128, 255)
1535 dnspass
= samba
.generate_random_password(128, 255)
1536 if ldapadminpass
is None:
1537 # Make a new, random password between Samba and it's LDAP server
1538 ldapadminpass
=samba
.generate_random_password(128, 255)
1540 if backend_type
is None:
1541 backend_type
= "ldb"
1543 sid_generator
= "internal"
1544 if backend_type
== "fedora-ds":
1545 sid_generator
= "backend"
1547 root_uid
= findnss_uid([root
or "root"])
1548 nobody_uid
= findnss_uid([nobody
or "nobody"])
1549 users_gid
= findnss_gid([users
or "users", 'users', 'other', 'staff'])
1551 wheel_gid
= findnss_gid(["wheel", "adm"])
1553 wheel_gid
= findnss_gid([wheel
])
1555 bind_gid
= findnss_gid(["bind", "named"])
1559 if targetdir
is not None:
1560 smbconf
= os
.path
.join(targetdir
, "etc", "smb.conf")
1561 elif smbconf
is None:
1562 smbconf
= samba
.param
.default_path()
1563 if not os
.path
.exists(os
.path
.dirname(smbconf
)):
1564 os
.makedirs(os
.path
.dirname(smbconf
))
1566 # only install a new smb.conf if there isn't one there already
1567 if os
.path
.exists(smbconf
):
1568 # if Samba Team members can't figure out the weird errors
1569 # loading an empty smb.conf gives, then we need to be smarter.
1570 # Pretend it just didn't exist --abartlet
1571 data
= open(smbconf
, 'r').read()
1572 data
= data
.lstrip()
1573 if data
is None or data
== "":
1574 make_smbconf(smbconf
, hostname
, domain
, realm
,
1575 serverrole
, targetdir
, sid_generator
, useeadb
,
1578 make_smbconf(smbconf
, hostname
, domain
, realm
, serverrole
,
1579 targetdir
, sid_generator
, useeadb
, lp
=lp
)
1582 lp
= samba
.param
.LoadParm()
1584 names
= guess_names(lp
=lp
, hostname
=hostname
, domain
=domain
,
1585 dnsdomain
=realm
, serverrole
=serverrole
, domaindn
=domaindn
,
1586 configdn
=configdn
, schemadn
=schemadn
, serverdn
=serverdn
,
1588 paths
= provision_paths_from_lp(lp
, names
.dnsdomain
)
1590 paths
.bind_gid
= bind_gid
1593 logger
.info("Looking up IPv4 addresses")
1594 hostips
= interface_ips_v4(lp
)
1595 if len(hostips
) > 0:
1597 if len(hostips
) > 1:
1598 logger
.warning("More than one IPv4 address found. Using %s",
1600 if hostip
== "127.0.0.1":
1603 logger
.warning("No IPv4 address will be assigned")
1606 logger
.info("Looking up IPv6 addresses")
1607 hostips
= interface_ips_v6(lp
, linklocal
=False)
1609 hostip6
= hostips
[0]
1610 if len(hostips
) > 1:
1611 logger
.warning("More than one IPv6 address found. Using %s", hostip6
)
1613 logger
.warning("No IPv6 address will be assigned")
1615 if serverrole
is None:
1616 serverrole
= lp
.get("server role")
1618 assert serverrole
in ("domain controller", "member server", "standalone")
1619 if invocationid
is None:
1620 invocationid
= str(uuid
.uuid4())
1622 if not os
.path
.exists(paths
.private_dir
):
1623 os
.mkdir(paths
.private_dir
)
1624 if not os
.path
.exists(os
.path
.join(paths
.private_dir
, "tls")):
1625 os
.mkdir(os
.path
.join(paths
.private_dir
, "tls"))
1627 ldapi_url
= "ldapi://%s" % urllib
.quote(paths
.s4_ldapi_path
, safe
="")
1629 schema
= Schema(domainsid
, invocationid
=invocationid
,
1630 schemadn
=names
.schemadn
)
1632 if backend_type
== "ldb":
1633 provision_backend
= LDBBackend(backend_type
, paths
=paths
,
1634 lp
=lp
, credentials
=credentials
,
1635 names
=names
, logger
=logger
)
1636 elif backend_type
== "existing":
1637 provision_backend
= ExistingBackend(backend_type
, paths
=paths
,
1638 lp
=lp
, credentials
=credentials
,
1639 names
=names
, logger
=logger
,
1640 ldap_backend_forced_uri
=ldap_backend_forced_uri
)
1641 elif backend_type
== "fedora-ds":
1642 provision_backend
= FDSBackend(backend_type
, paths
=paths
,
1643 lp
=lp
, credentials
=credentials
,
1644 names
=names
, logger
=logger
, domainsid
=domainsid
,
1645 schema
=schema
, hostname
=hostname
, ldapadminpass
=ldapadminpass
,
1646 slapd_path
=slapd_path
,
1647 ldap_backend_extra_port
=ldap_backend_extra_port
,
1648 ldap_dryrun_mode
=ldap_dryrun_mode
, root
=root
,
1649 setup_ds_path
=setup_ds_path
,
1650 ldap_backend_forced_uri
=ldap_backend_forced_uri
)
1651 elif backend_type
== "openldap":
1652 provision_backend
= OpenLDAPBackend(backend_type
, paths
=paths
,
1653 lp
=lp
, credentials
=credentials
,
1654 names
=names
, logger
=logger
, domainsid
=domainsid
,
1655 schema
=schema
, hostname
=hostname
, ldapadminpass
=ldapadminpass
,
1656 slapd_path
=slapd_path
,
1657 ldap_backend_extra_port
=ldap_backend_extra_port
,
1658 ldap_dryrun_mode
=ldap_dryrun_mode
, ol_mmr_urls
=ol_mmr_urls
,
1660 ldap_backend_forced_uri
=ldap_backend_forced_uri
)
1662 raise ValueError("Unknown LDAP backend type selected")
1664 provision_backend
.init()
1665 provision_backend
.start()
1667 # only install a new shares config db if there is none
1668 if not os
.path
.exists(paths
.shareconf
):
1669 logger
.info("Setting up share.ldb")
1670 share_ldb
= Ldb(paths
.shareconf
, session_info
=session_info
,
1672 share_ldb
.load_ldif_file_add(setup_path("share.ldif"))
1674 logger
.info("Setting up secrets.ldb")
1675 secrets_ldb
= setup_secretsdb(paths
,
1676 session_info
=session_info
,
1677 backend_credentials
=provision_backend
.secrets_credentials
, lp
=lp
)
1680 logger
.info("Setting up the registry")
1681 setup_registry(paths
.hklm
, session_info
,
1684 logger
.info("Setting up the privileges database")
1685 setup_privileges(paths
.privilege
, session_info
, lp
=lp
)
1687 logger
.info("Setting up idmap db")
1688 idmap
= setup_idmapdb(paths
.idmapdb
,
1689 session_info
=session_info
, lp
=lp
)
1691 logger
.info("Setting up SAM db")
1692 samdb
= setup_samdb(paths
.samdb
, session_info
,
1693 provision_backend
, lp
, names
, logger
=logger
,
1694 domainsid
=domainsid
, schema
=schema
, domainguid
=domainguid
,
1695 policyguid
=policyguid
, policyguid_dc
=policyguid_dc
,
1696 fill
=samdb_fill
, adminpass
=adminpass
, krbtgtpass
=krbtgtpass
,
1697 invocationid
=invocationid
, machinepass
=machinepass
,
1698 dnspass
=dnspass
, ntdsguid
=ntdsguid
, serverrole
=serverrole
,
1699 dom_for_fun_level
=dom_for_fun_level
, am_rodc
=am_rodc
,
1702 if serverrole
== "domain controller":
1703 if paths
.netlogon
is None:
1704 logger
.info("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1705 logger
.info("Please either remove %s or see the template at %s" %
1706 (paths
.smbconf
, setup_path("provision.smb.conf.dc")))
1707 assert paths
.netlogon
is not None
1709 if paths
.sysvol
is None:
1710 logger
.info("Existing smb.conf does not have a [sysvol] share, but you"
1711 " are configuring a DC.")
1712 logger
.info("Please either remove %s or see the template at %s" %
1713 (paths
.smbconf
, setup_path("provision.smb.conf.dc")))
1714 assert paths
.sysvol
is not None
1716 if not os
.path
.isdir(paths
.netlogon
):
1717 os
.makedirs(paths
.netlogon
, 0755)
1719 if samdb_fill
== FILL_FULL
:
1720 setup_name_mappings(samdb
, idmap
, str(domainsid
), names
.domaindn
,
1721 root_uid
=root_uid
, nobody_uid
=nobody_uid
,
1722 users_gid
=users_gid
, wheel_gid
=wheel_gid
)
1724 if serverrole
== "domain controller":
1725 # Set up group policies (domain policy and domain controller
1727 create_default_gpo(paths
.sysvol
, names
.dnsdomain
, policyguid
,
1729 setsysvolacl(samdb
, paths
.netlogon
, paths
.sysvol
, wheel_gid
,
1730 domainsid
, names
.dnsdomain
, names
.domaindn
, lp
)
1732 logger
.info("Setting up sam.ldb rootDSE marking as synchronized")
1733 setup_modify_ldif(samdb
, setup_path("provision_rootdse_modify.ldif"))
1735 secretsdb_self_join(secrets_ldb
, domain
=names
.domain
,
1736 realm
=names
.realm
, dnsdomain
=names
.dnsdomain
,
1737 netbiosname
=names
.netbiosname
, domainsid
=domainsid
,
1738 machinepass
=machinepass
, secure_channel_type
=SEC_CHAN_BDC
)
1740 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1741 # In future, this might be determined from some configuration
1742 kerberos_enctypes
= str(ENC_ALL_TYPES
)
1745 msg
= ldb
.Message(ldb
.Dn(samdb
,
1746 samdb
.searchone("distinguishedName",
1747 expression
="samAccountName=%s$" % names
.netbiosname
,
1748 scope
=ldb
.SCOPE_SUBTREE
)))
1749 msg
["msDS-SupportedEncryptionTypes"] = ldb
.MessageElement(
1750 elements
=kerberos_enctypes
, flags
=ldb
.FLAG_MOD_REPLACE
,
1751 name
="msDS-SupportedEncryptionTypes")
1753 except ldb
.LdbError
, (enum
, estr
):
1754 if enum
!= ldb
.ERR_NO_SUCH_ATTRIBUTE
:
1755 # It might be that this attribute does not exist in this schema
1758 if serverrole
== "domain controller":
1759 secretsdb_setup_dns(secrets_ldb
, names
,
1760 paths
.private_dir
, realm
=names
.realm
,
1761 dnsdomain
=names
.dnsdomain
,
1762 dns_keytab_path
=paths
.dns_keytab
, dnspass
=dnspass
)
1764 domainguid
= samdb
.searchone(basedn
=domaindn
,
1765 attribute
="objectGUID")
1766 assert isinstance(domainguid
, str)
1768 # Only make a zone file on the first DC, it should be
1769 # replicated with DNS replication
1770 create_zone_file(lp
, logger
, paths
, targetdir
,
1771 dnsdomain
=names
.dnsdomain
, hostip
=hostip
, hostip6
=hostip6
,
1772 hostname
=names
.hostname
, realm
=names
.realm
,
1773 domainguid
=domainguid
, ntdsguid
=names
.ntdsguid
)
1775 create_named_conf(paths
, realm
=names
.realm
,
1776 dnsdomain
=names
.dnsdomain
, private_dir
=paths
.private_dir
)
1778 create_named_txt(paths
.namedtxt
,
1779 realm
=names
.realm
, dnsdomain
=names
.dnsdomain
,
1780 dnsname
= "%s.%s" % (names
.hostname
, names
.dnsdomain
),
1781 private_dir
=paths
.private_dir
,
1782 keytab_name
=paths
.dns_keytab
)
1783 logger
.info("See %s for an example configuration include file for BIND", paths
.namedconf
)
1784 logger
.info("and %s for further documentation required for secure DNS "
1785 "updates", paths
.namedtxt
)
1787 lastProvisionUSNs
= get_last_provision_usn(samdb
)
1788 maxUSN
= get_max_usn(samdb
, str(names
.rootdn
))
1789 if lastProvisionUSNs
is not None:
1790 update_provision_usn(samdb
, 0, maxUSN
, invocationid
, 1)
1792 set_provision_usn(samdb
, 0, maxUSN
, invocationid
)
1794 create_krb5_conf(paths
.krb5conf
,
1795 dnsdomain
=names
.dnsdomain
, hostname
=names
.hostname
,
1797 logger
.info("A Kerberos configuration suitable for Samba 4 has been "
1798 "generated at %s", paths
.krb5conf
)
1800 if serverrole
== "domain controller":
1801 create_dns_update_list(lp
, logger
, paths
)
1803 provision_backend
.post_setup()
1804 provision_backend
.shutdown()
1806 create_phpldapadmin_config(paths
.phpldapadminconfig
,
1809 secrets_ldb
.transaction_cancel()
1812 # Now commit the secrets.ldb to disk
1813 secrets_ldb
.transaction_commit()
1815 # the commit creates the dns.keytab, now chown it
1816 dns_keytab_path
= os
.path
.join(paths
.private_dir
, paths
.dns_keytab
)
1817 if os
.path
.isfile(dns_keytab_path
) and paths
.bind_gid
is not None:
1819 os
.chmod(dns_keytab_path
, 0640)
1820 os
.chown(dns_keytab_path
, -1, paths
.bind_gid
)
1822 if not os
.environ
.has_key('SAMBA_SELFTEST'):
1823 logger
.info("Failed to chown %s to bind gid %u",
1824 dns_keytab_path
, paths
.bind_gid
)
1827 logger
.info("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php",
1828 paths
.phpldapadminconfig
)
1830 logger
.info("Once the above files are installed, your Samba4 server will be ready to use")
1831 logger
.info("Server Role: %s" % serverrole
)
1832 logger
.info("Hostname: %s" % names
.hostname
)
1833 logger
.info("NetBIOS Domain: %s" % names
.domain
)
1834 logger
.info("DNS Domain: %s" % names
.dnsdomain
)
1835 logger
.info("DOMAIN SID: %s" % str(domainsid
))
1836 if samdb_fill
== FILL_FULL
:
1837 logger
.info("Admin password: %s" % adminpass
)
1838 if provision_backend
.type is not "ldb":
1839 if provision_backend
.credentials
.get_bind_dn() is not None:
1840 logger
.info("LDAP Backend Admin DN: %s" %
1841 provision_backend
.credentials
.get_bind_dn())
1843 logger
.info("LDAP Admin User: %s" %
1844 provision_backend
.credentials
.get_username())
1846 logger
.info("LDAP Admin Password: %s" %
1847 provision_backend
.credentials
.get_password())
1849 if provision_backend
.slapd_command_escaped
is not None:
1850 # now display slapd_command_file.txt to show how slapd must be
1852 logger
.info("Use later the following commandline to start slapd, then Samba:")
1853 logger
.info(provision_backend
.slapd_command_escaped
)
1854 logger
.info("This slapd-Commandline is also stored under: %s/ldap_backend_startup.sh",
1855 provision_backend
.ldapdir
)
1857 result
= ProvisionResult()
1858 result
.domaindn
= domaindn
1859 result
.paths
= paths
1861 result
.samdb
= samdb
1865 def provision_become_dc(smbconf
=None, targetdir
=None,
1866 realm
=None, rootdn
=None, domaindn
=None, schemadn
=None, configdn
=None,
1867 serverdn
=None, domain
=None, hostname
=None, domainsid
=None,
1868 adminpass
=None, krbtgtpass
=None, domainguid
=None, policyguid
=None,
1869 policyguid_dc
=None, invocationid
=None, machinepass
=None, dnspass
=None,
1870 root
=None, nobody
=None, users
=None, wheel
=None, backup
=None,
1871 serverrole
=None, ldap_backend
=None, ldap_backend_type
=None,
1872 sitename
=None, debuglevel
=1):
1874 logger
= logging
.getLogger("provision")
1875 samba
.set_debug_level(debuglevel
)
1877 res
= provision(logger
, system_session(), None,
1878 smbconf
=smbconf
, targetdir
=targetdir
, samdb_fill
=FILL_DRS
,
1879 realm
=realm
, rootdn
=rootdn
, domaindn
=domaindn
, schemadn
=schemadn
,
1880 configdn
=configdn
, serverdn
=serverdn
, domain
=domain
,
1881 hostname
=hostname
, hostip
=None, domainsid
=domainsid
,
1882 machinepass
=machinepass
, serverrole
="domain controller",
1884 res
.lp
.set("debuglevel", str(debuglevel
))
1888 def create_phpldapadmin_config(path
, ldapi_uri
):
1889 """Create a PHP LDAP admin configuration file.
1891 :param path: Path to write the configuration to.
1893 setup_file(setup_path("phpldapadmin-config.php"), path
,
1894 {"S4_LDAPI_URI": ldapi_uri
})
1897 def create_zone_file(lp
, logger
, paths
, targetdir
, dnsdomain
,
1898 hostip
, hostip6
, hostname
, realm
, domainguid
,
1900 """Write out a DNS zone file, from the info in the current database.
1902 :param paths: paths object
1903 :param dnsdomain: DNS Domain name
1904 :param domaindn: DN of the Domain
1905 :param hostip: Local IPv4 IP
1906 :param hostip6: Local IPv6 IP
1907 :param hostname: Local hostname
1908 :param realm: Realm name
1909 :param domainguid: GUID of the domain.
1910 :param ntdsguid: GUID of the hosts nTDSDSA record.
1912 assert isinstance(domainguid
, str)
1914 if hostip6
is not None:
1915 hostip6_base_line
= " IN AAAA " + hostip6
1916 hostip6_host_line
= hostname
+ " IN AAAA " + hostip6
1917 gc_msdcs_ip6_line
= "gc._msdcs IN AAAA " + hostip6
1919 hostip6_base_line
= ""
1920 hostip6_host_line
= ""
1921 gc_msdcs_ip6_line
= ""
1923 if hostip
is not None:
1924 hostip_base_line
= " IN A " + hostip
1925 hostip_host_line
= hostname
+ " IN A " + hostip
1926 gc_msdcs_ip_line
= "gc._msdcs IN A " + hostip
1928 hostip_base_line
= ""
1929 hostip_host_line
= ""
1930 gc_msdcs_ip_line
= ""
1932 dns_dir
= os
.path
.dirname(paths
.dns
)
1935 shutil
.rmtree(dns_dir
, True)
1939 os
.mkdir(dns_dir
, 0775)
1941 # we need to freeze the zone while we update the contents
1942 if targetdir
is None:
1943 rndc
= ' '.join(lp
.get("rndc command"))
1944 os
.system(rndc
+ " freeze " + lp
.get("realm"))
1946 setup_file(setup_path("provision.zone"), paths
.dns
, {
1947 "HOSTNAME": hostname
,
1948 "DNSDOMAIN": dnsdomain
,
1950 "HOSTIP_BASE_LINE": hostip_base_line
,
1951 "HOSTIP_HOST_LINE": hostip_host_line
,
1952 "DOMAINGUID": domainguid
,
1953 "DATESTRING": time
.strftime("%Y%m%d%H"),
1954 "DEFAULTSITE": DEFAULTSITE
,
1955 "NTDSGUID": ntdsguid
,
1956 "HOSTIP6_BASE_LINE": hostip6_base_line
,
1957 "HOSTIP6_HOST_LINE": hostip6_host_line
,
1958 "GC_MSDCS_IP_LINE": gc_msdcs_ip_line
,
1959 "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line
,
1962 # note that we use no variable substitution on this file
1963 # the substitution is done at runtime by samba_dnsupdate
1964 setup_file(setup_path("dns_update_list"), paths
.dns_update_list
, None)
1966 # and the SPN update list
1967 setup_file(setup_path("spn_update_list"), paths
.spn_update_list
, None)
1969 if paths
.bind_gid
is not None:
1971 os
.chown(dns_dir
, -1, paths
.bind_gid
)
1972 os
.chown(paths
.dns
, -1, paths
.bind_gid
)
1973 # chmod needed to cope with umask
1974 os
.chmod(dns_dir
, 0775)
1975 os
.chmod(paths
.dns
, 0664)
1977 if not os
.environ
.has_key('SAMBA_SELFTEST'):
1978 logger
.error("Failed to chown %s to bind gid %u" % (
1979 dns_dir
, paths
.bind_gid
))
1981 if targetdir
is None:
1982 os
.system(rndc
+ " unfreeze " + lp
.get("realm"))
1985 def create_dns_update_list(lp
, logger
, paths
):
1986 """Write out a dns_update_list file"""
1987 # note that we use no variable substitution on this file
1988 # the substitution is done at runtime by samba_dnsupdate
1989 setup_file(setup_path("dns_update_list"), paths
.dns_update_list
, None)
1990 setup_file(setup_path("spn_update_list"), paths
.spn_update_list
, None)
1993 def create_named_conf(paths
, realm
, dnsdomain
,
1995 """Write out a file containing zone statements suitable for inclusion in a
1996 named.conf file (including GSS-TSIG configuration).
1998 :param paths: all paths
1999 :param realm: Realm name
2000 :param dnsdomain: DNS Domain name
2001 :param private_dir: Path to private directory
2002 :param keytab_name: File name of DNS keytab file
2005 setup_file(setup_path("named.conf"), paths
.namedconf
, {
2006 "DNSDOMAIN": dnsdomain
,
2008 "ZONE_FILE": paths
.dns
,
2009 "REALM_WC": "*." + ".".join(realm
.split(".")[1:]),
2010 "NAMED_CONF": paths
.namedconf
,
2011 "NAMED_CONF_UPDATE": paths
.namedconf_update
2014 setup_file(setup_path("named.conf.update"), paths
.namedconf_update
)
2017 def create_named_txt(path
, realm
, dnsdomain
, dnsname
, private_dir
,
2019 """Write out a file containing zone statements suitable for inclusion in a
2020 named.conf file (including GSS-TSIG configuration).
2022 :param path: Path of the new named.conf file.
2023 :param realm: Realm name
2024 :param dnsdomain: DNS Domain name
2025 :param private_dir: Path to private directory
2026 :param keytab_name: File name of DNS keytab file
2028 setup_file(setup_path("named.txt"), path
, {
2029 "DNSDOMAIN": dnsdomain
,
2030 "DNSNAME" : dnsname
,
2032 "DNS_KEYTAB": keytab_name
,
2033 "DNS_KEYTAB_ABS": os
.path
.join(private_dir
, keytab_name
),
2034 "PRIVATE_DIR": private_dir
2038 def create_krb5_conf(path
, dnsdomain
, hostname
, realm
):
2039 """Write out a file containing zone statements suitable for inclusion in a
2040 named.conf file (including GSS-TSIG configuration).
2042 :param path: Path of the new named.conf file.
2043 :param dnsdomain: DNS Domain name
2044 :param hostname: Local hostname
2045 :param realm: Realm name
2047 setup_file(setup_path("krb5.conf"), path
, {
2048 "DNSDOMAIN": dnsdomain
,
2049 "HOSTNAME": hostname
,
2054 class ProvisioningError(Exception):
2055 """A generic provision error."""
2057 def __init__(self
, value
):
2061 return "ProvisioningError: " + self
.value
2064 class InvalidNetbiosName(Exception):
2065 """A specified name was not a valid NetBIOS name."""
2066 def __init__(self
, name
):
2067 super(InvalidNetbiosName
, self
).__init
__(
2068 "The name '%r' is not a valid NetBIOS name" % name
)