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