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