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