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