s4:provision - Moved backend-specific variables into backend class.
[ira/wip.git] / source4 / scripting / python / samba / provision.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-2008
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 from base64 import b64encode
29 import os
30 import pwd
31 import grp
32 import time
33 import uuid
34 import socket
35 import param
36 import registry
37 import urllib
38 import shutil
39
40 import ldb
41
42 from samba.auth import system_session, admin_session
43 from samba import glue, version, Ldb, substitute_var, valid_netbios_name
44 from samba import check_all_substituted, read_and_sub_file, setup_file
45 from samba import DS_DOMAIN_FUNCTION_2003, DS_DC_FUNCTION_2008
46 from samba.dcerpc import security
47 from samba.dcerpc.misc import SEC_CHAN_BDC, SEC_CHAN_WKSTA
48 from samba.idmap import IDmapDB
49 from samba.ntacls import setntacl, dsacl2fsacl
50 from samba.ndr import ndr_pack,ndr_unpack
51 from samba.schema import Schema
52 from ms_display_specifiers import read_ms_ldif
53 from samba.provisionbackend import LDBBackend, ExistingBackend, FDSBackend, OpenLDAPBackend
54 from provisionexceptions import ProvisioningError, InvalidNetbiosName
55
56 __docformat__ = "restructuredText"
57
58 def find_setup_dir():
59     """Find the setup directory used by provision."""
60     dirname = os.path.dirname(__file__)
61     if "/site-packages/" in dirname:
62         prefix = "/".join(dirname[:dirname.index("/site-packages/")].split("/")[:-2])
63         for suffix in ["share/setup", "share/samba/setup", "setup"]:
64             ret = os.path.join(prefix, suffix)
65             if os.path.isdir(ret):
66                 return ret
67     # In source tree
68     ret = os.path.join(dirname, "../../../setup")
69     if os.path.isdir(ret):
70         return ret
71     raise Exception("Unable to find setup directory.")
72
73 # descriptors of the naming contexts
74 # hard coded at this point, but will probably be changed when
75 # we enable different fsmo roles
76
77 def get_config_descriptor(domain_sid):
78     sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
79            "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
80            "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
81            "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
82            "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
83            "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
84            "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
85            "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
86            "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
87            "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
88            "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
89            "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
90            "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
91            "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
92            "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
93     sec = security.descriptor.from_sddl(sddl, domain_sid)
94     return ndr_pack(sec)
95
96 def get_domain_descriptor(domain_sid):
97     sddl= "O:BAG:BAD:AI(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
98         "(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
99     "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
100     "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
101     "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
102     "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
103     "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
104     "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
105     "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
106     "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
107     "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
108     "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)" \
109     "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)" \
110     "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)" \
111     "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)" \
112     "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
113     "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
114     "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
115     "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
116     "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
117     "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
118     "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;IF)" \
119     "(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)" \
120     "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)" \
121     "(OA;CIIO;RPLCLORC;;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
122     "(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)" \
123     "(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
124     "(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)" \
125     "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
126     "(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)" \
127     "(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)" \
128     "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
129     "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
130     "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
131     "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
132     "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)" \
133     "(OA;CIIO;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \
134     "(A;;RPWPCRCCLCLORCWOWDSW;;;DA)" \
135     "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
136     "(A;;RPRC;;;RU)" \
137     "(A;CI;LC;;;RU)" \
138     "(A;CI;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
139     "(A;;RP;;;WD)" \
140     "(A;;RPLCLORC;;;ED)" \
141     "(A;;RPLCLORC;;;AU)" \
142     "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
143     "S:AI(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
144     "(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
145     "(AU;SA;CR;;;DU)(AU;SA;CR;;;BA)(AU;SA;WPWOWD;;;WD)"
146     sec = security.descriptor.from_sddl(sddl, domain_sid)
147     return ndr_pack(sec)
148
149 DEFAULTSITE = "Default-First-Site-Name"
150
151 # Exception classes
152
153 class ProvisionPaths(object):
154     def __init__(self):
155         self.shareconf = None
156         self.hklm = None
157         self.hkcu = None
158         self.hkcr = None
159         self.hku = None
160         self.hkpd = None
161         self.hkpt = None
162         self.samdb = None
163         self.idmapdb = None
164         self.secrets = None
165         self.keytab = None
166         self.dns_keytab = None
167         self.dns = None
168         self.winsdb = None
169         self.private_dir = None
170
171
172 class ProvisionNames(object):
173     def __init__(self):
174         self.rootdn = None
175         self.domaindn = None
176         self.configdn = None
177         self.schemadn = None
178         self.ldapmanagerdn = None
179         self.dnsdomain = None
180         self.realm = None
181         self.netbiosname = None
182         self.domain = None
183         self.hostname = None
184         self.sitename = None
185         self.smbconf = None
186     
187
188 class ProvisionResult(object):
189     def __init__(self):
190         self.paths = None
191         self.domaindn = None
192         self.lp = None
193         self.samdb = None
194
195 def check_install(lp, session_info, credentials):
196     """Check whether the current install seems ok.
197     
198     :param lp: Loadparm context
199     :param session_info: Session information
200     :param credentials: Credentials
201     """
202     if lp.get("realm") == "":
203         raise Exception("Realm empty")
204     ldb = Ldb(lp.get("sam database"), session_info=session_info, 
205             credentials=credentials, lp=lp)
206     if len(ldb.search("(cn=Administrator)")) != 1:
207         raise ProvisioningError("No administrator account found")
208
209
210 def findnss(nssfn, names):
211     """Find a user or group from a list of possibilities.
212     
213     :param nssfn: NSS Function to try (should raise KeyError if not found)
214     :param names: Names to check.
215     :return: Value return by first names list.
216     """
217     for name in names:
218         try:
219             return nssfn(name)
220         except KeyError:
221             pass
222     raise KeyError("Unable to find user/group %r" % names)
223
224
225 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
226 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
227
228
229 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
230     """Setup a ldb in the private dir.
231     
232     :param ldb: LDB file to import data into
233     :param ldif_path: Path of the LDIF file to load
234     :param subst_vars: Optional variables to subsitute in LDIF.
235     :param nocontrols: Optional list of controls, can be None for no controls
236     """
237     assert isinstance(ldif_path, str)
238     data = read_and_sub_file(ldif_path, subst_vars)
239     ldb.add_ldif(data,controls)
240
241
242 def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
243     """Modify a ldb in the private dir.
244     
245     :param ldb: LDB object.
246     :param ldif_path: LDIF file path.
247     :param subst_vars: Optional dictionary with substitution variables.
248     """
249     data = read_and_sub_file(ldif_path, subst_vars)
250
251     ldb.modify_ldif(data)
252
253
254 def setup_ldb(ldb, ldif_path, subst_vars):
255     """Import a LDIF a file into a LDB handle, optionally substituting variables.
256
257     :note: Either all LDIF data will be added or none (using transactions).
258
259     :param ldb: LDB file to import into.
260     :param ldif_path: Path to the LDIF file.
261     :param subst_vars: Dictionary with substitution variables.
262     """
263     assert ldb is not None
264     ldb.transaction_start()
265     try:
266         setup_add_ldif(ldb, ldif_path, subst_vars)
267     except:
268         ldb.transaction_cancel()
269         raise
270     ldb.transaction_commit()
271
272
273 def provision_paths_from_lp(lp, dnsdomain):
274     """Set the default paths for provisioning.
275
276     :param lp: Loadparm context.
277     :param dnsdomain: DNS Domain name
278     """
279     paths = ProvisionPaths()
280     paths.private_dir = lp.get("private dir")
281     paths.dns_keytab = "dns.keytab"
282
283     paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
284     paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
285     paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
286     paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
287     paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
288     paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
289     paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
290     paths.namedconf = os.path.join(paths.private_dir, "named.conf")
291     paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
292     paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
293     paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
294     paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
295     paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
296     paths.phpldapadminconfig = os.path.join(paths.private_dir, 
297                                             "phpldapadmin-config.php")
298     paths.hklm = "hklm.ldb"
299     paths.hkcr = "hkcr.ldb"
300     paths.hkcu = "hkcu.ldb"
301     paths.hku = "hku.ldb"
302     paths.hkpd = "hkpd.ldb"
303     paths.hkpt = "hkpt.ldb"
304
305     paths.sysvol = lp.get("path", "sysvol")
306
307     paths.netlogon = lp.get("path", "netlogon")
308
309     paths.smbconf = lp.configfile
310
311     return paths
312
313
314 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
315                 serverrole=None, rootdn=None, domaindn=None, configdn=None,
316                 schemadn=None, serverdn=None, sitename=None):
317     """Guess configuration settings to use."""
318
319     if hostname is None:
320         hostname = socket.gethostname().split(".")[0]
321
322     netbiosname = lp.get("netbios name")
323     if netbiosname is None:
324         netbiosname = hostname
325     assert netbiosname is not None
326     netbiosname = netbiosname.upper()
327     if not valid_netbios_name(netbiosname):
328         raise InvalidNetbiosName(netbiosname)
329
330     if dnsdomain is None:
331         dnsdomain = lp.get("realm")
332         if dnsdomain is None or dnsdomain == "":
333             raise ProvisioningError("guess_names: 'realm' not specified in supplied smb.conf!")
334
335     dnsdomain = dnsdomain.lower()
336
337     if serverrole is None:
338         serverrole = lp.get("server role")
339         if serverrole is None:
340             raise ProvisioningError("guess_names: 'server role' not specified in supplied smb.conf!")
341
342     serverrole = serverrole.lower()
343
344     realm = dnsdomain.upper()
345
346     if lp.get("realm").upper() != realm:
347         raise ProvisioningError("guess_names: Realm '%s' in smb.conf must match chosen realm '%s'!", lp.get("realm").upper(), realm)
348
349     if lp.get("server role").lower() != serverrole:
350         raise ProvisioningError("guess_names: server role '%s' in smb.conf must match chosen server role '%s'!", lp.get("server role").upper(), serverrole)
351
352     if serverrole == "domain controller":
353         if domain is None:
354             domain = lp.get("workgroup")
355         if domain is None:
356             raise ProvisioningError("guess_names: 'workgroup' not specified in supplied smb.conf!")
357         domain = domain.upper()
358
359         if lp.get("workgroup").upper() != domain:
360             raise ProvisioningError("guess_names: Workgroup '%s' in smb.conf must match chosen domain '%s'!", lp.get("workgroup").upper(), domain)
361
362         if domaindn is None:
363             domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
364     else:
365         domain = netbiosname
366         if domaindn is None:
367             domaindn = "DC=" + netbiosname
368         
369     if not valid_netbios_name(domain):
370         raise InvalidNetbiosName(domain)
371         
372     if hostname.upper() == realm:
373         raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!", realm, hostname)
374     if netbiosname == realm:
375         raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!", realm, netbiosname)
376     if domain == realm:
377         raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!", realm, domain)
378
379     if rootdn is None:
380        rootdn = domaindn
381        
382     if configdn is None:
383         configdn = "CN=Configuration," + rootdn
384     if schemadn is None:
385         schemadn = "CN=Schema," + configdn
386
387     if sitename is None:
388         sitename=DEFAULTSITE
389
390     names = ProvisionNames()
391     names.rootdn = rootdn
392     names.domaindn = domaindn
393     names.configdn = configdn
394     names.schemadn = schemadn
395     names.ldapmanagerdn = "CN=Manager," + rootdn
396     names.dnsdomain = dnsdomain
397     names.domain = domain
398     names.realm = realm
399     names.netbiosname = netbiosname
400     names.hostname = hostname
401     names.sitename = sitename
402     names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
403  
404     return names
405     
406
407 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, 
408                  targetdir, sid_generator,eadb):
409     """Create a new smb.conf file based on a couple of basic settings.
410     """
411     assert smbconf is not None
412     if hostname is None:
413         hostname = socket.gethostname().split(".")[0]
414     netbiosname = hostname.upper()
415
416     if serverrole is None:
417         serverrole = "standalone"
418
419     assert serverrole in ("domain controller", "member server", "standalone")
420     if serverrole == "domain controller":
421         smbconfsuffix = "dc"
422     elif serverrole == "member server":
423         smbconfsuffix = "member"
424     elif serverrole == "standalone":
425         smbconfsuffix = "standalone"
426
427     if sid_generator is None:
428         sid_generator = "internal"
429
430     assert domain is not None
431     domain = domain.upper()
432
433     assert realm is not None
434     realm = realm.upper()
435
436     default_lp = param.LoadParm()
437     #Load non-existant file
438     if os.path.exists(smbconf):
439         default_lp.load(smbconf)
440     if eadb:
441         if targetdir is not None:
442             privdir = os.path.join(targetdir, "private")
443         else:
444             privdir = default_lp.get("private dir")
445         posixeadb_line = "posix:eadb = " + os.path.abspath(os.path.join(privdir,"eadb.tdb"))
446     else:
447         posixeadb_line = ""
448
449     if targetdir is not None:
450         privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
451         lockdir_line = "lock dir = " + os.path.abspath(targetdir)
452
453         default_lp.set("lock dir", os.path.abspath(targetdir))
454     else:
455         privatedir_line = ""
456         lockdir_line = ""
457
458     if sid_generator == "internal":
459         sid_generator_line = ""
460     else:
461         sid_generator_line = "sid generator = " + sid_generator
462
463     sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
464     netlogon = os.path.join(sysvol, realm.lower(), "scripts")
465
466     setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix), 
467                smbconf, {
468             "NETBIOS_NAME": netbiosname,
469             "DOMAIN": domain,
470             "REALM": realm,
471             "SERVERROLE": serverrole,
472             "NETLOGONPATH": netlogon,
473             "SYSVOLPATH": sysvol,
474             "SIDGENERATOR_LINE": sid_generator_line,
475             "PRIVATEDIR_LINE": privatedir_line,
476             "LOCKDIR_LINE": lockdir_line,
477                         "POSIXEADB_LINE": posixeadb_line
478             })
479
480
481 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
482                         users_gid, wheel_gid):
483     """setup reasonable name mappings for sam names to unix names.
484
485     :param samdb: SamDB object.
486     :param idmap: IDmap db object.
487     :param sid: The domain sid.
488     :param domaindn: The domain DN.
489     :param root_uid: uid of the UNIX root user.
490     :param nobody_uid: uid of the UNIX nobody user.
491     :param users_gid: gid of the UNIX users group.
492     :param wheel_gid: gid of the UNIX wheel group."""
493
494     idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
495     idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
496     
497     idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
498     idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
499
500 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info, 
501                            provision_backend, names, schema,
502                            serverrole, 
503                            erase=False):
504     """Setup the partitions for the SAM database. 
505     
506     Alternatively, provision() may call this, and then populate the database.
507     
508     :note: This will wipe the Sam Database!
509     
510     :note: This function always removes the local SAM LDB file. The erase 
511         parameter controls whether to erase the existing data, which 
512         may not be stored locally but in LDAP.
513
514     """
515     assert session_info is not None
516
517     # We use options=["modules:"] to stop the modules loading - we
518     # just want to wipe and re-initialise the database, not start it up
519
520     try:
521         os.unlink(samdb_path)
522     except OSError:
523         pass
524
525     samdb = Ldb(url=samdb_path, session_info=session_info, 
526                 lp=lp, options=["modules:"])
527
528     ldap_backend_line = "# No LDAP backend"
529     if provision_backend.type is not "ldb":
530         ldap_backend_line = "ldapBackend: %s" % provision_backend.ldapi_uri
531
532     samdb.transaction_start()
533     try:
534         message("Setting up sam.ldb partitions and settings")
535         setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
536                 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(), 
537                 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
538                 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
539                 "LDAP_BACKEND_LINE": ldap_backend_line,
540         })
541
542         
543         setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
544                 "BACKEND_TYPE": provision_backend.type,
545                 "SERVER_ROLE": serverrole
546                 })
547
548         message("Setting up sam.ldb rootDSE")
549         setup_samdb_rootdse(samdb, setup_path, names)
550
551     except:
552         samdb.transaction_cancel()
553         raise
554
555     samdb.transaction_commit()
556
557         
558 def secretsdb_self_join(secretsdb, domain, 
559                         netbiosname, machinepass, domainsid=None,
560                         realm=None, dnsdomain=None,
561                         keytab_path=None, 
562                         key_version_number=1,
563                         secure_channel_type=SEC_CHAN_WKSTA):
564     """Add domain join-specific bits to a secrets database.
565     
566     :param secretsdb: Ldb Handle to the secrets database
567     :param machinepass: Machine password
568     """
569     attrs=["whenChanged",
570            "secret",
571            "priorSecret",
572            "priorChanged",
573            "krb5Keytab",
574            "privateKeytab"]
575     
576
577     msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
578     msg["secureChannelType"] = str(secure_channel_type)
579     msg["flatname"] = [domain]
580     msg["objectClass"] = ["top", "primaryDomain"]
581     if realm is not None:
582       if dnsdomain is None:
583         dnsdomain = realm.lower()
584       msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
585       msg["realm"] = realm
586       msg["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())
587       msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
588       msg["privateKeytab"] = ["secrets.keytab"]
589
590
591     msg["secret"] = [machinepass]
592     msg["samAccountName"] = ["%s$" % netbiosname]
593     msg["secureChannelType"] = [str(secure_channel_type)]
594     if domainsid is not None:
595         msg["objectSid"] = [ndr_pack(domainsid)]
596     
597     res = secretsdb.search(base="cn=Primary Domains", 
598                            attrs=attrs, 
599                            expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))), 
600                            scope=ldb.SCOPE_ONELEVEL)
601     
602     for del_msg in res:
603       if del_msg.dn is not msg.dn:
604         secretsdb.delete(del_msg.dn)
605
606     res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
607
608     if len(res) == 1:
609       msg["priorSecret"] = res[0]["secret"]
610       msg["priorWhenChanged"] = res[0]["whenChanged"]
611
612       if res["privateKeytab"] is not None:
613         msg["privateKeytab"] = res[0]["privateKeytab"]
614
615       if res["krb5Keytab"] is not None:
616         msg["krb5Keytab"] = res[0]["krb5Keytab"]
617
618       for el in msg:
619         el.set_flags(ldb.FLAG_MOD_REPLACE)
620         secretsdb.modify(msg)
621     else:
622       secretsdb.add(msg)
623
624
625 def secretsdb_setup_dns(secretsdb, setup_path, private_dir,
626                         realm, dnsdomain,
627                         dns_keytab_path, dnspass):
628     """Add DNS specific bits to a secrets database.
629     
630     :param secretsdb: Ldb Handle to the secrets database
631     :param setup_path: Setup path function
632     :param machinepass: Machine password
633     """
634     try:
635         os.unlink(os.path.join(private_dir, dns_keytab_path))
636     except OSError:
637         pass
638
639     setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), { 
640             "REALM": realm,
641             "DNSDOMAIN": dnsdomain,
642             "DNS_KEYTAB": dns_keytab_path,
643             "DNSPASS_B64": b64encode(dnspass),
644             })
645
646
647 def setup_secretsdb(path, setup_path, session_info, backend_credentials, lp):
648     """Setup the secrets database.
649
650     :param path: Path to the secrets database.
651     :param setup_path: Get the path to a setup file.
652     :param session_info: Session info.
653     :param credentials: Credentials
654     :param lp: Loadparm context
655     :return: LDB handle for the created secrets database
656     """
657     if os.path.exists(path):
658         os.unlink(path)
659     secrets_ldb = Ldb(path, session_info=session_info, 
660                       lp=lp)
661     secrets_ldb.erase()
662     secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
663     secrets_ldb = Ldb(path, session_info=session_info, 
664                       lp=lp)
665     secrets_ldb.transaction_start()
666     secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
667
668     if backend_credentials is not None and backend_credentials.authentication_requested():
669         if backend_credentials.get_bind_dn() is not None:
670             setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
671                     "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
672                     "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
673                     })
674         else:
675             setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
676                     "LDAPADMINUSER": backend_credentials.get_username(),
677                     "LDAPADMINREALM": backend_credentials.get_realm(),
678                     "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
679                     })
680
681     return secrets_ldb
682
683 def setup_privileges(path, setup_path, session_info, lp):
684     """Setup the privileges database.
685
686     :param path: Path to the privileges database.
687     :param setup_path: Get the path to a setup file.
688     :param session_info: Session info.
689     :param credentials: Credentials
690     :param lp: Loadparm context
691     :return: LDB handle for the created secrets database
692     """
693     if os.path.exists(path):
694         os.unlink(path)
695     privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
696     privilege_ldb.erase()
697     privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
698
699
700 def setup_registry(path, setup_path, session_info, lp):
701     """Setup the registry.
702     
703     :param path: Path to the registry database
704     :param setup_path: Function that returns the path to a setup.
705     :param session_info: Session information
706     :param credentials: Credentials
707     :param lp: Loadparm context
708     """
709     reg = registry.Registry()
710     hive = registry.open_ldb(path, session_info=session_info, 
711                          lp_ctx=lp)
712     reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
713     provision_reg = setup_path("provision.reg")
714     assert os.path.exists(provision_reg)
715     reg.diff_apply(provision_reg)
716
717
718 def setup_idmapdb(path, setup_path, session_info, lp):
719     """Setup the idmap database.
720
721     :param path: path to the idmap database
722     :param setup_path: Function that returns a path to a setup file
723     :param session_info: Session information
724     :param credentials: Credentials
725     :param lp: Loadparm context
726     """
727     if os.path.exists(path):
728         os.unlink(path)
729
730     idmap_ldb = IDmapDB(path, session_info=session_info,
731                         lp=lp)
732
733     idmap_ldb.erase()
734     idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
735     return idmap_ldb
736
737
738 def setup_samdb_rootdse(samdb, setup_path, names):
739     """Setup the SamDB rootdse.
740
741     :param samdb: Sam Database handle
742     :param setup_path: Obtain setup path
743     """
744     setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
745         "SCHEMADN": names.schemadn, 
746         "NETBIOSNAME": names.netbiosname,
747         "DNSDOMAIN": names.dnsdomain,
748         "REALM": names.realm,
749         "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
750         "DOMAINDN": names.domaindn,
751         "ROOTDN": names.rootdn,
752         "CONFIGDN": names.configdn,
753         "SERVERDN": names.serverdn,
754         })
755         
756
757 def setup_self_join(samdb, names,
758                     machinepass, dnspass, 
759                     domainsid, invocationid, setup_path,
760                     policyguid, policyguid_dc, domainControllerFunctionality,
761                     ntdsguid):
762     """Join a host to its own domain."""
763     assert isinstance(invocationid, str)
764     if ntdsguid is not None:
765         ntdsguid_line = "objectGUID: %s\n"%ntdsguid
766     else:
767         ntdsguid_line = ""
768     setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), { 
769               "CONFIGDN": names.configdn, 
770               "SCHEMADN": names.schemadn,
771               "DOMAINDN": names.domaindn,
772               "SERVERDN": names.serverdn,
773               "INVOCATIONID": invocationid,
774               "NETBIOSNAME": names.netbiosname,
775               "DEFAULTSITE": names.sitename,
776               "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
777               "MACHINEPASS_B64": b64encode(machinepass),
778               "REALM": names.realm,
779               "DOMAIN": names.domain,
780               "DOMAINSID": str(domainsid),
781               "DNSDOMAIN": names.dnsdomain,
782               "SAMBA_VERSION_STRING": version,
783               "NTDSGUID": ntdsguid_line,
784               "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
785
786     setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), { 
787               "POLICYGUID": policyguid,
788               "POLICYGUID_DC": policyguid_dc,
789               "DNSDOMAIN": names.dnsdomain,
790               "DOMAINSID": str(domainsid),
791               "DOMAINDN": names.domaindn})
792     
793     # add the NTDSGUID based SPNs
794     ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
795     names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
796                                      expression="", scope=ldb.SCOPE_BASE)
797     assert isinstance(names.ntdsguid, str)
798
799     # Setup fSMORoleOwner entries to point at the newly created DC entry
800     setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
801               "DOMAIN": names.domain,
802               "DNSDOMAIN": names.dnsdomain,
803               "DOMAINDN": names.domaindn,
804               "CONFIGDN": names.configdn,
805               "SCHEMADN": names.schemadn, 
806               "DEFAULTSITE": names.sitename,
807               "SERVERDN": names.serverdn,
808               "NETBIOSNAME": names.netbiosname,
809               "NTDSGUID": names.ntdsguid,
810               "DNSPASS_B64": b64encode(dnspass),
811               })
812
813
814 def setup_gpo(paths,names,samdb,policyguid,policyguid_dc,domainsid):
815     policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
816                                "{" + policyguid + "}")
817     os.makedirs(policy_path, 0755)
818     open(os.path.join(policy_path, "GPT.INI"), 'w').write(
819                       "[General]\r\nVersion=65543")
820     os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
821     os.makedirs(os.path.join(policy_path, "USER"), 0755)
822
823     policy_path_dc = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
824                                   "{" + policyguid_dc + "}")
825     os.makedirs(policy_path_dc, 0755)
826     open(os.path.join(policy_path_dc, "GPT.INI"), 'w').write(
827                       "[General]\r\nVersion=2")
828     os.makedirs(os.path.join(policy_path_dc, "MACHINE"), 0755)
829     os.makedirs(os.path.join(policy_path_dc, "USER"), 0755)
830
831
832 def setup_samdb(path, setup_path, session_info, provision_backend, lp, 
833                 names, message, 
834                 domainsid, domainguid, policyguid, policyguid_dc,
835                 fill, adminpass, krbtgtpass, 
836                 machinepass, invocationid, dnspass, ntdsguid,
837                 serverrole, dom_for_fun_level=None,
838                 schema=None):
839     """Setup a complete SAM Database.
840     
841     :note: This will wipe the main SAM database file!
842     """
843
844     # ATTENTION: Do NOT change these default values without discussion with the
845     # team and/or release manager. They have a big impact on the whole program!
846     domainControllerFunctionality = DS_DC_FUNCTION_2008
847
848     if dom_for_fun_level is None:
849         dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
850     if dom_for_fun_level < DS_DOMAIN_FUNCTION_2003:
851         message("You want to run SAMBA 4 on a domain and forest function level lower than Windows 2003 (Native). This is not recommended")
852
853     if dom_for_fun_level > domainControllerFunctionality:
854         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). This won't work!")
855
856     domainFunctionality = dom_for_fun_level
857     forestFunctionality = dom_for_fun_level
858
859     # Also wipes the database
860     setup_samdb_partitions(path, setup_path, message=message, lp=lp,
861                            provision_backend=provision_backend, session_info=session_info,
862                            names=names, 
863                            serverrole=serverrole, schema=schema)
864
865     if (schema == None):
866         schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
867
868     # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
869     samdb = Ldb(session_info=session_info, 
870                 credentials=provision_backend.credentials, lp=lp)
871
872     message("Pre-loading the Samba 4 and AD schema")
873
874     # Load the schema from the one we computed earlier
875     samdb.set_schema_from_ldb(schema.ldb)
876
877     # And now we can connect to the DB - the schema won't be loaded from the DB
878     samdb.connect(path)
879
880     if fill == FILL_DRS:
881         return samdb
882         
883     samdb.transaction_start()
884     try:
885         # Set the domain functionality levels onto the database.
886         # Various module (the password_hash module in particular) need
887         # to know what level of AD we are emulating.
888
889         # These will be fixed into the database via the database
890         # modifictions below, but we need them set from the start.
891         samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
892         samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
893         samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
894
895         samdb.set_domain_sid(str(domainsid))
896         samdb.set_invocation_id(invocationid)
897
898         message("Adding DomainDN: %s" % names.domaindn)
899
900 #impersonate domain admin
901         admin_session_info = admin_session(lp, str(domainsid))
902         samdb.set_session_info(admin_session_info)
903         if domainguid is not None:
904             domainguid_line = "objectGUID: %s\n-" % domainguid
905         else:
906             domainguid_line = ""
907
908         descr = b64encode(get_domain_descriptor(domainsid))
909         setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
910                 "DOMAINDN": names.domaindn,
911                 "DOMAINGUID": domainguid_line,
912                 "DESCRIPTOR": descr
913                 })
914
915
916         setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
917             "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
918             "DOMAINSID": str(domainsid),
919             "SCHEMADN": names.schemadn, 
920             "NETBIOSNAME": names.netbiosname,
921             "DEFAULTSITE": names.sitename,
922             "CONFIGDN": names.configdn,
923             "SERVERDN": names.serverdn,
924             "POLICYGUID": policyguid,
925             "DOMAINDN": names.domaindn,
926             "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
927             "SAMBA_VERSION_STRING": version
928             })
929
930         message("Adding configuration container")
931         descr = b64encode(get_config_descriptor(domainsid))
932         setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
933             "CONFIGDN": names.configdn, 
934             "DESCRIPTOR": descr,
935             })
936
937         # The LDIF here was created when the Schema object was constructed
938         message("Setting up sam.ldb schema")
939         samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
940         samdb.modify_ldif(schema.schema_dn_modify)
941         samdb.write_prefixes_from_schema()
942         samdb.add_ldif(schema.schema_data, controls=["relax:0"])
943         setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"), 
944                        {"SCHEMADN": names.schemadn})
945
946         message("Reopening sam.ldb with new schema")
947         samdb.transaction_commit()
948         samdb = Ldb(session_info=admin_session_info,
949                     credentials=provision_backend.credentials, lp=lp)
950         samdb.connect(path)
951         samdb.transaction_start()
952         samdb.set_invocation_id(invocationid)
953
954         message("Setting up sam.ldb configuration data")
955         setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
956             "CONFIGDN": names.configdn,
957             "NETBIOSNAME": names.netbiosname,
958             "DEFAULTSITE": names.sitename,
959             "DNSDOMAIN": names.dnsdomain,
960             "DOMAIN": names.domain,
961             "SCHEMADN": names.schemadn,
962             "DOMAINDN": names.domaindn,
963             "SERVERDN": names.serverdn,
964             "FOREST_FUNCTIONALALITY": str(forestFunctionality)
965             })
966
967         message("Setting up display specifiers")
968         display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
969         display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
970         check_all_substituted(display_specifiers_ldif)
971         samdb.add_ldif(display_specifiers_ldif)
972
973         message("Adding users container")
974         setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
975                 "DOMAINDN": names.domaindn})
976         message("Modifying users container")
977         setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
978                 "DOMAINDN": names.domaindn})
979         message("Adding computers container")
980         setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
981                 "DOMAINDN": names.domaindn})
982         message("Modifying computers container")
983         setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
984                 "DOMAINDN": names.domaindn})
985         message("Setting up sam.ldb data")
986         setup_add_ldif(samdb, setup_path("provision.ldif"), {
987             "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
988             "DOMAINDN": names.domaindn,
989             "NETBIOSNAME": names.netbiosname,
990             "DEFAULTSITE": names.sitename,
991             "CONFIGDN": names.configdn,
992             "SERVERDN": names.serverdn,
993             "POLICYGUID_DC": policyguid_dc
994             })
995
996         setup_modify_ldif(samdb, setup_path("provision_basedn_references.ldif"), {
997                 "DOMAINDN": names.domaindn})
998
999         setup_modify_ldif(samdb, setup_path("provision_configuration_references.ldif"), {
1000                 "CONFIGDN": names.configdn,
1001                 "SCHEMADN": names.schemadn})
1002         if fill == FILL_FULL:
1003             message("Setting up sam.ldb users and groups")
1004             setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1005                 "DOMAINDN": names.domaindn,
1006                 "DOMAINSID": str(domainsid),
1007                 "CONFIGDN": names.configdn,
1008                 "ADMINPASS_B64": b64encode(adminpass),
1009                 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1010                 })
1011
1012             message("Setting up self join")
1013             setup_self_join(samdb, names=names, invocationid=invocationid,
1014                             dnspass=dnspass,
1015                             machinepass=machinepass,
1016                             domainsid=domainsid, policyguid=policyguid,
1017                             policyguid_dc=policyguid_dc,
1018                             setup_path=setup_path,
1019                             domainControllerFunctionality=domainControllerFunctionality,
1020                             ntdsguid=ntdsguid)
1021
1022             ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
1023             names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1024                 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1025             assert isinstance(names.ntdsguid, str)
1026
1027     except:
1028         samdb.transaction_cancel()
1029         raise
1030
1031     samdb.transaction_commit()
1032     return samdb
1033
1034
1035 FILL_FULL = "FULL"
1036 FILL_NT4SYNC = "NT4SYNC"
1037 FILL_DRS = "DRS"
1038 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1039 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)"
1040
1041 def set_gpo_acl(path,acl,lp,domsid):
1042         setntacl(lp,path,acl,domsid)
1043         for root, dirs, files in os.walk(path, topdown=False):
1044                 for name in files:
1045                         setntacl(lp,os.path.join(root, name),acl,domsid)
1046                 for name in dirs:
1047                         setntacl(lp,os.path.join(root, name),acl,domsid)
1048
1049 def setsysvolacl(samdb,names,netlogon,sysvol,gid,domainsid,lp):
1050         canchown = 1
1051         try:
1052                 os.chown(sysvol,-1,gid)
1053         except:
1054                 canchown = 0
1055
1056         setntacl(lp,sysvol,SYSVOL_ACL,str(domainsid))
1057         for root, dirs, files in os.walk(sysvol, topdown=False):
1058                 for name in files:
1059                         if canchown:
1060                                 os.chown(os.path.join(root, name),-1,gid)
1061                         setntacl(lp,os.path.join(root, name),SYSVOL_ACL,str(domainsid))
1062                 for name in dirs:
1063                         if canchown:
1064                                 os.chown(os.path.join(root, name),-1,gid)
1065                         setntacl(lp,os.path.join(root, name),SYSVOL_ACL,str(domainsid))
1066
1067         # Set ACL for GPO
1068         policy_path = os.path.join(sysvol, names.dnsdomain, "Policies")
1069         set_gpo_acl(policy_path,dsacl2fsacl(POLICIES_ACL,str(domainsid)),lp,str(domainsid))
1070         res = samdb.search(base="CN=Policies,CN=System,%s"%(names.domaindn),
1071                                                 attrs=["cn","nTSecurityDescriptor"],
1072                                                 expression="", scope=ldb.SCOPE_ONELEVEL)
1073         for policy in res:
1074                 acl = ndr_unpack(security.descriptor,str(policy["nTSecurityDescriptor"])).as_sddl()
1075                 policy_path = os.path.join(sysvol, names.dnsdomain, "Policies",
1076                                                                          str(policy["cn"]))
1077                 set_gpo_acl(policy_path,dsacl2fsacl(acl,str(domainsid)),lp,str(domainsid))
1078
1079
1080
1081 def provision(setup_dir, message, session_info, 
1082               credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1083               realm=None, 
1084               rootdn=None, domaindn=None, schemadn=None, configdn=None, 
1085               serverdn=None,
1086               domain=None, hostname=None, hostip=None, hostip6=None, 
1087               domainsid=None, adminpass=None, ldapadminpass=None, 
1088               krbtgtpass=None, domainguid=None, 
1089               policyguid=None, policyguid_dc=None, invocationid=None,
1090               machinepass=None, ntdsguid=None,
1091               dnspass=None, root=None, nobody=None, users=None, 
1092               wheel=None, backup=None, aci=None, serverrole=None,
1093               dom_for_fun_level=None,
1094               ldap_backend_extra_port=None, backend_type=None,
1095               sitename=None,
1096               ol_mmr_urls=None, ol_olc=None, 
1097               setup_ds_path=None, slapd_path=None, nosync=False,
1098               ldap_dryrun_mode=False,useeadb=False):
1099     """Provision samba4
1100     
1101     :note: caution, this wipes all existing data!
1102     """
1103
1104     def setup_path(file):
1105       return os.path.join(setup_dir, file)
1106
1107     if domainsid is None:
1108       domainsid = security.random_sid()
1109     else:
1110       domainsid = security.dom_sid(domainsid)
1111
1112     # create/adapt the group policy GUIDs
1113     if policyguid is None:
1114         policyguid = str(uuid.uuid4())
1115     policyguid = policyguid.upper()
1116     if policyguid_dc is None:
1117         policyguid_dc = str(uuid.uuid4())
1118     policyguid_dc = policyguid_dc.upper()
1119
1120     if adminpass is None:
1121         adminpass = glue.generate_random_password(12, 32)
1122     if krbtgtpass is None:
1123         krbtgtpass = glue.generate_random_password(128, 255)
1124     if machinepass is None:
1125         machinepass  = glue.generate_random_password(128, 255)
1126     if dnspass is None:
1127         dnspass = glue.generate_random_password(128, 255)
1128     if ldapadminpass is None:
1129         #Make a new, random password between Samba and it's LDAP server
1130         ldapadminpass=glue.generate_random_password(128, 255)
1131
1132     if backend_type is None:
1133         backend_type = "ldb"
1134
1135     sid_generator = "internal"
1136     if backend_type == "fedora-ds":
1137         sid_generator = "backend"
1138
1139     root_uid = findnss_uid([root or "root"])
1140     nobody_uid = findnss_uid([nobody or "nobody"])
1141     users_gid = findnss_gid([users or "users"])
1142     if wheel is None:
1143         wheel_gid = findnss_gid(["wheel", "adm"])
1144     else:
1145         wheel_gid = findnss_gid([wheel])
1146     try:
1147         bind_gid = findnss_gid(["bind", "named"])
1148     except KeyError:
1149         bind_gid = None
1150
1151     if targetdir is not None:
1152         if (not os.path.exists(os.path.join(targetdir, "etc"))):
1153             os.makedirs(os.path.join(targetdir, "etc"))
1154         smbconf = os.path.join(targetdir, "etc", "smb.conf")
1155     elif smbconf is None:
1156         smbconf = param.default_path()
1157
1158     # only install a new smb.conf if there isn't one there already
1159     if os.path.exists(smbconf):
1160         # if Samba Team members can't figure out the weird errors
1161         # loading an empty smb.conf gives, then we need to be smarter.
1162         # Pretend it just didn't exist --abartlet
1163         data = open(smbconf, 'r').read()
1164         data = data.lstrip()
1165         if data is None or data == "":
1166             make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, 
1167                          targetdir, sid_generator, useeadb)
1168     else: 
1169         make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, 
1170                      targetdir, sid_generator, useeadb)
1171
1172     lp = param.LoadParm()
1173     lp.load(smbconf)
1174
1175     names = guess_names(lp=lp, hostname=hostname, domain=domain,
1176                         dnsdomain=realm, serverrole=serverrole,
1177                         domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1178                         serverdn=serverdn, sitename=sitename)
1179
1180     paths = provision_paths_from_lp(lp, names.dnsdomain)
1181
1182     paths.bind_gid = bind_gid
1183
1184     if hostip is None:
1185         try:
1186             for ip in socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP):
1187                 if hostip is None:
1188                     hostip = ip[-1][0]
1189                 if hostip.startswith('127.0.0.') and (not ip[-1][0].startswith('127.0.0.')):
1190                     hostip = ip[-1][0]
1191         except socket.gaierror, (socket.EAI_NODATA, msg):
1192             hostip = None
1193
1194     if hostip6 is None:
1195         try:
1196             for ip in socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP):
1197                 if hostip6 is None:
1198                     hostip6 = ip[-1][0]
1199                 if hostip6 == '::1' and ip[-1][0] != '::1':
1200                     hostip6 = ip[-1][0]
1201         except socket.gaierror, (socket.EAI_NODATA, msg): 
1202             hostip6 = None
1203
1204     if serverrole is None:
1205         serverrole = lp.get("server role")
1206
1207     assert serverrole in ("domain controller", "member server", "standalone")
1208     if invocationid is None:
1209         invocationid = str(uuid.uuid4())
1210
1211     if not os.path.exists(paths.private_dir):
1212         os.mkdir(paths.private_dir)
1213     if not os.path.exists(os.path.join(paths.private_dir,"tls")):
1214         os.mkdir(os.path.join(paths.private_dir,"tls"))
1215
1216     ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1217  
1218     schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
1219     
1220     if backend_type == "ldb":
1221         provision_backend = LDBBackend(backend_type,
1222                                          paths=paths, setup_path=setup_path,
1223                                          lp=lp, credentials=credentials, 
1224                                          names=names,
1225                                          message=message)
1226     elif backend_type == "existing":
1227         provision_backend = ExistingBackend(backend_type,
1228                                          paths=paths, setup_path=setup_path,
1229                                          lp=lp, credentials=credentials, 
1230                                          names=names,
1231                                          message=message,
1232                                          ldapi_url=ldapi_url)
1233     elif backend_type == "fedora-ds":
1234         provision_backend = FDSBackend(backend_type,
1235                                          paths=paths, setup_path=setup_path,
1236                                          lp=lp, credentials=credentials, 
1237                                          names=names,
1238                                          message=message,
1239                                          domainsid=domainsid,
1240                                          schema=schema,
1241                                          hostname=hostname,
1242                                          ldapadminpass=ldapadminpass,
1243                                          slapd_path=slapd_path,
1244                                          ldap_backend_extra_port=ldap_backend_extra_port,
1245                                          ldap_dryrun_mode=ldap_dryrun_mode,
1246                                          root=root,
1247                                          setup_ds_path=setup_ds_path)
1248     elif backend_type == "openldap":
1249         provision_backend = OpenLDAPBackend(backend_type,
1250                                          paths=paths, setup_path=setup_path,
1251                                          lp=lp, credentials=credentials, 
1252                                          names=names,
1253                                          message=message,
1254                                          domainsid=domainsid,
1255                                          schema=schema,
1256                                          hostname=hostname,
1257                                          ldapadminpass=ldapadminpass,
1258                                          slapd_path=slapd_path,
1259                                          ldap_backend_extra_port=ldap_backend_extra_port,
1260                                          ldap_dryrun_mode=ldap_dryrun_mode,
1261                                          ol_mmr_urls=ol_mmr_urls, 
1262                                          nosync=nosync)
1263     else:
1264         raise ProvisioningError("Unknown LDAP backend type selected")
1265
1266     provision_backend.init()
1267     provision_backend.start()
1268
1269     # only install a new shares config db if there is none
1270     if not os.path.exists(paths.shareconf):
1271         message("Setting up share.ldb")
1272         share_ldb = Ldb(paths.shareconf, session_info=session_info, 
1273                         lp=lp)
1274         share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1275
1276      
1277     message("Setting up secrets.ldb")
1278     secrets_ldb = setup_secretsdb(paths.secrets, setup_path, 
1279                                   session_info=session_info, 
1280                                   backend_credentials=provision_backend.secrets_credentials, lp=lp)
1281
1282     message("Setting up the registry")
1283     setup_registry(paths.hklm, setup_path, session_info, 
1284                    lp=lp)
1285
1286     message("Setting up the privileges database")
1287     setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1288
1289     message("Setting up idmap db")
1290     idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1291                           lp=lp)
1292
1293     message("Setting up SAM db")
1294     samdb = setup_samdb(paths.samdb, setup_path, session_info, 
1295                         provision_backend, lp, names,
1296                         message, 
1297                         domainsid=domainsid, 
1298                         schema=schema, domainguid=domainguid,
1299                         policyguid=policyguid, policyguid_dc=policyguid_dc,
1300                         fill=samdb_fill, 
1301                         adminpass=adminpass, krbtgtpass=krbtgtpass,
1302                         invocationid=invocationid, 
1303                         machinepass=machinepass, dnspass=dnspass, 
1304                         ntdsguid=ntdsguid, serverrole=serverrole,
1305                         dom_for_fun_level=dom_for_fun_level)
1306
1307     if serverrole == "domain controller":
1308         if paths.netlogon is None:
1309             message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1310             message("Please either remove %s or see the template at %s" % 
1311                     ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1312             assert(paths.netlogon is not None)
1313
1314         if paths.sysvol is None:
1315             message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1316             message("Please either remove %s or see the template at %s" % 
1317                     (paths.smbconf, setup_path("provision.smb.conf.dc")))
1318             assert(paths.sysvol is not None)            
1319             
1320
1321         if not os.path.isdir(paths.netlogon):
1322             os.makedirs(paths.netlogon, 0755)
1323
1324     if samdb_fill == FILL_FULL:
1325         setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1326                             root_uid=root_uid, nobody_uid=nobody_uid,
1327                             users_gid=users_gid, wheel_gid=wheel_gid)
1328
1329         if serverrole == "domain controller":
1330             # Set up group policies (domain policy and domain controller policy)
1331             setup_gpo(paths,names,samdb,policyguid,policyguid_dc,domainsid)
1332             setsysvolacl(samdb,names,paths.netlogon,paths.sysvol,wheel_gid,domainsid,lp)
1333
1334         message("Setting up sam.ldb rootDSE marking as synchronized")
1335         setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1336
1337         secretsdb_self_join(secrets_ldb, domain=names.domain,
1338                             realm=names.realm,
1339                             dnsdomain=names.dnsdomain,
1340                             netbiosname=names.netbiosname,
1341                             domainsid=domainsid, 
1342                             machinepass=machinepass,
1343                             secure_channel_type=SEC_CHAN_BDC)
1344
1345         if serverrole == "domain controller":
1346             secretsdb_setup_dns(secrets_ldb, setup_path,
1347                                 paths.private_dir,
1348                                 realm=names.realm, dnsdomain=names.dnsdomain,
1349                                 dns_keytab_path=paths.dns_keytab,
1350                                 dnspass=dnspass)
1351
1352             domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1353             assert isinstance(domainguid, str)
1354
1355             # Only make a zone file on the first DC, it should be replicated
1356             # with DNS replication
1357             create_zone_file(lp, message, paths, targetdir, setup_path, dnsdomain=names.dnsdomain,
1358                              hostip=hostip,
1359                              hostip6=hostip6, hostname=names.hostname,
1360                              realm=names.realm,
1361                              domainguid=domainguid, ntdsguid=names.ntdsguid)
1362
1363             create_named_conf(paths, setup_path, realm=names.realm,
1364                               dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1365
1366             create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1367                               dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1368                               keytab_name=paths.dns_keytab)
1369             message("See %s for an example configuration include file for BIND" % paths.namedconf)
1370             message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1371
1372             create_krb5_conf(paths.krb5conf, setup_path,
1373                              dnsdomain=names.dnsdomain, hostname=names.hostname,
1374                              realm=names.realm)
1375             message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1376
1377     if serverrole == "domain controller":
1378         create_dns_update_list(lp, message, paths, setup_path)
1379
1380     provision_backend.post_setup()
1381     provision_backend.shutdown()
1382     
1383     create_phpldapadmin_config(paths.phpldapadminconfig, setup_path, 
1384                                ldapi_url)
1385
1386     #Now commit the secrets.ldb to disk
1387     secrets_ldb.transaction_commit()
1388
1389     # the commit creates the dns.keytab, now chown it
1390     dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1391     if (os.path.isfile(dns_keytab_path) and paths.bind_gid is not None):
1392         try:
1393             os.chmod(dns_keytab_path, 0640)
1394             os.chown(dns_keytab_path, -1, paths.bind_gid)
1395         except OSError:
1396             message("Failed to chown %s to bind gid %u" % (dns_keytab_path, paths.bind_gid))
1397
1398
1399     message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1400
1401     message("Once the above files are installed, your Samba4 server will be ready to use")
1402     message("Server Role:           %s" % serverrole)
1403     message("Hostname:              %s" % names.hostname)
1404     message("NetBIOS Domain:        %s" % names.domain)
1405     message("DNS Domain:            %s" % names.dnsdomain)
1406     message("DOMAIN SID:            %s" % str(domainsid))
1407     if samdb_fill == FILL_FULL:
1408         message("Admin password:        %s" % adminpass)
1409     if provision_backend.type is not "ldb":
1410         if provision_backend.credentials.get_bind_dn() is not None:
1411             message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1412         else:
1413             message("LDAP Admin User:       %s" % provision_backend.credentials.get_username())
1414
1415         message("LDAP Admin Password:   %s" % provision_backend.credentials.get_password())
1416
1417         if provision_backend.slapd_command_escaped is not None:
1418             # now display slapd_command_file.txt to show how slapd must be started next time
1419             message("Use later the following commandline to start slapd, then Samba:")
1420             message(provision_backend.slapd_command_escaped)
1421             message("This slapd-Commandline is also stored under: " + provision_backend.ldapdir + "/ldap_backend_startup.sh")
1422
1423
1424     result = ProvisionResult()
1425     result.domaindn = domaindn
1426     result.paths = paths
1427     result.lp = lp
1428     result.samdb = samdb
1429     return result
1430
1431
1432
1433 def provision_become_dc(setup_dir=None,
1434                         smbconf=None, targetdir=None, realm=None, 
1435                         rootdn=None, domaindn=None, schemadn=None,
1436                         configdn=None, serverdn=None,
1437                         domain=None, hostname=None, domainsid=None, 
1438                         adminpass=None, krbtgtpass=None, domainguid=None, 
1439                         policyguid=None, policyguid_dc=None, invocationid=None,
1440                         machinepass=None, 
1441                         dnspass=None, root=None, nobody=None, users=None, 
1442                         wheel=None, backup=None, serverrole=None, 
1443                         ldap_backend=None, ldap_backend_type=None,
1444                         sitename=None, debuglevel=1):
1445
1446     def message(text):
1447         """print a message if quiet is not set."""
1448         print text
1449
1450     glue.set_debug_level(debuglevel)
1451
1452     return provision(setup_dir, message, system_session(), None,
1453               smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1454               realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1455               configdn=configdn, serverdn=serverdn, domain=domain,
1456               hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1457               machinepass=machinepass, serverrole="domain controller",
1458               sitename=sitename)
1459
1460
1461 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1462     """Create a PHP LDAP admin configuration file.
1463
1464     :param path: Path to write the configuration to.
1465     :param setup_path: Function to generate setup paths.
1466     """
1467     setup_file(setup_path("phpldapadmin-config.php"), path, 
1468             {"S4_LDAPI_URI": ldapi_uri})
1469
1470
1471 def create_zone_file(lp, message, paths, targetdir, setup_path, dnsdomain,
1472                      hostip, hostip6, hostname, realm, domainguid,
1473                      ntdsguid):
1474     """Write out a DNS zone file, from the info in the current database.
1475
1476     :param paths: paths object
1477     :param setup_path: Setup path function.
1478     :param dnsdomain: DNS Domain name
1479     :param domaindn: DN of the Domain
1480     :param hostip: Local IPv4 IP
1481     :param hostip6: Local IPv6 IP
1482     :param hostname: Local hostname
1483     :param realm: Realm name
1484     :param domainguid: GUID of the domain.
1485     :param ntdsguid: GUID of the hosts nTDSDSA record.
1486     """
1487     assert isinstance(domainguid, str)
1488
1489     if hostip6 is not None:
1490         hostip6_base_line = "            IN AAAA    " + hostip6
1491         hostip6_host_line = hostname + "        IN AAAA    " + hostip6
1492     else:
1493         hostip6_base_line = ""
1494         hostip6_host_line = ""
1495
1496     if hostip is not None:
1497         hostip_base_line = "            IN A    " + hostip
1498         hostip_host_line = hostname + "        IN A    " + hostip
1499     else:
1500         hostip_base_line = ""
1501         hostip_host_line = ""
1502
1503     dns_dir = os.path.dirname(paths.dns)
1504
1505     try:
1506         shutil.rmtree(dns_dir, True)
1507     except OSError:
1508         pass
1509
1510     os.mkdir(dns_dir, 0775)
1511
1512     # we need to freeze the zone while we update the contents
1513     if targetdir is None:
1514         rndc = ' '.join(lp.get("rndc command"))
1515         os.system(rndc + " freeze " + lp.get("realm"))
1516
1517     setup_file(setup_path("provision.zone"), paths.dns, {
1518             "HOSTNAME": hostname,
1519             "DNSDOMAIN": dnsdomain,
1520             "REALM": realm,
1521             "HOSTIP_BASE_LINE": hostip_base_line,
1522             "HOSTIP_HOST_LINE": hostip_host_line,
1523             "DOMAINGUID": domainguid,
1524             "DATESTRING": time.strftime("%Y%m%d%H"),
1525             "DEFAULTSITE": DEFAULTSITE,
1526             "NTDSGUID": ntdsguid,
1527             "HOSTIP6_BASE_LINE": hostip6_base_line,
1528             "HOSTIP6_HOST_LINE": hostip6_host_line,
1529         })
1530
1531     # note that we use no variable substitution on this file
1532     # the substitution is done at runtime by samba_dnsupdate
1533     setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1534
1535     if paths.bind_gid is not None:
1536         try:
1537             os.chown(dns_dir, -1, paths.bind_gid)
1538             os.chown(paths.dns, -1, paths.bind_gid)
1539             # chmod needed to cope with umask
1540             os.chmod(dns_dir, 0775)
1541             os.chmod(paths.dns, 0664)
1542         except OSError:
1543             message("Failed to chown %s to bind gid %u" % (dns_dir, paths.bind_gid))
1544
1545     if targetdir is None:
1546         os.system(rndc + " unfreeze " + lp.get("realm"))
1547
1548
1549 def create_dns_update_list(lp, message, paths, setup_path):
1550     """Write out a dns_update_list file"""
1551     # note that we use no variable substitution on this file
1552     # the substitution is done at runtime by samba_dnsupdate
1553     setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1554
1555
1556 def create_named_conf(paths, setup_path, realm, dnsdomain,
1557                       private_dir):
1558     """Write out a file containing zone statements suitable for inclusion in a
1559     named.conf file (including GSS-TSIG configuration).
1560     
1561     :param paths: all paths
1562     :param setup_path: Setup path function.
1563     :param realm: Realm name
1564     :param dnsdomain: DNS Domain name
1565     :param private_dir: Path to private directory
1566     :param keytab_name: File name of DNS keytab file
1567     """
1568
1569     setup_file(setup_path("named.conf"), paths.namedconf, {
1570             "DNSDOMAIN": dnsdomain,
1571             "REALM": realm,
1572             "ZONE_FILE": paths.dns,
1573             "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1574             "NAMED_CONF": paths.namedconf,
1575             "NAMED_CONF_UPDATE": paths.namedconf_update
1576             })
1577
1578     setup_file(setup_path("named.conf.update"), paths.namedconf_update)
1579
1580 def create_named_txt(path, setup_path, realm, dnsdomain,
1581                       private_dir, keytab_name):
1582     """Write out a file containing zone statements suitable for inclusion in a
1583     named.conf file (including GSS-TSIG configuration).
1584     
1585     :param path: Path of the new named.conf file.
1586     :param setup_path: Setup path function.
1587     :param realm: Realm name
1588     :param dnsdomain: DNS Domain name
1589     :param private_dir: Path to private directory
1590     :param keytab_name: File name of DNS keytab file
1591     """
1592
1593     setup_file(setup_path("named.txt"), path, {
1594             "DNSDOMAIN": dnsdomain,
1595             "REALM": realm,
1596             "DNS_KEYTAB": keytab_name,
1597             "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1598             "PRIVATE_DIR": private_dir
1599         })
1600
1601 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1602     """Write out a file containing zone statements suitable for inclusion in a
1603     named.conf file (including GSS-TSIG configuration).
1604     
1605     :param path: Path of the new named.conf file.
1606     :param setup_path: Setup path function.
1607     :param dnsdomain: DNS Domain name
1608     :param hostname: Local hostname
1609     :param realm: Realm name
1610     """
1611
1612     setup_file(setup_path("krb5.conf"), path, {
1613             "DNSDOMAIN": dnsdomain,
1614             "HOSTNAME": hostname,
1615             "REALM": realm,
1616         })
1617
1618