0b39167df41e584fcad3bb3bee2f3f0cfba2489f
[nivanova/samba-autobuild/.git] / source4 / scripting / python / samba / provision / __init__.py
1
2 # Unix SMB/CIFS implementation.
3 # backend code for provisioning a Samba4 server
4
5 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2010
6 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
7 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
8 #
9 # Based on the original in EJS:
10 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
11 #
12 # This program is free software; you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
14 # the Free Software Foundation; either version 3 of the License, or
15 # (at your option) any later version.
16 #
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 # GNU General Public License for more details.
21 #
22 # You should have received a copy of the GNU General Public License
23 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
24 #
25
26 """Functions for setting up a Samba configuration."""
27
28 __docformat__ = "restructuredText"
29
30 from base64 import b64encode
31 import os
32 import re
33 import pwd
34 import grp
35 import logging
36 import time
37 import uuid
38 import socket
39 import urllib
40 import shutil
41 import string
42
43 import ldb
44
45 from samba.auth import system_session, admin_session
46 import samba
47 from samba.dsdb import DS_DOMAIN_FUNCTION_2000
48 from samba import (
49     Ldb,
50     check_all_substituted,
51     read_and_sub_file,
52     setup_file,
53     substitute_var,
54     valid_netbios_name,
55     version,
56     )
57 from samba.dcerpc import security, misc
58 from samba.dcerpc.misc import (
59     SEC_CHAN_BDC,
60     SEC_CHAN_WKSTA,
61     )
62 from samba.dsdb import (
63     DS_DOMAIN_FUNCTION_2003,
64     DS_DOMAIN_FUNCTION_2008_R2,
65     ENC_ALL_TYPES,
66     )
67 from samba.idmap import IDmapDB
68 from samba.ms_display_specifiers import read_ms_ldif
69 from samba.ntacls import setntacl, dsacl2fsacl
70 from samba.ndr import ndr_pack, ndr_unpack
71 from samba.provision.backend import (
72     ExistingBackend,
73     FDSBackend,
74     LDBBackend,
75     OpenLDAPBackend,
76     )
77 from samba.provision.sambadns import setup_ad_dns
78
79 import samba.param
80 import samba.registry
81 from samba.schema import Schema
82 from samba.samdb import SamDB
83 from samba.dbchecker import dbcheck
84
85
86 VALID_NETBIOS_CHARS = " !#$%&'()-.@^_{}~"
87 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
88 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04fB984F9"
89 DEFAULTSITE = "Default-First-Site-Name"
90 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
91
92
93 def setup_path(file):
94     """Return an absolute path to the provision tempate file specified by file"""
95     return os.path.join(samba.param.setup_dir(), file)
96
97 # Descriptors of naming contexts and other important objects
98
99 # "get_schema_descriptor" is located in "schema.py"
100
101 def get_config_descriptor(domain_sid):
102     sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
103            "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
104            "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
105            "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
106            "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
107            "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
108            "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
109            "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
110            "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
111            "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
112            "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
113            "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
114            "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
115            "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
116            "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
117     sec = security.descriptor.from_sddl(sddl, domain_sid)
118     return ndr_pack(sec)
119
120
121 def get_domain_descriptor(domain_sid):
122     sddl= "O:BAG:BAD:AI(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
123         "(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
124     "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
125     "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
126     "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
127     "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
128     "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
129     "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
130     "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
131     "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
132     "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
133     "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)" \
134     "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)" \
135     "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)" \
136     "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)" \
137     "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
138     "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
139     "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
140     "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
141     "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
142     "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
143     "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;IF)" \
144     "(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)" \
145     "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)" \
146     "(OA;CIIO;RPLCLORC;;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
147     "(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)" \
148     "(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
149     "(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)" \
150     "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
151     "(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)" \
152     "(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)" \
153     "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
154     "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
155     "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
156     "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
157     "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)" \
158     "(OA;CIIO;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \
159     "(A;;RPWPCRCCLCLORCWOWDSW;;;DA)" \
160     "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
161     "(A;;RPRC;;;RU)" \
162     "(A;CI;LC;;;RU)" \
163     "(A;CI;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
164     "(A;;RP;;;WD)" \
165     "(A;;RPLCLORC;;;ED)" \
166     "(A;;RPLCLORC;;;AU)" \
167     "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
168     "S:AI(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
169     "(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
170     "(AU;SA;CR;;;DU)(AU;SA;CR;;;BA)(AU;SA;WPWOWD;;;WD)"
171     sec = security.descriptor.from_sddl(sddl, domain_sid)
172     return ndr_pack(sec)
173
174
175 class ProvisionPaths(object):
176
177     def __init__(self):
178         self.shareconf = None
179         self.hklm = None
180         self.hkcu = None
181         self.hkcr = None
182         self.hku = None
183         self.hkpd = None
184         self.hkpt = None
185         self.samdb = None
186         self.idmapdb = None
187         self.secrets = None
188         self.keytab = None
189         self.dns_keytab = None
190         self.dns = None
191         self.winsdb = None
192         self.private_dir = None
193
194
195 class ProvisionNames(object):
196
197     def __init__(self):
198         self.rootdn = None
199         self.domaindn = None
200         self.configdn = None
201         self.schemadn = None
202         self.ldapmanagerdn = None
203         self.dnsdomain = None
204         self.realm = None
205         self.netbiosname = None
206         self.domain = None
207         self.hostname = None
208         self.sitename = None
209         self.smbconf = None
210
211 def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf, lp):
212     """Get key provision parameters (realm, domain, ...) from a given provision
213
214     :param samdb: An LDB object connected to the sam.ldb file
215     :param secretsdb: An LDB object connected to the secrets.ldb file
216     :param idmapdb: An LDB object connected to the idmap.ldb file
217     :param paths: A list of path to provision object
218     :param smbconf: Path to the smb.conf file
219     :param lp: A LoadParm object
220     :return: A list of key provision parameters
221     """
222     names = ProvisionNames()
223     names.adminpass = None
224
225     # NT domain, kerberos realm, root dn, domain dn, domain dns name
226     names.domain = string.upper(lp.get("workgroup"))
227     names.realm = lp.get("realm")
228     names.dnsdomain = names.realm.lower()
229     basedn = samba.dn_from_dns_name(names.dnsdomain)
230     names.realm = string.upper(names.realm)
231     # netbiosname
232     # Get the netbiosname first (could be obtained from smb.conf in theory)
233     res = secretsdb.search(expression="(flatname=%s)" %
234                             names.domain,base="CN=Primary Domains",
235                             scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"])
236     names.netbiosname = str(res[0]["sAMAccountName"]).replace("$","")
237
238     names.smbconf = smbconf
239
240     # That's a bit simplistic but it's ok as long as we have only 3
241     # partitions
242     current = samdb.search(expression="(objectClass=*)",
243         base="", scope=ldb.SCOPE_BASE,
244         attrs=["defaultNamingContext", "schemaNamingContext",
245                "configurationNamingContext","rootDomainNamingContext"])
246
247     names.configdn = current[0]["configurationNamingContext"]
248     configdn = str(names.configdn)
249     names.schemadn = current[0]["schemaNamingContext"]
250     if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb,
251                                        current[0]["defaultNamingContext"][0]))):
252         raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
253                                  "is not the same ..." % (paths.samdb,
254                                     str(current[0]["defaultNamingContext"][0]),
255                                     paths.smbconf, basedn)))
256
257     names.domaindn=current[0]["defaultNamingContext"]
258     names.rootdn=current[0]["rootDomainNamingContext"]
259     # default site name
260     res3 = samdb.search(expression="(objectClass=site)",
261         base="CN=Sites," + configdn, scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
262     names.sitename = str(res3[0]["cn"])
263
264     # dns hostname and server dn
265     res4 = samdb.search(expression="(CN=%s)" % names.netbiosname,
266                             base="OU=Domain Controllers,%s" % basedn,
267                             scope=ldb.SCOPE_ONELEVEL, attrs=["dNSHostName"])
268     names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain,"")
269
270     server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
271                                 attrs=[], base=configdn)
272     names.serverdn = server_res[0].dn
273
274     # invocation id/objectguid
275     res5 = samdb.search(expression="(objectClass=*)",
276             base="CN=NTDS Settings,%s" % str(names.serverdn), scope=ldb.SCOPE_BASE,
277             attrs=["invocationID", "objectGUID"])
278     names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0]))
279     names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0]))
280
281     # domain guid/sid
282     res6 = samdb.search(expression="(objectClass=*)", base=basedn,
283             scope=ldb.SCOPE_BASE, attrs=["objectGUID",
284                 "objectSid","msDS-Behavior-Version" ])
285     names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
286     names.domainsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
287     if res6[0].get("msDS-Behavior-Version") is None or \
288         int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
289         names.domainlevel = DS_DOMAIN_FUNCTION_2000
290     else:
291         names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
292
293     # policy guid
294     res7 = samdb.search(expression="(displayName=Default Domain Policy)",
295                         base="CN=Policies,CN=System," + basedn,
296                         scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
297     names.policyid = str(res7[0]["cn"]).replace("{","").replace("}","")
298     # dc policy guid
299     res8 = samdb.search(expression="(displayName=Default Domain Controllers"
300                                    " Policy)",
301                             base="CN=Policies,CN=System," + basedn,
302                             scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
303     if len(res8) == 1:
304         names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","")
305     else:
306         names.policyid_dc = None
307     res9 = idmapdb.search(expression="(cn=%s)" %
308                             (security.SID_BUILTIN_ADMINISTRATORS),
309                             attrs=["xidNumber"])
310     if len(res9) == 1:
311         names.wheel_gid = res9[0]["xidNumber"]
312     else:
313         raise ProvisioningError("Unable to find uid/gid for Domain Admins rid")
314     return names
315
316 def update_provision_usn(samdb, low, high, id, replace=False):
317     """Update the field provisionUSN in sam.ldb
318
319     This field is used to track range of USN modified by provision and
320     upgradeprovision.
321     This value is used afterward by next provision to figure out if
322     the field have been modified since last provision.
323
324     :param samdb: An LDB object connect to sam.ldb
325     :param low: The lowest USN modified by this upgrade
326     :param high: The highest USN modified by this upgrade
327     :param id: The invocation id of the samba's dc
328     :param replace: A boolean indicating if the range should replace any
329                     existing one or appended (default)
330     """
331
332     tab = []
333     if not replace:
334         entry = samdb.search(base="@PROVISION",
335                              scope=ldb.SCOPE_BASE,
336                              attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
337         for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
338             if not re.search(';', e):
339                 e = "%s;%s" % (e, id)
340             tab.append(str(e))
341
342     tab.append("%s-%s;%s" % (low, high, id))
343     delta = ldb.Message()
344     delta.dn = ldb.Dn(samdb, "@PROVISION")
345     delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
346         ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE)
347     entry = samdb.search(expression='provisionnerID=*',
348                          base="@PROVISION", scope=ldb.SCOPE_BASE,
349                          attrs=["provisionnerID"])
350     if len(entry) == 0 or len(entry[0]) == 0:
351         delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
352     samdb.modify(delta)
353
354
355 def set_provision_usn(samdb, low, high, id):
356     """Set the field provisionUSN in sam.ldb
357     This field is used to track range of USN modified by provision and
358     upgradeprovision.
359     This value is used afterward by next provision to figure out if
360     the field have been modified since last provision.
361
362     :param samdb: An LDB object connect to sam.ldb
363     :param low: The lowest USN modified by this upgrade
364     :param high: The highest USN modified by this upgrade
365     :param id: The invocationId of the provision"""
366
367     tab = []
368     tab.append("%s-%s;%s" % (low, high, id))
369
370     delta = ldb.Message()
371     delta.dn = ldb.Dn(samdb, "@PROVISION")
372     delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
373         ldb.FLAG_MOD_ADD, LAST_PROVISION_USN_ATTRIBUTE)
374     samdb.add(delta)
375
376
377 def get_max_usn(samdb,basedn):
378     """ This function return the biggest USN present in the provision
379
380     :param samdb: A LDB object pointing to the sam.ldb
381     :param basedn: A string containing the base DN of the provision
382                     (ie. DC=foo, DC=bar)
383     :return: The biggest USN in the provision"""
384
385     res = samdb.search(expression="objectClass=*",base=basedn,
386                          scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
387                          controls=["search_options:1:2",
388                                    "server_sort:1:1:uSNChanged",
389                                    "paged_results:1:1"])
390     return res[0]["uSNChanged"]
391
392
393 def get_last_provision_usn(sam):
394     """Get USNs ranges modified by a provision or an upgradeprovision
395
396     :param sam: An LDB object pointing to the sam.ldb
397     :return: a dictionnary which keys are invocation id and values are an array
398              of integer representing the different ranges
399     """
400     try:
401         entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE,
402                            base="@PROVISION", scope=ldb.SCOPE_BASE,
403                            attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
404     except ldb.LdbError, (ecode, emsg):
405         if ecode == ldb.ERR_NO_SUCH_OBJECT:
406             return None
407         raise
408     if len(entry):
409         myids = []
410         range = {}
411         p = re.compile(r'-')
412         if entry[0].get("provisionnerID"):
413             for e in entry[0]["provisionnerID"]:
414                 myids.append(str(e))
415         for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
416             tab1 = str(r).split(';')
417             if len(tab1) == 2:
418                 id = tab1[1]
419             else:
420                 id = "default"
421             if (len(myids) > 0 and id not in myids):
422                 continue
423             tab2 = p.split(tab1[0])
424             if range.get(id) == None:
425                 range[id] = []
426             range[id].append(tab2[0])
427             range[id].append(tab2[1])
428         return range
429     else:
430         return None
431
432
433 class ProvisionResult(object):
434
435     def __init__(self):
436         self.paths = None
437         self.domaindn = None
438         self.lp = None
439         self.samdb = None
440         self.idmap = None
441         self.names = None
442
443
444 def check_install(lp, session_info, credentials):
445     """Check whether the current install seems ok.
446
447     :param lp: Loadparm context
448     :param session_info: Session information
449     :param credentials: Credentials
450     """
451     if lp.get("realm") == "":
452         raise Exception("Realm empty")
453     samdb = Ldb(lp.samdb_url(), session_info=session_info,
454             credentials=credentials, lp=lp)
455     if len(samdb.search("(cn=Administrator)")) != 1:
456         raise ProvisioningError("No administrator account found")
457
458
459 def findnss(nssfn, names):
460     """Find a user or group from a list of possibilities.
461
462     :param nssfn: NSS Function to try (should raise KeyError if not found)
463     :param names: Names to check.
464     :return: Value return by first names list.
465     """
466     for name in names:
467         try:
468             return nssfn(name)
469         except KeyError:
470             pass
471     raise KeyError("Unable to find user/group in %r" % names)
472
473
474 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
475 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
476
477
478 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
479     """Setup a ldb in the private dir.
480
481     :param ldb: LDB file to import data into
482     :param ldif_path: Path of the LDIF file to load
483     :param subst_vars: Optional variables to subsitute in LDIF.
484     :param nocontrols: Optional list of controls, can be None for no controls
485     """
486     assert isinstance(ldif_path, str)
487     data = read_and_sub_file(ldif_path, subst_vars)
488     ldb.add_ldif(data, controls)
489
490
491 def setup_modify_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
492     """Modify a ldb in the private dir.
493
494     :param ldb: LDB object.
495     :param ldif_path: LDIF file path.
496     :param subst_vars: Optional dictionary with substitution variables.
497     """
498     data = read_and_sub_file(ldif_path, subst_vars)
499     ldb.modify_ldif(data, controls)
500
501
502 def setup_ldb(ldb, ldif_path, subst_vars):
503     """Import a LDIF a file into a LDB handle, optionally substituting
504     variables.
505
506     :note: Either all LDIF data will be added or none (using transactions).
507
508     :param ldb: LDB file to import into.
509     :param ldif_path: Path to the LDIF file.
510     :param subst_vars: Dictionary with substitution variables.
511     """
512     assert ldb is not None
513     ldb.transaction_start()
514     try:
515         setup_add_ldif(ldb, ldif_path, subst_vars)
516     except Exception:
517         ldb.transaction_cancel()
518         raise
519     else:
520         ldb.transaction_commit()
521
522
523 def provision_paths_from_lp(lp, dnsdomain):
524     """Set the default paths for provisioning.
525
526     :param lp: Loadparm context.
527     :param dnsdomain: DNS Domain name
528     """
529     paths = ProvisionPaths()
530     paths.private_dir = lp.get("private dir")
531
532     # This is stored without path prefix for the "privateKeytab" attribute in
533     # "secrets_dns.ldif".
534     paths.dns_keytab = "dns.keytab"
535     paths.keytab = "secrets.keytab"
536
537     paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
538     paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
539     paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
540     paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
541     paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
542     paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
543     paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
544     paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
545     paths.namedconf = os.path.join(paths.private_dir, "named.conf")
546     paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
547     paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
548     paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
549     paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
550     paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
551     paths.phpldapadminconfig = os.path.join(paths.private_dir,
552                                             "phpldapadmin-config.php")
553     paths.hklm = "hklm.ldb"
554     paths.hkcr = "hkcr.ldb"
555     paths.hkcu = "hkcu.ldb"
556     paths.hku = "hku.ldb"
557     paths.hkpd = "hkpd.ldb"
558     paths.hkpt = "hkpt.ldb"
559     paths.sysvol = lp.get("path", "sysvol")
560     paths.netlogon = lp.get("path", "netlogon")
561     paths.smbconf = lp.configfile
562     return paths
563
564
565 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
566                 serverrole=None, rootdn=None, domaindn=None, configdn=None,
567                 schemadn=None, serverdn=None, sitename=None):
568     """Guess configuration settings to use."""
569
570     if hostname is None:
571         hostname = socket.gethostname().split(".")[0]
572
573     netbiosname = lp.get("netbios name")
574     if netbiosname is None:
575         netbiosname = hostname
576         # remove forbidden chars
577         newnbname = ""
578         for x in netbiosname:
579             if x.isalnum() or x in VALID_NETBIOS_CHARS:
580                 newnbname = "%s%c" % (newnbname, x)
581         # force the length to be <16
582         netbiosname = newnbname[0:15]
583     assert netbiosname is not None
584     netbiosname = netbiosname.upper()
585     if not valid_netbios_name(netbiosname):
586         raise InvalidNetbiosName(netbiosname)
587
588     if dnsdomain is None:
589         dnsdomain = lp.get("realm")
590         if dnsdomain is None or dnsdomain == "":
591             raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
592
593     dnsdomain = dnsdomain.lower()
594
595     if serverrole is None:
596         serverrole = lp.get("server role")
597         if serverrole is None:
598             raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
599
600     serverrole = serverrole.lower()
601
602     realm = dnsdomain.upper()
603
604     if lp.get("realm") == "":
605         raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s.  Please remove the smb.conf file and let provision generate it" % lp.configfile)
606
607     if lp.get("realm").upper() != realm:
608         raise ProvisioningError("guess_names: 'realm=%s' in %s must match chosen realm '%s'!  Please remove the smb.conf file and let provision generate it" % (lp.get("realm").upper(), realm, lp.configfile))
609
610     if lp.get("server role").lower() != serverrole:
611         raise ProvisioningError("guess_names: 'server role=%s' in %s must match chosen server role '%s'!  Please remove the smb.conf file and let provision generate it" % (lp.get("server role"), serverrole, lp.configfile))
612
613     if serverrole == "domain controller":
614         if domain is None:
615             # This will, for better or worse, default to 'WORKGROUP'
616             domain = lp.get("workgroup")
617         domain = domain.upper()
618
619         if lp.get("workgroup").upper() != domain:
620             raise ProvisioningError("guess_names: Workgroup '%s' in smb.conf must match chosen domain '%s'!  Please remove the %s file and let provision generate it" % (lp.get("workgroup").upper(), domain, lp.configfile))
621
622         if domaindn is None:
623             domaindn = samba.dn_from_dns_name(dnsdomain)
624
625         if domain == netbiosname:
626             raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
627     else:
628         domain = netbiosname
629         if domaindn is None:
630             domaindn = "DC=" + netbiosname
631
632     if not valid_netbios_name(domain):
633         raise InvalidNetbiosName(domain)
634
635     if hostname.upper() == realm:
636         raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
637     if netbiosname.upper() == realm:
638         raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
639     if domain == realm:
640         raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
641
642     if rootdn is None:
643        rootdn = domaindn
644
645     if configdn is None:
646         configdn = "CN=Configuration," + rootdn
647     if schemadn is None:
648         schemadn = "CN=Schema," + configdn
649
650     if sitename is None:
651         sitename=DEFAULTSITE
652
653     names = ProvisionNames()
654     names.rootdn = rootdn
655     names.domaindn = domaindn
656     names.configdn = configdn
657     names.schemadn = schemadn
658     names.ldapmanagerdn = "CN=Manager," + rootdn
659     names.dnsdomain = dnsdomain
660     names.domain = domain
661     names.realm = realm
662     names.netbiosname = netbiosname
663     names.hostname = hostname
664     names.sitename = sitename
665     names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
666         netbiosname, sitename, configdn)
667
668     return names
669
670
671 def make_smbconf(smbconf, hostname, domain, realm, serverrole,
672                  targetdir, sid_generator="internal", eadb=False, lp=None):
673     """Create a new smb.conf file based on a couple of basic settings.
674     """
675     assert smbconf is not None
676     if hostname is None:
677         hostname = socket.gethostname().split(".")[0]
678         netbiosname = hostname.upper()
679         # remove forbidden chars
680         newnbname = ""
681         for x in netbiosname:
682             if x.isalnum() or x in VALID_NETBIOS_CHARS:
683                 newnbname = "%s%c" % (newnbname, x)
684         #force the length to be <16
685         netbiosname = newnbname[0:15]
686     else:
687         netbiosname = hostname.upper()
688
689     if serverrole is None:
690         serverrole = "standalone"
691
692     assert serverrole in ("domain controller", "member server", "standalone")
693     if serverrole == "domain controller":
694         smbconfsuffix = "dc"
695     elif serverrole == "member server":
696         smbconfsuffix = "member"
697     elif serverrole == "standalone":
698         smbconfsuffix = "standalone"
699
700     if sid_generator is None:
701         sid_generator = "internal"
702
703     assert domain is not None
704     domain = domain.upper()
705
706     assert realm is not None
707     realm = realm.upper()
708
709     if lp is None:
710         lp = samba.param.LoadParm()
711     #Load non-existant file
712     if os.path.exists(smbconf):
713         lp.load(smbconf)
714     if eadb and not lp.get("posix:eadb"):
715         if targetdir is not None:
716             privdir = os.path.join(targetdir, "private")
717         else:
718             privdir = lp.get("private dir")
719         lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
720
721     if targetdir is not None:
722         privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
723         lockdir_line = "lock dir = " + os.path.abspath(targetdir)
724         statedir_line = "state directory = " + os.path.abspath(targetdir)
725         cachedir_line = "cache directory = " + os.path.abspath(targetdir)
726
727         lp.set("lock dir", os.path.abspath(targetdir))
728         lp.set("state directory", os.path.abspath(targetdir))
729         lp.set("cache directory", os.path.abspath(targetdir))
730     else:
731         privatedir_line = ""
732         lockdir_line = ""
733         statedir_line = ""
734         cachedir_line = ""
735
736     sysvol = os.path.join(lp.get("state directory"), "sysvol")
737     netlogon = os.path.join(sysvol, realm.lower(), "scripts")
738
739     setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
740                smbconf, {
741             "NETBIOS_NAME": netbiosname,
742             "DOMAIN": domain,
743             "REALM": realm,
744             "SERVERROLE": serverrole,
745             "NETLOGONPATH": netlogon,
746             "SYSVOLPATH": sysvol,
747             "PRIVATEDIR_LINE": privatedir_line,
748             "LOCKDIR_LINE": lockdir_line,
749             "STATEDIR_LINE": statedir_line,
750             "CACHEDIR_LINE": cachedir_line
751             })
752
753     # reload the smb.conf
754     lp.load(smbconf)
755
756     # and dump it without any values that are the default
757     # this ensures that any smb.conf parameters that were set
758     # on the provision/join command line are set in the resulting smb.conf
759     f = open(smbconf, mode='w')
760     lp.dump(f, False)
761     f.close()
762
763
764
765 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
766                         users_gid, wheel_gid):
767     """setup reasonable name mappings for sam names to unix names.
768
769     :param samdb: SamDB object.
770     :param idmap: IDmap db object.
771     :param sid: The domain sid.
772     :param domaindn: The domain DN.
773     :param root_uid: uid of the UNIX root user.
774     :param nobody_uid: uid of the UNIX nobody user.
775     :param users_gid: gid of the UNIX users group.
776     :param wheel_gid: gid of the UNIX wheel group.
777     """
778     idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
779     idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
780
781     idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
782     idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
783
784
785 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
786                            provision_backend, names, schema, serverrole,
787                            erase=False):
788     """Setup the partitions for the SAM database.
789
790     Alternatively, provision() may call this, and then populate the database.
791
792     :note: This will wipe the Sam Database!
793
794     :note: This function always removes the local SAM LDB file. The erase
795         parameter controls whether to erase the existing data, which
796         may not be stored locally but in LDAP.
797
798     """
799     assert session_info is not None
800
801     # We use options=["modules:"] to stop the modules loading - we
802     # just want to wipe and re-initialise the database, not start it up
803
804     try:
805         os.unlink(samdb_path)
806     except OSError:
807         pass
808
809     samdb = Ldb(url=samdb_path, session_info=session_info,
810                 lp=lp, options=["modules:"])
811
812     ldap_backend_line = "# No LDAP backend"
813     if provision_backend.type is not "ldb":
814         ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
815
816     samdb.transaction_start()
817     try:
818         logger.info("Setting up sam.ldb partitions and settings")
819         setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
820                 "LDAP_BACKEND_LINE": ldap_backend_line
821         })
822
823
824         setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
825                 "BACKEND_TYPE": provision_backend.type,
826                 "SERVER_ROLE": serverrole
827                 })
828
829         logger.info("Setting up sam.ldb rootDSE")
830         setup_samdb_rootdse(samdb, names)
831     except Exception:
832         samdb.transaction_cancel()
833         raise
834     else:
835         samdb.transaction_commit()
836
837
838 def secretsdb_self_join(secretsdb, domain,
839                         netbiosname, machinepass, domainsid=None,
840                         realm=None, dnsdomain=None,
841                         keytab_path=None,
842                         key_version_number=1,
843                         secure_channel_type=SEC_CHAN_WKSTA):
844     """Add domain join-specific bits to a secrets database.
845
846     :param secretsdb: Ldb Handle to the secrets database
847     :param machinepass: Machine password
848     """
849     attrs = ["whenChanged",
850            "secret",
851            "priorSecret",
852            "priorChanged",
853            "krb5Keytab",
854            "privateKeytab"]
855
856     if realm is not None:
857         if dnsdomain is None:
858             dnsdomain = realm.lower()
859         dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
860     else:
861         dnsname = None
862     shortname = netbiosname.lower()
863
864     # We don't need to set msg["flatname"] here, because rdn_name will handle
865     # it, and it causes problems for modifies anyway
866     msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
867     msg["secureChannelType"] = [str(secure_channel_type)]
868     msg["objectClass"] = ["top", "primaryDomain"]
869     if dnsname is not None:
870         msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
871         msg["realm"] = [realm]
872         msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
873         msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
874         msg["privateKeytab"] = ["secrets.keytab"]
875
876     msg["secret"] = [machinepass]
877     msg["samAccountName"] = ["%s$" % netbiosname]
878     msg["secureChannelType"] = [str(secure_channel_type)]
879     if domainsid is not None:
880         msg["objectSid"] = [ndr_pack(domainsid)]
881
882     # This complex expression tries to ensure that we don't have more
883     # than one record for this SID, realm or netbios domain at a time,
884     # but we don't delete the old record that we are about to modify,
885     # because that would delete the keytab and previous password.
886     res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
887         expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(dn=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
888         scope=ldb.SCOPE_ONELEVEL)
889
890     for del_msg in res:
891         secretsdb.delete(del_msg.dn)
892
893     res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
894
895     if len(res) == 1:
896         msg["priorSecret"] = [res[0]["secret"][0]]
897         msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
898
899         try:
900             msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
901         except KeyError:
902             pass
903
904         try:
905             msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
906         except KeyError:
907             pass
908
909         for el in msg:
910             if el != 'dn':
911                 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
912         secretsdb.modify(msg)
913         secretsdb.rename(res[0].dn, msg.dn)
914     else:
915         spn = [ 'HOST/%s' % shortname ]
916         if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
917             # we are a domain controller then we add servicePrincipalName
918             # entries for the keytab code to update.
919             spn.extend([ 'HOST/%s' % dnsname ])
920         msg["servicePrincipalName"] = spn
921
922         secretsdb.add(msg)
923
924
925 def secretsdb_setup_dns(secretsdb, names, private_dir, realm,
926                         dnsdomain, dns_keytab_path, dnspass):
927     """Add DNS specific bits to a secrets database.
928
929     :param secretsdb: Ldb Handle to the secrets database
930     :param machinepass: Machine password
931     """
932     try:
933         os.unlink(os.path.join(private_dir, dns_keytab_path))
934     except OSError:
935         pass
936
937     setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
938             "REALM": realm,
939             "DNSDOMAIN": dnsdomain,
940             "DNS_KEYTAB": dns_keytab_path,
941             "DNSPASS_B64": b64encode(dnspass),
942             "HOSTNAME": names.hostname,
943             "DNSNAME" : '%s.%s' % (
944                 names.netbiosname.lower(), names.dnsdomain.lower())
945             })
946
947
948 def setup_secretsdb(paths, session_info, backend_credentials, lp):
949     """Setup the secrets database.
950
951    :note: This function does not handle exceptions and transaction on purpose,
952        it's up to the caller to do this job.
953
954     :param path: Path to the secrets database.
955     :param session_info: Session info.
956     :param credentials: Credentials
957     :param lp: Loadparm context
958     :return: LDB handle for the created secrets database
959     """
960     if os.path.exists(paths.secrets):
961         os.unlink(paths.secrets)
962
963     keytab_path = os.path.join(paths.private_dir, paths.keytab)
964     if os.path.exists(keytab_path):
965         os.unlink(keytab_path)
966
967     dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
968     if os.path.exists(dns_keytab_path):
969         os.unlink(dns_keytab_path)
970
971     path = paths.secrets
972
973     secrets_ldb = Ldb(path, session_info=session_info,
974                       lp=lp)
975     secrets_ldb.erase()
976     secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
977     secrets_ldb = Ldb(path, session_info=session_info,
978                       lp=lp)
979     secrets_ldb.transaction_start()
980     try:
981         secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
982
983         if (backend_credentials is not None and
984             backend_credentials.authentication_requested()):
985             if backend_credentials.get_bind_dn() is not None:
986                 setup_add_ldif(secrets_ldb,
987                     setup_path("secrets_simple_ldap.ldif"), {
988                         "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
989                         "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
990                         })
991             else:
992                 setup_add_ldif(secrets_ldb,
993                     setup_path("secrets_sasl_ldap.ldif"), {
994                         "LDAPADMINUSER": backend_credentials.get_username(),
995                         "LDAPADMINREALM": backend_credentials.get_realm(),
996                         "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
997                         })
998
999         return secrets_ldb
1000     except Exception:
1001         secrets_ldb.transaction_cancel()
1002         raise
1003
1004
1005 def setup_privileges(path, session_info, lp):
1006     """Setup the privileges database.
1007
1008     :param path: Path to the privileges database.
1009     :param session_info: Session info.
1010     :param credentials: Credentials
1011     :param lp: Loadparm context
1012     :return: LDB handle for the created secrets database
1013     """
1014     if os.path.exists(path):
1015         os.unlink(path)
1016     privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
1017     privilege_ldb.erase()
1018     privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
1019
1020
1021 def setup_registry(path, session_info, lp):
1022     """Setup the registry.
1023
1024     :param path: Path to the registry database
1025     :param session_info: Session information
1026     :param credentials: Credentials
1027     :param lp: Loadparm context
1028     """
1029     reg = samba.registry.Registry()
1030     hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
1031     reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
1032     provision_reg = setup_path("provision.reg")
1033     assert os.path.exists(provision_reg)
1034     reg.diff_apply(provision_reg)
1035
1036
1037 def setup_idmapdb(path, session_info, lp):
1038     """Setup the idmap database.
1039
1040     :param path: path to the idmap database
1041     :param session_info: Session information
1042     :param credentials: Credentials
1043     :param lp: Loadparm context
1044     """
1045     if os.path.exists(path):
1046         os.unlink(path)
1047
1048     idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
1049     idmap_ldb.erase()
1050     idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
1051     return idmap_ldb
1052
1053
1054 def setup_samdb_rootdse(samdb, names):
1055     """Setup the SamDB rootdse.
1056
1057     :param samdb: Sam Database handle
1058     """
1059     setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
1060         "SCHEMADN": names.schemadn,
1061         "DOMAINDN": names.domaindn,
1062         "ROOTDN"  : names.rootdn,
1063         "CONFIGDN": names.configdn,
1064         "SERVERDN": names.serverdn,
1065         })
1066
1067
1068 def setup_self_join(samdb, 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         logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1601         setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1602                           { 'NTDSGUID' : names.ntdsguid })
1603
1604         secretsdb_self_join(secrets_ldb, domain=names.domain,
1605                             realm=names.realm, dnsdomain=names.dnsdomain,
1606                             netbiosname=names.netbiosname, domainsid=domainsid,
1607                             machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1608
1609         # Now set up the right msDS-SupportedEncryptionTypes into the DB
1610         # In future, this might be determined from some configuration
1611         kerberos_enctypes = str(ENC_ALL_TYPES)
1612
1613         try:
1614             msg = ldb.Message(ldb.Dn(samdb,
1615                                      samdb.searchone("distinguishedName",
1616                                                      expression="samAccountName=%s$" % names.netbiosname,
1617                                                      scope=ldb.SCOPE_SUBTREE)))
1618             msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1619                 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1620                 name="msDS-SupportedEncryptionTypes")
1621             samdb.modify(msg)
1622         except ldb.LdbError, (enum, estr):
1623             if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1624                 # It might be that this attribute does not exist in this schema
1625                 raise
1626
1627         if serverrole == "domain controller":
1628             secretsdb_setup_dns(secrets_ldb, names,
1629                                 paths.private_dir, realm=names.realm,
1630                                 dnsdomain=names.dnsdomain,
1631                                 dns_keytab_path=paths.dns_keytab, dnspass=dnspass)
1632
1633             # Default DNS backend is BIND9 using txt files for zone information
1634             if not dns_backend:
1635                 dns_backend = "BIND9"
1636
1637             setup_ad_dns(samdb, names, logger, hostip=hostip, hostip6=hostip6,
1638                          dns_backend=dns_backend, os_level=dom_for_fun_level)
1639
1640             domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1641                                          attribute="objectGUID")
1642             assert isinstance(domainguid, str)
1643
1644             create_dns_dir(logger, paths)
1645
1646             # Only make a zone file on the first DC, it should be
1647             # replicated with DNS replication
1648             if dns_backend == "BIND9":
1649                 create_zone_file(lp, logger, paths, targetdir,
1650                                  dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
1651                                  hostname=names.hostname, realm=names.realm,
1652                                  domainguid=domainguid, ntdsguid=names.ntdsguid)
1653
1654             create_named_conf(paths, realm=names.realm,
1655                               dnsdomain=names.dnsdomain, dns_backend=dns_backend)
1656
1657             create_named_txt(paths.namedtxt,
1658                              realm=names.realm, dnsdomain=names.dnsdomain,
1659                              dnsname = "%s.%s" % (names.hostname, names.dnsdomain),
1660                              private_dir=paths.private_dir,
1661                              keytab_name=paths.dns_keytab)
1662             logger.info("See %s for an example configuration include file for BIND", paths.namedconf)
1663             logger.info("and %s for further documentation required for secure DNS "
1664                         "updates", paths.namedtxt)
1665
1666             lastProvisionUSNs = get_last_provision_usn(samdb)
1667             maxUSN = get_max_usn(samdb, str(names.rootdn))
1668             if lastProvisionUSNs is not None:
1669                 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1670             else:
1671                 set_provision_usn(samdb, 0, maxUSN, invocationid)
1672
1673         # fix any dangling GUIDs from the provision
1674         logger.info("Fixing provision GUIDs")
1675         chk = dbcheck(samdb, samdb_schema=samdb,  verbose=False, fix=True, yes=True, quiet=True)
1676         samdb.transaction_start()
1677         # a small number of GUIDs are missing because of ordering issues in the
1678         # provision code
1679         for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1680             chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1681                                scope=ldb.SCOPE_BASE, attrs=['defaultObjectCategory'])
1682         chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1683                            scope=ldb.SCOPE_ONELEVEL,
1684                            attrs=['ipsecOwnersReference',
1685                                   'ipsecFilterReference',
1686                                   'ipsecISAKMPReference',
1687                                   'ipsecNegotiationPolicyReference',
1688                                   'ipsecNFAReference'])
1689         samdb.transaction_commit()
1690
1691
1692 def provision(logger, session_info, credentials, smbconf=None,
1693         targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1694         domaindn=None, schemadn=None, configdn=None, serverdn=None,
1695         domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1696         next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None, krbtgtpass=None,
1697         domainguid=None, policyguid=None, policyguid_dc=None,
1698         dns_backend=None, dnspass=None,
1699         invocationid=None, machinepass=None, ntdsguid=None,
1700         root=None, nobody=None, users=None, wheel=None, backup=None, aci=None,
1701         serverrole=None, dom_for_fun_level=None, ldap_backend_extra_port=None,
1702         ldap_backend_forced_uri=None, backend_type=None, sitename=None,
1703         ol_mmr_urls=None, ol_olc=None, setup_ds_path=None, slapd_path=None,
1704         nosync=False, ldap_dryrun_mode=False, useeadb=False, am_rodc=False,
1705         lp=None):
1706     """Provision samba4
1707
1708     :note: caution, this wipes all existing data!
1709     """
1710
1711     if ldapadminpass is None:
1712         # Make a new, random password between Samba and it's LDAP server
1713         ldapadminpass=samba.generate_random_password(128, 255)
1714
1715     if backend_type is None:
1716         backend_type = "ldb"
1717
1718     if domainsid is None:
1719         domainsid = security.random_sid()
1720     else:
1721         domainsid = security.dom_sid(domainsid)
1722
1723     sid_generator = "internal"
1724     if backend_type == "fedora-ds":
1725         sid_generator = "backend"
1726
1727     root_uid = findnss_uid([root or "root"])
1728     nobody_uid = findnss_uid([nobody or "nobody"])
1729     users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1730     if wheel is None:
1731         wheel_gid = findnss_gid(["wheel", "adm"])
1732     else:
1733         wheel_gid = findnss_gid([wheel])
1734     try:
1735         bind_gid = findnss_gid(["bind", "named"])
1736     except KeyError:
1737         bind_gid = None
1738
1739     if targetdir is not None:
1740         smbconf = os.path.join(targetdir, "etc", "smb.conf")
1741     elif smbconf is None:
1742         smbconf = samba.param.default_path()
1743     if not os.path.exists(os.path.dirname(smbconf)):
1744         os.makedirs(os.path.dirname(smbconf))
1745
1746     # only install a new smb.conf if there isn't one there already
1747     if os.path.exists(smbconf):
1748         # if Samba Team members can't figure out the weird errors
1749         # loading an empty smb.conf gives, then we need to be smarter.
1750         # Pretend it just didn't exist --abartlet
1751         data = open(smbconf, 'r').read()
1752         data = data.lstrip()
1753         if data is None or data == "":
1754             make_smbconf(smbconf, hostname, domain, realm,
1755                          serverrole, targetdir, sid_generator, useeadb,
1756                          lp=lp)
1757     else:
1758         make_smbconf(smbconf, hostname, domain, realm, serverrole,
1759                      targetdir, sid_generator, useeadb, lp=lp)
1760
1761     if lp is None:
1762         lp = samba.param.LoadParm()
1763     lp.load(smbconf)
1764     names = guess_names(lp=lp, hostname=hostname, domain=domain,
1765         dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
1766         configdn=configdn, schemadn=schemadn, serverdn=serverdn,
1767         sitename=sitename, rootdn=rootdn)
1768     paths = provision_paths_from_lp(lp, names.dnsdomain)
1769
1770     paths.bind_gid = bind_gid
1771     paths.wheel_gid = wheel_gid
1772
1773     if hostip is None:
1774         logger.info("Looking up IPv4 addresses")
1775         hostips = interface_ips_v4(lp)
1776         if len(hostips) > 0:
1777             hostip = hostips[0]
1778             if len(hostips) > 1:
1779                 logger.warning("More than one IPv4 address found. Using %s",
1780                     hostip)
1781     if hostip == "127.0.0.1":
1782         hostip = None
1783     if hostip is None:
1784         logger.warning("No IPv4 address will be assigned")
1785
1786     if hostip6 is None:
1787         logger.info("Looking up IPv6 addresses")
1788         hostips = interface_ips_v6(lp, linklocal=False)
1789         if hostips:
1790             hostip6 = hostips[0]
1791         if len(hostips) > 1:
1792             logger.warning("More than one IPv6 address found. Using %s", hostip6)
1793     if hostip6 is None:
1794         logger.warning("No IPv6 address will be assigned")
1795
1796     names.hostip = hostip
1797     names.hostip6 = hostip6
1798
1799     if serverrole is None:
1800         serverrole = lp.get("server role")
1801
1802     assert serverrole in ("domain controller", "member server", "standalone")
1803
1804     if not os.path.exists(paths.private_dir):
1805         os.mkdir(paths.private_dir)
1806     if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1807         os.mkdir(os.path.join(paths.private_dir, "tls"))
1808
1809     ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1810
1811     schema = Schema(domainsid, invocationid=invocationid,
1812         schemadn=names.schemadn)
1813
1814     if backend_type == "ldb":
1815         provision_backend = LDBBackend(backend_type, paths=paths,
1816             lp=lp, credentials=credentials,
1817             names=names, logger=logger)
1818     elif backend_type == "existing":
1819         provision_backend = ExistingBackend(backend_type, paths=paths,
1820             lp=lp, credentials=credentials,
1821             names=names, logger=logger,
1822             ldap_backend_forced_uri=ldap_backend_forced_uri)
1823     elif backend_type == "fedora-ds":
1824         provision_backend = FDSBackend(backend_type, paths=paths,
1825             lp=lp, credentials=credentials,
1826             names=names, logger=logger, domainsid=domainsid,
1827             schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1828             slapd_path=slapd_path,
1829             ldap_backend_extra_port=ldap_backend_extra_port,
1830             ldap_dryrun_mode=ldap_dryrun_mode, root=root,
1831             setup_ds_path=setup_ds_path,
1832             ldap_backend_forced_uri=ldap_backend_forced_uri)
1833     elif backend_type == "openldap":
1834         provision_backend = OpenLDAPBackend(backend_type, paths=paths,
1835             lp=lp, credentials=credentials,
1836             names=names, logger=logger, domainsid=domainsid,
1837             schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1838             slapd_path=slapd_path,
1839             ldap_backend_extra_port=ldap_backend_extra_port,
1840             ldap_dryrun_mode=ldap_dryrun_mode, ol_mmr_urls=ol_mmr_urls,
1841             nosync=nosync,
1842             ldap_backend_forced_uri=ldap_backend_forced_uri)
1843     else:
1844         raise ValueError("Unknown LDAP backend type selected")
1845
1846     provision_backend.init()
1847     provision_backend.start()
1848
1849     # only install a new shares config db if there is none
1850     if not os.path.exists(paths.shareconf):
1851         logger.info("Setting up share.ldb")
1852         share_ldb = Ldb(paths.shareconf, session_info=session_info,
1853                         lp=lp)
1854         share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1855
1856     logger.info("Setting up secrets.ldb")
1857     secrets_ldb = setup_secretsdb(paths,
1858         session_info=session_info,
1859         backend_credentials=provision_backend.secrets_credentials, lp=lp)
1860
1861     try:
1862         logger.info("Setting up the registry")
1863         setup_registry(paths.hklm, session_info,
1864                        lp=lp)
1865
1866         logger.info("Setting up the privileges database")
1867         setup_privileges(paths.privilege, session_info, lp=lp)
1868
1869         logger.info("Setting up idmap db")
1870         idmap = setup_idmapdb(paths.idmapdb,
1871             session_info=session_info, lp=lp)
1872
1873         setup_name_mappings(idmap, sid=str(domainsid),
1874                             root_uid=root_uid, nobody_uid=nobody_uid,
1875                             users_gid=users_gid, wheel_gid=wheel_gid)
1876
1877         logger.info("Setting up SAM db")
1878         samdb = setup_samdb(paths.samdb, session_info,
1879                             provision_backend, lp, names, logger=logger,
1880                             serverrole=serverrole,
1881                             schema=schema, fill=samdb_fill, am_rodc=am_rodc)
1882
1883         if serverrole == "domain controller":
1884             if paths.netlogon is None:
1885                 logger.info("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1886                 logger.info("Please either remove %s or see the template at %s" %
1887                         (paths.smbconf, setup_path("provision.smb.conf.dc")))
1888                 assert paths.netlogon is not None
1889
1890             if paths.sysvol is None:
1891                 logger.info("Existing smb.conf does not have a [sysvol] share, but you"
1892                         " are configuring a DC.")
1893                 logger.info("Please either remove %s or see the template at %s" %
1894                         (paths.smbconf, setup_path("provision.smb.conf.dc")))
1895                 assert paths.sysvol is not None
1896
1897             if not os.path.isdir(paths.netlogon):
1898                 os.makedirs(paths.netlogon, 0755)
1899
1900         if samdb_fill == FILL_FULL:
1901             provision_fill(samdb, secrets_ldb, logger,
1902                            names, paths, schema=schema, targetdir=targetdir,
1903                            samdb_fill=samdb_fill, hostip=hostip, hostip6=hostip6, domainsid=domainsid,
1904                            next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
1905                            krbtgtpass=krbtgtpass, domainguid=domainguid,
1906                            policyguid=policyguid, policyguid_dc=policyguid_dc,
1907                            invocationid=invocationid, machinepass=machinepass,
1908                            ntdsguid=ntdsguid, dns_backend=dns_backend, dnspass=dnspass,
1909                            serverrole=serverrole, dom_for_fun_level=dom_for_fun_level,
1910                            am_rodc=am_rodc, lp=lp)
1911
1912         create_krb5_conf(paths.krb5conf,
1913                          dnsdomain=names.dnsdomain, hostname=names.hostname,
1914                          realm=names.realm)
1915         logger.info("A Kerberos configuration suitable for Samba 4 has been "
1916                     "generated at %s", paths.krb5conf)
1917
1918         if serverrole == "domain controller":
1919             create_dns_update_list(lp, logger, paths)
1920
1921         provision_backend.post_setup()
1922         provision_backend.shutdown()
1923
1924         create_phpldapadmin_config(paths.phpldapadminconfig,
1925                                    ldapi_url)
1926     except Exception:
1927         secrets_ldb.transaction_cancel()
1928         raise
1929
1930     # Now commit the secrets.ldb to disk
1931     secrets_ldb.transaction_commit()
1932
1933     # the commit creates the dns.keytab, now chown it
1934     dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1935     if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
1936         try:
1937             os.chmod(dns_keytab_path, 0640)
1938             os.chown(dns_keytab_path, -1, paths.bind_gid)
1939         except OSError:
1940             if not os.environ.has_key('SAMBA_SELFTEST'):
1941                 logger.info("Failed to chown %s to bind gid %u",
1942                             dns_keytab_path, paths.bind_gid)
1943
1944     logger.info("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php",
1945             paths.phpldapadminconfig)
1946
1947     logger.info("Once the above files are installed, your Samba4 server will be ready to use")
1948     logger.info("Server Role:           %s" % serverrole)
1949     logger.info("Hostname:              %s" % names.hostname)
1950     logger.info("NetBIOS Domain:        %s" % names.domain)
1951     logger.info("DNS Domain:            %s" % names.dnsdomain)
1952     logger.info("DOMAIN SID:            %s" % str(domainsid))
1953     if samdb_fill == FILL_FULL:
1954         logger.info("Admin password:        %s" % adminpass)
1955     if provision_backend.type is not "ldb":
1956         if provision_backend.credentials.get_bind_dn() is not None:
1957             logger.info("LDAP Backend Admin DN: %s" %
1958                 provision_backend.credentials.get_bind_dn())
1959         else:
1960             logger.info("LDAP Admin User:       %s" %
1961                 provision_backend.credentials.get_username())
1962
1963         logger.info("LDAP Admin Password:   %s" %
1964             provision_backend.credentials.get_password())
1965
1966         if provision_backend.slapd_command_escaped is not None:
1967             # now display slapd_command_file.txt to show how slapd must be
1968             # started next time
1969             logger.info("Use later the following commandline to start slapd, then Samba:")
1970             logger.info(provision_backend.slapd_command_escaped)
1971             logger.info("This slapd-Commandline is also stored under: %s/ldap_backend_startup.sh",
1972                     provision_backend.ldapdir)
1973
1974     result = ProvisionResult()
1975     result.domaindn = domaindn
1976     result.paths = paths
1977     result.names = names
1978     result.lp = lp
1979     result.samdb = samdb
1980     result.idmap = idmap
1981     return result
1982
1983
1984 def provision_become_dc(smbconf=None, targetdir=None,
1985         realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
1986         serverdn=None, domain=None, hostname=None, domainsid=None,
1987         adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
1988         policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
1989         dns_backend=None, root=None, nobody=None, users=None, wheel=None, backup=None,
1990         serverrole=None, ldap_backend=None, ldap_backend_type=None,
1991         sitename=None, debuglevel=1):
1992
1993     logger = logging.getLogger("provision")
1994     samba.set_debug_level(debuglevel)
1995
1996     res = provision(logger, system_session(), None,
1997         smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1998         realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1999         configdn=configdn, serverdn=serverdn, domain=domain,
2000         hostname=hostname, hostip=None, domainsid=domainsid,
2001         machinepass=machinepass, serverrole="domain controller",
2002         sitename=sitename, dns_backend=dns_backend, dnspass=dnspass)
2003     res.lp.set("debuglevel", str(debuglevel))
2004     return res
2005
2006
2007 def create_phpldapadmin_config(path, ldapi_uri):
2008     """Create a PHP LDAP admin configuration file.
2009
2010     :param path: Path to write the configuration to.
2011     """
2012     setup_file(setup_path("phpldapadmin-config.php"), path,
2013             {"S4_LDAPI_URI": ldapi_uri})
2014
2015
2016 def create_dns_dir(logger, paths):
2017     """Write out a DNS zone file, from the info in the current database.
2018
2019     :param logger: Logger object
2020     :param paths: paths object
2021     """
2022     dns_dir = os.path.dirname(paths.dns)
2023
2024     try:
2025         shutil.rmtree(dns_dir, True)
2026     except OSError:
2027         pass
2028
2029     os.mkdir(dns_dir, 0770)
2030
2031     if paths.bind_gid is not None:
2032         try:
2033             os.chown(dns_dir, -1, paths.bind_gid)
2034             # chmod needed to cope with umask
2035             os.chmod(dns_dir, 0770)
2036         except OSError:
2037             if not os.environ.has_key('SAMBA_SELFTEST'):
2038                 logger.error("Failed to chown %s to bind gid %u" % (
2039                     dns_dir, paths.bind_gid))
2040
2041
2042 def create_zone_file(lp, logger, paths, targetdir, dnsdomain,
2043                      hostip, hostip6, hostname, realm, domainguid,
2044                      ntdsguid):
2045     """Write out a DNS zone file, from the info in the current database.
2046
2047     :param paths: paths object
2048     :param dnsdomain: DNS Domain name
2049     :param domaindn: DN of the Domain
2050     :param hostip: Local IPv4 IP
2051     :param hostip6: Local IPv6 IP
2052     :param hostname: Local hostname
2053     :param realm: Realm name
2054     :param domainguid: GUID of the domain.
2055     :param ntdsguid: GUID of the hosts nTDSDSA record.
2056     """
2057     assert isinstance(domainguid, str)
2058
2059     if hostip6 is not None:
2060         hostip6_base_line = "            IN AAAA    " + hostip6
2061         hostip6_host_line = hostname + "        IN AAAA    " + hostip6
2062         gc_msdcs_ip6_line = "gc._msdcs               IN AAAA    " + hostip6
2063     else:
2064         hostip6_base_line = ""
2065         hostip6_host_line = ""
2066         gc_msdcs_ip6_line = ""
2067
2068     if hostip is not None:
2069         hostip_base_line = "            IN A    " + hostip
2070         hostip_host_line = hostname + "        IN A    " + hostip
2071         gc_msdcs_ip_line = "gc._msdcs               IN A    " + hostip
2072     else:
2073         hostip_base_line = ""
2074         hostip_host_line = ""
2075         gc_msdcs_ip_line = ""
2076
2077     # we need to freeze the zone while we update the contents
2078     if targetdir is None:
2079         rndc = ' '.join(lp.get("rndc command"))
2080         os.system(rndc + " freeze " + lp.get("realm"))
2081
2082     setup_file(setup_path("provision.zone"), paths.dns, {
2083             "HOSTNAME": hostname,
2084             "DNSDOMAIN": dnsdomain,
2085             "REALM": realm,
2086             "HOSTIP_BASE_LINE": hostip_base_line,
2087             "HOSTIP_HOST_LINE": hostip_host_line,
2088             "DOMAINGUID": domainguid,
2089             "DATESTRING": time.strftime("%Y%m%d%H"),
2090             "DEFAULTSITE": DEFAULTSITE,
2091             "NTDSGUID": ntdsguid,
2092             "HOSTIP6_BASE_LINE": hostip6_base_line,
2093             "HOSTIP6_HOST_LINE": hostip6_host_line,
2094             "GC_MSDCS_IP_LINE": gc_msdcs_ip_line,
2095             "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line,
2096         })
2097
2098     if paths.bind_gid is not None:
2099         try:
2100             os.chown(paths.dns, -1, paths.bind_gid)
2101             # chmod needed to cope with umask
2102             os.chmod(paths.dns, 0664)
2103         except OSError:
2104             if not os.environ.has_key('SAMBA_SELFTEST'):
2105                 logger.error("Failed to chown %s to bind gid %u" % (
2106                     paths.dns, paths.bind_gid))
2107
2108     if targetdir is None:
2109         os.system(rndc + " unfreeze " + lp.get("realm"))
2110
2111
2112 def create_dns_update_list(lp, logger, paths):
2113     """Write out a dns_update_list file"""
2114     # note that we use no variable substitution on this file
2115     # the substitution is done at runtime by samba_dnsupdate, samba_spnupdate
2116     setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
2117     setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
2118
2119
2120 def create_named_conf(paths, realm, dnsdomain, dns_backend):
2121     """Write out a file containing zone statements suitable for inclusion in a
2122     named.conf file (including GSS-TSIG configuration).
2123
2124     :param paths: all paths
2125     :param realm: Realm name
2126     :param dnsdomain: DNS Domain name
2127     :param dns_backend: DNS backend type
2128     :param keytab_name: File name of DNS keytab file
2129     """
2130
2131     if dns_backend == "BIND9":
2132         setup_file(setup_path("named.conf"), paths.namedconf, {
2133                     "DNSDOMAIN": dnsdomain,
2134                     "REALM": realm,
2135                     "ZONE_FILE": paths.dns,
2136                     "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
2137                     "NAMED_CONF": paths.namedconf,
2138                     "NAMED_CONF_UPDATE": paths.namedconf_update
2139                     })
2140
2141         setup_file(setup_path("named.conf.update"), paths.namedconf_update)
2142
2143     elif dns_backend == "BIND9_DLZ":
2144         dlz_module_path = os.path.join(samba.param.modules_dir(), 
2145                                         "bind9/dlz_bind9.so")
2146         setup_file(setup_path("named.conf.dlz"), paths.namedconf, {
2147                     "NAMED_CONF": paths.namedconf,
2148                     "BIND9_DLZ_MODULE": dlz_module_path,
2149                     })
2150
2151
2152
2153 def create_named_txt(path, realm, dnsdomain, dnsname, private_dir,
2154     keytab_name):
2155     """Write out a file containing zone statements suitable for inclusion in a
2156     named.conf file (including GSS-TSIG configuration).
2157
2158     :param path: Path of the new named.conf file.
2159     :param realm: Realm name
2160     :param dnsdomain: DNS Domain name
2161     :param private_dir: Path to private directory
2162     :param keytab_name: File name of DNS keytab file
2163     """
2164     setup_file(setup_path("named.txt"), path, {
2165             "DNSDOMAIN": dnsdomain,
2166             "DNSNAME" : dnsname, 
2167             "REALM": realm,
2168             "DNS_KEYTAB": keytab_name,
2169             "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
2170             "PRIVATE_DIR": private_dir
2171         })
2172
2173
2174 def create_krb5_conf(path, dnsdomain, hostname, realm):
2175     """Write out a file containing zone statements suitable for inclusion in a
2176     named.conf file (including GSS-TSIG configuration).
2177
2178     :param path: Path of the new named.conf file.
2179     :param dnsdomain: DNS Domain name
2180     :param hostname: Local hostname
2181     :param realm: Realm name
2182     """
2183     setup_file(setup_path("krb5.conf"), path, {
2184             "DNSDOMAIN": dnsdomain,
2185             "HOSTNAME": hostname,
2186             "REALM": realm,
2187         })
2188
2189
2190 class ProvisioningError(Exception):
2191     """A generic provision error."""
2192
2193     def __init__(self, value):
2194         self.value = value
2195
2196     def __str__(self):
2197         return "ProvisioningError: " + self.value
2198
2199
2200 class InvalidNetbiosName(Exception):
2201     """A specified name was not a valid NetBIOS name."""
2202     def __init__(self, name):
2203         super(InvalidNetbiosName, self).__init__(
2204             "The name '%r' is not a valid NetBIOS name" % name)