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