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 (
77 from samba
.provision
.sambadns
import setup_ad_dns
81 from samba
.schema
import Schema
82 from samba
.samdb
import SamDB
83 from samba
.dbchecker
import dbcheck
86 VALID_NETBIOS_CHARS
= " !#$%&'()-.@^_{}~"
87 DEFAULT_POLICY_GUID
= "31B2F340-016D-11D2-945F-00C04FB984F9"
88 DEFAULT_DC_POLICY_GUID
= "6AC1786C-016F-11D2-945F-00C04fB984F9"
89 DEFAULTSITE
= "Default-First-Site-Name"
90 LAST_PROVISION_USN_ATTRIBUTE
= "lastProvisionUSN"
94 """Return an absolute path to the provision tempate file specified by file"""
95 return os
.path
.join(samba
.param
.setup_dir(), file)
97 # Descriptors of naming contexts and other important objects
99 # "get_schema_descriptor" is located in "schema.py"
101 def get_config_descriptor(domain_sid
):
102 sddl
= "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
103 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
104 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
105 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
106 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
107 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
108 "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
109 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
110 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
111 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
112 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
113 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
114 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
115 "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
116 "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
117 sec
= security
.descriptor
.from_sddl(sddl
, domain_sid
)
121 def get_domain_descriptor(domain_sid
):
122 sddl
= "O:BAG:BAD:AI(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
123 "(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
124 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
125 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
126 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
127 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
128 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
129 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
130 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
131 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
132 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
133 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)" \
134 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)" \
135 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)" \
136 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)" \
137 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
138 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
139 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
140 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
141 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
142 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
143 "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;IF)" \
144 "(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)" \
145 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)" \
146 "(OA;CIIO;RPLCLORC;;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
147 "(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)" \
148 "(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
149 "(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)" \
150 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
151 "(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)" \
152 "(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)" \
153 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
154 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
155 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
156 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
157 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)" \
158 "(OA;CIIO;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \
159 "(A;;RPWPCRCCLCLORCWOWDSW;;;DA)" \
160 "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
163 "(A;CI;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
165 "(A;;RPLCLORC;;;ED)" \
166 "(A;;RPLCLORC;;;AU)" \
167 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
168 "S:AI(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
169 "(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
170 "(AU;SA;CR;;;DU)(AU;SA;CR;;;BA)(AU;SA;WPWOWD;;;WD)"
171 sec
= security
.descriptor
.from_sddl(sddl
, domain_sid
)
175 class ProvisionPaths(object):
178 self
.shareconf
= None
189 self
.dns_keytab
= None
192 self
.private_dir
= None
195 class ProvisionNames(object):
202 self
.ldapmanagerdn
= None
203 self
.dnsdomain
= None
205 self
.netbiosname
= None
211 def find_provision_key_parameters(samdb
, secretsdb
, idmapdb
, paths
, smbconf
, lp
):
212 """Get key provision parameters (realm, domain, ...) from a given provision
214 :param samdb: An LDB object connected to the sam.ldb file
215 :param secretsdb: An LDB object connected to the secrets.ldb file
216 :param idmapdb: An LDB object connected to the idmap.ldb file
217 :param paths: A list of path to provision object
218 :param smbconf: Path to the smb.conf file
219 :param lp: A LoadParm object
220 :return: A list of key provision parameters
222 names
= ProvisionNames()
223 names
.adminpass
= None
225 # NT domain, kerberos realm, root dn, domain dn, domain dns name
226 names
.domain
= string
.upper(lp
.get("workgroup"))
227 names
.realm
= lp
.get("realm")
228 names
.dnsdomain
= names
.realm
.lower()
229 basedn
= samba
.dn_from_dns_name(names
.dnsdomain
)
230 names
.realm
= string
.upper(names
.realm
)
232 # Get the netbiosname first (could be obtained from smb.conf in theory)
233 res
= secretsdb
.search(expression
="(flatname=%s)" %
234 names
.domain
,base
="CN=Primary Domains",
235 scope
=ldb
.SCOPE_SUBTREE
, attrs
=["sAMAccountName"])
236 names
.netbiosname
= str(res
[0]["sAMAccountName"]).replace("$","")
238 names
.smbconf
= smbconf
240 # That's a bit simplistic but it's ok as long as we have only 3
242 current
= samdb
.search(expression
="(objectClass=*)",
243 base
="", scope
=ldb
.SCOPE_BASE
,
244 attrs
=["defaultNamingContext", "schemaNamingContext",
245 "configurationNamingContext","rootDomainNamingContext"])
247 names
.configdn
= current
[0]["configurationNamingContext"]
248 configdn
= str(names
.configdn
)
249 names
.schemadn
= current
[0]["schemaNamingContext"]
250 if not (ldb
.Dn(samdb
, basedn
) == (ldb
.Dn(samdb
,
251 current
[0]["defaultNamingContext"][0]))):
252 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
253 "is not the same ..." % (paths
.samdb
,
254 str(current
[0]["defaultNamingContext"][0]),
255 paths
.smbconf
, basedn
)))
257 names
.domaindn
=current
[0]["defaultNamingContext"]
258 names
.rootdn
=current
[0]["rootDomainNamingContext"]
260 res3
= samdb
.search(expression
="(objectClass=site)",
261 base
="CN=Sites," + configdn
, scope
=ldb
.SCOPE_ONELEVEL
, attrs
=["cn"])
262 names
.sitename
= str(res3
[0]["cn"])
264 # dns hostname and server dn
265 res4
= samdb
.search(expression
="(CN=%s)" % names
.netbiosname
,
266 base
="OU=Domain Controllers,%s" % basedn
,
267 scope
=ldb
.SCOPE_ONELEVEL
, attrs
=["dNSHostName"])
268 names
.hostname
= str(res4
[0]["dNSHostName"]).replace("." + names
.dnsdomain
,"")
270 server_res
= samdb
.search(expression
="serverReference=%s" % res4
[0].dn
,
271 attrs
=[], base
=configdn
)
272 names
.serverdn
= server_res
[0].dn
274 # invocation id/objectguid
275 res5
= samdb
.search(expression
="(objectClass=*)",
276 base
="CN=NTDS Settings,%s" % str(names
.serverdn
), scope
=ldb
.SCOPE_BASE
,
277 attrs
=["invocationID", "objectGUID"])
278 names
.invocation
= str(ndr_unpack(misc
.GUID
, res5
[0]["invocationId"][0]))
279 names
.ntdsguid
= str(ndr_unpack(misc
.GUID
, res5
[0]["objectGUID"][0]))
282 res6
= samdb
.search(expression
="(objectClass=*)", base
=basedn
,
283 scope
=ldb
.SCOPE_BASE
, attrs
=["objectGUID",
284 "objectSid","msDS-Behavior-Version" ])
285 names
.domainguid
= str(ndr_unpack(misc
.GUID
, res6
[0]["objectGUID"][0]))
286 names
.domainsid
= ndr_unpack( security
.dom_sid
, res6
[0]["objectSid"][0])
287 if res6
[0].get("msDS-Behavior-Version") is None or \
288 int(res6
[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000
:
289 names
.domainlevel
= DS_DOMAIN_FUNCTION_2000
291 names
.domainlevel
= int(res6
[0]["msDS-Behavior-Version"][0])
294 res7
= samdb
.search(expression
="(displayName=Default Domain Policy)",
295 base
="CN=Policies,CN=System," + basedn
,
296 scope
=ldb
.SCOPE_ONELEVEL
, attrs
=["cn","displayName"])
297 names
.policyid
= str(res7
[0]["cn"]).replace("{","").replace("}","")
299 res8
= samdb
.search(expression
="(displayName=Default Domain Controllers"
301 base
="CN=Policies,CN=System," + basedn
,
302 scope
=ldb
.SCOPE_ONELEVEL
, attrs
=["cn","displayName"])
304 names
.policyid_dc
= str(res8
[0]["cn"]).replace("{","").replace("}","")
306 names
.policyid_dc
= None
307 res9
= idmapdb
.search(expression
="(cn=%s)" %
308 (security
.SID_BUILTIN_ADMINISTRATORS
),
311 names
.wheel_gid
= res9
[0]["xidNumber"]
313 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid")
316 def update_provision_usn(samdb
, low
, high
, id, replace
=False):
317 """Update the field provisionUSN in sam.ldb
319 This field is used to track range of USN modified by provision and
321 This value is used afterward by next provision to figure out if
322 the field have been modified since last provision.
324 :param samdb: An LDB object connect to sam.ldb
325 :param low: The lowest USN modified by this upgrade
326 :param high: The highest USN modified by this upgrade
327 :param id: The invocation id of the samba's dc
328 :param replace: A boolean indicating if the range should replace any
329 existing one or appended (default)
334 entry
= samdb
.search(base
="@PROVISION",
335 scope
=ldb
.SCOPE_BASE
,
336 attrs
=[LAST_PROVISION_USN_ATTRIBUTE
, "dn"])
337 for e
in entry
[0][LAST_PROVISION_USN_ATTRIBUTE
]:
338 if not re
.search(';', e
):
339 e
= "%s;%s" % (e
, id)
342 tab
.append("%s-%s;%s" % (low
, high
, id))
343 delta
= ldb
.Message()
344 delta
.dn
= ldb
.Dn(samdb
, "@PROVISION")
345 delta
[LAST_PROVISION_USN_ATTRIBUTE
] = ldb
.MessageElement(tab
,
346 ldb
.FLAG_MOD_REPLACE
, LAST_PROVISION_USN_ATTRIBUTE
)
347 entry
= samdb
.search(expression
='provisionnerID=*',
348 base
="@PROVISION", scope
=ldb
.SCOPE_BASE
,
349 attrs
=["provisionnerID"])
350 if len(entry
) == 0 or len(entry
[0]) == 0:
351 delta
["provisionnerID"] = ldb
.MessageElement(id, ldb
.FLAG_MOD_ADD
, "provisionnerID")
355 def set_provision_usn(samdb
, low
, high
, id):
356 """Set the field provisionUSN in sam.ldb
357 This field is used to track range of USN modified by provision and
359 This value is used afterward by next provision to figure out if
360 the field have been modified since last provision.
362 :param samdb: An LDB object connect to sam.ldb
363 :param low: The lowest USN modified by this upgrade
364 :param high: The highest USN modified by this upgrade
365 :param id: The invocationId of the provision"""
368 tab
.append("%s-%s;%s" % (low
, high
, id))
370 delta
= ldb
.Message()
371 delta
.dn
= ldb
.Dn(samdb
, "@PROVISION")
372 delta
[LAST_PROVISION_USN_ATTRIBUTE
] = ldb
.MessageElement(tab
,
373 ldb
.FLAG_MOD_ADD
, LAST_PROVISION_USN_ATTRIBUTE
)
377 def get_max_usn(samdb
,basedn
):
378 """ This function return the biggest USN present in the provision
380 :param samdb: A LDB object pointing to the sam.ldb
381 :param basedn: A string containing the base DN of the provision
383 :return: The biggest USN in the provision"""
385 res
= samdb
.search(expression
="objectClass=*",base
=basedn
,
386 scope
=ldb
.SCOPE_SUBTREE
,attrs
=["uSNChanged"],
387 controls
=["search_options:1:2",
388 "server_sort:1:1:uSNChanged",
389 "paged_results:1:1"])
390 return res
[0]["uSNChanged"]
393 def get_last_provision_usn(sam
):
394 """Get USNs ranges modified by a provision or an upgradeprovision
396 :param sam: An LDB object pointing to the sam.ldb
397 :return: a dictionnary which keys are invocation id and values are an array
398 of integer representing the different ranges
401 entry
= sam
.search(expression
="%s=*" % LAST_PROVISION_USN_ATTRIBUTE
,
402 base
="@PROVISION", scope
=ldb
.SCOPE_BASE
,
403 attrs
=[LAST_PROVISION_USN_ATTRIBUTE
, "provisionnerID"])
404 except ldb
.LdbError
, (ecode
, emsg
):
405 if ecode
== ldb
.ERR_NO_SUCH_OBJECT
:
412 if entry
[0].get("provisionnerID"):
413 for e
in entry
[0]["provisionnerID"]:
415 for r
in entry
[0][LAST_PROVISION_USN_ATTRIBUTE
]:
416 tab1
= str(r
).split(';')
421 if (len(myids
) > 0 and id not in myids
):
423 tab2
= p
.split(tab1
[0])
424 if range.get(id) == None:
426 range[id].append(tab2
[0])
427 range[id].append(tab2
[1])
433 class ProvisionResult(object):
444 def check_install(lp
, session_info
, credentials
):
445 """Check whether the current install seems ok.
447 :param lp: Loadparm context
448 :param session_info: Session information
449 :param credentials: Credentials
451 if lp
.get("realm") == "":
452 raise Exception("Realm empty")
453 samdb
= Ldb(lp
.samdb_url(), session_info
=session_info
,
454 credentials
=credentials
, lp
=lp
)
455 if len(samdb
.search("(cn=Administrator)")) != 1:
456 raise ProvisioningError("No administrator account found")
459 def findnss(nssfn
, names
):
460 """Find a user or group from a list of possibilities.
462 :param nssfn: NSS Function to try (should raise KeyError if not found)
463 :param names: Names to check.
464 :return: Value return by first names list.
471 raise KeyError("Unable to find user/group in %r" % names
)
474 findnss_uid
= lambda names
: findnss(pwd
.getpwnam
, names
)[2]
475 findnss_gid
= lambda names
: findnss(grp
.getgrnam
, names
)[2]
478 def setup_add_ldif(ldb
, ldif_path
, subst_vars
=None,controls
=["relax:0"]):
479 """Setup a ldb in the private dir.
481 :param ldb: LDB file to import data into
482 :param ldif_path: Path of the LDIF file to load
483 :param subst_vars: Optional variables to subsitute in LDIF.
484 :param nocontrols: Optional list of controls, can be None for no controls
486 assert isinstance(ldif_path
, str)
487 data
= read_and_sub_file(ldif_path
, subst_vars
)
488 ldb
.add_ldif(data
, controls
)
491 def setup_modify_ldif(ldb
, ldif_path
, subst_vars
=None,controls
=["relax:0"]):
492 """Modify a ldb in the private dir.
494 :param ldb: LDB object.
495 :param ldif_path: LDIF file path.
496 :param subst_vars: Optional dictionary with substitution variables.
498 data
= read_and_sub_file(ldif_path
, subst_vars
)
499 ldb
.modify_ldif(data
, controls
)
502 def setup_ldb(ldb
, ldif_path
, subst_vars
):
503 """Import a LDIF a file into a LDB handle, optionally substituting
506 :note: Either all LDIF data will be added or none (using transactions).
508 :param ldb: LDB file to import into.
509 :param ldif_path: Path to the LDIF file.
510 :param subst_vars: Dictionary with substitution variables.
512 assert ldb
is not None
513 ldb
.transaction_start()
515 setup_add_ldif(ldb
, ldif_path
, subst_vars
)
517 ldb
.transaction_cancel()
520 ldb
.transaction_commit()
523 def provision_paths_from_lp(lp
, dnsdomain
):
524 """Set the default paths for provisioning.
526 :param lp: Loadparm context.
527 :param dnsdomain: DNS Domain name
529 paths
= ProvisionPaths()
530 paths
.private_dir
= lp
.get("private dir")
532 # This is stored without path prefix for the "privateKeytab" attribute in
533 # "secrets_dns.ldif".
534 paths
.dns_keytab
= "dns.keytab"
535 paths
.keytab
= "secrets.keytab"
537 paths
.shareconf
= os
.path
.join(paths
.private_dir
, "share.ldb")
538 paths
.samdb
= os
.path
.join(paths
.private_dir
, "sam.ldb")
539 paths
.idmapdb
= os
.path
.join(paths
.private_dir
, "idmap.ldb")
540 paths
.secrets
= os
.path
.join(paths
.private_dir
, "secrets.ldb")
541 paths
.privilege
= os
.path
.join(paths
.private_dir
, "privilege.ldb")
542 paths
.dns
= os
.path
.join(paths
.private_dir
, "dns", dnsdomain
+ ".zone")
543 paths
.dns_update_list
= os
.path
.join(paths
.private_dir
, "dns_update_list")
544 paths
.spn_update_list
= os
.path
.join(paths
.private_dir
, "spn_update_list")
545 paths
.namedconf
= os
.path
.join(paths
.private_dir
, "named.conf")
546 paths
.namedconf_update
= os
.path
.join(paths
.private_dir
, "named.conf.update")
547 paths
.namedtxt
= os
.path
.join(paths
.private_dir
, "named.txt")
548 paths
.krb5conf
= os
.path
.join(paths
.private_dir
, "krb5.conf")
549 paths
.winsdb
= os
.path
.join(paths
.private_dir
, "wins.ldb")
550 paths
.s4_ldapi_path
= os
.path
.join(paths
.private_dir
, "ldapi")
551 paths
.phpldapadminconfig
= os
.path
.join(paths
.private_dir
,
552 "phpldapadmin-config.php")
553 paths
.hklm
= "hklm.ldb"
554 paths
.hkcr
= "hkcr.ldb"
555 paths
.hkcu
= "hkcu.ldb"
556 paths
.hku
= "hku.ldb"
557 paths
.hkpd
= "hkpd.ldb"
558 paths
.hkpt
= "hkpt.ldb"
559 paths
.sysvol
= lp
.get("path", "sysvol")
560 paths
.netlogon
= lp
.get("path", "netlogon")
561 paths
.smbconf
= lp
.configfile
565 def guess_names(lp
=None, hostname
=None, domain
=None, dnsdomain
=None,
566 serverrole
=None, rootdn
=None, domaindn
=None, configdn
=None,
567 schemadn
=None, serverdn
=None, sitename
=None):
568 """Guess configuration settings to use."""
571 hostname
= socket
.gethostname().split(".")[0]
573 netbiosname
= lp
.get("netbios name")
574 if netbiosname
is None:
575 netbiosname
= hostname
576 # remove forbidden chars
578 for x
in netbiosname
:
579 if x
.isalnum() or x
in VALID_NETBIOS_CHARS
:
580 newnbname
= "%s%c" % (newnbname
, x
)
581 # force the length to be <16
582 netbiosname
= newnbname
[0:15]
583 assert netbiosname
is not None
584 netbiosname
= netbiosname
.upper()
585 if not valid_netbios_name(netbiosname
):
586 raise InvalidNetbiosName(netbiosname
)
588 if dnsdomain
is None:
589 dnsdomain
= lp
.get("realm")
590 if dnsdomain
is None or dnsdomain
== "":
591 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp
.configfile
)
593 dnsdomain
= dnsdomain
.lower()
595 if serverrole
is None:
596 serverrole
= lp
.get("server role")
597 if serverrole
is None:
598 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp
.configfile
)
600 serverrole
= serverrole
.lower()
602 realm
= dnsdomain
.upper()
604 if lp
.get("realm") == "":
605 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp
.configfile
)
607 if lp
.get("realm").upper() != realm
:
608 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
))
610 if lp
.get("server role").lower() != serverrole
:
611 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
))
613 if serverrole
== "domain controller":
615 # This will, for better or worse, default to 'WORKGROUP'
616 domain
= lp
.get("workgroup")
617 domain
= domain
.upper()
619 if lp
.get("workgroup").upper() != domain
:
620 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
))
623 domaindn
= samba
.dn_from_dns_name(dnsdomain
)
625 if domain
== netbiosname
:
626 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain
, netbiosname
))
630 domaindn
= "DC=" + netbiosname
632 if not valid_netbios_name(domain
):
633 raise InvalidNetbiosName(domain
)
635 if hostname
.upper() == realm
:
636 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm
, hostname
))
637 if netbiosname
.upper() == realm
:
638 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm
, netbiosname
))
640 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm
, domain
))
646 configdn
= "CN=Configuration," + rootdn
648 schemadn
= "CN=Schema," + configdn
653 names
= ProvisionNames()
654 names
.rootdn
= rootdn
655 names
.domaindn
= domaindn
656 names
.configdn
= configdn
657 names
.schemadn
= schemadn
658 names
.ldapmanagerdn
= "CN=Manager," + rootdn
659 names
.dnsdomain
= dnsdomain
660 names
.domain
= domain
662 names
.netbiosname
= netbiosname
663 names
.hostname
= hostname
664 names
.sitename
= sitename
665 names
.serverdn
= "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
666 netbiosname
, sitename
, configdn
)
671 def make_smbconf(smbconf
, hostname
, domain
, realm
, serverrole
,
672 targetdir
, sid_generator
="internal", eadb
=False, lp
=None):
673 """Create a new smb.conf file based on a couple of basic settings.
675 assert smbconf
is not None
677 hostname
= socket
.gethostname().split(".")[0]
678 netbiosname
= hostname
.upper()
679 # remove forbidden chars
681 for x
in netbiosname
:
682 if x
.isalnum() or x
in VALID_NETBIOS_CHARS
:
683 newnbname
= "%s%c" % (newnbname
, x
)
684 #force the length to be <16
685 netbiosname
= newnbname
[0:15]
687 netbiosname
= hostname
.upper()
689 if serverrole
is None:
690 serverrole
= "standalone"
692 assert serverrole
in ("domain controller", "member server", "standalone")
693 if serverrole
== "domain controller":
695 elif serverrole
== "member server":
696 smbconfsuffix
= "member"
697 elif serverrole
== "standalone":
698 smbconfsuffix
= "standalone"
700 if sid_generator
is None:
701 sid_generator
= "internal"
703 assert domain
is not None
704 domain
= domain
.upper()
706 assert realm
is not None
707 realm
= realm
.upper()
710 lp
= samba
.param
.LoadParm()
711 #Load non-existant file
712 if os
.path
.exists(smbconf
):
714 if eadb
and not lp
.get("posix:eadb"):
715 if targetdir
is not None:
716 privdir
= os
.path
.join(targetdir
, "private")
718 privdir
= lp
.get("private dir")
719 lp
.set("posix:eadb", os
.path
.abspath(os
.path
.join(privdir
, "eadb.tdb")))
721 if targetdir
is not None:
722 privatedir_line
= "private dir = " + os
.path
.abspath(os
.path
.join(targetdir
, "private"))
723 lockdir_line
= "lock dir = " + os
.path
.abspath(targetdir
)
724 statedir_line
= "state directory = " + os
.path
.abspath(targetdir
)
725 cachedir_line
= "cache directory = " + os
.path
.abspath(targetdir
)
727 lp
.set("lock dir", os
.path
.abspath(targetdir
))
728 lp
.set("state directory", os
.path
.abspath(targetdir
))
729 lp
.set("cache directory", os
.path
.abspath(targetdir
))
736 sysvol
= os
.path
.join(lp
.get("state directory"), "sysvol")
737 netlogon
= os
.path
.join(sysvol
, realm
.lower(), "scripts")
739 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix
),
741 "NETBIOS_NAME": netbiosname
,
744 "SERVERROLE": serverrole
,
745 "NETLOGONPATH": netlogon
,
746 "SYSVOLPATH": sysvol
,
747 "PRIVATEDIR_LINE": privatedir_line
,
748 "LOCKDIR_LINE": lockdir_line
,
749 "STATEDIR_LINE": statedir_line
,
750 "CACHEDIR_LINE": cachedir_line
753 # reload the smb.conf
756 # and dump it without any values that are the default
757 # this ensures that any smb.conf parameters that were set
758 # on the provision/join command line are set in the resulting smb.conf
759 f
= open(smbconf
, mode
='w')
765 def setup_name_mappings(idmap
, sid
, root_uid
, nobody_uid
,
766 users_gid
, wheel_gid
):
767 """setup reasonable name mappings for sam names to unix names.
769 :param samdb: SamDB object.
770 :param idmap: IDmap db object.
771 :param sid: The domain sid.
772 :param domaindn: The domain DN.
773 :param root_uid: uid of the UNIX root user.
774 :param nobody_uid: uid of the UNIX nobody user.
775 :param users_gid: gid of the UNIX users group.
776 :param wheel_gid: gid of the UNIX wheel group.
778 idmap
.setup_name_mapping("S-1-5-7", idmap
.TYPE_UID
, nobody_uid
)
779 idmap
.setup_name_mapping("S-1-5-32-544", idmap
.TYPE_GID
, wheel_gid
)
781 idmap
.setup_name_mapping(sid
+ "-500", idmap
.TYPE_UID
, root_uid
)
782 idmap
.setup_name_mapping(sid
+ "-513", idmap
.TYPE_GID
, users_gid
)
785 def setup_samdb_partitions(samdb_path
, logger
, lp
, session_info
,
786 provision_backend
, names
, schema
, serverrole
,
788 """Setup the partitions for the SAM database.
790 Alternatively, provision() may call this, and then populate the database.
792 :note: This will wipe the Sam Database!
794 :note: This function always removes the local SAM LDB file. The erase
795 parameter controls whether to erase the existing data, which
796 may not be stored locally but in LDAP.
799 assert session_info
is not None
801 # We use options=["modules:"] to stop the modules loading - we
802 # just want to wipe and re-initialise the database, not start it up
805 os
.unlink(samdb_path
)
809 samdb
= Ldb(url
=samdb_path
, session_info
=session_info
,
810 lp
=lp
, options
=["modules:"])
812 ldap_backend_line
= "# No LDAP backend"
813 if provision_backend
.type is not "ldb":
814 ldap_backend_line
= "ldapBackend: %s" % provision_backend
.ldap_uri
816 samdb
.transaction_start()
818 logger
.info("Setting up sam.ldb partitions and settings")
819 setup_add_ldif(samdb
, setup_path("provision_partitions.ldif"), {
820 "LDAP_BACKEND_LINE": ldap_backend_line
824 setup_add_ldif(samdb
, setup_path("provision_init.ldif"), {
825 "BACKEND_TYPE": provision_backend
.type,
826 "SERVER_ROLE": serverrole
829 logger
.info("Setting up sam.ldb rootDSE")
830 setup_samdb_rootdse(samdb
, names
)
832 samdb
.transaction_cancel()
835 samdb
.transaction_commit()
838 def secretsdb_self_join(secretsdb
, domain
,
839 netbiosname
, machinepass
, domainsid
=None,
840 realm
=None, dnsdomain
=None,
842 key_version_number
=1,
843 secure_channel_type
=SEC_CHAN_WKSTA
):
844 """Add domain join-specific bits to a secrets database.
846 :param secretsdb: Ldb Handle to the secrets database
847 :param machinepass: Machine password
849 attrs
= ["whenChanged",
856 if realm
is not None:
857 if dnsdomain
is None:
858 dnsdomain
= realm
.lower()
859 dnsname
= '%s.%s' % (netbiosname
.lower(), dnsdomain
.lower())
862 shortname
= netbiosname
.lower()
864 # We don't need to set msg["flatname"] here, because rdn_name will handle
865 # it, and it causes problems for modifies anyway
866 msg
= ldb
.Message(ldb
.Dn(secretsdb
, "flatname=%s,cn=Primary Domains" % domain
))
867 msg
["secureChannelType"] = [str(secure_channel_type
)]
868 msg
["objectClass"] = ["top", "primaryDomain"]
869 if dnsname
is not None:
870 msg
["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
871 msg
["realm"] = [realm
]
872 msg
["saltPrincipal"] = ["host/%s@%s" % (dnsname
, realm
.upper())]
873 msg
["msDS-KeyVersionNumber"] = [str(key_version_number
)]
874 msg
["privateKeytab"] = ["secrets.keytab"]
876 msg
["secret"] = [machinepass
]
877 msg
["samAccountName"] = ["%s$" % netbiosname
]
878 msg
["secureChannelType"] = [str(secure_channel_type
)]
879 if domainsid
is not None:
880 msg
["objectSid"] = [ndr_pack(domainsid
)]
882 # This complex expression tries to ensure that we don't have more
883 # than one record for this SID, realm or netbios domain at a time,
884 # but we don't delete the old record that we are about to modify,
885 # because that would delete the keytab and previous password.
886 res
= secretsdb
.search(base
="cn=Primary Domains", attrs
=attrs
,
887 expression
=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(dn=%s)))" % (domain
, realm
, str(domainsid
), str(msg
.dn
))),
888 scope
=ldb
.SCOPE_ONELEVEL
)
891 secretsdb
.delete(del_msg
.dn
)
893 res
= secretsdb
.search(base
=msg
.dn
, attrs
=attrs
, scope
=ldb
.SCOPE_BASE
)
896 msg
["priorSecret"] = [res
[0]["secret"][0]]
897 msg
["priorWhenChanged"] = [res
[0]["whenChanged"][0]]
900 msg
["privateKeytab"] = [res
[0]["privateKeytab"][0]]
905 msg
["krb5Keytab"] = [res
[0]["krb5Keytab"][0]]
911 msg
[el
].set_flags(ldb
.FLAG_MOD_REPLACE
)
912 secretsdb
.modify(msg
)
913 secretsdb
.rename(res
[0].dn
, msg
.dn
)
915 spn
= [ 'HOST/%s' % shortname
]
916 if secure_channel_type
== SEC_CHAN_BDC
and dnsname
is not None:
917 # we are a domain controller then we add servicePrincipalName
918 # entries for the keytab code to update.
919 spn
.extend([ 'HOST/%s' % dnsname
])
920 msg
["servicePrincipalName"] = spn
925 def setup_secretsdb(paths
, session_info
, backend_credentials
, lp
):
926 """Setup the secrets database.
928 :note: This function does not handle exceptions and transaction on purpose,
929 it's up to the caller to do this job.
931 :param path: Path to the secrets database.
932 :param session_info: Session info.
933 :param credentials: Credentials
934 :param lp: Loadparm context
935 :return: LDB handle for the created secrets database
937 if os
.path
.exists(paths
.secrets
):
938 os
.unlink(paths
.secrets
)
940 keytab_path
= os
.path
.join(paths
.private_dir
, paths
.keytab
)
941 if os
.path
.exists(keytab_path
):
942 os
.unlink(keytab_path
)
944 dns_keytab_path
= os
.path
.join(paths
.private_dir
, paths
.dns_keytab
)
945 if os
.path
.exists(dns_keytab_path
):
946 os
.unlink(dns_keytab_path
)
950 secrets_ldb
= Ldb(path
, session_info
=session_info
,
953 secrets_ldb
.load_ldif_file_add(setup_path("secrets_init.ldif"))
954 secrets_ldb
= Ldb(path
, session_info
=session_info
,
956 secrets_ldb
.transaction_start()
958 secrets_ldb
.load_ldif_file_add(setup_path("secrets.ldif"))
960 if (backend_credentials
is not None and
961 backend_credentials
.authentication_requested()):
962 if backend_credentials
.get_bind_dn() is not None:
963 setup_add_ldif(secrets_ldb
,
964 setup_path("secrets_simple_ldap.ldif"), {
965 "LDAPMANAGERDN": backend_credentials
.get_bind_dn(),
966 "LDAPMANAGERPASS_B64": b64encode(backend_credentials
.get_password())
969 setup_add_ldif(secrets_ldb
,
970 setup_path("secrets_sasl_ldap.ldif"), {
971 "LDAPADMINUSER": backend_credentials
.get_username(),
972 "LDAPADMINREALM": backend_credentials
.get_realm(),
973 "LDAPADMINPASS_B64": b64encode(backend_credentials
.get_password())
976 secrets_ldb
.transaction_cancel()
982 def setup_privileges(path
, session_info
, lp
):
983 """Setup the privileges database.
985 :param path: Path to the privileges database.
986 :param session_info: Session info.
987 :param credentials: Credentials
988 :param lp: Loadparm context
989 :return: LDB handle for the created secrets database
991 if os
.path
.exists(path
):
993 privilege_ldb
= Ldb(path
, session_info
=session_info
, lp
=lp
)
994 privilege_ldb
.erase()
995 privilege_ldb
.load_ldif_file_add(setup_path("provision_privilege.ldif"))
998 def setup_registry(path
, session_info
, lp
):
999 """Setup the registry.
1001 :param path: Path to the registry database
1002 :param session_info: Session information
1003 :param credentials: Credentials
1004 :param lp: Loadparm context
1006 reg
= samba
.registry
.Registry()
1007 hive
= samba
.registry
.open_ldb(path
, session_info
=session_info
, lp_ctx
=lp
)
1008 reg
.mount_hive(hive
, samba
.registry
.HKEY_LOCAL_MACHINE
)
1009 provision_reg
= setup_path("provision.reg")
1010 assert os
.path
.exists(provision_reg
)
1011 reg
.diff_apply(provision_reg
)
1014 def setup_idmapdb(path
, session_info
, lp
):
1015 """Setup the idmap database.
1017 :param path: path to the idmap database
1018 :param session_info: Session information
1019 :param credentials: Credentials
1020 :param lp: Loadparm context
1022 if os
.path
.exists(path
):
1025 idmap_ldb
= IDmapDB(path
, session_info
=session_info
, lp
=lp
)
1027 idmap_ldb
.load_ldif_file_add(setup_path("idmap_init.ldif"))
1031 def setup_samdb_rootdse(samdb
, names
):
1032 """Setup the SamDB rootdse.
1034 :param samdb: Sam Database handle
1036 setup_add_ldif(samdb
, setup_path("provision_rootdse_add.ldif"), {
1037 "SCHEMADN": names
.schemadn
,
1038 "DOMAINDN": names
.domaindn
,
1039 "ROOTDN" : names
.rootdn
,
1040 "CONFIGDN": names
.configdn
,
1041 "SERVERDN": names
.serverdn
,
1045 def setup_self_join(samdb
, admin_session_info
, names
, fill
, machinepass
, dnspass
,
1046 domainsid
, next_rid
, invocationid
,
1047 policyguid
, policyguid_dc
, domainControllerFunctionality
,
1048 ntdsguid
, dc_rid
=None):
1049 """Join a host to its own domain."""
1050 assert isinstance(invocationid
, str)
1051 if ntdsguid
is not None:
1052 ntdsguid_line
= "objectGUID: %s\n"%ntdsguid
1059 setup_add_ldif(samdb
, setup_path("provision_self_join.ldif"), {
1060 "CONFIGDN": names
.configdn
,
1061 "SCHEMADN": names
.schemadn
,
1062 "DOMAINDN": names
.domaindn
,
1063 "SERVERDN": names
.serverdn
,
1064 "INVOCATIONID": invocationid
,
1065 "NETBIOSNAME": names
.netbiosname
,
1066 "DNSNAME": "%s.%s" % (names
.hostname
, names
.dnsdomain
),
1067 "MACHINEPASS_B64": b64encode(machinepass
.encode('utf-16-le')),
1068 "DOMAINSID": str(domainsid
),
1069 "DCRID": str(dc_rid
),
1070 "SAMBA_VERSION_STRING": version
,
1071 "NTDSGUID": ntdsguid_line
,
1072 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1073 domainControllerFunctionality
),
1074 "RIDALLOCATIONSTART": str(next_rid
+ 100),
1075 "RIDALLOCATIONEND": str(next_rid
+ 100 + 499)})
1077 setup_add_ldif(samdb
, setup_path("provision_group_policy.ldif"), {
1078 "POLICYGUID": policyguid
,
1079 "POLICYGUID_DC": policyguid_dc
,
1080 "DNSDOMAIN": names
.dnsdomain
,
1081 "DOMAINDN": names
.domaindn
})
1083 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1084 if fill
== FILL_FULL
:
1085 setup_add_ldif(samdb
, setup_path("provision_self_join_config.ldif"), {
1086 "CONFIGDN": names
.configdn
,
1087 "SCHEMADN": names
.schemadn
,
1088 "DOMAINDN": names
.domaindn
,
1089 "SERVERDN": names
.serverdn
,
1090 "INVOCATIONID": invocationid
,
1091 "NETBIOSNAME": names
.netbiosname
,
1092 "DNSNAME": "%s.%s" % (names
.hostname
, names
.dnsdomain
),
1093 "MACHINEPASS_B64": b64encode(machinepass
.encode('utf-16-le')),
1094 "DOMAINSID": str(domainsid
),
1095 "DCRID": str(dc_rid
),
1096 "SAMBA_VERSION_STRING": version
,
1097 "NTDSGUID": ntdsguid_line
,
1098 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1099 domainControllerFunctionality
)})
1101 # Setup fSMORoleOwner entries to point at the newly created DC entry
1102 setup_modify_ldif(samdb
, setup_path("provision_self_join_modify_config.ldif"), {
1103 "CONFIGDN": names
.configdn
,
1104 "SCHEMADN": names
.schemadn
,
1105 "DEFAULTSITE": names
.sitename
,
1106 "NETBIOSNAME": names
.netbiosname
,
1107 "SERVERDN": names
.serverdn
,
1110 system_session_info
= system_session()
1111 samdb
.set_session_info(system_session_info
)
1112 # Setup fSMORoleOwner entries to point at the newly created DC entry
1114 # to modify a serverReference under cn=config when we are a subdomain, we must
1115 # be system due to ACLs
1116 setup_modify_ldif(samdb
, setup_path("provision_self_join_modify.ldif"), {
1117 "DOMAINDN": names
.domaindn
,
1118 "SERVERDN": names
.serverdn
,
1119 "NETBIOSNAME": names
.netbiosname
,
1122 samdb
.set_session_info(admin_session_info
)
1124 # This is Samba4 specific and should be replaced by the correct
1125 # DNS AD-style setup
1126 setup_add_ldif(samdb
, setup_path("provision_dns_add_samba.ldif"), {
1127 "DNSDOMAIN": names
.dnsdomain
,
1128 "DOMAINDN": names
.domaindn
,
1129 "DNSPASS_B64": b64encode(dnspass
.encode('utf-16-le')),
1130 "HOSTNAME" : names
.hostname
,
1131 "DNSNAME" : '%s.%s' % (
1132 names
.netbiosname
.lower(), names
.dnsdomain
.lower())
1136 def getpolicypath(sysvolpath
, dnsdomain
, guid
):
1137 """Return the physical path of policy given its guid.
1139 :param sysvolpath: Path to the sysvol folder
1140 :param dnsdomain: DNS name of the AD domain
1141 :param guid: The GUID of the policy
1142 :return: A string with the complete path to the policy folder
1146 guid
= "{%s}" % guid
1147 policy_path
= os
.path
.join(sysvolpath
, dnsdomain
, "Policies", guid
)
1151 def create_gpo_struct(policy_path
):
1152 if not os
.path
.exists(policy_path
):
1153 os
.makedirs(policy_path
, 0775)
1154 open(os
.path
.join(policy_path
, "GPT.INI"), 'w').write(
1155 "[General]\r\nVersion=0")
1156 p
= os
.path
.join(policy_path
, "MACHINE")
1157 if not os
.path
.exists(p
):
1158 os
.makedirs(p
, 0775)
1159 p
= os
.path
.join(policy_path
, "USER")
1160 if not os
.path
.exists(p
):
1161 os
.makedirs(p
, 0775)
1164 def create_default_gpo(sysvolpath
, dnsdomain
, policyguid
, policyguid_dc
):
1165 """Create the default GPO for a domain
1167 :param sysvolpath: Physical path for the sysvol folder
1168 :param dnsdomain: DNS domain name of the AD domain
1169 :param policyguid: GUID of the default domain policy
1170 :param policyguid_dc: GUID of the default domain controler policy
1172 policy_path
= getpolicypath(sysvolpath
,dnsdomain
,policyguid
)
1173 create_gpo_struct(policy_path
)
1175 policy_path
= getpolicypath(sysvolpath
,dnsdomain
,policyguid_dc
)
1176 create_gpo_struct(policy_path
)
1179 def setup_samdb(path
, session_info
, provision_backend
, lp
, names
,
1180 logger
, fill
, serverrole
, schema
, am_rodc
=False):
1181 """Setup a complete SAM Database.
1183 :note: This will wipe the main SAM database file!
1186 # Also wipes the database
1187 setup_samdb_partitions(path
, logger
=logger
, lp
=lp
,
1188 provision_backend
=provision_backend
, session_info
=session_info
,
1189 names
=names
, serverrole
=serverrole
, schema
=schema
)
1191 # Load the database, but don's load the global schema and don't connect
1193 samdb
= SamDB(session_info
=session_info
, url
=None, auto_connect
=False,
1194 credentials
=provision_backend
.credentials
, lp
=lp
,
1195 global_schema
=False, am_rodc
=am_rodc
)
1197 logger
.info("Pre-loading the Samba 4 and AD schema")
1199 # Load the schema from the one we computed earlier
1200 samdb
.set_schema(schema
)
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 # And now we can connect to the DB - the schema won't be loaded from the
1212 def fill_samdb(samdb
, lp
, names
,
1213 logger
, domainsid
, domainguid
, policyguid
, policyguid_dc
, fill
,
1214 adminpass
, krbtgtpass
, machinepass
, invocationid
, dnspass
, ntdsguid
,
1215 serverrole
, am_rodc
=False, dom_for_fun_level
=None, schema
=None,
1216 next_rid
=None, dc_rid
=None):
1218 if next_rid
is None:
1221 # Provision does not make much sense values larger than 1000000000
1222 # as the upper range of the rIDAvailablePool is 1073741823 and
1223 # we don't want to create a domain that cannot allocate rids.
1224 if next_rid
< 1000 or next_rid
> 1000000000:
1225 error
= "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid
)
1226 error
+= "the valid range is %u-%u. The default is %u." % (
1227 1000, 1000000000, 1000)
1228 raise ProvisioningError(error
)
1230 # ATTENTION: Do NOT change these default values without discussion with the
1231 # team and/or release manager. They have a big impact on the whole program!
1232 domainControllerFunctionality
= DS_DOMAIN_FUNCTION_2008_R2
1234 if dom_for_fun_level
is None:
1235 dom_for_fun_level
= DS_DOMAIN_FUNCTION_2003
1237 if dom_for_fun_level
> domainControllerFunctionality
:
1238 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!")
1240 domainFunctionality
= dom_for_fun_level
1241 forestFunctionality
= dom_for_fun_level
1243 # Set the NTDS settings DN manually - in order to have it already around
1244 # before the provisioned tree exists and we connect
1245 samdb
.set_ntds_settings_dn("CN=NTDS Settings,%s" % names
.serverdn
)
1247 samdb
.transaction_start()
1249 # Set the domain functionality levels onto the database.
1250 # Various module (the password_hash module in particular) need
1251 # to know what level of AD we are emulating.
1253 # These will be fixed into the database via the database
1254 # modifictions below, but we need them set from the start.
1255 samdb
.set_opaque_integer("domainFunctionality", domainFunctionality
)
1256 samdb
.set_opaque_integer("forestFunctionality", forestFunctionality
)
1257 samdb
.set_opaque_integer("domainControllerFunctionality",
1258 domainControllerFunctionality
)
1260 samdb
.set_domain_sid(str(domainsid
))
1261 samdb
.set_invocation_id(invocationid
)
1263 logger
.info("Adding DomainDN: %s" % names
.domaindn
)
1265 # impersonate domain admin
1266 admin_session_info
= admin_session(lp
, str(domainsid
))
1267 samdb
.set_session_info(admin_session_info
)
1268 if domainguid
is not None:
1269 domainguid_line
= "objectGUID: %s\n-" % domainguid
1271 domainguid_line
= ""
1273 descr
= b64encode(get_domain_descriptor(domainsid
))
1274 setup_add_ldif(samdb
, setup_path("provision_basedn.ldif"), {
1275 "DOMAINDN": names
.domaindn
,
1276 "DOMAINSID": str(domainsid
),
1277 "DESCRIPTOR": descr
,
1278 "DOMAINGUID": domainguid_line
1281 setup_modify_ldif(samdb
, setup_path("provision_basedn_modify.ldif"), {
1282 "DOMAINDN": names
.domaindn
,
1283 "CREATTIME": str(samba
.unix2nttime(int(time
.time()))),
1284 "NEXTRID": str(next_rid
),
1285 "DEFAULTSITE": names
.sitename
,
1286 "CONFIGDN": names
.configdn
,
1287 "POLICYGUID": policyguid
,
1288 "DOMAIN_FUNCTIONALITY": str(domainFunctionality
),
1289 "SAMBA_VERSION_STRING": version
1292 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1293 if fill
== FILL_FULL
:
1294 logger
.info("Adding configuration container")
1295 descr
= b64encode(get_config_descriptor(domainsid
))
1296 setup_add_ldif(samdb
, setup_path("provision_configuration_basedn.ldif"), {
1297 "CONFIGDN": names
.configdn
,
1298 "DESCRIPTOR": descr
,
1301 # The LDIF here was created when the Schema object was constructed
1302 logger
.info("Setting up sam.ldb schema")
1303 samdb
.add_ldif(schema
.schema_dn_add
, controls
=["relax:0"])
1304 samdb
.modify_ldif(schema
.schema_dn_modify
)
1305 samdb
.write_prefixes_from_schema()
1306 samdb
.add_ldif(schema
.schema_data
, controls
=["relax:0"])
1307 setup_add_ldif(samdb
, setup_path("aggregate_schema.ldif"),
1308 {"SCHEMADN": names
.schemadn
})
1310 # Now register this container in the root of the forest
1311 msg
= ldb
.Message(ldb
.Dn(samdb
, names
.domaindn
))
1312 msg
["subRefs"] = ldb
.MessageElement(names
.configdn
, ldb
.FLAG_MOD_ADD
,
1316 samdb
.transaction_cancel()
1319 samdb
.transaction_commit()
1321 samdb
.transaction_start()
1323 samdb
.invocation_id
= invocationid
1325 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1326 if fill
== FILL_FULL
:
1327 logger
.info("Setting up sam.ldb configuration data")
1328 setup_add_ldif(samdb
, setup_path("provision_configuration.ldif"), {
1329 "CONFIGDN": names
.configdn
,
1330 "NETBIOSNAME": names
.netbiosname
,
1331 "DEFAULTSITE": names
.sitename
,
1332 "DNSDOMAIN": names
.dnsdomain
,
1333 "DOMAIN": names
.domain
,
1334 "SCHEMADN": names
.schemadn
,
1335 "DOMAINDN": names
.domaindn
,
1336 "SERVERDN": names
.serverdn
,
1337 "FOREST_FUNCTIONALITY": str(forestFunctionality
),
1338 "DOMAIN_FUNCTIONALITY": str(domainFunctionality
),
1341 logger
.info("Setting up display specifiers")
1342 display_specifiers_ldif
= read_ms_ldif(
1343 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1344 display_specifiers_ldif
= substitute_var(display_specifiers_ldif
,
1345 {"CONFIGDN": names
.configdn
})
1346 check_all_substituted(display_specifiers_ldif
)
1347 samdb
.add_ldif(display_specifiers_ldif
)
1349 logger
.info("Adding users container")
1350 setup_add_ldif(samdb
, setup_path("provision_users_add.ldif"), {
1351 "DOMAINDN": names
.domaindn
})
1352 logger
.info("Modifying users container")
1353 setup_modify_ldif(samdb
, setup_path("provision_users_modify.ldif"), {
1354 "DOMAINDN": names
.domaindn
})
1355 logger
.info("Adding computers container")
1356 setup_add_ldif(samdb
, setup_path("provision_computers_add.ldif"), {
1357 "DOMAINDN": names
.domaindn
})
1358 logger
.info("Modifying computers container")
1359 setup_modify_ldif(samdb
,
1360 setup_path("provision_computers_modify.ldif"), {
1361 "DOMAINDN": names
.domaindn
})
1362 logger
.info("Setting up sam.ldb data")
1363 setup_add_ldif(samdb
, setup_path("provision.ldif"), {
1364 "CREATTIME": str(samba
.unix2nttime(int(time
.time()))),
1365 "DOMAINDN": names
.domaindn
,
1366 "NETBIOSNAME": names
.netbiosname
,
1367 "DEFAULTSITE": names
.sitename
,
1368 "CONFIGDN": names
.configdn
,
1369 "SERVERDN": names
.serverdn
,
1370 "RIDAVAILABLESTART": str(next_rid
+ 600),
1371 "POLICYGUID_DC": policyguid_dc
1374 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1375 if fill
== FILL_FULL
:
1376 setup_modify_ldif(samdb
,
1377 setup_path("provision_configuration_references.ldif"), {
1378 "CONFIGDN": names
.configdn
,
1379 "SCHEMADN": names
.schemadn
})
1381 logger
.info("Setting up well known security principals")
1382 setup_add_ldif(samdb
, setup_path("provision_well_known_sec_princ.ldif"), {
1383 "CONFIGDN": names
.configdn
,
1386 if fill
== FILL_FULL
or fill
== FILL_SUBDOMAIN
:
1387 setup_modify_ldif(samdb
,
1388 setup_path("provision_basedn_references.ldif"),
1389 {"DOMAINDN": names
.domaindn
})
1391 logger
.info("Setting up sam.ldb users and groups")
1392 setup_add_ldif(samdb
, setup_path("provision_users.ldif"), {
1393 "DOMAINDN": names
.domaindn
,
1394 "DOMAINSID": str(domainsid
),
1395 "ADMINPASS_B64": b64encode(adminpass
.encode('utf-16-le')),
1396 "KRBTGTPASS_B64": b64encode(krbtgtpass
.encode('utf-16-le'))
1399 logger
.info("Setting up self join")
1400 setup_self_join(samdb
, admin_session_info
, names
=names
, fill
=fill
, invocationid
=invocationid
,
1402 machinepass
=machinepass
,
1403 domainsid
=domainsid
,
1406 policyguid
=policyguid
,
1407 policyguid_dc
=policyguid_dc
,
1408 domainControllerFunctionality
=domainControllerFunctionality
,
1411 ntds_dn
= "CN=NTDS Settings,%s" % names
.serverdn
1412 names
.ntdsguid
= samdb
.searchone(basedn
=ntds_dn
,
1413 attribute
="objectGUID", expression
="", scope
=ldb
.SCOPE_BASE
)
1414 assert isinstance(names
.ntdsguid
, str)
1416 samdb
.transaction_cancel()
1419 samdb
.transaction_commit()
1424 FILL_SUBDOMAIN
= "SUBDOMAIN"
1425 FILL_NT4SYNC
= "NT4SYNC"
1427 SYSVOL_ACL
= "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1428 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)"
1431 def set_dir_acl(path
, acl
, lp
, domsid
):
1432 setntacl(lp
, path
, acl
, domsid
)
1433 for root
, dirs
, files
in os
.walk(path
, topdown
=False):
1435 setntacl(lp
, os
.path
.join(root
, name
), acl
, domsid
)
1437 setntacl(lp
, os
.path
.join(root
, name
), acl
, domsid
)
1440 def set_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
):
1441 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1444 :param sysvol: Physical path for the sysvol folder
1445 :param dnsdomain: The DNS name of the domain
1446 :param domainsid: The SID of the domain
1447 :param domaindn: The DN of the domain (ie. DC=...)
1448 :param samdb: An LDB object on the SAM db
1449 :param lp: an LP object
1452 # Set ACL for GPO root folder
1453 root_policy_path
= os
.path
.join(sysvol
, dnsdomain
, "Policies")
1454 setntacl(lp
, root_policy_path
, POLICIES_ACL
, str(domainsid
))
1456 res
= samdb
.search(base
="CN=Policies,CN=System,%s"%(domaindn),
1457 attrs
=["cn", "nTSecurityDescriptor"],
1458 expression
="", scope
=ldb
.SCOPE_ONELEVEL
)
1461 acl
= ndr_unpack(security
.descriptor
,
1462 str(policy
["nTSecurityDescriptor"])).as_sddl()
1463 policy_path
= getpolicypath(sysvol
, dnsdomain
, str(policy
["cn"]))
1464 set_dir_acl(policy_path
, dsacl2fsacl(acl
, str(domainsid
)), lp
,
1468 def setsysvolacl(samdb
, netlogon
, sysvol
, gid
, domainsid
, dnsdomain
, domaindn
,
1470 """Set the ACL for the sysvol share and the subfolders
1472 :param samdb: An LDB object on the SAM db
1473 :param netlogon: Physical path for the netlogon folder
1474 :param sysvol: Physical path for the sysvol folder
1475 :param gid: The GID of the "Domain adminstrators" group
1476 :param domainsid: The SID of the domain
1477 :param dnsdomain: The DNS name of the domain
1478 :param domaindn: The DN of the domain (ie. DC=...)
1482 os
.chown(sysvol
, -1, gid
)
1488 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1489 setntacl(lp
,sysvol
, SYSVOL_ACL
, str(domainsid
))
1490 for root
, dirs
, files
in os
.walk(sysvol
, topdown
=False):
1493 os
.chown(os
.path
.join(root
, name
), -1, gid
)
1494 setntacl(lp
, os
.path
.join(root
, name
), SYSVOL_ACL
, str(domainsid
))
1497 os
.chown(os
.path
.join(root
, name
), -1, gid
)
1498 setntacl(lp
, os
.path
.join(root
, name
), SYSVOL_ACL
, str(domainsid
))
1500 # Set acls on Policy folder and policies folders
1501 set_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
)
1504 def interface_ips_v4(lp
):
1505 '''return only IPv4 IPs'''
1506 ips
= samba
.interface_ips(lp
, False)
1509 if i
.find(':') == -1:
1513 def interface_ips_v6(lp
, linklocal
=False):
1514 '''return only IPv6 IPs'''
1515 ips
= samba
.interface_ips(lp
, False)
1518 if i
.find(':') != -1 and (linklocal
or i
.find('%') == -1):
1523 def provision_fill(samdb
, secrets_ldb
, logger
, names
, paths
,
1524 domainsid
, schema
=None,
1525 targetdir
=None, samdb_fill
=FILL_FULL
,
1526 hostip
=None, hostip6
=None,
1527 next_rid
=1000, dc_rid
=None, adminpass
=None, krbtgtpass
=None,
1528 domainguid
=None, policyguid
=None, policyguid_dc
=None,
1529 invocationid
=None, machinepass
=None, ntdsguid
=None,
1530 dns_backend
=None, dnspass
=None,
1531 serverrole
=None, dom_for_fun_level
=None,
1532 am_rodc
=False, lp
=None):
1533 # create/adapt the group policy GUIDs
1534 # Default GUID for default policy are described at
1535 # "How Core Group Policy Works"
1536 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1537 if policyguid
is None:
1538 policyguid
= DEFAULT_POLICY_GUID
1539 policyguid
= policyguid
.upper()
1540 if policyguid_dc
is None:
1541 policyguid_dc
= DEFAULT_DC_POLICY_GUID
1542 policyguid_dc
= policyguid_dc
.upper()
1544 if invocationid
is None:
1545 invocationid
= str(uuid
.uuid4())
1547 if adminpass
is None:
1548 adminpass
= samba
.generate_random_password(12, 32)
1549 if krbtgtpass
is None:
1550 krbtgtpass
= samba
.generate_random_password(128, 255)
1551 if machinepass
is None:
1552 machinepass
= samba
.generate_random_password(128, 255)
1554 dnspass
= samba
.generate_random_password(128, 255)
1556 samdb
= fill_samdb(samdb
, lp
, names
, logger
=logger
,
1557 domainsid
=domainsid
, schema
=schema
, domainguid
=domainguid
,
1558 policyguid
=policyguid
, policyguid_dc
=policyguid_dc
,
1559 fill
=samdb_fill
, adminpass
=adminpass
, krbtgtpass
=krbtgtpass
,
1560 invocationid
=invocationid
, machinepass
=machinepass
,
1561 dnspass
=dnspass
, ntdsguid
=ntdsguid
, serverrole
=serverrole
,
1562 dom_for_fun_level
=dom_for_fun_level
, am_rodc
=am_rodc
,
1563 next_rid
=next_rid
, dc_rid
=dc_rid
)
1565 if serverrole
== "domain controller":
1566 # Set up group policies (domain policy and domain controller
1568 create_default_gpo(paths
.sysvol
, names
.dnsdomain
, policyguid
,
1570 setsysvolacl(samdb
, paths
.netlogon
, paths
.sysvol
, paths
.wheel_gid
,
1571 domainsid
, names
.dnsdomain
, names
.domaindn
, lp
)
1573 secretsdb_self_join(secrets_ldb
, domain
=names
.domain
,
1574 realm
=names
.realm
, dnsdomain
=names
.dnsdomain
,
1575 netbiosname
=names
.netbiosname
, domainsid
=domainsid
,
1576 machinepass
=machinepass
, secure_channel_type
=SEC_CHAN_BDC
)
1578 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1579 # In future, this might be determined from some configuration
1580 kerberos_enctypes
= str(ENC_ALL_TYPES
)
1583 msg
= ldb
.Message(ldb
.Dn(samdb
,
1584 samdb
.searchone("distinguishedName",
1585 expression
="samAccountName=%s$" % names
.netbiosname
,
1586 scope
=ldb
.SCOPE_SUBTREE
)))
1587 msg
["msDS-SupportedEncryptionTypes"] = ldb
.MessageElement(
1588 elements
=kerberos_enctypes
, flags
=ldb
.FLAG_MOD_REPLACE
,
1589 name
="msDS-SupportedEncryptionTypes")
1591 except ldb
.LdbError
, (enum
, estr
):
1592 if enum
!= ldb
.ERR_NO_SUCH_ATTRIBUTE
:
1593 # It might be that this attribute does not exist in this schema
1596 setup_ad_dns(samdb
, secrets_ldb
, names
, paths
, logger
, hostip
=hostip
,
1597 hostip6
=hostip6
, dns_backend
=dns_backend
,
1598 dnspass
=dnspass
, os_level
=dom_for_fun_level
)
1600 domainguid
= samdb
.searchone(basedn
=samdb
.get_default_basedn(),
1601 attribute
="objectGUID")
1602 assert isinstance(domainguid
, str)
1604 create_dns_dir(logger
, paths
)
1606 # Only make a zone file on the first DC, it should be
1607 # replicated with DNS replication
1608 if dns_backend
== "BIND9_FLATFILE":
1609 create_zone_file(lp
, logger
, paths
, targetdir
,
1610 dnsdomain
=names
.dnsdomain
, hostip
=hostip
, hostip6
=hostip6
,
1611 hostname
=names
.hostname
, realm
=names
.realm
,
1612 domainguid
=domainguid
, ntdsguid
=names
.ntdsguid
)
1614 create_named_conf(paths
, realm
=names
.realm
,
1615 dnsdomain
=names
.dnsdomain
, dns_backend
=dns_backend
)
1617 create_named_txt(paths
.namedtxt
,
1618 realm
=names
.realm
, dnsdomain
=names
.dnsdomain
,
1619 dnsname
= "%s.%s" % (names
.hostname
, names
.dnsdomain
),
1620 private_dir
=paths
.private_dir
,
1621 keytab_name
=paths
.dns_keytab
)
1622 logger
.info("See %s for an example configuration include file for BIND", paths
.namedconf
)
1623 logger
.info("and %s for further documentation required for secure DNS "
1624 "updates", paths
.namedtxt
)
1626 lastProvisionUSNs
= get_last_provision_usn(samdb
)
1627 maxUSN
= get_max_usn(samdb
, str(names
.rootdn
))
1628 if lastProvisionUSNs
is not None:
1629 update_provision_usn(samdb
, 0, maxUSN
, invocationid
, 1)
1631 set_provision_usn(samdb
, 0, maxUSN
, invocationid
)
1633 logger
.info("Setting up sam.ldb rootDSE marking as synchronized")
1634 setup_modify_ldif(samdb
, setup_path("provision_rootdse_modify.ldif"),
1635 { 'NTDSGUID' : names
.ntdsguid
})
1637 # fix any dangling GUIDs from the provision
1638 logger
.info("Fixing provision GUIDs")
1639 chk
= dbcheck(samdb
, samdb_schema
=samdb
, verbose
=False, fix
=True, yes
=True, quiet
=True)
1640 samdb
.transaction_start()
1641 # a small number of GUIDs are missing because of ordering issues in the
1643 for schema_obj
in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1644 chk
.check_database(DN
="%s,%s" % (schema_obj
, names
.schemadn
),
1645 scope
=ldb
.SCOPE_BASE
, attrs
=['defaultObjectCategory'])
1646 chk
.check_database(DN
="CN=IP Security,CN=System,%s" % names
.domaindn
,
1647 scope
=ldb
.SCOPE_ONELEVEL
,
1648 attrs
=['ipsecOwnersReference',
1649 'ipsecFilterReference',
1650 'ipsecISAKMPReference',
1651 'ipsecNegotiationPolicyReference',
1652 'ipsecNFAReference'])
1653 samdb
.transaction_commit()
1656 def provision(logger
, session_info
, credentials
, smbconf
=None,
1657 targetdir
=None, samdb_fill
=FILL_FULL
, realm
=None, rootdn
=None,
1658 domaindn
=None, schemadn
=None, configdn
=None, serverdn
=None,
1659 domain
=None, hostname
=None, hostip
=None, hostip6
=None, domainsid
=None,
1660 next_rid
=1000, dc_rid
=None, adminpass
=None, ldapadminpass
=None, krbtgtpass
=None,
1661 domainguid
=None, policyguid
=None, policyguid_dc
=None,
1662 dns_backend
=None, dnspass
=None,
1663 invocationid
=None, machinepass
=None, ntdsguid
=None,
1664 root
=None, nobody
=None, users
=None, wheel
=None, backup
=None, aci
=None,
1665 serverrole
=None, dom_for_fun_level
=None, ldap_backend_extra_port
=None,
1666 ldap_backend_forced_uri
=None, backend_type
=None, sitename
=None,
1667 ol_mmr_urls
=None, ol_olc
=None, setup_ds_path
=None, slapd_path
=None,
1668 nosync
=False, ldap_dryrun_mode
=False, useeadb
=False, am_rodc
=False,
1672 :note: caution, this wipes all existing data!
1675 if ldapadminpass
is None:
1676 # Make a new, random password between Samba and it's LDAP server
1677 ldapadminpass
=samba
.generate_random_password(128, 255)
1679 if backend_type
is None:
1680 backend_type
= "ldb"
1682 if domainsid
is None:
1683 domainsid
= security
.random_sid()
1685 domainsid
= security
.dom_sid(domainsid
)
1687 sid_generator
= "internal"
1688 if backend_type
== "fedora-ds":
1689 sid_generator
= "backend"
1691 root_uid
= findnss_uid([root
or "root"])
1692 nobody_uid
= findnss_uid([nobody
or "nobody"])
1693 users_gid
= findnss_gid([users
or "users", 'users', 'other', 'staff'])
1695 wheel_gid
= findnss_gid(["wheel", "adm"])
1697 wheel_gid
= findnss_gid([wheel
])
1699 bind_gid
= findnss_gid(["bind", "named"])
1703 if targetdir
is not None:
1704 smbconf
= os
.path
.join(targetdir
, "etc", "smb.conf")
1705 elif smbconf
is None:
1706 smbconf
= samba
.param
.default_path()
1707 if not os
.path
.exists(os
.path
.dirname(smbconf
)):
1708 os
.makedirs(os
.path
.dirname(smbconf
))
1710 # only install a new smb.conf if there isn't one there already
1711 if os
.path
.exists(smbconf
):
1712 # if Samba Team members can't figure out the weird errors
1713 # loading an empty smb.conf gives, then we need to be smarter.
1714 # Pretend it just didn't exist --abartlet
1715 data
= open(smbconf
, 'r').read()
1716 data
= data
.lstrip()
1717 if data
is None or data
== "":
1718 make_smbconf(smbconf
, hostname
, domain
, realm
,
1719 serverrole
, targetdir
, sid_generator
, useeadb
,
1722 make_smbconf(smbconf
, hostname
, domain
, realm
, serverrole
,
1723 targetdir
, sid_generator
, useeadb
, lp
=lp
)
1726 lp
= samba
.param
.LoadParm()
1728 names
= guess_names(lp
=lp
, hostname
=hostname
, domain
=domain
,
1729 dnsdomain
=realm
, serverrole
=serverrole
, domaindn
=domaindn
,
1730 configdn
=configdn
, schemadn
=schemadn
, serverdn
=serverdn
,
1731 sitename
=sitename
, rootdn
=rootdn
)
1732 paths
= provision_paths_from_lp(lp
, names
.dnsdomain
)
1734 paths
.bind_gid
= bind_gid
1735 paths
.wheel_gid
= wheel_gid
1738 logger
.info("Looking up IPv4 addresses")
1739 hostips
= interface_ips_v4(lp
)
1740 if len(hostips
) > 0:
1742 if len(hostips
) > 1:
1743 logger
.warning("More than one IPv4 address found. Using %s",
1745 if hostip
== "127.0.0.1":
1748 logger
.warning("No IPv4 address will be assigned")
1751 logger
.info("Looking up IPv6 addresses")
1752 hostips
= interface_ips_v6(lp
, linklocal
=False)
1754 hostip6
= hostips
[0]
1755 if len(hostips
) > 1:
1756 logger
.warning("More than one IPv6 address found. Using %s", hostip6
)
1758 logger
.warning("No IPv6 address will be assigned")
1760 names
.hostip
= hostip
1761 names
.hostip6
= hostip6
1763 if serverrole
is None:
1764 serverrole
= lp
.get("server role")
1766 assert serverrole
in ("domain controller", "member server", "standalone")
1768 if not os
.path
.exists(paths
.private_dir
):
1769 os
.mkdir(paths
.private_dir
)
1770 if not os
.path
.exists(os
.path
.join(paths
.private_dir
, "tls")):
1771 os
.mkdir(os
.path
.join(paths
.private_dir
, "tls"))
1773 ldapi_url
= "ldapi://%s" % urllib
.quote(paths
.s4_ldapi_path
, safe
="")
1775 schema
= Schema(domainsid
, invocationid
=invocationid
,
1776 schemadn
=names
.schemadn
)
1778 if backend_type
== "ldb":
1779 provision_backend
= LDBBackend(backend_type
, paths
=paths
,
1780 lp
=lp
, credentials
=credentials
,
1781 names
=names
, logger
=logger
)
1782 elif backend_type
== "existing":
1783 provision_backend
= ExistingBackend(backend_type
, paths
=paths
,
1784 lp
=lp
, credentials
=credentials
,
1785 names
=names
, logger
=logger
,
1786 ldap_backend_forced_uri
=ldap_backend_forced_uri
)
1787 elif backend_type
== "fedora-ds":
1788 provision_backend
= FDSBackend(backend_type
, paths
=paths
,
1789 lp
=lp
, credentials
=credentials
,
1790 names
=names
, logger
=logger
, domainsid
=domainsid
,
1791 schema
=schema
, hostname
=hostname
, ldapadminpass
=ldapadminpass
,
1792 slapd_path
=slapd_path
,
1793 ldap_backend_extra_port
=ldap_backend_extra_port
,
1794 ldap_dryrun_mode
=ldap_dryrun_mode
, root
=root
,
1795 setup_ds_path
=setup_ds_path
,
1796 ldap_backend_forced_uri
=ldap_backend_forced_uri
)
1797 elif backend_type
== "openldap":
1798 provision_backend
= OpenLDAPBackend(backend_type
, paths
=paths
,
1799 lp
=lp
, credentials
=credentials
,
1800 names
=names
, logger
=logger
, domainsid
=domainsid
,
1801 schema
=schema
, hostname
=hostname
, ldapadminpass
=ldapadminpass
,
1802 slapd_path
=slapd_path
,
1803 ldap_backend_extra_port
=ldap_backend_extra_port
,
1804 ldap_dryrun_mode
=ldap_dryrun_mode
, ol_mmr_urls
=ol_mmr_urls
,
1806 ldap_backend_forced_uri
=ldap_backend_forced_uri
)
1808 raise ValueError("Unknown LDAP backend type selected")
1810 provision_backend
.init()
1811 provision_backend
.start()
1813 # only install a new shares config db if there is none
1814 if not os
.path
.exists(paths
.shareconf
):
1815 logger
.info("Setting up share.ldb")
1816 share_ldb
= Ldb(paths
.shareconf
, session_info
=session_info
,
1818 share_ldb
.load_ldif_file_add(setup_path("share.ldif"))
1820 logger
.info("Setting up secrets.ldb")
1821 secrets_ldb
= setup_secretsdb(paths
,
1822 session_info
=session_info
,
1823 backend_credentials
=provision_backend
.secrets_credentials
, lp
=lp
)
1826 logger
.info("Setting up the registry")
1827 setup_registry(paths
.hklm
, session_info
,
1830 logger
.info("Setting up the privileges database")
1831 setup_privileges(paths
.privilege
, session_info
, lp
=lp
)
1833 logger
.info("Setting up idmap db")
1834 idmap
= setup_idmapdb(paths
.idmapdb
,
1835 session_info
=session_info
, lp
=lp
)
1837 setup_name_mappings(idmap
, sid
=str(domainsid
),
1838 root_uid
=root_uid
, nobody_uid
=nobody_uid
,
1839 users_gid
=users_gid
, wheel_gid
=wheel_gid
)
1841 logger
.info("Setting up SAM db")
1842 samdb
= setup_samdb(paths
.samdb
, session_info
,
1843 provision_backend
, lp
, names
, logger
=logger
,
1844 serverrole
=serverrole
,
1845 schema
=schema
, fill
=samdb_fill
, am_rodc
=am_rodc
)
1847 if serverrole
== "domain controller":
1848 if paths
.netlogon
is None:
1849 logger
.info("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1850 logger
.info("Please either remove %s or see the template at %s" %
1851 (paths
.smbconf
, setup_path("provision.smb.conf.dc")))
1852 assert paths
.netlogon
is not None
1854 if paths
.sysvol
is None:
1855 logger
.info("Existing smb.conf does not have a [sysvol] share, but you"
1856 " are configuring a DC.")
1857 logger
.info("Please either remove %s or see the template at %s" %
1858 (paths
.smbconf
, setup_path("provision.smb.conf.dc")))
1859 assert paths
.sysvol
is not None
1861 if not os
.path
.isdir(paths
.netlogon
):
1862 os
.makedirs(paths
.netlogon
, 0755)
1864 if samdb_fill
== FILL_FULL
:
1865 provision_fill(samdb
, secrets_ldb
, logger
,
1866 names
, paths
, schema
=schema
, targetdir
=targetdir
,
1867 samdb_fill
=samdb_fill
, hostip
=hostip
, hostip6
=hostip6
, domainsid
=domainsid
,
1868 next_rid
=next_rid
, dc_rid
=dc_rid
, adminpass
=adminpass
,
1869 krbtgtpass
=krbtgtpass
, domainguid
=domainguid
,
1870 policyguid
=policyguid
, policyguid_dc
=policyguid_dc
,
1871 invocationid
=invocationid
, machinepass
=machinepass
,
1872 ntdsguid
=ntdsguid
, dns_backend
=dns_backend
, dnspass
=dnspass
,
1873 serverrole
=serverrole
, dom_for_fun_level
=dom_for_fun_level
,
1874 am_rodc
=am_rodc
, lp
=lp
)
1876 create_krb5_conf(paths
.krb5conf
,
1877 dnsdomain
=names
.dnsdomain
, hostname
=names
.hostname
,
1879 logger
.info("A Kerberos configuration suitable for Samba 4 has been "
1880 "generated at %s", paths
.krb5conf
)
1882 if serverrole
== "domain controller":
1883 create_dns_update_list(lp
, logger
, paths
)
1885 provision_backend
.post_setup()
1886 provision_backend
.shutdown()
1888 create_phpldapadmin_config(paths
.phpldapadminconfig
,
1891 secrets_ldb
.transaction_cancel()
1894 # Now commit the secrets.ldb to disk
1895 secrets_ldb
.transaction_commit()
1897 # the commit creates the dns.keytab, now chown it
1898 dns_keytab_path
= os
.path
.join(paths
.private_dir
, paths
.dns_keytab
)
1899 if os
.path
.isfile(dns_keytab_path
) and paths
.bind_gid
is not None:
1901 os
.chmod(dns_keytab_path
, 0640)
1902 os
.chown(dns_keytab_path
, -1, paths
.bind_gid
)
1904 if not os
.environ
.has_key('SAMBA_SELFTEST'):
1905 logger
.info("Failed to chown %s to bind gid %u",
1906 dns_keytab_path
, paths
.bind_gid
)
1908 logger
.info("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php",
1909 paths
.phpldapadminconfig
)
1911 logger
.info("Once the above files are installed, your Samba4 server will be ready to use")
1912 logger
.info("Server Role: %s" % serverrole
)
1913 logger
.info("Hostname: %s" % names
.hostname
)
1914 logger
.info("NetBIOS Domain: %s" % names
.domain
)
1915 logger
.info("DNS Domain: %s" % names
.dnsdomain
)
1916 logger
.info("DOMAIN SID: %s" % str(domainsid
))
1917 if samdb_fill
== FILL_FULL
:
1918 logger
.info("Admin password: %s" % adminpass
)
1919 if provision_backend
.type is not "ldb":
1920 if provision_backend
.credentials
.get_bind_dn() is not None:
1921 logger
.info("LDAP Backend Admin DN: %s" %
1922 provision_backend
.credentials
.get_bind_dn())
1924 logger
.info("LDAP Admin User: %s" %
1925 provision_backend
.credentials
.get_username())
1927 logger
.info("LDAP Admin Password: %s" %
1928 provision_backend
.credentials
.get_password())
1930 if provision_backend
.slapd_command_escaped
is not None:
1931 # now display slapd_command_file.txt to show how slapd must be
1933 logger
.info("Use later the following commandline to start slapd, then Samba:")
1934 logger
.info(provision_backend
.slapd_command_escaped
)
1935 logger
.info("This slapd-Commandline is also stored under: %s/ldap_backend_startup.sh",
1936 provision_backend
.ldapdir
)
1938 result
= ProvisionResult()
1939 result
.domaindn
= domaindn
1940 result
.paths
= paths
1941 result
.names
= names
1943 result
.samdb
= samdb
1944 result
.idmap
= idmap
1948 def provision_become_dc(smbconf
=None, targetdir
=None,
1949 realm
=None, rootdn
=None, domaindn
=None, schemadn
=None, configdn
=None,
1950 serverdn
=None, domain
=None, hostname
=None, domainsid
=None,
1951 adminpass
=None, krbtgtpass
=None, domainguid
=None, policyguid
=None,
1952 policyguid_dc
=None, invocationid
=None, machinepass
=None, dnspass
=None,
1953 dns_backend
=None, root
=None, nobody
=None, users
=None, wheel
=None, backup
=None,
1954 serverrole
=None, ldap_backend
=None, ldap_backend_type
=None,
1955 sitename
=None, debuglevel
=1):
1957 logger
= logging
.getLogger("provision")
1958 samba
.set_debug_level(debuglevel
)
1960 res
= provision(logger
, system_session(), None,
1961 smbconf
=smbconf
, targetdir
=targetdir
, samdb_fill
=FILL_DRS
,
1962 realm
=realm
, rootdn
=rootdn
, domaindn
=domaindn
, schemadn
=schemadn
,
1963 configdn
=configdn
, serverdn
=serverdn
, domain
=domain
,
1964 hostname
=hostname
, hostip
=None, domainsid
=domainsid
,
1965 machinepass
=machinepass
, serverrole
="domain controller",
1966 sitename
=sitename
, dns_backend
=dns_backend
, dnspass
=dnspass
)
1967 res
.lp
.set("debuglevel", str(debuglevel
))
1971 def create_phpldapadmin_config(path
, ldapi_uri
):
1972 """Create a PHP LDAP admin configuration file.
1974 :param path: Path to write the configuration to.
1976 setup_file(setup_path("phpldapadmin-config.php"), path
,
1977 {"S4_LDAPI_URI": ldapi_uri
})
1980 def create_dns_dir(logger
, paths
):
1981 """Write out a DNS zone file, from the info in the current database.
1983 :param logger: Logger object
1984 :param paths: paths object
1986 dns_dir
= os
.path
.dirname(paths
.dns
)
1989 shutil
.rmtree(dns_dir
, True)
1993 os
.mkdir(dns_dir
, 0770)
1995 if paths
.bind_gid
is not None:
1997 os
.chown(dns_dir
, -1, paths
.bind_gid
)
1998 # chmod needed to cope with umask
1999 os
.chmod(dns_dir
, 0770)
2001 if not os
.environ
.has_key('SAMBA_SELFTEST'):
2002 logger
.error("Failed to chown %s to bind gid %u" % (
2003 dns_dir
, paths
.bind_gid
))
2006 def create_zone_file(lp
, logger
, paths
, targetdir
, dnsdomain
,
2007 hostip
, hostip6
, hostname
, realm
, domainguid
,
2009 """Write out a DNS zone file, from the info in the current database.
2011 :param paths: paths object
2012 :param dnsdomain: DNS Domain name
2013 :param domaindn: DN of the Domain
2014 :param hostip: Local IPv4 IP
2015 :param hostip6: Local IPv6 IP
2016 :param hostname: Local hostname
2017 :param realm: Realm name
2018 :param domainguid: GUID of the domain.
2019 :param ntdsguid: GUID of the hosts nTDSDSA record.
2021 assert isinstance(domainguid
, str)
2023 if hostip6
is not None:
2024 hostip6_base_line
= " IN AAAA " + hostip6
2025 hostip6_host_line
= hostname
+ " IN AAAA " + hostip6
2026 gc_msdcs_ip6_line
= "gc._msdcs IN AAAA " + hostip6
2028 hostip6_base_line
= ""
2029 hostip6_host_line
= ""
2030 gc_msdcs_ip6_line
= ""
2032 if hostip
is not None:
2033 hostip_base_line
= " IN A " + hostip
2034 hostip_host_line
= hostname
+ " IN A " + hostip
2035 gc_msdcs_ip_line
= "gc._msdcs IN A " + hostip
2037 hostip_base_line
= ""
2038 hostip_host_line
= ""
2039 gc_msdcs_ip_line
= ""
2041 # we need to freeze the zone while we update the contents
2042 if targetdir
is None:
2043 rndc
= ' '.join(lp
.get("rndc command"))
2044 os
.system(rndc
+ " freeze " + lp
.get("realm"))
2046 setup_file(setup_path("provision.zone"), paths
.dns
, {
2047 "HOSTNAME": hostname
,
2048 "DNSDOMAIN": dnsdomain
,
2050 "HOSTIP_BASE_LINE": hostip_base_line
,
2051 "HOSTIP_HOST_LINE": hostip_host_line
,
2052 "DOMAINGUID": domainguid
,
2053 "DATESTRING": time
.strftime("%Y%m%d%H"),
2054 "DEFAULTSITE": DEFAULTSITE
,
2055 "NTDSGUID": ntdsguid
,
2056 "HOSTIP6_BASE_LINE": hostip6_base_line
,
2057 "HOSTIP6_HOST_LINE": hostip6_host_line
,
2058 "GC_MSDCS_IP_LINE": gc_msdcs_ip_line
,
2059 "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line
,
2062 if paths
.bind_gid
is not None:
2064 os
.chown(paths
.dns
, -1, paths
.bind_gid
)
2065 # chmod needed to cope with umask
2066 os
.chmod(paths
.dns
, 0664)
2068 if not os
.environ
.has_key('SAMBA_SELFTEST'):
2069 logger
.error("Failed to chown %s to bind gid %u" % (
2070 paths
.dns
, paths
.bind_gid
))
2072 if targetdir
is None:
2073 os
.system(rndc
+ " unfreeze " + lp
.get("realm"))
2076 def create_dns_update_list(lp
, logger
, paths
):
2077 """Write out a dns_update_list file"""
2078 # note that we use no variable substitution on this file
2079 # the substitution is done at runtime by samba_dnsupdate, samba_spnupdate
2080 setup_file(setup_path("dns_update_list"), paths
.dns_update_list
, None)
2081 setup_file(setup_path("spn_update_list"), paths
.spn_update_list
, None)
2084 def create_named_conf(paths
, realm
, dnsdomain
, dns_backend
):
2085 """Write out a file containing zone statements suitable for inclusion in a
2086 named.conf file (including GSS-TSIG configuration).
2088 :param paths: all paths
2089 :param realm: Realm name
2090 :param dnsdomain: DNS Domain name
2091 :param dns_backend: DNS backend type
2092 :param keytab_name: File name of DNS keytab file
2095 if dns_backend
== "BIND9_FLATFILE":
2096 setup_file(setup_path("named.conf"), paths
.namedconf
, {
2097 "DNSDOMAIN": dnsdomain
,
2099 "ZONE_FILE": paths
.dns
,
2100 "REALM_WC": "*." + ".".join(realm
.split(".")[1:]),
2101 "NAMED_CONF": paths
.namedconf
,
2102 "NAMED_CONF_UPDATE": paths
.namedconf_update
2105 setup_file(setup_path("named.conf.update"), paths
.namedconf_update
)
2107 elif dns_backend
== "BIND9_DLZ":
2108 dlz_module_path
= os
.path
.join(samba
.param
.modules_dir(),
2109 "bind9/dlz_bind9.so")
2110 setup_file(setup_path("named.conf.dlz"), paths
.namedconf
, {
2111 "NAMED_CONF": paths
.namedconf
,
2112 "BIND9_DLZ_MODULE": dlz_module_path
,
2117 def create_named_txt(path
, realm
, dnsdomain
, dnsname
, private_dir
,
2119 """Write out a file containing zone statements suitable for inclusion in a
2120 named.conf file (including GSS-TSIG configuration).
2122 :param path: Path of the new named.conf file.
2123 :param realm: Realm name
2124 :param dnsdomain: DNS Domain name
2125 :param private_dir: Path to private directory
2126 :param keytab_name: File name of DNS keytab file
2128 setup_file(setup_path("named.txt"), path
, {
2129 "DNSDOMAIN": dnsdomain
,
2130 "DNSNAME" : dnsname
,
2132 "DNS_KEYTAB": keytab_name
,
2133 "DNS_KEYTAB_ABS": os
.path
.join(private_dir
, keytab_name
),
2134 "PRIVATE_DIR": private_dir
2138 def create_krb5_conf(path
, dnsdomain
, hostname
, realm
):
2139 """Write out a file containing zone statements suitable for inclusion in a
2140 named.conf file (including GSS-TSIG configuration).
2142 :param path: Path of the new named.conf file.
2143 :param dnsdomain: DNS Domain name
2144 :param hostname: Local hostname
2145 :param realm: Realm name
2147 setup_file(setup_path("krb5.conf"), path
, {
2148 "DNSDOMAIN": dnsdomain
,
2149 "HOSTNAME": hostname
,
2154 class ProvisioningError(Exception):
2155 """A generic provision error."""
2157 def __init__(self
, value
):
2161 return "ProvisioningError: " + self
.value
2164 class InvalidNetbiosName(Exception):
2165 """A specified name was not a valid NetBIOS name."""
2166 def __init__(self
, name
):
2167 super(InvalidNetbiosName
, self
).__init
__(
2168 "The name '%r' is not a valid NetBIOS name" % name
)