s4:provision.py - make more use of "names.serverdn" on NTDS settings location
[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     #We don't need to set msg["flatname"] here, because rdn_name will handle it, and it causes problems for modifies anyway
692     msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
693     msg["secureChannelType"] = [str(secure_channel_type)]
694     msg["objectClass"] = ["top", "primaryDomain"]
695     if realm is not None:
696       if dnsdomain is None:
697         dnsdomain = realm.lower()
698       msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
699       msg["realm"] = [realm]
700       msg["saltPrincipal"] = ["host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())]
701       msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
702       msg["privateKeytab"] = ["secrets.keytab"]
703
704
705     msg["secret"] = [machinepass]
706     msg["samAccountName"] = ["%s$" % netbiosname]
707     msg["secureChannelType"] = [str(secure_channel_type)]
708     if domainsid is not None:
709         msg["objectSid"] = [ndr_pack(domainsid)]
710     
711     # This complex expression tries to ensure that we don't have more
712     # than one record for this SID, realm or netbios domain at a time,
713     # but we don't delete the old record that we are about to modify,
714     # because that would delete the keytab and previous password.
715     res = secretsdb.search(base="cn=Primary Domains", 
716                            attrs=attrs, 
717                            expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(dn=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
718                            scope=ldb.SCOPE_ONELEVEL)
719     
720     for del_msg in res:
721         secretsdb.delete(del_msg.dn)
722
723     res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
724
725     if len(res) == 1:
726       msg["priorSecret"] = [res[0]["secret"][0]]
727       msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
728
729       try:
730         msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
731       except KeyError:
732         pass
733
734       try:
735         msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
736       except KeyError:
737         pass
738
739       for el in msg:
740           if el != 'dn':
741               msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
742       secretsdb.modify(msg)
743       secretsdb.rename(res[0].dn, msg.dn)
744     else:
745       secretsdb.add(msg)
746
747
748 def secretsdb_setup_dns(secretsdb, setup_path, private_dir,
749                         realm, dnsdomain,
750                         dns_keytab_path, dnspass):
751     """Add DNS specific bits to a secrets database.
752     
753     :param secretsdb: Ldb Handle to the secrets database
754     :param setup_path: Setup path function
755     :param machinepass: Machine password
756     """
757     try:
758         os.unlink(os.path.join(private_dir, dns_keytab_path))
759     except OSError:
760         pass
761
762     setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), { 
763             "REALM": realm,
764             "DNSDOMAIN": dnsdomain,
765             "DNS_KEYTAB": dns_keytab_path,
766             "DNSPASS_B64": b64encode(dnspass),
767             })
768
769
770 def setup_secretsdb(path, setup_path, session_info, backend_credentials, lp):
771     """Setup the secrets database.
772
773    :note: This function does not handle exceptions and transaction on purpose,
774    it's up to the caller to do this job.
775
776     :param path: Path to the secrets database.
777     :param setup_path: Get the path to a setup file.
778     :param session_info: Session info.
779     :param credentials: Credentials
780     :param lp: Loadparm context
781     :return: LDB handle for the created secrets database
782     """
783     if os.path.exists(path):
784         os.unlink(path)
785     secrets_ldb = Ldb(path, session_info=session_info, 
786                       lp=lp)
787     secrets_ldb.erase()
788     secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
789     secrets_ldb = Ldb(path, session_info=session_info, 
790                       lp=lp)
791     secrets_ldb.transaction_start()
792     try:
793         secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
794
795         if backend_credentials is not None and backend_credentials.authentication_requested():
796             if backend_credentials.get_bind_dn() is not None:
797                 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
798                         "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
799                         "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
800                         })
801             else:
802                 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
803                         "LDAPADMINUSER": backend_credentials.get_username(),
804                         "LDAPADMINREALM": backend_credentials.get_realm(),
805                         "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
806                         })
807
808         return secrets_ldb
809     except:
810         secrets_ldb.transaction_cancel()
811         raise
812
813 def setup_privileges(path, setup_path, session_info, lp):
814     """Setup the privileges database.
815
816     :param path: Path to the privileges database.
817     :param setup_path: Get the path to a setup file.
818     :param session_info: Session info.
819     :param credentials: Credentials
820     :param lp: Loadparm context
821     :return: LDB handle for the created secrets database
822     """
823     if os.path.exists(path):
824         os.unlink(path)
825     privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
826     privilege_ldb.erase()
827     privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
828
829
830 def setup_registry(path, setup_path, session_info, lp):
831     """Setup the registry.
832     
833     :param path: Path to the registry database
834     :param setup_path: Function that returns the path to a setup.
835     :param session_info: Session information
836     :param credentials: Credentials
837     :param lp: Loadparm context
838     """
839     reg = samba.registry.Registry()
840     hive = samba.registry.open_ldb(path, session_info=session_info, 
841                          lp_ctx=lp)
842     reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
843     provision_reg = setup_path("provision.reg")
844     assert os.path.exists(provision_reg)
845     reg.diff_apply(provision_reg)
846
847
848 def setup_idmapdb(path, setup_path, session_info, lp):
849     """Setup the idmap database.
850
851     :param path: path to the idmap database
852     :param setup_path: Function that returns a path to a setup file
853     :param session_info: Session information
854     :param credentials: Credentials
855     :param lp: Loadparm context
856     """
857     if os.path.exists(path):
858         os.unlink(path)
859
860     idmap_ldb = IDmapDB(path, session_info=session_info,
861                         lp=lp)
862
863     idmap_ldb.erase()
864     idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
865     return idmap_ldb
866
867
868 def setup_samdb_rootdse(samdb, setup_path, names):
869     """Setup the SamDB rootdse.
870
871     :param samdb: Sam Database handle
872     :param setup_path: Obtain setup path
873     """
874     setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
875         "SCHEMADN": names.schemadn, 
876         "NETBIOSNAME": names.netbiosname,
877         "DNSDOMAIN": names.dnsdomain,
878         "REALM": names.realm,
879         "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
880         "DOMAINDN": names.domaindn,
881         "ROOTDN": names.rootdn,
882         "CONFIGDN": names.configdn,
883         "SERVERDN": names.serverdn,
884         })
885         
886
887 def setup_self_join(samdb, names,
888                     machinepass, dnspass, 
889                     domainsid, next_rid, invocationid, setup_path,
890                     policyguid, policyguid_dc, domainControllerFunctionality,
891                     ntdsguid):
892     """Join a host to its own domain."""
893     assert isinstance(invocationid, str)
894     if ntdsguid is not None:
895         ntdsguid_line = "objectGUID: %s\n"%ntdsguid
896     else:
897         ntdsguid_line = ""
898     setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), { 
899               "CONFIGDN": names.configdn, 
900               "SCHEMADN": names.schemadn,
901               "DOMAINDN": names.domaindn,
902               "SERVERDN": names.serverdn,
903               "INVOCATIONID": invocationid,
904               "NETBIOSNAME": names.netbiosname,
905               "DEFAULTSITE": names.sitename,
906               "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
907               "MACHINEPASS_B64": b64encode(machinepass),
908               "REALM": names.realm,
909               "DOMAIN": names.domain,
910               "DOMAINSID": str(domainsid),
911               "DCRID": str(next_rid),
912               "DNSDOMAIN": names.dnsdomain,
913               "SAMBA_VERSION_STRING": version,
914               "NTDSGUID": ntdsguid_line,
915               "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
916
917     setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), { 
918               "POLICYGUID": policyguid,
919               "POLICYGUID_DC": policyguid_dc,
920               "DNSDOMAIN": names.dnsdomain,
921               "DOMAINSID": str(domainsid),
922               "DOMAINDN": names.domaindn})
923     
924     # add the NTDSGUID based SPNs
925     ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
926     names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
927                                      expression="", scope=ldb.SCOPE_BASE)
928     assert isinstance(names.ntdsguid, str)
929
930     # Setup fSMORoleOwner entries to point at the newly created DC entry
931     setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
932               "DOMAIN": names.domain,
933               "DNSDOMAIN": names.dnsdomain,
934               "DOMAINDN": names.domaindn,
935               "CONFIGDN": names.configdn,
936               "SCHEMADN": names.schemadn, 
937               "DEFAULTSITE": names.sitename,
938               "SERVERDN": names.serverdn,
939               "NETBIOSNAME": names.netbiosname,
940               "NTDSGUID": names.ntdsguid,
941               "RIDALLOCATIONSTART": str(next_rid + 100),
942               "RIDALLOCATIONEND": str(next_rid + 100 + 499),
943               })
944
945     # This is partially Samba4 specific and should be replaced by the correct
946     # DNS AD-style setup
947     setup_add_ldif(samdb, setup_path("provision_dns_add.ldif"), {
948               "DNSDOMAIN": names.dnsdomain,
949               "DOMAINDN": names.domaindn,
950               "DNSPASS_B64": b64encode(dnspass),
951               })
952
953 def getpolicypath(sysvolpath, dnsdomain, guid):
954     """Return the physical path of policy given its guid.
955
956     :param sysvolpath: Path to the sysvol folder
957     :param dnsdomain: DNS name of the AD domain
958     :param guid: The GUID of the policy
959     :return: A string with the complete path to the policy folder
960     """
961
962     if guid[0] != "{":
963         guid = "{%s}" % guid
964     policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
965     return policy_path
966
967 def create_gpo_struct(policy_path):
968     if not os.path.exists(policy_path):
969         os.makedirs(policy_path, 0755)
970     open(os.path.join(policy_path, "GPT.INI"), 'w').write(
971                       "[General]\r\nVersion=65543")
972     p = os.path.join(policy_path, "MACHINE")
973     if not os.path.exists(p):
974         os.makedirs(p, 0755)
975     p = os.path.join(policy_path, "USER")
976     if not os.path.exists(p):
977         os.makedirs(p, 0755)
978
979
980 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
981     """Create the default GPO for a domain
982
983         :param sysvolpath: Physical path for the sysvol folder
984         :param dnsdomain: DNS domain name of the AD domain
985         :param policyguid: GUID of the default domain policy
986         :param policyguid_dc: GUID of the default domain controler policy
987     """
988
989     policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
990     create_gpo_struct(policy_path)
991
992     policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
993     create_gpo_struct(policy_path)
994
995
996 def setup_samdb(path, setup_path, session_info, provision_backend, lp, names,
997         logger, domainsid, domainguid, policyguid, policyguid_dc, fill,
998         adminpass, krbtgtpass, machinepass, invocationid, dnspass, ntdsguid,
999         serverrole, am_rodc=False, dom_for_fun_level=None, schema=None,
1000         next_rid=1000):
1001     """Setup a complete SAM Database.
1002     
1003     :note: This will wipe the main SAM database file!
1004     """
1005
1006
1007     # Provision does not make much sense values larger than 1000000000
1008     # as the upper range of the rIDAvailablePool is 1073741823 and
1009     # we don't want to create a domain that cannot allocate rids.
1010     if next_rid < 1000 or next_rid > 1000000000:
1011         error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1012         error += "the valid range is %u-%u. The default is %u." % (1000, 1000000000, 1000)
1013         raise ProvisioningError(error)
1014
1015     # ATTENTION: Do NOT change these default values without discussion with the
1016     # team and/or release manager. They have a big impact on the whole program!
1017     domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1018
1019     if dom_for_fun_level is None:
1020         dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1021
1022     if dom_for_fun_level > domainControllerFunctionality:
1023         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!")
1024
1025     domainFunctionality = dom_for_fun_level
1026     forestFunctionality = dom_for_fun_level
1027
1028     # Also wipes the database
1029     setup_samdb_partitions(path, setup_path, logger=logger, lp=lp,
1030         provision_backend=provision_backend, session_info=session_info,
1031         names=names, serverrole=serverrole, schema=schema)
1032
1033     if schema is None:
1034         schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
1035
1036     # Load the database, but don's load the global schema and don't connect quite yet
1037     samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1038                   credentials=provision_backend.credentials, lp=lp, global_schema=False,
1039                   am_rodc=am_rodc)
1040
1041     logger.info("Pre-loading the Samba 4 and AD schema")
1042
1043     # Load the schema from the one we computed earlier
1044     samdb.set_schema(schema)
1045
1046     # And now we can connect to the DB - the schema won't be loaded from the DB
1047     samdb.connect(path)
1048
1049     if fill == FILL_DRS:
1050         return samdb
1051         
1052     samdb.transaction_start()
1053     try:
1054         # Set the domain functionality levels onto the database.
1055         # Various module (the password_hash module in particular) need
1056         # to know what level of AD we are emulating.
1057
1058         # These will be fixed into the database via the database
1059         # modifictions below, but we need them set from the start.
1060         samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1061         samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1062         samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
1063
1064         samdb.set_domain_sid(str(domainsid))
1065         samdb.set_invocation_id(invocationid)
1066         samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1067
1068         logger.info("Adding DomainDN: %s" % names.domaindn)
1069
1070 #impersonate domain admin
1071         admin_session_info = admin_session(lp, str(domainsid))
1072         samdb.set_session_info(admin_session_info)
1073         if domainguid is not None:
1074             domainguid_line = "objectGUID: %s\n-" % domainguid
1075         else:
1076             domainguid_line = ""
1077
1078         descr = b64encode(get_domain_descriptor(domainsid))
1079         setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1080                 "DOMAINDN": names.domaindn,
1081                 "DOMAINGUID": domainguid_line,
1082                 "DESCRIPTOR": descr
1083                 })
1084
1085
1086         setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1087             "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1088             "DOMAINSID": str(domainsid),
1089             "NEXTRID": str(next_rid),
1090             "SCHEMADN": names.schemadn, 
1091             "NETBIOSNAME": names.netbiosname,
1092             "DEFAULTSITE": names.sitename,
1093             "CONFIGDN": names.configdn,
1094             "SERVERDN": names.serverdn,
1095             "POLICYGUID": policyguid,
1096             "DOMAINDN": names.domaindn,
1097             "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1098             "SAMBA_VERSION_STRING": version
1099             })
1100
1101         logger.info("Adding configuration container")
1102         descr = b64encode(get_config_descriptor(domainsid))
1103         setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1104             "CONFIGDN": names.configdn, 
1105             "DESCRIPTOR": descr,
1106             })
1107
1108         # The LDIF here was created when the Schema object was constructed
1109         logger.info("Setting up sam.ldb schema")
1110         samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1111         samdb.modify_ldif(schema.schema_dn_modify)
1112         samdb.write_prefixes_from_schema()
1113         samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1114         setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"), 
1115                        {"SCHEMADN": names.schemadn})
1116
1117         logger.info("Reopening sam.ldb with new schema")
1118     except:
1119         samdb.transaction_cancel()
1120         raise
1121     else:
1122         samdb.transaction_commit()
1123
1124     samdb = SamDB(session_info=admin_session_info,
1125                 credentials=provision_backend.credentials, lp=lp,
1126                 global_schema=False, am_rodc=am_rodc)
1127     samdb.connect(path)
1128     samdb.transaction_start()
1129     try:
1130         samdb.invocation_id = invocationid
1131
1132         logger.info("Setting up sam.ldb configuration data")
1133         setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1134             "CONFIGDN": names.configdn,
1135             "NETBIOSNAME": names.netbiosname,
1136             "DEFAULTSITE": names.sitename,
1137             "DNSDOMAIN": names.dnsdomain,
1138             "DOMAIN": names.domain,
1139             "SCHEMADN": names.schemadn,
1140             "DOMAINDN": names.domaindn,
1141             "SERVERDN": names.serverdn,
1142             "FOREST_FUNCTIONALITY": str(forestFunctionality),
1143             "DOMAIN_FUNCTIONALITY": str(domainFunctionality)
1144             })
1145
1146         logger.info("Setting up display specifiers")
1147         display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1148         display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
1149         check_all_substituted(display_specifiers_ldif)
1150         samdb.add_ldif(display_specifiers_ldif)
1151
1152         logger.info("Adding users container")
1153         setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1154                 "DOMAINDN": names.domaindn})
1155         logger.info("Modifying users container")
1156         setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1157                 "DOMAINDN": names.domaindn})
1158         logger.info("Adding computers container")
1159         setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1160                 "DOMAINDN": names.domaindn})
1161         logger.info("Modifying computers container")
1162         setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1163                 "DOMAINDN": names.domaindn})
1164         logger.info("Setting up sam.ldb data")
1165         setup_add_ldif(samdb, setup_path("provision.ldif"), {
1166             "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1167             "DOMAINDN": names.domaindn,
1168             "NETBIOSNAME": names.netbiosname,
1169             "DEFAULTSITE": names.sitename,
1170             "CONFIGDN": names.configdn,
1171             "SERVERDN": names.serverdn,
1172             "RIDAVAILABLESTART": str(next_rid + 600),
1173             "POLICYGUID_DC": policyguid_dc
1174             })
1175
1176         setup_modify_ldif(samdb, setup_path("provision_basedn_references.ldif"), {
1177                 "DOMAINDN": names.domaindn})
1178
1179         setup_modify_ldif(samdb, setup_path("provision_configuration_references.ldif"), {
1180                 "CONFIGDN": names.configdn,
1181                 "SCHEMADN": names.schemadn})
1182         if fill == FILL_FULL:
1183             logger.info("Setting up sam.ldb users and groups")
1184             setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1185                 "DOMAINDN": names.domaindn,
1186                 "DOMAINSID": str(domainsid),
1187                 "CONFIGDN": names.configdn,
1188                 "ADMINPASS_B64": b64encode(adminpass),
1189                 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1190                 })
1191
1192             logger.info("Setting up self join")
1193             setup_self_join(samdb, names=names, invocationid=invocationid,
1194                             dnspass=dnspass,
1195                             machinepass=machinepass,
1196                             domainsid=domainsid,
1197                             next_rid=next_rid,
1198                             policyguid=policyguid,
1199                             policyguid_dc=policyguid_dc,
1200                             setup_path=setup_path,
1201                             domainControllerFunctionality=domainControllerFunctionality,
1202                             ntdsguid=ntdsguid)
1203
1204             ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1205             names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1206                 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1207             assert isinstance(names.ntdsguid, str)
1208     except:
1209         samdb.transaction_cancel()
1210         raise
1211     else:
1212         samdb.transaction_commit()
1213         return samdb
1214
1215
1216 FILL_FULL = "FULL"
1217 FILL_NT4SYNC = "NT4SYNC"
1218 FILL_DRS = "DRS"
1219 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1220 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)"
1221
1222 def set_dir_acl(path, acl, lp, domsid):
1223     setntacl(lp, path, acl, domsid)
1224     for root, dirs, files in os.walk(path, topdown=False):
1225         for name in files:
1226             setntacl(lp, os.path.join(root, name), acl, domsid)
1227         for name in dirs:
1228             setntacl(lp, os.path.join(root, name), acl, domsid)
1229
1230
1231 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1232     """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1233     folders beneath.
1234
1235     :param sysvol: Physical path for the sysvol folder
1236     :param dnsdomain: The DNS name of the domain
1237     :param domainsid: The SID of the domain
1238     :param domaindn: The DN of the domain (ie. DC=...)
1239     :param samdb: An LDB object on the SAM db
1240     :param lp: an LP object
1241     """
1242
1243     # Set ACL for GPO root folder
1244     root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1245     setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid))
1246
1247     res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1248                         attrs=["cn", "nTSecurityDescriptor"],
1249                         expression="", scope=ldb.SCOPE_ONELEVEL)
1250
1251     for policy in res:
1252         acl = ndr_unpack(security.descriptor, 
1253                          str(policy["nTSecurityDescriptor"])).as_sddl()
1254         policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1255         set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp, 
1256                     str(domainsid))
1257
1258 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1259     lp):
1260     """Set the ACL for the sysvol share and the subfolders
1261
1262     :param samdb: An LDB object on the SAM db
1263     :param netlogon: Physical path for the netlogon folder
1264     :param sysvol: Physical path for the sysvol folder
1265     :param gid: The GID of the "Domain adminstrators" group
1266     :param domainsid: The SID of the domain
1267     :param dnsdomain: The DNS name of the domain
1268     :param domaindn: The DN of the domain (ie. DC=...)
1269     """
1270
1271     try:
1272         os.chown(sysvol,-1,gid)
1273     except:
1274         canchown = False
1275     else:
1276         canchown = True
1277
1278     # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1279     setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid))
1280     for root, dirs, files in os.walk(sysvol, topdown=False):
1281         for name in files:
1282             if canchown:
1283                 os.chown(os.path.join(root, name), -1, gid)
1284             setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1285         for name in dirs:
1286             if canchown:
1287                 os.chown(os.path.join(root, name), -1, gid)
1288             setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1289
1290     # Set acls on Policy folder and policies folders
1291     set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp)
1292
1293
1294 def provision(setup_dir, logger, session_info, 
1295               credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1296               realm=None, 
1297               rootdn=None, domaindn=None, schemadn=None, configdn=None, 
1298               serverdn=None,
1299               domain=None, hostname=None, hostip=None, hostip6=None, 
1300               domainsid=None, next_rid=1000,
1301               adminpass=None, ldapadminpass=None,
1302               krbtgtpass=None, domainguid=None, 
1303               policyguid=None, policyguid_dc=None, invocationid=None,
1304               machinepass=None, ntdsguid=None,
1305               dnspass=None, root=None, nobody=None, users=None, 
1306               wheel=None, backup=None, aci=None, serverrole=None,
1307               dom_for_fun_level=None,
1308               ldap_backend_extra_port=None, backend_type=None,
1309               sitename=None,
1310               ol_mmr_urls=None, ol_olc=None, 
1311               setup_ds_path=None, slapd_path=None, nosync=False,
1312               ldap_dryrun_mode=False, useeadb=False, am_rodc=False):
1313     """Provision samba4
1314     
1315     :note: caution, this wipes all existing data!
1316     """
1317
1318     def setup_path(file):
1319       return os.path.join(setup_dir, file)
1320
1321     if domainsid is None:
1322       domainsid = security.random_sid()
1323     else:
1324       domainsid = security.dom_sid(domainsid)
1325
1326     # create/adapt the group policy GUIDs
1327     # Default GUID for default policy are described at
1328     # "How Core Group Policy Works"
1329     # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1330     if policyguid is None:
1331         policyguid = DEFAULT_POLICY_GUID
1332     policyguid = policyguid.upper()
1333     if policyguid_dc is None:
1334         policyguid_dc = DEFAULT_DC_POLICY_GUID
1335     policyguid_dc = policyguid_dc.upper()
1336
1337     if adminpass is None:
1338         adminpass = samba.generate_random_password(12, 32)
1339     if krbtgtpass is None:
1340         krbtgtpass = samba.generate_random_password(128, 255)
1341     if machinepass is None:
1342         machinepass  = samba.generate_random_password(128, 255)
1343     if dnspass is None:
1344         dnspass = samba.generate_random_password(128, 255)
1345     if ldapadminpass is None:
1346         #Make a new, random password between Samba and it's LDAP server
1347         ldapadminpass=samba.generate_random_password(128, 255)
1348
1349     if backend_type is None:
1350         backend_type = "ldb"
1351
1352     sid_generator = "internal"
1353     if backend_type == "fedora-ds":
1354         sid_generator = "backend"
1355
1356     root_uid = findnss_uid([root or "root"])
1357     nobody_uid = findnss_uid([nobody or "nobody"])
1358     users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1359     if wheel is None:
1360         wheel_gid = findnss_gid(["wheel", "adm"])
1361     else:
1362         wheel_gid = findnss_gid([wheel])
1363     try:
1364         bind_gid = findnss_gid(["bind", "named"])
1365     except KeyError:
1366         bind_gid = None
1367
1368     if targetdir is not None:
1369         smbconf = os.path.join(targetdir, "etc", "smb.conf")
1370     elif smbconf is None:
1371         smbconf = samba.param.default_path()
1372     if not os.path.exists(os.path.dirname(smbconf)):
1373         os.makedirs(os.path.dirname(smbconf))
1374
1375     # only install a new smb.conf if there isn't one there already
1376     if os.path.exists(smbconf):
1377         # if Samba Team members can't figure out the weird errors
1378         # loading an empty smb.conf gives, then we need to be smarter.
1379         # Pretend it just didn't exist --abartlet
1380         data = open(smbconf, 'r').read()
1381         data = data.lstrip()
1382         if data is None or data == "":
1383             make_smbconf(smbconf, setup_path, hostname, domain, realm,
1384                          serverrole, targetdir, sid_generator, useeadb)
1385     else: 
1386         make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, 
1387                      targetdir, sid_generator, useeadb)
1388
1389     lp = samba.param.LoadParm()
1390     lp.load(smbconf)
1391
1392     names = guess_names(lp=lp, hostname=hostname, domain=domain,
1393                         dnsdomain=realm, serverrole=serverrole,
1394                         domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1395                         serverdn=serverdn, sitename=sitename)
1396
1397     paths = provision_paths_from_lp(lp, names.dnsdomain)
1398
1399     paths.bind_gid = bind_gid
1400
1401     if hostip is None:
1402         hostips = samba.interface_ips(lp, False)
1403         if len(hostips) == 0:
1404             logger.warning("No external IPv4 address has been found. Using loopback.")
1405             hostip = '127.0.0.1'
1406         else:
1407             hostip = hostips[0]
1408             if len(hostips) > 1:
1409                 logger.warning("More than one IPv4 address found. Using %s.", hostip)
1410
1411     if hostip6 is None:
1412         try:
1413             for ip in socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP):
1414                 if hostip6 is None:
1415                     hostip6 = ip[-1][0]
1416                 if hostip6 == '::1' and ip[-1][0] != '::1':
1417                     hostip6 = ip[-1][0]
1418         except socket.gaierror, (socket.EAI_NODATA, msg): 
1419             hostip6 = None
1420
1421     if serverrole is None:
1422         serverrole = lp.get("server role")
1423
1424     assert serverrole in ("domain controller", "member server", "standalone")
1425     if invocationid is None:
1426         invocationid = str(uuid.uuid4())
1427
1428     if not os.path.exists(paths.private_dir):
1429         os.mkdir(paths.private_dir)
1430     if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1431         os.mkdir(os.path.join(paths.private_dir, "tls"))
1432
1433     ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1434  
1435     schema = Schema(setup_path, domainsid, invocationid=invocationid, schemadn=names.schemadn,
1436                     serverdn=names.serverdn)
1437
1438     if backend_type == "ldb":
1439         provision_backend = LDBBackend(backend_type,
1440                                          paths=paths, setup_path=setup_path,
1441                                          lp=lp, credentials=credentials, 
1442                                          names=names,
1443                                          logger=logger)
1444     elif backend_type == "existing":
1445         provision_backend = ExistingBackend(backend_type,
1446                                          paths=paths, setup_path=setup_path,
1447                                          lp=lp, credentials=credentials, 
1448                                          names=names,
1449                                          logger=logger,
1450                                          ldapi_url=ldapi_url)
1451     elif backend_type == "fedora-ds":
1452         provision_backend = FDSBackend(backend_type,
1453                                          paths=paths, setup_path=setup_path,
1454                                          lp=lp, credentials=credentials, 
1455                                          names=names,
1456                                          logger=logger,
1457                                          domainsid=domainsid,
1458                                          schema=schema,
1459                                          hostname=hostname,
1460                                          ldapadminpass=ldapadminpass,
1461                                          slapd_path=slapd_path,
1462                                          ldap_backend_extra_port=ldap_backend_extra_port,
1463                                          ldap_dryrun_mode=ldap_dryrun_mode,
1464                                          root=root,
1465                                          setup_ds_path=setup_ds_path)
1466     elif backend_type == "openldap":
1467         provision_backend = OpenLDAPBackend(backend_type,
1468                                          paths=paths, setup_path=setup_path,
1469                                          lp=lp, credentials=credentials, 
1470                                          names=names,
1471                                          logger=logger,
1472                                          domainsid=domainsid,
1473                                          schema=schema,
1474                                          hostname=hostname,
1475                                          ldapadminpass=ldapadminpass,
1476                                          slapd_path=slapd_path,
1477                                          ldap_backend_extra_port=ldap_backend_extra_port,
1478                                          ldap_dryrun_mode=ldap_dryrun_mode,
1479                                          ol_mmr_urls=ol_mmr_urls, 
1480                                          nosync=nosync)
1481     else:
1482         raise ValueError("Unknown LDAP backend type selected")
1483
1484     provision_backend.init()
1485     provision_backend.start()
1486
1487     # only install a new shares config db if there is none
1488     if not os.path.exists(paths.shareconf):
1489         logger.info("Setting up share.ldb")
1490         share_ldb = Ldb(paths.shareconf, session_info=session_info, 
1491                         lp=lp)
1492         share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1493
1494     logger.info("Setting up secrets.ldb")
1495     secrets_ldb = setup_secretsdb(paths.secrets, setup_path, 
1496         session_info=session_info,
1497         backend_credentials=provision_backend.secrets_credentials, lp=lp)
1498
1499     try:
1500         logger.info("Setting up the registry")
1501         setup_registry(paths.hklm, setup_path, session_info, 
1502                        lp=lp)
1503
1504         logger.info("Setting up the privileges database")
1505         setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1506
1507         logger.info("Setting up idmap db")
1508         idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1509                               lp=lp)
1510
1511         logger.info("Setting up SAM db")
1512         samdb = setup_samdb(paths.samdb, setup_path, session_info, 
1513                             provision_backend, lp, names,
1514                             logger=logger, 
1515                             domainsid=domainsid, 
1516                             schema=schema, domainguid=domainguid,
1517                             policyguid=policyguid, policyguid_dc=policyguid_dc,
1518                             fill=samdb_fill, 
1519                             adminpass=adminpass, krbtgtpass=krbtgtpass,
1520                             invocationid=invocationid, 
1521                             machinepass=machinepass, dnspass=dnspass, 
1522                             ntdsguid=ntdsguid, serverrole=serverrole,
1523                             dom_for_fun_level=dom_for_fun_level,
1524                             am_rodc=am_rodc, next_rid=next_rid)
1525
1526         if serverrole == "domain controller":
1527             if paths.netlogon is None:
1528                 logger.info("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1529                 logger.info("Please either remove %s or see the template at %s" % 
1530                         (paths.smbconf, setup_path("provision.smb.conf.dc")))
1531                 assert paths.netlogon is not None
1532
1533             if paths.sysvol is None:
1534                 logger.info("Existing smb.conf does not have a [sysvol] share, but you"
1535                         " are configuring a DC.")
1536                 logger.info("Please either remove %s or see the template at %s" % 
1537                         (paths.smbconf, setup_path("provision.smb.conf.dc")))
1538                 assert paths.sysvol is not None
1539
1540             if not os.path.isdir(paths.netlogon):
1541                 os.makedirs(paths.netlogon, 0755)
1542
1543         if samdb_fill == FILL_FULL:
1544             setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1545                                 root_uid=root_uid, nobody_uid=nobody_uid,
1546                                 users_gid=users_gid, wheel_gid=wheel_gid)
1547
1548             if serverrole == "domain controller":
1549                 # Set up group policies (domain policy and domain controller policy)
1550                 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid, policyguid_dc)
1551                 setsysvolacl(samdb, paths.netlogon, paths.sysvol, wheel_gid, 
1552                              domainsid, names.dnsdomain, names.domaindn, lp)
1553
1554             logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1555             setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1556
1557             secretsdb_self_join(secrets_ldb, domain=names.domain,
1558                                 realm=names.realm,
1559                                 dnsdomain=names.dnsdomain,
1560                                 netbiosname=names.netbiosname,
1561                                 domainsid=domainsid, 
1562                                 machinepass=machinepass,
1563                                 secure_channel_type=SEC_CHAN_BDC)
1564
1565             # Now set up the right msDS-SupportedEncryptionTypes into the DB
1566             # In future, this might be determined from some configuration
1567             kerberos_enctypes = str(ENC_ALL_TYPES)
1568
1569             try:
1570                 msg = ldb.Message(ldb.Dn(samdb, samdb.searchone("distinguishedName", expression="samAccountName=%s$" % names.netbiosname, scope=ldb.SCOPE_SUBTREE)))
1571                 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(elements=kerberos_enctypes, 
1572                                                                           flags=ldb.FLAG_MOD_REPLACE, 
1573                                                                           name="msDS-SupportedEncryptionTypes")
1574                 samdb.modify(msg)
1575             except ldb.LdbError, (ldb.ERR_NO_SUCH_ATTRIBUTE, _):
1576                 # It might be that this attribute does not exist in this schema
1577                 pass
1578
1579
1580             if serverrole == "domain controller":
1581                 secretsdb_setup_dns(secrets_ldb, setup_path,
1582                                     paths.private_dir,
1583                                     realm=names.realm, dnsdomain=names.dnsdomain,
1584                                     dns_keytab_path=paths.dns_keytab,
1585                                     dnspass=dnspass)
1586
1587                 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1588                 assert isinstance(domainguid, str)
1589
1590                 # Only make a zone file on the first DC, it should be replicated
1591                 # with DNS replication
1592                 create_zone_file(lp, logger, paths, targetdir, setup_path,
1593                     dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
1594                     hostname=names.hostname, realm=names.realm, 
1595                     domainguid=domainguid, ntdsguid=names.ntdsguid)
1596
1597                 create_named_conf(paths, setup_path, realm=names.realm,
1598                                   dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1599
1600                 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1601                                   dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1602                                   keytab_name=paths.dns_keytab)
1603                 logger.info("See %s for an example configuration include file for BIND", paths.namedconf)
1604                 logger.info("and %s for further documentation required for secure DNS "
1605                         "updates", paths.namedtxt)
1606
1607                 create_krb5_conf(paths.krb5conf, setup_path,
1608                                  dnsdomain=names.dnsdomain, hostname=names.hostname,
1609                                  realm=names.realm)
1610                 logger.info("A Kerberos configuration suitable for Samba 4 has been "
1611                         "generated at %s", paths.krb5conf)
1612
1613             lastProvisionUSNs = get_last_provision_usn(samdb)
1614             maxUSN = get_max_usn(samdb, str(names.rootdn))
1615             if lastProvisionUSNs is not None:
1616                 update_provision_usn(samdb, 0, maxUSN, 1)
1617             else:
1618                 set_provision_usn(samdb, 0, maxUSN)
1619
1620         if serverrole == "domain controller":
1621             create_dns_update_list(lp, logger, paths, setup_path)
1622
1623         provision_backend.post_setup()
1624         provision_backend.shutdown()
1625
1626         create_phpldapadmin_config(paths.phpldapadminconfig, setup_path, 
1627                                    ldapi_url)
1628     except:
1629         secrets_ldb.transaction_cancel()
1630         raise
1631
1632     #Now commit the secrets.ldb to disk
1633     secrets_ldb.transaction_commit()
1634
1635     # the commit creates the dns.keytab, now chown it
1636     dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1637     if (os.path.isfile(dns_keytab_path) and paths.bind_gid is not None):
1638         try:
1639             os.chmod(dns_keytab_path, 0640)
1640             os.chown(dns_keytab_path, -1, paths.bind_gid)
1641         except OSError:
1642             logger.info("Failed to chown %s to bind gid %u", dns_keytab_path,
1643                 paths.bind_gid)
1644
1645
1646     logger.info("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php",
1647             paths.phpldapadminconfig)
1648
1649     logger.info("Once the above files are installed, your Samba4 server will be ready to use")
1650     logger.info("Server Role:           %s" % serverrole)
1651     logger.info("Hostname:              %s" % names.hostname)
1652     logger.info("NetBIOS Domain:        %s" % names.domain)
1653     logger.info("DNS Domain:            %s" % names.dnsdomain)
1654     logger.info("DOMAIN SID:            %s" % str(domainsid))
1655     if samdb_fill == FILL_FULL:
1656         logger.info("Admin password:        %s" % adminpass)
1657     if provision_backend.type is not "ldb":
1658         if provision_backend.credentials.get_bind_dn() is not None:
1659             logger.info("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1660         else:
1661             logger.info("LDAP Admin User:       %s" % provision_backend.credentials.get_username())
1662
1663         logger.info("LDAP Admin Password:   %s" % provision_backend.credentials.get_password())
1664
1665         if provision_backend.slapd_command_escaped is not None:
1666             # now display slapd_command_file.txt to show how slapd must be started next time
1667             logger.info("Use later the following commandline to start slapd, then Samba:")
1668             logger.info(provision_backend.slapd_command_escaped)
1669             logger.info("This slapd-Commandline is also stored under: %s/ldap_backend_startup.sh", 
1670                     provision_backend.ldapdir)
1671
1672     result = ProvisionResult()
1673     result.domaindn = domaindn
1674     result.paths = paths
1675     result.lp = lp
1676     result.samdb = samdb
1677     return result
1678
1679
1680 def provision_become_dc(setup_dir=None,
1681                         smbconf=None, targetdir=None, realm=None, 
1682                         rootdn=None, domaindn=None, schemadn=None,
1683                         configdn=None, serverdn=None,
1684                         domain=None, hostname=None, domainsid=None, 
1685                         adminpass=None, krbtgtpass=None, domainguid=None, 
1686                         policyguid=None, policyguid_dc=None, invocationid=None,
1687                         machinepass=None, 
1688                         dnspass=None, root=None, nobody=None, users=None, 
1689                         wheel=None, backup=None, serverrole=None, 
1690                         ldap_backend=None, ldap_backend_type=None,
1691                         sitename=None, debuglevel=1):
1692
1693     logger = logging.getLogger("provision")
1694     samba.set_debug_level(debuglevel)
1695
1696     return provision(setup_dir, logger, system_session(), None,
1697               smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1698               realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1699               configdn=configdn, serverdn=serverdn, domain=domain,
1700               hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1701               machinepass=machinepass, serverrole="domain controller",
1702               sitename=sitename)
1703
1704
1705 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1706     """Create a PHP LDAP admin configuration file.
1707
1708     :param path: Path to write the configuration to.
1709     :param setup_path: Function to generate setup paths.
1710     """
1711     setup_file(setup_path("phpldapadmin-config.php"), path, 
1712             {"S4_LDAPI_URI": ldapi_uri})
1713
1714
1715 def create_zone_file(lp, logger, paths, targetdir, setup_path, dnsdomain,
1716                      hostip, hostip6, hostname, realm, domainguid,
1717                      ntdsguid):
1718     """Write out a DNS zone file, from the info in the current database.
1719
1720     :param paths: paths object
1721     :param setup_path: Setup path function.
1722     :param dnsdomain: DNS Domain name
1723     :param domaindn: DN of the Domain
1724     :param hostip: Local IPv4 IP
1725     :param hostip6: Local IPv6 IP
1726     :param hostname: Local hostname
1727     :param realm: Realm name
1728     :param domainguid: GUID of the domain.
1729     :param ntdsguid: GUID of the hosts nTDSDSA record.
1730     """
1731     assert isinstance(domainguid, str)
1732
1733     if hostip6 is not None:
1734         hostip6_base_line = "            IN AAAA    " + hostip6
1735         hostip6_host_line = hostname + "        IN AAAA    " + hostip6
1736         gc_msdcs_ip6_line = "gc._msdcs               IN AAAA    " + hostip6
1737     else:
1738         hostip6_base_line = ""
1739         hostip6_host_line = ""
1740         gc_msdcs_ip6_line = ""
1741
1742     if hostip is not None:
1743         hostip_base_line = "            IN A    " + hostip
1744         hostip_host_line = hostname + "        IN A    " + hostip
1745         gc_msdcs_ip_line = "gc._msdcs               IN A    " + hostip
1746     else:
1747         hostip_base_line = ""
1748         hostip_host_line = ""
1749         gc_msdcs_ip_line = ""
1750
1751     dns_dir = os.path.dirname(paths.dns)
1752
1753     try:
1754         shutil.rmtree(dns_dir, True)
1755     except OSError:
1756         pass
1757
1758     os.mkdir(dns_dir, 0775)
1759
1760     # we need to freeze the zone while we update the contents
1761     if targetdir is None:
1762         rndc = ' '.join(lp.get("rndc command"))
1763         os.system(rndc + " freeze " + lp.get("realm"))
1764
1765     setup_file(setup_path("provision.zone"), paths.dns, {
1766             "HOSTNAME": hostname,
1767             "DNSDOMAIN": dnsdomain,
1768             "REALM": realm,
1769             "HOSTIP_BASE_LINE": hostip_base_line,
1770             "HOSTIP_HOST_LINE": hostip_host_line,
1771             "DOMAINGUID": domainguid,
1772             "DATESTRING": time.strftime("%Y%m%d%H"),
1773             "DEFAULTSITE": DEFAULTSITE,
1774             "NTDSGUID": ntdsguid,
1775             "HOSTIP6_BASE_LINE": hostip6_base_line,
1776             "HOSTIP6_HOST_LINE": hostip6_host_line,
1777             "GC_MSDCS_IP_LINE": gc_msdcs_ip_line,
1778             "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line,
1779         })
1780
1781     # note that we use no variable substitution on this file
1782     # the substitution is done at runtime by samba_dnsupdate
1783     setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1784
1785     # and the SPN update list
1786     setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1787
1788     if paths.bind_gid is not None:
1789         try:
1790             os.chown(dns_dir, -1, paths.bind_gid)
1791             os.chown(paths.dns, -1, paths.bind_gid)
1792             # chmod needed to cope with umask
1793             os.chmod(dns_dir, 0775)
1794             os.chmod(paths.dns, 0664)
1795         except OSError:
1796             logger.error("Failed to chown %s to bind gid %u" % (dns_dir, paths.bind_gid))
1797
1798     if targetdir is None:
1799         os.system(rndc + " unfreeze " + lp.get("realm"))
1800
1801
1802 def create_dns_update_list(lp, logger, paths, setup_path):
1803     """Write out a dns_update_list file"""
1804     # note that we use no variable substitution on this file
1805     # the substitution is done at runtime by samba_dnsupdate
1806     setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1807     setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1808
1809
1810 def create_named_conf(paths, setup_path, realm, dnsdomain,
1811                       private_dir):
1812     """Write out a file containing zone statements suitable for inclusion in a
1813     named.conf file (including GSS-TSIG configuration).
1814     
1815     :param paths: all paths
1816     :param setup_path: Setup path function.
1817     :param realm: Realm name
1818     :param dnsdomain: DNS Domain name
1819     :param private_dir: Path to private directory
1820     :param keytab_name: File name of DNS keytab file
1821     """
1822
1823     setup_file(setup_path("named.conf"), paths.namedconf, {
1824             "DNSDOMAIN": dnsdomain,
1825             "REALM": realm,
1826             "ZONE_FILE": paths.dns,
1827             "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1828             "NAMED_CONF": paths.namedconf,
1829             "NAMED_CONF_UPDATE": paths.namedconf_update
1830             })
1831
1832     setup_file(setup_path("named.conf.update"), paths.namedconf_update)
1833
1834
1835 def create_named_txt(path, setup_path, realm, dnsdomain,
1836                       private_dir, keytab_name):
1837     """Write out a file containing zone statements suitable for inclusion in a
1838     named.conf file (including GSS-TSIG configuration).
1839     
1840     :param path: Path of the new named.conf file.
1841     :param setup_path: Setup path function.
1842     :param realm: Realm name
1843     :param dnsdomain: DNS Domain name
1844     :param private_dir: Path to private directory
1845     :param keytab_name: File name of DNS keytab file
1846     """
1847
1848     setup_file(setup_path("named.txt"), path, {
1849             "DNSDOMAIN": dnsdomain,
1850             "REALM": realm,
1851             "DNS_KEYTAB": keytab_name,
1852             "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1853             "PRIVATE_DIR": private_dir
1854         })
1855
1856
1857 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
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 dnsdomain: DNS Domain name
1864     :param hostname: Local hostname
1865     :param realm: Realm name
1866     """
1867     setup_file(setup_path("krb5.conf"), path, {
1868             "DNSDOMAIN": dnsdomain,
1869             "HOSTNAME": hostname,
1870             "REALM": realm,
1871         })
1872
1873
1874 class ProvisioningError(Exception):
1875     """A generic provision error."""
1876
1877     def __init__(self, value):
1878         self.value = value
1879
1880     def __str__(self):
1881         return "ProvisioningError: " + self.value
1882
1883
1884 class InvalidNetbiosName(Exception):
1885     """A specified name was not a valid NetBIOS name."""
1886     def __init__(self, name):
1887         super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)