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