01ffc16a24df9402cb2e56b2ec0a4abc369d411e
[ira/wip.git] / source4 / scripting / python / samba / provision.py
1 #
2 # Unix SMB/CIFS implementation.
3 # backend code for provisioning a Samba4 server
4
5 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
6 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
7 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
8 #
9 # Based on the original in EJS:
10 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
11 #
12 # This program is free software; you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
14 # the Free Software Foundation; either version 3 of the License, or
15 # (at your option) any later version.
16 #   
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 # GNU General Public License for more details.
21 #   
22 # You should have received a copy of the GNU General Public License
23 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
24 #
25
26 """Functions for setting up a Samba configuration."""
27
28 from base64 import b64encode
29 import os
30 import 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
1134     ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1135     
1136     schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
1137     
1138     if backend_type == "ldb":
1139         provision_backend = LDBBackend(backend_type,
1140                                          paths=paths, setup_path=setup_path,
1141                                          lp=lp, credentials=credentials, 
1142                                          names=names,
1143                                          message=message)
1144     elif backend_type == "existing":
1145         provision_backend = ExistingBackend(backend_type,
1146                                          paths=paths, setup_path=setup_path,
1147                                          lp=lp, credentials=credentials, 
1148                                          names=names,
1149                                          message=message)
1150     elif backend_type == "fedora-ds":
1151         provision_backend = FDSBackend(backend_type,
1152                                          paths=paths, setup_path=setup_path,
1153                                          lp=lp, credentials=credentials, 
1154                                          names=names,
1155                                          message=message,
1156                                          domainsid=domainsid,
1157                                          schema=schema,
1158                                          hostname=hostname,
1159                                          ldapadminpass=ldapadminpass,
1160                                          slapd_path=slapd_path,
1161                                          ldap_backend_extra_port=ldap_backend_extra_port,
1162                                          ldap_dryrun_mode=ldap_dryrun_mode,
1163                                          root=root,
1164                                          setup_ds_path=setup_ds_path)
1165     elif backend_type == "openldap":
1166         provision_backend = OpenLDAPBackend(backend_type,
1167                                          paths=paths, setup_path=setup_path,
1168                                          lp=lp, credentials=credentials, 
1169                                          names=names,
1170                                          message=message,
1171                                          domainsid=domainsid,
1172                                          schema=schema,
1173                                          hostname=hostname,
1174                                          ldapadminpass=ldapadminpass,
1175                                          slapd_path=slapd_path,
1176                                          ldap_backend_extra_port=ldap_backend_extra_port,
1177                                          ldap_dryrun_mode=ldap_dryrun_mode,
1178                                          ol_mmr_urls=ol_mmr_urls, 
1179                                          nosync=nosync)
1180     else:
1181         raise ProvisioningError("Unknown LDAP backend type selected")
1182
1183     provision_backend.init()
1184     provision_backend.start()
1185
1186     # only install a new shares config db if there is none
1187     if not os.path.exists(paths.shareconf):
1188         message("Setting up share.ldb")
1189         share_ldb = Ldb(paths.shareconf, session_info=session_info, 
1190                         lp=lp)
1191         share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1192
1193      
1194     message("Setting up secrets.ldb")
1195     secrets_ldb = setup_secretsdb(paths.secrets, setup_path, 
1196                                   session_info=session_info, 
1197                                   backend_credentials=provision_backend.secrets_credentials, lp=lp)
1198
1199     message("Setting up the registry")
1200     setup_registry(paths.hklm, setup_path, session_info, 
1201                    lp=lp)
1202
1203     message("Setting up the privileges database")
1204     setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1205
1206     message("Setting up idmap db")
1207     idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1208                           lp=lp)
1209
1210     message("Setting up SAM db")
1211     samdb = setup_samdb(paths.samdb, setup_path, session_info, 
1212                         provision_backend, lp, names,
1213                         message, 
1214                         domainsid=domainsid, 
1215                         schema=schema, domainguid=domainguid,
1216                         policyguid=policyguid, policyguid_dc=policyguid_dc,
1217                         fill=samdb_fill, 
1218                         adminpass=adminpass, krbtgtpass=krbtgtpass,
1219                         invocationid=invocationid, 
1220                         machinepass=machinepass, dnspass=dnspass, 
1221                         ntdsguid=ntdsguid, serverrole=serverrole,
1222                         dom_for_fun_level=dom_for_fun_level)
1223
1224     if serverrole == "domain controller":
1225         if paths.netlogon is None:
1226             message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1227             message("Please either remove %s or see the template at %s" % 
1228                     ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1229             assert(paths.netlogon is not None)
1230
1231         if paths.sysvol is None:
1232             message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1233             message("Please either remove %s or see the template at %s" % 
1234                     (paths.smbconf, setup_path("provision.smb.conf.dc")))
1235             assert(paths.sysvol is not None)            
1236             
1237         # Set up group policies (domain policy and domain controller policy)
1238
1239         policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1240                                    "{" + policyguid + "}")
1241         os.makedirs(policy_path, 0755)
1242         open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1243                                    "[General]\r\nVersion=65543")
1244         os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
1245         os.makedirs(os.path.join(policy_path, "USER"), 0755)
1246
1247         policy_path_dc = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1248                                    "{" + policyguid_dc + "}")
1249         os.makedirs(policy_path_dc, 0755)
1250         open(os.path.join(policy_path_dc, "GPT.INI"), 'w').write(
1251                                    "[General]\r\nVersion=2")
1252         os.makedirs(os.path.join(policy_path_dc, "MACHINE"), 0755)
1253         os.makedirs(os.path.join(policy_path_dc, "USER"), 0755)
1254
1255         if not os.path.isdir(paths.netlogon):
1256             os.makedirs(paths.netlogon, 0755)
1257
1258     if samdb_fill == FILL_FULL:
1259         setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1260                             root_uid=root_uid, nobody_uid=nobody_uid,
1261                             users_gid=users_gid, wheel_gid=wheel_gid)
1262
1263         message("Setting up sam.ldb rootDSE marking as synchronized")
1264         setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1265
1266         # Only make a zone file on the first DC, it should be replicated with DNS replication
1267         if serverrole == "domain controller":
1268             secretsdb_self_join(secrets_ldb, domain=domain,
1269                                 realm=names.realm,
1270                                 dnsdomain=names.dnsdomain,
1271                                 netbiosname=names.netbiosname,
1272                                 domainsid=domainsid, 
1273                                 machinepass=machinepass,
1274                                 secure_channel_type=SEC_CHAN_BDC)
1275
1276             secretsdb_setup_dns(secrets_ldb, setup_path, 
1277                                 realm=names.realm, dnsdomain=names.dnsdomain,
1278                                 dns_keytab_path=paths.dns_keytab,
1279                                 dnspass=dnspass)
1280
1281             domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1282             assert isinstance(domainguid, str)
1283
1284             create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1285                              hostip=hostip,
1286                              hostip6=hostip6, hostname=names.hostname,
1287                              realm=names.realm,
1288                              domainguid=domainguid, ntdsguid=names.ntdsguid)
1289
1290             create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1291                               dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1292
1293             create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1294                               dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1295                               keytab_name=paths.dns_keytab)
1296             message("See %s for an example configuration include file for BIND" % paths.namedconf)
1297             message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1298
1299             create_krb5_conf(paths.krb5conf, setup_path,
1300                              dnsdomain=names.dnsdomain, hostname=names.hostname,
1301                              realm=names.realm)
1302             message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1303
1304     provision_backend.post_setup()
1305     provision_backend.shutdown()
1306     
1307     create_phpldapadmin_config(paths.phpldapadminconfig, setup_path, 
1308                                ldapi_url)
1309
1310     #Now commit the secrets.ldb to disk
1311     secrets_ldb.transaction_commit()
1312
1313     message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1314
1315     message("Once the above files are installed, your Samba4 server will be ready to use")
1316     message("Server Role:           %s" % serverrole)
1317     message("Hostname:              %s" % names.hostname)
1318     message("NetBIOS Domain:        %s" % names.domain)
1319     message("DNS Domain:            %s" % names.dnsdomain)
1320     message("DOMAIN SID:            %s" % str(domainsid))
1321     if samdb_fill == FILL_FULL:
1322         message("Admin password:    %s" % adminpass)
1323     if provision_backend.type is not "ldb":
1324         if provision_backend.credentials.get_bind_dn() is not None:
1325             message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1326         else:
1327             message("LDAP Admin User:       %s" % provision_backend.credentials.get_username())
1328
1329         message("LDAP Admin Password:   %s" % provision_backend.credentials.get_password())
1330
1331         if provision_backend.slapd_command_escaped is not None:
1332             # now display slapd_command_file.txt to show how slapd must be started next time
1333             message("Use later the following commandline to start slapd, then Samba:")
1334             message(provision_backend.slapd_command_escaped)
1335             message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh")
1336
1337
1338     result = ProvisionResult()
1339     result.domaindn = domaindn
1340     result.paths = paths
1341     result.lp = lp
1342     result.samdb = samdb
1343     return result
1344
1345
1346
1347 def provision_become_dc(setup_dir=None,
1348                         smbconf=None, targetdir=None, realm=None, 
1349                         rootdn=None, domaindn=None, schemadn=None,
1350                         configdn=None, serverdn=None,
1351                         domain=None, hostname=None, domainsid=None, 
1352                         adminpass=None, krbtgtpass=None, domainguid=None, 
1353                         policyguid=None, policyguid_dc=None, invocationid=None,
1354                         machinepass=None, 
1355                         dnspass=None, root=None, nobody=None, users=None, 
1356                         wheel=None, backup=None, serverrole=None, 
1357                         ldap_backend=None, ldap_backend_type=None,
1358                         sitename=None, debuglevel=1):
1359
1360     def message(text):
1361         """print a message if quiet is not set."""
1362         print text
1363
1364     glue.set_debug_level(debuglevel)
1365
1366     return provision(setup_dir, message, system_session(), None,
1367               smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1368               realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1369               configdn=configdn, serverdn=serverdn, domain=domain,
1370               hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1371               machinepass=machinepass, serverrole="domain controller",
1372               sitename=sitename)
1373
1374
1375 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1376     """Create a PHP LDAP admin configuration file.
1377
1378     :param path: Path to write the configuration to.
1379     :param setup_path: Function to generate setup paths.
1380     """
1381     setup_file(setup_path("phpldapadmin-config.php"), path, 
1382             {"S4_LDAPI_URI": ldapi_uri})
1383
1384
1385 def create_zone_file(path, setup_path, dnsdomain, 
1386                      hostip, hostip6, hostname, realm, domainguid,
1387                      ntdsguid):
1388     """Write out a DNS zone file, from the info in the current database.
1389
1390     :param path: Path of the new zone file.
1391     :param setup_path: Setup path function.
1392     :param dnsdomain: DNS Domain name
1393     :param domaindn: DN of the Domain
1394     :param hostip: Local IPv4 IP
1395     :param hostip6: Local IPv6 IP
1396     :param hostname: Local hostname
1397     :param realm: Realm name
1398     :param domainguid: GUID of the domain.
1399     :param ntdsguid: GUID of the hosts nTDSDSA record.
1400     """
1401     assert isinstance(domainguid, str)
1402
1403     if hostip6 is not None:
1404         hostip6_base_line = "            IN AAAA    " + hostip6
1405         hostip6_host_line = hostname + "        IN AAAA    " + hostip6
1406     else:
1407         hostip6_base_line = ""
1408         hostip6_host_line = ""
1409
1410     if hostip is not None:
1411         hostip_base_line = "            IN A    " + hostip
1412         hostip_host_line = hostname + "        IN A    " + hostip
1413     else:
1414         hostip_base_line = ""
1415         hostip_host_line = ""
1416
1417     setup_file(setup_path("provision.zone"), path, {
1418             "HOSTNAME": hostname,
1419             "DNSDOMAIN": dnsdomain,
1420             "REALM": realm,
1421             "HOSTIP_BASE_LINE": hostip_base_line,
1422             "HOSTIP_HOST_LINE": hostip_host_line,
1423             "DOMAINGUID": domainguid,
1424             "DATESTRING": time.strftime("%Y%m%d%H"),
1425             "DEFAULTSITE": DEFAULTSITE,
1426             "NTDSGUID": ntdsguid,
1427             "HOSTIP6_BASE_LINE": hostip6_base_line,
1428             "HOSTIP6_HOST_LINE": hostip6_host_line,
1429         })
1430
1431
1432 def create_named_conf(path, setup_path, realm, dnsdomain,
1433                       private_dir):
1434     """Write out a file containing zone statements suitable for inclusion in a
1435     named.conf file (including GSS-TSIG configuration).
1436     
1437     :param path: Path of the new named.conf file.
1438     :param setup_path: Setup path function.
1439     :param realm: Realm name
1440     :param dnsdomain: DNS Domain name
1441     :param private_dir: Path to private directory
1442     :param keytab_name: File name of DNS keytab file
1443     """
1444
1445     setup_file(setup_path("named.conf"), path, {
1446             "DNSDOMAIN": dnsdomain,
1447             "REALM": realm,
1448             "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1449             "PRIVATE_DIR": private_dir
1450             })
1451
1452 def create_named_txt(path, setup_path, realm, dnsdomain,
1453                       private_dir, keytab_name):
1454     """Write out a file containing zone statements suitable for inclusion in a
1455     named.conf file (including GSS-TSIG configuration).
1456     
1457     :param path: Path of the new named.conf file.
1458     :param setup_path: Setup path function.
1459     :param realm: Realm name
1460     :param dnsdomain: DNS Domain name
1461     :param private_dir: Path to private directory
1462     :param keytab_name: File name of DNS keytab file
1463     """
1464
1465     setup_file(setup_path("named.txt"), path, {
1466             "DNSDOMAIN": dnsdomain,
1467             "REALM": realm,
1468             "DNS_KEYTAB": keytab_name,
1469             "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1470             "PRIVATE_DIR": private_dir
1471         })
1472
1473 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1474     """Write out a file containing zone statements suitable for inclusion in a
1475     named.conf file (including GSS-TSIG configuration).
1476     
1477     :param path: Path of the new named.conf file.
1478     :param setup_path: Setup path function.
1479     :param dnsdomain: DNS Domain name
1480     :param hostname: Local hostname
1481     :param realm: Realm name
1482     """
1483
1484     setup_file(setup_path("krb5.conf"), path, {
1485             "DNSDOMAIN": dnsdomain,
1486             "HOSTNAME": hostname,
1487             "REALM": realm,
1488         })
1489
1490