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