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