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