s4-provision: fix the value of creation timestamp to be a MS timestamp
[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 from samba.provision.sambadns import setup_ad_dns
78
79 import samba.param
80 import samba.registry
81 from samba.schema import Schema
82 from samba.samdb import SamDB
83
84 VALID_NETBIOS_CHARS = " !#$%&'()-.@^_{}~"
85 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
86 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04fB984F9"
87 DEFAULTSITE = "Default-First-Site-Name"
88 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
89
90
91 def setup_path(file):
92     """Return an absolute path to the provision tempate file specified by file"""
93     return os.path.join(samba.param.setup_dir(), file)
94
95 # Descriptors of naming contexts and other important objects
96
97 # "get_schema_descriptor" is located in "schema.py"
98
99 def get_config_descriptor(domain_sid):
100     sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
101            "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
102            "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
103            "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
104            "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
105            "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
106            "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
107            "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
108            "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
109            "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
110            "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
111            "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
112            "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
113            "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
114            "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
115     sec = security.descriptor.from_sddl(sddl, domain_sid)
116     return ndr_pack(sec)
117
118
119 def get_domain_descriptor(domain_sid):
120     sddl= "O:BAG:BAD:AI(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
121         "(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
122     "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
123     "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
124     "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
125     "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
126     "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
127     "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
128     "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
129     "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
130     "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
131     "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)" \
132     "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)" \
133     "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)" \
134     "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)" \
135     "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
136     "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
137     "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
138     "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
139     "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
140     "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
141     "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;IF)" \
142     "(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)" \
143     "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)" \
144     "(OA;CIIO;RPLCLORC;;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
145     "(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)" \
146     "(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
147     "(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)" \
148     "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
149     "(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)" \
150     "(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)" \
151     "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
152     "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
153     "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
154     "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
155     "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)" \
156     "(OA;CIIO;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \
157     "(A;;RPWPCRCCLCLORCWOWDSW;;;DA)" \
158     "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
159     "(A;;RPRC;;;RU)" \
160     "(A;CI;LC;;;RU)" \
161     "(A;CI;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
162     "(A;;RP;;;WD)" \
163     "(A;;RPLCLORC;;;ED)" \
164     "(A;;RPLCLORC;;;AU)" \
165     "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
166     "S:AI(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
167     "(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
168     "(AU;SA;CR;;;DU)(AU;SA;CR;;;BA)(AU;SA;WPWOWD;;;WD)"
169     sec = security.descriptor.from_sddl(sddl, domain_sid)
170     return ndr_pack(sec)
171
172
173 class ProvisionPaths(object):
174
175     def __init__(self):
176         self.shareconf = None
177         self.hklm = None
178         self.hkcu = None
179         self.hkcr = None
180         self.hku = None
181         self.hkpd = None
182         self.hkpt = None
183         self.samdb = None
184         self.idmapdb = None
185         self.secrets = None
186         self.keytab = None
187         self.dns_keytab = None
188         self.dns = None
189         self.winsdb = None
190         self.private_dir = None
191
192
193 class ProvisionNames(object):
194
195     def __init__(self):
196         self.rootdn = None
197         self.domaindn = None
198         self.configdn = None
199         self.schemadn = None
200         self.ldapmanagerdn = None
201         self.dnsdomain = None
202         self.realm = None
203         self.netbiosname = None
204         self.domain = None
205         self.hostname = None
206         self.sitename = None
207         self.smbconf = None
208
209 def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf, lp):
210     """Get key provision parameters (realm, domain, ...) from a given provision
211
212     :param samdb: An LDB object connected to the sam.ldb file
213     :param secretsdb: An LDB object connected to the secrets.ldb file
214     :param idmapdb: An LDB object connected to the idmap.ldb file
215     :param paths: A list of path to provision object
216     :param smbconf: Path to the smb.conf file
217     :param lp: A LoadParm object
218     :return: A list of key provision parameters
219     """
220     names = ProvisionNames()
221     names.adminpass = None
222
223     # NT domain, kerberos realm, root dn, domain dn, domain dns name
224     names.domain = string.upper(lp.get("workgroup"))
225     names.realm = lp.get("realm")
226     basedn = "DC=" + names.realm.replace(".",",DC=")
227     names.dnsdomain = names.realm.lower()
228     names.realm = string.upper(names.realm)
229     # netbiosname
230     # Get the netbiosname first (could be obtained from smb.conf in theory)
231     res = secretsdb.search(expression="(flatname=%s)" %
232                             names.domain,base="CN=Primary Domains",
233                             scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"])
234     names.netbiosname = str(res[0]["sAMAccountName"]).replace("$","")
235
236     names.smbconf = smbconf
237
238     # That's a bit simplistic but it's ok as long as we have only 3
239     # partitions
240     current = samdb.search(expression="(objectClass=*)",
241         base="", scope=ldb.SCOPE_BASE,
242         attrs=["defaultNamingContext", "schemaNamingContext",
243                "configurationNamingContext","rootDomainNamingContext"])
244
245     names.configdn = current[0]["configurationNamingContext"]
246     configdn = str(names.configdn)
247     names.schemadn = current[0]["schemaNamingContext"]
248     if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb,
249                                        current[0]["defaultNamingContext"][0]))):
250         raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
251                                  "is not the same ..." % (paths.samdb,
252                                     str(current[0]["defaultNamingContext"][0]),
253                                     paths.smbconf, basedn)))
254
255     names.domaindn=current[0]["defaultNamingContext"]
256     names.rootdn=current[0]["rootDomainNamingContext"]
257     # default site name
258     res3 = samdb.search(expression="(objectClass=site)",
259         base="CN=Sites," + configdn, scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
260     names.sitename = str(res3[0]["cn"])
261
262     # dns hostname and server dn
263     res4 = samdb.search(expression="(CN=%s)" % names.netbiosname,
264                             base="OU=Domain Controllers,%s" % basedn,
265                             scope=ldb.SCOPE_ONELEVEL, attrs=["dNSHostName"])
266     names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain,"")
267
268     server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
269                                 attrs=[], base=configdn)
270     names.serverdn = server_res[0].dn
271
272     # invocation id/objectguid
273     res5 = samdb.search(expression="(objectClass=*)",
274             base="CN=NTDS Settings,%s" % str(names.serverdn), scope=ldb.SCOPE_BASE,
275             attrs=["invocationID", "objectGUID"])
276     names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0]))
277     names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0]))
278
279     # domain guid/sid
280     res6 = samdb.search(expression="(objectClass=*)", base=basedn,
281             scope=ldb.SCOPE_BASE, attrs=["objectGUID",
282                 "objectSid","msDS-Behavior-Version" ])
283     names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
284     names.domainsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
285     if res6[0].get("msDS-Behavior-Version") is None or \
286         int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
287         names.domainlevel = DS_DOMAIN_FUNCTION_2000
288     else:
289         names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
290
291     # policy guid
292     res7 = samdb.search(expression="(displayName=Default Domain Policy)",
293                         base="CN=Policies,CN=System," + basedn,
294                         scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
295     names.policyid = str(res7[0]["cn"]).replace("{","").replace("}","")
296     # dc policy guid
297     res8 = samdb.search(expression="(displayName=Default Domain Controllers"
298                                    " Policy)",
299                             base="CN=Policies,CN=System," + basedn,
300                             scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
301     if len(res8) == 1:
302         names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","")
303     else:
304         names.policyid_dc = None
305     res9 = idmapdb.search(expression="(cn=%s)" %
306                             (security.SID_BUILTIN_ADMINISTRATORS),
307                             attrs=["xidNumber"])
308     if len(res9) == 1:
309         names.wheel_gid = res9[0]["xidNumber"]
310     else:
311         raise ProvisioningError("Unable to find uid/gid for Domain Admins rid")
312     return names
313
314 def update_provision_usn(samdb, low, high, id, replace=False):
315     """Update the field provisionUSN in sam.ldb
316
317     This field is used to track range of USN modified by provision and
318     upgradeprovision.
319     This value is used afterward by next provision to figure out if
320     the field have been modified since last provision.
321
322     :param samdb: An LDB object connect to sam.ldb
323     :param low: The lowest USN modified by this upgrade
324     :param high: The highest USN modified by this upgrade
325     :param id: The invocation id of the samba's dc
326     :param replace: A boolean indicating if the range should replace any
327                     existing one or appended (default)
328     """
329
330     tab = []
331     if not replace:
332         entry = samdb.search(expression="(&(dn=@PROVISION)(%s=*))" %
333                                 LAST_PROVISION_USN_ATTRIBUTE, base="",
334                                 scope=ldb.SCOPE_SUBTREE,
335                                 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
336         for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
337             if not re.search(';', e):
338                 e = "%s;%s" % (e, id)
339             tab.append(str(e))
340
341     tab.append("%s-%s;%s" % (low, high, id))
342     delta = ldb.Message()
343     delta.dn = ldb.Dn(samdb, "@PROVISION")
344     delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
345         ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE)
346     entry = samdb.search(expression="(&(dn=@PROVISION)(provisionnerID=*))",
347                             base="", scope=ldb.SCOPE_SUBTREE,
348                             attrs=["provisionnerID"])
349     if len(entry) == 0 or len(entry[0]) == 0:
350         delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
351     samdb.modify(delta)
352
353
354 def set_provision_usn(samdb, low, high, id):
355     """Set the field provisionUSN in sam.ldb
356     This field is used to track range of USN modified by provision and
357     upgradeprovision.
358     This value is used afterward by next provision to figure out if
359     the field have been modified since last provision.
360
361     :param samdb: An LDB object connect to sam.ldb
362     :param low: The lowest USN modified by this upgrade
363     :param high: The highest USN modified by this upgrade
364     :param id: The invocationId of the provision"""
365
366     tab = []
367     tab.append("%s-%s;%s" % (low, high, id))
368
369     delta = ldb.Message()
370     delta.dn = ldb.Dn(samdb, "@PROVISION")
371     delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
372         ldb.FLAG_MOD_ADD, LAST_PROVISION_USN_ATTRIBUTE)
373     samdb.add(delta)
374
375
376 def get_max_usn(samdb,basedn):
377     """ This function return the biggest USN present in the provision
378
379     :param samdb: A LDB object pointing to the sam.ldb
380     :param basedn: A string containing the base DN of the provision
381                     (ie. DC=foo, DC=bar)
382     :return: The biggest USN in the provision"""
383
384     res = samdb.search(expression="objectClass=*",base=basedn,
385                          scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
386                          controls=["search_options:1:2",
387                                    "server_sort:1:1:uSNChanged",
388                                    "paged_results:1:1"])
389     return res[0]["uSNChanged"]
390
391
392 def get_last_provision_usn(sam):
393     """Get USNs ranges modified by a provision or an upgradeprovision
394
395     :param sam: An LDB object pointing to the sam.ldb
396     :return: a dictionnary which keys are invocation id and values are an array
397              of integer representing the different ranges
398     """
399     entry = sam.search(expression="(&(dn=@PROVISION)(%s=*))" %
400                         LAST_PROVISION_USN_ATTRIBUTE,
401                         base="", scope=ldb.SCOPE_SUBTREE,
402                         attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
403     if len(entry):
404         myids = []
405         range = {}
406         p = re.compile(r'-')
407         if entry[0].get("provisionnerID"):
408             for e in entry[0]["provisionnerID"]:
409                 myids.append(str(e))
410         for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
411             tab1 = str(r).split(';')
412             if len(tab1) == 2:
413                 id = tab1[1]
414             else:
415                 id = "default"
416             if (len(myids) > 0 and id not in myids):
417                 continue
418             tab2 = p.split(tab1[0])
419             if range.get(id) == None:
420                 range[id] = []
421             range[id].append(tab2[0])
422             range[id].append(tab2[1])
423         return range
424     else:
425         return None
426
427
428 class ProvisionResult(object):
429
430     def __init__(self):
431         self.paths = None
432         self.domaindn = None
433         self.lp = None
434         self.samdb = None
435
436
437 def check_install(lp, session_info, credentials):
438     """Check whether the current install seems ok.
439
440     :param lp: Loadparm context
441     :param session_info: Session information
442     :param credentials: Credentials
443     """
444     if lp.get("realm") == "":
445         raise Exception("Realm empty")
446     samdb = Ldb(lp.samdb_url(), session_info=session_info,
447             credentials=credentials, lp=lp)
448     if len(samdb.search("(cn=Administrator)")) != 1:
449         raise ProvisioningError("No administrator account found")
450
451
452 def findnss(nssfn, names):
453     """Find a user or group from a list of possibilities.
454
455     :param nssfn: NSS Function to try (should raise KeyError if not found)
456     :param names: Names to check.
457     :return: Value return by first names list.
458     """
459     for name in names:
460         try:
461             return nssfn(name)
462         except KeyError:
463             pass
464     raise KeyError("Unable to find user/group in %r" % names)
465
466
467 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
468 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
469
470
471 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
472     """Setup a ldb in the private dir.
473
474     :param ldb: LDB file to import data into
475     :param ldif_path: Path of the LDIF file to load
476     :param subst_vars: Optional variables to subsitute in LDIF.
477     :param nocontrols: Optional list of controls, can be None for no controls
478     """
479     assert isinstance(ldif_path, str)
480     data = read_and_sub_file(ldif_path, subst_vars)
481     ldb.add_ldif(data, controls)
482
483
484 def setup_modify_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
485     """Modify a ldb in the private dir.
486
487     :param ldb: LDB object.
488     :param ldif_path: LDIF file path.
489     :param subst_vars: Optional dictionary with substitution variables.
490     """
491     data = read_and_sub_file(ldif_path, subst_vars)
492     ldb.modify_ldif(data, controls)
493
494
495 def setup_ldb(ldb, ldif_path, subst_vars):
496     """Import a LDIF a file into a LDB handle, optionally substituting
497     variables.
498
499     :note: Either all LDIF data will be added or none (using transactions).
500
501     :param ldb: LDB file to import into.
502     :param ldif_path: Path to the LDIF file.
503     :param subst_vars: Dictionary with substitution variables.
504     """
505     assert ldb is not None
506     ldb.transaction_start()
507     try:
508         setup_add_ldif(ldb, ldif_path, subst_vars)
509     except Exception:
510         ldb.transaction_cancel()
511         raise
512     else:
513         ldb.transaction_commit()
514
515
516 def provision_paths_from_lp(lp, dnsdomain):
517     """Set the default paths for provisioning.
518
519     :param lp: Loadparm context.
520     :param dnsdomain: DNS Domain name
521     """
522     paths = ProvisionPaths()
523     paths.private_dir = lp.get("private dir")
524
525     # This is stored without path prefix for the "privateKeytab" attribute in
526     # "secrets_dns.ldif".
527     paths.dns_keytab = "dns.keytab"
528     paths.keytab = "secrets.keytab"
529
530     paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
531     paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
532     paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
533     paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
534     paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
535     paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
536     paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
537     paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
538     paths.namedconf = os.path.join(paths.private_dir, "named.conf")
539     paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
540     paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
541     paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
542     paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
543     paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
544     paths.phpldapadminconfig = os.path.join(paths.private_dir,
545                                             "phpldapadmin-config.php")
546     paths.hklm = "hklm.ldb"
547     paths.hkcr = "hkcr.ldb"
548     paths.hkcu = "hkcu.ldb"
549     paths.hku = "hku.ldb"
550     paths.hkpd = "hkpd.ldb"
551     paths.hkpt = "hkpt.ldb"
552     paths.sysvol = lp.get("path", "sysvol")
553     paths.netlogon = lp.get("path", "netlogon")
554     paths.smbconf = lp.configfile
555     return paths
556
557
558 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
559                 serverrole=None, rootdn=None, domaindn=None, configdn=None,
560                 schemadn=None, serverdn=None, sitename=None):
561     """Guess configuration settings to use."""
562
563     if hostname is None:
564         hostname = socket.gethostname().split(".")[0]
565
566     netbiosname = lp.get("netbios name")
567     if netbiosname is None:
568         netbiosname = hostname
569         # remove forbidden chars
570         newnbname = ""
571         for x in netbiosname:
572             if x.isalnum() or x in VALID_NETBIOS_CHARS:
573                 newnbname = "%s%c" % (newnbname, x)
574         # force the length to be <16
575         netbiosname = newnbname[0:15]
576     assert netbiosname is not None
577     netbiosname = netbiosname.upper()
578     if not valid_netbios_name(netbiosname):
579         raise InvalidNetbiosName(netbiosname)
580
581     if dnsdomain is None:
582         dnsdomain = lp.get("realm")
583         if dnsdomain is None or dnsdomain == "":
584             raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
585
586     dnsdomain = dnsdomain.lower()
587
588     if serverrole is None:
589         serverrole = lp.get("server role")
590         if serverrole is None:
591             raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
592
593     serverrole = serverrole.lower()
594
595     realm = dnsdomain.upper()
596
597     if lp.get("realm") == "":
598         raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s.  Please remove the smb.conf file and let provision generate it" % lp.configfile)
599
600     if lp.get("realm").upper() != realm:
601         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))
602
603     if lp.get("server role").lower() != serverrole:
604         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))
605
606     if serverrole == "domain controller":
607         if domain is None:
608             # This will, for better or worse, default to 'WORKGROUP'
609             domain = lp.get("workgroup")
610         domain = domain.upper()
611
612         if lp.get("workgroup").upper() != domain:
613             raise ProvisioningError("guess_names: Workgroup '%s' in smb.conf must match chosen domain '%s'!  Please remove the %s file and let provision generate it" % (lp.get("workgroup").upper(), domain, lp.configfile))
614
615         if domaindn is None:
616             domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
617
618         if domain == netbiosname:
619             raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
620     else:
621         domain = netbiosname
622         if domaindn is None:
623             domaindn = "DC=" + netbiosname
624
625     if not valid_netbios_name(domain):
626         raise InvalidNetbiosName(domain)
627
628     if hostname.upper() == realm:
629         raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
630     if netbiosname.upper() == realm:
631         raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
632     if domain == realm:
633         raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
634
635     if rootdn is None:
636        rootdn = domaindn
637
638     if configdn is None:
639         configdn = "CN=Configuration," + rootdn
640     if schemadn is None:
641         schemadn = "CN=Schema," + configdn
642
643     if sitename is None:
644         sitename=DEFAULTSITE
645
646     names = ProvisionNames()
647     names.rootdn = rootdn
648     names.domaindn = domaindn
649     names.configdn = configdn
650     names.schemadn = schemadn
651     names.ldapmanagerdn = "CN=Manager," + rootdn
652     names.dnsdomain = dnsdomain
653     names.domain = domain
654     names.realm = realm
655     names.netbiosname = netbiosname
656     names.hostname = hostname
657     names.sitename = sitename
658     names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
659         netbiosname, sitename, configdn)
660
661     return names
662
663
664 def make_smbconf(smbconf, hostname, domain, realm, serverrole,
665                  targetdir, sid_generator="internal", eadb=False, lp=None):
666     """Create a new smb.conf file based on a couple of basic settings.
667     """
668     assert smbconf is not None
669     if hostname is None:
670         hostname = socket.gethostname().split(".")[0]
671         netbiosname = hostname.upper()
672         # remove forbidden chars
673         newnbname = ""
674         for x in netbiosname:
675             if x.isalnum() or x in VALID_NETBIOS_CHARS:
676                 newnbname = "%s%c" % (newnbname, x)
677         #force the length to be <16
678         netbiosname = newnbname[0:15]
679     else:
680         netbiosname = hostname.upper()
681
682     if serverrole is None:
683         serverrole = "standalone"
684
685     assert serverrole in ("domain controller", "member server", "standalone")
686     if serverrole == "domain controller":
687         smbconfsuffix = "dc"
688     elif serverrole == "member server":
689         smbconfsuffix = "member"
690     elif serverrole == "standalone":
691         smbconfsuffix = "standalone"
692
693     if sid_generator is None:
694         sid_generator = "internal"
695
696     assert domain is not None
697     domain = domain.upper()
698
699     assert realm is not None
700     realm = realm.upper()
701
702     if lp is None:
703         lp = samba.param.LoadParm()
704     #Load non-existant file
705     if os.path.exists(smbconf):
706         lp.load(smbconf)
707     if eadb and not lp.get("posix:eadb"):
708         if targetdir is not None:
709             privdir = os.path.join(targetdir, "private")
710         else:
711             privdir = lp.get("private dir")
712         lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
713
714     if targetdir is not None:
715         privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
716         lockdir_line = "lock dir = " + os.path.abspath(targetdir)
717
718         lp.set("lock dir", os.path.abspath(targetdir))
719     else:
720         privatedir_line = ""
721         lockdir_line = ""
722
723     sysvol = os.path.join(lp.get("lock dir"), "sysvol")
724     netlogon = os.path.join(sysvol, realm.lower(), "scripts")
725
726     setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
727                smbconf, {
728             "NETBIOS_NAME": netbiosname,
729             "DOMAIN": domain,
730             "REALM": realm,
731             "SERVERROLE": serverrole,
732             "NETLOGONPATH": netlogon,
733             "SYSVOLPATH": sysvol,
734             "PRIVATEDIR_LINE": privatedir_line,
735             "LOCKDIR_LINE": lockdir_line
736             })
737
738     # reload the smb.conf
739     lp.load(smbconf)
740
741     # and dump it without any values that are the default
742     # this ensures that any smb.conf parameters that were set
743     # on the provision/join command line are set in the resulting smb.conf
744     f = open(smbconf, mode='w')
745     lp.dump(f, False)
746     f.close()
747
748
749
750 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
751                         users_gid, wheel_gid):
752     """setup reasonable name mappings for sam names to unix names.
753
754     :param samdb: SamDB object.
755     :param idmap: IDmap db object.
756     :param sid: The domain sid.
757     :param domaindn: The domain DN.
758     :param root_uid: uid of the UNIX root user.
759     :param nobody_uid: uid of the UNIX nobody user.
760     :param users_gid: gid of the UNIX users group.
761     :param wheel_gid: gid of the UNIX wheel group.
762     """
763     idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
764     idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
765
766     idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
767     idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
768
769
770 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
771                            provision_backend, names, schema, serverrole,
772                            erase=False):
773     """Setup the partitions for the SAM database.
774
775     Alternatively, provision() may call this, and then populate the database.
776
777     :note: This will wipe the Sam Database!
778
779     :note: This function always removes the local SAM LDB file. The erase
780         parameter controls whether to erase the existing data, which
781         may not be stored locally but in LDAP.
782
783     """
784     assert session_info is not None
785
786     # We use options=["modules:"] to stop the modules loading - we
787     # just want to wipe and re-initialise the database, not start it up
788
789     try:
790         os.unlink(samdb_path)
791     except OSError:
792         pass
793
794     samdb = Ldb(url=samdb_path, session_info=session_info,
795                 lp=lp, options=["modules:"])
796
797     ldap_backend_line = "# No LDAP backend"
798     if provision_backend.type is not "ldb":
799         ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
800
801     samdb.transaction_start()
802     try:
803         logger.info("Setting up sam.ldb partitions and settings")
804         setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
805                 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(),
806                 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
807                 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
808                 "LDAP_BACKEND_LINE": ldap_backend_line,
809         })
810
811
812         setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
813                 "BACKEND_TYPE": provision_backend.type,
814                 "SERVER_ROLE": serverrole
815                 })
816
817         logger.info("Setting up sam.ldb rootDSE")
818         setup_samdb_rootdse(samdb, names)
819     except Exception:
820         samdb.transaction_cancel()
821         raise
822     else:
823         samdb.transaction_commit()
824
825
826 def secretsdb_self_join(secretsdb, domain,
827                         netbiosname, machinepass, domainsid=None,
828                         realm=None, dnsdomain=None,
829                         keytab_path=None,
830                         key_version_number=1,
831                         secure_channel_type=SEC_CHAN_WKSTA):
832     """Add domain join-specific bits to a secrets database.
833
834     :param secretsdb: Ldb Handle to the secrets database
835     :param machinepass: Machine password
836     """
837     attrs = ["whenChanged",
838            "secret",
839            "priorSecret",
840            "priorChanged",
841            "krb5Keytab",
842            "privateKeytab"]
843
844     if realm is not None:
845         if dnsdomain is None:
846             dnsdomain = realm.lower()
847         dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
848     else:
849         dnsname = None
850     shortname = netbiosname.lower()
851
852     # We don't need to set msg["flatname"] here, because rdn_name will handle
853     # it, and it causes problems for modifies anyway
854     msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
855     msg["secureChannelType"] = [str(secure_channel_type)]
856     msg["objectClass"] = ["top", "primaryDomain"]
857     if dnsname is not None:
858         msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
859         msg["realm"] = [realm]
860         msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
861         msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
862         msg["privateKeytab"] = ["secrets.keytab"]
863
864     msg["secret"] = [machinepass]
865     msg["samAccountName"] = ["%s$" % netbiosname]
866     msg["secureChannelType"] = [str(secure_channel_type)]
867     if domainsid is not None:
868         msg["objectSid"] = [ndr_pack(domainsid)]
869
870     # This complex expression tries to ensure that we don't have more
871     # than one record for this SID, realm or netbios domain at a time,
872     # but we don't delete the old record that we are about to modify,
873     # because that would delete the keytab and previous password.
874     res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
875         expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(dn=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
876         scope=ldb.SCOPE_ONELEVEL)
877
878     for del_msg in res:
879         secretsdb.delete(del_msg.dn)
880
881     res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
882
883     if len(res) == 1:
884         msg["priorSecret"] = [res[0]["secret"][0]]
885         msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
886
887         try:
888             msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
889         except KeyError:
890             pass
891
892         try:
893             msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
894         except KeyError:
895             pass
896
897         for el in msg:
898             if el != 'dn':
899                 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
900         secretsdb.modify(msg)
901         secretsdb.rename(res[0].dn, msg.dn)
902     else:
903         spn = [ 'HOST/%s' % shortname ]
904         if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
905             # we are a domain controller then we add servicePrincipalName
906             # entries for the keytab code to update.
907             spn.extend([ 'HOST/%s' % dnsname ])
908         msg["servicePrincipalName"] = spn
909
910         secretsdb.add(msg)
911
912
913 def secretsdb_setup_dns(secretsdb, names, private_dir, realm,
914                         dnsdomain, dns_keytab_path, dnspass):
915     """Add DNS specific bits to a secrets database.
916
917     :param secretsdb: Ldb Handle to the secrets database
918     :param machinepass: Machine password
919     """
920     try:
921         os.unlink(os.path.join(private_dir, dns_keytab_path))
922     except OSError:
923         pass
924
925     setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
926             "REALM": realm,
927             "DNSDOMAIN": dnsdomain,
928             "DNS_KEYTAB": dns_keytab_path,
929             "DNSPASS_B64": b64encode(dnspass),
930             "HOSTNAME": names.hostname,
931             "DNSNAME" : '%s.%s' % (
932                 names.netbiosname.lower(), names.dnsdomain.lower())
933             })
934
935
936 def setup_secretsdb(paths, session_info, backend_credentials, lp):
937     """Setup the secrets database.
938
939    :note: This function does not handle exceptions and transaction on purpose,
940        it's up to the caller to do this job.
941
942     :param path: Path to the secrets database.
943     :param session_info: Session info.
944     :param credentials: Credentials
945     :param lp: Loadparm context
946     :return: LDB handle for the created secrets database
947     """
948     if os.path.exists(paths.secrets):
949         os.unlink(paths.secrets)
950
951     keytab_path = os.path.join(paths.private_dir, paths.keytab)
952     if os.path.exists(keytab_path):
953         os.unlink(keytab_path)
954
955     dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
956     if os.path.exists(dns_keytab_path):
957         os.unlink(dns_keytab_path)
958
959     path = paths.secrets
960
961     secrets_ldb = Ldb(path, session_info=session_info,
962                       lp=lp)
963     secrets_ldb.erase()
964     secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
965     secrets_ldb = Ldb(path, session_info=session_info,
966                       lp=lp)
967     secrets_ldb.transaction_start()
968     try:
969         secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
970
971         if (backend_credentials is not None and
972             backend_credentials.authentication_requested()):
973             if backend_credentials.get_bind_dn() is not None:
974                 setup_add_ldif(secrets_ldb,
975                     setup_path("secrets_simple_ldap.ldif"), {
976                         "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
977                         "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
978                         })
979             else:
980                 setup_add_ldif(secrets_ldb,
981                     setup_path("secrets_sasl_ldap.ldif"), {
982                         "LDAPADMINUSER": backend_credentials.get_username(),
983                         "LDAPADMINREALM": backend_credentials.get_realm(),
984                         "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
985                         })
986
987         return secrets_ldb
988     except Exception:
989         secrets_ldb.transaction_cancel()
990         raise
991
992
993 def setup_privileges(path, session_info, lp):
994     """Setup the privileges database.
995
996     :param path: Path to the privileges database.
997     :param session_info: Session info.
998     :param credentials: Credentials
999     :param lp: Loadparm context
1000     :return: LDB handle for the created secrets database
1001     """
1002     if os.path.exists(path):
1003         os.unlink(path)
1004     privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
1005     privilege_ldb.erase()
1006     privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
1007
1008
1009 def setup_registry(path, session_info, lp):
1010     """Setup the registry.
1011
1012     :param path: Path to the registry database
1013     :param session_info: Session information
1014     :param credentials: Credentials
1015     :param lp: Loadparm context
1016     """
1017     reg = samba.registry.Registry()
1018     hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
1019     reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
1020     provision_reg = setup_path("provision.reg")
1021     assert os.path.exists(provision_reg)
1022     reg.diff_apply(provision_reg)
1023
1024
1025 def setup_idmapdb(path, session_info, lp):
1026     """Setup the idmap database.
1027
1028     :param path: path to the idmap database
1029     :param session_info: Session information
1030     :param credentials: Credentials
1031     :param lp: Loadparm context
1032     """
1033     if os.path.exists(path):
1034         os.unlink(path)
1035
1036     idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
1037     idmap_ldb.erase()
1038     idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
1039     return idmap_ldb
1040
1041
1042 def setup_samdb_rootdse(samdb, names):
1043     """Setup the SamDB rootdse.
1044
1045     :param samdb: Sam Database handle
1046     """
1047     setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
1048         "SCHEMADN": names.schemadn,
1049         "DOMAINDN": names.domaindn,
1050         "ROOTDN": names.rootdn,
1051         "CONFIGDN": names.configdn,
1052         "SERVERDN": names.serverdn,
1053         })
1054
1055
1056 def setup_self_join(samdb, names, machinepass, dnspass,
1057                     domainsid, next_rid, invocationid,
1058                     policyguid, policyguid_dc, domainControllerFunctionality,
1059                     ntdsguid):
1060     """Join a host to its own domain."""
1061     assert isinstance(invocationid, str)
1062     if ntdsguid is not None:
1063         ntdsguid_line = "objectGUID: %s\n"%ntdsguid
1064     else:
1065         ntdsguid_line = ""
1066     setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
1067               "CONFIGDN": names.configdn,
1068               "SCHEMADN": names.schemadn,
1069               "DOMAINDN": names.domaindn,
1070               "SERVERDN": names.serverdn,
1071               "INVOCATIONID": invocationid,
1072               "NETBIOSNAME": names.netbiosname,
1073               "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1074               "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1075               "DOMAINSID": str(domainsid),
1076               "DCRID": str(next_rid),
1077               "SAMBA_VERSION_STRING": version,
1078               "NTDSGUID": ntdsguid_line,
1079               "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1080                   domainControllerFunctionality)})
1081
1082     setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1083               "POLICYGUID": policyguid,
1084               "POLICYGUID_DC": policyguid_dc,
1085               "DNSDOMAIN": names.dnsdomain,
1086               "DOMAINDN": names.domaindn})
1087
1088     # add the NTDSGUID based SPNs
1089     ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1090     names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
1091                                      expression="", scope=ldb.SCOPE_BASE)
1092     assert isinstance(names.ntdsguid, str)
1093
1094     # Setup fSMORoleOwner entries to point at the newly created DC entry
1095     setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1096               "DOMAINDN": names.domaindn,
1097               "CONFIGDN": names.configdn,
1098               "SCHEMADN": names.schemadn,
1099               "DEFAULTSITE": names.sitename,
1100               "SERVERDN": names.serverdn,
1101               "NETBIOSNAME": names.netbiosname,
1102               "RIDALLOCATIONSTART": str(next_rid + 100),
1103               "RIDALLOCATIONEND": str(next_rid + 100 + 499),
1104               })
1105
1106     # This is Samba4 specific and should be replaced by the correct
1107     # DNS AD-style setup
1108     setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1109               "DNSDOMAIN": names.dnsdomain,
1110               "DOMAINDN": names.domaindn,
1111               "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1112               "HOSTNAME" : names.hostname,
1113               "DNSNAME" : '%s.%s' % (
1114                   names.netbiosname.lower(), names.dnsdomain.lower())
1115               })
1116
1117
1118 def getpolicypath(sysvolpath, dnsdomain, guid):
1119     """Return the physical path of policy given its guid.
1120
1121     :param sysvolpath: Path to the sysvol folder
1122     :param dnsdomain: DNS name of the AD domain
1123     :param guid: The GUID of the policy
1124     :return: A string with the complete path to the policy folder
1125     """
1126
1127     if guid[0] != "{":
1128         guid = "{%s}" % guid
1129     policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1130     return policy_path
1131
1132
1133 def create_gpo_struct(policy_path):
1134     if not os.path.exists(policy_path):
1135         os.makedirs(policy_path, 0775)
1136     open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1137                       "[General]\r\nVersion=0")
1138     p = os.path.join(policy_path, "MACHINE")
1139     if not os.path.exists(p):
1140         os.makedirs(p, 0775)
1141     p = os.path.join(policy_path, "USER")
1142     if not os.path.exists(p):
1143         os.makedirs(p, 0775)
1144
1145
1146 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1147     """Create the default GPO for a domain
1148
1149     :param sysvolpath: Physical path for the sysvol folder
1150     :param dnsdomain: DNS domain name of the AD domain
1151     :param policyguid: GUID of the default domain policy
1152     :param policyguid_dc: GUID of the default domain controler policy
1153     """
1154     policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1155     create_gpo_struct(policy_path)
1156
1157     policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1158     create_gpo_struct(policy_path)
1159
1160
1161 def setup_samdb(path, session_info, provision_backend, lp, names,
1162         logger, domainsid, domainguid, policyguid, policyguid_dc, fill,
1163         adminpass, krbtgtpass, machinepass, invocationid, dnspass, ntdsguid,
1164         serverrole, am_rodc=False, dom_for_fun_level=None, schema=None,
1165         next_rid=1000):
1166     """Setup a complete SAM Database.
1167
1168     :note: This will wipe the main SAM database file!
1169     """
1170
1171     # Provision does not make much sense values larger than 1000000000
1172     # as the upper range of the rIDAvailablePool is 1073741823 and
1173     # we don't want to create a domain that cannot allocate rids.
1174     if next_rid < 1000 or next_rid > 1000000000:
1175         error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1176         error += "the valid range is %u-%u. The default is %u." % (
1177             1000, 1000000000, 1000)
1178         raise ProvisioningError(error)
1179
1180     # ATTENTION: Do NOT change these default values without discussion with the
1181     # team and/or release manager. They have a big impact on the whole program!
1182     domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1183
1184     if dom_for_fun_level is None:
1185         dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1186
1187     if dom_for_fun_level > domainControllerFunctionality:
1188         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!")
1189
1190     domainFunctionality = dom_for_fun_level
1191     forestFunctionality = dom_for_fun_level
1192
1193     # Also wipes the database
1194     setup_samdb_partitions(path, logger=logger, lp=lp,
1195         provision_backend=provision_backend, session_info=session_info,
1196         names=names, serverrole=serverrole, schema=schema)
1197
1198     if schema is None:
1199         schema = Schema(domainsid, schemadn=names.schemadn)
1200
1201     # Load the database, but don's load the global schema and don't connect
1202     # quite yet
1203     samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1204                   credentials=provision_backend.credentials, lp=lp,
1205                   global_schema=False, am_rodc=am_rodc)
1206
1207     logger.info("Pre-loading the Samba 4 and AD schema")
1208
1209     # Load the schema from the one we computed earlier
1210     samdb.set_schema(schema)
1211
1212     # Set the NTDS settings DN manually - in order to have it already around
1213     # before the provisioned tree exists and we connect
1214     samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1215
1216     # And now we can connect to the DB - the schema won't be loaded from the
1217     # DB
1218     samdb.connect(path)
1219
1220     if fill == FILL_DRS:
1221         return samdb
1222
1223     samdb.transaction_start()
1224     try:
1225         # Set the domain functionality levels onto the database.
1226         # Various module (the password_hash module in particular) need
1227         # to know what level of AD we are emulating.
1228
1229         # These will be fixed into the database via the database
1230         # modifictions below, but we need them set from the start.
1231         samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1232         samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1233         samdb.set_opaque_integer("domainControllerFunctionality",
1234             domainControllerFunctionality)
1235
1236         samdb.set_domain_sid(str(domainsid))
1237         samdb.set_invocation_id(invocationid)
1238
1239         logger.info("Adding DomainDN: %s" % names.domaindn)
1240
1241         # impersonate domain admin
1242         admin_session_info = admin_session(lp, str(domainsid))
1243         samdb.set_session_info(admin_session_info)
1244         if domainguid is not None:
1245             domainguid_line = "objectGUID: %s\n-" % domainguid
1246         else:
1247             domainguid_line = ""
1248
1249         descr = b64encode(get_domain_descriptor(domainsid))
1250         setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1251                 "DOMAINDN": names.domaindn,
1252                 "DOMAINSID": str(domainsid),
1253                 "DESCRIPTOR": descr,
1254                 "DOMAINGUID": domainguid_line
1255                 })
1256
1257         setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1258             "DOMAINDN": names.domaindn,
1259             "CREATTIME": str(int((time.time() + 11644473600) * 1e7)), # seconds -> MS time (since 1/1/1601)
1260             "NEXTRID": str(next_rid),
1261             "DEFAULTSITE": names.sitename,
1262             "CONFIGDN": names.configdn,
1263             "POLICYGUID": policyguid,
1264             "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1265             "SAMBA_VERSION_STRING": version
1266             })
1267
1268         logger.info("Adding configuration container")
1269         descr = b64encode(get_config_descriptor(domainsid))
1270         setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1271             "CONFIGDN": names.configdn,
1272             "DESCRIPTOR": descr,
1273             })
1274
1275         # Now register this container in the root of the forest
1276         msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1277         msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1278                     "subRefs")
1279
1280         # The LDIF here was created when the Schema object was constructed
1281         logger.info("Setting up sam.ldb schema")
1282         samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1283         samdb.modify_ldif(schema.schema_dn_modify)
1284         samdb.write_prefixes_from_schema()
1285         samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1286         setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1287                        {"SCHEMADN": names.schemadn})
1288
1289         logger.info("Reopening sam.ldb with new schema")
1290     except Exception:
1291         samdb.transaction_cancel()
1292         raise
1293     else:
1294         samdb.transaction_commit()
1295
1296     samdb = SamDB(session_info=admin_session_info, auto_connect=False,
1297                 credentials=provision_backend.credentials, lp=lp,
1298                 global_schema=False, am_rodc=am_rodc)
1299
1300     # Set the NTDS settings DN manually - in order to have it already around
1301     # before the provisioned tree exists and we connect
1302     samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1303     samdb.connect(path)
1304
1305     samdb.transaction_start()
1306     try:
1307         samdb.invocation_id = invocationid
1308
1309         logger.info("Setting up sam.ldb configuration data")
1310         setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1311             "CONFIGDN": names.configdn,
1312             "NETBIOSNAME": names.netbiosname,
1313             "DEFAULTSITE": names.sitename,
1314             "DNSDOMAIN": names.dnsdomain,
1315             "DOMAIN": names.domain,
1316             "SCHEMADN": names.schemadn,
1317             "DOMAINDN": names.domaindn,
1318             "SERVERDN": names.serverdn,
1319             "FOREST_FUNCTIONALITY": str(forestFunctionality),
1320             "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1321             })
1322
1323         logger.info("Setting up display specifiers")
1324         display_specifiers_ldif = read_ms_ldif(
1325             setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1326         display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1327             {"CONFIGDN": names.configdn})
1328         check_all_substituted(display_specifiers_ldif)
1329         samdb.add_ldif(display_specifiers_ldif)
1330
1331         logger.info("Adding users container")
1332         setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1333                 "DOMAINDN": names.domaindn})
1334         logger.info("Modifying users container")
1335         setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1336                 "DOMAINDN": names.domaindn})
1337         logger.info("Adding computers container")
1338         setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1339                 "DOMAINDN": names.domaindn})
1340         logger.info("Modifying computers container")
1341         setup_modify_ldif(samdb,
1342             setup_path("provision_computers_modify.ldif"), {
1343                 "DOMAINDN": names.domaindn})
1344         logger.info("Setting up sam.ldb data")
1345         setup_add_ldif(samdb, setup_path("provision.ldif"), {
1346             "CREATTIME": str(int((time.time() + 11644473600) * 1e7)), # seconds -> MS time (since 1/1/1601)
1347             "DOMAINDN": names.domaindn,
1348             "NETBIOSNAME": names.netbiosname,
1349             "DEFAULTSITE": names.sitename,
1350             "CONFIGDN": names.configdn,
1351             "SERVERDN": names.serverdn,
1352             "RIDAVAILABLESTART": str(next_rid + 600),
1353             "POLICYGUID_DC": policyguid_dc
1354             })
1355
1356         setup_modify_ldif(samdb,
1357             setup_path("provision_basedn_references.ldif"), {
1358                 "DOMAINDN": names.domaindn})
1359
1360         setup_modify_ldif(samdb,
1361             setup_path("provision_configuration_references.ldif"), {
1362                 "CONFIGDN": names.configdn,
1363                 "SCHEMADN": names.schemadn})
1364         if fill == FILL_FULL:
1365             logger.info("Setting up sam.ldb users and groups")
1366             setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1367                 "DOMAINDN": names.domaindn,
1368                 "DOMAINSID": str(domainsid),
1369                 "CONFIGDN": names.configdn,
1370                 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1371                 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1372                 })
1373
1374             logger.info("Setting up self join")
1375             setup_self_join(samdb, names=names, invocationid=invocationid,
1376                 dnspass=dnspass,
1377                 machinepass=machinepass,
1378                 domainsid=domainsid,
1379                 next_rid=next_rid,
1380                 policyguid=policyguid,
1381                 policyguid_dc=policyguid_dc,
1382                 domainControllerFunctionality=domainControllerFunctionality,
1383                 ntdsguid=ntdsguid)
1384
1385             ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1386             names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1387                 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1388             assert isinstance(names.ntdsguid, str)
1389     except Exception:
1390         samdb.transaction_cancel()
1391         raise
1392     else:
1393         samdb.transaction_commit()
1394         return samdb
1395
1396
1397 FILL_FULL = "FULL"
1398 FILL_NT4SYNC = "NT4SYNC"
1399 FILL_DRS = "DRS"
1400 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1401 POLICIES_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)(A;OICI;0x001301bf;;;PA)"
1402
1403
1404 def set_dir_acl(path, acl, lp, domsid):
1405     setntacl(lp, path, acl, domsid)
1406     for root, dirs, files in os.walk(path, topdown=False):
1407         for name in files:
1408             setntacl(lp, os.path.join(root, name), acl, domsid)
1409         for name in dirs:
1410             setntacl(lp, os.path.join(root, name), acl, domsid)
1411
1412
1413 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1414     """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1415     folders beneath.
1416
1417     :param sysvol: Physical path for the sysvol folder
1418     :param dnsdomain: The DNS name of the domain
1419     :param domainsid: The SID of the domain
1420     :param domaindn: The DN of the domain (ie. DC=...)
1421     :param samdb: An LDB object on the SAM db
1422     :param lp: an LP object
1423     """
1424
1425     # Set ACL for GPO root folder
1426     root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1427     setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid))
1428
1429     res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1430                         attrs=["cn", "nTSecurityDescriptor"],
1431                         expression="", scope=ldb.SCOPE_ONELEVEL)
1432
1433     for policy in res:
1434         acl = ndr_unpack(security.descriptor,
1435                          str(policy["nTSecurityDescriptor"])).as_sddl()
1436         policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1437         set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1438                     str(domainsid))
1439
1440
1441 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1442     lp):
1443     """Set the ACL for the sysvol share and the subfolders
1444
1445     :param samdb: An LDB object on the SAM db
1446     :param netlogon: Physical path for the netlogon folder
1447     :param sysvol: Physical path for the sysvol folder
1448     :param gid: The GID of the "Domain adminstrators" group
1449     :param domainsid: The SID of the domain
1450     :param dnsdomain: The DNS name of the domain
1451     :param domaindn: The DN of the domain (ie. DC=...)
1452     """
1453
1454     try:
1455         os.chown(sysvol, -1, gid)
1456     except OSError:
1457         canchown = False
1458     else:
1459         canchown = True
1460
1461     # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1462     setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid))
1463     for root, dirs, files in os.walk(sysvol, topdown=False):
1464         for name in files:
1465             if canchown:
1466                 os.chown(os.path.join(root, name), -1, gid)
1467             setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1468         for name in dirs:
1469             if canchown:
1470                 os.chown(os.path.join(root, name), -1, gid)
1471             setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1472
1473     # Set acls on Policy folder and policies folders
1474     set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp)
1475
1476
1477 def interface_ips_v4(lp):
1478     '''return only IPv4 IPs'''
1479     ips = samba.interface_ips(lp, False)
1480     ret = []
1481     for i in ips:
1482         if i.find(':') == -1:
1483             ret.append(i)
1484     return ret
1485
1486 def interface_ips_v6(lp, linklocal=False):
1487     '''return only IPv6 IPs'''
1488     ips = samba.interface_ips(lp, False)
1489     ret = []
1490     for i in ips:
1491         if i.find(':') != -1 and (linklocal or i.find('%') == -1):
1492             ret.append(i)
1493     return ret
1494
1495
1496 def provision(logger, session_info, credentials, smbconf=None,
1497         targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1498         domaindn=None, schemadn=None, configdn=None, serverdn=None,
1499         domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1500         next_rid=1000, adminpass=None, ldapadminpass=None, krbtgtpass=None,
1501         domainguid=None, policyguid=None, policyguid_dc=None,
1502         invocationid=None, machinepass=None, ntdsguid=None, dnspass=None,
1503         root=None, nobody=None, users=None, wheel=None, backup=None, aci=None,
1504         serverrole=None, dom_for_fun_level=None, ldap_backend_extra_port=None,
1505         ldap_backend_forced_uri=None, backend_type=None, sitename=None,
1506         ol_mmr_urls=None, ol_olc=None, setup_ds_path=None, slapd_path=None,
1507         nosync=False, ldap_dryrun_mode=False, useeadb=False, am_rodc=False,
1508         lp=None):
1509     """Provision samba4
1510
1511     :note: caution, this wipes all existing data!
1512     """
1513
1514     if domainsid is None:
1515         domainsid = security.random_sid()
1516     else:
1517         domainsid = security.dom_sid(domainsid)
1518
1519     # create/adapt the group policy GUIDs
1520     # Default GUID for default policy are described at
1521     # "How Core Group Policy Works"
1522     # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1523     if policyguid is None:
1524         policyguid = DEFAULT_POLICY_GUID
1525     policyguid = policyguid.upper()
1526     if policyguid_dc is None:
1527         policyguid_dc = DEFAULT_DC_POLICY_GUID
1528     policyguid_dc = policyguid_dc.upper()
1529
1530     if adminpass is None:
1531         adminpass = samba.generate_random_password(12, 32)
1532     if krbtgtpass is None:
1533         krbtgtpass = samba.generate_random_password(128, 255)
1534     if machinepass is None:
1535         machinepass  = samba.generate_random_password(128, 255)
1536     if dnspass is None:
1537         dnspass = samba.generate_random_password(128, 255)
1538     if ldapadminpass is None:
1539         # Make a new, random password between Samba and it's LDAP server
1540         ldapadminpass=samba.generate_random_password(128, 255)
1541
1542     if backend_type is None:
1543         backend_type = "ldb"
1544
1545     sid_generator = "internal"
1546     if backend_type == "fedora-ds":
1547         sid_generator = "backend"
1548
1549     root_uid = findnss_uid([root or "root"])
1550     nobody_uid = findnss_uid([nobody or "nobody"])
1551     users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1552     if wheel is None:
1553         wheel_gid = findnss_gid(["wheel", "adm"])
1554     else:
1555         wheel_gid = findnss_gid([wheel])
1556     try:
1557         bind_gid = findnss_gid(["bind", "named"])
1558     except KeyError:
1559         bind_gid = None
1560
1561     if targetdir is not None:
1562         smbconf = os.path.join(targetdir, "etc", "smb.conf")
1563     elif smbconf is None:
1564         smbconf = samba.param.default_path()
1565     if not os.path.exists(os.path.dirname(smbconf)):
1566         os.makedirs(os.path.dirname(smbconf))
1567
1568     # only install a new smb.conf if there isn't one there already
1569     if os.path.exists(smbconf):
1570         # if Samba Team members can't figure out the weird errors
1571         # loading an empty smb.conf gives, then we need to be smarter.
1572         # Pretend it just didn't exist --abartlet
1573         data = open(smbconf, 'r').read()
1574         data = data.lstrip()
1575         if data is None or data == "":
1576             make_smbconf(smbconf, hostname, domain, realm,
1577                          serverrole, targetdir, sid_generator, useeadb,
1578                          lp=lp)
1579     else:
1580         make_smbconf(smbconf, hostname, domain, realm, serverrole,
1581                      targetdir, sid_generator, useeadb, lp=lp)
1582
1583     if lp is None:
1584         lp = samba.param.LoadParm()
1585     lp.load(smbconf)
1586     names = guess_names(lp=lp, hostname=hostname, domain=domain,
1587         dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
1588         configdn=configdn, schemadn=schemadn, serverdn=serverdn,
1589         sitename=sitename)
1590     paths = provision_paths_from_lp(lp, names.dnsdomain)
1591
1592     paths.bind_gid = bind_gid
1593
1594     if hostip is None:
1595         logger.info("Looking up IPv4 addresses")
1596         hostips = interface_ips_v4(lp)
1597         if len(hostips) > 0:
1598             hostip = hostips[0]
1599             if len(hostips) > 1:
1600                 logger.warning("More than one IPv4 address found. Using %s",
1601                     hostip)
1602     if hostip == "127.0.0.1":
1603         hostip = None
1604     if hostip is None:
1605         logger.warning("No IPv4 address will be assigned")
1606
1607     if hostip6 is None:
1608         logger.info("Looking up IPv6 addresses")
1609         hostips = interface_ips_v6(lp, linklocal=False)
1610         if hostips:
1611             hostip6 = hostips[0]
1612         if len(hostips) > 1:
1613             logger.warning("More than one IPv6 address found. Using %s", hostip6)
1614     if hostip6 is None:
1615         logger.warning("No IPv6 address will be assigned")
1616
1617     if serverrole is None:
1618         serverrole = lp.get("server role")
1619
1620     assert serverrole in ("domain controller", "member server", "standalone")
1621     if invocationid is None:
1622         invocationid = str(uuid.uuid4())
1623
1624     if not os.path.exists(paths.private_dir):
1625         os.mkdir(paths.private_dir)
1626     if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1627         os.mkdir(os.path.join(paths.private_dir, "tls"))
1628
1629     ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1630
1631     schema = Schema(domainsid, invocationid=invocationid,
1632         schemadn=names.schemadn)
1633
1634     if backend_type == "ldb":
1635         provision_backend = LDBBackend(backend_type, paths=paths,
1636             lp=lp, credentials=credentials,
1637             names=names, logger=logger)
1638     elif backend_type == "existing":
1639         provision_backend = ExistingBackend(backend_type, paths=paths,
1640             lp=lp, credentials=credentials,
1641             names=names, logger=logger,
1642             ldap_backend_forced_uri=ldap_backend_forced_uri)
1643     elif backend_type == "fedora-ds":
1644         provision_backend = FDSBackend(backend_type, paths=paths,
1645             lp=lp, credentials=credentials,
1646             names=names, logger=logger, domainsid=domainsid,
1647             schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1648             slapd_path=slapd_path,
1649             ldap_backend_extra_port=ldap_backend_extra_port,
1650             ldap_dryrun_mode=ldap_dryrun_mode, root=root,
1651             setup_ds_path=setup_ds_path,
1652             ldap_backend_forced_uri=ldap_backend_forced_uri)
1653     elif backend_type == "openldap":
1654         provision_backend = OpenLDAPBackend(backend_type, paths=paths,
1655             lp=lp, credentials=credentials,
1656             names=names, logger=logger, domainsid=domainsid,
1657             schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1658             slapd_path=slapd_path,
1659             ldap_backend_extra_port=ldap_backend_extra_port,
1660             ldap_dryrun_mode=ldap_dryrun_mode, ol_mmr_urls=ol_mmr_urls,
1661             nosync=nosync,
1662             ldap_backend_forced_uri=ldap_backend_forced_uri)
1663     else:
1664         raise ValueError("Unknown LDAP backend type selected")
1665
1666     provision_backend.init()
1667     provision_backend.start()
1668
1669     # only install a new shares config db if there is none
1670     if not os.path.exists(paths.shareconf):
1671         logger.info("Setting up share.ldb")
1672         share_ldb = Ldb(paths.shareconf, session_info=session_info,
1673                         lp=lp)
1674         share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1675
1676     logger.info("Setting up secrets.ldb")
1677     secrets_ldb = setup_secretsdb(paths,
1678         session_info=session_info,
1679         backend_credentials=provision_backend.secrets_credentials, lp=lp)
1680
1681     try:
1682         logger.info("Setting up the registry")
1683         setup_registry(paths.hklm, session_info,
1684                        lp=lp)
1685
1686         logger.info("Setting up the privileges database")
1687         setup_privileges(paths.privilege, session_info, lp=lp)
1688
1689         logger.info("Setting up idmap db")
1690         idmap = setup_idmapdb(paths.idmapdb,
1691             session_info=session_info, lp=lp)
1692
1693         logger.info("Setting up SAM db")
1694         samdb = setup_samdb(paths.samdb, session_info,
1695             provision_backend, lp, names, logger=logger,
1696             domainsid=domainsid, schema=schema, domainguid=domainguid,
1697             policyguid=policyguid, policyguid_dc=policyguid_dc,
1698             fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1699             invocationid=invocationid, machinepass=machinepass,
1700             dnspass=dnspass, ntdsguid=ntdsguid, serverrole=serverrole,
1701             dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1702             next_rid=next_rid)
1703
1704         if serverrole == "domain controller":
1705             if paths.netlogon is None:
1706                 logger.info("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1707                 logger.info("Please either remove %s or see the template at %s" %
1708                         (paths.smbconf, setup_path("provision.smb.conf.dc")))
1709                 assert paths.netlogon is not None
1710
1711             if paths.sysvol is None:
1712                 logger.info("Existing smb.conf does not have a [sysvol] share, but you"
1713                         " are configuring a DC.")
1714                 logger.info("Please either remove %s or see the template at %s" %
1715                         (paths.smbconf, setup_path("provision.smb.conf.dc")))
1716                 assert paths.sysvol is not None
1717
1718             if not os.path.isdir(paths.netlogon):
1719                 os.makedirs(paths.netlogon, 0755)
1720
1721         if samdb_fill == FILL_FULL:
1722             setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1723                                 root_uid=root_uid, nobody_uid=nobody_uid,
1724                                 users_gid=users_gid, wheel_gid=wheel_gid)
1725
1726             if serverrole == "domain controller":
1727                 # Set up group policies (domain policy and domain controller
1728                 # policy)
1729                 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1730                     policyguid_dc)
1731                 setsysvolacl(samdb, paths.netlogon, paths.sysvol, wheel_gid,
1732                     domainsid, names.dnsdomain, names.domaindn, lp)
1733
1734             logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1735             setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1736
1737             secretsdb_self_join(secrets_ldb, domain=names.domain,
1738                 realm=names.realm, dnsdomain=names.dnsdomain,
1739                 netbiosname=names.netbiosname, domainsid=domainsid,
1740                 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1741
1742             # Now set up the right msDS-SupportedEncryptionTypes into the DB
1743             # In future, this might be determined from some configuration
1744             kerberos_enctypes = str(ENC_ALL_TYPES)
1745
1746             try:
1747                 msg = ldb.Message(ldb.Dn(samdb,
1748                     samdb.searchone("distinguishedName",
1749                         expression="samAccountName=%s$" % names.netbiosname,
1750                         scope=ldb.SCOPE_SUBTREE)))
1751                 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1752                     elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1753                     name="msDS-SupportedEncryptionTypes")
1754                 samdb.modify(msg)
1755             except ldb.LdbError, (enum, estr):
1756                 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1757                     # It might be that this attribute does not exist in this schema
1758                     raise
1759
1760             if serverrole == "domain controller":
1761                 secretsdb_setup_dns(secrets_ldb, names,
1762                     paths.private_dir, realm=names.realm,
1763                     dnsdomain=names.dnsdomain,
1764                     dns_keytab_path=paths.dns_keytab, dnspass=dnspass)
1765
1766                 setup_ad_dns(samdb, names=names, hostip=hostip, hostip6=hostip6)
1767
1768                 domainguid = samdb.searchone(basedn=domaindn,
1769                     attribute="objectGUID")
1770                 assert isinstance(domainguid, str)
1771
1772                 # Only make a zone file on the first DC, it should be
1773                 # replicated with DNS replication
1774                 create_zone_file(lp, logger, paths, targetdir,
1775                     dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
1776                     hostname=names.hostname, realm=names.realm,
1777                     domainguid=domainguid, ntdsguid=names.ntdsguid)
1778
1779                 create_named_conf(paths, realm=names.realm,
1780                     dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1781
1782                 create_named_txt(paths.namedtxt,
1783                     realm=names.realm, dnsdomain=names.dnsdomain,
1784                     dnsname = "%s.%s" % (names.hostname, names.dnsdomain),
1785                     private_dir=paths.private_dir,
1786                     keytab_name=paths.dns_keytab)
1787                 logger.info("See %s for an example configuration include file for BIND", paths.namedconf)
1788                 logger.info("and %s for further documentation required for secure DNS "
1789                         "updates", paths.namedtxt)
1790
1791             lastProvisionUSNs = get_last_provision_usn(samdb)
1792             maxUSN = get_max_usn(samdb, str(names.rootdn))
1793             if lastProvisionUSNs is not None:
1794                 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1795             else:
1796                 set_provision_usn(samdb, 0, maxUSN, invocationid)
1797
1798         create_krb5_conf(paths.krb5conf,
1799                          dnsdomain=names.dnsdomain, hostname=names.hostname,
1800                          realm=names.realm)
1801         logger.info("A Kerberos configuration suitable for Samba 4 has been "
1802                     "generated at %s", paths.krb5conf)
1803
1804         if serverrole == "domain controller":
1805             create_dns_update_list(lp, logger, paths)
1806
1807         provision_backend.post_setup()
1808         provision_backend.shutdown()
1809
1810         create_phpldapadmin_config(paths.phpldapadminconfig,
1811                                    ldapi_url)
1812     except Exception:
1813         secrets_ldb.transaction_cancel()
1814         raise
1815
1816     # Now commit the secrets.ldb to disk
1817     secrets_ldb.transaction_commit()
1818
1819     # the commit creates the dns.keytab, now chown it
1820     dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1821     if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
1822         try:
1823             os.chmod(dns_keytab_path, 0640)
1824             os.chown(dns_keytab_path, -1, paths.bind_gid)
1825         except OSError:
1826             if not os.environ.has_key('SAMBA_SELFTEST'):
1827                 logger.info("Failed to chown %s to bind gid %u",
1828                             dns_keytab_path, paths.bind_gid)
1829
1830
1831     logger.info("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php",
1832             paths.phpldapadminconfig)
1833
1834     logger.info("Once the above files are installed, your Samba4 server will be ready to use")
1835     logger.info("Server Role:           %s" % serverrole)
1836     logger.info("Hostname:              %s" % names.hostname)
1837     logger.info("NetBIOS Domain:        %s" % names.domain)
1838     logger.info("DNS Domain:            %s" % names.dnsdomain)
1839     logger.info("DOMAIN SID:            %s" % str(domainsid))
1840     if samdb_fill == FILL_FULL:
1841         logger.info("Admin password:        %s" % adminpass)
1842     if provision_backend.type is not "ldb":
1843         if provision_backend.credentials.get_bind_dn() is not None:
1844             logger.info("LDAP Backend Admin DN: %s" %
1845                 provision_backend.credentials.get_bind_dn())
1846         else:
1847             logger.info("LDAP Admin User:       %s" %
1848                 provision_backend.credentials.get_username())
1849
1850         logger.info("LDAP Admin Password:   %s" %
1851             provision_backend.credentials.get_password())
1852
1853         if provision_backend.slapd_command_escaped is not None:
1854             # now display slapd_command_file.txt to show how slapd must be
1855             # started next time
1856             logger.info("Use later the following commandline to start slapd, then Samba:")
1857             logger.info(provision_backend.slapd_command_escaped)
1858             logger.info("This slapd-Commandline is also stored under: %s/ldap_backend_startup.sh",
1859                     provision_backend.ldapdir)
1860
1861     result = ProvisionResult()
1862     result.domaindn = domaindn
1863     result.paths = paths
1864     result.lp = lp
1865     result.samdb = samdb
1866     return result
1867
1868
1869 def provision_become_dc(smbconf=None, targetdir=None,
1870         realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
1871         serverdn=None, domain=None, hostname=None, domainsid=None,
1872         adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
1873         policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
1874         root=None, nobody=None, users=None, wheel=None, backup=None,
1875         serverrole=None, ldap_backend=None, ldap_backend_type=None,
1876         sitename=None, debuglevel=1):
1877
1878     logger = logging.getLogger("provision")
1879     samba.set_debug_level(debuglevel)
1880
1881     res = provision(logger, system_session(), None,
1882         smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1883         realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1884         configdn=configdn, serverdn=serverdn, domain=domain,
1885         hostname=hostname, hostip=None, domainsid=domainsid,
1886         machinepass=machinepass, serverrole="domain controller",
1887         sitename=sitename)
1888     res.lp.set("debuglevel", str(debuglevel))
1889     return res
1890
1891
1892 def create_phpldapadmin_config(path, ldapi_uri):
1893     """Create a PHP LDAP admin configuration file.
1894
1895     :param path: Path to write the configuration to.
1896     """
1897     setup_file(setup_path("phpldapadmin-config.php"), path,
1898             {"S4_LDAPI_URI": ldapi_uri})
1899
1900
1901 def create_zone_file(lp, logger, paths, targetdir, dnsdomain,
1902                      hostip, hostip6, hostname, realm, domainguid,
1903                      ntdsguid):
1904     """Write out a DNS zone file, from the info in the current database.
1905
1906     :param paths: paths object
1907     :param dnsdomain: DNS Domain name
1908     :param domaindn: DN of the Domain
1909     :param hostip: Local IPv4 IP
1910     :param hostip6: Local IPv6 IP
1911     :param hostname: Local hostname
1912     :param realm: Realm name
1913     :param domainguid: GUID of the domain.
1914     :param ntdsguid: GUID of the hosts nTDSDSA record.
1915     """
1916     assert isinstance(domainguid, str)
1917
1918     if hostip6 is not None:
1919         hostip6_base_line = "            IN AAAA    " + hostip6
1920         hostip6_host_line = hostname + "        IN AAAA    " + hostip6
1921         gc_msdcs_ip6_line = "gc._msdcs               IN AAAA    " + hostip6
1922     else:
1923         hostip6_base_line = ""
1924         hostip6_host_line = ""
1925         gc_msdcs_ip6_line = ""
1926
1927     if hostip is not None:
1928         hostip_base_line = "            IN A    " + hostip
1929         hostip_host_line = hostname + "        IN A    " + hostip
1930         gc_msdcs_ip_line = "gc._msdcs               IN A    " + hostip
1931     else:
1932         hostip_base_line = ""
1933         hostip_host_line = ""
1934         gc_msdcs_ip_line = ""
1935
1936     dns_dir = os.path.dirname(paths.dns)
1937
1938     try:
1939         shutil.rmtree(dns_dir, True)
1940     except OSError:
1941         pass
1942
1943     os.mkdir(dns_dir, 0775)
1944
1945     # we need to freeze the zone while we update the contents
1946     if targetdir is None:
1947         rndc = ' '.join(lp.get("rndc command"))
1948         os.system(rndc + " freeze " + lp.get("realm"))
1949
1950     setup_file(setup_path("provision.zone"), paths.dns, {
1951             "HOSTNAME": hostname,
1952             "DNSDOMAIN": dnsdomain,
1953             "REALM": realm,
1954             "HOSTIP_BASE_LINE": hostip_base_line,
1955             "HOSTIP_HOST_LINE": hostip_host_line,
1956             "DOMAINGUID": domainguid,
1957             "DATESTRING": time.strftime("%Y%m%d%H"),
1958             "DEFAULTSITE": DEFAULTSITE,
1959             "NTDSGUID": ntdsguid,
1960             "HOSTIP6_BASE_LINE": hostip6_base_line,
1961             "HOSTIP6_HOST_LINE": hostip6_host_line,
1962             "GC_MSDCS_IP_LINE": gc_msdcs_ip_line,
1963             "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line,
1964         })
1965
1966     # note that we use no variable substitution on this file
1967     # the substitution is done at runtime by samba_dnsupdate
1968     setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1969
1970     # and the SPN update list
1971     setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1972
1973     if paths.bind_gid is not None:
1974         try:
1975             os.chown(dns_dir, -1, paths.bind_gid)
1976             os.chown(paths.dns, -1, paths.bind_gid)
1977             # chmod needed to cope with umask
1978             os.chmod(dns_dir, 0775)
1979             os.chmod(paths.dns, 0664)
1980         except OSError:
1981             if not os.environ.has_key('SAMBA_SELFTEST'):
1982                 logger.error("Failed to chown %s to bind gid %u" % (
1983                     dns_dir, paths.bind_gid))
1984
1985     if targetdir is None:
1986         os.system(rndc + " unfreeze " + lp.get("realm"))
1987
1988
1989 def create_dns_update_list(lp, logger, paths):
1990     """Write out a dns_update_list file"""
1991     # note that we use no variable substitution on this file
1992     # the substitution is done at runtime by samba_dnsupdate
1993     setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1994     setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1995
1996
1997 def create_named_conf(paths, realm, dnsdomain,
1998                       private_dir):
1999     """Write out a file containing zone statements suitable for inclusion in a
2000     named.conf file (including GSS-TSIG configuration).
2001
2002     :param paths: all paths
2003     :param realm: Realm name
2004     :param dnsdomain: DNS Domain name
2005     :param private_dir: Path to private directory
2006     :param keytab_name: File name of DNS keytab file
2007     """
2008
2009     setup_file(setup_path("named.conf"), paths.namedconf, {
2010             "DNSDOMAIN": dnsdomain,
2011             "REALM": realm,
2012             "ZONE_FILE": paths.dns,
2013             "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
2014             "NAMED_CONF": paths.namedconf,
2015             "NAMED_CONF_UPDATE": paths.namedconf_update
2016             })
2017
2018     setup_file(setup_path("named.conf.update"), paths.namedconf_update)
2019
2020
2021 def create_named_txt(path, realm, dnsdomain, dnsname, private_dir,
2022     keytab_name):
2023     """Write out a file containing zone statements suitable for inclusion in a
2024     named.conf file (including GSS-TSIG configuration).
2025
2026     :param path: Path of the new named.conf file.
2027     :param realm: Realm name
2028     :param dnsdomain: DNS Domain name
2029     :param private_dir: Path to private directory
2030     :param keytab_name: File name of DNS keytab file
2031     """
2032     setup_file(setup_path("named.txt"), path, {
2033             "DNSDOMAIN": dnsdomain,
2034             "DNSNAME" : dnsname, 
2035             "REALM": realm,
2036             "DNS_KEYTAB": keytab_name,
2037             "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
2038             "PRIVATE_DIR": private_dir
2039         })
2040
2041
2042 def create_krb5_conf(path, dnsdomain, hostname, realm):
2043     """Write out a file containing zone statements suitable for inclusion in a
2044     named.conf file (including GSS-TSIG configuration).
2045
2046     :param path: Path of the new named.conf file.
2047     :param dnsdomain: DNS Domain name
2048     :param hostname: Local hostname
2049     :param realm: Realm name
2050     """
2051     setup_file(setup_path("krb5.conf"), path, {
2052             "DNSDOMAIN": dnsdomain,
2053             "HOSTNAME": hostname,
2054             "REALM": realm,
2055         })
2056
2057
2058 class ProvisioningError(Exception):
2059     """A generic provision error."""
2060
2061     def __init__(self, value):
2062         self.value = value
2063
2064     def __str__(self):
2065         return "ProvisioningError: " + self.value
2066
2067
2068 class InvalidNetbiosName(Exception):
2069     """A specified name was not a valid NetBIOS name."""
2070     def __init__(self, name):
2071         super(InvalidNetbiosName, self).__init__(
2072             "The name '%r' is not a valid NetBIOS name" % name)