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