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