s4 provision: Rename bind9 flatfile backend to BIND9_FLATFILE
[obnox/samba/samba-obnox.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     except Exception:
999         secrets_ldb.transaction_cancel()
1000         raise
1001     return secrets_ldb
1002
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, admin_session_info, 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               "RIDALLOCATIONSTART": str(next_rid + 100),
1098               "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
1099
1100     setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1101               "POLICYGUID": policyguid,
1102               "POLICYGUID_DC": policyguid_dc,
1103               "DNSDOMAIN": names.dnsdomain,
1104               "DOMAINDN": names.domaindn})
1105
1106     # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1107     if fill == FILL_FULL:
1108         setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
1109                 "CONFIGDN": names.configdn,
1110                 "SCHEMADN": names.schemadn,
1111                 "DOMAINDN": names.domaindn,
1112                 "SERVERDN": names.serverdn,
1113                 "INVOCATIONID": invocationid,
1114                 "NETBIOSNAME": names.netbiosname,
1115                 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1116                 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1117                 "DOMAINSID": str(domainsid),
1118                 "DCRID": str(dc_rid),
1119                 "SAMBA_VERSION_STRING": version,
1120                 "NTDSGUID": ntdsguid_line,
1121                 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1122                     domainControllerFunctionality)})
1123
1124     # Setup fSMORoleOwner entries to point at the newly created DC entry
1125         setup_modify_ldif(samdb, setup_path("provision_self_join_modify_config.ldif"), {
1126                 "CONFIGDN": names.configdn,
1127                 "SCHEMADN": names.schemadn,
1128                 "DEFAULTSITE": names.sitename,
1129                 "NETBIOSNAME": names.netbiosname,
1130                 "SERVERDN": names.serverdn,
1131                 })
1132
1133     system_session_info = system_session()
1134     samdb.set_session_info(system_session_info)
1135     # Setup fSMORoleOwner entries to point at the newly created DC entry
1136
1137     # to modify a serverReference under cn=config when we are a subdomain, we must
1138     # be system due to ACLs
1139     setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1140               "DOMAINDN": names.domaindn,
1141               "SERVERDN": names.serverdn,
1142               "NETBIOSNAME": names.netbiosname,
1143               })
1144
1145     samdb.set_session_info(admin_session_info)
1146
1147     # This is Samba4 specific and should be replaced by the correct
1148     # DNS AD-style setup
1149     setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1150               "DNSDOMAIN": names.dnsdomain,
1151               "DOMAINDN": names.domaindn,
1152               "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1153               "HOSTNAME" : names.hostname,
1154               "DNSNAME" : '%s.%s' % (
1155                   names.netbiosname.lower(), names.dnsdomain.lower())
1156               })
1157
1158
1159 def getpolicypath(sysvolpath, dnsdomain, guid):
1160     """Return the physical path of policy given its guid.
1161
1162     :param sysvolpath: Path to the sysvol folder
1163     :param dnsdomain: DNS name of the AD domain
1164     :param guid: The GUID of the policy
1165     :return: A string with the complete path to the policy folder
1166     """
1167
1168     if guid[0] != "{":
1169         guid = "{%s}" % guid
1170     policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1171     return policy_path
1172
1173
1174 def create_gpo_struct(policy_path):
1175     if not os.path.exists(policy_path):
1176         os.makedirs(policy_path, 0775)
1177     open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1178                       "[General]\r\nVersion=0")
1179     p = os.path.join(policy_path, "MACHINE")
1180     if not os.path.exists(p):
1181         os.makedirs(p, 0775)
1182     p = os.path.join(policy_path, "USER")
1183     if not os.path.exists(p):
1184         os.makedirs(p, 0775)
1185
1186
1187 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1188     """Create the default GPO for a domain
1189
1190     :param sysvolpath: Physical path for the sysvol folder
1191     :param dnsdomain: DNS domain name of the AD domain
1192     :param policyguid: GUID of the default domain policy
1193     :param policyguid_dc: GUID of the default domain controler policy
1194     """
1195     policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1196     create_gpo_struct(policy_path)
1197
1198     policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1199     create_gpo_struct(policy_path)
1200
1201
1202 def setup_samdb(path, session_info, provision_backend, lp, names,
1203         logger, fill, serverrole,
1204         am_rodc=False, schema=None):
1205     """Setup a complete SAM Database.
1206
1207     :note: This will wipe the main SAM database file!
1208     """
1209
1210     # Also wipes the database
1211     setup_samdb_partitions(path, logger=logger, lp=lp,
1212         provision_backend=provision_backend, session_info=session_info,
1213         names=names, serverrole=serverrole, schema=schema)
1214
1215     if schema is None:
1216         schema = Schema(domainsid, schemadn=names.schemadn)
1217
1218     # Load the database, but don's load the global schema and don't connect
1219     # quite yet
1220     samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1221                   credentials=provision_backend.credentials, lp=lp,
1222                   global_schema=False, am_rodc=am_rodc)
1223
1224     logger.info("Pre-loading the Samba 4 and AD schema")
1225
1226     # Load the schema from the one we computed earlier
1227     samdb.set_schema(schema)
1228
1229     # Set the NTDS settings DN manually - in order to have it already around
1230     # before the provisioned tree exists and we connect
1231     samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1232
1233     # And now we can connect to the DB - the schema won't be loaded from the
1234     # DB
1235     samdb.connect(path)
1236
1237     return samdb
1238
1239 def fill_samdb(samdb, lp, names,
1240         logger, domainsid, domainguid, policyguid, policyguid_dc, fill,
1241         adminpass, krbtgtpass, machinepass, invocationid, dnspass, ntdsguid,
1242         serverrole, am_rodc=False, dom_for_fun_level=None, schema=None,
1243         next_rid=None, dc_rid=None):
1244
1245     if next_rid is None:
1246         next_rid = 1000
1247
1248     # Provision does not make much sense values larger than 1000000000
1249     # as the upper range of the rIDAvailablePool is 1073741823 and
1250     # we don't want to create a domain that cannot allocate rids.
1251     if next_rid < 1000 or next_rid > 1000000000:
1252         error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1253         error += "the valid range is %u-%u. The default is %u." % (
1254             1000, 1000000000, 1000)
1255         raise ProvisioningError(error)
1256
1257     # ATTENTION: Do NOT change these default values without discussion with the
1258     # team and/or release manager. They have a big impact on the whole program!
1259     domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1260
1261     if dom_for_fun_level is None:
1262         dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1263
1264     if dom_for_fun_level > domainControllerFunctionality:
1265         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!")
1266
1267     domainFunctionality = dom_for_fun_level
1268     forestFunctionality = dom_for_fun_level
1269
1270     # Set the NTDS settings DN manually - in order to have it already around
1271     # before the provisioned tree exists and we connect
1272     samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1273
1274     samdb.transaction_start()
1275     try:
1276         # Set the domain functionality levels onto the database.
1277         # Various module (the password_hash module in particular) need
1278         # to know what level of AD we are emulating.
1279
1280         # These will be fixed into the database via the database
1281         # modifictions below, but we need them set from the start.
1282         samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1283         samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1284         samdb.set_opaque_integer("domainControllerFunctionality",
1285             domainControllerFunctionality)
1286
1287         samdb.set_domain_sid(str(domainsid))
1288         samdb.set_invocation_id(invocationid)
1289
1290         logger.info("Adding DomainDN: %s" % names.domaindn)
1291
1292         # impersonate domain admin
1293         admin_session_info = admin_session(lp, str(domainsid))
1294         samdb.set_session_info(admin_session_info)
1295         if domainguid is not None:
1296             domainguid_line = "objectGUID: %s\n-" % domainguid
1297         else:
1298             domainguid_line = ""
1299
1300         descr = b64encode(get_domain_descriptor(domainsid))
1301         setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1302                 "DOMAINDN": names.domaindn,
1303                 "DOMAINSID": str(domainsid),
1304                 "DESCRIPTOR": descr,
1305                 "DOMAINGUID": domainguid_line
1306                 })
1307
1308         setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1309             "DOMAINDN": names.domaindn,
1310             "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1311             "NEXTRID": str(next_rid),
1312             "DEFAULTSITE": names.sitename,
1313             "CONFIGDN": names.configdn,
1314             "POLICYGUID": policyguid,
1315             "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1316             "SAMBA_VERSION_STRING": version
1317             })
1318
1319         # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1320         if fill == FILL_FULL:
1321             logger.info("Adding configuration container")
1322             descr = b64encode(get_config_descriptor(domainsid))
1323             setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1324                     "CONFIGDN": names.configdn,
1325                     "DESCRIPTOR": descr,
1326                     })
1327
1328             # The LDIF here was created when the Schema object was constructed
1329             logger.info("Setting up sam.ldb schema")
1330             samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1331             samdb.modify_ldif(schema.schema_dn_modify)
1332             samdb.write_prefixes_from_schema()
1333             samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1334             setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1335                            {"SCHEMADN": names.schemadn})
1336
1337         # Now register this container in the root of the forest
1338         msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1339         msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1340                     "subRefs")
1341
1342     except Exception:
1343         samdb.transaction_cancel()
1344         raise
1345     else:
1346         samdb.transaction_commit()
1347
1348     samdb.transaction_start()
1349     try:
1350         samdb.invocation_id = invocationid
1351
1352         # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1353         if fill == FILL_FULL:
1354             logger.info("Setting up sam.ldb configuration data")
1355             setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1356                     "CONFIGDN": names.configdn,
1357                     "NETBIOSNAME": names.netbiosname,
1358                     "DEFAULTSITE": names.sitename,
1359                     "DNSDOMAIN": names.dnsdomain,
1360                     "DOMAIN": names.domain,
1361                     "SCHEMADN": names.schemadn,
1362                     "DOMAINDN": names.domaindn,
1363                     "SERVERDN": names.serverdn,
1364                     "FOREST_FUNCTIONALITY": str(forestFunctionality),
1365                     "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1366                     })
1367
1368             logger.info("Setting up display specifiers")
1369             display_specifiers_ldif = read_ms_ldif(
1370                 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1371             display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1372                                                      {"CONFIGDN": names.configdn})
1373             check_all_substituted(display_specifiers_ldif)
1374             samdb.add_ldif(display_specifiers_ldif)
1375
1376         logger.info("Adding users container")
1377         setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1378                 "DOMAINDN": names.domaindn})
1379         logger.info("Modifying users container")
1380         setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1381                 "DOMAINDN": names.domaindn})
1382         logger.info("Adding computers container")
1383         setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1384                 "DOMAINDN": names.domaindn})
1385         logger.info("Modifying computers container")
1386         setup_modify_ldif(samdb,
1387             setup_path("provision_computers_modify.ldif"), {
1388                 "DOMAINDN": names.domaindn})
1389         logger.info("Setting up sam.ldb data")
1390         setup_add_ldif(samdb, setup_path("provision.ldif"), {
1391             "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1392             "DOMAINDN": names.domaindn,
1393             "NETBIOSNAME": names.netbiosname,
1394             "DEFAULTSITE": names.sitename,
1395             "CONFIGDN": names.configdn,
1396             "SERVERDN": names.serverdn,
1397             "RIDAVAILABLESTART": str(next_rid + 600),
1398             "POLICYGUID_DC": policyguid_dc
1399             })
1400
1401         # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1402         if fill == FILL_FULL:
1403             setup_modify_ldif(samdb,
1404                               setup_path("provision_configuration_references.ldif"), {
1405                     "CONFIGDN": names.configdn,
1406                     "SCHEMADN": names.schemadn})
1407
1408             logger.info("Setting up well known security principals")
1409             setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1410                 "CONFIGDN": names.configdn,
1411                 })
1412
1413         if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1414             setup_modify_ldif(samdb,
1415                               setup_path("provision_basedn_references.ldif"),
1416                               {"DOMAINDN": names.domaindn})
1417
1418             logger.info("Setting up sam.ldb users and groups")
1419             setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1420                 "DOMAINDN": names.domaindn,
1421                 "DOMAINSID": str(domainsid),
1422                 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1423                 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1424                 })
1425
1426             logger.info("Setting up self join")
1427             setup_self_join(samdb, admin_session_info, names=names, fill=fill, invocationid=invocationid,
1428                             dnspass=dnspass,
1429                             machinepass=machinepass,
1430                             domainsid=domainsid,
1431                             next_rid=next_rid,
1432                             dc_rid=dc_rid,
1433                             policyguid=policyguid,
1434                             policyguid_dc=policyguid_dc,
1435                             domainControllerFunctionality=domainControllerFunctionality,
1436                             ntdsguid=ntdsguid)
1437
1438             ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1439             names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1440                 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1441             assert isinstance(names.ntdsguid, str)
1442     except Exception:
1443         samdb.transaction_cancel()
1444         raise
1445     else:
1446         samdb.transaction_commit()
1447         return samdb
1448
1449
1450 FILL_FULL = "FULL"
1451 FILL_SUBDOMAIN = "SUBDOMAIN"
1452 FILL_NT4SYNC = "NT4SYNC"
1453 FILL_DRS = "DRS"
1454 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1455 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)"
1456
1457
1458 def set_dir_acl(path, acl, lp, domsid):
1459     setntacl(lp, path, acl, domsid)
1460     for root, dirs, files in os.walk(path, topdown=False):
1461         for name in files:
1462             setntacl(lp, os.path.join(root, name), acl, domsid)
1463         for name in dirs:
1464             setntacl(lp, os.path.join(root, name), acl, domsid)
1465
1466
1467 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1468     """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1469     folders beneath.
1470
1471     :param sysvol: Physical path for the sysvol folder
1472     :param dnsdomain: The DNS name of the domain
1473     :param domainsid: The SID of the domain
1474     :param domaindn: The DN of the domain (ie. DC=...)
1475     :param samdb: An LDB object on the SAM db
1476     :param lp: an LP object
1477     """
1478
1479     # Set ACL for GPO root folder
1480     root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1481     setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid))
1482
1483     res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1484                         attrs=["cn", "nTSecurityDescriptor"],
1485                         expression="", scope=ldb.SCOPE_ONELEVEL)
1486
1487     for policy in res:
1488         acl = ndr_unpack(security.descriptor,
1489                          str(policy["nTSecurityDescriptor"])).as_sddl()
1490         policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1491         set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1492                     str(domainsid))
1493
1494
1495 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1496     lp):
1497     """Set the ACL for the sysvol share and the subfolders
1498
1499     :param samdb: An LDB object on the SAM db
1500     :param netlogon: Physical path for the netlogon folder
1501     :param sysvol: Physical path for the sysvol folder
1502     :param gid: The GID of the "Domain adminstrators" group
1503     :param domainsid: The SID of the domain
1504     :param dnsdomain: The DNS name of the domain
1505     :param domaindn: The DN of the domain (ie. DC=...)
1506     """
1507
1508     try:
1509         os.chown(sysvol, -1, gid)
1510     except OSError:
1511         canchown = False
1512     else:
1513         canchown = True
1514
1515     # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1516     setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid))
1517     for root, dirs, files in os.walk(sysvol, topdown=False):
1518         for name in files:
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         for name in dirs:
1523             if canchown:
1524                 os.chown(os.path.join(root, name), -1, gid)
1525             setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1526
1527     # Set acls on Policy folder and policies folders
1528     set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp)
1529
1530
1531 def interface_ips_v4(lp):
1532     '''return only IPv4 IPs'''
1533     ips = samba.interface_ips(lp, False)
1534     ret = []
1535     for i in ips:
1536         if i.find(':') == -1:
1537             ret.append(i)
1538     return ret
1539
1540 def interface_ips_v6(lp, linklocal=False):
1541     '''return only IPv6 IPs'''
1542     ips = samba.interface_ips(lp, False)
1543     ret = []
1544     for i in ips:
1545         if i.find(':') != -1 and (linklocal or i.find('%') == -1):
1546             ret.append(i)
1547     return ret
1548
1549
1550 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1551                    domainsid, schema=None,
1552                    targetdir=None, samdb_fill=FILL_FULL,
1553                    hostip=None, hostip6=None,
1554                    next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1555                    domainguid=None, policyguid=None, policyguid_dc=None,
1556                    invocationid=None, machinepass=None, ntdsguid=None,
1557                    dns_backend=None, dnspass=None,
1558                    serverrole=None, dom_for_fun_level=None,
1559                    am_rodc=False, lp=None):
1560     # create/adapt the group policy GUIDs
1561     # Default GUID for default policy are described at
1562     # "How Core Group Policy Works"
1563     # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1564     if policyguid is None:
1565         policyguid = DEFAULT_POLICY_GUID
1566     policyguid = policyguid.upper()
1567     if policyguid_dc is None:
1568         policyguid_dc = DEFAULT_DC_POLICY_GUID
1569     policyguid_dc = policyguid_dc.upper()
1570
1571     if invocationid is None:
1572         invocationid = str(uuid.uuid4())
1573
1574     if adminpass is None:
1575         adminpass = samba.generate_random_password(12, 32)
1576     if krbtgtpass is None:
1577         krbtgtpass = samba.generate_random_password(128, 255)
1578     if machinepass is None:
1579         machinepass  = samba.generate_random_password(128, 255)
1580     if dnspass is None:
1581         dnspass = samba.generate_random_password(128, 255)
1582
1583     samdb = fill_samdb(samdb, lp, names, logger=logger,
1584                        domainsid=domainsid, schema=schema, domainguid=domainguid,
1585                        policyguid=policyguid, policyguid_dc=policyguid_dc,
1586                        fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1587                        invocationid=invocationid, machinepass=machinepass,
1588                        dnspass=dnspass, ntdsguid=ntdsguid, serverrole=serverrole,
1589                        dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1590                        next_rid=next_rid, dc_rid=dc_rid)
1591
1592     if serverrole == "domain controller":
1593         # Set up group policies (domain policy and domain controller
1594         # policy)
1595         create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1596                            policyguid_dc)
1597         setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.wheel_gid,
1598                      domainsid, names.dnsdomain, names.domaindn, lp)
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         secretsdb_setup_dns(secrets_ldb, names,
1624                             paths.private_dir, realm=names.realm,
1625                             dnsdomain=names.dnsdomain,
1626                             dns_keytab_path=paths.dns_keytab, dnspass=dnspass)
1627
1628         # Default DNS backend is BIND9_FLATFILE using txt files for zone information
1629         if not dns_backend:
1630             dns_backend = "BIND9_FLATFILE"
1631
1632         setup_ad_dns(samdb, names, logger, hostip=hostip, hostip6=hostip6,
1633                      dns_backend=dns_backend, os_level=dom_for_fun_level)
1634
1635         domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1636                                      attribute="objectGUID")
1637         assert isinstance(domainguid, str)
1638
1639         create_dns_dir(logger, paths)
1640
1641         # Only make a zone file on the first DC, it should be
1642         # replicated with DNS replication
1643         if dns_backend == "BIND9_FLATFILE":
1644             create_zone_file(lp, logger, paths, targetdir,
1645                              dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
1646                              hostname=names.hostname, realm=names.realm,
1647                              domainguid=domainguid, ntdsguid=names.ntdsguid)
1648
1649         create_named_conf(paths, realm=names.realm,
1650                           dnsdomain=names.dnsdomain, dns_backend=dns_backend)
1651
1652         create_named_txt(paths.namedtxt,
1653                          realm=names.realm, dnsdomain=names.dnsdomain,
1654                          dnsname = "%s.%s" % (names.hostname, names.dnsdomain),
1655                          private_dir=paths.private_dir,
1656                          keytab_name=paths.dns_keytab)
1657         logger.info("See %s for an example configuration include file for BIND", paths.namedconf)
1658         logger.info("and %s for further documentation required for secure DNS "
1659                     "updates", paths.namedtxt)
1660
1661     lastProvisionUSNs = get_last_provision_usn(samdb)
1662     maxUSN = get_max_usn(samdb, str(names.rootdn))
1663     if lastProvisionUSNs is not None:
1664         update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1665     else:
1666         set_provision_usn(samdb, 0, maxUSN, invocationid)
1667
1668     logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1669     setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1670                       { 'NTDSGUID' : names.ntdsguid })
1671
1672     # fix any dangling GUIDs from the provision
1673     logger.info("Fixing provision GUIDs")
1674     chk = dbcheck(samdb, samdb_schema=samdb,  verbose=False, fix=True, yes=True, quiet=True)
1675     samdb.transaction_start()
1676     # a small number of GUIDs are missing because of ordering issues in the
1677     # provision code
1678     for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1679         chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1680                            scope=ldb.SCOPE_BASE, attrs=['defaultObjectCategory'])
1681     chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1682                        scope=ldb.SCOPE_ONELEVEL,
1683                        attrs=['ipsecOwnersReference',
1684                               'ipsecFilterReference',
1685                               'ipsecISAKMPReference',
1686                               'ipsecNegotiationPolicyReference',
1687                               'ipsecNFAReference'])
1688     samdb.transaction_commit()
1689
1690
1691 def provision(logger, session_info, credentials, smbconf=None,
1692         targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1693         domaindn=None, schemadn=None, configdn=None, serverdn=None,
1694         domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1695         next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None, krbtgtpass=None,
1696         domainguid=None, policyguid=None, policyguid_dc=None,
1697         dns_backend=None, dnspass=None,
1698         invocationid=None, machinepass=None, ntdsguid=None,
1699         root=None, nobody=None, users=None, wheel=None, backup=None, aci=None,
1700         serverrole=None, dom_for_fun_level=None, ldap_backend_extra_port=None,
1701         ldap_backend_forced_uri=None, backend_type=None, sitename=None,
1702         ol_mmr_urls=None, ol_olc=None, setup_ds_path=None, slapd_path=None,
1703         nosync=False, ldap_dryrun_mode=False, useeadb=False, am_rodc=False,
1704         lp=None):
1705     """Provision samba4
1706
1707     :note: caution, this wipes all existing data!
1708     """
1709
1710     if ldapadminpass is None:
1711         # Make a new, random password between Samba and it's LDAP server
1712         ldapadminpass=samba.generate_random_password(128, 255)
1713
1714     if backend_type is None:
1715         backend_type = "ldb"
1716
1717     if domainsid is None:
1718         domainsid = security.random_sid()
1719     else:
1720         domainsid = security.dom_sid(domainsid)
1721
1722     sid_generator = "internal"
1723     if backend_type == "fedora-ds":
1724         sid_generator = "backend"
1725
1726     root_uid = findnss_uid([root or "root"])
1727     nobody_uid = findnss_uid([nobody or "nobody"])
1728     users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1729     if wheel is None:
1730         wheel_gid = findnss_gid(["wheel", "adm"])
1731     else:
1732         wheel_gid = findnss_gid([wheel])
1733     try:
1734         bind_gid = findnss_gid(["bind", "named"])
1735     except KeyError:
1736         bind_gid = None
1737
1738     if targetdir is not None:
1739         smbconf = os.path.join(targetdir, "etc", "smb.conf")
1740     elif smbconf is None:
1741         smbconf = samba.param.default_path()
1742     if not os.path.exists(os.path.dirname(smbconf)):
1743         os.makedirs(os.path.dirname(smbconf))
1744
1745     # only install a new smb.conf if there isn't one there already
1746     if os.path.exists(smbconf):
1747         # if Samba Team members can't figure out the weird errors
1748         # loading an empty smb.conf gives, then we need to be smarter.
1749         # Pretend it just didn't exist --abartlet
1750         data = open(smbconf, 'r').read()
1751         data = data.lstrip()
1752         if data is None or data == "":
1753             make_smbconf(smbconf, hostname, domain, realm,
1754                          serverrole, targetdir, sid_generator, useeadb,
1755                          lp=lp)
1756     else:
1757         make_smbconf(smbconf, hostname, domain, realm, serverrole,
1758                      targetdir, sid_generator, useeadb, lp=lp)
1759
1760     if lp is None:
1761         lp = samba.param.LoadParm()
1762     lp.load(smbconf)
1763     names = guess_names(lp=lp, hostname=hostname, domain=domain,
1764         dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
1765         configdn=configdn, schemadn=schemadn, serverdn=serverdn,
1766         sitename=sitename, rootdn=rootdn)
1767     paths = provision_paths_from_lp(lp, names.dnsdomain)
1768
1769     paths.bind_gid = bind_gid
1770     paths.wheel_gid = wheel_gid
1771
1772     if hostip is None:
1773         logger.info("Looking up IPv4 addresses")
1774         hostips = interface_ips_v4(lp)
1775         if len(hostips) > 0:
1776             hostip = hostips[0]
1777             if len(hostips) > 1:
1778                 logger.warning("More than one IPv4 address found. Using %s",
1779                     hostip)
1780     if hostip == "127.0.0.1":
1781         hostip = None
1782     if hostip is None:
1783         logger.warning("No IPv4 address will be assigned")
1784
1785     if hostip6 is None:
1786         logger.info("Looking up IPv6 addresses")
1787         hostips = interface_ips_v6(lp, linklocal=False)
1788         if hostips:
1789             hostip6 = hostips[0]
1790         if len(hostips) > 1:
1791             logger.warning("More than one IPv6 address found. Using %s", hostip6)
1792     if hostip6 is None:
1793         logger.warning("No IPv6 address will be assigned")
1794
1795     names.hostip = hostip
1796     names.hostip6 = hostip6
1797
1798     if serverrole is None:
1799         serverrole = lp.get("server role")
1800
1801     assert serverrole in ("domain controller", "member server", "standalone")
1802
1803     if not os.path.exists(paths.private_dir):
1804         os.mkdir(paths.private_dir)
1805     if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1806         os.mkdir(os.path.join(paths.private_dir, "tls"))
1807
1808     ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1809
1810     schema = Schema(domainsid, invocationid=invocationid,
1811         schemadn=names.schemadn)
1812
1813     if backend_type == "ldb":
1814         provision_backend = LDBBackend(backend_type, paths=paths,
1815             lp=lp, credentials=credentials,
1816             names=names, logger=logger)
1817     elif backend_type == "existing":
1818         provision_backend = ExistingBackend(backend_type, paths=paths,
1819             lp=lp, credentials=credentials,
1820             names=names, logger=logger,
1821             ldap_backend_forced_uri=ldap_backend_forced_uri)
1822     elif backend_type == "fedora-ds":
1823         provision_backend = FDSBackend(backend_type, paths=paths,
1824             lp=lp, credentials=credentials,
1825             names=names, logger=logger, domainsid=domainsid,
1826             schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1827             slapd_path=slapd_path,
1828             ldap_backend_extra_port=ldap_backend_extra_port,
1829             ldap_dryrun_mode=ldap_dryrun_mode, root=root,
1830             setup_ds_path=setup_ds_path,
1831             ldap_backend_forced_uri=ldap_backend_forced_uri)
1832     elif backend_type == "openldap":
1833         provision_backend = OpenLDAPBackend(backend_type, paths=paths,
1834             lp=lp, credentials=credentials,
1835             names=names, logger=logger, domainsid=domainsid,
1836             schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1837             slapd_path=slapd_path,
1838             ldap_backend_extra_port=ldap_backend_extra_port,
1839             ldap_dryrun_mode=ldap_dryrun_mode, ol_mmr_urls=ol_mmr_urls,
1840             nosync=nosync,
1841             ldap_backend_forced_uri=ldap_backend_forced_uri)
1842     else:
1843         raise ValueError("Unknown LDAP backend type selected")
1844
1845     provision_backend.init()
1846     provision_backend.start()
1847
1848     # only install a new shares config db if there is none
1849     if not os.path.exists(paths.shareconf):
1850         logger.info("Setting up share.ldb")
1851         share_ldb = Ldb(paths.shareconf, session_info=session_info,
1852                         lp=lp)
1853         share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1854
1855     logger.info("Setting up secrets.ldb")
1856     secrets_ldb = setup_secretsdb(paths,
1857         session_info=session_info,
1858         backend_credentials=provision_backend.secrets_credentials, lp=lp)
1859
1860     try:
1861         logger.info("Setting up the registry")
1862         setup_registry(paths.hklm, session_info,
1863                        lp=lp)
1864
1865         logger.info("Setting up the privileges database")
1866         setup_privileges(paths.privilege, session_info, lp=lp)
1867
1868         logger.info("Setting up idmap db")
1869         idmap = setup_idmapdb(paths.idmapdb,
1870             session_info=session_info, lp=lp)
1871
1872         setup_name_mappings(idmap, sid=str(domainsid),
1873                             root_uid=root_uid, nobody_uid=nobody_uid,
1874                             users_gid=users_gid, wheel_gid=wheel_gid)
1875
1876         logger.info("Setting up SAM db")
1877         samdb = setup_samdb(paths.samdb, session_info,
1878                             provision_backend, lp, names, logger=logger,
1879                             serverrole=serverrole,
1880                             schema=schema, fill=samdb_fill, am_rodc=am_rodc)
1881
1882         if serverrole == "domain controller":
1883             if paths.netlogon is None:
1884                 logger.info("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1885                 logger.info("Please either remove %s or see the template at %s" %
1886                         (paths.smbconf, setup_path("provision.smb.conf.dc")))
1887                 assert paths.netlogon is not None
1888
1889             if paths.sysvol is None:
1890                 logger.info("Existing smb.conf does not have a [sysvol] share, but you"
1891                         " are configuring a DC.")
1892                 logger.info("Please either remove %s or see the template at %s" %
1893                         (paths.smbconf, setup_path("provision.smb.conf.dc")))
1894                 assert paths.sysvol is not None
1895
1896             if not os.path.isdir(paths.netlogon):
1897                 os.makedirs(paths.netlogon, 0755)
1898
1899         if samdb_fill == FILL_FULL:
1900             provision_fill(samdb, secrets_ldb, logger,
1901                            names, paths, schema=schema, targetdir=targetdir,
1902                            samdb_fill=samdb_fill, hostip=hostip, hostip6=hostip6, domainsid=domainsid,
1903                            next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
1904                            krbtgtpass=krbtgtpass, domainguid=domainguid,
1905                            policyguid=policyguid, policyguid_dc=policyguid_dc,
1906                            invocationid=invocationid, machinepass=machinepass,
1907                            ntdsguid=ntdsguid, dns_backend=dns_backend, dnspass=dnspass,
1908                            serverrole=serverrole, dom_for_fun_level=dom_for_fun_level,
1909                            am_rodc=am_rodc, lp=lp)
1910
1911         create_krb5_conf(paths.krb5conf,
1912                          dnsdomain=names.dnsdomain, hostname=names.hostname,
1913                          realm=names.realm)
1914         logger.info("A Kerberos configuration suitable for Samba 4 has been "
1915                     "generated at %s", paths.krb5conf)
1916
1917         if serverrole == "domain controller":
1918             create_dns_update_list(lp, logger, paths)
1919
1920         provision_backend.post_setup()
1921         provision_backend.shutdown()
1922
1923         create_phpldapadmin_config(paths.phpldapadminconfig,
1924                                    ldapi_url)
1925     except Exception:
1926         secrets_ldb.transaction_cancel()
1927         raise
1928
1929     # Now commit the secrets.ldb to disk
1930     secrets_ldb.transaction_commit()
1931
1932     # the commit creates the dns.keytab, now chown it
1933     dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1934     if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
1935         try:
1936             os.chmod(dns_keytab_path, 0640)
1937             os.chown(dns_keytab_path, -1, paths.bind_gid)
1938         except OSError:
1939             if not os.environ.has_key('SAMBA_SELFTEST'):
1940                 logger.info("Failed to chown %s to bind gid %u",
1941                             dns_keytab_path, paths.bind_gid)
1942
1943     logger.info("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php",
1944             paths.phpldapadminconfig)
1945
1946     logger.info("Once the above files are installed, your Samba4 server will be ready to use")
1947     logger.info("Server Role:           %s" % serverrole)
1948     logger.info("Hostname:              %s" % names.hostname)
1949     logger.info("NetBIOS Domain:        %s" % names.domain)
1950     logger.info("DNS Domain:            %s" % names.dnsdomain)
1951     logger.info("DOMAIN SID:            %s" % str(domainsid))
1952     if samdb_fill == FILL_FULL:
1953         logger.info("Admin password:        %s" % adminpass)
1954     if provision_backend.type is not "ldb":
1955         if provision_backend.credentials.get_bind_dn() is not None:
1956             logger.info("LDAP Backend Admin DN: %s" %
1957                 provision_backend.credentials.get_bind_dn())
1958         else:
1959             logger.info("LDAP Admin User:       %s" %
1960                 provision_backend.credentials.get_username())
1961
1962         logger.info("LDAP Admin Password:   %s" %
1963             provision_backend.credentials.get_password())
1964
1965         if provision_backend.slapd_command_escaped is not None:
1966             # now display slapd_command_file.txt to show how slapd must be
1967             # started next time
1968             logger.info("Use later the following commandline to start slapd, then Samba:")
1969             logger.info(provision_backend.slapd_command_escaped)
1970             logger.info("This slapd-Commandline is also stored under: %s/ldap_backend_startup.sh",
1971                     provision_backend.ldapdir)
1972
1973     result = ProvisionResult()
1974     result.domaindn = domaindn
1975     result.paths = paths
1976     result.names = names
1977     result.lp = lp
1978     result.samdb = samdb
1979     result.idmap = idmap
1980     return result
1981
1982
1983 def provision_become_dc(smbconf=None, targetdir=None,
1984         realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
1985         serverdn=None, domain=None, hostname=None, domainsid=None,
1986         adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
1987         policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
1988         dns_backend=None, root=None, nobody=None, users=None, wheel=None, backup=None,
1989         serverrole=None, ldap_backend=None, ldap_backend_type=None,
1990         sitename=None, debuglevel=1):
1991
1992     logger = logging.getLogger("provision")
1993     samba.set_debug_level(debuglevel)
1994
1995     res = provision(logger, system_session(), None,
1996         smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1997         realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1998         configdn=configdn, serverdn=serverdn, domain=domain,
1999         hostname=hostname, hostip=None, domainsid=domainsid,
2000         machinepass=machinepass, serverrole="domain controller",
2001         sitename=sitename, dns_backend=dns_backend, dnspass=dnspass)
2002     res.lp.set("debuglevel", str(debuglevel))
2003     return res
2004
2005
2006 def create_phpldapadmin_config(path, ldapi_uri):
2007     """Create a PHP LDAP admin configuration file.
2008
2009     :param path: Path to write the configuration to.
2010     """
2011     setup_file(setup_path("phpldapadmin-config.php"), path,
2012             {"S4_LDAPI_URI": ldapi_uri})
2013
2014
2015 def create_dns_dir(logger, paths):
2016     """Write out a DNS zone file, from the info in the current database.
2017
2018     :param logger: Logger object
2019     :param paths: paths object
2020     """
2021     dns_dir = os.path.dirname(paths.dns)
2022
2023     try:
2024         shutil.rmtree(dns_dir, True)
2025     except OSError:
2026         pass
2027
2028     os.mkdir(dns_dir, 0770)
2029
2030     if paths.bind_gid is not None:
2031         try:
2032             os.chown(dns_dir, -1, paths.bind_gid)
2033             # chmod needed to cope with umask
2034             os.chmod(dns_dir, 0770)
2035         except OSError:
2036             if not os.environ.has_key('SAMBA_SELFTEST'):
2037                 logger.error("Failed to chown %s to bind gid %u" % (
2038                     dns_dir, paths.bind_gid))
2039
2040
2041 def create_zone_file(lp, logger, paths, targetdir, dnsdomain,
2042                      hostip, hostip6, hostname, realm, domainguid,
2043                      ntdsguid):
2044     """Write out a DNS zone file, from the info in the current database.
2045
2046     :param paths: paths object
2047     :param dnsdomain: DNS Domain name
2048     :param domaindn: DN of the Domain
2049     :param hostip: Local IPv4 IP
2050     :param hostip6: Local IPv6 IP
2051     :param hostname: Local hostname
2052     :param realm: Realm name
2053     :param domainguid: GUID of the domain.
2054     :param ntdsguid: GUID of the hosts nTDSDSA record.
2055     """
2056     assert isinstance(domainguid, str)
2057
2058     if hostip6 is not None:
2059         hostip6_base_line = "            IN AAAA    " + hostip6
2060         hostip6_host_line = hostname + "        IN AAAA    " + hostip6
2061         gc_msdcs_ip6_line = "gc._msdcs               IN AAAA    " + hostip6
2062     else:
2063         hostip6_base_line = ""
2064         hostip6_host_line = ""
2065         gc_msdcs_ip6_line = ""
2066
2067     if hostip is not None:
2068         hostip_base_line = "            IN A    " + hostip
2069         hostip_host_line = hostname + "        IN A    " + hostip
2070         gc_msdcs_ip_line = "gc._msdcs               IN A    " + hostip
2071     else:
2072         hostip_base_line = ""
2073         hostip_host_line = ""
2074         gc_msdcs_ip_line = ""
2075
2076     # we need to freeze the zone while we update the contents
2077     if targetdir is None:
2078         rndc = ' '.join(lp.get("rndc command"))
2079         os.system(rndc + " freeze " + lp.get("realm"))
2080
2081     setup_file(setup_path("provision.zone"), paths.dns, {
2082             "HOSTNAME": hostname,
2083             "DNSDOMAIN": dnsdomain,
2084             "REALM": realm,
2085             "HOSTIP_BASE_LINE": hostip_base_line,
2086             "HOSTIP_HOST_LINE": hostip_host_line,
2087             "DOMAINGUID": domainguid,
2088             "DATESTRING": time.strftime("%Y%m%d%H"),
2089             "DEFAULTSITE": DEFAULTSITE,
2090             "NTDSGUID": ntdsguid,
2091             "HOSTIP6_BASE_LINE": hostip6_base_line,
2092             "HOSTIP6_HOST_LINE": hostip6_host_line,
2093             "GC_MSDCS_IP_LINE": gc_msdcs_ip_line,
2094             "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line,
2095         })
2096
2097     if paths.bind_gid is not None:
2098         try:
2099             os.chown(paths.dns, -1, paths.bind_gid)
2100             # chmod needed to cope with umask
2101             os.chmod(paths.dns, 0664)
2102         except OSError:
2103             if not os.environ.has_key('SAMBA_SELFTEST'):
2104                 logger.error("Failed to chown %s to bind gid %u" % (
2105                     paths.dns, paths.bind_gid))
2106
2107     if targetdir is None:
2108         os.system(rndc + " unfreeze " + lp.get("realm"))
2109
2110
2111 def create_dns_update_list(lp, logger, paths):
2112     """Write out a dns_update_list file"""
2113     # note that we use no variable substitution on this file
2114     # the substitution is done at runtime by samba_dnsupdate, samba_spnupdate
2115     setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
2116     setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
2117
2118
2119 def create_named_conf(paths, realm, dnsdomain, dns_backend):
2120     """Write out a file containing zone statements suitable for inclusion in a
2121     named.conf file (including GSS-TSIG configuration).
2122
2123     :param paths: all paths
2124     :param realm: Realm name
2125     :param dnsdomain: DNS Domain name
2126     :param dns_backend: DNS backend type
2127     :param keytab_name: File name of DNS keytab file
2128     """
2129
2130     if dns_backend == "BIND9_FLATFILE":
2131         setup_file(setup_path("named.conf"), paths.namedconf, {
2132                     "DNSDOMAIN": dnsdomain,
2133                     "REALM": realm,
2134                     "ZONE_FILE": paths.dns,
2135                     "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
2136                     "NAMED_CONF": paths.namedconf,
2137                     "NAMED_CONF_UPDATE": paths.namedconf_update
2138                     })
2139
2140         setup_file(setup_path("named.conf.update"), paths.namedconf_update)
2141
2142     elif dns_backend == "BIND9_DLZ":
2143         dlz_module_path = os.path.join(samba.param.modules_dir(), 
2144                                         "bind9/dlz_bind9.so")
2145         setup_file(setup_path("named.conf.dlz"), paths.namedconf, {
2146                     "NAMED_CONF": paths.namedconf,
2147                     "BIND9_DLZ_MODULE": dlz_module_path,
2148                     })
2149
2150
2151
2152 def create_named_txt(path, realm, dnsdomain, dnsname, private_dir,
2153     keytab_name):
2154     """Write out a file containing zone statements suitable for inclusion in a
2155     named.conf file (including GSS-TSIG configuration).
2156
2157     :param path: Path of the new named.conf file.
2158     :param realm: Realm name
2159     :param dnsdomain: DNS Domain name
2160     :param private_dir: Path to private directory
2161     :param keytab_name: File name of DNS keytab file
2162     """
2163     setup_file(setup_path("named.txt"), path, {
2164             "DNSDOMAIN": dnsdomain,
2165             "DNSNAME" : dnsname, 
2166             "REALM": realm,
2167             "DNS_KEYTAB": keytab_name,
2168             "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
2169             "PRIVATE_DIR": private_dir
2170         })
2171
2172
2173 def create_krb5_conf(path, dnsdomain, hostname, realm):
2174     """Write out a file containing zone statements suitable for inclusion in a
2175     named.conf file (including GSS-TSIG configuration).
2176
2177     :param path: Path of the new named.conf file.
2178     :param dnsdomain: DNS Domain name
2179     :param hostname: Local hostname
2180     :param realm: Realm name
2181     """
2182     setup_file(setup_path("krb5.conf"), path, {
2183             "DNSDOMAIN": dnsdomain,
2184             "HOSTNAME": hostname,
2185             "REALM": realm,
2186         })
2187
2188
2189 class ProvisioningError(Exception):
2190     """A generic provision error."""
2191
2192     def __init__(self, value):
2193         self.value = value
2194
2195     def __str__(self):
2196         return "ProvisioningError: " + self.value
2197
2198
2199 class InvalidNetbiosName(Exception):
2200     """A specified name was not a valid NetBIOS name."""
2201     def __init__(self, name):
2202         super(InvalidNetbiosName, self).__init__(
2203             "The name '%r' is not a valid NetBIOS name" % name)