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