5aabd36c1a8e6b6a79bef4d6ff3f0fbd7ea31bcb
[kai/samba.git] / source4 / scripting / python / samba / provision / __init__.py
1
2 # Unix SMB/CIFS implementation.
3 # backend code for provisioning a Samba4 server
4
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
8 #
9 # Based on the original in EJS:
10 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
11 #
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.
16 #
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.
21 #
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/>.
24 #
25
26 """Functions for setting up a Samba configuration."""
27
28 __docformat__ = "restructuredText"
29
30 from base64 import b64encode
31 import os
32 import re
33 import pwd
34 import grp
35 import logging
36 import time
37 import uuid
38 import socket
39 import urllib
40 import shutil
41 import string
42
43 import ldb
44
45 from samba.auth import system_session, admin_session
46 import samba
47 from samba.dsdb import DS_DOMAIN_FUNCTION_2000
48 from samba import (
49     Ldb,
50     check_all_substituted,
51     read_and_sub_file,
52     setup_file,
53     substitute_var,
54     valid_netbios_name,
55     version,
56     )
57 from samba.dcerpc import security, misc
58 from samba.dcerpc.misc import (
59     SEC_CHAN_BDC,
60     SEC_CHAN_WKSTA,
61     )
62 from samba.dsdb import (
63     DS_DOMAIN_FUNCTION_2003,
64     DS_DOMAIN_FUNCTION_2008_R2,
65     ENC_ALL_TYPES,
66     )
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 (
72     ExistingBackend,
73     FDSBackend,
74     LDBBackend,
75     OpenLDAPBackend,
76     )
77 import samba.param
78 import samba.registry
79 from samba.schema import Schema
80 from samba.samdb import SamDB
81
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"
87
88
89 def setup_path(file):
90     """Return an absolute path to the provision tempate file specified by file"""
91     return os.path.join(samba.param.setup_dir(), file)
92
93 # Descriptors of naming contexts and other important objects
94
95 # "get_schema_descriptor" is located in "schema.py"
96
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)
114     return ndr_pack(sec)
115
116
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)" \
157     "(A;;RPRC;;;RU)" \
158     "(A;CI;LC;;;RU)" \
159     "(A;CI;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
160     "(A;;RP;;;WD)" \
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)
168     return ndr_pack(sec)
169
170
171 class ProvisionPaths(object):
172
173     def __init__(self):
174         self.shareconf = None
175         self.hklm = None
176         self.hkcu = None
177         self.hkcr = None
178         self.hku = None
179         self.hkpd = None
180         self.hkpt = None
181         self.samdb = None
182         self.idmapdb = None
183         self.secrets = None
184         self.keytab = None
185         self.dns_keytab = None
186         self.dns = None
187         self.winsdb = None
188         self.private_dir = None
189
190
191 class ProvisionNames(object):
192
193     def __init__(self):
194         self.rootdn = None
195         self.domaindn = None
196         self.configdn = None
197         self.schemadn = None
198         self.ldapmanagerdn = None
199         self.dnsdomain = None
200         self.realm = None
201         self.netbiosname = None
202         self.domain = None
203         self.hostname = None
204         self.sitename = None
205         self.smbconf = None
206
207 def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf, lp):
208     """Get key provision parameters (realm, domain, ...) from a given provision
209
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
217     """
218     names = ProvisionNames()
219     names.adminpass = None
220
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)
227     # netbiosname
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("$","")
233
234     names.smbconf = smbconf
235
236     # That's a bit simplistic but it's ok as long as we have only 3
237     # partitions
238     current = samdb.search(expression="(objectClass=*)",
239         base="", scope=ldb.SCOPE_BASE,
240         attrs=["defaultNamingContext", "schemaNamingContext",
241                "configurationNamingContext","rootDomainNamingContext"])
242
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)))
252
253     names.domaindn=current[0]["defaultNamingContext"]
254     names.rootdn=current[0]["rootDomainNamingContext"]
255     # default site name
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"])
259
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,"")
265
266     server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
267                                 attrs=[], base=configdn)
268     names.serverdn = server_res[0].dn
269
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]))
276
277     # domain guid/sid
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
286     else:
287         names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
288
289     # policy guid
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("}","")
294     # dc policy guid
295     res8 = samdb.search(expression="(displayName=Default Domain Controllers"
296                                    " Policy)",
297                             base="CN=Policies,CN=System," + basedn,
298                             scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
299     if len(res8) == 1:
300         names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","")
301     else:
302         names.policyid_dc = None
303     res9 = idmapdb.search(expression="(cn=%s)" %
304                             (security.SID_BUILTIN_ADMINISTRATORS),
305                             attrs=["xidNumber"])
306     if len(res9) == 1:
307         names.wheel_gid = res9[0]["xidNumber"]
308     else:
309         raise ProvisioningError("Unable to find uid/gid for Domain Admins rid")
310     return names
311
312 def update_provision_usn(samdb, low, high, id, replace=False):
313     """Update the field provisionUSN in sam.ldb
314
315     This field is used to track range of USN modified by provision and
316     upgradeprovision.
317     This value is used afterward by next provision to figure out if
318     the field have been modified since last provision.
319
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)
326     """
327
328     tab = []
329     if not replace:
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)
337             tab.append(str(e))
338
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")
349     samdb.modify(delta)
350
351
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
355     upgradeprovision.
356     This value is used afterward by next provision to figure out if
357     the field have been modified since last provision.
358
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"""
363
364     tab = []
365     tab.append("%s-%s;%s" % (low, high, id))
366
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)
371     samdb.add(delta)
372
373
374 def get_max_usn(samdb,basedn):
375     """ This function return the biggest USN present in the provision
376
377     :param samdb: A LDB object pointing to the sam.ldb
378     :param basedn: A string containing the base DN of the provision
379                     (ie. DC=foo, DC=bar)
380     :return: The biggest USN in the provision"""
381
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"]
388
389
390 def get_last_provision_usn(sam):
391     """Get USNs ranges modified by a provision or an upgradeprovision
392
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
396     """
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"])
401     if len(entry):
402         myids = []
403         range = {}
404         p = re.compile(r'-')
405         if entry[0].get("provisionnerID"):
406             for e in entry[0]["provisionnerID"]:
407                 myids.append(str(e))
408         for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
409             tab1 = str(r).split(';')
410             if len(tab1) == 2:
411                 id = tab1[1]
412             else:
413                 id = "default"
414             if (len(myids) > 0 and id not in myids):
415                 continue
416             tab2 = p.split(tab1[0])
417             if range.get(id) == None:
418                 range[id] = []
419             range[id].append(tab2[0])
420             range[id].append(tab2[1])
421         return range
422     else:
423         return None
424
425
426 class ProvisionResult(object):
427
428     def __init__(self):
429         self.paths = None
430         self.domaindn = None
431         self.lp = None
432         self.samdb = None
433
434
435 def check_install(lp, session_info, credentials):
436     """Check whether the current install seems ok.
437
438     :param lp: Loadparm context
439     :param session_info: Session information
440     :param credentials: Credentials
441     """
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")
448
449
450 def findnss(nssfn, names):
451     """Find a user or group from a list of possibilities.
452
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.
456     """
457     for name in names:
458         try:
459             return nssfn(name)
460         except KeyError:
461             pass
462     raise KeyError("Unable to find user/group in %r" % names)
463
464
465 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
466 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
467
468
469 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
470     """Setup a ldb in the private dir.
471
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
476     """
477     assert isinstance(ldif_path, str)
478     data = read_and_sub_file(ldif_path, subst_vars)
479     ldb.add_ldif(data, controls)
480
481
482 def setup_modify_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
483     """Modify a ldb in the private dir.
484
485     :param ldb: LDB object.
486     :param ldif_path: LDIF file path.
487     :param subst_vars: Optional dictionary with substitution variables.
488     """
489     data = read_and_sub_file(ldif_path, subst_vars)
490     ldb.modify_ldif(data, controls)
491
492
493 def setup_ldb(ldb, ldif_path, subst_vars):
494     """Import a LDIF a file into a LDB handle, optionally substituting
495     variables.
496
497     :note: Either all LDIF data will be added or none (using transactions).
498
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.
502     """
503     assert ldb is not None
504     ldb.transaction_start()
505     try:
506         setup_add_ldif(ldb, ldif_path, subst_vars)
507     except Exception:
508         ldb.transaction_cancel()
509         raise
510     else:
511         ldb.transaction_commit()
512
513
514 def provision_paths_from_lp(lp, dnsdomain):
515     """Set the default paths for provisioning.
516
517     :param lp: Loadparm context.
518     :param dnsdomain: DNS Domain name
519     """
520     paths = ProvisionPaths()
521     paths.private_dir = lp.get("private dir")
522
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"
527
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
553     return paths
554
555
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."""
560
561     if hostname is None:
562         hostname = socket.gethostname().split(".")[0]
563
564     netbiosname = lp.get("netbios name")
565     if netbiosname is None:
566         netbiosname = hostname
567         # remove forbidden chars
568         newnbname = ""
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)
578
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)
583
584     dnsdomain = dnsdomain.lower()
585
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)
590
591     serverrole = serverrole.lower()
592
593     realm = dnsdomain.upper()
594
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)
597
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))
600
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))
603
604     if serverrole == "domain controller":
605         if domain is None:
606             # This will, for better or worse, default to 'WORKGROUP'
607             domain = lp.get("workgroup")
608         domain = domain.upper()
609
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))
612
613         if domaindn is None:
614             domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
615
616         if domain == netbiosname:
617             raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
618     else:
619         domain = netbiosname
620         if domaindn is None:
621             domaindn = "DC=" + netbiosname
622
623     if not valid_netbios_name(domain):
624         raise InvalidNetbiosName(domain)
625
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))
630     if domain == realm:
631         raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
632
633     if rootdn is None:
634        rootdn = domaindn
635
636     if configdn is None:
637         configdn = "CN=Configuration," + rootdn
638     if schemadn is None:
639         schemadn = "CN=Schema," + configdn
640
641     if sitename is None:
642         sitename=DEFAULTSITE
643
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
652     names.realm = realm
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)
658
659     return names
660
661
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.
665     """
666     assert smbconf is not None
667     if hostname is None:
668         hostname = socket.gethostname().split(".")[0]
669         netbiosname = hostname.upper()
670         # remove forbidden chars
671         newnbname = ""
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]
677     else:
678         netbiosname = hostname.upper()
679
680     if serverrole is None:
681         serverrole = "standalone"
682
683     assert serverrole in ("domain controller", "member server", "standalone")
684     if serverrole == "domain controller":
685         smbconfsuffix = "dc"
686     elif serverrole == "member server":
687         smbconfsuffix = "member"
688     elif serverrole == "standalone":
689         smbconfsuffix = "standalone"
690
691     if sid_generator is None:
692         sid_generator = "internal"
693
694     assert domain is not None
695     domain = domain.upper()
696
697     assert realm is not None
698     realm = realm.upper()
699
700     if lp is None:
701         lp = samba.param.LoadParm()
702     #Load non-existant file
703     if os.path.exists(smbconf):
704         lp.load(smbconf)
705     if eadb and not lp.get("posix:eadb"):
706         if targetdir is not None:
707             privdir = os.path.join(targetdir, "private")
708         else:
709             privdir = lp.get("private dir")
710         lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
711
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)
715
716         lp.set("lock dir", os.path.abspath(targetdir))
717     else:
718         privatedir_line = ""
719         lockdir_line = ""
720
721     sysvol = os.path.join(lp.get("lock dir"), "sysvol")
722     netlogon = os.path.join(sysvol, realm.lower(), "scripts")
723
724     setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
725                smbconf, {
726             "NETBIOS_NAME": netbiosname,
727             "DOMAIN": domain,
728             "REALM": realm,
729             "SERVERROLE": serverrole,
730             "NETLOGONPATH": netlogon,
731             "SYSVOLPATH": sysvol,
732             "PRIVATEDIR_LINE": privatedir_line,
733             "LOCKDIR_LINE": lockdir_line
734             })
735
736     # reload the smb.conf
737     lp.load(smbconf)
738
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')
743     lp.dump(f, False)
744     f.close()
745
746
747
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.
751
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.
760     """
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)
763
764     idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
765     idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
766
767
768 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
769                            provision_backend, names, schema, serverrole,
770                            erase=False):
771     """Setup the partitions for the SAM database.
772
773     Alternatively, provision() may call this, and then populate the database.
774
775     :note: This will wipe the Sam Database!
776
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.
780
781     """
782     assert session_info is not None
783
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
786
787     try:
788         os.unlink(samdb_path)
789     except OSError:
790         pass
791
792     samdb = Ldb(url=samdb_path, session_info=session_info,
793                 lp=lp, options=["modules:"])
794
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
798
799     samdb.transaction_start()
800     try:
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,
807         })
808
809
810         setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
811                 "BACKEND_TYPE": provision_backend.type,
812                 "SERVER_ROLE": serverrole
813                 })
814
815         logger.info("Setting up sam.ldb rootDSE")
816         setup_samdb_rootdse(samdb, names)
817     except Exception:
818         samdb.transaction_cancel()
819         raise
820     else:
821         samdb.transaction_commit()
822
823
824 def secretsdb_self_join(secretsdb, domain,
825                         netbiosname, machinepass, domainsid=None,
826                         realm=None, dnsdomain=None,
827                         keytab_path=None,
828                         key_version_number=1,
829                         secure_channel_type=SEC_CHAN_WKSTA):
830     """Add domain join-specific bits to a secrets database.
831
832     :param secretsdb: Ldb Handle to the secrets database
833     :param machinepass: Machine password
834     """
835     attrs = ["whenChanged",
836            "secret",
837            "priorSecret",
838            "priorChanged",
839            "krb5Keytab",
840            "privateKeytab"]
841
842     if realm is not None:
843         if dnsdomain is None:
844             dnsdomain = realm.lower()
845         dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
846     else:
847         dnsname = None
848     shortname = netbiosname.lower()
849
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"]
861
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)]
867
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)
875
876     for del_msg in res:
877         secretsdb.delete(del_msg.dn)
878
879     res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
880
881     if len(res) == 1:
882         msg["priorSecret"] = [res[0]["secret"][0]]
883         msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
884
885         try:
886             msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
887         except KeyError:
888             pass
889
890         try:
891             msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
892         except KeyError:
893             pass
894
895         for el in msg:
896             if el != 'dn':
897                 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
898         secretsdb.modify(msg)
899         secretsdb.rename(res[0].dn, msg.dn)
900     else:
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
907
908         secretsdb.add(msg)
909
910
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.
914
915     :param secretsdb: Ldb Handle to the secrets database
916     :param machinepass: Machine password
917     """
918     try:
919         os.unlink(os.path.join(private_dir, dns_keytab_path))
920     except OSError:
921         pass
922
923     setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
924             "REALM": realm,
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())
931             })
932
933
934 def setup_secretsdb(paths, session_info, backend_credentials, lp):
935     """Setup the secrets database.
936
937    :note: This function does not handle exceptions and transaction on purpose,
938        it's up to the caller to do this job.
939
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
945     """
946     if os.path.exists(paths.secrets):
947         os.unlink(paths.secrets)
948
949     keytab_path = os.path.join(paths.private_dir, paths.keytab)
950     if os.path.exists(keytab_path):
951         os.unlink(keytab_path)
952
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)
956
957     path = paths.secrets
958
959     secrets_ldb = Ldb(path, session_info=session_info,
960                       lp=lp)
961     secrets_ldb.erase()
962     secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
963     secrets_ldb = Ldb(path, session_info=session_info,
964                       lp=lp)
965     secrets_ldb.transaction_start()
966     try:
967         secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
968
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())
976                         })
977             else:
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())
983                         })
984
985         return secrets_ldb
986     except Exception:
987         secrets_ldb.transaction_cancel()
988         raise
989
990
991 def setup_privileges(path, session_info, lp):
992     """Setup the privileges database.
993
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
999     """
1000     if os.path.exists(path):
1001         os.unlink(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"))
1005
1006
1007 def setup_registry(path, session_info, lp):
1008     """Setup the registry.
1009
1010     :param path: Path to the registry database
1011     :param session_info: Session information
1012     :param credentials: Credentials
1013     :param lp: Loadparm context
1014     """
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)
1021
1022
1023 def setup_idmapdb(path, session_info, lp):
1024     """Setup the idmap database.
1025
1026     :param path: path to the idmap database
1027     :param session_info: Session information
1028     :param credentials: Credentials
1029     :param lp: Loadparm context
1030     """
1031     if os.path.exists(path):
1032         os.unlink(path)
1033
1034     idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
1035     idmap_ldb.erase()
1036     idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
1037     return idmap_ldb
1038
1039
1040 def setup_samdb_rootdse(samdb, names):
1041     """Setup the SamDB rootdse.
1042
1043     :param samdb: Sam Database handle
1044     """
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,
1051         })
1052
1053
1054 def setup_self_join(samdb, names, machinepass, dnspass,
1055                     domainsid, next_rid, invocationid,
1056                     policyguid, policyguid_dc, domainControllerFunctionality,
1057                     ntdsguid):
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
1062     else:
1063         ntdsguid_line = ""
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)})
1079
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})
1085
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)
1091
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),
1102               })
1103
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())
1113               })
1114
1115
1116 def getpolicypath(sysvolpath, dnsdomain, guid):
1117     """Return the physical path of policy given its guid.
1118
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
1123     """
1124
1125     if guid[0] != "{":
1126         guid = "{%s}" % guid
1127     policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1128     return policy_path
1129
1130
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)
1142
1143
1144 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1145     """Create the default GPO for a domain
1146
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
1151     """
1152     policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1153     create_gpo_struct(policy_path)
1154
1155     policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1156     create_gpo_struct(policy_path)
1157
1158
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,
1163         next_rid=1000):
1164     """Setup a complete SAM Database.
1165
1166     :note: This will wipe the main SAM database file!
1167     """
1168
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)
1177
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
1181
1182     if dom_for_fun_level is None:
1183         dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1184
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!")
1187
1188     domainFunctionality = dom_for_fun_level
1189     forestFunctionality = dom_for_fun_level
1190
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)
1195
1196     if schema is None:
1197         schema = Schema(domainsid, schemadn=names.schemadn)
1198
1199     # Load the database, but don's load the global schema and don't connect
1200     # quite yet
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)
1204
1205     logger.info("Pre-loading the Samba 4 and AD schema")
1206
1207     # Load the schema from the one we computed earlier
1208     samdb.set_schema(schema)
1209
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)
1213
1214     # And now we can connect to the DB - the schema won't be loaded from the
1215     # DB
1216     samdb.connect(path)
1217
1218     if fill == FILL_DRS:
1219         return samdb
1220
1221     samdb.transaction_start()
1222     try:
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.
1226
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)
1233
1234         samdb.set_domain_sid(str(domainsid))
1235         samdb.set_invocation_id(invocationid)
1236
1237         logger.info("Adding DomainDN: %s" % names.domaindn)
1238
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
1244         else:
1245             domainguid_line = ""
1246
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
1253                 })
1254
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
1264             })
1265
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,
1271             })
1272
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,
1276                     "subRefs")
1277
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})
1286
1287         logger.info("Reopening sam.ldb with new schema")
1288     except Exception:
1289         samdb.transaction_cancel()
1290         raise
1291     else:
1292         samdb.transaction_commit()
1293
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)
1297
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)
1301     samdb.connect(path)
1302
1303     samdb.transaction_start()
1304     try:
1305         samdb.invocation_id = invocationid
1306
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),
1319             })
1320
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)
1328
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
1352             })
1353
1354         setup_modify_ldif(samdb,
1355             setup_path("provision_basedn_references.ldif"), {
1356                 "DOMAINDN": names.domaindn})
1357
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'))
1370                 })
1371
1372             logger.info("Setting up self join")
1373             setup_self_join(samdb, names=names, invocationid=invocationid,
1374                 dnspass=dnspass,
1375                 machinepass=machinepass,
1376                 domainsid=domainsid,
1377                 next_rid=next_rid,
1378                 policyguid=policyguid,
1379                 policyguid_dc=policyguid_dc,
1380                 domainControllerFunctionality=domainControllerFunctionality,
1381                 ntdsguid=ntdsguid)
1382
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)
1387     except Exception:
1388         samdb.transaction_cancel()
1389         raise
1390     else:
1391         samdb.transaction_commit()
1392         return samdb
1393
1394
1395 FILL_FULL = "FULL"
1396 FILL_NT4SYNC = "NT4SYNC"
1397 FILL_DRS = "DRS"
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)"
1400
1401
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):
1405         for name in files:
1406             setntacl(lp, os.path.join(root, name), acl, domsid)
1407         for name in dirs:
1408             setntacl(lp, os.path.join(root, name), acl, domsid)
1409
1410
1411 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1412     """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1413     folders beneath.
1414
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
1421     """
1422
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))
1426
1427     res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1428                         attrs=["cn", "nTSecurityDescriptor"],
1429                         expression="", scope=ldb.SCOPE_ONELEVEL)
1430
1431     for policy in res:
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,
1436                     str(domainsid))
1437
1438
1439 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1440     lp):
1441     """Set the ACL for the sysvol share and the subfolders
1442
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=...)
1450     """
1451
1452     try:
1453         os.chown(sysvol, -1, gid)
1454     except OSError:
1455         canchown = False
1456     else:
1457         canchown = True
1458
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):
1462         for name in files:
1463             if canchown:
1464                 os.chown(os.path.join(root, name), -1, gid)
1465             setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1466         for name in dirs:
1467             if canchown:
1468                 os.chown(os.path.join(root, name), -1, gid)
1469             setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1470
1471     # Set acls on Policy folder and policies folders
1472     set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp)
1473
1474
1475 def interface_ips_v4(lp):
1476     '''return only IPv4 IPs'''
1477     ips = samba.interface_ips(lp, False)
1478     ret = []
1479     for i in ips:
1480         if i.find(':') == -1:
1481             ret.append(i)
1482     return ret
1483
1484 def interface_ips_v6(lp, linklocal=False):
1485     '''return only IPv6 IPs'''
1486     ips = samba.interface_ips(lp, False)
1487     ret = []
1488     for i in ips:
1489         if i.find(':') != -1 and (linklocal or i.find('%') == -1):
1490             ret.append(i)
1491     return ret
1492
1493
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,
1506         lp=None):
1507     """Provision samba4
1508
1509     :note: caution, this wipes all existing data!
1510     """
1511
1512     if domainsid is None:
1513         domainsid = security.random_sid()
1514     else:
1515         domainsid = security.dom_sid(domainsid)
1516
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()
1527
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)
1534     if dnspass is None:
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)
1539
1540     if backend_type is None:
1541         backend_type = "ldb"
1542
1543     sid_generator = "internal"
1544     if backend_type == "fedora-ds":
1545         sid_generator = "backend"
1546
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'])
1550     if wheel is None:
1551         wheel_gid = findnss_gid(["wheel", "adm"])
1552     else:
1553         wheel_gid = findnss_gid([wheel])
1554     try:
1555         bind_gid = findnss_gid(["bind", "named"])
1556     except KeyError:
1557         bind_gid = None
1558
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))
1565
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,
1576                          lp=lp)
1577     else:
1578         make_smbconf(smbconf, hostname, domain, realm, serverrole,
1579                      targetdir, sid_generator, useeadb, lp=lp)
1580
1581     if lp is None:
1582         lp = samba.param.LoadParm()
1583     lp.load(smbconf)
1584     names = guess_names(lp=lp, hostname=hostname, domain=domain,
1585         dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
1586         configdn=configdn, schemadn=schemadn, serverdn=serverdn,
1587         sitename=sitename)
1588     paths = provision_paths_from_lp(lp, names.dnsdomain)
1589
1590     paths.bind_gid = bind_gid
1591
1592     if hostip is None:
1593         logger.info("Looking up IPv4 addresses")
1594         hostips = interface_ips_v4(lp)
1595         if len(hostips) > 0:
1596             hostip = hostips[0]
1597             if len(hostips) > 1:
1598                 logger.warning("More than one IPv4 address found. Using %s",
1599                     hostip)
1600     if hostip == "127.0.0.1":
1601         hostip = None
1602     if hostip is None:
1603         logger.warning("No IPv4 address will be assigned")
1604
1605     if hostip6 is None:
1606         logger.info("Looking up IPv6 addresses")
1607         hostips = interface_ips_v6(lp, linklocal=False)
1608         if hostips:
1609             hostip6 = hostips[0]
1610         if len(hostips) > 1:
1611             logger.warning("More than one IPv6 address found. Using %s", hostip6)
1612     if hostip6 is None:
1613         logger.warning("No IPv6 address will be assigned")
1614
1615     if serverrole is None:
1616         serverrole = lp.get("server role")
1617
1618     assert serverrole in ("domain controller", "member server", "standalone")
1619     if invocationid is None:
1620         invocationid = str(uuid.uuid4())
1621
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"))
1626
1627     ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1628
1629     schema = Schema(domainsid, invocationid=invocationid,
1630         schemadn=names.schemadn)
1631
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,
1659             nosync=nosync,
1660             ldap_backend_forced_uri=ldap_backend_forced_uri)
1661     else:
1662         raise ValueError("Unknown LDAP backend type selected")
1663
1664     provision_backend.init()
1665     provision_backend.start()
1666
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,
1671                         lp=lp)
1672         share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1673
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)
1678
1679     try:
1680         logger.info("Setting up the registry")
1681         setup_registry(paths.hklm, session_info,
1682                        lp=lp)
1683
1684         logger.info("Setting up the privileges database")
1685         setup_privileges(paths.privilege, session_info, lp=lp)
1686
1687         logger.info("Setting up idmap db")
1688         idmap = setup_idmapdb(paths.idmapdb,
1689             session_info=session_info, lp=lp)
1690
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,
1700             next_rid=next_rid)
1701
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
1708
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
1715
1716             if not os.path.isdir(paths.netlogon):
1717                 os.makedirs(paths.netlogon, 0755)
1718
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)
1723
1724             if serverrole == "domain controller":
1725                 # Set up group policies (domain policy and domain controller
1726                 # policy)
1727                 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1728                     policyguid_dc)
1729                 setsysvolacl(samdb, paths.netlogon, paths.sysvol, wheel_gid,
1730                     domainsid, names.dnsdomain, names.domaindn, lp)
1731
1732             logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1733             setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1734
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)
1739
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)
1743
1744             try:
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")
1752                 samdb.modify(msg)
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
1756                     raise
1757
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)
1763
1764                 domainguid = samdb.searchone(basedn=domaindn,
1765                     attribute="objectGUID")
1766                 assert isinstance(domainguid, str)
1767
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)
1774
1775                 create_named_conf(paths, realm=names.realm,
1776                     dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1777
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)
1786
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)
1791             else:
1792                 set_provision_usn(samdb, 0, maxUSN, invocationid)
1793
1794         create_krb5_conf(paths.krb5conf,
1795                          dnsdomain=names.dnsdomain, hostname=names.hostname,
1796                          realm=names.realm)
1797         logger.info("A Kerberos configuration suitable for Samba 4 has been "
1798                     "generated at %s", paths.krb5conf)
1799
1800         if serverrole == "domain controller":
1801             create_dns_update_list(lp, logger, paths)
1802
1803         provision_backend.post_setup()
1804         provision_backend.shutdown()
1805
1806         create_phpldapadmin_config(paths.phpldapadminconfig,
1807                                    ldapi_url)
1808     except Exception:
1809         secrets_ldb.transaction_cancel()
1810         raise
1811
1812     # Now commit the secrets.ldb to disk
1813     secrets_ldb.transaction_commit()
1814
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:
1818         try:
1819             os.chmod(dns_keytab_path, 0640)
1820             os.chown(dns_keytab_path, -1, paths.bind_gid)
1821         except OSError:
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)
1825
1826
1827     logger.info("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php",
1828             paths.phpldapadminconfig)
1829
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())
1842         else:
1843             logger.info("LDAP Admin User:       %s" %
1844                 provision_backend.credentials.get_username())
1845
1846         logger.info("LDAP Admin Password:   %s" %
1847             provision_backend.credentials.get_password())
1848
1849         if provision_backend.slapd_command_escaped is not None:
1850             # now display slapd_command_file.txt to show how slapd must be
1851             # started next time
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)
1856
1857     result = ProvisionResult()
1858     result.domaindn = domaindn
1859     result.paths = paths
1860     result.lp = lp
1861     result.samdb = samdb
1862     return result
1863
1864
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):
1873
1874     logger = logging.getLogger("provision")
1875     samba.set_debug_level(debuglevel)
1876
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",
1883         sitename=sitename)
1884     res.lp.set("debuglevel", str(debuglevel))
1885     return res
1886
1887
1888 def create_phpldapadmin_config(path, ldapi_uri):
1889     """Create a PHP LDAP admin configuration file.
1890
1891     :param path: Path to write the configuration to.
1892     """
1893     setup_file(setup_path("phpldapadmin-config.php"), path,
1894             {"S4_LDAPI_URI": ldapi_uri})
1895
1896
1897 def create_zone_file(lp, logger, paths, targetdir, dnsdomain,
1898                      hostip, hostip6, hostname, realm, domainguid,
1899                      ntdsguid):
1900     """Write out a DNS zone file, from the info in the current database.
1901
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.
1911     """
1912     assert isinstance(domainguid, str)
1913
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
1918     else:
1919         hostip6_base_line = ""
1920         hostip6_host_line = ""
1921         gc_msdcs_ip6_line = ""
1922
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
1927     else:
1928         hostip_base_line = ""
1929         hostip_host_line = ""
1930         gc_msdcs_ip_line = ""
1931
1932     dns_dir = os.path.dirname(paths.dns)
1933
1934     try:
1935         shutil.rmtree(dns_dir, True)
1936     except OSError:
1937         pass
1938
1939     os.mkdir(dns_dir, 0775)
1940
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"))
1945
1946     setup_file(setup_path("provision.zone"), paths.dns, {
1947             "HOSTNAME": hostname,
1948             "DNSDOMAIN": dnsdomain,
1949             "REALM": realm,
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,
1960         })
1961
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)
1965
1966     # and the SPN update list
1967     setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1968
1969     if paths.bind_gid is not None:
1970         try:
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)
1976         except OSError:
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))
1980
1981     if targetdir is None:
1982         os.system(rndc + " unfreeze " + lp.get("realm"))
1983
1984
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)
1991
1992
1993 def create_named_conf(paths, realm, dnsdomain,
1994                       private_dir):
1995     """Write out a file containing zone statements suitable for inclusion in a
1996     named.conf file (including GSS-TSIG configuration).
1997
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
2003     """
2004
2005     setup_file(setup_path("named.conf"), paths.namedconf, {
2006             "DNSDOMAIN": dnsdomain,
2007             "REALM": realm,
2008             "ZONE_FILE": paths.dns,
2009             "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
2010             "NAMED_CONF": paths.namedconf,
2011             "NAMED_CONF_UPDATE": paths.namedconf_update
2012             })
2013
2014     setup_file(setup_path("named.conf.update"), paths.namedconf_update)
2015
2016
2017 def create_named_txt(path, realm, dnsdomain, dnsname, private_dir,
2018     keytab_name):
2019     """Write out a file containing zone statements suitable for inclusion in a
2020     named.conf file (including GSS-TSIG configuration).
2021
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
2027     """
2028     setup_file(setup_path("named.txt"), path, {
2029             "DNSDOMAIN": dnsdomain,
2030             "DNSNAME" : dnsname, 
2031             "REALM": realm,
2032             "DNS_KEYTAB": keytab_name,
2033             "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
2034             "PRIVATE_DIR": private_dir
2035         })
2036
2037
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).
2041
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
2046     """
2047     setup_file(setup_path("krb5.conf"), path, {
2048             "DNSDOMAIN": dnsdomain,
2049             "HOSTNAME": hostname,
2050             "REALM": realm,
2051         })
2052
2053
2054 class ProvisioningError(Exception):
2055     """A generic provision error."""
2056
2057     def __init__(self, value):
2058         self.value = value
2059
2060     def __str__(self):
2061         return "ProvisioningError: " + self.value
2062
2063
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)