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