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