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