7558ad3d7c1bfc633f59d3aa51b96770b9cc8b5c
[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 import shutil
43 from credentials import Credentials, DONT_USE_KERBEROS
44 from auth import system_session, admin_session
45 from samba import version, Ldb, substitute_var, valid_netbios_name
46 from samba import check_all_substituted
47 from samba import DS_DOMAIN_FUNCTION_2003, DS_DOMAIN_FUNCTION_2008, DS_DC_FUNCTION_2008
48 from samba.samdb import SamDB
49 from samba.idmap import IDmapDB
50 from samba.dcerpc import security
51 from samba.ndr import ndr_pack
52 import urllib
53 from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError, timestring
54 from ms_schema import read_ms_schema
55 from ms_display_specifiers import read_ms_ldif
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 def get_schema_descriptor(domain_sid):
77     sddl = "O:SAG:SAD:(A;CI;RPLCLORC;;;AU)(A;CI;RPWPCRCCLCLORCWOWDSW;;;SA)" \
78            "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
79            "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
80            "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
81            "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
82            "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
83            "S:(AU;SA;WPCCDCWOWDSDDTSW;;;WD)" \
84            "(AU;CISA;WP;;;WD)(AU;SA;CR;;;BA)" \
85            "(AU;SA;CR;;;DU)(OU;SA;CR;e12b56b6-0a95-11d1-adbb-00c04fd8d5cd;;WD)" \
86            "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
87     sec = security.descriptor.from_sddl(sddl, domain_sid)
88     return b64encode(ndr_pack(sec))
89
90 def get_config_descriptor(domain_sid):
91     sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
92            "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
93            "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
94            "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
95            "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
96            "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
97            "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
98            "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
99            "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
100            "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
101            "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
102            "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
103            "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;S-1-5-21-3191434175-1265308384-3577286990-498)" \
104            "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
105            "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
106     sec = security.descriptor.from_sddl(sddl, domain_sid)
107     return b64encode(ndr_pack(sec))
108
109
110 DEFAULTSITE = "Default-First-Site-Name"
111
112 # Exception classes
113
114 class ProvisioningError(Exception):
115     """A generic provision error."""
116
117 class InvalidNetbiosName(Exception):
118     """A specified name was not a valid NetBIOS name."""
119     def __init__(self, name):
120         super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)
121
122
123 class ProvisionPaths(object):
124     def __init__(self):
125         self.shareconf = None
126         self.hklm = None
127         self.hkcu = None
128         self.hkcr = None
129         self.hku = None
130         self.hkpd = None
131         self.hkpt = None
132         self.samdb = None
133         self.idmapdb = None
134         self.secrets = None
135         self.keytab = None
136         self.dns_keytab = None
137         self.dns = None
138         self.winsdb = None
139         self.private_dir = None
140         self.ldapdir = None
141         self.slapdconf = None
142         self.modulesconf = None
143         self.memberofconf = None
144         self.fedoradsinf = None
145         self.fedoradspartitions = None
146         self.fedoradssasl = None
147         self.fedoradsdna = None
148         self.fedoradspam = None
149         self.fedoradsrefint = None
150         self.fedoradslinkedattributes = None
151         self.fedoradsindex = None
152         self.fedoradssamba = None
153         self.olmmron = None
154         self.olmmrserveridsconf = None
155         self.olmmrsyncreplconf = None
156         self.olcdir = None
157         self.olslapd = None
158         self.olcseedldif = None
159
160
161 class ProvisionNames(object):
162     def __init__(self):
163         self.rootdn = None
164         self.domaindn = None
165         self.configdn = None
166         self.schemadn = None
167         self.sambadn = None
168         self.ldapmanagerdn = None
169         self.dnsdomain = None
170         self.realm = None
171         self.netbiosname = None
172         self.domain = None
173         self.hostname = None
174         self.sitename = None
175         self.smbconf = None
176     
177
178 class ProvisionResult(object):
179     def __init__(self):
180         self.paths = None
181         self.domaindn = None
182         self.lp = None
183         self.samdb = None
184         
185 class Schema(object):
186     def __init__(self, setup_path, domain_sid, schemadn=None,
187                  serverdn=None, sambadn=None):
188         """Load schema for the SamDB from the AD schema files and samba4_schema.ldif
189         
190         :param samdb: Load a schema into a SamDB.
191         :param setup_path: Setup path function.
192         :param schemadn: DN of the schema
193         :param serverdn: DN of the server
194         
195         Returns the schema data loaded, to avoid double-parsing when then needing to add it to the db
196         """
197         
198         self.ldb = Ldb()
199         self.schema_data = read_ms_schema(setup_path('ad-schema/MS-AD_Schema_2K8_Attributes.txt'),
200                                           setup_path('ad-schema/MS-AD_Schema_2K8_Classes.txt'))
201         self.schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
202         self.schema_data = substitute_var(self.schema_data, {"SCHEMADN": schemadn})
203         check_all_substituted(self.schema_data)
204
205         self.schema_dn_modify = read_and_sub_file(setup_path("provision_schema_basedn_modify.ldif"),
206                                                   {"SCHEMADN": schemadn,
207                                                    "SERVERDN": serverdn,
208                                                    })
209
210         descr = get_schema_descriptor(domain_sid)
211         self.schema_dn_add = read_and_sub_file(setup_path("provision_schema_basedn.ldif"),
212                                                {"SCHEMADN": schemadn,
213                                                 "DESCRIPTOR": descr
214                                                 })
215
216         prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
217         prefixmap = b64encode(prefixmap)
218
219         
220
221         # We don't actually add this ldif, just parse it
222         prefixmap_ldif = "dn: cn=schema\nprefixMap:: %s\n\n" % prefixmap
223         self.ldb.set_schema_from_ldif(prefixmap_ldif, self.schema_data)
224
225
226 # Return a hash with the forward attribute as a key and the back as the value 
227 def get_linked_attributes(schemadn,schemaldb):
228     attrs = ["linkID", "lDAPDisplayName"]
229     res = schemaldb.search(expression="(&(linkID=*)(!(linkID:1.2.840.113556.1.4.803:=1))(objectclass=attributeSchema)(attributeSyntax=2.5.5.1))", base=schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
230     attributes = {}
231     for i in range (0, len(res)):
232         expression = "(&(objectclass=attributeSchema)(linkID=%d)(attributeSyntax=2.5.5.1))" % (int(res[i]["linkID"][0])+1)
233         target = schemaldb.searchone(basedn=schemadn, 
234                                      expression=expression, 
235                                      attribute="lDAPDisplayName", 
236                                      scope=SCOPE_SUBTREE)
237         if target is not None:
238             attributes[str(res[i]["lDAPDisplayName"])]=str(target)
239             
240     return attributes
241
242 def get_dnsyntax_attributes(schemadn,schemaldb):
243     attrs = ["linkID", "lDAPDisplayName"]
244     res = schemaldb.search(expression="(&(!(linkID=*))(objectclass=attributeSchema)(attributeSyntax=2.5.5.1))", base=schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
245     attributes = []
246     for i in range (0, len(res)):
247         attributes.append(str(res[i]["lDAPDisplayName"]))
248         
249     return attributes
250     
251     
252 def check_install(lp, session_info, credentials):
253     """Check whether the current install seems ok.
254     
255     :param lp: Loadparm context
256     :param session_info: Session information
257     :param credentials: Credentials
258     """
259     if lp.get("realm") == "":
260         raise Exception("Realm empty")
261     ldb = Ldb(lp.get("sam database"), session_info=session_info, 
262             credentials=credentials, lp=lp)
263     if len(ldb.search("(cn=Administrator)")) != 1:
264         raise ProvisioningError("No administrator account found")
265
266
267 def findnss(nssfn, names):
268     """Find a user or group from a list of possibilities.
269     
270     :param nssfn: NSS Function to try (should raise KeyError if not found)
271     :param names: Names to check.
272     :return: Value return by first names list.
273     """
274     for name in names:
275         try:
276             return nssfn(name)
277         except KeyError:
278             pass
279     raise KeyError("Unable to find user/group %r" % names)
280
281
282 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
283 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
284
285
286 def read_and_sub_file(file, subst_vars):
287     """Read a file and sub in variables found in it
288     
289     :param file: File to be read (typically from setup directory)
290      param subst_vars: Optional variables to subsitute in the file.
291     """
292     data = open(file, 'r').read()
293     if subst_vars is not None:
294         data = substitute_var(data, subst_vars)
295     check_all_substituted(data)
296     return data
297
298
299 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
300     """Setup a ldb in the private dir.
301     
302     :param ldb: LDB file to import data into
303     :param ldif_path: Path of the LDIF file to load
304     :param subst_vars: Optional variables to subsitute in LDIF.
305     :param nocontrols: Optional list of controls, can be None for no controls
306     """
307     assert isinstance(ldif_path, str)
308     data = read_and_sub_file(ldif_path, subst_vars)
309     ldb.add_ldif(data,controls)
310
311
312 def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
313     """Modify a ldb in the private dir.
314     
315     :param ldb: LDB object.
316     :param ldif_path: LDIF file path.
317     :param subst_vars: Optional dictionary with substitution variables.
318     """
319     data = read_and_sub_file(ldif_path, subst_vars)
320
321     ldb.modify_ldif(data)
322
323
324 def setup_ldb(ldb, ldif_path, subst_vars):
325     """Import a LDIF a file into a LDB handle, optionally substituting variables.
326
327     :note: Either all LDIF data will be added or none (using transactions).
328
329     :param ldb: LDB file to import into.
330     :param ldif_path: Path to the LDIF file.
331     :param subst_vars: Dictionary with substitution variables.
332     """
333     assert ldb is not None
334     ldb.transaction_start()
335     try:
336         setup_add_ldif(ldb, ldif_path, subst_vars)
337     except:
338         ldb.transaction_cancel()
339         raise
340     ldb.transaction_commit()
341
342
343 def setup_file(template, fname, subst_vars=None):
344     """Setup a file in the private dir.
345
346     :param template: Path of the template file.
347     :param fname: Path of the file to create.
348     :param subst_vars: Substitution variables.
349     """
350     f = fname
351
352     if os.path.exists(f):
353         os.unlink(f)
354
355     data = read_and_sub_file(template, subst_vars)
356     open(f, 'w').write(data)
357
358
359 def provision_paths_from_lp(lp, dnsdomain):
360     """Set the default paths for provisioning.
361
362     :param lp: Loadparm context.
363     :param dnsdomain: DNS Domain name
364     """
365     paths = ProvisionPaths()
366     paths.private_dir = lp.get("private dir")
367     paths.dns_keytab = "dns.keytab"
368
369     paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
370     paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
371     paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
372     paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
373     paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
374     paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
375     paths.namedconf = os.path.join(paths.private_dir, "named.conf")
376     paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
377     paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
378     paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
379     paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
380     paths.phpldapadminconfig = os.path.join(paths.private_dir, 
381                                             "phpldapadmin-config.php")
382     paths.ldapdir = os.path.join(paths.private_dir, 
383                                  "ldap")
384     paths.slapdconf = os.path.join(paths.ldapdir, 
385                                    "slapd.conf")
386     paths.slapdpid = os.path.join(paths.ldapdir, 
387                                    "slapd.pid")
388     paths.modulesconf = os.path.join(paths.ldapdir, 
389                                      "modules.conf")
390     paths.memberofconf = os.path.join(paths.ldapdir, 
391                                       "memberof.conf")
392     paths.fedoradsinf = os.path.join(paths.ldapdir, 
393                                      "fedorads.inf")
394     paths.fedoradspartitions = os.path.join(paths.ldapdir, 
395                                             "fedorads-partitions.ldif")
396     paths.fedoradssasl = os.path.join(paths.ldapdir, 
397                                       "fedorads-sasl.ldif")
398     paths.fedoradsdna = os.path.join(paths.ldapdir, 
399                                      "fedorads-dna.ldif")
400     paths.fedoradspam = os.path.join(paths.ldapdir,
401                                      "fedorads-pam.ldif")
402     paths.fedoradsrefint = os.path.join(paths.ldapdir,
403                                         "fedorads-refint.ldif")
404     paths.fedoradslinkedattributes = os.path.join(paths.ldapdir,
405                                                   "fedorads-linked-attributes.ldif")
406     paths.fedoradsindex = os.path.join(paths.ldapdir,
407                                        "fedorads-index.ldif")
408     paths.fedoradssamba = os.path.join(paths.ldapdir, 
409                                        "fedorads-samba.ldif")
410     paths.olmmrserveridsconf = os.path.join(paths.ldapdir, 
411                                             "mmr_serverids.conf")
412     paths.olmmrsyncreplconf = os.path.join(paths.ldapdir, 
413                                            "mmr_syncrepl.conf")
414     paths.olcdir = os.path.join(paths.ldapdir, 
415                                  "slapd.d")
416     paths.olcseedldif = os.path.join(paths.ldapdir, 
417                                  "olc_seed.ldif")
418     paths.hklm = "hklm.ldb"
419     paths.hkcr = "hkcr.ldb"
420     paths.hkcu = "hkcu.ldb"
421     paths.hku = "hku.ldb"
422     paths.hkpd = "hkpd.ldb"
423     paths.hkpt = "hkpt.ldb"
424
425     paths.sysvol = lp.get("path", "sysvol")
426
427     paths.netlogon = lp.get("path", "netlogon")
428
429     paths.smbconf = lp.configfile
430
431     return paths
432
433
434 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
435                 serverrole=None, rootdn=None, domaindn=None, configdn=None,
436                 schemadn=None, serverdn=None, sitename=None, sambadn=None):
437     """Guess configuration settings to use."""
438
439     if hostname is None:
440         hostname = socket.gethostname().split(".")[0]
441
442     netbiosname = lp.get("netbios name")
443     if netbiosname is None:
444         netbiosname = hostname
445     assert netbiosname is not None
446     netbiosname = netbiosname.upper()
447     if not valid_netbios_name(netbiosname):
448         raise InvalidNetbiosName(netbiosname)
449
450     if dnsdomain is None:
451         dnsdomain = lp.get("realm")
452     assert dnsdomain is not None
453     dnsdomain = dnsdomain.lower()
454
455     if serverrole is None:
456         serverrole = lp.get("server role")
457     assert serverrole is not None
458     serverrole = serverrole.lower()
459
460     realm = dnsdomain.upper()
461
462     if lp.get("realm").upper() != realm:
463         raise ProvisioningError("guess_names: Realm '%s' in smb.conf must match chosen realm '%s'!", lp.get("realm").upper(), realm)
464
465     if serverrole == "domain controller":
466         if domain is None:
467             domain = lp.get("workgroup")
468         assert domain is not None
469         domain = domain.upper()
470
471         if lp.get("workgroup").upper() != domain:
472             raise ProvisioningError("guess_names: Workgroup '%s' in smb.conf must match chosen domain '%s'!", lp.get("workgroup").upper(), domain)
473
474         if domaindn is None:
475             domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
476     else:
477         domain = netbiosname
478         if domaindn is None:
479             domaindn = "DC=" + netbiosname
480         
481     if not valid_netbios_name(domain):
482         raise InvalidNetbiosName(domain)
483         
484     if hostname.upper() == realm:
485         raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!", realm, hostname)
486     if netbiosname == realm:
487         raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!", realm, netbiosname)
488     if domain == realm:
489         raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!", realm, domain)
490
491     if rootdn is None:
492        rootdn = domaindn
493        
494     if configdn is None:
495         configdn = "CN=Configuration," + rootdn
496     if schemadn is None:
497         schemadn = "CN=Schema," + configdn
498     if sambadn is None:
499         sambadn = "CN=Samba"
500
501     if sitename is None:
502         sitename=DEFAULTSITE
503
504     names = ProvisionNames()
505     names.rootdn = rootdn
506     names.domaindn = domaindn
507     names.configdn = configdn
508     names.schemadn = schemadn
509     names.sambadn = sambadn
510     names.ldapmanagerdn = "CN=Manager," + rootdn
511     names.dnsdomain = dnsdomain
512     names.domain = domain
513     names.realm = realm
514     names.netbiosname = netbiosname
515     names.hostname = hostname
516     names.sitename = sitename
517     names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
518  
519     return names
520     
521
522 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, 
523                  targetdir, sid_generator):
524     """Create a new smb.conf file based on a couple of basic settings.
525     """
526     assert smbconf is not None
527     if hostname is None:
528         hostname = socket.gethostname().split(".")[0]
529     netbiosname = hostname.upper()
530
531     if serverrole is None:
532         serverrole = "standalone"
533
534     assert serverrole in ("domain controller", "member server", "standalone")
535     if serverrole == "domain controller":
536         smbconfsuffix = "dc"
537     elif serverrole == "member server":
538         smbconfsuffix = "member"
539     elif serverrole == "standalone":
540         smbconfsuffix = "standalone"
541
542     if sid_generator is None:
543         sid_generator = "internal"
544
545     assert domain is not None
546     domain = domain.upper()
547
548     assert realm is not None
549     realm = realm.upper()
550
551     default_lp = param.LoadParm()
552     #Load non-existant file
553     if os.path.exists(smbconf):
554         default_lp.load(smbconf)
555     
556     if targetdir is not None:
557         privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
558         lockdir_line = "lock dir = " + os.path.abspath(targetdir)
559
560         default_lp.set("lock dir", os.path.abspath(targetdir))
561     else:
562         privatedir_line = ""
563         lockdir_line = ""
564
565     if sid_generator == "internal":
566         sid_generator_line = ""
567     else:
568         sid_generator_line = "sid generator = " + sid_generator
569
570     sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
571     netlogon = os.path.join(sysvol, realm.lower(), "scripts")
572
573     setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix), 
574                smbconf, {
575             "NETBIOS_NAME": netbiosname,
576             "DOMAIN": domain,
577             "REALM": realm,
578             "SERVERROLE": serverrole,
579             "NETLOGONPATH": netlogon,
580             "SYSVOLPATH": sysvol,
581             "SIDGENERATOR_LINE": sid_generator_line,
582             "PRIVATEDIR_LINE": privatedir_line,
583             "LOCKDIR_LINE": lockdir_line
584             })
585
586
587 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
588                         users_gid, wheel_gid):
589     """setup reasonable name mappings for sam names to unix names.
590
591     :param samdb: SamDB object.
592     :param idmap: IDmap db object.
593     :param sid: The domain sid.
594     :param domaindn: The domain DN.
595     :param root_uid: uid of the UNIX root user.
596     :param nobody_uid: uid of the UNIX nobody user.
597     :param users_gid: gid of the UNIX users group.
598     :param wheel_gid: gid of the UNIX wheel group."""
599
600     idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
601     idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
602     
603     idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
604     idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
605
606 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info, 
607                            provision_backend, names, schema,
608                            serverrole, 
609                            erase=False):
610     """Setup the partitions for the SAM database. 
611     
612     Alternatively, provision() may call this, and then populate the database.
613     
614     :note: This will wipe the Sam Database!
615     
616     :note: This function always removes the local SAM LDB file. The erase 
617         parameter controls whether to erase the existing data, which 
618         may not be stored locally but in LDAP.
619
620     """
621     assert session_info is not None
622
623     old_partitions = None
624     new_partitions = None
625
626     # We use options=["modules:"] to stop the modules loading - we
627     # just want to wipe and re-initialise the database, not start it up
628
629     try:
630         samdb = Ldb(url=samdb_path, session_info=session_info, 
631                       lp=lp, options=["modules:"])
632         res = samdb.search(base="@PARTITION", scope=SCOPE_BASE, attrs=["partition"], expression="partition=*")
633         if len(res) == 1:
634             try:
635                 old_partitions = res[0]["partition"]
636             except KeyError:
637                 pass
638
639         if old_partitions is not None:
640             new_partitions = [];
641             for old_partition in old_partitions:
642                 new_partition = old_partition
643                 if old_partition.endswith(".ldb"):
644                     p = old_partition.split(":")[0]
645                     dn = ldb.Dn(schema.ldb, p)
646                     new_partition = dn.get_casefold()
647                 new_partitions.append(new_partition)
648
649         # Wipes the database
650         samdb.erase_except_schema_controlled()
651     except LdbError:
652         os.unlink(samdb_path)
653         samdb = Ldb(url=samdb_path, session_info=session_info, 
654                       lp=lp, options=["modules:"])
655          # Wipes the database
656         samdb.erase_except_schema_controlled()
657
658     #Add modules to the list to activate them by default
659     #beware often order is important
660     #
661     # Some Known ordering constraints:
662     # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
663     # - objectclass must be before password_hash, because password_hash checks
664     #   that the objectclass is of type person (filled in by objectclass
665     #   module when expanding the objectclass list)
666     # - partition must be last
667     # - each partition has its own module list then
668     modules_list = ["resolve_oids",
669                     "rootdse",
670                     "lazy_commit",
671                     "acl",
672                     "paged_results",
673                     "ranged_results",
674                     "anr",
675                     "server_sort",
676                     "asq",
677                     "extended_dn_store",
678                     "extended_dn_in",
679                     "rdn_name",
680                     "objectclass",
681                     "descriptor",
682                     "samldb",
683                     "password_hash",
684                     "operational",
685                     "kludge_acl", 
686                     "instancetype"]
687     tdb_modules_list = [
688                     "subtree_rename",
689                     "subtree_delete",
690                     "linked_attributes",
691                     "extended_dn_out_ldb"]
692     modules_list2 = ["show_deleted",
693                      "schema_load",
694                      "new_partition",
695                      "partition"]
696
697     ldap_backend_line = "# No LDAP backend"
698     if provision_backend.type is not "ldb":
699         ldap_backend_line = "ldapBackend: %s" % provision_backend.ldapi_uri
700         
701         if provision_backend.ldap_backend_type == "fedora-ds":
702             backend_modules = ["nsuniqueid", "paged_searches"]
703             # We can handle linked attributes here, as we don't have directory-side subtree operations
704             tdb_modules_list = ["extended_dn_out_fds"]
705         elif ldap_backend.ldap_backend_type == "openldap":
706             backend_modules = ["entryuuid", "paged_searches"]
707             # OpenLDAP handles subtree renames, so we don't want to do any of these things
708             tdb_modules_list = ["extended_dn_out_openldap"]
709
710     elif serverrole == "domain controller":
711         tdb_modules_list.insert(0, "repl_meta_data")
712         backend_modules = []
713     else:
714         backend_modules = ["objectguid"]
715
716     if tdb_modules_list is None:
717         tdb_modules_list_as_string = ""
718     else:
719         tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
720         
721     samdb.transaction_start()
722     try:
723         message("Setting up sam.ldb partitions and settings")
724         setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
725                 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(), 
726                 "SCHEMADN_MOD2": ",objectguid",
727                 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
728                 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
729                 "SCHEMADN_MOD": "schema_data",
730                 "CONFIGDN_MOD": "naming_fsmo",
731                 "DOMAINDN_MOD": "pdc_fsmo",
732                 "MODULES_LIST": ",".join(modules_list),
733                 "TDB_MODULES_LIST": tdb_modules_list_as_string,
734                 "MODULES_LIST2": ",".join(modules_list2),
735                 "BACKEND_MOD": ",".join(backend_modules),
736                 "LDAP_BACKEND_LINE": ldap_backend_line,
737         })
738
739         
740         if new_partitions is not None:
741             m = ldb.Message()
742             m.dn = ldb.Dn(samdb, "@PARTITION")
743             
744             m["partition"] = ldb.MessageElement(new_partitions, ldb.FLAG_MOD_ADD, "partition")
745             samdb.modify(m)
746
747         samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
748
749         message("Setting up sam.ldb rootDSE")
750         setup_samdb_rootdse(samdb, setup_path, names)
751
752     except:
753         samdb.transaction_cancel()
754         raise
755
756     samdb.transaction_commit()
757
758         
759 def secretsdb_self_join(secretsdb, domain, 
760                         netbiosname, domainsid, machinepass, 
761                         realm=None, dnsdomain=None,
762                         keytab_path=None, 
763                         key_version_number=1,
764                         secure_channel_type=SEC_CHAN_WKSTA):
765     """Add domain join-specific bits to a secrets database.
766     
767     :param secretsdb: Ldb Handle to the secrets database
768     :param machinepass: Machine password
769     """
770     attrs=["whenChanged",
771            "secret",
772            "priorSecret",
773            "priorChanged",
774            "krb5Keytab",
775            "privateKeytab"]
776     
777
778     msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain));
779     msg["secureChannelType"] = str(secure_channel_type)
780     msg["flatname"] = [domain]
781     msg["objectClass"] = ["top", "primaryDomain"]
782     if realm is not None:
783       if dnsdomain is None:
784         dnsdomain = realm.lower()
785       msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
786       msg["realm"] = realm
787       msg["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())
788       msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
789       msg["privateKeytab"] = ["secrets.keytab"];
790
791
792     msg["secret"] = [machinepass]
793     msg["samAccountName"] = ["%s$" % netbiosname]
794     msg["secureChannelType"] = [str(secure_channel_type)]
795     msg["objectSid"] = [ndr_pack(domainsid)]
796     
797     res = secretsdb.search(base="cn=Primary Domains", 
798                            attrs=attrs, 
799                            expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))), 
800                            scope=SCOPE_ONELEVEL)
801     
802     for del_msg in res:
803       if del_msg.dn is not msg.dn:
804         secretsdb.delete(del_msg.dn)
805
806     res = secretsdb.search(base=msg.dn, attrs=attrs, scope=SCOPE_BASE)
807
808     if len(res) == 1:
809       msg["priorSecret"] = res[0]["secret"]
810       msg["priorWhenChanged"] = res[0]["whenChanged"]
811
812       if res["privateKeytab"] is not None:
813         msg["privateKeytab"] = res[0]["privateKeytab"]
814
815       if res["krb5Keytab"] is not None:
816         msg["krb5Keytab"] = res[0]["krb5Keytab"]
817
818       for el in msg:
819         el.set_flags(ldb.FLAG_MOD_REPLACE)
820         secretsdb.modify(msg)
821     else:
822       secretsdb.add(msg)
823
824
825 def secretsdb_setup_dns(secretsdb, setup_path, realm, dnsdomain, 
826                         dns_keytab_path, dnspass):
827     """Add DNS specific bits to a secrets database.
828     
829     :param secretsdb: Ldb Handle to the secrets database
830     :param setup_path: Setup path function
831     :param machinepass: Machine password
832     """
833     setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), { 
834             "REALM": realm,
835             "DNSDOMAIN": dnsdomain,
836             "DNS_KEYTAB": dns_keytab_path,
837             "DNSPASS_B64": b64encode(dnspass),
838             })
839
840
841 def setup_secretsdb(path, setup_path, session_info, backend_credentials, lp):
842     """Setup the secrets database.
843
844     :param path: Path to the secrets database.
845     :param setup_path: Get the path to a setup file.
846     :param session_info: Session info.
847     :param credentials: Credentials
848     :param lp: Loadparm context
849     :return: LDB handle for the created secrets database
850     """
851     if os.path.exists(path):
852         os.unlink(path)
853     secrets_ldb = Ldb(path, session_info=session_info, 
854                       lp=lp)
855     secrets_ldb.erase()
856     secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
857     secrets_ldb = Ldb(path, session_info=session_info, 
858                       lp=lp)
859     secrets_ldb.transaction_start()
860     secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
861
862     if backend_credentials is not None and backend_credentials.authentication_requested():
863         if backend_credentials.get_bind_dn() is not None:
864             setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
865                     "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
866                     "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
867                     })
868         else:
869             setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
870                     "LDAPADMINUSER": backend_credentials.get_username(),
871                     "LDAPADMINREALM": backend_credentials.get_realm(),
872                     "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
873                     })
874
875     return secrets_ldb
876
877 def setup_privileges(path, setup_path, session_info, lp):
878     """Setup the privileges database.
879
880     :param path: Path to the privileges database.
881     :param setup_path: Get the path to a setup file.
882     :param session_info: Session info.
883     :param credentials: Credentials
884     :param lp: Loadparm context
885     :return: LDB handle for the created secrets database
886     """
887     if os.path.exists(path):
888         os.unlink(path)
889     privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
890     privilege_ldb.erase()
891     privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
892
893
894 def setup_registry(path, setup_path, session_info, lp):
895     """Setup the registry.
896     
897     :param path: Path to the registry database
898     :param setup_path: Function that returns the path to a setup.
899     :param session_info: Session information
900     :param credentials: Credentials
901     :param lp: Loadparm context
902     """
903     reg = registry.Registry()
904     hive = registry.open_ldb(path, session_info=session_info, 
905                          lp_ctx=lp)
906     reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
907     provision_reg = setup_path("provision.reg")
908     assert os.path.exists(provision_reg)
909     reg.diff_apply(provision_reg)
910
911
912 def setup_idmapdb(path, setup_path, session_info, lp):
913     """Setup the idmap database.
914
915     :param path: path to the idmap database
916     :param setup_path: Function that returns a path to a setup file
917     :param session_info: Session information
918     :param credentials: Credentials
919     :param lp: Loadparm context
920     """
921     if os.path.exists(path):
922         os.unlink(path)
923
924     idmap_ldb = IDmapDB(path, session_info=session_info,
925                         lp=lp)
926
927     idmap_ldb.erase()
928     idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
929     return idmap_ldb
930
931
932 def setup_samdb_rootdse(samdb, setup_path, names):
933     """Setup the SamDB rootdse.
934
935     :param samdb: Sam Database handle
936     :param setup_path: Obtain setup path
937     """
938     setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
939         "SCHEMADN": names.schemadn, 
940         "NETBIOSNAME": names.netbiosname,
941         "DNSDOMAIN": names.dnsdomain,
942         "REALM": names.realm,
943         "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
944         "DOMAINDN": names.domaindn,
945         "ROOTDN": names.rootdn,
946         "CONFIGDN": names.configdn,
947         "SERVERDN": names.serverdn,
948         })
949         
950
951 def setup_self_join(samdb, names,
952                     machinepass, dnspass, 
953                     domainsid, invocationid, setup_path,
954                     policyguid, policyguid_dc, domainControllerFunctionality,
955                     ntdsguid):
956     """Join a host to its own domain."""
957     assert isinstance(invocationid, str)
958     if ntdsguid is not None:
959         ntdsguid_line = "objectGUID: %s\n"%ntdsguid
960     else:
961         ntdsguid_line = ""
962     setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), { 
963               "CONFIGDN": names.configdn, 
964               "SCHEMADN": names.schemadn,
965               "DOMAINDN": names.domaindn,
966               "SERVERDN": names.serverdn,
967               "INVOCATIONID": invocationid,
968               "NETBIOSNAME": names.netbiosname,
969               "DEFAULTSITE": names.sitename,
970               "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
971               "MACHINEPASS_B64": b64encode(machinepass),
972               "DNSPASS_B64": b64encode(dnspass),
973               "REALM": names.realm,
974               "DOMAIN": names.domain,
975               "DNSDOMAIN": names.dnsdomain,
976               "SAMBA_VERSION_STRING": version,
977               "NTDSGUID": ntdsguid_line,
978               "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
979
980     setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), { 
981               "POLICYGUID": policyguid,
982               "POLICYGUID_DC": policyguid_dc,
983               "DNSDOMAIN": names.dnsdomain,
984               "DOMAINSID": str(domainsid),
985               "DOMAINDN": names.domaindn})
986     
987     # add the NTDSGUID based SPNs
988     ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
989     names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
990                                      expression="", scope=SCOPE_BASE)
991     assert isinstance(names.ntdsguid, str)
992
993     # Setup fSMORoleOwner entries to point at the newly created DC entry
994     setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
995               "DOMAIN": names.domain,
996               "DNSDOMAIN": names.dnsdomain,
997               "DOMAINDN": names.domaindn,
998               "CONFIGDN": names.configdn,
999               "SCHEMADN": names.schemadn, 
1000               "DEFAULTSITE": names.sitename,
1001               "SERVERDN": names.serverdn,
1002               "NETBIOSNAME": names.netbiosname,
1003               "NTDSGUID": names.ntdsguid
1004               })
1005
1006
1007 def setup_samdb(path, setup_path, session_info, provision_backend, lp, 
1008                 names, message, 
1009                 domainsid, domainguid, policyguid, policyguid_dc,
1010                 fill, adminpass, krbtgtpass, 
1011                 machinepass, invocationid, dnspass, ntdsguid,
1012                 serverrole, dom_for_fun_level=None,
1013                 schema=None):
1014     """Setup a complete SAM Database.
1015     
1016     :note: This will wipe the main SAM database file!
1017     """
1018
1019     # ATTENTION: Do NOT change these default values without discussion with the
1020     # team and/or release manager. They have a big impact on the whole program!
1021     domainControllerFunctionality = DS_DC_FUNCTION_2008
1022
1023     if dom_for_fun_level is None:
1024         dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1025     if dom_for_fun_level < DS_DOMAIN_FUNCTION_2003:
1026         raise ProvisioningError("You want to run SAMBA 4 on a domain and forest function level lower than Windows 2003 (Native). This isn't supported!")
1027
1028     if dom_for_fun_level > domainControllerFunctionality:
1029         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!")
1030
1031     domainFunctionality = dom_for_fun_level
1032     forestFunctionality = dom_for_fun_level
1033
1034     # Also wipes the database
1035     setup_samdb_partitions(path, setup_path, message=message, lp=lp,
1036                            provision_backend=provision_backend, session_info=session_info,
1037                            names=names, 
1038                            serverrole=serverrole, schema=schema)
1039
1040     if (schema == None):
1041         schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn,
1042                         sambadn=names.sambadn)
1043
1044     # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
1045     samdb = Ldb(session_info=session_info, 
1046                 credentials=provision_backend.credentials, lp=lp)
1047
1048     message("Pre-loading the Samba 4 and AD schema")
1049
1050     # Load the schema from the one we computed earlier
1051     samdb.set_schema_from_ldb(schema.ldb)
1052
1053     # And now we can connect to the DB - the schema won't be loaded from the DB
1054     samdb.connect(path)
1055
1056     if fill == FILL_DRS:
1057         return samdb
1058         
1059     samdb.transaction_start()
1060     try:
1061         message("Erasing data from partitions")
1062         # Load the schema (again).  This time it will force a reindex,
1063         # and will therefore make the erase_partitions() below
1064         # computationally sane
1065         samdb.set_schema_from_ldb(schema.ldb)
1066         samdb.erase_partitions()
1067     
1068         # Set the domain functionality levels onto the database.
1069         # Various module (the password_hash module in particular) need
1070         # to know what level of AD we are emulating.
1071
1072         # These will be fixed into the database via the database
1073         # modifictions below, but we need them set from the start.
1074         samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1075         samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1076         samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
1077
1078         samdb.set_domain_sid(str(domainsid))
1079         if serverrole == "domain controller":
1080             samdb.set_invocation_id(invocationid)
1081
1082         message("Adding DomainDN: %s" % names.domaindn)
1083
1084 #impersonate domain admin
1085         admin_session_info = admin_session(lp, str(domainsid))
1086         samdb.set_session_info(admin_session_info)
1087         if domainguid is not None:
1088             domainguid_line = "objectGUID: %s\n-" % domainguid
1089         else:
1090             domainguid_line = ""
1091         setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1092                 "DOMAINDN": names.domaindn,
1093                 "DOMAINGUID": domainguid_line
1094                 })
1095
1096
1097         setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1098             "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1099             "DOMAINSID": str(domainsid),
1100             "SCHEMADN": names.schemadn, 
1101             "NETBIOSNAME": names.netbiosname,
1102             "DEFAULTSITE": names.sitename,
1103             "CONFIGDN": names.configdn,
1104             "SERVERDN": names.serverdn,
1105             "POLICYGUID": policyguid,
1106             "DOMAINDN": names.domaindn,
1107             "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1108             "SAMBA_VERSION_STRING": version
1109             })
1110
1111         message("Adding configuration container")
1112         descr = get_config_descriptor(domainsid);
1113         setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1114             "CONFIGDN": names.configdn, 
1115             "DESCRIPTOR": descr,
1116             })
1117         message("Modifying configuration container")
1118         setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
1119             "CONFIGDN": names.configdn, 
1120             "SCHEMADN": names.schemadn,
1121             })
1122
1123         # The LDIF here was created when the Schema object was constructed
1124         message("Setting up sam.ldb schema")
1125         samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1126         samdb.modify_ldif(schema.schema_dn_modify)
1127         samdb.write_prefixes_from_schema()
1128         samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1129         setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"), 
1130                        {"SCHEMADN": names.schemadn})
1131
1132         message("Setting up sam.ldb configuration data")
1133         setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1134             "CONFIGDN": names.configdn,
1135             "NETBIOSNAME": names.netbiosname,
1136             "DEFAULTSITE": names.sitename,
1137             "DNSDOMAIN": names.dnsdomain,
1138             "DOMAIN": names.domain,
1139             "SCHEMADN": names.schemadn,
1140             "DOMAINDN": names.domaindn,
1141             "SERVERDN": names.serverdn,
1142             "FOREST_FUNCTIONALALITY": str(forestFunctionality)
1143             })
1144
1145         message("Setting up display specifiers")
1146         display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1147         display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
1148         check_all_substituted(display_specifiers_ldif)
1149         samdb.add_ldif(display_specifiers_ldif)
1150
1151         message("Adding users container")
1152         setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1153                 "DOMAINDN": names.domaindn})
1154         message("Modifying users container")
1155         setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1156                 "DOMAINDN": names.domaindn})
1157         message("Adding computers container")
1158         setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1159                 "DOMAINDN": names.domaindn})
1160         message("Modifying computers container")
1161         setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1162                 "DOMAINDN": names.domaindn})
1163         message("Setting up sam.ldb data")
1164         setup_add_ldif(samdb, setup_path("provision.ldif"), {
1165             "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1166             "DOMAINDN": names.domaindn,
1167             "NETBIOSNAME": names.netbiosname,
1168             "DEFAULTSITE": names.sitename,
1169             "CONFIGDN": names.configdn,
1170             "SERVERDN": names.serverdn,
1171             "POLICYGUID_DC": policyguid_dc
1172             })
1173
1174         if fill == FILL_FULL:
1175             message("Setting up sam.ldb users and groups")
1176             setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1177                 "DOMAINDN": names.domaindn,
1178                 "DOMAINSID": str(domainsid),
1179                 "CONFIGDN": names.configdn,
1180                 "ADMINPASS_B64": b64encode(adminpass),
1181                 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1182                 })
1183
1184             if serverrole == "domain controller":
1185                 message("Setting up self join")
1186                 setup_self_join(samdb, names=names, invocationid=invocationid, 
1187                                 dnspass=dnspass,  
1188                                 machinepass=machinepass, 
1189                                 domainsid=domainsid, policyguid=policyguid,
1190                                 policyguid_dc=policyguid_dc,
1191                                 setup_path=setup_path,
1192                                 domainControllerFunctionality=domainControllerFunctionality,
1193                                 ntdsguid=ntdsguid)
1194
1195                 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
1196                 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1197                   attribute="objectGUID", expression="", scope=SCOPE_BASE)
1198                 assert isinstance(names.ntdsguid, str)
1199
1200     except:
1201         samdb.transaction_cancel()
1202         raise
1203
1204     samdb.transaction_commit()
1205     return samdb
1206
1207
1208 FILL_FULL = "FULL"
1209 FILL_NT4SYNC = "NT4SYNC"
1210 FILL_DRS = "DRS"
1211
1212
1213 def provision(setup_dir, message, session_info, 
1214               credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1215               realm=None, 
1216               rootdn=None, domaindn=None, schemadn=None, configdn=None, 
1217               serverdn=None,
1218               domain=None, hostname=None, hostip=None, hostip6=None, 
1219               domainsid=None, adminpass=None, ldapadminpass=None, 
1220               krbtgtpass=None, domainguid=None, 
1221               policyguid=None, policyguid_dc=None, invocationid=None,
1222               machinepass=None, ntdsguid=None,
1223               dnspass=None, root=None, nobody=None, users=None, 
1224               wheel=None, backup=None, aci=None, serverrole=None,
1225               dom_for_fun_level=None,
1226               ldap_backend_extra_port=None, backend_type=None,
1227               sitename=None,
1228               ol_mmr_urls=None, ol_olc=None, 
1229               setup_ds_path=None, slapd_path=None, nosync=False,
1230               ldap_dryrun_mode=False):
1231     """Provision samba4
1232     
1233     :note: caution, this wipes all existing data!
1234     """
1235
1236     def setup_path(file):
1237       return os.path.join(setup_dir, file)
1238
1239     if domainsid is None:
1240       domainsid = security.random_sid()
1241     else:
1242       domainsid = security.dom_sid(domainsid)
1243
1244     # create/adapt the group policy GUIDs
1245     if policyguid is None:
1246         policyguid = str(uuid.uuid4())
1247     policyguid = policyguid.upper()
1248     if policyguid_dc is None:
1249         policyguid_dc = str(uuid.uuid4())
1250     policyguid_dc = policyguid_dc.upper()
1251
1252     if adminpass is None:
1253         adminpass = glue.generate_random_str(12)
1254     if krbtgtpass is None:
1255         krbtgtpass = glue.generate_random_str(12)
1256     if machinepass is None:
1257         machinepass  = glue.generate_random_str(12)
1258     if dnspass is None:
1259         dnspass = glue.generate_random_str(12)
1260     if ldapadminpass is None:
1261         #Make a new, random password between Samba and it's LDAP server
1262         ldapadminpass=glue.generate_random_str(12)        
1263
1264     if backend_type is None:
1265         backend_type = "ldb"
1266
1267     sid_generator = "internal"
1268     if backend_type == "fedora-ds":
1269         sid_generator = "backend"
1270
1271     root_uid = findnss_uid([root or "root"])
1272     nobody_uid = findnss_uid([nobody or "nobody"])
1273     users_gid = findnss_gid([users or "users"])
1274     if wheel is None:
1275         wheel_gid = findnss_gid(["wheel", "adm"])
1276     else:
1277         wheel_gid = findnss_gid([wheel])
1278
1279     if targetdir is not None:
1280         if (not os.path.exists(os.path.join(targetdir, "etc"))):
1281             os.makedirs(os.path.join(targetdir, "etc"))
1282         smbconf = os.path.join(targetdir, "etc", "smb.conf")
1283     elif smbconf is None:
1284         smbconf = param.default_path()
1285
1286     # only install a new smb.conf if there isn't one there already
1287     if not os.path.exists(smbconf):
1288         make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, 
1289                      targetdir, sid_generator)
1290
1291     lp = param.LoadParm()
1292     lp.load(smbconf)
1293
1294     names = guess_names(lp=lp, hostname=hostname, domain=domain,
1295                         dnsdomain=realm, serverrole=serverrole,
1296                         domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1297                         serverdn=serverdn, sitename=sitename)
1298
1299     paths = provision_paths_from_lp(lp, names.dnsdomain)
1300
1301     if hostip is None:
1302         try:
1303             hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1304         except socket.gaierror, (socket.EAI_NODATA, msg):
1305             hostip = None
1306
1307     if hostip6 is None:
1308         try:
1309             hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1310         except socket.gaierror, (socket.EAI_NODATA, msg): 
1311             hostip6 = None
1312
1313     if serverrole is None:
1314         serverrole = lp.get("server role")
1315
1316     assert serverrole in ("domain controller", "member server", "standalone")
1317     if invocationid is None and serverrole == "domain controller":
1318         invocationid = str(uuid.uuid4())
1319
1320     if not os.path.exists(paths.private_dir):
1321         os.mkdir(paths.private_dir)
1322
1323     ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1324     
1325     schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn,
1326                     sambadn=names.sambadn)
1327     
1328     provision_backend = ProvisionBackend(backend_type,
1329                                          paths=paths, setup_path=setup_path,
1330                                          lp=lp, credentials=credentials, 
1331                                          names=names,
1332                                          message=message, hostname=hostname,
1333                                          root=root, schema=schema,
1334                                          ldapadminpass=ldapadminpass,
1335                                          ldap_backend_extra_port=ldap_backend_extra_port,
1336                                          ol_mmr_urls=ol_mmr_urls, 
1337                                          slapd_path=slapd_path,
1338                                          setup_ds_path=setup_ds_path,
1339                                          ldap_dryrun_mode=ldap_dryrun_mode,
1340                                          domainsid=domainsid)
1341
1342     # only install a new shares config db if there is none
1343     if not os.path.exists(paths.shareconf):
1344         message("Setting up share.ldb")
1345         share_ldb = Ldb(paths.shareconf, session_info=session_info, 
1346                         lp=lp)
1347         share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1348
1349      
1350     message("Setting up secrets.ldb")
1351     secrets_ldb = setup_secretsdb(paths.secrets, setup_path, 
1352                                   session_info=session_info, 
1353                                   backend_credentials=provision_backend.secrets_credentials, lp=lp)
1354
1355     message("Setting up the registry")
1356     setup_registry(paths.hklm, setup_path, session_info, 
1357                    lp=lp)
1358
1359     message("Setting up the privileges database")
1360     setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1361
1362     message("Setting up idmap db")
1363     idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1364                           lp=lp)
1365
1366     message("Setting up SAM db")
1367     samdb = setup_samdb(paths.samdb, setup_path, session_info, 
1368                         provision_backend, lp, names,
1369                         message, 
1370                         domainsid=domainsid, 
1371                         schema=schema, domainguid=domainguid,
1372                         policyguid=policyguid, policyguid_dc=policyguid_dc,
1373                         fill=samdb_fill, 
1374                         adminpass=adminpass, krbtgtpass=krbtgtpass,
1375                         invocationid=invocationid, 
1376                         machinepass=machinepass, dnspass=dnspass, 
1377                         ntdsguid=ntdsguid, serverrole=serverrole,
1378                         dom_for_fun_level=dom_for_fun_level)
1379
1380     if serverrole == "domain controller":
1381         if paths.netlogon is None:
1382             message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1383             message("Please either remove %s or see the template at %s" % 
1384                     ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1385             assert(paths.netlogon is not None)
1386
1387         if paths.sysvol is None:
1388             message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1389             message("Please either remove %s or see the template at %s" % 
1390                     (paths.smbconf, setup_path("provision.smb.conf.dc")))
1391             assert(paths.sysvol is not None)            
1392             
1393         # Set up group policies (domain policy and domain controller policy)
1394
1395         policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1396                                    "{" + policyguid + "}")
1397         os.makedirs(policy_path, 0755)
1398         open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1399                                    "[General]\r\nVersion=65543")
1400         os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
1401         os.makedirs(os.path.join(policy_path, "USER"), 0755)
1402
1403         policy_path_dc = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1404                                    "{" + policyguid_dc + "}")
1405         os.makedirs(policy_path_dc, 0755)
1406         open(os.path.join(policy_path_dc, "GPT.INI"), 'w').write(
1407                                    "[General]\r\nVersion=2")
1408         os.makedirs(os.path.join(policy_path_dc, "MACHINE"), 0755)
1409         os.makedirs(os.path.join(policy_path_dc, "USER"), 0755)
1410
1411         if not os.path.isdir(paths.netlogon):
1412             os.makedirs(paths.netlogon, 0755)
1413
1414     if samdb_fill == FILL_FULL:
1415         setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1416                             root_uid=root_uid, nobody_uid=nobody_uid,
1417                             users_gid=users_gid, wheel_gid=wheel_gid)
1418
1419         message("Setting up sam.ldb rootDSE marking as synchronized")
1420         setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1421
1422         # Only make a zone file on the first DC, it should be replicated with DNS replication
1423         if serverrole == "domain controller":
1424             secretsdb_self_join(secrets_ldb, domain=domain,
1425                                 realm=names.realm,
1426                                 dnsdomain=names.dnsdomain,
1427                                 netbiosname=names.netbiosname,
1428                                 domainsid=domainsid, 
1429                                 machinepass=machinepass,
1430                                 secure_channel_type=SEC_CHAN_BDC)
1431
1432             secretsdb_setup_dns(secrets_ldb, setup_path, 
1433                                 realm=names.realm, dnsdomain=names.dnsdomain,
1434                                 dns_keytab_path=paths.dns_keytab,
1435                                 dnspass=dnspass)
1436
1437             domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1438             assert isinstance(domainguid, str)
1439
1440             create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1441                              hostip=hostip,
1442                              hostip6=hostip6, hostname=names.hostname,
1443                              realm=names.realm,
1444                              domainguid=domainguid, ntdsguid=names.ntdsguid)
1445
1446             create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1447                               dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1448
1449             create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1450                               dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1451                               keytab_name=paths.dns_keytab)
1452             message("See %s for an example configuration include file for BIND" % paths.namedconf)
1453             message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1454
1455             create_krb5_conf(paths.krb5conf, setup_path,
1456                              dnsdomain=names.dnsdomain, hostname=names.hostname,
1457                              realm=names.realm)
1458             message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1459
1460     if provision_backend.post_setup is not None:
1461         provision_backend.post_setup()
1462
1463     if provision_backend.shutdown is not None:
1464         provision_backend.shutdown()
1465     
1466     create_phpldapadmin_config(paths.phpldapadminconfig, setup_path, 
1467                                ldapi_url)
1468
1469     #Now commit the secrets.ldb to disk
1470     secrets_ldb.transaction_commit()
1471
1472     message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1473
1474     message("Once the above files are installed, your Samba4 server will be ready to use")
1475     message("Server Role:           %s" % serverrole)
1476     message("Hostname:              %s" % names.hostname)
1477     message("NetBIOS Domain:        %s" % names.domain)
1478     message("DNS Domain:            %s" % names.dnsdomain)
1479     message("DOMAIN SID:            %s" % str(domainsid))
1480     if samdb_fill == FILL_FULL:
1481         message("Admin password:    %s" % adminpass)
1482     if provision_backend.type is not "ldb":
1483         if provision_backend.credentials.get_bind_dn() is not None:
1484             message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1485         else:
1486             message("LDAP Admin User:       %s" % provision_backend.credentials.get_username())
1487
1488         message("LDAP Admin Password:   %s" % provision_backend.credentials.get_password())
1489
1490         if provision_backend.slapd_command_escaped is not None:
1491             # now display slapd_command_file.txt to show how slapd must be started next time
1492             message("Use later the following commandline to start slapd, then Samba:")
1493             message(provision_backend.slapd_command_escaped)
1494             message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh")
1495
1496
1497     result = ProvisionResult()
1498     result.domaindn = domaindn
1499     result.paths = paths
1500     result.lp = lp
1501     result.samdb = samdb
1502     return result
1503
1504
1505
1506 def provision_become_dc(setup_dir=None,
1507                         smbconf=None, targetdir=None, realm=None, 
1508                         rootdn=None, domaindn=None, schemadn=None,
1509                         configdn=None, serverdn=None,
1510                         domain=None, hostname=None, domainsid=None, 
1511                         adminpass=None, krbtgtpass=None, domainguid=None, 
1512                         policyguid=None, policyguid_dc=None, invocationid=None,
1513                         machinepass=None, 
1514                         dnspass=None, root=None, nobody=None, users=None, 
1515                         wheel=None, backup=None, serverrole=None, 
1516                         ldap_backend=None, ldap_backend_type=None,
1517                         sitename=None, debuglevel=1):
1518
1519     def message(text):
1520         """print a message if quiet is not set."""
1521         print text
1522
1523     glue.set_debug_level(debuglevel)
1524
1525     return provision(setup_dir, message, system_session(), None,
1526               smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1527               realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1528               configdn=configdn, serverdn=serverdn, domain=domain,
1529               hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1530               machinepass=machinepass, serverrole="domain controller",
1531               sitename=sitename)
1532
1533
1534 def setup_db_config(setup_path, dbdir):
1535     """Setup a Berkeley database.
1536     
1537     :param setup_path: Setup path function.
1538     :param dbdir: Database directory."""
1539     if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
1540         os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700)
1541         if not os.path.isdir(os.path.join(dbdir, "tmp")):
1542             os.makedirs(os.path.join(dbdir, "tmp"), 0700)
1543
1544     setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
1545                {"LDAPDBDIR": dbdir})
1546
1547 def ldap_backend_shutdown(self):
1548       # if an LDAP backend is in use, terminate slapd after final provision and check its proper termination
1549       if self.slapd.poll() is None:
1550         #Kill the slapd
1551         if hasattr(self.slapd, "terminate"):
1552           self.slapd.terminate()
1553         else:
1554           # Older python versions don't have .terminate()
1555           import signal
1556           os.kill(self.slapd.pid, signal.SIGTERM)
1557             
1558         #and now wait for it to die
1559         self.slapd.communicate()
1560
1561
1562 class ProvisionBackend(object):
1563     def __init__(self, backend_type, paths=None, setup_path=None, lp=None, credentials=None, 
1564                  names=None, message=None, 
1565                  hostname=None, root=None, 
1566                  schema=None, ldapadminpass=None,
1567                  ldap_backend_extra_port=None,
1568                  ol_mmr_urls=None, 
1569                  setup_ds_path=None, slapd_path=None, 
1570                  nosync=False, ldap_dryrun_mode=False,
1571                  domainsid=None):
1572         """Provision an LDAP backend for samba4
1573         
1574         This works for OpenLDAP and Fedora DS
1575         """
1576         self.paths = paths
1577         self.slapd_command = None
1578         self.slapd_command_escaped = None
1579
1580         self.type = backend_type
1581         
1582         # Set a default - the code for "existing" below replaces this
1583         self.ldap_backend_type = backend_type
1584
1585         self.post_setup = None
1586         self.shutdown = None
1587
1588         if self.type is "ldb":
1589             self.credentials = None
1590             self.secrets_credentials = None
1591             return
1592
1593         self.ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.ldapdir, "ldapi"), safe="")
1594         
1595         if self.type == "existing":
1596             #Check to see that this 'existing' LDAP backend in fact exists
1597             ldapi_db = Ldb(self.ldapi_uri, credentials=credentials)
1598             search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1599                                                 expression="(objectClass=OpenLDAProotDSE)")
1600
1601             # If we have got here, then we must have a valid connection to the LDAP server, with valid credentials supplied
1602             self.credentials = credentials
1603             # This caused them to be set into the long-term database later in the script.
1604             self.secrets_credentials = credentials
1605
1606             self.ldap_backend_type = "openldap" #For now, assume existing backends at least emulate OpenLDAP
1607             return
1608     
1609         # we will shortly start slapd with ldapi for final provisioning. first check with ldapsearch -> rootDSE via self.ldapi_uri
1610         # if another instance of slapd is already running 
1611         try:
1612             ldapi_db = Ldb(self.ldapi_uri)
1613             search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1614                                                 expression="(objectClass=OpenLDAProotDSE)");
1615             try:
1616                 f = open(paths.slapdpid, "r")
1617                 p = f.read()
1618                 f.close()
1619                 message("Check for slapd Process with PID: " + str(p) + " and terminate it manually.")
1620             except:
1621                 pass
1622             
1623             raise ProvisioningError("Warning: Another slapd Instance seems already running on this host, listening to " + self.ldapi_uri + ". Please shut it down before you continue. ")
1624         
1625         except LdbError, e:
1626             pass
1627
1628         # Try to print helpful messages when the user has not specified the path to slapd
1629         if slapd_path is None:
1630             raise ProvisioningError("Warning: LDAP-Backend must be setup with path to slapd, e.g. --slapd-path=\"/usr/local/libexec/slapd\"!")
1631         if not os.path.exists(slapd_path):
1632             message (slapd_path)
1633             raise ProvisioningError("Warning: Given Path to slapd does not exist!")
1634
1635
1636         if not os.path.isdir(paths.ldapdir):
1637             os.makedirs(paths.ldapdir, 0700)
1638
1639         schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
1640         try:
1641             os.unlink(schemadb_path)
1642         except OSError:
1643             pass
1644
1645         # Put the LDIF of the schema into a database so we can search on
1646         # it to generate schema-dependent configurations in Fedora DS and
1647         # OpenLDAP
1648         os.path.join(paths.ldapdir, "schema-tmp.ldb")
1649         schema.ldb.connect(schemadb_path)
1650         schema.ldb.transaction_start()
1651     
1652         # These bits of LDIF are supplied when the Schema object is created
1653         schema.ldb.add_ldif(schema.schema_dn_add)
1654         schema.ldb.modify_ldif(schema.schema_dn_modify)
1655         schema.ldb.add_ldif(schema.schema_data)
1656         schema.ldb.transaction_commit()
1657
1658         self.credentials = Credentials()
1659         self.credentials.guess(lp)
1660         #Kerberos to an ldapi:// backend makes no sense
1661         self.credentials.set_kerberos_state(DONT_USE_KERBEROS)
1662
1663         self.secrets_credentials = Credentials()
1664         self.secrets_credentials.guess(lp)
1665         #Kerberos to an ldapi:// backend makes no sense
1666         self.secrets_credentials.set_kerberos_state(DONT_USE_KERBEROS)
1667
1668         self.shutdown = ldap_backend_shutdown
1669
1670         if self.type == "fedora-ds":
1671             provision_fds_backend(self, setup_path=setup_path,
1672                                   names=names, message=message, 
1673                                   hostname=hostname,
1674                                   ldapadminpass=ldapadminpass, root=root, 
1675                                   schema=schema,
1676                                   ldap_backend_extra_port=ldap_backend_extra_port, 
1677                                   setup_ds_path=setup_ds_path,
1678                                   slapd_path=slapd_path,
1679                                   nosync=nosync,
1680                                   ldap_dryrun_mode=ldap_dryrun_mode,
1681                                   domainsid=domainsid)
1682             
1683         elif self.type == "openldap":
1684             provision_openldap_backend(self, setup_path=setup_path,
1685                                        names=names, message=message, 
1686                                        hostname=hostname,
1687                                        ldapadminpass=ldapadminpass, root=root, 
1688                                        schema=schema,
1689                                        ldap_backend_extra_port=ldap_backend_extra_port, 
1690                                        ol_mmr_urls=ol_mmr_urls, 
1691                                        slapd_path=slapd_path,
1692                                        nosync=nosync,
1693                                        ldap_dryrun_mode=ldap_dryrun_mode)
1694         else:
1695             raise ProvisioningError("Unknown LDAP backend type selected")
1696
1697         self.credentials.set_password(ldapadminpass)
1698         self.secrets_credentials.set_username("samba-admin")
1699         self.secrets_credentials.set_password(ldapadminpass)
1700
1701         self.slapd_command_escaped = "\'" + "\' \'".join(self.slapd_command) + "\'"
1702         setup_file(setup_path("ldap_backend_startup.sh"), paths.ldapdir + "/ldap_backend_startup.sh", {
1703                 "SLAPD_COMMAND" : slapd_command})
1704
1705         # Now start the slapd, so we can provision onto it.  We keep the
1706         # subprocess context around, to kill this off at the successful
1707         # end of the script
1708         self.slapd = subprocess.Popen(self.slapd_provision_command, close_fds=True, shell=False)
1709     
1710         while self.slapd.poll() is None:
1711             # Wait until the socket appears
1712             try:
1713                 ldapi_db = Ldb(self.ldapi_uri, lp=lp, credentials=self.credentials)
1714                 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1715                                                     expression="(objectClass=OpenLDAProotDSE)")
1716                 # If we have got here, then we must have a valid connection to the LDAP server!
1717                 return
1718             except LdbError, e:
1719                 time.sleep(1)
1720                 pass
1721         
1722         raise ProvisioningError("slapd died before we could make a connection to it")
1723
1724
1725 def provision_openldap_backend(result, setup_path=None, names=None,
1726                                message=None, 
1727                                hostname=None, ldapadminpass=None, root=None, 
1728                                schema=None, 
1729                                ldap_backend_extra_port=None,
1730                                ol_mmr_urls=None, 
1731                                slapd_path=None, nosync=False,
1732                                ldap_dryrun_mode=False):
1733
1734     #Allow the test scripts to turn off fsync() for OpenLDAP as for TDB and LDB
1735     nosync_config = ""
1736     if nosync:
1737         nosync_config = "dbnosync"
1738         
1739     lnkattr = get_linked_attributes(names.schemadn,schema.ldb)
1740     refint_attributes = ""
1741     memberof_config = "# Generated from Samba4 schema\n"
1742     for att in  lnkattr.keys():
1743         if lnkattr[att] is not None:
1744             refint_attributes = refint_attributes + " " + att 
1745             
1746             memberof_config += read_and_sub_file(setup_path("memberof.conf"),
1747                                                  { "MEMBER_ATTR" : att ,
1748                                                    "MEMBEROF_ATTR" : lnkattr[att] })
1749             
1750     refint_config = read_and_sub_file(setup_path("refint.conf"),
1751                                       { "LINK_ATTRS" : refint_attributes})
1752     
1753     attrs = ["linkID", "lDAPDisplayName"]
1754     res = schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
1755     index_config = ""
1756     for i in range (0, len(res)):
1757         index_attr = res[i]["lDAPDisplayName"][0]
1758         if index_attr == "objectGUID":
1759             index_attr = "entryUUID"
1760             
1761         index_config += "index " + index_attr + " eq\n"
1762
1763 # generate serverids, ldap-urls and syncrepl-blocks for mmr hosts
1764     mmr_on_config = ""
1765     mmr_replicator_acl = ""
1766     mmr_serverids_config = ""
1767     mmr_syncrepl_schema_config = "" 
1768     mmr_syncrepl_config_config = "" 
1769     mmr_syncrepl_user_config = "" 
1770        
1771     
1772     if ol_mmr_urls is not None:
1773         # For now, make these equal
1774         mmr_pass = ldapadminpass
1775         
1776         url_list=filter(None,ol_mmr_urls.split(' ')) 
1777         if (len(url_list) == 1):
1778             url_list=filter(None,ol_mmr_urls.split(',')) 
1779                      
1780             
1781             mmr_on_config = "MirrorMode On"
1782             mmr_replicator_acl = "  by dn=cn=replicator,cn=samba read"
1783             serverid=0
1784             for url in url_list:
1785                 serverid=serverid+1
1786                 mmr_serverids_config += read_and_sub_file(setup_path("mmr_serverids.conf"),
1787                                                           { "SERVERID" : str(serverid),
1788                                                             "LDAPSERVER" : url })
1789                 rid=serverid*10
1790                 rid=rid+1
1791                 mmr_syncrepl_schema_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1792                                                                 {  "RID" : str(rid),
1793                                                                    "MMRDN": names.schemadn,
1794                                                                    "LDAPSERVER" : url,
1795                                                                    "MMR_PASSWORD": mmr_pass})
1796                 
1797                 rid=rid+1
1798                 mmr_syncrepl_config_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1799                                                                 {  "RID" : str(rid),
1800                                                                    "MMRDN": names.configdn,
1801                                                                    "LDAPSERVER" : url,
1802                                                                    "MMR_PASSWORD": mmr_pass})
1803                 
1804                 rid=rid+1
1805                 mmr_syncrepl_user_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1806                                                               {  "RID" : str(rid),
1807                                                                  "MMRDN": names.domaindn,
1808                                                                  "LDAPSERVER" : url,
1809                                                                  "MMR_PASSWORD": mmr_pass })
1810     # OpenLDAP cn=config initialisation
1811     olc_syncrepl_config = ""
1812     olc_mmr_config = "" 
1813     # if mmr = yes, generate cn=config-replication directives
1814     # and olc_seed.lif for the other mmr-servers
1815     if ol_mmr_urls is not None:
1816         serverid=0
1817         olc_serverids_config = ""
1818         olc_syncrepl_seed_config = ""
1819         olc_mmr_config += read_and_sub_file(setup_path("olc_mmr.conf"),{})
1820         rid=1000
1821         for url in url_list:
1822             serverid=serverid+1
1823             olc_serverids_config += read_and_sub_file(setup_path("olc_serverid.conf"),
1824                                                       { "SERVERID" : str(serverid),
1825                                                         "LDAPSERVER" : url })
1826             
1827             rid=rid+1
1828             olc_syncrepl_config += read_and_sub_file(setup_path("olc_syncrepl.conf"),
1829                                                      {  "RID" : str(rid),
1830                                                         "LDAPSERVER" : url,
1831                                                         "MMR_PASSWORD": mmr_pass})
1832             
1833             olc_syncrepl_seed_config += read_and_sub_file(setup_path("olc_syncrepl_seed.conf"),
1834                                                           {  "RID" : str(rid),
1835                                                              "LDAPSERVER" : url})
1836                 
1837         setup_file(setup_path("olc_seed.ldif"), result.paths.olcseedldif,
1838                    {"OLC_SERVER_ID_CONF": olc_serverids_config,
1839                     "OLC_PW": ldapadminpass,
1840                     "OLC_SYNCREPL_CONF": olc_syncrepl_seed_config})
1841     # end olc
1842                 
1843     setup_file(setup_path("slapd.conf"), result.paths.slapdconf,
1844                {"DNSDOMAIN": names.dnsdomain,
1845                 "LDAPDIR": result.paths.ldapdir,
1846                 "DOMAINDN": names.domaindn,
1847                 "CONFIGDN": names.configdn,
1848                 "SCHEMADN": names.schemadn,
1849                 "MEMBEROF_CONFIG": memberof_config,
1850                 "MIRRORMODE": mmr_on_config,
1851                 "REPLICATOR_ACL": mmr_replicator_acl,
1852                 "MMR_SERVERIDS_CONFIG": mmr_serverids_config,
1853                 "MMR_SYNCREPL_SCHEMA_CONFIG": mmr_syncrepl_schema_config,
1854                 "MMR_SYNCREPL_CONFIG_CONFIG": mmr_syncrepl_config_config,
1855                 "MMR_SYNCREPL_USER_CONFIG": mmr_syncrepl_user_config,
1856                 "OLC_SYNCREPL_CONFIG": olc_syncrepl_config,
1857                 "OLC_MMR_CONFIG": olc_mmr_config,
1858                 "REFINT_CONFIG": refint_config,
1859                 "INDEX_CONFIG": index_config,
1860                 "NOSYNC": nosync_config})
1861         
1862     setup_db_config(setup_path, os.path.join(result.paths.ldapdir, "db", "user"))
1863     setup_db_config(setup_path, os.path.join(result.paths.ldapdir, "db", "config"))
1864     setup_db_config(setup_path, os.path.join(result.paths.ldapdir, "db", "schema"))
1865     
1866     if not os.path.exists(os.path.join(result.paths.ldapdir, "db", "samba",  "cn=samba")):
1867         os.makedirs(os.path.join(result.paths.ldapdir, "db", "samba",  "cn=samba"), 0700)
1868         
1869     setup_file(setup_path("cn=samba.ldif"), 
1870                os.path.join(result.paths.ldapdir, "db", "samba",  "cn=samba.ldif"),
1871                { "UUID": str(uuid.uuid4()), 
1872                  "LDAPTIME": timestring(int(time.time()))} )
1873     setup_file(setup_path("cn=samba-admin.ldif"), 
1874                os.path.join(result.paths.ldapdir, "db", "samba",  "cn=samba", "cn=samba-admin.ldif"),
1875                {"LDAPADMINPASS_B64": b64encode(ldapadminpass),
1876                 "UUID": str(uuid.uuid4()), 
1877                 "LDAPTIME": timestring(int(time.time()))} )
1878     
1879     if ol_mmr_urls is not None:
1880         setup_file(setup_path("cn=replicator.ldif"),
1881                    os.path.join(result.paths.ldapdir, "db", "samba",  "cn=samba", "cn=replicator.ldif"),
1882                    {"MMR_PASSWORD_B64": b64encode(mmr_pass),
1883                     "UUID": str(uuid.uuid4()),
1884                     "LDAPTIME": timestring(int(time.time()))} )
1885         
1886
1887     mapping = "schema-map-openldap-2.3"
1888     backend_schema = "backend-schema.schema"
1889
1890     backend_schema_data = schema.ldb.convert_schema_to_openldap("openldap", open(setup_path(mapping), 'r').read())
1891     assert backend_schema_data is not None
1892     open(os.path.join(result.paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1893
1894     # now we generate the needed strings to start slapd automatically,
1895     # first ldapi_uri...
1896     if ldap_backend_extra_port is not None:
1897         # When we use MMR, we can't use 0.0.0.0 as it uses the name
1898         # specified there as part of it's clue as to it's own name,
1899         # and not to replicate to itself
1900         if ol_mmr_urls is None:
1901             server_port_string = "ldap://0.0.0.0:%d" % ldap_backend_extra_port
1902         else:
1903             server_port_string = "ldap://" + names.hostname + "." + names.dnsdomain +":%d" % ldap_backend_extra_port
1904     else:
1905         server_port_string = ""
1906
1907     # Prepare the 'result' information - the commands to return in particular
1908     result.slapd_provision_command = [slapd_path]
1909
1910     result.slapd_provision_command.append("-F" + result.paths.olcdir)
1911
1912     result.slapd_provision_command.append("-h")
1913
1914     # copy this command so we have two version, one with -d0 and only ldapi, and one with all the listen commands
1915     result.slapd_command = list(result.slapd_provision_command)
1916     
1917     result.slapd_provision_command.append(result.ldapi_uri)
1918     result.slapd_provision_command.append("-d0")
1919
1920     uris = result.ldapi_uri
1921     if server_port_string is not "":
1922         uris = uris + " " + server_port_string
1923
1924     result.slapd_command.append(uris)
1925
1926     # Set the username - done here because Fedora DS still uses the admin DN and simple bind
1927     result.credentials.set_username("samba-admin")
1928     
1929     # If we were just looking for crashes up to this point, it's a
1930     # good time to exit before we realise we don't have OpenLDAP on
1931     # this system
1932     if ldap_dryrun_mode:
1933         sys.exit(0)
1934
1935     # Finally, convert the configuration into cn=config style!
1936     if not os.path.isdir(result.paths.olcdir):
1937         os.makedirs(result.paths.olcdir, 0770)
1938
1939         retcode = subprocess.call([slapd_path, "-Ttest", "-f", result.paths.slapdconf, "-F", result.paths.olcdir], close_fds=True, shell=False)
1940
1941 #        We can't do this, as OpenLDAP is strange.  It gives an error
1942 #        output to the above, but does the conversion sucessfully...
1943 #
1944 #        if retcode != 0:
1945 #            raise ProvisioningError("conversion from slapd.conf to cn=config failed")
1946
1947         if not os.path.exists(os.path.join(result.paths.olcdir, "cn=config.ldif")):
1948             raise ProvisioningError("conversion from slapd.conf to cn=config failed")
1949
1950         # Don't confuse the admin by leaving the slapd.conf around
1951         os.remove(result.paths.slapdconf)        
1952
1953
1954 def provision_fds_backend(result, setup_path=None, names=None,
1955                           message=None, 
1956                           hostname=None, ldapadminpass=None, root=None, 
1957                           schema=None,
1958                           ldap_backend_extra_port=None,
1959                           setup_ds_path=None,
1960                           slapd_path=None,
1961                           nosync=False, 
1962                           ldap_dryrun_mode=False,
1963                           domainsid=None):
1964
1965     if ldap_backend_extra_port is not None:
1966         serverport = "ServerPort=%d" % ldap_backend_extra_port
1967     else:
1968         serverport = ""
1969         
1970     setup_file(setup_path("fedorads.inf"), result.paths.fedoradsinf, 
1971                {"ROOT": root,
1972                 "HOSTNAME": hostname,
1973                 "DNSDOMAIN": names.dnsdomain,
1974                 "LDAPDIR": result.paths.ldapdir,
1975                 "DOMAINDN": names.domaindn,
1976                 "LDAPMANAGERDN": names.ldapmanagerdn,
1977                 "LDAPMANAGERPASS": ldapadminpass, 
1978                 "SERVERPORT": serverport})
1979
1980     setup_file(setup_path("fedorads-partitions.ldif"), result.paths.fedoradspartitions, 
1981                {"CONFIGDN": names.configdn,
1982                 "SCHEMADN": names.schemadn,
1983                 "SAMBADN": names.sambadn,
1984                 })
1985
1986     setup_file(setup_path("fedorads-sasl.ldif"), result.paths.fedoradssasl, 
1987                {"SAMBADN": names.sambadn,
1988                 })
1989
1990     setup_file(setup_path("fedorads-dna.ldif"), result.paths.fedoradsdna, 
1991                {"DOMAINDN": names.domaindn,
1992                 "SAMBADN": names.sambadn,
1993                 "DOMAINSID": str(domainsid),
1994                 })
1995
1996     setup_file(setup_path("fedorads-pam.ldif"), result.paths.fedoradspam)
1997
1998     lnkattr = get_linked_attributes(names.schemadn,schema.ldb)
1999
2000     refint_config = data = open(setup_path("fedorads-refint-delete.ldif"), 'r').read()
2001     memberof_config = ""
2002     index_config = ""
2003     argnum = 3
2004
2005     for attr in lnkattr.keys():
2006         if lnkattr[attr] is not None:
2007             refint_config += read_and_sub_file(setup_path("fedorads-refint-add.ldif"),
2008                                                  { "ARG_NUMBER" : str(argnum) ,
2009                                                    "LINK_ATTR" : attr })
2010             memberof_config += read_and_sub_file(setup_path("fedorads-linked-attributes.ldif"),
2011                                                  { "MEMBER_ATTR" : attr ,
2012                                                    "MEMBEROF_ATTR" : lnkattr[attr] })
2013             index_config += read_and_sub_file(setup_path("fedorads-index.ldif"),
2014                                                  { "ATTR" : attr })
2015             argnum += 1
2016
2017     open(result.paths.fedoradsrefint, 'w').write(refint_config)
2018     open(result.paths.fedoradslinkedattributes, 'w').write(memberof_config)
2019
2020     attrs = ["lDAPDisplayName"]
2021     res = schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
2022
2023     for i in range (0, len(res)):
2024         attr = res[i]["lDAPDisplayName"][0]
2025
2026         if attr == "objectGUID":
2027             attr = "nsUniqueId"
2028
2029         index_config += read_and_sub_file(setup_path("fedorads-index.ldif"),
2030                                              { "ATTR" : attr })
2031
2032     open(result.paths.fedoradsindex, 'w').write(index_config)
2033
2034     setup_file(setup_path("fedorads-samba.ldif"), result.paths.fedoradssamba,
2035                 {"SAMBADN": names.sambadn, 
2036                  "LDAPADMINPASS": ldapadminpass
2037                 })
2038
2039     mapping = "schema-map-fedora-ds-1.0"
2040     backend_schema = "99_ad.ldif"
2041     
2042     # Build a schema file in Fedora DS format
2043     backend_schema_data = schema.ldb.convert_schema_to_openldap("fedora-ds", open(setup_path(mapping), 'r').read())
2044     assert backend_schema_data is not None
2045     open(os.path.join(result.paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
2046
2047     result.credentials.set_bind_dn(names.ldapmanagerdn)
2048
2049     # Destory the target directory, or else setup-ds.pl will complain
2050     fedora_ds_dir = os.path.join(result.paths.ldapdir, "slapd-samba4")
2051     shutil.rmtree(fedora_ds_dir, True)
2052
2053     result.slapd_provision_command = [slapd_path, "-D", fedora_ds_dir, "-i", result.paths.slapdpid];
2054     #In the 'provision' command line, stay in the foreground so we can easily kill it
2055     result.slapd_provision_command.append("-d0")
2056
2057     #the command for the final run is the normal script
2058     result.slapd_command = [os.path.join(result.paths.ldapdir, "slapd-samba4", "start-slapd")]
2059
2060     # If we were just looking for crashes up to this point, it's a
2061     # good time to exit before we realise we don't have Fedora DS on
2062     if ldap_dryrun_mode:
2063         sys.exit(0)
2064
2065     # Try to print helpful messages when the user has not specified the path to the setup-ds tool
2066     if setup_ds_path is None:
2067         raise ProvisioningError("Warning: Fedora DS LDAP-Backend must be setup with path to setup-ds, e.g. --setup-ds-path=\"/usr/sbin/setup-ds.pl\"!")
2068     if not os.path.exists(setup_ds_path):
2069         message (setup_ds_path)
2070         raise ProvisioningError("Warning: Given Path to slapd does not exist!")
2071
2072     # Run the Fedora DS setup utility
2073     retcode = subprocess.call([setup_ds_path, "--silent", "--file", result.paths.fedoradsinf], close_fds=True, shell=False)
2074     if retcode != 0:
2075         raise ProvisioningError("setup-ds failed")
2076
2077     # Load samba-admin
2078     retcode = subprocess.call([
2079         os.path.join(result.paths.ldapdir, "slapd-samba4", "ldif2db"), "-s", names.sambadn, "-i", result.paths.fedoradssamba],
2080         close_fds=True, shell=False)
2081     if retcode != 0:
2082         raise("ldib2db failed")
2083
2084     # Leave a hook to do the 'post initilisation' setup
2085     def fds_post_setup(self):
2086         ldapi_db = Ldb(self.ldapi_uri, credentials=self.credentials)
2087
2088         # delete default SASL mappings
2089         res = ldapi_db.search(expression="(!(cn=samba-admin mapping))", base="cn=mapping,cn=sasl,cn=config", scope=SCOPE_ONELEVEL, attrs=["dn"])
2090     
2091         # configure in-directory access control on Fedora DS via the aci attribute (over a direct ldapi:// socket)
2092         for i in range (0, len(res)):
2093             dn = str(res[i]["dn"])
2094             ldapi_db.delete(dn)
2095             
2096             aci = """(targetattr = "*") (version 3.0;acl "full access to all by samba-admin";allow (all)(userdn = "ldap:///CN=samba-admin,%s");)""" % names.sambadn
2097         
2098             m = ldb.Message()
2099             m["aci"] = ldb.MessageElement([aci], ldb.FLAG_MOD_REPLACE, "aci")
2100
2101             m.dn = ldb.Dn(1, names.domaindn)
2102             ldapi_db.modify(m)
2103             
2104             m.dn = ldb.Dn(1, names.configdn)
2105             ldapi_db.modify(m)
2106             
2107             m.dn = ldb.Dn(1, names.schemadn)
2108             ldapi_db.modify(m)
2109             
2110     result.post_setup = fds_post_setup
2111     
2112
2113 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
2114     """Create a PHP LDAP admin configuration file.
2115
2116     :param path: Path to write the configuration to.
2117     :param setup_path: Function to generate setup paths.
2118     """
2119     setup_file(setup_path("phpldapadmin-config.php"), path, 
2120             {"S4_LDAPI_URI": ldapi_uri})
2121
2122
2123 def create_zone_file(path, setup_path, dnsdomain, 
2124                      hostip, hostip6, hostname, realm, domainguid,
2125                      ntdsguid):
2126     """Write out a DNS zone file, from the info in the current database.
2127
2128     :param path: Path of the new zone file.
2129     :param setup_path: Setup path function.
2130     :param dnsdomain: DNS Domain name
2131     :param domaindn: DN of the Domain
2132     :param hostip: Local IPv4 IP
2133     :param hostip6: Local IPv6 IP
2134     :param hostname: Local hostname
2135     :param realm: Realm name
2136     :param domainguid: GUID of the domain.
2137     :param ntdsguid: GUID of the hosts nTDSDSA record.
2138     """
2139     assert isinstance(domainguid, str)
2140
2141     if hostip6 is not None:
2142         hostip6_base_line = "            IN AAAA    " + hostip6
2143         hostip6_host_line = hostname + "        IN AAAA    " + hostip6
2144     else:
2145         hostip6_base_line = ""
2146         hostip6_host_line = ""
2147
2148     if hostip is not None:
2149         hostip_base_line = "            IN A    " + hostip
2150         hostip_host_line = hostname + "        IN A    " + hostip
2151     else:
2152         hostip_base_line = ""
2153         hostip_host_line = ""
2154
2155     setup_file(setup_path("provision.zone"), path, {
2156             "HOSTNAME": hostname,
2157             "DNSDOMAIN": dnsdomain,
2158             "REALM": realm,
2159             "HOSTIP_BASE_LINE": hostip_base_line,
2160             "HOSTIP_HOST_LINE": hostip_host_line,
2161             "DOMAINGUID": domainguid,
2162             "DATESTRING": time.strftime("%Y%m%d%H"),
2163             "DEFAULTSITE": DEFAULTSITE,
2164             "NTDSGUID": ntdsguid,
2165             "HOSTIP6_BASE_LINE": hostip6_base_line,
2166             "HOSTIP6_HOST_LINE": hostip6_host_line,
2167         })
2168
2169
2170 def create_named_conf(path, setup_path, realm, dnsdomain,
2171                       private_dir):
2172     """Write out a file containing zone statements suitable for inclusion in a
2173     named.conf file (including GSS-TSIG configuration).
2174     
2175     :param path: Path of the new named.conf file.
2176     :param setup_path: Setup path function.
2177     :param realm: Realm name
2178     :param dnsdomain: DNS Domain name
2179     :param private_dir: Path to private directory
2180     :param keytab_name: File name of DNS keytab file
2181     """
2182
2183     setup_file(setup_path("named.conf"), path, {
2184             "DNSDOMAIN": dnsdomain,
2185             "REALM": realm,
2186             "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
2187             "PRIVATE_DIR": private_dir
2188             })
2189
2190 def create_named_txt(path, setup_path, realm, dnsdomain,
2191                       private_dir, keytab_name):
2192     """Write out a file containing zone statements suitable for inclusion in a
2193     named.conf file (including GSS-TSIG configuration).
2194     
2195     :param path: Path of the new named.conf file.
2196     :param setup_path: Setup path function.
2197     :param realm: Realm name
2198     :param dnsdomain: DNS Domain name
2199     :param private_dir: Path to private directory
2200     :param keytab_name: File name of DNS keytab file
2201     """
2202
2203     setup_file(setup_path("named.txt"), path, {
2204             "DNSDOMAIN": dnsdomain,
2205             "REALM": realm,
2206             "DNS_KEYTAB": keytab_name,
2207             "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
2208             "PRIVATE_DIR": private_dir
2209         })
2210
2211 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
2212     """Write out a file containing zone statements suitable for inclusion in a
2213     named.conf file (including GSS-TSIG configuration).
2214     
2215     :param path: Path of the new named.conf file.
2216     :param setup_path: Setup path function.
2217     :param dnsdomain: DNS Domain name
2218     :param hostname: Local hostname
2219     :param realm: Realm name
2220     """
2221
2222     setup_file(setup_path("krb5.conf"), path, {
2223             "DNSDOMAIN": dnsdomain,
2224             "HOSTNAME": hostname,
2225             "REALM": realm,
2226         })
2227
2228