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