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