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