441c2b540b98d8edeb2511c2e4fa7e7fc5a67406
[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                 "DOMAINGUID": domainguid_line,
1131                 "DESCRIPTOR": descr
1132                 })
1133
1134
1135         setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1136             "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1137             "DOMAINSID": str(domainsid),
1138             "NEXTRID": str(next_rid),
1139             "SCHEMADN": names.schemadn, 
1140             "NETBIOSNAME": names.netbiosname,
1141             "DEFAULTSITE": names.sitename,
1142             "CONFIGDN": names.configdn,
1143             "SERVERDN": names.serverdn,
1144             "POLICYGUID": policyguid,
1145             "DOMAINDN": names.domaindn,
1146             "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1147             "SAMBA_VERSION_STRING": version
1148             })
1149
1150         logger.info("Adding configuration container")
1151         descr = b64encode(get_config_descriptor(domainsid))
1152         setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1153             "CONFIGDN": names.configdn, 
1154             "DESCRIPTOR": descr,
1155             })
1156
1157         # The LDIF here was created when the Schema object was constructed
1158         logger.info("Setting up sam.ldb schema")
1159         samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1160         samdb.modify_ldif(schema.schema_dn_modify)
1161         samdb.write_prefixes_from_schema()
1162         samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1163         setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"), 
1164                        {"SCHEMADN": names.schemadn})
1165
1166         logger.info("Reopening sam.ldb with new schema")
1167     except:
1168         samdb.transaction_cancel()
1169         raise
1170     else:
1171         samdb.transaction_commit()
1172
1173     samdb = SamDB(session_info=admin_session_info, auto_connect=False,
1174                 credentials=provision_backend.credentials, lp=lp,
1175                 global_schema=False, am_rodc=am_rodc)
1176
1177     # Set the NTDS settings DN manually - in order to have it already around
1178     # before the provisioned tree exists and we connect
1179     samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1180
1181     samdb.connect(path)
1182
1183     samdb.transaction_start()
1184     try:
1185         samdb.invocation_id = invocationid
1186
1187         logger.info("Setting up sam.ldb configuration data")
1188         descr = b64encode(get_sites_descriptor(domainsid))
1189         setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1190             "CONFIGDN": names.configdn,
1191             "NETBIOSNAME": names.netbiosname,
1192             "DEFAULTSITE": names.sitename,
1193             "DNSDOMAIN": names.dnsdomain,
1194             "DOMAIN": names.domain,
1195             "SCHEMADN": names.schemadn,
1196             "DOMAINDN": names.domaindn,
1197             "SERVERDN": names.serverdn,
1198             "FOREST_FUNCTIONALITY": str(forestFunctionality),
1199             "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1200             "SITES_DESCRIPTOR": descr
1201             })
1202
1203         logger.info("Setting up display specifiers")
1204         display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1205         display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
1206         check_all_substituted(display_specifiers_ldif)
1207         samdb.add_ldif(display_specifiers_ldif)
1208
1209         logger.info("Adding users container")
1210         setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1211                 "DOMAINDN": names.domaindn})
1212         logger.info("Modifying users container")
1213         setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1214                 "DOMAINDN": names.domaindn})
1215         logger.info("Adding computers container")
1216         setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1217                 "DOMAINDN": names.domaindn})
1218         logger.info("Modifying computers container")
1219         setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1220                 "DOMAINDN": names.domaindn})
1221         logger.info("Setting up sam.ldb data")
1222         setup_add_ldif(samdb, setup_path("provision.ldif"), {
1223             "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1224             "DOMAINDN": names.domaindn,
1225             "NETBIOSNAME": names.netbiosname,
1226             "DEFAULTSITE": names.sitename,
1227             "CONFIGDN": names.configdn,
1228             "SERVERDN": names.serverdn,
1229             "RIDAVAILABLESTART": str(next_rid + 600),
1230             "POLICYGUID_DC": policyguid_dc
1231             })
1232
1233         setup_modify_ldif(samdb, setup_path("provision_basedn_references.ldif"), {
1234                 "DOMAINDN": names.domaindn})
1235
1236         setup_modify_ldif(samdb, setup_path("provision_configuration_references.ldif"), {
1237                 "CONFIGDN": names.configdn,
1238                 "SCHEMADN": names.schemadn})
1239         if fill == FILL_FULL:
1240             logger.info("Setting up sam.ldb users and groups")
1241             setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1242                 "DOMAINDN": names.domaindn,
1243                 "DOMAINSID": str(domainsid),
1244                 "CONFIGDN": names.configdn,
1245                 "ADMINPASS_B64": b64encode(adminpass),
1246                 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1247                 })
1248
1249             logger.info("Setting up self join")
1250             setup_self_join(samdb, names=names, invocationid=invocationid,
1251                             dnspass=dnspass,
1252                             machinepass=machinepass,
1253                             domainsid=domainsid,
1254                             next_rid=next_rid,
1255                             policyguid=policyguid,
1256                             policyguid_dc=policyguid_dc,
1257                             setup_path=setup_path,
1258                             domainControllerFunctionality=domainControllerFunctionality,
1259                             ntdsguid=ntdsguid)
1260
1261             ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1262             names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1263                 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1264             assert isinstance(names.ntdsguid, str)
1265     except:
1266         samdb.transaction_cancel()
1267         raise
1268     else:
1269         samdb.transaction_commit()
1270         return samdb
1271
1272
1273 FILL_FULL = "FULL"
1274 FILL_NT4SYNC = "NT4SYNC"
1275 FILL_DRS = "DRS"
1276 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1277 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)"
1278
1279 def set_dir_acl(path, acl, lp, domsid):
1280     setntacl(lp, path, acl, domsid)
1281     for root, dirs, files in os.walk(path, topdown=False):
1282         for name in files:
1283             setntacl(lp, os.path.join(root, name), acl, domsid)
1284         for name in dirs:
1285             setntacl(lp, os.path.join(root, name), acl, domsid)
1286
1287
1288 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1289     """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1290     folders beneath.
1291
1292     :param sysvol: Physical path for the sysvol folder
1293     :param dnsdomain: The DNS name of the domain
1294     :param domainsid: The SID of the domain
1295     :param domaindn: The DN of the domain (ie. DC=...)
1296     :param samdb: An LDB object on the SAM db
1297     :param lp: an LP object
1298     """
1299
1300     # Set ACL for GPO root folder
1301     root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1302     setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid))
1303
1304     res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1305                         attrs=["cn", "nTSecurityDescriptor"],
1306                         expression="", scope=ldb.SCOPE_ONELEVEL)
1307
1308     for policy in res:
1309         acl = ndr_unpack(security.descriptor, 
1310                          str(policy["nTSecurityDescriptor"])).as_sddl()
1311         policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1312         set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp, 
1313                     str(domainsid))
1314
1315 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1316     lp):
1317     """Set the ACL for the sysvol share and the subfolders
1318
1319     :param samdb: An LDB object on the SAM db
1320     :param netlogon: Physical path for the netlogon folder
1321     :param sysvol: Physical path for the sysvol folder
1322     :param gid: The GID of the "Domain adminstrators" group
1323     :param domainsid: The SID of the domain
1324     :param dnsdomain: The DNS name of the domain
1325     :param domaindn: The DN of the domain (ie. DC=...)
1326     """
1327
1328     try:
1329         os.chown(sysvol,-1,gid)
1330     except:
1331         canchown = False
1332     else:
1333         canchown = True
1334
1335     # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1336     setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid))
1337     for root, dirs, files in os.walk(sysvol, topdown=False):
1338         for name in files:
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         for name in dirs:
1343             if canchown:
1344                 os.chown(os.path.join(root, name), -1, gid)
1345             setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1346
1347     # Set acls on Policy folder and policies folders
1348     set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp)
1349
1350
1351 def provision(setup_dir, logger, session_info, 
1352               credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1353               realm=None, 
1354               rootdn=None, domaindn=None, schemadn=None, configdn=None, 
1355               serverdn=None,
1356               domain=None, hostname=None, hostip=None, hostip6=None, 
1357               domainsid=None, next_rid=1000,
1358               adminpass=None, ldapadminpass=None,
1359               krbtgtpass=None, domainguid=None, 
1360               policyguid=None, policyguid_dc=None, invocationid=None,
1361               machinepass=None, ntdsguid=None,
1362               dnspass=None, root=None, nobody=None, users=None, 
1363               wheel=None, backup=None, aci=None, serverrole=None,
1364               dom_for_fun_level=None,
1365               ldap_backend_extra_port=None, ldap_backend_forced_uri=None, backend_type=None,
1366               sitename=None,
1367               ol_mmr_urls=None, ol_olc=None, 
1368               setup_ds_path=None, slapd_path=None, nosync=False,
1369               ldap_dryrun_mode=False, useeadb=False, am_rodc=False):
1370     """Provision samba4
1371     
1372     :note: caution, this wipes all existing data!
1373     """
1374
1375     def setup_path(file):
1376       return os.path.join(setup_dir, file)
1377
1378     if domainsid is None:
1379       domainsid = security.random_sid()
1380     else:
1381       domainsid = security.dom_sid(domainsid)
1382
1383     # create/adapt the group policy GUIDs
1384     # Default GUID for default policy are described at
1385     # "How Core Group Policy Works"
1386     # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1387     if policyguid is None:
1388         policyguid = DEFAULT_POLICY_GUID
1389     policyguid = policyguid.upper()
1390     if policyguid_dc is None:
1391         policyguid_dc = DEFAULT_DC_POLICY_GUID
1392     policyguid_dc = policyguid_dc.upper()
1393
1394     if adminpass is None:
1395         adminpass = samba.generate_random_password(12, 32)
1396     if krbtgtpass is None:
1397         krbtgtpass = samba.generate_random_password(128, 255)
1398     if machinepass is None:
1399         machinepass  = samba.generate_random_password(128, 255)
1400     if dnspass is None:
1401         dnspass = samba.generate_random_password(128, 255)
1402     if ldapadminpass is None:
1403         #Make a new, random password between Samba and it's LDAP server
1404         ldapadminpass=samba.generate_random_password(128, 255)
1405
1406     if backend_type is None:
1407         backend_type = "ldb"
1408
1409     sid_generator = "internal"
1410     if backend_type == "fedora-ds":
1411         sid_generator = "backend"
1412
1413     root_uid = findnss_uid([root or "root"])
1414     nobody_uid = findnss_uid([nobody or "nobody"])
1415     users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1416     if wheel is None:
1417         wheel_gid = findnss_gid(["wheel", "adm"])
1418     else:
1419         wheel_gid = findnss_gid([wheel])
1420     try:
1421         bind_gid = findnss_gid(["bind", "named"])
1422     except KeyError:
1423         bind_gid = None
1424
1425     if targetdir is not None:
1426         smbconf = os.path.join(targetdir, "etc", "smb.conf")
1427     elif smbconf is None:
1428         smbconf = samba.param.default_path()
1429     if not os.path.exists(os.path.dirname(smbconf)):
1430         os.makedirs(os.path.dirname(smbconf))
1431
1432     # only install a new smb.conf if there isn't one there already
1433     if os.path.exists(smbconf):
1434         # if Samba Team members can't figure out the weird errors
1435         # loading an empty smb.conf gives, then we need to be smarter.
1436         # Pretend it just didn't exist --abartlet
1437         data = open(smbconf, 'r').read()
1438         data = data.lstrip()
1439         if data is None or data == "":
1440             make_smbconf(smbconf, setup_path, hostname, domain, realm,
1441                          serverrole, targetdir, sid_generator, useeadb)
1442     else: 
1443         make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, 
1444                      targetdir, sid_generator, useeadb)
1445
1446     lp = samba.param.LoadParm()
1447     lp.load(smbconf)
1448     names = guess_names(lp=lp, hostname=hostname, domain=domain,
1449                         dnsdomain=realm, serverrole=serverrole,
1450                         domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1451                         serverdn=serverdn, sitename=sitename)
1452     paths = provision_paths_from_lp(lp, names.dnsdomain)
1453
1454     paths.bind_gid = bind_gid
1455
1456     if hostip is None:
1457         hostips = samba.interface_ips(lp, False)
1458         if len(hostips) == 0:
1459             logger.warning("No external IPv4 address has been found. Using loopback.")
1460             hostip = '127.0.0.1'
1461         else:
1462             hostip = hostips[0]
1463             if len(hostips) > 1:
1464                 logger.warning("More than one IPv4 address found. Using %s.", hostip)
1465
1466     if hostip6 is None:
1467         try:
1468             for ip in socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP):
1469                 if hostip6 is None:
1470                     hostip6 = ip[-1][0]
1471                 if hostip6 == '::1' and ip[-1][0] != '::1':
1472                     hostip6 = ip[-1][0]
1473         except socket.gaierror, (socket.EAI_NODATA, msg): 
1474             hostip6 = None
1475
1476     if serverrole is None:
1477         serverrole = lp.get("server role")
1478
1479     assert serverrole in ("domain controller", "member server", "standalone")
1480     if invocationid is None:
1481         invocationid = str(uuid.uuid4())
1482
1483     if not os.path.exists(paths.private_dir):
1484         os.mkdir(paths.private_dir)
1485     if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1486         os.mkdir(os.path.join(paths.private_dir, "tls"))
1487
1488     ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1489  
1490     schema = Schema(setup_path, domainsid, invocationid=invocationid, schemadn=names.schemadn)
1491
1492     if backend_type == "ldb":
1493         provision_backend = LDBBackend(backend_type,
1494                                        paths=paths, setup_path=setup_path,
1495                                        lp=lp, credentials=credentials, 
1496                                        names=names,
1497                                        logger=logger)
1498     elif backend_type == "existing":
1499         provision_backend = ExistingBackend(backend_type,
1500                                             paths=paths, setup_path=setup_path,
1501                                             lp=lp, credentials=credentials, 
1502                                             names=names,
1503                                             logger=logger,
1504                                             ldap_backend_forced_uri=ldap_backend_forced_uri)
1505     elif backend_type == "fedora-ds":
1506         provision_backend = FDSBackend(backend_type,
1507                                        paths=paths, setup_path=setup_path,
1508                                        lp=lp, credentials=credentials, 
1509                                        names=names,
1510                                        logger=logger,
1511                                        domainsid=domainsid,
1512                                        schema=schema,
1513                                        hostname=hostname,
1514                                        ldapadminpass=ldapadminpass,
1515                                        slapd_path=slapd_path,
1516                                        ldap_backend_extra_port=ldap_backend_extra_port,
1517                                        ldap_dryrun_mode=ldap_dryrun_mode,
1518                                        root=root,
1519                                        setup_ds_path=setup_ds_path,
1520                                        ldap_backend_forced_uri=ldap_backend_forced_uri)
1521     elif backend_type == "openldap":
1522         provision_backend = OpenLDAPBackend(backend_type,
1523                                             paths=paths, setup_path=setup_path,
1524                                             lp=lp, credentials=credentials, 
1525                                             names=names,
1526                                             logger=logger,
1527                                             domainsid=domainsid,
1528                                             schema=schema,
1529                                             hostname=hostname,
1530                                             ldapadminpass=ldapadminpass,
1531                                             slapd_path=slapd_path,
1532                                             ldap_backend_extra_port=ldap_backend_extra_port,
1533                                             ldap_dryrun_mode=ldap_dryrun_mode,
1534                                             ol_mmr_urls=ol_mmr_urls, 
1535                                             nosync=nosync,
1536                                             ldap_backend_forced_uri=ldap_backend_forced_uri)
1537     else:
1538         raise ValueError("Unknown LDAP backend type selected")
1539
1540     provision_backend.init()
1541     provision_backend.start()
1542
1543     # only install a new shares config db if there is none
1544     if not os.path.exists(paths.shareconf):
1545         logger.info("Setting up share.ldb")
1546         share_ldb = Ldb(paths.shareconf, session_info=session_info, 
1547                         lp=lp)
1548         share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1549
1550     logger.info("Setting up secrets.ldb")
1551     secrets_ldb = setup_secretsdb(paths, setup_path,
1552         session_info=session_info,
1553         backend_credentials=provision_backend.secrets_credentials, lp=lp)
1554
1555     try:
1556         logger.info("Setting up the registry")
1557         setup_registry(paths.hklm, setup_path, session_info, 
1558                        lp=lp)
1559
1560         logger.info("Setting up the privileges database")
1561         setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1562
1563         logger.info("Setting up idmap db")
1564         idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1565                               lp=lp)
1566
1567         logger.info("Setting up SAM db")
1568         samdb = setup_samdb(paths.samdb, setup_path, session_info, 
1569                             provision_backend, lp, names,
1570                             logger=logger, 
1571                             domainsid=domainsid, 
1572                             schema=schema, domainguid=domainguid,
1573                             policyguid=policyguid, policyguid_dc=policyguid_dc,
1574                             fill=samdb_fill, 
1575                             adminpass=adminpass, krbtgtpass=krbtgtpass,
1576                             invocationid=invocationid, 
1577                             machinepass=machinepass, dnspass=dnspass, 
1578                             ntdsguid=ntdsguid, serverrole=serverrole,
1579                             dom_for_fun_level=dom_for_fun_level,
1580                             am_rodc=am_rodc, next_rid=next_rid)
1581
1582         if serverrole == "domain controller":
1583             if paths.netlogon is None:
1584                 logger.info("Existing smb.conf does not have a [netlogon] share, but you 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.netlogon is not None
1588
1589             if paths.sysvol is None:
1590                 logger.info("Existing smb.conf does not have a [sysvol] share, but you"
1591                         " are configuring a DC.")
1592                 logger.info("Please either remove %s or see the template at %s" % 
1593                         (paths.smbconf, setup_path("provision.smb.conf.dc")))
1594                 assert paths.sysvol is not None
1595
1596             if not os.path.isdir(paths.netlogon):
1597                 os.makedirs(paths.netlogon, 0755)
1598
1599         if samdb_fill == FILL_FULL:
1600             setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1601                                 root_uid=root_uid, nobody_uid=nobody_uid,
1602                                 users_gid=users_gid, wheel_gid=wheel_gid)
1603
1604             if serverrole == "domain controller":
1605                 # Set up group policies (domain policy and domain controller policy)
1606                 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid, policyguid_dc)
1607                 setsysvolacl(samdb, paths.netlogon, paths.sysvol, wheel_gid, 
1608                              domainsid, names.dnsdomain, names.domaindn, lp)
1609
1610             logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1611             setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1612
1613             secretsdb_self_join(secrets_ldb, domain=names.domain,
1614                                 realm=names.realm,
1615                                 dnsdomain=names.dnsdomain,
1616                                 netbiosname=names.netbiosname,
1617                                 domainsid=domainsid, 
1618                                 machinepass=machinepass,
1619                                 secure_channel_type=SEC_CHAN_BDC)
1620
1621             # Now set up the right msDS-SupportedEncryptionTypes into the DB
1622             # In future, this might be determined from some configuration
1623             kerberos_enctypes = str(ENC_ALL_TYPES)
1624
1625             try:
1626                 msg = ldb.Message(ldb.Dn(samdb, samdb.searchone("distinguishedName", expression="samAccountName=%s$" % names.netbiosname, scope=ldb.SCOPE_SUBTREE)))
1627                 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(elements=kerberos_enctypes, 
1628                                                                           flags=ldb.FLAG_MOD_REPLACE, 
1629                                                                           name="msDS-SupportedEncryptionTypes")
1630                 samdb.modify(msg)
1631             except ldb.LdbError, (ldb.ERR_NO_SUCH_ATTRIBUTE, _):
1632                 # It might be that this attribute does not exist in this schema
1633                 pass
1634
1635
1636             if serverrole == "domain controller":
1637                 secretsdb_setup_dns(secrets_ldb, setup_path, names,
1638                                     paths.private_dir,
1639                                     realm=names.realm, dnsdomain=names.dnsdomain,
1640                                     dns_keytab_path=paths.dns_keytab,
1641                                     dnspass=dnspass)
1642
1643                 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1644                 assert isinstance(domainguid, str)
1645
1646                 # Only make a zone file on the first DC, it should be replicated
1647                 # with DNS replication
1648                 create_zone_file(lp, logger, paths, targetdir, setup_path,
1649                     dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
1650                     hostname=names.hostname, realm=names.realm, 
1651                     domainguid=domainguid, ntdsguid=names.ntdsguid)
1652
1653                 create_named_conf(paths, setup_path, realm=names.realm,
1654                                   dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1655
1656                 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1657                                   dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1658                                   keytab_name=paths.dns_keytab)
1659                 logger.info("See %s for an example configuration include file for BIND", paths.namedconf)
1660                 logger.info("and %s for further documentation required for secure DNS "
1661                         "updates", paths.namedtxt)
1662
1663             lastProvisionUSNs = get_last_provision_usn(samdb)
1664             maxUSN = get_max_usn(samdb, str(names.rootdn))
1665             if lastProvisionUSNs is not None:
1666                 update_provision_usn(samdb, 0, maxUSN, 1)
1667             else:
1668                 set_provision_usn(samdb, 0, maxUSN)
1669
1670         create_krb5_conf(paths.krb5conf, setup_path,
1671                          dnsdomain=names.dnsdomain, hostname=names.hostname,
1672                          realm=names.realm)
1673         logger.info("A Kerberos configuration suitable for Samba 4 has been "
1674                     "generated at %s", paths.krb5conf)
1675
1676         if serverrole == "domain controller":
1677             create_dns_update_list(lp, logger, paths, setup_path)
1678
1679         provision_backend.post_setup()
1680         provision_backend.shutdown()
1681
1682         create_phpldapadmin_config(paths.phpldapadminconfig, setup_path, 
1683                                    ldapi_url)
1684     except:
1685         secrets_ldb.transaction_cancel()
1686         raise
1687
1688     #Now commit the secrets.ldb to disk
1689     secrets_ldb.transaction_commit()
1690
1691     # the commit creates the dns.keytab, now chown it
1692     dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1693     if (os.path.isfile(dns_keytab_path) and paths.bind_gid is not None):
1694         try:
1695             os.chmod(dns_keytab_path, 0640)
1696             os.chown(dns_keytab_path, -1, paths.bind_gid)
1697         except OSError:
1698             if not os.environ.has_key('SAMBA_SELFTEST'):
1699                 logger.info("Failed to chown %s to bind gid %u", dns_keytab_path,
1700                             paths.bind_gid)
1701
1702
1703     logger.info("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php",
1704             paths.phpldapadminconfig)
1705
1706     logger.info("Once the above files are installed, your Samba4 server will be ready to use")
1707     logger.info("Server Role:           %s" % serverrole)
1708     logger.info("Hostname:              %s" % names.hostname)
1709     logger.info("NetBIOS Domain:        %s" % names.domain)
1710     logger.info("DNS Domain:            %s" % names.dnsdomain)
1711     logger.info("DOMAIN SID:            %s" % str(domainsid))
1712     if samdb_fill == FILL_FULL:
1713         logger.info("Admin password:        %s" % adminpass)
1714     if provision_backend.type is not "ldb":
1715         if provision_backend.credentials.get_bind_dn() is not None:
1716             logger.info("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1717         else:
1718             logger.info("LDAP Admin User:       %s" % provision_backend.credentials.get_username())
1719
1720         logger.info("LDAP Admin Password:   %s" % provision_backend.credentials.get_password())
1721
1722         if provision_backend.slapd_command_escaped is not None:
1723             # now display slapd_command_file.txt to show how slapd must be started next time
1724             logger.info("Use later the following commandline to start slapd, then Samba:")
1725             logger.info(provision_backend.slapd_command_escaped)
1726             logger.info("This slapd-Commandline is also stored under: %s/ldap_backend_startup.sh", 
1727                     provision_backend.ldapdir)
1728
1729     result = ProvisionResult()
1730     result.domaindn = domaindn
1731     result.paths = paths
1732     result.lp = lp
1733     result.samdb = samdb
1734     return result
1735
1736
1737 def provision_become_dc(setup_dir=None,
1738                         smbconf=None, targetdir=None, realm=None, 
1739                         rootdn=None, domaindn=None, schemadn=None,
1740                         configdn=None, serverdn=None,
1741                         domain=None, hostname=None, domainsid=None, 
1742                         adminpass=None, krbtgtpass=None, domainguid=None, 
1743                         policyguid=None, policyguid_dc=None, invocationid=None,
1744                         machinepass=None, 
1745                         dnspass=None, root=None, nobody=None, users=None, 
1746                         wheel=None, backup=None, serverrole=None, 
1747                         ldap_backend=None, ldap_backend_type=None,
1748                         sitename=None, debuglevel=1):
1749
1750     logger = logging.getLogger("provision")
1751     samba.set_debug_level(debuglevel)
1752
1753     res = provision(setup_dir, logger, system_session(), None,
1754                     smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1755                     realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1756                     configdn=configdn, serverdn=serverdn, domain=domain,
1757                     hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1758                     machinepass=machinepass, serverrole="domain controller",
1759                     sitename=sitename)
1760     res.lp.set("debuglevel", str(debuglevel))
1761     return res
1762
1763
1764 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1765     """Create a PHP LDAP admin configuration file.
1766
1767     :param path: Path to write the configuration to.
1768     :param setup_path: Function to generate setup paths.
1769     """
1770     setup_file(setup_path("phpldapadmin-config.php"), path, 
1771             {"S4_LDAPI_URI": ldapi_uri})
1772
1773
1774 def create_zone_file(lp, logger, paths, targetdir, setup_path, dnsdomain,
1775                      hostip, hostip6, hostname, realm, domainguid,
1776                      ntdsguid):
1777     """Write out a DNS zone file, from the info in the current database.
1778
1779     :param paths: paths object
1780     :param setup_path: Setup path function.
1781     :param dnsdomain: DNS Domain name
1782     :param domaindn: DN of the Domain
1783     :param hostip: Local IPv4 IP
1784     :param hostip6: Local IPv6 IP
1785     :param hostname: Local hostname
1786     :param realm: Realm name
1787     :param domainguid: GUID of the domain.
1788     :param ntdsguid: GUID of the hosts nTDSDSA record.
1789     """
1790     assert isinstance(domainguid, str)
1791
1792     if hostip6 is not None:
1793         hostip6_base_line = "            IN AAAA    " + hostip6
1794         hostip6_host_line = hostname + "        IN AAAA    " + hostip6
1795         gc_msdcs_ip6_line = "gc._msdcs               IN AAAA    " + hostip6
1796     else:
1797         hostip6_base_line = ""
1798         hostip6_host_line = ""
1799         gc_msdcs_ip6_line = ""
1800
1801     if hostip is not None:
1802         hostip_base_line = "            IN A    " + hostip
1803         hostip_host_line = hostname + "        IN A    " + hostip
1804         gc_msdcs_ip_line = "gc._msdcs               IN A    " + hostip
1805     else:
1806         hostip_base_line = ""
1807         hostip_host_line = ""
1808         gc_msdcs_ip_line = ""
1809
1810     dns_dir = os.path.dirname(paths.dns)
1811
1812     try:
1813         shutil.rmtree(dns_dir, True)
1814     except OSError:
1815         pass
1816
1817     os.mkdir(dns_dir, 0775)
1818
1819     # we need to freeze the zone while we update the contents
1820     if targetdir is None:
1821         rndc = ' '.join(lp.get("rndc command"))
1822         os.system(rndc + " freeze " + lp.get("realm"))
1823
1824     setup_file(setup_path("provision.zone"), paths.dns, {
1825             "HOSTNAME": hostname,
1826             "DNSDOMAIN": dnsdomain,
1827             "REALM": realm,
1828             "HOSTIP_BASE_LINE": hostip_base_line,
1829             "HOSTIP_HOST_LINE": hostip_host_line,
1830             "DOMAINGUID": domainguid,
1831             "DATESTRING": time.strftime("%Y%m%d%H"),
1832             "DEFAULTSITE": DEFAULTSITE,
1833             "NTDSGUID": ntdsguid,
1834             "HOSTIP6_BASE_LINE": hostip6_base_line,
1835             "HOSTIP6_HOST_LINE": hostip6_host_line,
1836             "GC_MSDCS_IP_LINE": gc_msdcs_ip_line,
1837             "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line,
1838         })
1839
1840     # note that we use no variable substitution on this file
1841     # the substitution is done at runtime by samba_dnsupdate
1842     setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1843
1844     # and the SPN update list
1845     setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1846
1847     if paths.bind_gid is not None:
1848         try:
1849             os.chown(dns_dir, -1, paths.bind_gid)
1850             os.chown(paths.dns, -1, paths.bind_gid)
1851             # chmod needed to cope with umask
1852             os.chmod(dns_dir, 0775)
1853             os.chmod(paths.dns, 0664)
1854         except OSError:
1855             if not os.environ.has_key('SAMBA_SELFTEST'):
1856                 logger.error("Failed to chown %s to bind gid %u" % (dns_dir, paths.bind_gid))
1857
1858     if targetdir is None:
1859         os.system(rndc + " unfreeze " + lp.get("realm"))
1860
1861
1862 def create_dns_update_list(lp, logger, paths, setup_path):
1863     """Write out a dns_update_list file"""
1864     # note that we use no variable substitution on this file
1865     # the substitution is done at runtime by samba_dnsupdate
1866     setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1867     setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1868
1869
1870 def create_named_conf(paths, setup_path, realm, dnsdomain,
1871                       private_dir):
1872     """Write out a file containing zone statements suitable for inclusion in a
1873     named.conf file (including GSS-TSIG configuration).
1874     
1875     :param paths: all paths
1876     :param setup_path: Setup path function.
1877     :param realm: Realm name
1878     :param dnsdomain: DNS Domain name
1879     :param private_dir: Path to private directory
1880     :param keytab_name: File name of DNS keytab file
1881     """
1882
1883     setup_file(setup_path("named.conf"), paths.namedconf, {
1884             "DNSDOMAIN": dnsdomain,
1885             "REALM": realm,
1886             "ZONE_FILE": paths.dns,
1887             "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1888             "NAMED_CONF": paths.namedconf,
1889             "NAMED_CONF_UPDATE": paths.namedconf_update
1890             })
1891
1892     setup_file(setup_path("named.conf.update"), paths.namedconf_update)
1893
1894
1895 def create_named_txt(path, setup_path, realm, dnsdomain,
1896                       private_dir, keytab_name):
1897     """Write out a file containing zone statements suitable for inclusion in a
1898     named.conf file (including GSS-TSIG configuration).
1899     
1900     :param path: Path of the new named.conf file.
1901     :param setup_path: Setup path function.
1902     :param realm: Realm name
1903     :param dnsdomain: DNS Domain name
1904     :param private_dir: Path to private directory
1905     :param keytab_name: File name of DNS keytab file
1906     """
1907
1908     setup_file(setup_path("named.txt"), path, {
1909             "DNSDOMAIN": dnsdomain,
1910             "REALM": realm,
1911             "DNS_KEYTAB": keytab_name,
1912             "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1913             "PRIVATE_DIR": private_dir
1914         })
1915
1916
1917 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1918     """Write out a file containing zone statements suitable for inclusion in a
1919     named.conf file (including GSS-TSIG configuration).
1920     
1921     :param path: Path of the new named.conf file.
1922     :param setup_path: Setup path function.
1923     :param dnsdomain: DNS Domain name
1924     :param hostname: Local hostname
1925     :param realm: Realm name
1926     """
1927     setup_file(setup_path("krb5.conf"), path, {
1928             "DNSDOMAIN": dnsdomain,
1929             "HOSTNAME": hostname,
1930             "REALM": realm,
1931         })
1932
1933
1934 class ProvisioningError(Exception):
1935     """A generic provision error."""
1936
1937     def __init__(self, value):
1938         self.value = value
1939
1940     def __str__(self):
1941         return "ProvisioningError: " + self.value
1942
1943
1944 class InvalidNetbiosName(Exception):
1945     """A specified name was not a valid NetBIOS name."""
1946     def __init__(self, name):
1947         super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)