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