4b2ce7cd225d8f4134773e8b648e99001ff216da
[mat/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     setup_ad_dns(samdb, names)
1105     # This is Samba4 specific and should be replaced by the correct
1106     # DNS AD-style setup
1107     setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1108               "DNSDOMAIN": names.dnsdomain,
1109               "DOMAINDN": names.domaindn,
1110               "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1111               "HOSTNAME" : names.hostname,
1112               "DNSNAME" : '%s.%s' % (
1113                   names.netbiosname.lower(), names.dnsdomain.lower())
1114               })
1115
1116
1117 def setup_ad_dns(samdb, names):
1118     setup_add_ldif(samdb, setup_path("provision_dns_add.ldif"), {
1119               "DOMAINDN": names.domaindn,
1120               "DNSNAME" : '%s.%s' % (
1121                   names.netbiosname.lower(), names.dnsdomain.lower())
1122               })
1123
1124 def getpolicypath(sysvolpath, dnsdomain, guid):
1125     """Return the physical path of policy given its guid.
1126
1127     :param sysvolpath: Path to the sysvol folder
1128     :param dnsdomain: DNS name of the AD domain
1129     :param guid: The GUID of the policy
1130     :return: A string with the complete path to the policy folder
1131     """
1132
1133     if guid[0] != "{":
1134         guid = "{%s}" % guid
1135     policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1136     return policy_path
1137
1138
1139 def create_gpo_struct(policy_path):
1140     if not os.path.exists(policy_path):
1141         os.makedirs(policy_path, 0775)
1142     open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1143                       "[General]\r\nVersion=0")
1144     p = os.path.join(policy_path, "MACHINE")
1145     if not os.path.exists(p):
1146         os.makedirs(p, 0775)
1147     p = os.path.join(policy_path, "USER")
1148     if not os.path.exists(p):
1149         os.makedirs(p, 0775)
1150
1151
1152 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1153     """Create the default GPO for a domain
1154
1155     :param sysvolpath: Physical path for the sysvol folder
1156     :param dnsdomain: DNS domain name of the AD domain
1157     :param policyguid: GUID of the default domain policy
1158     :param policyguid_dc: GUID of the default domain controler policy
1159     """
1160     policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1161     create_gpo_struct(policy_path)
1162
1163     policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1164     create_gpo_struct(policy_path)
1165
1166
1167 def setup_samdb(path, session_info, provision_backend, lp, names,
1168         logger, domainsid, domainguid, policyguid, policyguid_dc, fill,
1169         adminpass, krbtgtpass, machinepass, invocationid, dnspass, ntdsguid,
1170         serverrole, am_rodc=False, dom_for_fun_level=None, schema=None,
1171         next_rid=1000):
1172     """Setup a complete SAM Database.
1173
1174     :note: This will wipe the main SAM database file!
1175     """
1176
1177     # Provision does not make much sense values larger than 1000000000
1178     # as the upper range of the rIDAvailablePool is 1073741823 and
1179     # we don't want to create a domain that cannot allocate rids.
1180     if next_rid < 1000 or next_rid > 1000000000:
1181         error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1182         error += "the valid range is %u-%u. The default is %u." % (
1183             1000, 1000000000, 1000)
1184         raise ProvisioningError(error)
1185
1186     # ATTENTION: Do NOT change these default values without discussion with the
1187     # team and/or release manager. They have a big impact on the whole program!
1188     domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1189
1190     if dom_for_fun_level is None:
1191         dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1192
1193     if dom_for_fun_level > domainControllerFunctionality:
1194         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!")
1195
1196     domainFunctionality = dom_for_fun_level
1197     forestFunctionality = dom_for_fun_level
1198
1199     # Also wipes the database
1200     setup_samdb_partitions(path, logger=logger, lp=lp,
1201         provision_backend=provision_backend, session_info=session_info,
1202         names=names, serverrole=serverrole, schema=schema)
1203
1204     if schema is None:
1205         schema = Schema(domainsid, schemadn=names.schemadn)
1206
1207     # Load the database, but don's load the global schema and don't connect
1208     # quite yet
1209     samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1210                   credentials=provision_backend.credentials, lp=lp,
1211                   global_schema=False, am_rodc=am_rodc)
1212
1213     logger.info("Pre-loading the Samba 4 and AD schema")
1214
1215     # Load the schema from the one we computed earlier
1216     samdb.set_schema(schema)
1217
1218     # Set the NTDS settings DN manually - in order to have it already around
1219     # before the provisioned tree exists and we connect
1220     samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1221
1222     # And now we can connect to the DB - the schema won't be loaded from the
1223     # DB
1224     samdb.connect(path)
1225
1226     if fill == FILL_DRS:
1227         return samdb
1228
1229     samdb.transaction_start()
1230     try:
1231         # Set the domain functionality levels onto the database.
1232         # Various module (the password_hash module in particular) need
1233         # to know what level of AD we are emulating.
1234
1235         # These will be fixed into the database via the database
1236         # modifictions below, but we need them set from the start.
1237         samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1238         samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1239         samdb.set_opaque_integer("domainControllerFunctionality",
1240             domainControllerFunctionality)
1241
1242         samdb.set_domain_sid(str(domainsid))
1243         samdb.set_invocation_id(invocationid)
1244
1245         logger.info("Adding DomainDN: %s" % names.domaindn)
1246
1247         # impersonate domain admin
1248         admin_session_info = admin_session(lp, str(domainsid))
1249         samdb.set_session_info(admin_session_info)
1250         if domainguid is not None:
1251             domainguid_line = "objectGUID: %s\n-" % domainguid
1252         else:
1253             domainguid_line = ""
1254
1255         descr = b64encode(get_domain_descriptor(domainsid))
1256         setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1257                 "DOMAINDN": names.domaindn,
1258                 "DOMAINSID": str(domainsid),
1259                 "DESCRIPTOR": descr,
1260                 "DOMAINGUID": domainguid_line
1261                 })
1262
1263         setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1264             "DOMAINDN": names.domaindn,
1265             "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1266             "NEXTRID": str(next_rid),
1267             "DEFAULTSITE": names.sitename,
1268             "CONFIGDN": names.configdn,
1269             "POLICYGUID": policyguid,
1270             "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1271             "SAMBA_VERSION_STRING": version
1272             })
1273
1274         logger.info("Adding configuration container")
1275         descr = b64encode(get_config_descriptor(domainsid))
1276         setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1277             "CONFIGDN": names.configdn,
1278             "DESCRIPTOR": descr,
1279             })
1280
1281         # Now register this container in the root of the forest
1282         msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1283         msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1284                     "subRefs")
1285
1286         # The LDIF here was created when the Schema object was constructed
1287         logger.info("Setting up sam.ldb schema")
1288         samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1289         samdb.modify_ldif(schema.schema_dn_modify)
1290         samdb.write_prefixes_from_schema()
1291         samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1292         setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1293                        {"SCHEMADN": names.schemadn})
1294
1295         logger.info("Reopening sam.ldb with new schema")
1296     except Exception:
1297         samdb.transaction_cancel()
1298         raise
1299     else:
1300         samdb.transaction_commit()
1301
1302     samdb = SamDB(session_info=admin_session_info, auto_connect=False,
1303                 credentials=provision_backend.credentials, lp=lp,
1304                 global_schema=False, am_rodc=am_rodc)
1305
1306     # Set the NTDS settings DN manually - in order to have it already around
1307     # before the provisioned tree exists and we connect
1308     samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1309     samdb.connect(path)
1310
1311     samdb.transaction_start()
1312     try:
1313         samdb.invocation_id = invocationid
1314
1315         logger.info("Setting up sam.ldb configuration data")
1316         setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1317             "CONFIGDN": names.configdn,
1318             "NETBIOSNAME": names.netbiosname,
1319             "DEFAULTSITE": names.sitename,
1320             "DNSDOMAIN": names.dnsdomain,
1321             "DOMAIN": names.domain,
1322             "SCHEMADN": names.schemadn,
1323             "DOMAINDN": names.domaindn,
1324             "SERVERDN": names.serverdn,
1325             "FOREST_FUNCTIONALITY": str(forestFunctionality),
1326             "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1327             })
1328
1329         logger.info("Setting up display specifiers")
1330         display_specifiers_ldif = read_ms_ldif(
1331             setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1332         display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1333             {"CONFIGDN": names.configdn})
1334         check_all_substituted(display_specifiers_ldif)
1335         samdb.add_ldif(display_specifiers_ldif)
1336
1337         logger.info("Adding users container")
1338         setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1339                 "DOMAINDN": names.domaindn})
1340         logger.info("Modifying users container")
1341         setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1342                 "DOMAINDN": names.domaindn})
1343         logger.info("Adding computers container")
1344         setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1345                 "DOMAINDN": names.domaindn})
1346         logger.info("Modifying computers container")
1347         setup_modify_ldif(samdb,
1348             setup_path("provision_computers_modify.ldif"), {
1349                 "DOMAINDN": names.domaindn})
1350         logger.info("Setting up sam.ldb data")
1351         setup_add_ldif(samdb, setup_path("provision.ldif"), {
1352             "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1353             "DOMAINDN": names.domaindn,
1354             "NETBIOSNAME": names.netbiosname,
1355             "DEFAULTSITE": names.sitename,
1356             "CONFIGDN": names.configdn,
1357             "SERVERDN": names.serverdn,
1358             "RIDAVAILABLESTART": str(next_rid + 600),
1359             "POLICYGUID_DC": policyguid_dc
1360             })
1361
1362         setup_modify_ldif(samdb,
1363             setup_path("provision_basedn_references.ldif"), {
1364                 "DOMAINDN": names.domaindn})
1365
1366         setup_modify_ldif(samdb,
1367             setup_path("provision_configuration_references.ldif"), {
1368                 "CONFIGDN": names.configdn,
1369                 "SCHEMADN": names.schemadn})
1370         if fill == FILL_FULL:
1371             logger.info("Setting up sam.ldb users and groups")
1372             setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1373                 "DOMAINDN": names.domaindn,
1374                 "DOMAINSID": str(domainsid),
1375                 "CONFIGDN": names.configdn,
1376                 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1377                 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1378                 })
1379
1380             logger.info("Setting up self join")
1381             setup_self_join(samdb, names=names, invocationid=invocationid,
1382                 dnspass=dnspass,
1383                 machinepass=machinepass,
1384                 domainsid=domainsid,
1385                 next_rid=next_rid,
1386                 policyguid=policyguid,
1387                 policyguid_dc=policyguid_dc,
1388                 domainControllerFunctionality=domainControllerFunctionality,
1389                 ntdsguid=ntdsguid)
1390
1391             ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1392             names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1393                 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1394             assert isinstance(names.ntdsguid, str)
1395     except Exception:
1396         samdb.transaction_cancel()
1397         raise
1398     else:
1399         samdb.transaction_commit()
1400         return samdb
1401
1402
1403 FILL_FULL = "FULL"
1404 FILL_NT4SYNC = "NT4SYNC"
1405 FILL_DRS = "DRS"
1406 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1407 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)"
1408
1409
1410 def set_dir_acl(path, acl, lp, domsid):
1411     setntacl(lp, path, acl, domsid)
1412     for root, dirs, files in os.walk(path, topdown=False):
1413         for name in files:
1414             setntacl(lp, os.path.join(root, name), acl, domsid)
1415         for name in dirs:
1416             setntacl(lp, os.path.join(root, name), acl, domsid)
1417
1418
1419 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1420     """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1421     folders beneath.
1422
1423     :param sysvol: Physical path for the sysvol folder
1424     :param dnsdomain: The DNS name of the domain
1425     :param domainsid: The SID of the domain
1426     :param domaindn: The DN of the domain (ie. DC=...)
1427     :param samdb: An LDB object on the SAM db
1428     :param lp: an LP object
1429     """
1430
1431     # Set ACL for GPO root folder
1432     root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1433     setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid))
1434
1435     res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1436                         attrs=["cn", "nTSecurityDescriptor"],
1437                         expression="", scope=ldb.SCOPE_ONELEVEL)
1438
1439     for policy in res:
1440         acl = ndr_unpack(security.descriptor,
1441                          str(policy["nTSecurityDescriptor"])).as_sddl()
1442         policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1443         set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1444                     str(domainsid))
1445
1446
1447 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1448     lp):
1449     """Set the ACL for the sysvol share and the subfolders
1450
1451     :param samdb: An LDB object on the SAM db
1452     :param netlogon: Physical path for the netlogon folder
1453     :param sysvol: Physical path for the sysvol folder
1454     :param gid: The GID of the "Domain adminstrators" group
1455     :param domainsid: The SID of the domain
1456     :param dnsdomain: The DNS name of the domain
1457     :param domaindn: The DN of the domain (ie. DC=...)
1458     """
1459
1460     try:
1461         os.chown(sysvol, -1, gid)
1462     except OSError:
1463         canchown = False
1464     else:
1465         canchown = True
1466
1467     # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1468     setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid))
1469     for root, dirs, files in os.walk(sysvol, topdown=False):
1470         for name in files:
1471             if canchown:
1472                 os.chown(os.path.join(root, name), -1, gid)
1473             setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1474         for name in dirs:
1475             if canchown:
1476                 os.chown(os.path.join(root, name), -1, gid)
1477             setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1478
1479     # Set acls on Policy folder and policies folders
1480     set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp)
1481
1482
1483 def interface_ips_v4(lp):
1484     '''return only IPv4 IPs'''
1485     ips = samba.interface_ips(lp, False)
1486     ret = []
1487     for i in ips:
1488         if i.find(':') == -1:
1489             ret.append(i)
1490     return ret
1491
1492 def interface_ips_v6(lp, linklocal=False):
1493     '''return only IPv6 IPs'''
1494     ips = samba.interface_ips(lp, False)
1495     ret = []
1496     for i in ips:
1497         if i.find(':') != -1 and (linklocal or i.find('%') == -1):
1498             ret.append(i)
1499     return ret
1500
1501
1502 def provision(logger, session_info, credentials, smbconf=None,
1503         targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1504         domaindn=None, schemadn=None, configdn=None, serverdn=None,
1505         domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1506         next_rid=1000, adminpass=None, ldapadminpass=None, krbtgtpass=None,
1507         domainguid=None, policyguid=None, policyguid_dc=None,
1508         invocationid=None, machinepass=None, ntdsguid=None, dnspass=None,
1509         root=None, nobody=None, users=None, wheel=None, backup=None, aci=None,
1510         serverrole=None, dom_for_fun_level=None, ldap_backend_extra_port=None,
1511         ldap_backend_forced_uri=None, backend_type=None, sitename=None,
1512         ol_mmr_urls=None, ol_olc=None, setup_ds_path=None, slapd_path=None,
1513         nosync=False, ldap_dryrun_mode=False, useeadb=False, am_rodc=False,
1514         lp=None):
1515     """Provision samba4
1516
1517     :note: caution, this wipes all existing data!
1518     """
1519
1520     if domainsid is None:
1521         domainsid = security.random_sid()
1522     else:
1523         domainsid = security.dom_sid(domainsid)
1524
1525     # create/adapt the group policy GUIDs
1526     # Default GUID for default policy are described at
1527     # "How Core Group Policy Works"
1528     # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1529     if policyguid is None:
1530         policyguid = DEFAULT_POLICY_GUID
1531     policyguid = policyguid.upper()
1532     if policyguid_dc is None:
1533         policyguid_dc = DEFAULT_DC_POLICY_GUID
1534     policyguid_dc = policyguid_dc.upper()
1535
1536     if adminpass is None:
1537         adminpass = samba.generate_random_password(12, 32)
1538     if krbtgtpass is None:
1539         krbtgtpass = samba.generate_random_password(128, 255)
1540     if machinepass is None:
1541         machinepass  = samba.generate_random_password(128, 255)
1542     if dnspass is None:
1543         dnspass = samba.generate_random_password(128, 255)
1544     if ldapadminpass is None:
1545         # Make a new, random password between Samba and it's LDAP server
1546         ldapadminpass=samba.generate_random_password(128, 255)
1547
1548     if backend_type is None:
1549         backend_type = "ldb"
1550
1551     sid_generator = "internal"
1552     if backend_type == "fedora-ds":
1553         sid_generator = "backend"
1554
1555     root_uid = findnss_uid([root or "root"])
1556     nobody_uid = findnss_uid([nobody or "nobody"])
1557     users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1558     if wheel is None:
1559         wheel_gid = findnss_gid(["wheel", "adm"])
1560     else:
1561         wheel_gid = findnss_gid([wheel])
1562     try:
1563         bind_gid = findnss_gid(["bind", "named"])
1564     except KeyError:
1565         bind_gid = None
1566
1567     if targetdir is not None:
1568         smbconf = os.path.join(targetdir, "etc", "smb.conf")
1569     elif smbconf is None:
1570         smbconf = samba.param.default_path()
1571     if not os.path.exists(os.path.dirname(smbconf)):
1572         os.makedirs(os.path.dirname(smbconf))
1573
1574     # only install a new smb.conf if there isn't one there already
1575     if os.path.exists(smbconf):
1576         # if Samba Team members can't figure out the weird errors
1577         # loading an empty smb.conf gives, then we need to be smarter.
1578         # Pretend it just didn't exist --abartlet
1579         data = open(smbconf, 'r').read()
1580         data = data.lstrip()
1581         if data is None or data == "":
1582             make_smbconf(smbconf, hostname, domain, realm,
1583                          serverrole, targetdir, sid_generator, useeadb,
1584                          lp=lp)
1585     else:
1586         make_smbconf(smbconf, hostname, domain, realm, serverrole,
1587                      targetdir, sid_generator, useeadb, lp=lp)
1588
1589     if lp is None:
1590         lp = samba.param.LoadParm()
1591     lp.load(smbconf)
1592     names = guess_names(lp=lp, hostname=hostname, domain=domain,
1593         dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
1594         configdn=configdn, schemadn=schemadn, serverdn=serverdn,
1595         sitename=sitename)
1596     paths = provision_paths_from_lp(lp, names.dnsdomain)
1597
1598     paths.bind_gid = bind_gid
1599
1600     if hostip is None:
1601         logger.info("Looking up IPv4 addresses")
1602         hostips = interface_ips_v4(lp)
1603         if len(hostips) > 0:
1604             hostip = hostips[0]
1605             if len(hostips) > 1:
1606                 logger.warning("More than one IPv4 address found. Using %s",
1607                     hostip)
1608     if hostip == "127.0.0.1":
1609         hostip = None
1610     if hostip is None:
1611         logger.warning("No IPv4 address will be assigned")
1612
1613     if hostip6 is None:
1614         logger.info("Looking up IPv6 addresses")
1615         hostips = interface_ips_v6(lp, linklocal=False)
1616         if hostips:
1617             hostip6 = hostips[0]
1618         if len(hostips) > 1:
1619             logger.warning("More than one IPv6 address found. Using %s", hostip6)
1620     if hostip6 is None:
1621         logger.warning("No IPv6 address will be assigned")
1622
1623     if serverrole is None:
1624         serverrole = lp.get("server role")
1625
1626     assert serverrole in ("domain controller", "member server", "standalone")
1627     if invocationid is None:
1628         invocationid = str(uuid.uuid4())
1629
1630     if not os.path.exists(paths.private_dir):
1631         os.mkdir(paths.private_dir)
1632     if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1633         os.mkdir(os.path.join(paths.private_dir, "tls"))
1634
1635     ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1636
1637     schema = Schema(domainsid, invocationid=invocationid,
1638         schemadn=names.schemadn)
1639
1640     if backend_type == "ldb":
1641         provision_backend = LDBBackend(backend_type, paths=paths,
1642             lp=lp, credentials=credentials,
1643             names=names, logger=logger)
1644     elif backend_type == "existing":
1645         provision_backend = ExistingBackend(backend_type, paths=paths,
1646             lp=lp, credentials=credentials,
1647             names=names, logger=logger,
1648             ldap_backend_forced_uri=ldap_backend_forced_uri)
1649     elif backend_type == "fedora-ds":
1650         provision_backend = FDSBackend(backend_type, paths=paths,
1651             lp=lp, credentials=credentials,
1652             names=names, logger=logger, domainsid=domainsid,
1653             schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1654             slapd_path=slapd_path,
1655             ldap_backend_extra_port=ldap_backend_extra_port,
1656             ldap_dryrun_mode=ldap_dryrun_mode, root=root,
1657             setup_ds_path=setup_ds_path,
1658             ldap_backend_forced_uri=ldap_backend_forced_uri)
1659     elif backend_type == "openldap":
1660         provision_backend = OpenLDAPBackend(backend_type, paths=paths,
1661             lp=lp, credentials=credentials,
1662             names=names, logger=logger, domainsid=domainsid,
1663             schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1664             slapd_path=slapd_path,
1665             ldap_backend_extra_port=ldap_backend_extra_port,
1666             ldap_dryrun_mode=ldap_dryrun_mode, ol_mmr_urls=ol_mmr_urls,
1667             nosync=nosync,
1668             ldap_backend_forced_uri=ldap_backend_forced_uri)
1669     else:
1670         raise ValueError("Unknown LDAP backend type selected")
1671
1672     provision_backend.init()
1673     provision_backend.start()
1674
1675     # only install a new shares config db if there is none
1676     if not os.path.exists(paths.shareconf):
1677         logger.info("Setting up share.ldb")
1678         share_ldb = Ldb(paths.shareconf, session_info=session_info,
1679                         lp=lp)
1680         share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1681
1682     logger.info("Setting up secrets.ldb")
1683     secrets_ldb = setup_secretsdb(paths,
1684         session_info=session_info,
1685         backend_credentials=provision_backend.secrets_credentials, lp=lp)
1686
1687     try:
1688         logger.info("Setting up the registry")
1689         setup_registry(paths.hklm, session_info,
1690                        lp=lp)
1691
1692         logger.info("Setting up the privileges database")
1693         setup_privileges(paths.privilege, session_info, lp=lp)
1694
1695         logger.info("Setting up idmap db")
1696         idmap = setup_idmapdb(paths.idmapdb,
1697             session_info=session_info, lp=lp)
1698
1699         logger.info("Setting up SAM db")
1700         samdb = setup_samdb(paths.samdb, session_info,
1701             provision_backend, lp, names, logger=logger,
1702             domainsid=domainsid, schema=schema, domainguid=domainguid,
1703             policyguid=policyguid, policyguid_dc=policyguid_dc,
1704             fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1705             invocationid=invocationid, machinepass=machinepass,
1706             dnspass=dnspass, ntdsguid=ntdsguid, serverrole=serverrole,
1707             dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1708             next_rid=next_rid)
1709
1710         if serverrole == "domain controller":
1711             if paths.netlogon is None:
1712                 logger.info("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1713                 logger.info("Please either remove %s or see the template at %s" %
1714                         (paths.smbconf, setup_path("provision.smb.conf.dc")))
1715                 assert paths.netlogon is not None
1716
1717             if paths.sysvol is None:
1718                 logger.info("Existing smb.conf does not have a [sysvol] share, but you"
1719                         " are configuring a DC.")
1720                 logger.info("Please either remove %s or see the template at %s" %
1721                         (paths.smbconf, setup_path("provision.smb.conf.dc")))
1722                 assert paths.sysvol is not None
1723
1724             if not os.path.isdir(paths.netlogon):
1725                 os.makedirs(paths.netlogon, 0755)
1726
1727         if samdb_fill == FILL_FULL:
1728             setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1729                                 root_uid=root_uid, nobody_uid=nobody_uid,
1730                                 users_gid=users_gid, wheel_gid=wheel_gid)
1731
1732             if serverrole == "domain controller":
1733                 # Set up group policies (domain policy and domain controller
1734                 # policy)
1735                 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1736                     policyguid_dc)
1737                 setsysvolacl(samdb, paths.netlogon, paths.sysvol, wheel_gid,
1738                     domainsid, names.dnsdomain, names.domaindn, lp)
1739
1740             logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1741             setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1742
1743             secretsdb_self_join(secrets_ldb, domain=names.domain,
1744                 realm=names.realm, dnsdomain=names.dnsdomain,
1745                 netbiosname=names.netbiosname, domainsid=domainsid,
1746                 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1747
1748             # Now set up the right msDS-SupportedEncryptionTypes into the DB
1749             # In future, this might be determined from some configuration
1750             kerberos_enctypes = str(ENC_ALL_TYPES)
1751
1752             try:
1753                 msg = ldb.Message(ldb.Dn(samdb,
1754                     samdb.searchone("distinguishedName",
1755                         expression="samAccountName=%s$" % names.netbiosname,
1756                         scope=ldb.SCOPE_SUBTREE)))
1757                 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1758                     elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1759                     name="msDS-SupportedEncryptionTypes")
1760                 samdb.modify(msg)
1761             except ldb.LdbError, (enum, estr):
1762                 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1763                     # It might be that this attribute does not exist in this schema
1764                     raise
1765
1766             if serverrole == "domain controller":
1767                 secretsdb_setup_dns(secrets_ldb, names,
1768                     paths.private_dir, realm=names.realm,
1769                     dnsdomain=names.dnsdomain,
1770                     dns_keytab_path=paths.dns_keytab, dnspass=dnspass)
1771
1772                 domainguid = samdb.searchone(basedn=domaindn,
1773                     attribute="objectGUID")
1774                 assert isinstance(domainguid, str)
1775
1776                 # Only make a zone file on the first DC, it should be
1777                 # replicated with DNS replication
1778                 create_zone_file(lp, logger, paths, targetdir,
1779                     dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
1780                     hostname=names.hostname, realm=names.realm,
1781                     domainguid=domainguid, ntdsguid=names.ntdsguid)
1782
1783                 create_named_conf(paths, realm=names.realm,
1784                     dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1785
1786                 create_named_txt(paths.namedtxt,
1787                     realm=names.realm, dnsdomain=names.dnsdomain,
1788                     dnsname = "%s.%s" % (names.hostname, names.dnsdomain),
1789                     private_dir=paths.private_dir,
1790                     keytab_name=paths.dns_keytab)
1791                 logger.info("See %s for an example configuration include file for BIND", paths.namedconf)
1792                 logger.info("and %s for further documentation required for secure DNS "
1793                         "updates", paths.namedtxt)
1794
1795             lastProvisionUSNs = get_last_provision_usn(samdb)
1796             maxUSN = get_max_usn(samdb, str(names.rootdn))
1797             if lastProvisionUSNs is not None:
1798                 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1799             else:
1800                 set_provision_usn(samdb, 0, maxUSN, invocationid)
1801
1802         create_krb5_conf(paths.krb5conf,
1803                          dnsdomain=names.dnsdomain, hostname=names.hostname,
1804                          realm=names.realm)
1805         logger.info("A Kerberos configuration suitable for Samba 4 has been "
1806                     "generated at %s", paths.krb5conf)
1807
1808         if serverrole == "domain controller":
1809             create_dns_update_list(lp, logger, paths)
1810
1811         provision_backend.post_setup()
1812         provision_backend.shutdown()
1813
1814         create_phpldapadmin_config(paths.phpldapadminconfig,
1815                                    ldapi_url)
1816     except Exception:
1817         secrets_ldb.transaction_cancel()
1818         raise
1819
1820     # Now commit the secrets.ldb to disk
1821     secrets_ldb.transaction_commit()
1822
1823     # the commit creates the dns.keytab, now chown it
1824     dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1825     if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
1826         try:
1827             os.chmod(dns_keytab_path, 0640)
1828             os.chown(dns_keytab_path, -1, paths.bind_gid)
1829         except OSError:
1830             if not os.environ.has_key('SAMBA_SELFTEST'):
1831                 logger.info("Failed to chown %s to bind gid %u",
1832                             dns_keytab_path, paths.bind_gid)
1833
1834
1835     logger.info("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php",
1836             paths.phpldapadminconfig)
1837
1838     logger.info("Once the above files are installed, your Samba4 server will be ready to use")
1839     logger.info("Server Role:           %s" % serverrole)
1840     logger.info("Hostname:              %s" % names.hostname)
1841     logger.info("NetBIOS Domain:        %s" % names.domain)
1842     logger.info("DNS Domain:            %s" % names.dnsdomain)
1843     logger.info("DOMAIN SID:            %s" % str(domainsid))
1844     if samdb_fill == FILL_FULL:
1845         logger.info("Admin password:        %s" % adminpass)
1846     if provision_backend.type is not "ldb":
1847         if provision_backend.credentials.get_bind_dn() is not None:
1848             logger.info("LDAP Backend Admin DN: %s" %
1849                 provision_backend.credentials.get_bind_dn())
1850         else:
1851             logger.info("LDAP Admin User:       %s" %
1852                 provision_backend.credentials.get_username())
1853
1854         logger.info("LDAP Admin Password:   %s" %
1855             provision_backend.credentials.get_password())
1856
1857         if provision_backend.slapd_command_escaped is not None:
1858             # now display slapd_command_file.txt to show how slapd must be
1859             # started next time
1860             logger.info("Use later the following commandline to start slapd, then Samba:")
1861             logger.info(provision_backend.slapd_command_escaped)
1862             logger.info("This slapd-Commandline is also stored under: %s/ldap_backend_startup.sh",
1863                     provision_backend.ldapdir)
1864
1865     result = ProvisionResult()
1866     result.domaindn = domaindn
1867     result.paths = paths
1868     result.lp = lp
1869     result.samdb = samdb
1870     return result
1871
1872
1873 def provision_become_dc(smbconf=None, targetdir=None,
1874         realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
1875         serverdn=None, domain=None, hostname=None, domainsid=None,
1876         adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
1877         policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
1878         root=None, nobody=None, users=None, wheel=None, backup=None,
1879         serverrole=None, ldap_backend=None, ldap_backend_type=None,
1880         sitename=None, debuglevel=1):
1881
1882     logger = logging.getLogger("provision")
1883     samba.set_debug_level(debuglevel)
1884
1885     res = provision(logger, system_session(), None,
1886         smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1887         realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1888         configdn=configdn, serverdn=serverdn, domain=domain,
1889         hostname=hostname, hostip=None, domainsid=domainsid,
1890         machinepass=machinepass, serverrole="domain controller",
1891         sitename=sitename)
1892     res.lp.set("debuglevel", str(debuglevel))
1893     return res
1894
1895
1896 def create_phpldapadmin_config(path, ldapi_uri):
1897     """Create a PHP LDAP admin configuration file.
1898
1899     :param path: Path to write the configuration to.
1900     """
1901     setup_file(setup_path("phpldapadmin-config.php"), path,
1902             {"S4_LDAPI_URI": ldapi_uri})
1903
1904
1905 def create_zone_file(lp, logger, paths, targetdir, dnsdomain,
1906                      hostip, hostip6, hostname, realm, domainguid,
1907                      ntdsguid):
1908     """Write out a DNS zone file, from the info in the current database.
1909
1910     :param paths: paths object
1911     :param dnsdomain: DNS Domain name
1912     :param domaindn: DN of the Domain
1913     :param hostip: Local IPv4 IP
1914     :param hostip6: Local IPv6 IP
1915     :param hostname: Local hostname
1916     :param realm: Realm name
1917     :param domainguid: GUID of the domain.
1918     :param ntdsguid: GUID of the hosts nTDSDSA record.
1919     """
1920     assert isinstance(domainguid, str)
1921
1922     if hostip6 is not None:
1923         hostip6_base_line = "            IN AAAA    " + hostip6
1924         hostip6_host_line = hostname + "        IN AAAA    " + hostip6
1925         gc_msdcs_ip6_line = "gc._msdcs               IN AAAA    " + hostip6
1926     else:
1927         hostip6_base_line = ""
1928         hostip6_host_line = ""
1929         gc_msdcs_ip6_line = ""
1930
1931     if hostip is not None:
1932         hostip_base_line = "            IN A    " + hostip
1933         hostip_host_line = hostname + "        IN A    " + hostip
1934         gc_msdcs_ip_line = "gc._msdcs               IN A    " + hostip
1935     else:
1936         hostip_base_line = ""
1937         hostip_host_line = ""
1938         gc_msdcs_ip_line = ""
1939
1940     dns_dir = os.path.dirname(paths.dns)
1941
1942     try:
1943         shutil.rmtree(dns_dir, True)
1944     except OSError:
1945         pass
1946
1947     os.mkdir(dns_dir, 0775)
1948
1949     # we need to freeze the zone while we update the contents
1950     if targetdir is None:
1951         rndc = ' '.join(lp.get("rndc command"))
1952         os.system(rndc + " freeze " + lp.get("realm"))
1953
1954     setup_file(setup_path("provision.zone"), paths.dns, {
1955             "HOSTNAME": hostname,
1956             "DNSDOMAIN": dnsdomain,
1957             "REALM": realm,
1958             "HOSTIP_BASE_LINE": hostip_base_line,
1959             "HOSTIP_HOST_LINE": hostip_host_line,
1960             "DOMAINGUID": domainguid,
1961             "DATESTRING": time.strftime("%Y%m%d%H"),
1962             "DEFAULTSITE": DEFAULTSITE,
1963             "NTDSGUID": ntdsguid,
1964             "HOSTIP6_BASE_LINE": hostip6_base_line,
1965             "HOSTIP6_HOST_LINE": hostip6_host_line,
1966             "GC_MSDCS_IP_LINE": gc_msdcs_ip_line,
1967             "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line,
1968         })
1969
1970     # note that we use no variable substitution on this file
1971     # the substitution is done at runtime by samba_dnsupdate
1972     setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1973
1974     # and the SPN update list
1975     setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1976
1977     if paths.bind_gid is not None:
1978         try:
1979             os.chown(dns_dir, -1, paths.bind_gid)
1980             os.chown(paths.dns, -1, paths.bind_gid)
1981             # chmod needed to cope with umask
1982             os.chmod(dns_dir, 0775)
1983             os.chmod(paths.dns, 0664)
1984         except OSError:
1985             if not os.environ.has_key('SAMBA_SELFTEST'):
1986                 logger.error("Failed to chown %s to bind gid %u" % (
1987                     dns_dir, paths.bind_gid))
1988
1989     if targetdir is None:
1990         os.system(rndc + " unfreeze " + lp.get("realm"))
1991
1992
1993 def create_dns_update_list(lp, logger, paths):
1994     """Write out a dns_update_list file"""
1995     # note that we use no variable substitution on this file
1996     # the substitution is done at runtime by samba_dnsupdate
1997     setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1998     setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1999
2000
2001 def create_named_conf(paths, realm, dnsdomain,
2002                       private_dir):
2003     """Write out a file containing zone statements suitable for inclusion in a
2004     named.conf file (including GSS-TSIG configuration).
2005
2006     :param paths: all paths
2007     :param realm: Realm name
2008     :param dnsdomain: DNS Domain name
2009     :param private_dir: Path to private directory
2010     :param keytab_name: File name of DNS keytab file
2011     """
2012
2013     setup_file(setup_path("named.conf"), paths.namedconf, {
2014             "DNSDOMAIN": dnsdomain,
2015             "REALM": realm,
2016             "ZONE_FILE": paths.dns,
2017             "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
2018             "NAMED_CONF": paths.namedconf,
2019             "NAMED_CONF_UPDATE": paths.namedconf_update
2020             })
2021
2022     setup_file(setup_path("named.conf.update"), paths.namedconf_update)
2023
2024
2025 def create_named_txt(path, realm, dnsdomain, dnsname, private_dir,
2026     keytab_name):
2027     """Write out a file containing zone statements suitable for inclusion in a
2028     named.conf file (including GSS-TSIG configuration).
2029
2030     :param path: Path of the new named.conf file.
2031     :param realm: Realm name
2032     :param dnsdomain: DNS Domain name
2033     :param private_dir: Path to private directory
2034     :param keytab_name: File name of DNS keytab file
2035     """
2036     setup_file(setup_path("named.txt"), path, {
2037             "DNSDOMAIN": dnsdomain,
2038             "DNSNAME" : dnsname, 
2039             "REALM": realm,
2040             "DNS_KEYTAB": keytab_name,
2041             "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
2042             "PRIVATE_DIR": private_dir
2043         })
2044
2045
2046 def create_krb5_conf(path, dnsdomain, hostname, realm):
2047     """Write out a file containing zone statements suitable for inclusion in a
2048     named.conf file (including GSS-TSIG configuration).
2049
2050     :param path: Path of the new named.conf file.
2051     :param dnsdomain: DNS Domain name
2052     :param hostname: Local hostname
2053     :param realm: Realm name
2054     """
2055     setup_file(setup_path("krb5.conf"), path, {
2056             "DNSDOMAIN": dnsdomain,
2057             "HOSTNAME": hostname,
2058             "REALM": realm,
2059         })
2060
2061
2062 class ProvisioningError(Exception):
2063     """A generic provision error."""
2064
2065     def __init__(self, value):
2066         self.value = value
2067
2068     def __str__(self):
2069         return "ProvisioningError: " + self.value
2070
2071
2072 class InvalidNetbiosName(Exception):
2073     """A specified name was not a valid NetBIOS name."""
2074     def __init__(self, name):
2075         super(InvalidNetbiosName, self).__init__(
2076             "The name '%r' is not a valid NetBIOS name" % name)