s4-loadparm: removed "setup directory" option
[obnox/samba/samba-obnox.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     sysvol = os.path.join(lp.get("lock dir"), "sysvol")
628     netlogon = os.path.join(sysvol, realm.lower(), "scripts")
629
630     setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
631                smbconf, {
632             "NETBIOS_NAME": netbiosname,
633             "DOMAIN": domain,
634             "REALM": realm,
635             "SERVERROLE": serverrole,
636             "NETLOGONPATH": netlogon,
637             "SYSVOLPATH": sysvol,
638             "SIDGENERATOR_LINE": sid_generator_line,
639             "PRIVATEDIR_LINE": privatedir_line,
640             "LOCKDIR_LINE": lockdir_line
641             })
642
643     # reload the smb.conf
644     lp.load(smbconf)
645
646     # and dump it without any values that are the default
647     # this ensures that any smb.conf parameters that were set
648     # on the provision/join command line are set in the resulting smb.conf
649     f = open(smbconf, mode='w')
650     lp.dump(f, False)
651     f.close()
652
653
654
655 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
656                         users_gid, wheel_gid):
657     """setup reasonable name mappings for sam names to unix names.
658
659     :param samdb: SamDB object.
660     :param idmap: IDmap db object.
661     :param sid: The domain sid.
662     :param domaindn: The domain DN.
663     :param root_uid: uid of the UNIX root user.
664     :param nobody_uid: uid of the UNIX nobody user.
665     :param users_gid: gid of the UNIX users group.
666     :param wheel_gid: gid of the UNIX wheel group.
667     """
668     idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
669     idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
670
671     idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
672     idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
673
674
675 def setup_samdb_partitions(samdb_path, setup_path, logger, lp, session_info,
676                            provision_backend, names, schema, serverrole,
677                            erase=False):
678     """Setup the partitions for the SAM database.
679
680     Alternatively, provision() may call this, and then populate the database.
681
682     :note: This will wipe the Sam Database!
683
684     :note: This function always removes the local SAM LDB file. The erase
685         parameter controls whether to erase the existing data, which
686         may not be stored locally but in LDAP.
687
688     """
689     assert session_info is not None
690
691     # We use options=["modules:"] to stop the modules loading - we
692     # just want to wipe and re-initialise the database, not start it up
693
694     try:
695         os.unlink(samdb_path)
696     except OSError:
697         pass
698
699     samdb = Ldb(url=samdb_path, session_info=session_info,
700                 lp=lp, options=["modules:"])
701
702     ldap_backend_line = "# No LDAP backend"
703     if provision_backend.type is not "ldb":
704         ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
705
706     samdb.transaction_start()
707     try:
708         logger.info("Setting up sam.ldb partitions and settings")
709         setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
710                 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(),
711                 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
712                 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
713                 "LDAP_BACKEND_LINE": ldap_backend_line,
714         })
715
716
717         setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
718                 "BACKEND_TYPE": provision_backend.type,
719                 "SERVER_ROLE": serverrole
720                 })
721
722         logger.info("Setting up sam.ldb rootDSE")
723         setup_samdb_rootdse(samdb, setup_path, names)
724     except Exception:
725         samdb.transaction_cancel()
726         raise
727     else:
728         samdb.transaction_commit()
729
730
731 def secretsdb_self_join(secretsdb, domain,
732                         netbiosname, machinepass, domainsid=None,
733                         realm=None, dnsdomain=None,
734                         keytab_path=None,
735                         key_version_number=1,
736                         secure_channel_type=SEC_CHAN_WKSTA):
737     """Add domain join-specific bits to a secrets database.
738
739     :param secretsdb: Ldb Handle to the secrets database
740     :param machinepass: Machine password
741     """
742     attrs = ["whenChanged",
743            "secret",
744            "priorSecret",
745            "priorChanged",
746            "krb5Keytab",
747            "privateKeytab"]
748
749     if realm is not None:
750         if dnsdomain is None:
751             dnsdomain = realm.lower()
752         dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
753     else:
754         dnsname = None
755     shortname = netbiosname.lower()
756
757     # We don't need to set msg["flatname"] here, because rdn_name will handle
758     # it, and it causes problems for modifies anyway
759     msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
760     msg["secureChannelType"] = [str(secure_channel_type)]
761     msg["objectClass"] = ["top", "primaryDomain"]
762     if dnsname is not None:
763         msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
764         msg["realm"] = [realm]
765         msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
766         msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
767         msg["privateKeytab"] = ["secrets.keytab"]
768
769     msg["secret"] = [machinepass]
770     msg["samAccountName"] = ["%s$" % netbiosname]
771     msg["secureChannelType"] = [str(secure_channel_type)]
772     if domainsid is not None:
773         msg["objectSid"] = [ndr_pack(domainsid)]
774
775     # This complex expression tries to ensure that we don't have more
776     # than one record for this SID, realm or netbios domain at a time,
777     # but we don't delete the old record that we are about to modify,
778     # because that would delete the keytab and previous password.
779     res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
780         expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(dn=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
781         scope=ldb.SCOPE_ONELEVEL)
782
783     for del_msg in res:
784         secretsdb.delete(del_msg.dn)
785
786     res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
787
788     if len(res) == 1:
789         msg["priorSecret"] = [res[0]["secret"][0]]
790         msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
791
792         try:
793             msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
794         except KeyError:
795             pass
796
797         try:
798             msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
799         except KeyError:
800             pass
801
802         for el in msg:
803             if el != 'dn':
804                 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
805         secretsdb.modify(msg)
806         secretsdb.rename(res[0].dn, msg.dn)
807     else:
808         spn = [ 'HOST/%s' % shortname ]
809         if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
810             # we are a domain controller then we add servicePrincipalName
811             # entries for the keytab code to update.
812             spn.extend([ 'HOST/%s' % dnsname ])
813         msg["servicePrincipalName"] = spn
814
815         secretsdb.add(msg)
816
817
818 def secretsdb_setup_dns(secretsdb, setup_path, names, private_dir, realm,
819                         dnsdomain, dns_keytab_path, dnspass):
820     """Add DNS specific bits to a secrets database.
821
822     :param secretsdb: Ldb Handle to the secrets database
823     :param setup_path: Setup path function
824     :param machinepass: Machine password
825     """
826     try:
827         os.unlink(os.path.join(private_dir, dns_keytab_path))
828     except OSError:
829         pass
830
831     setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
832             "REALM": realm,
833             "DNSDOMAIN": dnsdomain,
834             "DNS_KEYTAB": dns_keytab_path,
835             "DNSPASS_B64": b64encode(dnspass),
836             "HOSTNAME": names.hostname,
837             "DNSNAME" : '%s.%s' % (
838                 names.netbiosname.lower(), names.dnsdomain.lower())
839             })
840
841
842 def setup_secretsdb(paths, setup_path, session_info, backend_credentials, lp):
843     """Setup the secrets database.
844
845    :note: This function does not handle exceptions and transaction on purpose,
846        it's up to the caller to do this job.
847
848     :param path: Path to the secrets database.
849     :param setup_path: Get the path to a setup file.
850     :param session_info: Session info.
851     :param credentials: Credentials
852     :param lp: Loadparm context
853     :return: LDB handle for the created secrets database
854     """
855     if os.path.exists(paths.secrets):
856         os.unlink(paths.secrets)
857
858     keytab_path = os.path.join(paths.private_dir, paths.keytab)
859     if os.path.exists(keytab_path):
860         os.unlink(keytab_path)
861
862     dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
863     if os.path.exists(dns_keytab_path):
864         os.unlink(dns_keytab_path)
865
866     path = paths.secrets
867
868     secrets_ldb = Ldb(path, session_info=session_info,
869                       lp=lp)
870     secrets_ldb.erase()
871     secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
872     secrets_ldb = Ldb(path, session_info=session_info,
873                       lp=lp)
874     secrets_ldb.transaction_start()
875     try:
876         secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
877
878         if (backend_credentials is not None and
879             backend_credentials.authentication_requested()):
880             if backend_credentials.get_bind_dn() is not None:
881                 setup_add_ldif(secrets_ldb,
882                     setup_path("secrets_simple_ldap.ldif"), {
883                         "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
884                         "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
885                         })
886             else:
887                 setup_add_ldif(secrets_ldb,
888                     setup_path("secrets_sasl_ldap.ldif"), {
889                         "LDAPADMINUSER": backend_credentials.get_username(),
890                         "LDAPADMINREALM": backend_credentials.get_realm(),
891                         "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
892                         })
893
894         return secrets_ldb
895     except Exception:
896         secrets_ldb.transaction_cancel()
897         raise
898
899
900 def setup_privileges(path, setup_path, session_info, lp):
901     """Setup the privileges database.
902
903     :param path: Path to the privileges database.
904     :param setup_path: Get the path to a setup file.
905     :param session_info: Session info.
906     :param credentials: Credentials
907     :param lp: Loadparm context
908     :return: LDB handle for the created secrets database
909     """
910     if os.path.exists(path):
911         os.unlink(path)
912     privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
913     privilege_ldb.erase()
914     privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
915
916
917 def setup_registry(path, setup_path, session_info, lp):
918     """Setup the registry.
919
920     :param path: Path to the registry database
921     :param setup_path: Function that returns the path to a setup.
922     :param session_info: Session information
923     :param credentials: Credentials
924     :param lp: Loadparm context
925     """
926     reg = samba.registry.Registry()
927     hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
928     reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
929     provision_reg = setup_path("provision.reg")
930     assert os.path.exists(provision_reg)
931     reg.diff_apply(provision_reg)
932
933
934 def setup_idmapdb(path, setup_path, session_info, lp):
935     """Setup the idmap database.
936
937     :param path: path to the idmap database
938     :param setup_path: Function that returns a path to a setup file
939     :param session_info: Session information
940     :param credentials: Credentials
941     :param lp: Loadparm context
942     """
943     if os.path.exists(path):
944         os.unlink(path)
945
946     idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
947     idmap_ldb.erase()
948     idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
949     return idmap_ldb
950
951
952 def setup_samdb_rootdse(samdb, setup_path, names):
953     """Setup the SamDB rootdse.
954
955     :param samdb: Sam Database handle
956     :param setup_path: Obtain setup path
957     """
958     setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
959         "SCHEMADN": names.schemadn,
960         "DOMAINDN": names.domaindn,
961         "ROOTDN": names.rootdn,
962         "CONFIGDN": names.configdn,
963         "SERVERDN": names.serverdn,
964         })
965
966
967 def setup_self_join(samdb, names, machinepass, dnspass,
968                     domainsid, next_rid, invocationid, setup_path,
969                     policyguid, policyguid_dc, domainControllerFunctionality,
970                     ntdsguid):
971     """Join a host to its own domain."""
972     assert isinstance(invocationid, str)
973     if ntdsguid is not None:
974         ntdsguid_line = "objectGUID: %s\n"%ntdsguid
975     else:
976         ntdsguid_line = ""
977     setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
978               "CONFIGDN": names.configdn,
979               "SCHEMADN": names.schemadn,
980               "DOMAINDN": names.domaindn,
981               "SERVERDN": names.serverdn,
982               "INVOCATIONID": invocationid,
983               "NETBIOSNAME": names.netbiosname,
984               "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
985               "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
986               "DOMAINSID": str(domainsid),
987               "DCRID": str(next_rid),
988               "SAMBA_VERSION_STRING": version,
989               "NTDSGUID": ntdsguid_line,
990               "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
991                   domainControllerFunctionality)})
992
993     setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
994               "POLICYGUID": policyguid,
995               "POLICYGUID_DC": policyguid_dc,
996               "DNSDOMAIN": names.dnsdomain,
997               "DOMAINDN": names.domaindn})
998
999     # add the NTDSGUID based SPNs
1000     ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1001     names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
1002                                      expression="", scope=ldb.SCOPE_BASE)
1003     assert isinstance(names.ntdsguid, str)
1004
1005     # Setup fSMORoleOwner entries to point at the newly created DC entry
1006     setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1007               "DOMAINDN": names.domaindn,
1008               "CONFIGDN": names.configdn,
1009               "SCHEMADN": names.schemadn,
1010               "DEFAULTSITE": names.sitename,
1011               "SERVERDN": names.serverdn,
1012               "NETBIOSNAME": names.netbiosname,
1013               "RIDALLOCATIONSTART": str(next_rid + 100),
1014               "RIDALLOCATIONEND": str(next_rid + 100 + 499),
1015               })
1016
1017     # This is partially Samba4 specific and should be replaced by the correct
1018     # DNS AD-style setup
1019     setup_add_ldif(samdb, setup_path("provision_dns_add.ldif"), {
1020               "DNSDOMAIN": names.dnsdomain,
1021               "DOMAINDN": names.domaindn,
1022               "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1023               "HOSTNAME" : names.hostname,
1024               "DNSNAME" : '%s.%s' % (
1025                   names.netbiosname.lower(), names.dnsdomain.lower())
1026               })
1027
1028
1029 def getpolicypath(sysvolpath, dnsdomain, guid):
1030     """Return the physical path of policy given its guid.
1031
1032     :param sysvolpath: Path to the sysvol folder
1033     :param dnsdomain: DNS name of the AD domain
1034     :param guid: The GUID of the policy
1035     :return: A string with the complete path to the policy folder
1036     """
1037
1038     if guid[0] != "{":
1039         guid = "{%s}" % guid
1040     policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1041     return policy_path
1042
1043
1044 def create_gpo_struct(policy_path):
1045     if not os.path.exists(policy_path):
1046         os.makedirs(policy_path, 0775)
1047     open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1048                       "[General]\r\nVersion=0")
1049     p = os.path.join(policy_path, "MACHINE")
1050     if not os.path.exists(p):
1051         os.makedirs(p, 0775)
1052     p = os.path.join(policy_path, "USER")
1053     if not os.path.exists(p):
1054         os.makedirs(p, 0775)
1055
1056
1057 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1058     """Create the default GPO for a domain
1059
1060     :param sysvolpath: Physical path for the sysvol folder
1061     :param dnsdomain: DNS domain name of the AD domain
1062     :param policyguid: GUID of the default domain policy
1063     :param policyguid_dc: GUID of the default domain controler policy
1064     """
1065     policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1066     create_gpo_struct(policy_path)
1067
1068     policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1069     create_gpo_struct(policy_path)
1070
1071
1072 def setup_samdb(path, setup_path, session_info, provision_backend, lp, names,
1073         logger, domainsid, domainguid, policyguid, policyguid_dc, fill,
1074         adminpass, krbtgtpass, machinepass, invocationid, dnspass, ntdsguid,
1075         serverrole, am_rodc=False, dom_for_fun_level=None, schema=None,
1076         next_rid=1000):
1077     """Setup a complete SAM Database.
1078
1079     :note: This will wipe the main SAM database file!
1080     """
1081
1082     # Provision does not make much sense values larger than 1000000000
1083     # as the upper range of the rIDAvailablePool is 1073741823 and
1084     # we don't want to create a domain that cannot allocate rids.
1085     if next_rid < 1000 or next_rid > 1000000000:
1086         error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1087         error += "the valid range is %u-%u. The default is %u." % (
1088             1000, 1000000000, 1000)
1089         raise ProvisioningError(error)
1090
1091     # ATTENTION: Do NOT change these default values without discussion with the
1092     # team and/or release manager. They have a big impact on the whole program!
1093     domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1094
1095     if dom_for_fun_level is None:
1096         dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1097
1098     if dom_for_fun_level > domainControllerFunctionality:
1099         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!")
1100
1101     domainFunctionality = dom_for_fun_level
1102     forestFunctionality = dom_for_fun_level
1103
1104     # Also wipes the database
1105     setup_samdb_partitions(path, setup_path, logger=logger, lp=lp,
1106         provision_backend=provision_backend, session_info=session_info,
1107         names=names, serverrole=serverrole, schema=schema)
1108
1109     if schema is None:
1110         schema = Schema(setup_path, domainsid, schemadn=names.schemadn)
1111
1112     # Load the database, but don's load the global schema and don't connect
1113     # quite yet
1114     samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1115                   credentials=provision_backend.credentials, lp=lp,
1116                   global_schema=False, am_rodc=am_rodc)
1117
1118     logger.info("Pre-loading the Samba 4 and AD schema")
1119
1120     # Load the schema from the one we computed earlier
1121     samdb.set_schema(schema)
1122
1123     # Set the NTDS settings DN manually - in order to have it already around
1124     # before the provisioned tree exists and we connect
1125     samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1126
1127     # And now we can connect to the DB - the schema won't be loaded from the
1128     # DB
1129     samdb.connect(path)
1130
1131     if fill == FILL_DRS:
1132         return samdb
1133
1134     samdb.transaction_start()
1135     try:
1136         # Set the domain functionality levels onto the database.
1137         # Various module (the password_hash module in particular) need
1138         # to know what level of AD we are emulating.
1139
1140         # These will be fixed into the database via the database
1141         # modifictions below, but we need them set from the start.
1142         samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1143         samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1144         samdb.set_opaque_integer("domainControllerFunctionality",
1145             domainControllerFunctionality)
1146
1147         samdb.set_domain_sid(str(domainsid))
1148         samdb.set_invocation_id(invocationid)
1149
1150         logger.info("Adding DomainDN: %s" % names.domaindn)
1151
1152         # impersonate domain admin
1153         admin_session_info = admin_session(lp, str(domainsid))
1154         samdb.set_session_info(admin_session_info)
1155         if domainguid is not None:
1156             domainguid_line = "objectGUID: %s\n-" % domainguid
1157         else:
1158             domainguid_line = ""
1159
1160         descr = b64encode(get_domain_descriptor(domainsid))
1161         setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1162                 "DOMAINDN": names.domaindn,
1163                 "DOMAINSID": str(domainsid),
1164                 "DESCRIPTOR": descr,
1165                 "DOMAINGUID": domainguid_line
1166                 })
1167
1168         setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1169             "DOMAINDN": names.domaindn,
1170             "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1171             "NEXTRID": str(next_rid),
1172             "DEFAULTSITE": names.sitename,
1173             "CONFIGDN": names.configdn,
1174             "POLICYGUID": policyguid,
1175             "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1176             "SAMBA_VERSION_STRING": version
1177             })
1178
1179         logger.info("Adding configuration container")
1180         descr = b64encode(get_config_descriptor(domainsid))
1181         setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1182             "CONFIGDN": names.configdn,
1183             "DESCRIPTOR": descr,
1184             })
1185
1186         # The LDIF here was created when the Schema object was constructed
1187         logger.info("Setting up sam.ldb schema")
1188         samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1189         samdb.modify_ldif(schema.schema_dn_modify)
1190         samdb.write_prefixes_from_schema()
1191         samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1192         setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1193                        {"SCHEMADN": names.schemadn})
1194
1195         logger.info("Reopening sam.ldb with new schema")
1196     except Exception:
1197         samdb.transaction_cancel()
1198         raise
1199     else:
1200         samdb.transaction_commit()
1201
1202     samdb = SamDB(session_info=admin_session_info, auto_connect=False,
1203                 credentials=provision_backend.credentials, lp=lp,
1204                 global_schema=False, am_rodc=am_rodc)
1205
1206     # Set the NTDS settings DN manually - in order to have it already around
1207     # before the provisioned tree exists and we connect
1208     samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1209     samdb.connect(path)
1210
1211     samdb.transaction_start()
1212     try:
1213         samdb.invocation_id = invocationid
1214
1215         logger.info("Setting up sam.ldb configuration data")
1216         descr = b64encode(get_sites_descriptor(domainsid))
1217         setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1218             "CONFIGDN": names.configdn,
1219             "NETBIOSNAME": names.netbiosname,
1220             "DEFAULTSITE": names.sitename,
1221             "DNSDOMAIN": names.dnsdomain,
1222             "DOMAIN": names.domain,
1223             "SCHEMADN": names.schemadn,
1224             "DOMAINDN": names.domaindn,
1225             "SERVERDN": names.serverdn,
1226             "FOREST_FUNCTIONALITY": str(forestFunctionality),
1227             "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1228             "SITES_DESCRIPTOR": descr
1229             })
1230
1231         logger.info("Setting up display specifiers")
1232         display_specifiers_ldif = read_ms_ldif(
1233             setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1234         display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1235             {"CONFIGDN": names.configdn})
1236         check_all_substituted(display_specifiers_ldif)
1237         samdb.add_ldif(display_specifiers_ldif)
1238
1239         logger.info("Adding users container")
1240         setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1241                 "DOMAINDN": names.domaindn})
1242         logger.info("Modifying users container")
1243         setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1244                 "DOMAINDN": names.domaindn})
1245         logger.info("Adding computers container")
1246         setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1247                 "DOMAINDN": names.domaindn})
1248         logger.info("Modifying computers container")
1249         setup_modify_ldif(samdb,
1250             setup_path("provision_computers_modify.ldif"), {
1251                 "DOMAINDN": names.domaindn})
1252         logger.info("Setting up sam.ldb data")
1253         setup_add_ldif(samdb, setup_path("provision.ldif"), {
1254             "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1255             "DOMAINDN": names.domaindn,
1256             "NETBIOSNAME": names.netbiosname,
1257             "DEFAULTSITE": names.sitename,
1258             "CONFIGDN": names.configdn,
1259             "SERVERDN": names.serverdn,
1260             "RIDAVAILABLESTART": str(next_rid + 600),
1261             "POLICYGUID_DC": policyguid_dc
1262             })
1263
1264         setup_modify_ldif(samdb,
1265             setup_path("provision_basedn_references.ldif"), {
1266                 "DOMAINDN": names.domaindn})
1267
1268         setup_modify_ldif(samdb,
1269             setup_path("provision_configuration_references.ldif"), {
1270                 "CONFIGDN": names.configdn,
1271                 "SCHEMADN": names.schemadn})
1272         if fill == FILL_FULL:
1273             logger.info("Setting up sam.ldb users and groups")
1274             setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1275                 "DOMAINDN": names.domaindn,
1276                 "DOMAINSID": str(domainsid),
1277                 "CONFIGDN": names.configdn,
1278                 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1279                 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1280                 })
1281
1282             logger.info("Setting up self join")
1283             setup_self_join(samdb, names=names, invocationid=invocationid,
1284                 dnspass=dnspass,
1285                 machinepass=machinepass,
1286                 domainsid=domainsid,
1287                 next_rid=next_rid,
1288                 policyguid=policyguid,
1289                 policyguid_dc=policyguid_dc,
1290                 setup_path=setup_path,
1291                 domainControllerFunctionality=domainControllerFunctionality,
1292                 ntdsguid=ntdsguid)
1293
1294             ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1295             names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1296                 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1297             assert isinstance(names.ntdsguid, str)
1298     except Exception:
1299         samdb.transaction_cancel()
1300         raise
1301     else:
1302         samdb.transaction_commit()
1303         return samdb
1304
1305
1306 FILL_FULL = "FULL"
1307 FILL_NT4SYNC = "NT4SYNC"
1308 FILL_DRS = "DRS"
1309 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1310 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)"
1311
1312
1313 def set_dir_acl(path, acl, lp, domsid):
1314     setntacl(lp, path, acl, domsid)
1315     for root, dirs, files in os.walk(path, topdown=False):
1316         for name in files:
1317             setntacl(lp, os.path.join(root, name), acl, domsid)
1318         for name in dirs:
1319             setntacl(lp, os.path.join(root, name), acl, domsid)
1320
1321
1322 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1323     """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1324     folders beneath.
1325
1326     :param sysvol: Physical path for the sysvol folder
1327     :param dnsdomain: The DNS name of the domain
1328     :param domainsid: The SID of the domain
1329     :param domaindn: The DN of the domain (ie. DC=...)
1330     :param samdb: An LDB object on the SAM db
1331     :param lp: an LP object
1332     """
1333
1334     # Set ACL for GPO root folder
1335     root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1336     setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid))
1337
1338     res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1339                         attrs=["cn", "nTSecurityDescriptor"],
1340                         expression="", scope=ldb.SCOPE_ONELEVEL)
1341
1342     for policy in res:
1343         acl = ndr_unpack(security.descriptor,
1344                          str(policy["nTSecurityDescriptor"])).as_sddl()
1345         policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1346         set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1347                     str(domainsid))
1348
1349
1350 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1351     lp):
1352     """Set the ACL for the sysvol share and the subfolders
1353
1354     :param samdb: An LDB object on the SAM db
1355     :param netlogon: Physical path for the netlogon folder
1356     :param sysvol: Physical path for the sysvol folder
1357     :param gid: The GID of the "Domain adminstrators" group
1358     :param domainsid: The SID of the domain
1359     :param dnsdomain: The DNS name of the domain
1360     :param domaindn: The DN of the domain (ie. DC=...)
1361     """
1362
1363     try:
1364         os.chown(sysvol, -1, gid)
1365     except OSError:
1366         canchown = False
1367     else:
1368         canchown = True
1369
1370     # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1371     setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid))
1372     for root, dirs, files in os.walk(sysvol, topdown=False):
1373         for name in files:
1374             if canchown:
1375                 os.chown(os.path.join(root, name), -1, gid)
1376             setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1377         for name in dirs:
1378             if canchown:
1379                 os.chown(os.path.join(root, name), -1, gid)
1380             setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1381
1382     # Set acls on Policy folder and policies folders
1383     set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp)
1384
1385
1386 def provision(setup_dir, logger, session_info, credentials, smbconf=None,
1387         targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1388         domaindn=None, schemadn=None, configdn=None, serverdn=None,
1389         domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1390         next_rid=1000, adminpass=None, ldapadminpass=None, krbtgtpass=None,
1391         domainguid=None, policyguid=None, policyguid_dc=None,
1392         invocationid=None, machinepass=None, ntdsguid=None, dnspass=None,
1393         root=None, nobody=None, users=None, wheel=None, backup=None, aci=None,
1394         serverrole=None, dom_for_fun_level=None, ldap_backend_extra_port=None,
1395         ldap_backend_forced_uri=None, backend_type=None, sitename=None,
1396         ol_mmr_urls=None, ol_olc=None, setup_ds_path=None, slapd_path=None,
1397         nosync=False, ldap_dryrun_mode=False, useeadb=False, am_rodc=False,
1398         lp=None):
1399     """Provision samba4
1400
1401     :note: caution, this wipes all existing data!
1402     """
1403
1404     def setup_path(file):
1405         return os.path.join(setup_dir, file)
1406
1407     if domainsid is None:
1408         domainsid = security.random_sid()
1409     else:
1410         domainsid = security.dom_sid(domainsid)
1411
1412     # create/adapt the group policy GUIDs
1413     # Default GUID for default policy are described at
1414     # "How Core Group Policy Works"
1415     # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1416     if policyguid is None:
1417         policyguid = DEFAULT_POLICY_GUID
1418     policyguid = policyguid.upper()
1419     if policyguid_dc is None:
1420         policyguid_dc = DEFAULT_DC_POLICY_GUID
1421     policyguid_dc = policyguid_dc.upper()
1422
1423     if adminpass is None:
1424         adminpass = samba.generate_random_password(12, 32)
1425     if krbtgtpass is None:
1426         krbtgtpass = samba.generate_random_password(128, 255)
1427     if machinepass is None:
1428         machinepass  = samba.generate_random_password(128, 255)
1429     if dnspass is None:
1430         dnspass = samba.generate_random_password(128, 255)
1431     if ldapadminpass is None:
1432         # Make a new, random password between Samba and it's LDAP server
1433         ldapadminpass=samba.generate_random_password(128, 255)
1434
1435     if backend_type is None:
1436         backend_type = "ldb"
1437
1438     sid_generator = "internal"
1439     if backend_type == "fedora-ds":
1440         sid_generator = "backend"
1441
1442     root_uid = findnss_uid([root or "root"])
1443     nobody_uid = findnss_uid([nobody or "nobody"])
1444     users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1445     if wheel is None:
1446         wheel_gid = findnss_gid(["wheel", "adm"])
1447     else:
1448         wheel_gid = findnss_gid([wheel])
1449     try:
1450         bind_gid = findnss_gid(["bind", "named"])
1451     except KeyError:
1452         bind_gid = None
1453
1454     if targetdir is not None:
1455         smbconf = os.path.join(targetdir, "etc", "smb.conf")
1456     elif smbconf is None:
1457         smbconf = samba.param.default_path()
1458     if not os.path.exists(os.path.dirname(smbconf)):
1459         os.makedirs(os.path.dirname(smbconf))
1460
1461     # only install a new smb.conf if there isn't one there already
1462     if os.path.exists(smbconf):
1463         # if Samba Team members can't figure out the weird errors
1464         # loading an empty smb.conf gives, then we need to be smarter.
1465         # Pretend it just didn't exist --abartlet
1466         data = open(smbconf, 'r').read()
1467         data = data.lstrip()
1468         if data is None or data == "":
1469             make_smbconf(smbconf, setup_path, hostname, domain, realm,
1470                          serverrole, targetdir, sid_generator, useeadb,
1471                          lp=lp)
1472     else:
1473         make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1474                      targetdir, sid_generator, useeadb, lp=lp)
1475
1476     if lp is None:
1477         lp = samba.param.LoadParm()
1478     lp.load(smbconf)
1479     names = guess_names(lp=lp, hostname=hostname, domain=domain,
1480         dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
1481         configdn=configdn, schemadn=schemadn, serverdn=serverdn,
1482         sitename=sitename)
1483     paths = provision_paths_from_lp(lp, names.dnsdomain)
1484
1485     paths.bind_gid = bind_gid
1486
1487     if hostip is None:
1488         logger.info("Looking up IPv4 addresses")
1489         hostips = samba.interface_ips(lp, False)
1490         if len(hostips) == 0:
1491             logger.warning("No external IPv4 address has been found. Using loopback.")
1492             hostip = '127.0.0.1'
1493         else:
1494             hostip = hostips[0]
1495             if len(hostips) > 1:
1496                 logger.warning("More than one IPv4 address found. Using %s.",
1497                     hostip)
1498
1499     if serverrole is None:
1500         serverrole = lp.get("server role")
1501
1502     assert serverrole in ("domain controller", "member server", "standalone")
1503     if invocationid is None:
1504         invocationid = str(uuid.uuid4())
1505
1506     if not os.path.exists(paths.private_dir):
1507         os.mkdir(paths.private_dir)
1508     if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1509         os.mkdir(os.path.join(paths.private_dir, "tls"))
1510
1511     ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1512
1513     schema = Schema(setup_path, domainsid, invocationid=invocationid,
1514         schemadn=names.schemadn)
1515
1516     if backend_type == "ldb":
1517         provision_backend = LDBBackend(backend_type, paths=paths,
1518             setup_path=setup_path, lp=lp, credentials=credentials,
1519             names=names, logger=logger)
1520     elif backend_type == "existing":
1521         provision_backend = ExistingBackend(backend_type, paths=paths,
1522             setup_path=setup_path, lp=lp, credentials=credentials,
1523             names=names, logger=logger,
1524             ldap_backend_forced_uri=ldap_backend_forced_uri)
1525     elif backend_type == "fedora-ds":
1526         provision_backend = FDSBackend(backend_type, paths=paths,
1527             setup_path=setup_path, lp=lp, credentials=credentials,
1528             names=names, logger=logger, domainsid=domainsid,
1529             schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1530             slapd_path=slapd_path,
1531             ldap_backend_extra_port=ldap_backend_extra_port,
1532             ldap_dryrun_mode=ldap_dryrun_mode, root=root,
1533             setup_ds_path=setup_ds_path,
1534             ldap_backend_forced_uri=ldap_backend_forced_uri)
1535     elif backend_type == "openldap":
1536         provision_backend = OpenLDAPBackend(backend_type, paths=paths,
1537             setup_path=setup_path, lp=lp, credentials=credentials,
1538             names=names, logger=logger, domainsid=domainsid,
1539             schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1540             slapd_path=slapd_path,
1541             ldap_backend_extra_port=ldap_backend_extra_port,
1542             ldap_dryrun_mode=ldap_dryrun_mode, ol_mmr_urls=ol_mmr_urls,
1543             nosync=nosync,
1544             ldap_backend_forced_uri=ldap_backend_forced_uri)
1545     else:
1546         raise ValueError("Unknown LDAP backend type selected")
1547
1548     provision_backend.init()
1549     provision_backend.start()
1550
1551     # only install a new shares config db if there is none
1552     if not os.path.exists(paths.shareconf):
1553         logger.info("Setting up share.ldb")
1554         share_ldb = Ldb(paths.shareconf, session_info=session_info,
1555                         lp=lp)
1556         share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1557
1558     logger.info("Setting up secrets.ldb")
1559     secrets_ldb = setup_secretsdb(paths, setup_path,
1560         session_info=session_info,
1561         backend_credentials=provision_backend.secrets_credentials, lp=lp)
1562
1563     try:
1564         logger.info("Setting up the registry")
1565         setup_registry(paths.hklm, setup_path, session_info,
1566                        lp=lp)
1567
1568         logger.info("Setting up the privileges database")
1569         setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1570
1571         logger.info("Setting up idmap db")
1572         idmap = setup_idmapdb(paths.idmapdb, setup_path,
1573             session_info=session_info, lp=lp)
1574
1575         logger.info("Setting up SAM db")
1576         samdb = setup_samdb(paths.samdb, setup_path, session_info,
1577             provision_backend, lp, names, logger=logger,
1578             domainsid=domainsid, schema=schema, domainguid=domainguid,
1579             policyguid=policyguid, policyguid_dc=policyguid_dc,
1580             fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1581             invocationid=invocationid, machinepass=machinepass,
1582             dnspass=dnspass, ntdsguid=ntdsguid, serverrole=serverrole,
1583             dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1584             next_rid=next_rid)
1585
1586         if serverrole == "domain controller":
1587             if paths.netlogon is None:
1588                 logger.info("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1589                 logger.info("Please either remove %s or see the template at %s" %
1590                         (paths.smbconf, setup_path("provision.smb.conf.dc")))
1591                 assert paths.netlogon is not None
1592
1593             if paths.sysvol is None:
1594                 logger.info("Existing smb.conf does not have a [sysvol] share, but you"
1595                         " 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.sysvol is not None
1599
1600             if not os.path.isdir(paths.netlogon):
1601                 os.makedirs(paths.netlogon, 0755)
1602
1603         if samdb_fill == FILL_FULL:
1604             setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1605                                 root_uid=root_uid, nobody_uid=nobody_uid,
1606                                 users_gid=users_gid, wheel_gid=wheel_gid)
1607
1608             if serverrole == "domain controller":
1609                 # Set up group policies (domain policy and domain controller
1610                 # policy)
1611                 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1612                     policyguid_dc)
1613                 setsysvolacl(samdb, paths.netlogon, paths.sysvol, wheel_gid,
1614                     domainsid, names.dnsdomain, names.domaindn, lp)
1615
1616             logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1617             setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1618
1619             secretsdb_self_join(secrets_ldb, domain=names.domain,
1620                 realm=names.realm, dnsdomain=names.dnsdomain,
1621                 netbiosname=names.netbiosname, domainsid=domainsid,
1622                 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1623
1624             # Now set up the right msDS-SupportedEncryptionTypes into the DB
1625             # In future, this might be determined from some configuration
1626             kerberos_enctypes = str(ENC_ALL_TYPES)
1627
1628             try:
1629                 msg = ldb.Message(ldb.Dn(samdb,
1630                     samdb.searchone("distinguishedName",
1631                         expression="samAccountName=%s$" % names.netbiosname,
1632                         scope=ldb.SCOPE_SUBTREE)))
1633                 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1634                     elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1635                     name="msDS-SupportedEncryptionTypes")
1636                 samdb.modify(msg)
1637             except ldb.LdbError, (enum, estr):
1638                 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1639                     # It might be that this attribute does not exist in this schema
1640                     raise
1641
1642             if serverrole == "domain controller":
1643                 secretsdb_setup_dns(secrets_ldb, setup_path, names,
1644                     paths.private_dir, realm=names.realm,
1645                     dnsdomain=names.dnsdomain,
1646                     dns_keytab_path=paths.dns_keytab, dnspass=dnspass)
1647
1648                 domainguid = samdb.searchone(basedn=domaindn,
1649                     attribute="objectGUID")
1650                 assert isinstance(domainguid, str)
1651
1652                 # Only make a zone file on the first DC, it should be
1653                 # replicated with DNS replication
1654                 create_zone_file(lp, logger, paths, targetdir, setup_path,
1655                     dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
1656                     hostname=names.hostname, realm=names.realm,
1657                     domainguid=domainguid, ntdsguid=names.ntdsguid)
1658
1659                 create_named_conf(paths, setup_path, realm=names.realm,
1660                     dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1661
1662                 create_named_txt(paths.namedtxt, setup_path,
1663                     realm=names.realm, dnsdomain=names.dnsdomain,
1664                     private_dir=paths.private_dir,
1665                     keytab_name=paths.dns_keytab)
1666                 logger.info("See %s for an example configuration include file for BIND", paths.namedconf)
1667                 logger.info("and %s for further documentation required for secure DNS "
1668                         "updates", paths.namedtxt)
1669
1670             lastProvisionUSNs = get_last_provision_usn(samdb)
1671             maxUSN = get_max_usn(samdb, str(names.rootdn))
1672             if lastProvisionUSNs is not None:
1673                 update_provision_usn(samdb, 0, maxUSN, 1)
1674             else:
1675                 set_provision_usn(samdb, 0, maxUSN)
1676
1677         create_krb5_conf(paths.krb5conf, setup_path,
1678                          dnsdomain=names.dnsdomain, hostname=names.hostname,
1679                          realm=names.realm)
1680         logger.info("A Kerberos configuration suitable for Samba 4 has been "
1681                     "generated at %s", paths.krb5conf)
1682
1683         if serverrole == "domain controller":
1684             create_dns_update_list(lp, logger, paths, setup_path)
1685
1686         provision_backend.post_setup()
1687         provision_backend.shutdown()
1688
1689         create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1690                                    ldapi_url)
1691     except Exception:
1692         secrets_ldb.transaction_cancel()
1693         raise
1694
1695     # Now commit the secrets.ldb to disk
1696     secrets_ldb.transaction_commit()
1697
1698     # the commit creates the dns.keytab, now chown it
1699     dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1700     if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
1701         try:
1702             os.chmod(dns_keytab_path, 0640)
1703             os.chown(dns_keytab_path, -1, paths.bind_gid)
1704         except OSError:
1705             if not os.environ.has_key('SAMBA_SELFTEST'):
1706                 logger.info("Failed to chown %s to bind gid %u",
1707                             dns_keytab_path, paths.bind_gid)
1708
1709
1710     logger.info("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php",
1711             paths.phpldapadminconfig)
1712
1713     logger.info("Once the above files are installed, your Samba4 server will be ready to use")
1714     logger.info("Server Role:           %s" % serverrole)
1715     logger.info("Hostname:              %s" % names.hostname)
1716     logger.info("NetBIOS Domain:        %s" % names.domain)
1717     logger.info("DNS Domain:            %s" % names.dnsdomain)
1718     logger.info("DOMAIN SID:            %s" % str(domainsid))
1719     if samdb_fill == FILL_FULL:
1720         logger.info("Admin password:        %s" % adminpass)
1721     if provision_backend.type is not "ldb":
1722         if provision_backend.credentials.get_bind_dn() is not None:
1723             logger.info("LDAP Backend Admin DN: %s" %
1724                 provision_backend.credentials.get_bind_dn())
1725         else:
1726             logger.info("LDAP Admin User:       %s" %
1727                 provision_backend.credentials.get_username())
1728
1729         logger.info("LDAP Admin Password:   %s" %
1730             provision_backend.credentials.get_password())
1731
1732         if provision_backend.slapd_command_escaped is not None:
1733             # now display slapd_command_file.txt to show how slapd must be
1734             # started next time
1735             logger.info("Use later the following commandline to start slapd, then Samba:")
1736             logger.info(provision_backend.slapd_command_escaped)
1737             logger.info("This slapd-Commandline is also stored under: %s/ldap_backend_startup.sh",
1738                     provision_backend.ldapdir)
1739
1740     result = ProvisionResult()
1741     result.domaindn = domaindn
1742     result.paths = paths
1743     result.lp = lp
1744     result.samdb = samdb
1745     return result
1746
1747
1748 def provision_become_dc(setup_dir=None, smbconf=None, targetdir=None,
1749         realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
1750         serverdn=None, domain=None, hostname=None, domainsid=None,
1751         adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
1752         policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
1753         root=None, nobody=None, users=None, wheel=None, backup=None,
1754         serverrole=None, ldap_backend=None, ldap_backend_type=None,
1755         sitename=None, debuglevel=1):
1756
1757     logger = logging.getLogger("provision")
1758     samba.set_debug_level(debuglevel)
1759
1760     res = provision(setup_dir, logger, system_session(), None,
1761         smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1762         realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1763         configdn=configdn, serverdn=serverdn, domain=domain,
1764         hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1765         machinepass=machinepass, serverrole="domain controller",
1766         sitename=sitename)
1767     res.lp.set("debuglevel", str(debuglevel))
1768     return res
1769
1770
1771 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1772     """Create a PHP LDAP admin configuration file.
1773
1774     :param path: Path to write the configuration to.
1775     :param setup_path: Function to generate setup paths.
1776     """
1777     setup_file(setup_path("phpldapadmin-config.php"), path,
1778             {"S4_LDAPI_URI": ldapi_uri})
1779
1780
1781 def create_zone_file(lp, logger, paths, targetdir, setup_path, dnsdomain,
1782                      hostip, hostip6, hostname, realm, domainguid,
1783                      ntdsguid):
1784     """Write out a DNS zone file, from the info in the current database.
1785
1786     :param paths: paths object
1787     :param setup_path: Setup path function.
1788     :param dnsdomain: DNS Domain name
1789     :param domaindn: DN of the Domain
1790     :param hostip: Local IPv4 IP
1791     :param hostip6: Local IPv6 IP
1792     :param hostname: Local hostname
1793     :param realm: Realm name
1794     :param domainguid: GUID of the domain.
1795     :param ntdsguid: GUID of the hosts nTDSDSA record.
1796     """
1797     assert isinstance(domainguid, str)
1798
1799     if hostip6 is not None:
1800         hostip6_base_line = "            IN AAAA    " + hostip6
1801         hostip6_host_line = hostname + "        IN AAAA    " + hostip6
1802         gc_msdcs_ip6_line = "gc._msdcs               IN AAAA    " + hostip6
1803     else:
1804         hostip6_base_line = ""
1805         hostip6_host_line = ""
1806         gc_msdcs_ip6_line = ""
1807
1808     if hostip is not None:
1809         hostip_base_line = "            IN A    " + hostip
1810         hostip_host_line = hostname + "        IN A    " + hostip
1811         gc_msdcs_ip_line = "gc._msdcs               IN A    " + hostip
1812     else:
1813         hostip_base_line = ""
1814         hostip_host_line = ""
1815         gc_msdcs_ip_line = ""
1816
1817     dns_dir = os.path.dirname(paths.dns)
1818
1819     try:
1820         shutil.rmtree(dns_dir, True)
1821     except OSError:
1822         pass
1823
1824     os.mkdir(dns_dir, 0775)
1825
1826     # we need to freeze the zone while we update the contents
1827     if targetdir is None:
1828         rndc = ' '.join(lp.get("rndc command"))
1829         os.system(rndc + " freeze " + lp.get("realm"))
1830
1831     setup_file(setup_path("provision.zone"), paths.dns, {
1832             "HOSTNAME": hostname,
1833             "DNSDOMAIN": dnsdomain,
1834             "REALM": realm,
1835             "HOSTIP_BASE_LINE": hostip_base_line,
1836             "HOSTIP_HOST_LINE": hostip_host_line,
1837             "DOMAINGUID": domainguid,
1838             "DATESTRING": time.strftime("%Y%m%d%H"),
1839             "DEFAULTSITE": DEFAULTSITE,
1840             "NTDSGUID": ntdsguid,
1841             "HOSTIP6_BASE_LINE": hostip6_base_line,
1842             "HOSTIP6_HOST_LINE": hostip6_host_line,
1843             "GC_MSDCS_IP_LINE": gc_msdcs_ip_line,
1844             "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line,
1845         })
1846
1847     # note that we use no variable substitution on this file
1848     # the substitution is done at runtime by samba_dnsupdate
1849     setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1850
1851     # and the SPN update list
1852     setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1853
1854     if paths.bind_gid is not None:
1855         try:
1856             os.chown(dns_dir, -1, paths.bind_gid)
1857             os.chown(paths.dns, -1, paths.bind_gid)
1858             # chmod needed to cope with umask
1859             os.chmod(dns_dir, 0775)
1860             os.chmod(paths.dns, 0664)
1861         except OSError:
1862             if not os.environ.has_key('SAMBA_SELFTEST'):
1863                 logger.error("Failed to chown %s to bind gid %u" % (
1864                     dns_dir, paths.bind_gid))
1865
1866     if targetdir is None:
1867         os.system(rndc + " unfreeze " + lp.get("realm"))
1868
1869
1870 def create_dns_update_list(lp, logger, paths, setup_path):
1871     """Write out a dns_update_list file"""
1872     # note that we use no variable substitution on this file
1873     # the substitution is done at runtime by samba_dnsupdate
1874     setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1875     setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1876
1877
1878 def create_named_conf(paths, setup_path, realm, dnsdomain,
1879                       private_dir):
1880     """Write out a file containing zone statements suitable for inclusion in a
1881     named.conf file (including GSS-TSIG configuration).
1882
1883     :param paths: all paths
1884     :param setup_path: Setup path function.
1885     :param realm: Realm name
1886     :param dnsdomain: DNS Domain name
1887     :param private_dir: Path to private directory
1888     :param keytab_name: File name of DNS keytab file
1889     """
1890
1891     setup_file(setup_path("named.conf"), paths.namedconf, {
1892             "DNSDOMAIN": dnsdomain,
1893             "REALM": realm,
1894             "ZONE_FILE": paths.dns,
1895             "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1896             "NAMED_CONF": paths.namedconf,
1897             "NAMED_CONF_UPDATE": paths.namedconf_update
1898             })
1899
1900     setup_file(setup_path("named.conf.update"), paths.namedconf_update)
1901
1902
1903 def create_named_txt(path, setup_path, realm, dnsdomain, private_dir,
1904     keytab_name):
1905     """Write out a file containing zone statements suitable for inclusion in a
1906     named.conf file (including GSS-TSIG configuration).
1907
1908     :param path: Path of the new named.conf file.
1909     :param setup_path: Setup path function.
1910     :param realm: Realm name
1911     :param dnsdomain: DNS Domain name
1912     :param private_dir: Path to private directory
1913     :param keytab_name: File name of DNS keytab file
1914     """
1915     setup_file(setup_path("named.txt"), path, {
1916             "DNSDOMAIN": dnsdomain,
1917             "REALM": realm,
1918             "DNS_KEYTAB": keytab_name,
1919             "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1920             "PRIVATE_DIR": private_dir
1921         })
1922
1923
1924 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1925     """Write out a file containing zone statements suitable for inclusion in a
1926     named.conf file (including GSS-TSIG configuration).
1927
1928     :param path: Path of the new named.conf file.
1929     :param setup_path: Setup path function.
1930     :param dnsdomain: DNS Domain name
1931     :param hostname: Local hostname
1932     :param realm: Realm name
1933     """
1934     setup_file(setup_path("krb5.conf"), path, {
1935             "DNSDOMAIN": dnsdomain,
1936             "HOSTNAME": hostname,
1937             "REALM": realm,
1938         })
1939
1940
1941 class ProvisioningError(Exception):
1942     """A generic provision error."""
1943
1944     def __init__(self, value):
1945         self.value = value
1946
1947     def __str__(self):
1948         return "ProvisioningError: " + self.value
1949
1950
1951 class InvalidNetbiosName(Exception):
1952     """A specified name was not a valid NetBIOS name."""
1953     def __init__(self, name):
1954         super(InvalidNetbiosName, self).__init__(
1955             "The name '%r' is not a valid NetBIOS name" % name)