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