s4: Exceptions in "provision.py"
[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
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, serverrole=None,
371                 rootdn=None, domaindn=None, configdn=None, schemadn=None, serverdn=None, 
372                 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         setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
867                 "DOMAINDN": names.domaindn,
868                 "DOMAIN_OC": domain_oc
869                 })
870
871         message("Modifying DomainDN: " + names.domaindn + "")
872         if domainguid is not None:
873             domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
874         else:
875             domainguid_mod = ""
876
877         setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
878             "LDAPTIME": timestring(int(time.time())),
879             "DOMAINSID": str(domainsid),
880             "SCHEMADN": names.schemadn, 
881             "NETBIOSNAME": names.netbiosname,
882             "DEFAULTSITE": names.sitename,
883             "CONFIGDN": names.configdn,
884             "SERVERDN": names.serverdn,
885             "POLICYGUID": policyguid,
886             "DOMAINDN": names.domaindn,
887             "DOMAINGUID_MOD": domainguid_mod,
888             "DOMAIN_FUNCTIONALITY": str(domainFunctionality)
889             })
890
891         message("Adding configuration container")
892         setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
893             "CONFIGDN": names.configdn, 
894             })
895         message("Modifying configuration container")
896         setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
897             "CONFIGDN": names.configdn, 
898             "SCHEMADN": names.schemadn,
899             })
900
901         # The LDIF here was created when the Schema object was constructed
902         message("Setting up sam.ldb schema")
903         samdb.add_ldif(schema.schema_dn_add)
904         samdb.modify_ldif(schema.schema_dn_modify)
905         samdb.write_prefixes_from_schema()
906         samdb.add_ldif(schema.schema_data)
907         setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"), 
908                        {"SCHEMADN": names.schemadn})
909
910         message("Setting up sam.ldb configuration data")
911         setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
912             "CONFIGDN": names.configdn,
913             "NETBIOSNAME": names.netbiosname,
914             "DEFAULTSITE": names.sitename,
915             "DNSDOMAIN": names.dnsdomain,
916             "DOMAIN": names.domain,
917             "SCHEMADN": names.schemadn,
918             "DOMAINDN": names.domaindn,
919             "SERVERDN": names.serverdn,
920             "FOREST_FUNCTIONALALITY": str(forestFunctionality)
921             })
922
923         message("Setting up display specifiers")
924         display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
925         display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
926         check_all_substituted(display_specifiers_ldif)
927         samdb.add_ldif(display_specifiers_ldif)
928
929         message("Adding users container")
930         setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
931                 "DOMAINDN": names.domaindn})
932         message("Modifying users container")
933         setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
934                 "DOMAINDN": names.domaindn})
935         message("Adding computers container")
936         setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
937                 "DOMAINDN": names.domaindn})
938         message("Modifying computers container")
939         setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
940                 "DOMAINDN": names.domaindn})
941         message("Setting up sam.ldb data")
942         setup_add_ldif(samdb, setup_path("provision.ldif"), {
943             "DOMAINDN": names.domaindn,
944             "NETBIOSNAME": names.netbiosname,
945             "DEFAULTSITE": names.sitename,
946             "CONFIGDN": names.configdn,
947             "SERVERDN": names.serverdn
948             })
949
950         if fill == FILL_FULL:
951             message("Setting up sam.ldb users and groups")
952             setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
953                 "DOMAINDN": names.domaindn,
954                 "DOMAINSID": str(domainsid),
955                 "CONFIGDN": names.configdn,
956                 "ADMINPASS_B64": b64encode(adminpass),
957                 "KRBTGTPASS_B64": b64encode(krbtgtpass),
958                 })
959
960             if serverrole == "domain controller":
961                 message("Setting up self join")
962                 setup_self_join(samdb, names=names, invocationid=invocationid, 
963                                 dnspass=dnspass,  
964                                 machinepass=machinepass, 
965                                 domainsid=domainsid, policyguid=policyguid,
966                                 setup_path=setup_path, domainControllerFunctionality=domainControllerFunctionality)
967
968     except:
969         samdb.transaction_cancel()
970         raise
971
972     samdb.transaction_commit()
973     return samdb
974
975
976 FILL_FULL = "FULL"
977 FILL_NT4SYNC = "NT4SYNC"
978 FILL_DRS = "DRS"
979
980
981 def provision(setup_dir, message, session_info, 
982               credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL, realm=None, 
983               rootdn=None, domaindn=None, schemadn=None, configdn=None, 
984               serverdn=None,
985               domain=None, hostname=None, hostip=None, hostip6=None, 
986               domainsid=None, adminpass=None, ldapadminpass=None, 
987               krbtgtpass=None, domainguid=None, 
988               policyguid=None, invocationid=None, machinepass=None, 
989               dnspass=None, root=None, nobody=None, users=None, 
990               wheel=None, backup=None, aci=None, serverrole=None, 
991               ldap_backend_extra_port=None, ldap_backend_type=None, sitename=None,
992               ol_mmr_urls=None, ol_olc=None, 
993               setup_ds_path=None, slapd_path=None, nosync=False,
994               ldap_dryrun_mode=False):
995     """Provision samba4
996     
997     :note: caution, this wipes all existing data!
998     """
999
1000     def setup_path(file):
1001         return os.path.join(setup_dir, file)
1002
1003     if domainsid is None:
1004         domainsid = security.random_sid()
1005
1006     if policyguid is None:
1007         policyguid = str(uuid.uuid4())
1008     if adminpass is None:
1009         adminpass = glue.generate_random_str(12)
1010     if krbtgtpass is None:
1011         krbtgtpass = glue.generate_random_str(12)
1012     if machinepass is None:
1013         machinepass  = glue.generate_random_str(12)
1014     if dnspass is None:
1015         dnspass = glue.generate_random_str(12)
1016     if ldapadminpass is None:
1017         #Make a new, random password between Samba and it's LDAP server
1018         ldapadminpass=glue.generate_random_str(12)        
1019
1020
1021     root_uid = findnss_uid([root or "root"])
1022     nobody_uid = findnss_uid([nobody or "nobody"])
1023     users_gid = findnss_gid([users or "users"])
1024     if wheel is None:
1025         wheel_gid = findnss_gid(["wheel", "adm"])
1026     else:
1027         wheel_gid = findnss_gid([wheel])
1028
1029     if targetdir is not None:
1030         if (not os.path.exists(os.path.join(targetdir, "etc"))):
1031             os.makedirs(os.path.join(targetdir, "etc"))
1032         smbconf = os.path.join(targetdir, "etc", "smb.conf")
1033     elif smbconf is None:
1034         smbconf = param.default_path()
1035
1036     # only install a new smb.conf if there isn't one there already
1037     if not os.path.exists(smbconf):
1038         make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, 
1039                      targetdir)
1040
1041     lp = param.LoadParm()
1042     lp.load(smbconf)
1043
1044     names = guess_names(lp=lp, hostname=hostname, domain=domain, 
1045                         dnsdomain=realm, serverrole=serverrole, sitename=sitename,
1046                         rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1047                         serverdn=serverdn)
1048
1049     paths = provision_paths_from_lp(lp, names.dnsdomain)
1050
1051     if hostip is None:
1052         try:
1053             hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1054         except socket.gaierror, (socket.EAI_NODATA, msg):
1055             hostip = None
1056
1057     if hostip6 is None:
1058         try:
1059             hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1060         except socket.gaierror, (socket.EAI_NODATA, msg): 
1061             hostip6 = None
1062
1063     if serverrole is None:
1064         serverrole = lp.get("server role")
1065
1066     assert serverrole in ("domain controller", "member server", "standalone")
1067     if invocationid is None and serverrole == "domain controller":
1068         invocationid = str(uuid.uuid4())
1069
1070     if not os.path.exists(paths.private_dir):
1071         os.mkdir(paths.private_dir)
1072
1073     ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1074     
1075     schema = Schema(setup_path, schemadn=names.schemadn, serverdn=names.serverdn)
1076     
1077     provision_backend = None
1078     if ldap_backend_type:
1079         # We only support an LDAP backend over ldapi://
1080
1081         provision_backend = ProvisionBackend(paths=paths, setup_path=setup_path, lp=lp, credentials=credentials, 
1082                                              names=names,
1083                                              message=message, hostname=hostname, 
1084                                              root=root, schema=schema, ldap_backend_type=ldap_backend_type,
1085                                              ldapadminpass=ldapadminpass,
1086                                              ldap_backend_extra_port=ldap_backend_extra_port,
1087                                              ol_mmr_urls=ol_mmr_urls, 
1088                                              slapd_path=slapd_path,
1089                                              setup_ds_path=setup_ds_path,
1090                                              ldap_dryrun_mode=ldap_dryrun_mode)
1091
1092         # Now use the backend credentials to access the databases
1093         credentials = provision_backend.credentials
1094
1095     # only install a new shares config db if there is none
1096     if not os.path.exists(paths.shareconf):
1097         message("Setting up share.ldb")
1098         share_ldb = Ldb(paths.shareconf, session_info=session_info, 
1099                         credentials=credentials, lp=lp)
1100         share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1101
1102      
1103     message("Setting up secrets.ldb")
1104     secrets_ldb = setup_secretsdb(paths.secrets, setup_path, 
1105                                   session_info=session_info, 
1106                                   credentials=credentials, lp=lp)
1107
1108     message("Setting up the registry")
1109     setup_registry(paths.hklm, setup_path, session_info, 
1110                    lp=lp)
1111
1112     message("Setting up idmap db")
1113     idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1114                           lp=lp)
1115
1116     message("Setting up SAM db")
1117     samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info, 
1118                         credentials=credentials, lp=lp, names=names,
1119                         message=message, 
1120                         domainsid=domainsid, 
1121                         schema=schema, domainguid=domainguid, policyguid=policyguid, 
1122                         fill=samdb_fill, 
1123                         adminpass=adminpass, krbtgtpass=krbtgtpass,
1124                         invocationid=invocationid, 
1125                         machinepass=machinepass, dnspass=dnspass,
1126                         serverrole=serverrole, ldap_backend=provision_backend)
1127
1128     if serverrole == "domain controller":
1129         if paths.netlogon is None:
1130             message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1131             message("Please either remove %s or see the template at %s" % 
1132                     ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1133             assert(paths.netlogon is not None)
1134
1135         if paths.sysvol is None:
1136             message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1137             message("Please either remove %s or see the template at %s" % 
1138                     (paths.smbconf, setup_path("provision.smb.conf.dc")))
1139             assert(paths.sysvol is not None)            
1140             
1141         policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies", 
1142                                    "{" + policyguid + "}")
1143         os.makedirs(policy_path, 0755)
1144         open(os.path.join(policy_path, "GPT.INI"), 'w').write("")
1145         os.makedirs(os.path.join(policy_path, "Machine"), 0755)
1146         os.makedirs(os.path.join(policy_path, "User"), 0755)
1147         if not os.path.isdir(paths.netlogon):
1148             os.makedirs(paths.netlogon, 0755)
1149
1150     if samdb_fill == FILL_FULL:
1151         setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1152                             root_uid=root_uid, nobody_uid=nobody_uid,
1153                             users_gid=users_gid, wheel_gid=wheel_gid)
1154
1155         message("Setting up sam.ldb rootDSE marking as synchronized")
1156         setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1157
1158         # Only make a zone file on the first DC, it should be replicated with DNS replication
1159         if serverrole == "domain controller":
1160             secrets_ldb = Ldb(paths.secrets, session_info=session_info, 
1161                               credentials=credentials, lp=lp)
1162             secretsdb_become_dc(secrets_ldb, setup_path, domain=domain, realm=names.realm,
1163                                 netbiosname=names.netbiosname, domainsid=domainsid, 
1164                                 keytab_path=paths.keytab, samdb_url=paths.samdb, 
1165                                 dns_keytab_path=paths.dns_keytab, dnspass=dnspass, 
1166                                 machinepass=machinepass, dnsdomain=names.dnsdomain)
1167
1168             domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1169             assert isinstance(domainguid, str)
1170             hostguid = samdb.searchone(basedn=domaindn, attribute="objectGUID",
1171                                        expression="(&(objectClass=computer)(cn=%s))" % names.hostname,
1172                                        scope=SCOPE_SUBTREE)
1173             assert isinstance(hostguid, str)
1174
1175             create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1176                              domaindn=names.domaindn, hostip=hostip,
1177                              hostip6=hostip6, hostname=names.hostname,
1178                              dnspass=dnspass, realm=names.realm,
1179                              domainguid=domainguid, hostguid=hostguid)
1180
1181             create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1182                               dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1183
1184             create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1185                               dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1186                               keytab_name=paths.dns_keytab)
1187             message("See %s for an example configuration include file for BIND" % paths.namedconf)
1188             message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1189
1190             create_krb5_conf(paths.krb5conf, setup_path, dnsdomain=names.dnsdomain,
1191                              hostname=names.hostname, realm=names.realm)
1192             message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1193
1194
1195     # if backend is openldap, terminate slapd after final provision and check its proper termination
1196     if provision_backend is not None and provision_backend.slapd is not None:
1197         if provision_backend.slapd.poll() is None:
1198             #Kill the slapd
1199             if hasattr(provision_backend.slapd, "terminate"):
1200                 provision_backend.slapd.terminate()
1201             else:
1202                 import signal
1203                 os.kill(provision_backend.slapd.pid, signal.SIGTERM)
1204             
1205             #and now wait for it to die
1206             provision_backend.slapd.communicate()
1207             
1208     # now display slapd_command_file.txt to show how slapd must be started next time
1209         message("Use later the following commandline to start slapd, then Samba:")
1210         slapd_command = "\'" + "\' \'".join(provision_backend.slapd_command) + "\'"
1211         message(slapd_command)
1212         message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh")
1213
1214         setup_file(setup_path("ldap_backend_startup.sh"), paths.ldapdir + "/ldap_backend_startup.sh", {
1215                 "SLAPD_COMMAND" : slapd_command})
1216
1217     
1218     create_phpldapadmin_config(paths.phpldapadminconfig, setup_path, 
1219                                ldapi_url)
1220
1221     message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1222
1223     message("Once the above files are installed, your Samba4 server will be ready to use")
1224     message("Server Role:           %s" % serverrole)
1225     message("Hostname:              %s" % names.hostname)
1226     message("NetBIOS Domain:        %s" % names.domain)
1227     message("DNS Domain:            %s" % names.dnsdomain)
1228     message("DOMAIN SID:            %s" % str(domainsid))
1229     if samdb_fill == FILL_FULL:
1230         message("Admin password:    %s" % adminpass)
1231     if provision_backend:
1232         if provision_backend.credentials.get_bind_dn() is not None:
1233             message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1234         else:
1235             message("LDAP Admin User:       %s" % provision_backend.credentials.get_username())
1236
1237         message("LDAP Admin Password:   %s" % provision_backend.credentials.get_password())
1238   
1239     result = ProvisionResult()
1240     result.domaindn = domaindn
1241     result.paths = paths
1242     result.lp = lp
1243     result.samdb = samdb
1244     return result
1245
1246
1247
1248 def provision_become_dc(setup_dir=None,
1249                         smbconf=None, targetdir=None, realm=None, 
1250                         rootdn=None, domaindn=None, schemadn=None, configdn=None,
1251                         serverdn=None,
1252                         domain=None, hostname=None, domainsid=None, 
1253                         adminpass=None, krbtgtpass=None, domainguid=None, 
1254                         policyguid=None, invocationid=None, machinepass=None, 
1255                         dnspass=None, root=None, nobody=None, users=None, 
1256                         wheel=None, backup=None, serverrole=None, 
1257                         ldap_backend=None, ldap_backend_type=None, sitename=None, debuglevel=1):
1258
1259     def message(text):
1260         """print a message if quiet is not set."""
1261         print text
1262
1263     glue.set_debug_level(debuglevel)
1264
1265     return provision(setup_dir, message, system_session(), None,
1266               smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS, realm=realm, 
1267               rootdn=rootdn, domaindn=domaindn, schemadn=schemadn, configdn=configdn, serverdn=serverdn,
1268               domain=domain, hostname=hostname, hostip="127.0.0.1", domainsid=domainsid, machinepass=machinepass, serverrole="domain controller", sitename=sitename)
1269     
1270
1271 def setup_db_config(setup_path, dbdir):
1272     """Setup a Berkeley database.
1273     
1274     :param setup_path: Setup path function.
1275     :param dbdir: Database directory."""
1276     if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
1277         os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700)
1278         if not os.path.isdir(os.path.join(dbdir, "tmp")):
1279             os.makedirs(os.path.join(dbdir, "tmp"), 0700)
1280
1281     setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
1282                {"LDAPDBDIR": dbdir})
1283     
1284 class ProvisionBackend(object):
1285     def __init__(self, paths=None, setup_path=None, lp=None, credentials=None, 
1286                  names=None, message=None, 
1287                  hostname=None, root=None, 
1288                  schema=None, ldapadminpass=None,
1289                  ldap_backend_type=None, ldap_backend_extra_port=None,
1290                  ol_mmr_urls=None, 
1291                  setup_ds_path=None, slapd_path=None, 
1292                  nosync=False, ldap_dryrun_mode=False):
1293         """Provision an LDAP backend for samba4
1294         
1295         This works for OpenLDAP and Fedora DS
1296         """
1297
1298         self.ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.ldapdir, "ldapi"), safe="")
1299         
1300         if not os.path.isdir(paths.ldapdir):
1301             os.makedirs(paths.ldapdir, 0700)
1302             
1303         if ldap_backend_type == "existing":
1304             #Check to see that this 'existing' LDAP backend in fact exists
1305             ldapi_db = Ldb(self.ldapi_uri, credentials=credentials)
1306             search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1307                                                 expression="(objectClass=OpenLDAProotDSE)")
1308
1309             # If we have got here, then we must have a valid connection to the LDAP server, with valid credentials supplied
1310             # This caused them to be set into the long-term database later in the script.
1311             self.credentials = credentials
1312             self.ldap_backend_type = "openldap" #For now, assume existing backends at least emulate OpenLDAP
1313             return
1314     
1315         # we will shortly start slapd with ldapi for final provisioning. first check with ldapsearch -> rootDSE via self.ldapi_uri
1316         # if another instance of slapd is already running 
1317         try:
1318             ldapi_db = Ldb(self.ldapi_uri)
1319             search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1320                                                 expression="(objectClass=OpenLDAProotDSE)");
1321             try:
1322                 f = open(paths.slapdpid, "r")
1323                 p = f.read()
1324                 f.close()
1325                 message("Check for slapd Process with PID: " + str(p) + " and terminate it manually.")
1326             except:
1327                 pass
1328             
1329             raise ProvisioningError("Warning: Another slapd Instance seems already running on this host, listening to " + self.ldapi_uri + ". Please shut it down before you continue. ")
1330         
1331         except LdbError, e:
1332             pass
1333
1334         # Try to print helpful messages when the user has not specified the path to slapd
1335         if slapd_path is None:
1336             raise ProvisioningError("Warning: LDAP-Backend must be setup with path to slapd, e.g. --slapd-path=\"/usr/local/libexec/slapd\"!")
1337         if not os.path.exists(slapd_path):
1338             message (slapd_path)
1339             raise ProvisioningError("Warning: Given Path to slapd does not exist!")
1340
1341         schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
1342         try:
1343             os.unlink(schemadb_path)
1344         except OSError:
1345             pass
1346
1347
1348         # Put the LDIF of the schema into a database so we can search on
1349         # it to generate schema-dependent configurations in Fedora DS and
1350         # OpenLDAP
1351         os.path.join(paths.ldapdir, "schema-tmp.ldb")
1352         schema.ldb.connect(schemadb_path)
1353         schema.ldb.transaction_start()
1354     
1355         # These bits of LDIF are supplied when the Schema object is created
1356         schema.ldb.add_ldif(schema.schema_dn_add)
1357         schema.ldb.modify_ldif(schema.schema_dn_modify)
1358         schema.ldb.add_ldif(schema.schema_data)
1359         schema.ldb.transaction_commit()
1360
1361         self.credentials = Credentials()
1362         self.credentials.guess(lp)
1363         #Kerberos to an ldapi:// backend makes no sense
1364         self.credentials.set_kerberos_state(DONT_USE_KERBEROS)
1365         self.ldap_backend_type = ldap_backend_type
1366
1367         if ldap_backend_type == "fedora-ds":
1368             provision_fds_backend(self, paths=paths, setup_path=setup_path, names=names, message=message, 
1369                                   hostname=hostname, ldapadminpass=ldapadminpass, root=root, 
1370                                   schema=schema, ldap_backend_extra_port=ldap_backend_extra_port, 
1371                                   setup_ds_path=setup_ds_path, slapd_path=slapd_path,
1372                                   nosync=nosync, ldap_dryrun_mode=ldap_dryrun_mode)
1373             
1374         elif ldap_backend_type == "openldap":
1375             provision_openldap_backend(self, paths=paths, setup_path=setup_path, names=names, message=message, 
1376                                        hostname=hostname, ldapadminpass=ldapadminpass, root=root, 
1377                                        schema=schema, ldap_backend_extra_port=ldap_backend_extra_port, 
1378                                        ol_mmr_urls=ol_mmr_urls, 
1379                                        slapd_path=slapd_path,
1380                                        nosync=nosync, ldap_dryrun_mode=ldap_dryrun_mode)
1381         else:
1382             raise ProvisioningError("Unknown LDAP backend type selected")
1383
1384         self.credentials.set_password(ldapadminpass)
1385
1386         # Now start the slapd, so we can provision onto it.  We keep the
1387         # subprocess context around, to kill this off at the successful
1388         # end of the script
1389         self.slapd = subprocess.Popen(self.slapd_provision_command, close_fds=True, shell=False)
1390     
1391         while self.slapd.poll() is None:
1392             # Wait until the socket appears
1393             try:
1394                 ldapi_db = Ldb(self.ldapi_uri, lp=lp, credentials=self.credentials)
1395                 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1396                                                     expression="(objectClass=OpenLDAProotDSE)")
1397                 # If we have got here, then we must have a valid connection to the LDAP server!
1398                 return
1399             except LdbError, e:
1400                 time.sleep(1)
1401                 pass
1402         
1403         raise ProvisioningError("slapd died before we could make a connection to it")
1404
1405
1406 def provision_openldap_backend(result, paths=None, setup_path=None, names=None, message=None, 
1407                                hostname=None, ldapadminpass=None, root=None, 
1408                                schema=None, 
1409                                ldap_backend_extra_port=None,
1410                                ol_mmr_urls=None, 
1411                                slapd_path=None, nosync=False,
1412                                ldap_dryrun_mode=False):
1413
1414     #Allow the test scripts to turn off fsync() for OpenLDAP as for TDB and LDB
1415     nosync_config = ""
1416     if nosync:
1417         nosync_config = "dbnosync"
1418         
1419     lnkattr = get_linked_attributes(names.schemadn,schema.ldb)
1420     refint_attributes = ""
1421     memberof_config = "# Generated from Samba4 schema\n"
1422     for att in  lnkattr.keys():
1423         if lnkattr[att] is not None:
1424             refint_attributes = refint_attributes + " " + att 
1425             
1426             memberof_config += read_and_sub_file(setup_path("memberof.conf"),
1427                                                  { "MEMBER_ATTR" : att ,
1428                                                    "MEMBEROF_ATTR" : lnkattr[att] })
1429             
1430     refint_config = read_and_sub_file(setup_path("refint.conf"),
1431                                       { "LINK_ATTRS" : refint_attributes})
1432     
1433     attrs = ["linkID", "lDAPDisplayName"]
1434     res = schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
1435     index_config = ""
1436     for i in range (0, len(res)):
1437         index_attr = res[i]["lDAPDisplayName"][0]
1438         if index_attr == "objectGUID":
1439             index_attr = "entryUUID"
1440             
1441         index_config += "index " + index_attr + " eq\n"
1442
1443 # generate serverids, ldap-urls and syncrepl-blocks for mmr hosts
1444     mmr_on_config = ""
1445     mmr_replicator_acl = ""
1446     mmr_serverids_config = ""
1447     mmr_syncrepl_schema_config = "" 
1448     mmr_syncrepl_config_config = "" 
1449     mmr_syncrepl_user_config = "" 
1450        
1451     
1452     if ol_mmr_urls is not None:
1453         # For now, make these equal
1454         mmr_pass = ldapadminpass
1455         
1456         url_list=filter(None,ol_mmr_urls.split(' ')) 
1457         if (len(url_list) == 1):
1458             url_list=filter(None,ol_mmr_urls.split(',')) 
1459                      
1460             
1461             mmr_on_config = "MirrorMode On"
1462             mmr_replicator_acl = "  by dn=cn=replicator,cn=samba read"
1463             serverid=0
1464             for url in url_list:
1465                 serverid=serverid+1
1466                 mmr_serverids_config += read_and_sub_file(setup_path("mmr_serverids.conf"),
1467                                                           { "SERVERID" : str(serverid),
1468                                                             "LDAPSERVER" : url })
1469                 rid=serverid*10
1470                 rid=rid+1
1471                 mmr_syncrepl_schema_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1472                                                                 {  "RID" : str(rid),
1473                                                                    "MMRDN": names.schemadn,
1474                                                                    "LDAPSERVER" : url,
1475                                                                    "MMR_PASSWORD": mmr_pass})
1476                 
1477                 rid=rid+1
1478                 mmr_syncrepl_config_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1479                                                                 {  "RID" : str(rid),
1480                                                                    "MMRDN": names.configdn,
1481                                                                    "LDAPSERVER" : url,
1482                                                                    "MMR_PASSWORD": mmr_pass})
1483                 
1484                 rid=rid+1
1485                 mmr_syncrepl_user_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1486                                                               {  "RID" : str(rid),
1487                                                                  "MMRDN": names.domaindn,
1488                                                                  "LDAPSERVER" : url,
1489                                                                  "MMR_PASSWORD": mmr_pass })
1490     # OpenLDAP cn=config initialisation
1491     olc_syncrepl_config = ""
1492     olc_mmr_config = "" 
1493     # if mmr = yes, generate cn=config-replication directives
1494     # and olc_seed.lif for the other mmr-servers
1495     if ol_mmr_urls is not None:
1496         serverid=0
1497         olc_serverids_config = ""
1498         olc_syncrepl_seed_config = ""
1499         olc_mmr_config += read_and_sub_file(setup_path("olc_mmr.conf"),{})
1500         rid=1000
1501         for url in url_list:
1502             serverid=serverid+1
1503             olc_serverids_config += read_and_sub_file(setup_path("olc_serverid.conf"),
1504                                                       { "SERVERID" : str(serverid),
1505                                                         "LDAPSERVER" : url })
1506             
1507             rid=rid+1
1508             olc_syncrepl_config += read_and_sub_file(setup_path("olc_syncrepl.conf"),
1509                                                      {  "RID" : str(rid),
1510                                                         "LDAPSERVER" : url,
1511                                                         "MMR_PASSWORD": mmr_pass})
1512             
1513             olc_syncrepl_seed_config += read_and_sub_file(setup_path("olc_syncrepl_seed.conf"),
1514                                                           {  "RID" : str(rid),
1515                                                              "LDAPSERVER" : url})
1516                 
1517         setup_file(setup_path("olc_seed.ldif"), paths.olcseedldif,
1518                    {"OLC_SERVER_ID_CONF": olc_serverids_config,
1519                     "OLC_PW": ldapadminpass,
1520                     "OLC_SYNCREPL_CONF": olc_syncrepl_seed_config})
1521     # end olc
1522                 
1523     setup_file(setup_path("slapd.conf"), paths.slapdconf,
1524                {"DNSDOMAIN": names.dnsdomain,
1525                 "LDAPDIR": paths.ldapdir,
1526                 "DOMAINDN": names.domaindn,
1527                 "CONFIGDN": names.configdn,
1528                 "SCHEMADN": names.schemadn,
1529                 "MEMBEROF_CONFIG": memberof_config,
1530                 "MIRRORMODE": mmr_on_config,
1531                 "REPLICATOR_ACL": mmr_replicator_acl,
1532                 "MMR_SERVERIDS_CONFIG": mmr_serverids_config,
1533                 "MMR_SYNCREPL_SCHEMA_CONFIG": mmr_syncrepl_schema_config,
1534                 "MMR_SYNCREPL_CONFIG_CONFIG": mmr_syncrepl_config_config,
1535                 "MMR_SYNCREPL_USER_CONFIG": mmr_syncrepl_user_config,
1536                 "OLC_SYNCREPL_CONFIG": olc_syncrepl_config,
1537                 "OLC_MMR_CONFIG": olc_mmr_config,
1538                 "REFINT_CONFIG": refint_config,
1539                 "INDEX_CONFIG": index_config,
1540                 "NOSYNC": nosync_config})
1541         
1542     setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "user"))
1543     setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "config"))
1544     setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "schema"))
1545     
1546     if not os.path.exists(os.path.join(paths.ldapdir, "db", "samba",  "cn=samba")):
1547         os.makedirs(os.path.join(paths.ldapdir, "db", "samba",  "cn=samba"), 0700)
1548         
1549     setup_file(setup_path("cn=samba.ldif"), 
1550                os.path.join(paths.ldapdir, "db", "samba",  "cn=samba.ldif"),
1551                { "UUID": str(uuid.uuid4()), 
1552                  "LDAPTIME": timestring(int(time.time()))} )
1553     setup_file(setup_path("cn=samba-admin.ldif"), 
1554                os.path.join(paths.ldapdir, "db", "samba",  "cn=samba", "cn=samba-admin.ldif"),
1555                {"LDAPADMINPASS_B64": b64encode(ldapadminpass),
1556                 "UUID": str(uuid.uuid4()), 
1557                 "LDAPTIME": timestring(int(time.time()))} )
1558     
1559     if ol_mmr_urls is not None:
1560         setup_file(setup_path("cn=replicator.ldif"),
1561                    os.path.join(paths.ldapdir, "db", "samba",  "cn=samba", "cn=replicator.ldif"),
1562                    {"MMR_PASSWORD_B64": b64encode(mmr_pass),
1563                     "UUID": str(uuid.uuid4()),
1564                     "LDAPTIME": timestring(int(time.time()))} )
1565         
1566
1567     mapping = "schema-map-openldap-2.3"
1568     backend_schema = "backend-schema.schema"
1569
1570     backend_schema_data = schema.ldb.convert_schema_to_openldap("openldap", open(setup_path(mapping), 'r').read())
1571     assert backend_schema_data is not None
1572     open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1573
1574     # now we generate the needed strings to start slapd automatically,
1575     # first ldapi_uri...
1576     if ldap_backend_extra_port is not None:
1577         # When we use MMR, we can't use 0.0.0.0 as it uses the name
1578         # specified there as part of it's clue as to it's own name,
1579         # and not to replicate to itself
1580         if ol_mmr_urls is None:
1581             server_port_string = "ldap://0.0.0.0:%d" % ldap_backend_extra_port
1582         else:
1583             server_port_string = "ldap://" + names.hostname + "." + names.dnsdomain +":%d" % ldap_backend_extra_port
1584     else:
1585         server_port_string = ""
1586
1587     # Prepare the 'result' information - the commands to return in particular
1588     result.slapd_provision_command = [slapd_path]
1589
1590     result.slapd_provision_command.append("-F" + paths.olcdir)
1591
1592     result.slapd_provision_command.append("-h")
1593
1594     # copy this command so we have two version, one with -d0 and only ldapi, and one with all the listen commands
1595     result.slapd_command = list(result.slapd_provision_command)
1596     
1597     result.slapd_provision_command.append(result.ldapi_uri)
1598     result.slapd_provision_command.append("-d0")
1599
1600     uris = result.ldapi_uri
1601     if server_port_string is not "":
1602         uris = uris + " " + server_port_string
1603
1604     result.slapd_command.append(uris)
1605
1606     # Set the username - done here because Fedora DS still uses the admin DN and simple bind
1607     result.credentials.set_username("samba-admin")
1608     
1609     # If we were just looking for crashes up to this point, it's a
1610     # good time to exit before we realise we don't have OpenLDAP on
1611     # this system
1612     if ldap_dryrun_mode:
1613         sys.exit(0)
1614
1615     # Finally, convert the configuration into cn=config style!
1616     if not os.path.isdir(paths.olcdir):
1617         os.makedirs(paths.olcdir, 0770)
1618
1619         retcode = subprocess.call([slapd_path, "-Ttest", "-f", paths.slapdconf, "-F", paths.olcdir], close_fds=True, shell=False)
1620
1621 #        We can't do this, as OpenLDAP is strange.  It gives an error
1622 #        output to the above, but does the conversion sucessfully...
1623 #
1624 #        if retcode != 0:
1625 #            raise ProvisioningError("conversion from slapd.conf to cn=config failed")
1626
1627         if not os.path.exists(os.path.join(paths.olcdir, "cn=config.ldif")):
1628             raise ProvisioningError("conversion from slapd.conf to cn=config failed")
1629
1630         # Don't confuse the admin by leaving the slapd.conf around
1631         os.remove(paths.slapdconf)        
1632           
1633
1634 def provision_fds_backend(result, paths=None, setup_path=None, names=None, message=None, 
1635                           hostname=None, ldapadminpass=None, root=None, 
1636                           schema=None,
1637                           ldap_backend_extra_port=None,
1638                           setup_ds_path=None,
1639                           slapd_path=None,
1640                           nosync=False, 
1641                           ldap_dryrun_mode=False):
1642
1643     if ldap_backend_extra_port is not None:
1644         serverport = "ServerPort=%d" % ldap_backend_extra_port
1645     else:
1646         serverport = ""
1647         
1648     setup_file(setup_path("fedorads.inf"), paths.fedoradsinf, 
1649                {"ROOT": root,
1650                 "HOSTNAME": hostname,
1651                 "DNSDOMAIN": names.dnsdomain,
1652                 "LDAPDIR": paths.ldapdir,
1653                 "DOMAINDN": names.domaindn,
1654                 "LDAPMANAGERDN": names.ldapmanagerdn,
1655                 "LDAPMANAGERPASS": ldapadminpass, 
1656                 "SERVERPORT": serverport})
1657
1658     setup_file(setup_path("fedorads-partitions.ldif"), paths.fedoradspartitions, 
1659                {"CONFIGDN": names.configdn,
1660                 "SCHEMADN": names.schemadn,
1661                 })
1662
1663     mapping = "schema-map-fedora-ds-1.0"
1664     backend_schema = "99_ad.ldif"
1665     
1666     # Build a schema file in Fedora DS format
1667     backend_schema_data = schema.ldb.convert_schema_to_openldap("fedora-ds", open(setup_path(mapping), 'r').read())
1668     assert backend_schema_data is not None
1669     open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1670
1671     result.credentials.set_bind_dn(names.ldapmanagerdn)
1672
1673     # Destory the target directory, or else setup-ds.pl will complain
1674     fedora_ds_dir = os.path.join(paths.ldapdir, "slapd-samba4")
1675     shutil.rmtree(fedora_ds_dir, True)
1676
1677     result.slapd_provision_command = [slapd_path, "-D", fedora_ds_dir, "-i", paths.slapdpid];
1678     #In the 'provision' command line, stay in the foreground so we can easily kill it
1679     result.slapd_provision_command.append("-d0")
1680
1681     #the command for the final run is the normal script
1682     result.slapd_command = [os.path.join(paths.ldapdir, "slapd-samba4", "start-slapd")]
1683
1684     # If we were just looking for crashes up to this point, it's a
1685     # good time to exit before we realise we don't have Fedora DS on
1686     if ldap_dryrun_mode:
1687         sys.exit(0)
1688
1689     # Try to print helpful messages when the user has not specified the path to the setup-ds tool
1690     if setup_ds_path is None:
1691         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\"!")
1692     if not os.path.exists(setup_ds_path):
1693         message (setup_ds_path)
1694         raise ProvisioningError("Warning: Given Path to slapd does not exist!")
1695
1696     # Run the Fedora DS setup utility
1697     retcode = subprocess.call([setup_ds_path, "--silent", "--file", paths.fedoradsinf], close_fds=True, shell=False)
1698     if retcode != 0:
1699         raise ProvisioningError("setup-ds failed")
1700
1701 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1702     """Create a PHP LDAP admin configuration file.
1703
1704     :param path: Path to write the configuration to.
1705     :param setup_path: Function to generate setup paths.
1706     """
1707     setup_file(setup_path("phpldapadmin-config.php"), path, 
1708             {"S4_LDAPI_URI": ldapi_uri})
1709
1710
1711 def create_zone_file(path, setup_path, dnsdomain, domaindn, 
1712                      hostip, hostip6, hostname, dnspass, realm, domainguid, hostguid):
1713     """Write out a DNS zone file, from the info in the current database.
1714
1715     :param path: Path of the new zone file.
1716     :param setup_path: Setup path function.
1717     :param dnsdomain: DNS Domain name
1718     :param domaindn: DN of the Domain
1719     :param hostip: Local IPv4 IP
1720     :param hostip6: Local IPv6 IP
1721     :param hostname: Local hostname
1722     :param dnspass: Password for DNS
1723     :param realm: Realm name
1724     :param domainguid: GUID of the domain.
1725     :param hostguid: GUID of the host.
1726     """
1727     assert isinstance(domainguid, str)
1728
1729     if hostip6 is not None:
1730         hostip6_base_line = "            IN AAAA    " + hostip6
1731         hostip6_host_line = hostname + "        IN AAAA    " + hostip6
1732     else:
1733         hostip6_base_line = ""
1734         hostip6_host_line = ""
1735
1736     if hostip is not None:
1737         hostip_base_line = "            IN A    " + hostip
1738         hostip_host_line = hostname + "        IN A    " + hostip
1739     else:
1740         hostip_base_line = ""
1741         hostip_host_line = ""
1742
1743     setup_file(setup_path("provision.zone"), path, {
1744             "DNSPASS_B64": b64encode(dnspass),
1745             "HOSTNAME": hostname,
1746             "DNSDOMAIN": dnsdomain,
1747             "REALM": realm,
1748             "HOSTIP_BASE_LINE": hostip_base_line,
1749             "HOSTIP_HOST_LINE": hostip_host_line,
1750             "DOMAINGUID": domainguid,
1751             "DATESTRING": time.strftime("%Y%m%d%H"),
1752             "DEFAULTSITE": DEFAULTSITE,
1753             "HOSTGUID": hostguid,
1754             "HOSTIP6_BASE_LINE": hostip6_base_line,
1755             "HOSTIP6_HOST_LINE": hostip6_host_line,
1756         })
1757
1758
1759 def create_named_conf(path, setup_path, realm, dnsdomain,
1760                       private_dir):
1761     """Write out a file containing zone statements suitable for inclusion in a
1762     named.conf file (including GSS-TSIG configuration).
1763     
1764     :param path: Path of the new named.conf file.
1765     :param setup_path: Setup path function.
1766     :param realm: Realm name
1767     :param dnsdomain: DNS Domain name
1768     :param private_dir: Path to private directory
1769     :param keytab_name: File name of DNS keytab file
1770     """
1771
1772     setup_file(setup_path("named.conf"), path, {
1773             "DNSDOMAIN": dnsdomain,
1774             "REALM": realm,
1775             "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1776             "PRIVATE_DIR": private_dir
1777             })
1778
1779 def create_named_txt(path, setup_path, realm, dnsdomain,
1780                       private_dir, keytab_name):
1781     """Write out a file containing zone statements suitable for inclusion in a
1782     named.conf file (including GSS-TSIG configuration).
1783     
1784     :param path: Path of the new named.conf file.
1785     :param setup_path: Setup path function.
1786     :param realm: Realm name
1787     :param dnsdomain: DNS Domain name
1788     :param private_dir: Path to private directory
1789     :param keytab_name: File name of DNS keytab file
1790     """
1791
1792     setup_file(setup_path("named.txt"), path, {
1793             "DNSDOMAIN": dnsdomain,
1794             "REALM": realm,
1795             "DNS_KEYTAB": keytab_name,
1796             "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1797             "PRIVATE_DIR": private_dir
1798         })
1799
1800 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1801     """Write out a file containing zone statements suitable for inclusion in a
1802     named.conf file (including GSS-TSIG configuration).
1803     
1804     :param path: Path of the new named.conf file.
1805     :param setup_path: Setup path function.
1806     :param dnsdomain: DNS Domain name
1807     :param hostname: Local hostname
1808     :param realm: Realm name
1809     """
1810
1811     setup_file(setup_path("krb5.conf"), path, {
1812             "DNSDOMAIN": dnsdomain,
1813             "HOSTNAME": hostname,
1814             "REALM": realm,
1815         })
1816
1817
1818