s4:provision.py - cosmetic output correction
[kai/samba-autobuild/.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, machinepass, domainsid=None,
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     if domainsid is not None:
611         msg["objectSid"] = [ndr_pack(domainsid)]
612     
613     res = secretsdb.search(base="cn=Primary Domains", 
614                            attrs=attrs, 
615                            expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))), 
616                            scope=SCOPE_ONELEVEL)
617     
618     for del_msg in res:
619       if del_msg.dn is not msg.dn:
620         secretsdb.delete(del_msg.dn)
621
622     res = secretsdb.search(base=msg.dn, attrs=attrs, scope=SCOPE_BASE)
623
624     if len(res) == 1:
625       msg["priorSecret"] = res[0]["secret"]
626       msg["priorWhenChanged"] = res[0]["whenChanged"]
627
628       if res["privateKeytab"] is not None:
629         msg["privateKeytab"] = res[0]["privateKeytab"]
630
631       if res["krb5Keytab"] is not None:
632         msg["krb5Keytab"] = res[0]["krb5Keytab"]
633
634       for el in msg:
635         el.set_flags(ldb.FLAG_MOD_REPLACE)
636         secretsdb.modify(msg)
637     else:
638       secretsdb.add(msg)
639
640
641 def secretsdb_setup_dns(secretsdb, setup_path, realm, dnsdomain, 
642                         dns_keytab_path, dnspass):
643     """Add DNS specific bits to a secrets database.
644     
645     :param secretsdb: Ldb Handle to the secrets database
646     :param setup_path: Setup path function
647     :param machinepass: Machine password
648     """
649     setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), { 
650             "REALM": realm,
651             "DNSDOMAIN": dnsdomain,
652             "DNS_KEYTAB": dns_keytab_path,
653             "DNSPASS_B64": b64encode(dnspass),
654             })
655
656
657 def setup_secretsdb(path, setup_path, session_info, backend_credentials, lp):
658     """Setup the secrets database.
659
660     :param path: Path to the secrets database.
661     :param setup_path: Get the path to a setup file.
662     :param session_info: Session info.
663     :param credentials: Credentials
664     :param lp: Loadparm context
665     :return: LDB handle for the created secrets database
666     """
667     if os.path.exists(path):
668         os.unlink(path)
669     secrets_ldb = Ldb(path, session_info=session_info, 
670                       lp=lp)
671     secrets_ldb.erase()
672     secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
673     secrets_ldb = Ldb(path, session_info=session_info, 
674                       lp=lp)
675     secrets_ldb.transaction_start()
676     secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
677
678     if backend_credentials is not None and backend_credentials.authentication_requested():
679         if backend_credentials.get_bind_dn() is not None:
680             setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
681                     "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
682                     "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
683                     })
684         else:
685             setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
686                     "LDAPADMINUSER": backend_credentials.get_username(),
687                     "LDAPADMINREALM": backend_credentials.get_realm(),
688                     "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
689                     })
690
691     return secrets_ldb
692
693 def setup_privileges(path, setup_path, session_info, lp):
694     """Setup the privileges database.
695
696     :param path: Path to the privileges database.
697     :param setup_path: Get the path to a setup file.
698     :param session_info: Session info.
699     :param credentials: Credentials
700     :param lp: Loadparm context
701     :return: LDB handle for the created secrets database
702     """
703     if os.path.exists(path):
704         os.unlink(path)
705     privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
706     privilege_ldb.erase()
707     privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
708
709
710 def setup_registry(path, setup_path, session_info, lp):
711     """Setup the registry.
712     
713     :param path: Path to the registry database
714     :param setup_path: Function that returns the path to a setup.
715     :param session_info: Session information
716     :param credentials: Credentials
717     :param lp: Loadparm context
718     """
719     reg = registry.Registry()
720     hive = registry.open_ldb(path, session_info=session_info, 
721                          lp_ctx=lp)
722     reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
723     provision_reg = setup_path("provision.reg")
724     assert os.path.exists(provision_reg)
725     reg.diff_apply(provision_reg)
726
727
728 def setup_idmapdb(path, setup_path, session_info, lp):
729     """Setup the idmap database.
730
731     :param path: path to the idmap database
732     :param setup_path: Function that returns a path to a setup file
733     :param session_info: Session information
734     :param credentials: Credentials
735     :param lp: Loadparm context
736     """
737     if os.path.exists(path):
738         os.unlink(path)
739
740     idmap_ldb = IDmapDB(path, session_info=session_info,
741                         lp=lp)
742
743     idmap_ldb.erase()
744     idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
745     return idmap_ldb
746
747
748 def setup_samdb_rootdse(samdb, setup_path, names):
749     """Setup the SamDB rootdse.
750
751     :param samdb: Sam Database handle
752     :param setup_path: Obtain setup path
753     """
754     setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
755         "SCHEMADN": names.schemadn, 
756         "NETBIOSNAME": names.netbiosname,
757         "DNSDOMAIN": names.dnsdomain,
758         "REALM": names.realm,
759         "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
760         "DOMAINDN": names.domaindn,
761         "ROOTDN": names.rootdn,
762         "CONFIGDN": names.configdn,
763         "SERVERDN": names.serverdn,
764         })
765         
766
767 def setup_self_join(samdb, names,
768                     machinepass, dnspass, 
769                     domainsid, invocationid, setup_path,
770                     policyguid, policyguid_dc, domainControllerFunctionality,
771                     ntdsguid):
772     """Join a host to its own domain."""
773     assert isinstance(invocationid, str)
774     if ntdsguid is not None:
775         ntdsguid_line = "objectGUID: %s\n"%ntdsguid
776     else:
777         ntdsguid_line = ""
778     setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), { 
779               "CONFIGDN": names.configdn, 
780               "SCHEMADN": names.schemadn,
781               "DOMAINDN": names.domaindn,
782               "SERVERDN": names.serverdn,
783               "INVOCATIONID": invocationid,
784               "NETBIOSNAME": names.netbiosname,
785               "DEFAULTSITE": names.sitename,
786               "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
787               "MACHINEPASS_B64": b64encode(machinepass),
788               "DNSPASS_B64": b64encode(dnspass),
789               "REALM": names.realm,
790               "DOMAIN": names.domain,
791               "DNSDOMAIN": names.dnsdomain,
792               "SAMBA_VERSION_STRING": version,
793               "NTDSGUID": ntdsguid_line,
794               "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
795
796     setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), { 
797               "POLICYGUID": policyguid,
798               "POLICYGUID_DC": policyguid_dc,
799               "DNSDOMAIN": names.dnsdomain,
800               "DOMAINSID": str(domainsid),
801               "DOMAINDN": names.domaindn})
802     
803     # add the NTDSGUID based SPNs
804     ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
805     names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
806                                      expression="", scope=SCOPE_BASE)
807     assert isinstance(names.ntdsguid, str)
808
809     # Setup fSMORoleOwner entries to point at the newly created DC entry
810     setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
811               "DOMAIN": names.domain,
812               "DNSDOMAIN": names.dnsdomain,
813               "DOMAINDN": names.domaindn,
814               "CONFIGDN": names.configdn,
815               "SCHEMADN": names.schemadn, 
816               "DEFAULTSITE": names.sitename,
817               "SERVERDN": names.serverdn,
818               "NETBIOSNAME": names.netbiosname,
819               "NTDSGUID": names.ntdsguid
820               })
821
822
823 def setup_samdb(path, setup_path, session_info, provision_backend, lp, 
824                 names, message, 
825                 domainsid, domainguid, policyguid, policyguid_dc,
826                 fill, adminpass, krbtgtpass, 
827                 machinepass, invocationid, dnspass, ntdsguid,
828                 serverrole, dom_for_fun_level=None,
829                 schema=None):
830     """Setup a complete SAM Database.
831     
832     :note: This will wipe the main SAM database file!
833     """
834
835     # ATTENTION: Do NOT change these default values without discussion with the
836     # team and/or release manager. They have a big impact on the whole program!
837     domainControllerFunctionality = DS_DC_FUNCTION_2008
838
839     if dom_for_fun_level is None:
840         dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
841     if dom_for_fun_level < DS_DOMAIN_FUNCTION_2003:
842         raise ProvisioningError("You want to run SAMBA 4 on a domain and forest function level lower than Windows 2003 (Native). This isn't supported!")
843
844     if dom_for_fun_level > domainControllerFunctionality:
845         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!")
846
847     domainFunctionality = dom_for_fun_level
848     forestFunctionality = dom_for_fun_level
849
850     # Also wipes the database
851     setup_samdb_partitions(path, setup_path, message=message, lp=lp,
852                            provision_backend=provision_backend, session_info=session_info,
853                            names=names, 
854                            serverrole=serverrole, schema=schema)
855
856     if (schema == None):
857         schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
858
859     # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
860     samdb = Ldb(session_info=session_info, 
861                 credentials=provision_backend.credentials, lp=lp)
862
863     message("Pre-loading the Samba 4 and AD schema")
864
865     # Load the schema from the one we computed earlier
866     samdb.set_schema_from_ldb(schema.ldb)
867
868     # And now we can connect to the DB - the schema won't be loaded from the DB
869     samdb.connect(path)
870
871     if fill == FILL_DRS:
872         return samdb
873         
874     samdb.transaction_start()
875     try:
876         # Set the domain functionality levels onto the database.
877         # Various module (the password_hash module in particular) need
878         # to know what level of AD we are emulating.
879
880         # These will be fixed into the database via the database
881         # modifictions below, but we need them set from the start.
882         samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
883         samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
884         samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
885
886         samdb.set_domain_sid(str(domainsid))
887         if serverrole == "domain controller":
888             samdb.set_invocation_id(invocationid)
889
890         message("Adding DomainDN: %s" % names.domaindn)
891
892 #impersonate domain admin
893         admin_session_info = admin_session(lp, str(domainsid))
894         samdb.set_session_info(admin_session_info)
895         if domainguid is not None:
896             domainguid_line = "objectGUID: %s\n-" % domainguid
897         else:
898             domainguid_line = ""
899
900         descr = get_domain_descriptor(domainsid)
901         setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
902                 "DOMAINDN": names.domaindn,
903                 "DOMAINGUID": domainguid_line,
904                 "DESCRIPTOR": descr
905                 })
906
907
908         setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
909             "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
910             "DOMAINSID": str(domainsid),
911             "SCHEMADN": names.schemadn, 
912             "NETBIOSNAME": names.netbiosname,
913             "DEFAULTSITE": names.sitename,
914             "CONFIGDN": names.configdn,
915             "SERVERDN": names.serverdn,
916             "POLICYGUID": policyguid,
917             "DOMAINDN": names.domaindn,
918             "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
919             "SAMBA_VERSION_STRING": version
920             })
921
922         message("Adding configuration container")
923         descr = get_config_descriptor(domainsid);
924         setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
925             "CONFIGDN": names.configdn, 
926             "DESCRIPTOR": descr,
927             })
928
929         # The LDIF here was created when the Schema object was constructed
930         message("Setting up sam.ldb schema")
931         samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
932         samdb.modify_ldif(schema.schema_dn_modify)
933         samdb.write_prefixes_from_schema()
934         samdb.add_ldif(schema.schema_data, controls=["relax:0"])
935         setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"), 
936                        {"SCHEMADN": names.schemadn})
937
938         message("Setting up sam.ldb configuration data")
939         setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
940             "CONFIGDN": names.configdn,
941             "NETBIOSNAME": names.netbiosname,
942             "DEFAULTSITE": names.sitename,
943             "DNSDOMAIN": names.dnsdomain,
944             "DOMAIN": names.domain,
945             "SCHEMADN": names.schemadn,
946             "DOMAINDN": names.domaindn,
947             "SERVERDN": names.serverdn,
948             "FOREST_FUNCTIONALALITY": str(forestFunctionality)
949             })
950
951         message("Setting up display specifiers")
952         display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
953         display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
954         check_all_substituted(display_specifiers_ldif)
955         samdb.add_ldif(display_specifiers_ldif)
956
957         message("Adding users container")
958         setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
959                 "DOMAINDN": names.domaindn})
960         message("Modifying users container")
961         setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
962                 "DOMAINDN": names.domaindn})
963         message("Adding computers container")
964         setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
965                 "DOMAINDN": names.domaindn})
966         message("Modifying computers container")
967         setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
968                 "DOMAINDN": names.domaindn})
969         message("Setting up sam.ldb data")
970         setup_add_ldif(samdb, setup_path("provision.ldif"), {
971             "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
972             "DOMAINDN": names.domaindn,
973             "NETBIOSNAME": names.netbiosname,
974             "DEFAULTSITE": names.sitename,
975             "CONFIGDN": names.configdn,
976             "SERVERDN": names.serverdn,
977             "POLICYGUID_DC": policyguid_dc
978             })
979
980         setup_modify_ldif(samdb, setup_path("provision_basedn_references.ldif"), {
981                 "DOMAINDN": names.domaindn})
982
983         setup_modify_ldif(samdb, setup_path("provision_configuration_references.ldif"), {
984                 "CONFIGDN": names.configdn,
985                 "SCHEMADN": names.schemadn})
986         if fill == FILL_FULL:
987             message("Setting up sam.ldb users and groups")
988             setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
989                 "DOMAINDN": names.domaindn,
990                 "DOMAINSID": str(domainsid),
991                 "CONFIGDN": names.configdn,
992                 "ADMINPASS_B64": b64encode(adminpass),
993                 "KRBTGTPASS_B64": b64encode(krbtgtpass),
994                 })
995
996             if serverrole == "domain controller":
997                 message("Setting up self join")
998                 setup_self_join(samdb, names=names, invocationid=invocationid, 
999                                 dnspass=dnspass,  
1000                                 machinepass=machinepass, 
1001                                 domainsid=domainsid, policyguid=policyguid,
1002                                 policyguid_dc=policyguid_dc,
1003                                 setup_path=setup_path,
1004                                 domainControllerFunctionality=domainControllerFunctionality,
1005                                 ntdsguid=ntdsguid)
1006
1007                 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
1008                 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1009                   attribute="objectGUID", expression="", scope=SCOPE_BASE)
1010                 assert isinstance(names.ntdsguid, str)
1011
1012     except:
1013         samdb.transaction_cancel()
1014         raise
1015
1016     samdb.transaction_commit()
1017     return samdb
1018
1019
1020 FILL_FULL = "FULL"
1021 FILL_NT4SYNC = "NT4SYNC"
1022 FILL_DRS = "DRS"
1023
1024
1025 def provision(setup_dir, message, session_info, 
1026               credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1027               realm=None, 
1028               rootdn=None, domaindn=None, schemadn=None, configdn=None, 
1029               serverdn=None,
1030               domain=None, hostname=None, hostip=None, hostip6=None, 
1031               domainsid=None, adminpass=None, ldapadminpass=None, 
1032               krbtgtpass=None, domainguid=None, 
1033               policyguid=None, policyguid_dc=None, invocationid=None,
1034               machinepass=None, ntdsguid=None,
1035               dnspass=None, root=None, nobody=None, users=None, 
1036               wheel=None, backup=None, aci=None, serverrole=None,
1037               dom_for_fun_level=None,
1038               ldap_backend_extra_port=None, backend_type=None,
1039               sitename=None,
1040               ol_mmr_urls=None, ol_olc=None, 
1041               setup_ds_path=None, slapd_path=None, nosync=False,
1042               ldap_dryrun_mode=False):
1043     """Provision samba4
1044     
1045     :note: caution, this wipes all existing data!
1046     """
1047
1048     def setup_path(file):
1049       return os.path.join(setup_dir, file)
1050
1051     if domainsid is None:
1052       domainsid = security.random_sid()
1053     else:
1054       domainsid = security.dom_sid(domainsid)
1055
1056     # create/adapt the group policy GUIDs
1057     if policyguid is None:
1058         policyguid = str(uuid.uuid4())
1059     policyguid = policyguid.upper()
1060     if policyguid_dc is None:
1061         policyguid_dc = str(uuid.uuid4())
1062     policyguid_dc = policyguid_dc.upper()
1063
1064     if adminpass is None:
1065         adminpass = glue.generate_random_str(12)
1066     if krbtgtpass is None:
1067         krbtgtpass = glue.generate_random_str(12)
1068     if machinepass is None:
1069         machinepass  = glue.generate_random_str(12)
1070     if dnspass is None:
1071         dnspass = glue.generate_random_str(12)
1072     if ldapadminpass is None:
1073         #Make a new, random password between Samba and it's LDAP server
1074         ldapadminpass=glue.generate_random_str(12)        
1075
1076     if backend_type is None:
1077         backend_type = "ldb"
1078
1079     sid_generator = "internal"
1080     if backend_type == "fedora-ds":
1081         sid_generator = "backend"
1082
1083     root_uid = findnss_uid([root or "root"])
1084     nobody_uid = findnss_uid([nobody or "nobody"])
1085     users_gid = findnss_gid([users or "users"])
1086     if wheel is None:
1087         wheel_gid = findnss_gid(["wheel", "adm"])
1088     else:
1089         wheel_gid = findnss_gid([wheel])
1090
1091     if targetdir is not None:
1092         if (not os.path.exists(os.path.join(targetdir, "etc"))):
1093             os.makedirs(os.path.join(targetdir, "etc"))
1094         smbconf = os.path.join(targetdir, "etc", "smb.conf")
1095     elif smbconf is None:
1096         smbconf = param.default_path()
1097
1098     # only install a new smb.conf if there isn't one there already
1099     if not os.path.exists(smbconf):
1100         make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, 
1101                      targetdir, sid_generator)
1102
1103     lp = param.LoadParm()
1104     lp.load(smbconf)
1105
1106     names = guess_names(lp=lp, hostname=hostname, domain=domain,
1107                         dnsdomain=realm, serverrole=serverrole,
1108                         domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1109                         serverdn=serverdn, sitename=sitename)
1110
1111     paths = provision_paths_from_lp(lp, names.dnsdomain)
1112
1113     if hostip is None:
1114         try:
1115             hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1116         except socket.gaierror, (socket.EAI_NODATA, msg):
1117             hostip = None
1118
1119     if hostip6 is None:
1120         try:
1121             hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1122         except socket.gaierror, (socket.EAI_NODATA, msg): 
1123             hostip6 = None
1124
1125     if serverrole is None:
1126         serverrole = lp.get("server role")
1127
1128     assert serverrole in ("domain controller", "member server", "standalone")
1129     if invocationid is None and serverrole == "domain controller":
1130         invocationid = str(uuid.uuid4())
1131
1132     if not os.path.exists(paths.private_dir):
1133         os.mkdir(paths.private_dir)
1134     if not os.path.exists(os.path.join(paths.private_dir,"tls")):
1135         os.mkdir(os.path.join(paths.private_dir,"tls"))
1136
1137     ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1138     
1139     schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
1140     
1141     if backend_type == "ldb":
1142         provision_backend = LDBBackend(backend_type,
1143                                          paths=paths, setup_path=setup_path,
1144                                          lp=lp, credentials=credentials, 
1145                                          names=names,
1146                                          message=message)
1147     elif backend_type == "existing":
1148         provision_backend = ExistingBackend(backend_type,
1149                                          paths=paths, setup_path=setup_path,
1150                                          lp=lp, credentials=credentials, 
1151                                          names=names,
1152                                          message=message)
1153     elif backend_type == "fedora-ds":
1154         provision_backend = FDSBackend(backend_type,
1155                                          paths=paths, setup_path=setup_path,
1156                                          lp=lp, credentials=credentials, 
1157                                          names=names,
1158                                          message=message,
1159                                          domainsid=domainsid,
1160                                          schema=schema,
1161                                          hostname=hostname,
1162                                          ldapadminpass=ldapadminpass,
1163                                          slapd_path=slapd_path,
1164                                          ldap_backend_extra_port=ldap_backend_extra_port,
1165                                          ldap_dryrun_mode=ldap_dryrun_mode,
1166                                          root=root,
1167                                          setup_ds_path=setup_ds_path)
1168     elif backend_type == "openldap":
1169         provision_backend = OpenLDAPBackend(backend_type,
1170                                          paths=paths, setup_path=setup_path,
1171                                          lp=lp, credentials=credentials, 
1172                                          names=names,
1173                                          message=message,
1174                                          domainsid=domainsid,
1175                                          schema=schema,
1176                                          hostname=hostname,
1177                                          ldapadminpass=ldapadminpass,
1178                                          slapd_path=slapd_path,
1179                                          ldap_backend_extra_port=ldap_backend_extra_port,
1180                                          ldap_dryrun_mode=ldap_dryrun_mode,
1181                                          ol_mmr_urls=ol_mmr_urls, 
1182                                          nosync=nosync)
1183     else:
1184         raise ProvisioningError("Unknown LDAP backend type selected")
1185
1186     provision_backend.init()
1187     provision_backend.start()
1188
1189     # only install a new shares config db if there is none
1190     if not os.path.exists(paths.shareconf):
1191         message("Setting up share.ldb")
1192         share_ldb = Ldb(paths.shareconf, session_info=session_info, 
1193                         lp=lp)
1194         share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1195
1196      
1197     message("Setting up secrets.ldb")
1198     secrets_ldb = setup_secretsdb(paths.secrets, setup_path, 
1199                                   session_info=session_info, 
1200                                   backend_credentials=provision_backend.secrets_credentials, lp=lp)
1201
1202     message("Setting up the registry")
1203     setup_registry(paths.hklm, setup_path, session_info, 
1204                    lp=lp)
1205
1206     message("Setting up the privileges database")
1207     setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1208
1209     message("Setting up idmap db")
1210     idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1211                           lp=lp)
1212
1213     message("Setting up SAM db")
1214     samdb = setup_samdb(paths.samdb, setup_path, session_info, 
1215                         provision_backend, lp, names,
1216                         message, 
1217                         domainsid=domainsid, 
1218                         schema=schema, domainguid=domainguid,
1219                         policyguid=policyguid, policyguid_dc=policyguid_dc,
1220                         fill=samdb_fill, 
1221                         adminpass=adminpass, krbtgtpass=krbtgtpass,
1222                         invocationid=invocationid, 
1223                         machinepass=machinepass, dnspass=dnspass, 
1224                         ntdsguid=ntdsguid, serverrole=serverrole,
1225                         dom_for_fun_level=dom_for_fun_level)
1226
1227     if serverrole == "domain controller":
1228         if paths.netlogon is None:
1229             message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1230             message("Please either remove %s or see the template at %s" % 
1231                     ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1232             assert(paths.netlogon is not None)
1233
1234         if paths.sysvol is None:
1235             message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1236             message("Please either remove %s or see the template at %s" % 
1237                     (paths.smbconf, setup_path("provision.smb.conf.dc")))
1238             assert(paths.sysvol is not None)            
1239             
1240         # Set up group policies (domain policy and domain controller policy)
1241
1242         policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1243                                    "{" + policyguid + "}")
1244         os.makedirs(policy_path, 0755)
1245         open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1246                                    "[General]\r\nVersion=65543")
1247         os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
1248         os.makedirs(os.path.join(policy_path, "USER"), 0755)
1249
1250         policy_path_dc = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1251                                    "{" + policyguid_dc + "}")
1252         os.makedirs(policy_path_dc, 0755)
1253         open(os.path.join(policy_path_dc, "GPT.INI"), 'w').write(
1254                                    "[General]\r\nVersion=2")
1255         os.makedirs(os.path.join(policy_path_dc, "MACHINE"), 0755)
1256         os.makedirs(os.path.join(policy_path_dc, "USER"), 0755)
1257
1258         if not os.path.isdir(paths.netlogon):
1259             os.makedirs(paths.netlogon, 0755)
1260
1261     if samdb_fill == FILL_FULL:
1262         setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1263                             root_uid=root_uid, nobody_uid=nobody_uid,
1264                             users_gid=users_gid, wheel_gid=wheel_gid)
1265
1266         message("Setting up sam.ldb rootDSE marking as synchronized")
1267         setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1268
1269         # Only make a zone file on the first DC, it should be replicated with DNS replication
1270         if serverrole == "domain controller":
1271             secretsdb_self_join(secrets_ldb, domain=names.domain,
1272                                 realm=names.realm,
1273                                 dnsdomain=names.dnsdomain,
1274                                 netbiosname=names.netbiosname,
1275                                 domainsid=domainsid, 
1276                                 machinepass=machinepass,
1277                                 secure_channel_type=SEC_CHAN_BDC)
1278
1279             secretsdb_setup_dns(secrets_ldb, setup_path, 
1280                                 realm=names.realm, dnsdomain=names.dnsdomain,
1281                                 dns_keytab_path=paths.dns_keytab,
1282                                 dnspass=dnspass)
1283
1284             domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1285             assert isinstance(domainguid, str)
1286
1287             create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1288                              hostip=hostip,
1289                              hostip6=hostip6, hostname=names.hostname,
1290                              realm=names.realm,
1291                              domainguid=domainguid, ntdsguid=names.ntdsguid)
1292
1293             create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1294                               dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1295
1296             create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1297                               dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1298                               keytab_name=paths.dns_keytab)
1299             message("See %s for an example configuration include file for BIND" % paths.namedconf)
1300             message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1301
1302             create_krb5_conf(paths.krb5conf, setup_path,
1303                              dnsdomain=names.dnsdomain, hostname=names.hostname,
1304                              realm=names.realm)
1305             message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1306
1307     provision_backend.post_setup()
1308     provision_backend.shutdown()
1309     
1310     create_phpldapadmin_config(paths.phpldapadminconfig, setup_path, 
1311                                ldapi_url)
1312
1313     #Now commit the secrets.ldb to disk
1314     secrets_ldb.transaction_commit()
1315
1316     message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1317
1318     message("Once the above files are installed, your Samba4 server will be ready to use")
1319     message("Server Role:           %s" % serverrole)
1320     message("Hostname:              %s" % names.hostname)
1321     message("NetBIOS Domain:        %s" % names.domain)
1322     message("DNS Domain:            %s" % names.dnsdomain)
1323     message("DOMAIN SID:            %s" % str(domainsid))
1324     if samdb_fill == FILL_FULL:
1325         message("Admin password:        %s" % adminpass)
1326     if provision_backend.type is not "ldb":
1327         if provision_backend.credentials.get_bind_dn() is not None:
1328             message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1329         else:
1330             message("LDAP Admin User:       %s" % provision_backend.credentials.get_username())
1331
1332         message("LDAP Admin Password:   %s" % provision_backend.credentials.get_password())
1333
1334         if provision_backend.slapd_command_escaped is not None:
1335             # now display slapd_command_file.txt to show how slapd must be started next time
1336             message("Use later the following commandline to start slapd, then Samba:")
1337             message(provision_backend.slapd_command_escaped)
1338             message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh")
1339
1340
1341     result = ProvisionResult()
1342     result.domaindn = domaindn
1343     result.paths = paths
1344     result.lp = lp
1345     result.samdb = samdb
1346     return result
1347
1348
1349
1350 def provision_become_dc(setup_dir=None,
1351                         smbconf=None, targetdir=None, realm=None, 
1352                         rootdn=None, domaindn=None, schemadn=None,
1353                         configdn=None, serverdn=None,
1354                         domain=None, hostname=None, domainsid=None, 
1355                         adminpass=None, krbtgtpass=None, domainguid=None, 
1356                         policyguid=None, policyguid_dc=None, invocationid=None,
1357                         machinepass=None, 
1358                         dnspass=None, root=None, nobody=None, users=None, 
1359                         wheel=None, backup=None, serverrole=None, 
1360                         ldap_backend=None, ldap_backend_type=None,
1361                         sitename=None, debuglevel=1):
1362
1363     def message(text):
1364         """print a message if quiet is not set."""
1365         print text
1366
1367     glue.set_debug_level(debuglevel)
1368
1369     return provision(setup_dir, message, system_session(), None,
1370               smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1371               realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1372               configdn=configdn, serverdn=serverdn, domain=domain,
1373               hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1374               machinepass=machinepass, serverrole="domain controller",
1375               sitename=sitename)
1376
1377
1378 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1379     """Create a PHP LDAP admin configuration file.
1380
1381     :param path: Path to write the configuration to.
1382     :param setup_path: Function to generate setup paths.
1383     """
1384     setup_file(setup_path("phpldapadmin-config.php"), path, 
1385             {"S4_LDAPI_URI": ldapi_uri})
1386
1387
1388 def create_zone_file(path, setup_path, dnsdomain, 
1389                      hostip, hostip6, hostname, realm, domainguid,
1390                      ntdsguid):
1391     """Write out a DNS zone file, from the info in the current database.
1392
1393     :param path: Path of the new zone file.
1394     :param setup_path: Setup path function.
1395     :param dnsdomain: DNS Domain name
1396     :param domaindn: DN of the Domain
1397     :param hostip: Local IPv4 IP
1398     :param hostip6: Local IPv6 IP
1399     :param hostname: Local hostname
1400     :param realm: Realm name
1401     :param domainguid: GUID of the domain.
1402     :param ntdsguid: GUID of the hosts nTDSDSA record.
1403     """
1404     assert isinstance(domainguid, str)
1405
1406     if hostip6 is not None:
1407         hostip6_base_line = "            IN AAAA    " + hostip6
1408         hostip6_host_line = hostname + "        IN AAAA    " + hostip6
1409     else:
1410         hostip6_base_line = ""
1411         hostip6_host_line = ""
1412
1413     if hostip is not None:
1414         hostip_base_line = "            IN A    " + hostip
1415         hostip_host_line = hostname + "        IN A    " + hostip
1416     else:
1417         hostip_base_line = ""
1418         hostip_host_line = ""
1419
1420     setup_file(setup_path("provision.zone"), path, {
1421             "HOSTNAME": hostname,
1422             "DNSDOMAIN": dnsdomain,
1423             "REALM": realm,
1424             "HOSTIP_BASE_LINE": hostip_base_line,
1425             "HOSTIP_HOST_LINE": hostip_host_line,
1426             "DOMAINGUID": domainguid,
1427             "DATESTRING": time.strftime("%Y%m%d%H"),
1428             "DEFAULTSITE": DEFAULTSITE,
1429             "NTDSGUID": ntdsguid,
1430             "HOSTIP6_BASE_LINE": hostip6_base_line,
1431             "HOSTIP6_HOST_LINE": hostip6_host_line,
1432         })
1433
1434
1435 def create_named_conf(path, setup_path, realm, dnsdomain,
1436                       private_dir):
1437     """Write out a file containing zone statements suitable for inclusion in a
1438     named.conf file (including GSS-TSIG configuration).
1439     
1440     :param path: Path of the new named.conf file.
1441     :param setup_path: Setup path function.
1442     :param realm: Realm name
1443     :param dnsdomain: DNS Domain name
1444     :param private_dir: Path to private directory
1445     :param keytab_name: File name of DNS keytab file
1446     """
1447
1448     setup_file(setup_path("named.conf"), path, {
1449             "DNSDOMAIN": dnsdomain,
1450             "REALM": realm,
1451             "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1452             "PRIVATE_DIR": private_dir
1453             })
1454
1455 def create_named_txt(path, setup_path, realm, dnsdomain,
1456                       private_dir, keytab_name):
1457     """Write out a file containing zone statements suitable for inclusion in a
1458     named.conf file (including GSS-TSIG configuration).
1459     
1460     :param path: Path of the new named.conf file.
1461     :param setup_path: Setup path function.
1462     :param realm: Realm name
1463     :param dnsdomain: DNS Domain name
1464     :param private_dir: Path to private directory
1465     :param keytab_name: File name of DNS keytab file
1466     """
1467
1468     setup_file(setup_path("named.txt"), path, {
1469             "DNSDOMAIN": dnsdomain,
1470             "REALM": realm,
1471             "DNS_KEYTAB": keytab_name,
1472             "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1473             "PRIVATE_DIR": private_dir
1474         })
1475
1476 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1477     """Write out a file containing zone statements suitable for inclusion in a
1478     named.conf file (including GSS-TSIG configuration).
1479     
1480     :param path: Path of the new named.conf file.
1481     :param setup_path: Setup path function.
1482     :param dnsdomain: DNS Domain name
1483     :param hostname: Local hostname
1484     :param realm: Realm name
1485     """
1486
1487     setup_file(setup_path("krb5.conf"), path, {
1488             "DNSDOMAIN": dnsdomain,
1489             "HOSTNAME": hostname,
1490             "REALM": realm,
1491         })
1492
1493