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