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