aa6b6789ddef4a3ac099db5f2f56f359ddf0002b
[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_foreign(samdb, domaindn, "S-1-5-7", "Anonymous")
491     add_foreign(samdb, domaindn, "S-1-1-0", "World")
492     add_foreign(samdb, domaindn, "S-1-5-2", "Network")
493     add_foreign(samdb, domaindn, "S-1-5-18", "System")
494     add_foreign(samdb, domaindn, "S-1-5-11", "Authenticated Users")
495     
496     idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
497     idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
498     
499     idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
500     idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
501
502 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info, 
503                            credentials, names,
504                            serverrole, ldap_backend=None, 
505                            erase=False):
506     """Setup the partitions for the SAM database. 
507     
508     Alternatively, provision() may call this, and then populate the database.
509     
510     :note: This will wipe the Sam Database!
511     
512     :note: This function always removes the local SAM LDB file. The erase 
513         parameter controls whether to erase the existing data, which 
514         may not be stored locally but in LDAP.
515     """
516     assert session_info is not None
517
518     # We use options=["modules:"] to stop the modules loading - we
519     # just want to wipe and re-initialise the database, not start it up
520
521     try:
522         samdb = Ldb(url=samdb_path, session_info=session_info, 
523                       credentials=credentials, lp=lp, options=["modules:"])
524         # Wipes the database
525         samdb.erase_except_schema_controlled()
526     except LdbError:
527         os.unlink(samdb_path)
528         samdb = Ldb(url=samdb_path, session_info=session_info, 
529                       credentials=credentials, lp=lp, options=["modules:"])
530          # Wipes the database
531         samdb.erase_except_schema_controlled()
532         
533
534     #Add modules to the list to activate them by default
535     #beware often order is important
536     #
537     # Some Known ordering constraints:
538     # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
539     # - objectclass must be before password_hash, because password_hash checks
540     #   that the objectclass is of type person (filled in by objectclass
541     #   module when expanding the objectclass list)
542     # - partition must be last
543     # - each partition has its own module list then
544     modules_list = ["rootdse",
545                     "paged_results",
546                     "ranged_results",
547                     "anr",
548                     "server_sort",
549                     "asq",
550                     "extended_dn_store",
551                     "extended_dn_in",
552                     "rdn_name",
553                     "objectclass",
554                     "samldb",
555                     "kludge_acl",
556                     "password_hash",
557                     "operational"]
558     tdb_modules_list = [
559                     "subtree_rename",
560                     "subtree_delete",
561                     "linked_attributes",
562                     "extended_dn_out_ldb"]
563     modules_list2 = ["show_deleted",
564                     "partition"]
565  
566     domaindn_ldb = "users.ldb"
567     configdn_ldb = "configuration.ldb"
568     schemadn_ldb = "schema.ldb"
569     if ldap_backend is not None:
570         domaindn_ldb = ldap_backend.ldapi_uri
571         configdn_ldb = ldap_backend.ldapi_uri
572         schemadn_ldb = ldap_backend.ldapi_uri
573         
574         if ldap_backend.ldap_backend_type == "fedora-ds":
575             backend_modules = ["nsuniqueid", "paged_searches"]
576             # We can handle linked attributes here, as we don't have directory-side subtree operations
577             tdb_modules_list = ["linked_attributes", "extended_dn_out_dereference"]
578         elif ldap_backend.ldap_backend_type == "openldap":
579             backend_modules = ["entryuuid", "paged_searches"]
580             # OpenLDAP handles subtree renames, so we don't want to do any of these things
581             tdb_modules_list = ["extended_dn_out_dereference"]
582
583     elif serverrole == "domain controller":
584         backend_modules = ["repl_meta_data"]
585     else:
586         backend_modules = ["objectguid"]
587
588     if tdb_modules_list is None:
589         tdb_modules_list_as_string = ""
590     else:
591         tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
592         
593     samdb.transaction_start()
594     try:
595         message("Setting up sam.ldb partitions and settings")
596         setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
597                 "SCHEMADN": names.schemadn, 
598                 "SCHEMADN_LDB": schemadn_ldb,
599                 "SCHEMADN_MOD2": ",objectguid",
600                 "CONFIGDN": names.configdn,
601                 "CONFIGDN_LDB": configdn_ldb,
602                 "DOMAINDN": names.domaindn,
603                 "DOMAINDN_LDB": domaindn_ldb,
604                 "SCHEMADN_MOD": "schema_fsmo,instancetype",
605                 "CONFIGDN_MOD": "naming_fsmo,instancetype",
606                 "DOMAINDN_MOD": "pdc_fsmo,instancetype",
607                 "MODULES_LIST": ",".join(modules_list),
608                 "TDB_MODULES_LIST": tdb_modules_list_as_string,
609                 "MODULES_LIST2": ",".join(modules_list2),
610                 "BACKEND_MOD": ",".join(backend_modules),
611         })
612
613         samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
614
615         message("Setting up sam.ldb rootDSE")
616         setup_samdb_rootdse(samdb, setup_path, names)
617
618     except:
619         samdb.transaction_cancel()
620         raise
621
622     samdb.transaction_commit()
623     
624
625
626 def secretsdb_become_dc(secretsdb, setup_path, domain, realm, dnsdomain, 
627                         netbiosname, domainsid, keytab_path, samdb_url, 
628                         dns_keytab_path, dnspass, machinepass):
629     """Add DC-specific bits to a secrets database.
630     
631     :param secretsdb: Ldb Handle to the secrets database
632     :param setup_path: Setup path function
633     :param machinepass: Machine password
634     """
635     setup_ldb(secretsdb, setup_path("secrets_dc.ldif"), { 
636             "MACHINEPASS_B64": b64encode(machinepass),
637             "DOMAIN": domain,
638             "REALM": realm,
639             "DNSDOMAIN": dnsdomain,
640             "DOMAINSID": str(domainsid),
641             "SECRETS_KEYTAB": keytab_path,
642             "NETBIOSNAME": netbiosname,
643             "SAM_LDB": samdb_url,
644             "DNS_KEYTAB": dns_keytab_path,
645             "DNSPASS_B64": b64encode(dnspass),
646             })
647
648
649 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
650     """Setup the secrets database.
651
652     :param path: Path to the secrets database.
653     :param setup_path: Get the path to a setup file.
654     :param session_info: Session info.
655     :param credentials: Credentials
656     :param lp: Loadparm context
657     :return: LDB handle for the created secrets database
658     """
659     if os.path.exists(path):
660         os.unlink(path)
661     secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
662                       lp=lp)
663     secrets_ldb.erase()
664     secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
665     secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
666                       lp=lp)
667     secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
668
669     if credentials is not None and credentials.authentication_requested():
670         if credentials.get_bind_dn() is not None:
671             setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
672                     "LDAPMANAGERDN": credentials.get_bind_dn(),
673                     "LDAPMANAGERPASS_B64": b64encode(credentials.get_password())
674                     })
675         else:
676             setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
677                     "LDAPADMINUSER": credentials.get_username(),
678                     "LDAPADMINREALM": credentials.get_realm(),
679                     "LDAPADMINPASS_B64": b64encode(credentials.get_password())
680                     })
681
682     return secrets_ldb
683
684
685 def setup_templatesdb(path, setup_path, session_info, lp):
686     """Setup the templates database.
687
688     :param path: Path to the database.
689     :param setup_path: Function for obtaining the path to setup files.
690     :param session_info: Session info
691     :param credentials: Credentials
692     :param lp: Loadparm context
693     """
694     templates_ldb = Ldb(url=path, session_info=session_info,
695                         lp=lp)
696     # Wipes the database
697     try:
698         templates_ldb.erase()
699     # This should be 'except LdbError', but on a re-provision the assert in ldb.erase fires, and we need to catch that too
700     except:
701         os.unlink(path)
702
703     templates_ldb.load_ldif_file_add(setup_path("provision_templates_init.ldif"))
704
705     templates_ldb = Ldb(url=path, session_info=session_info,
706                         lp=lp)
707
708     templates_ldb.load_ldif_file_add(setup_path("provision_templates.ldif"))
709
710
711 def setup_registry(path, setup_path, session_info, lp):
712     """Setup the registry.
713     
714     :param path: Path to the registry database
715     :param setup_path: Function that returns the path to a setup.
716     :param session_info: Session information
717     :param credentials: Credentials
718     :param lp: Loadparm context
719     """
720     reg = registry.Registry()
721     hive = registry.open_ldb(path, session_info=session_info, 
722                          lp_ctx=lp)
723     reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
724     provision_reg = setup_path("provision.reg")
725     assert os.path.exists(provision_reg)
726     reg.diff_apply(provision_reg)
727
728
729 def setup_idmapdb(path, setup_path, session_info, lp):
730     """Setup the idmap database.
731
732     :param path: path to the idmap database
733     :param setup_path: Function that returns a path to a setup file
734     :param session_info: Session information
735     :param credentials: Credentials
736     :param lp: Loadparm context
737     """
738     if os.path.exists(path):
739         os.unlink(path)
740
741     idmap_ldb = IDmapDB(path, session_info=session_info,
742                         lp=lp)
743
744     idmap_ldb.erase()
745     idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
746     return idmap_ldb
747
748
749 def setup_samdb_rootdse(samdb, setup_path, names):
750     """Setup the SamDB rootdse.
751
752     :param samdb: Sam Database handle
753     :param setup_path: Obtain setup path
754     """
755     setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
756         "SCHEMADN": names.schemadn, 
757         "NETBIOSNAME": names.netbiosname,
758         "DNSDOMAIN": names.dnsdomain,
759         "REALM": names.realm,
760         "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
761         "DOMAINDN": names.domaindn,
762         "ROOTDN": names.rootdn,
763         "CONFIGDN": names.configdn,
764         "SERVERDN": names.serverdn,
765         })
766         
767
768 def setup_self_join(samdb, names,
769                     machinepass, dnspass, 
770                     domainsid, invocationid, setup_path,
771                     policyguid, domainControllerFunctionality):
772     """Join a host to its own domain."""
773     assert isinstance(invocationid, str)
774     setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), { 
775               "CONFIGDN": names.configdn, 
776               "SCHEMADN": names.schemadn,
777               "DOMAINDN": names.domaindn,
778               "SERVERDN": names.serverdn,
779               "INVOCATIONID": invocationid,
780               "NETBIOSNAME": names.netbiosname,
781               "DEFAULTSITE": names.sitename,
782               "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
783               "MACHINEPASS_B64": b64encode(machinepass),
784               "DNSPASS_B64": b64encode(dnspass),
785               "REALM": names.realm,
786               "DOMAIN": names.domain,
787               "DNSDOMAIN": names.dnsdomain,
788               "SAMBA_VERSION_STRING": version,
789               "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
790
791     setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), { 
792               "POLICYGUID": policyguid,
793               "DNSDOMAIN": names.dnsdomain,
794               "DOMAINSID": str(domainsid),
795               "DOMAINDN": names.domaindn})
796
797     # Setup fSMORoleOwner entries to point at the newly created DC entry
798     setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
799               "DOMAINDN": names.domaindn,
800               "CONFIGDN": names.configdn,
801               "SCHEMADN": names.schemadn, 
802               "DEFAULTSITE": names.sitename,
803               "SERVERDN": names.serverdn
804               })
805
806
807 def setup_samdb(path, setup_path, session_info, credentials, lp, 
808                 names, message, 
809                 domainsid, domainguid, policyguid, 
810                 fill, adminpass, krbtgtpass, 
811                 machinepass, invocationid, dnspass,
812                 serverrole, schema=None, ldap_backend=None):
813     """Setup a complete SAM Database.
814     
815     :note: This will wipe the main SAM database file!
816     """
817
818     domainFunctionality = DS_BEHAVIOR_WIN2008
819     forestFunctionality = DS_BEHAVIOR_WIN2008
820     domainControllerFunctionality = DS_BEHAVIOR_WIN2008
821
822     # Also wipes the database
823     setup_samdb_partitions(path, setup_path, message=message, lp=lp,
824                            credentials=credentials, session_info=session_info,
825                            names=names, 
826                            ldap_backend=ldap_backend, serverrole=serverrole)
827
828     if (schema == None):
829         schema = Schema(setup_path, schemadn=names.schemadn, serverdn=names.serverdn)
830
831     # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
832     samdb = Ldb(session_info=session_info, 
833                 credentials=credentials, lp=lp)
834
835     message("Pre-loading the Samba 4 and AD schema")
836
837     # Load the schema from the one we computed earlier
838     samdb.set_schema_from_ldb(schema.ldb)
839
840     # And now we can connect to the DB - the schema won't be loaded from the DB
841     samdb.connect(path)
842     if fill == FILL_DRS:
843         return samdb
844
845     samdb.transaction_start()
846     try:
847         message("Erasing data from partitions")
848         # Load the schema (again).  This time it will force a reindex,
849         # and will therefore make the erase_partitions() below
850         # computationally sane
851         samdb.set_schema_from_ldb(schema.ldb)
852         samdb.erase_partitions()
853     
854         # Set the domain functionality levels onto the database.
855         # Various module (the password_hash module in particular) need
856         # to know what level of AD we are emulating.
857
858         # These will be fixed into the database via the database
859         # modifictions below, but we need them set from the start.
860         samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
861         samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
862         samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
863
864         samdb.set_domain_sid(str(domainsid))
865         if serverrole == "domain controller":
866             samdb.set_invocation_id(invocationid)
867
868         message("Adding DomainDN: %s" % names.domaindn)
869         if serverrole == "domain controller":
870             domain_oc = "domainDNS"
871         else:
872             domain_oc = "samba4LocalDomain"
873
874         setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
875                 "DOMAINDN": names.domaindn,
876                 "DOMAIN_OC": domain_oc
877                 })
878
879         message("Modifying DomainDN: " + names.domaindn + "")
880         if domainguid is not None:
881             domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
882         else:
883             domainguid_mod = ""
884
885         setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
886             "LDAPTIME": timestring(int(time.time())),
887             "DOMAINSID": str(domainsid),
888             "SCHEMADN": names.schemadn, 
889             "NETBIOSNAME": names.netbiosname,
890             "DEFAULTSITE": names.sitename,
891             "CONFIGDN": names.configdn,
892             "SERVERDN": names.serverdn,
893             "POLICYGUID": policyguid,
894             "DOMAINDN": names.domaindn,
895             "DOMAINGUID_MOD": domainguid_mod,
896             "DOMAIN_FUNCTIONALITY": str(domainFunctionality)
897             })
898
899         message("Adding configuration container")
900         setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
901             "CONFIGDN": names.configdn, 
902             })
903         message("Modifying configuration container")
904         setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
905             "CONFIGDN": names.configdn, 
906             "SCHEMADN": names.schemadn,
907             })
908
909         # The LDIF here was created when the Schema object was constructed
910         message("Setting up sam.ldb schema")
911         samdb.add_ldif(schema.schema_dn_add)
912         samdb.modify_ldif(schema.schema_dn_modify)
913         samdb.add_ldif(schema.schema_data)
914         setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"), 
915                        {"SCHEMADN": names.schemadn})
916
917         message("Setting up sam.ldb configuration data")
918         setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
919             "CONFIGDN": names.configdn,
920             "NETBIOSNAME": names.netbiosname,
921             "DEFAULTSITE": names.sitename,
922             "DNSDOMAIN": names.dnsdomain,
923             "DOMAIN": names.domain,
924             "SCHEMADN": names.schemadn,
925             "DOMAINDN": names.domaindn,
926             "SERVERDN": names.serverdn,
927             "FOREST_FUNCTIONALALITY": str(forestFunctionality)
928             })
929
930         message("Setting up display specifiers")
931         setup_add_ldif(samdb, setup_path("display_specifiers.ldif"), 
932                        {"CONFIGDN": names.configdn})
933
934         message("Adding users container")
935         setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
936                 "DOMAINDN": names.domaindn})
937         message("Modifying users container")
938         setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
939                 "DOMAINDN": names.domaindn})
940         message("Adding computers container")
941         setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
942                 "DOMAINDN": names.domaindn})
943         message("Modifying computers container")
944         setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
945                 "DOMAINDN": names.domaindn})
946         message("Setting up sam.ldb data")
947         setup_add_ldif(samdb, setup_path("provision.ldif"), {
948             "DOMAINDN": names.domaindn,
949             "NETBIOSNAME": names.netbiosname,
950             "DEFAULTSITE": names.sitename,
951             "CONFIGDN": names.configdn,
952             "SERVERDN": names.serverdn
953             })
954
955         if fill == FILL_FULL:
956             message("Setting up sam.ldb users and groups")
957             setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
958                 "DOMAINDN": names.domaindn,
959                 "DOMAINSID": str(domainsid),
960                 "CONFIGDN": names.configdn,
961                 "ADMINPASS_B64": b64encode(adminpass),
962                 "KRBTGTPASS_B64": b64encode(krbtgtpass),
963                 })
964
965             if serverrole == "domain controller":
966                 message("Setting up self join")
967                 setup_self_join(samdb, names=names, invocationid=invocationid, 
968                                 dnspass=dnspass,  
969                                 machinepass=machinepass, 
970                                 domainsid=domainsid, policyguid=policyguid,
971                                 setup_path=setup_path, domainControllerFunctionality=domainControllerFunctionality)
972
973     except:
974         samdb.transaction_cancel()
975         raise
976
977     samdb.transaction_commit()
978     return samdb
979
980
981 FILL_FULL = "FULL"
982 FILL_NT4SYNC = "NT4SYNC"
983 FILL_DRS = "DRS"
984
985
986 def provision(setup_dir, message, session_info, 
987               credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL, realm=None, 
988               rootdn=None, domaindn=None, schemadn=None, configdn=None, 
989               serverdn=None,
990               domain=None, hostname=None, hostip=None, hostip6=None, 
991               domainsid=None, adminpass=None, ldapadminpass=None, 
992               krbtgtpass=None, domainguid=None, 
993               policyguid=None, invocationid=None, machinepass=None, 
994               dnspass=None, root=None, nobody=None, users=None, 
995               wheel=None, backup=None, aci=None, serverrole=None, 
996               ldap_backend_extra_port=None, ldap_backend_type=None, sitename=None,
997               ol_mmr_urls=None, ol_olc=None, 
998               setup_ds_path=None, slapd_path=None, nosync=False,
999               ldap_dryrun_mode=False):
1000     """Provision samba4
1001     
1002     :note: caution, this wipes all existing data!
1003     """
1004
1005     def setup_path(file):
1006         return os.path.join(setup_dir, file)
1007
1008     if domainsid is None:
1009         domainsid = security.random_sid()
1010
1011     if policyguid is None:
1012         policyguid = str(uuid.uuid4())
1013     if adminpass is None:
1014         adminpass = glue.generate_random_str(12)
1015     if krbtgtpass is None:
1016         krbtgtpass = glue.generate_random_str(12)
1017     if machinepass is None:
1018         machinepass  = glue.generate_random_str(12)
1019     if dnspass is None:
1020         dnspass = glue.generate_random_str(12)
1021     if ldapadminpass is None:
1022         #Make a new, random password between Samba and it's LDAP server
1023         ldapadminpass=glue.generate_random_str(12)        
1024
1025
1026     root_uid = findnss_uid([root or "root"])
1027     nobody_uid = findnss_uid([nobody or "nobody"])
1028     users_gid = findnss_gid([users or "users"])
1029     if wheel is None:
1030         wheel_gid = findnss_gid(["wheel", "adm"])
1031     else:
1032         wheel_gid = findnss_gid([wheel])
1033
1034     if targetdir is not None:
1035         if (not os.path.exists(os.path.join(targetdir, "etc"))):
1036             os.makedirs(os.path.join(targetdir, "etc"))
1037         smbconf = os.path.join(targetdir, "etc", "smb.conf")
1038     elif smbconf is None:
1039         smbconf = param.default_path()
1040
1041     # only install a new smb.conf if there isn't one there already
1042     if not os.path.exists(smbconf):
1043         make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, 
1044                      targetdir)
1045
1046     lp = param.LoadParm()
1047     lp.load(smbconf)
1048
1049     names = guess_names(lp=lp, hostname=hostname, domain=domain, 
1050                         dnsdomain=realm, serverrole=serverrole, sitename=sitename,
1051                         rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1052                         serverdn=serverdn)
1053
1054     paths = provision_paths_from_lp(lp, names.dnsdomain)
1055
1056     if hostip is None:
1057         try:
1058             hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1059         except socket.gaierror, (socket.EAI_NODATA, msg):
1060             hostip = None
1061
1062     if hostip6 is None:
1063         try:
1064             hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1065         except socket.gaierror, (socket.EAI_NODATA, msg): 
1066             hostip6 = None
1067
1068     if serverrole is None:
1069         serverrole = lp.get("server role")
1070
1071     assert serverrole in ("domain controller", "member server", "standalone")
1072     if invocationid is None and serverrole == "domain controller":
1073         invocationid = str(uuid.uuid4())
1074
1075     if not os.path.exists(paths.private_dir):
1076         os.mkdir(paths.private_dir)
1077
1078     ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1079     
1080     schema = Schema(setup_path, schemadn=names.schemadn, serverdn=names.serverdn)
1081     
1082     provision_backend = None
1083     if ldap_backend_type:
1084         # We only support an LDAP backend over ldapi://
1085
1086         provision_backend = ProvisionBackend(paths=paths, setup_path=setup_path, lp=lp, credentials=credentials, 
1087                                              names=names,
1088                                              message=message, hostname=hostname, 
1089                                              root=root, schema=schema, ldap_backend_type=ldap_backend_type,
1090                                              ldapadminpass=ldapadminpass,
1091                                              ldap_backend_extra_port=ldap_backend_extra_port,
1092                                              ol_mmr_urls=ol_mmr_urls, 
1093                                              slapd_path=slapd_path,
1094                                              setup_ds_path=setup_ds_path,
1095                                              ldap_dryrun_mode=ldap_dryrun_mode)
1096
1097         # Now use the backend credentials to access the databases
1098         credentials = provision_backend.credentials
1099
1100     # only install a new shares config db if there is none
1101     if not os.path.exists(paths.shareconf):
1102         message("Setting up share.ldb")
1103         share_ldb = Ldb(paths.shareconf, session_info=session_info, 
1104                         credentials=credentials, lp=lp)
1105         share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1106
1107      
1108     message("Setting up secrets.ldb")
1109     secrets_ldb = setup_secretsdb(paths.secrets, setup_path, 
1110                                   session_info=session_info, 
1111                                   credentials=credentials, lp=lp)
1112
1113     message("Setting up the registry")
1114     setup_registry(paths.hklm, setup_path, session_info, 
1115                    lp=lp)
1116
1117     message("Setting up templates db")
1118     setup_templatesdb(paths.templates, setup_path, session_info=session_info, 
1119                       lp=lp)
1120
1121     message("Setting up idmap db")
1122     idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1123                           lp=lp)
1124
1125     message("Setting up SAM db")
1126     samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info, 
1127                         credentials=credentials, lp=lp, names=names,
1128                         message=message, 
1129                         domainsid=domainsid, 
1130                         schema=schema, domainguid=domainguid, policyguid=policyguid, 
1131                         fill=samdb_fill, 
1132                         adminpass=adminpass, krbtgtpass=krbtgtpass,
1133                         invocationid=invocationid, 
1134                         machinepass=machinepass, dnspass=dnspass,
1135                         serverrole=serverrole, ldap_backend=provision_backend)
1136
1137     if serverrole == "domain controller":
1138         if paths.netlogon is None:
1139             message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1140             message("Please either remove %s or see the template at %s" % 
1141                     ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1142             assert(paths.netlogon is not None)
1143
1144         if paths.sysvol is None:
1145             message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1146             message("Please either remove %s or see the template at %s" % 
1147                     (paths.smbconf, setup_path("provision.smb.conf.dc")))
1148             assert(paths.sysvol is not None)            
1149             
1150         policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies", 
1151                                    "{" + policyguid + "}")
1152         os.makedirs(policy_path, 0755)
1153         open(os.path.join(policy_path, "GPT.INI"), 'w').write("")
1154         os.makedirs(os.path.join(policy_path, "Machine"), 0755)
1155         os.makedirs(os.path.join(policy_path, "User"), 0755)
1156         if not os.path.isdir(paths.netlogon):
1157             os.makedirs(paths.netlogon, 0755)
1158
1159     if samdb_fill == FILL_FULL:
1160         setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1161                             root_uid=root_uid, nobody_uid=nobody_uid,
1162                             users_gid=users_gid, wheel_gid=wheel_gid)
1163
1164         message("Setting up sam.ldb rootDSE marking as synchronized")
1165         setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1166
1167         # Only make a zone file on the first DC, it should be replicated with DNS replication
1168         if serverrole == "domain controller":
1169             secrets_ldb = Ldb(paths.secrets, session_info=session_info, 
1170                               credentials=credentials, lp=lp)
1171             secretsdb_become_dc(secrets_ldb, setup_path, domain=domain, realm=names.realm,
1172                                 netbiosname=names.netbiosname, domainsid=domainsid, 
1173                                 keytab_path=paths.keytab, samdb_url=paths.samdb, 
1174                                 dns_keytab_path=paths.dns_keytab, dnspass=dnspass, 
1175                                 machinepass=machinepass, dnsdomain=names.dnsdomain)
1176
1177             domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1178             assert isinstance(domainguid, str)
1179             hostguid = samdb.searchone(basedn=domaindn, attribute="objectGUID",
1180                                        expression="(&(objectClass=computer)(cn=%s))" % names.hostname,
1181                                        scope=SCOPE_SUBTREE)
1182             assert isinstance(hostguid, str)
1183
1184             create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1185                              domaindn=names.domaindn, hostip=hostip,
1186                              hostip6=hostip6, hostname=names.hostname,
1187                              dnspass=dnspass, realm=names.realm,
1188                              domainguid=domainguid, hostguid=hostguid)
1189
1190             create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1191                               dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1192
1193             create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1194                               dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1195                               keytab_name=paths.dns_keytab)
1196             message("See %s for an example configuration include file for BIND" % paths.namedconf)
1197             message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1198
1199             create_krb5_conf(paths.krb5conf, setup_path, dnsdomain=names.dnsdomain,
1200                              hostname=names.hostname, realm=names.realm)
1201             message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1202
1203
1204     # if backend is openldap, terminate slapd after final provision and check its proper termination
1205     if provision_backend is not None and provision_backend.slapd is not None:
1206         if provision_backend.slapd.poll() is None:
1207             #Kill the slapd
1208             if hasattr(provision_backend.slapd, "terminate"):
1209                 provision_backend.slapd.terminate()
1210             else:
1211                 import signal
1212                 os.kill(provision_backend.slapd.pid, signal.SIGTERM)
1213             
1214             #and now wait for it to die
1215             provision_backend.slapd.communicate()
1216             
1217     # now display slapd_command_file.txt to show how slapd must be started next time
1218         message("Use later the following commandline to start slapd, then Samba:")
1219         slapd_command = "\'" + "\' \'".join(provision_backend.slapd_command) + "\'"
1220         message(slapd_command)
1221         message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh")
1222
1223         setup_file(setup_path("ldap_backend_startup.sh"), paths.ldapdir + "/ldap_backend_startup.sh", {
1224                 "SLAPD_COMMAND" : slapd_command})
1225
1226     
1227     create_phpldapadmin_config(paths.phpldapadminconfig, setup_path, 
1228                                ldapi_url)
1229
1230     message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1231
1232     message("Once the above files are installed, your Samba4 server will be ready to use")
1233     message("Server Role:           %s" % serverrole)
1234     message("Hostname:              %s" % names.hostname)
1235     message("NetBIOS Domain:        %s" % names.domain)
1236     message("DNS Domain:            %s" % names.dnsdomain)
1237     message("DOMAIN SID:            %s" % str(domainsid))
1238     if samdb_fill == FILL_FULL:
1239         message("Admin password:    %s" % adminpass)
1240     if provision_backend:
1241         if provision_backend.credentials.get_bind_dn() is not None:
1242             message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1243         else:
1244             message("LDAP Admin User:       %s" % provision_backend.credentials.get_username())
1245
1246         message("LDAP Admin Password:   %s" % provision_backend.credentials.get_password())
1247   
1248     result = ProvisionResult()
1249     result.domaindn = domaindn
1250     result.paths = paths
1251     result.lp = lp
1252     result.samdb = samdb
1253     return result
1254
1255
1256
1257 def provision_become_dc(setup_dir=None,
1258                         smbconf=None, targetdir=None, realm=None, 
1259                         rootdn=None, domaindn=None, schemadn=None, configdn=None,
1260                         serverdn=None,
1261                         domain=None, hostname=None, domainsid=None, 
1262                         adminpass=None, krbtgtpass=None, domainguid=None, 
1263                         policyguid=None, invocationid=None, machinepass=None, 
1264                         dnspass=None, root=None, nobody=None, users=None, 
1265                         wheel=None, backup=None, serverrole=None, 
1266                         ldap_backend=None, ldap_backend_type=None, sitename=None):
1267
1268     def message(text):
1269         """print a message if quiet is not set."""
1270         print text
1271
1272     return provision(setup_dir, message, system_session(), None,
1273               smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS, realm=realm, 
1274               rootdn=rootdn, domaindn=domaindn, schemadn=schemadn, configdn=configdn, serverdn=serverdn,
1275               domain=domain, hostname=hostname, hostip="127.0.0.1", domainsid=domainsid, machinepass=machinepass, serverrole="domain controller", sitename=sitename)
1276     
1277
1278 def setup_db_config(setup_path, dbdir):
1279     """Setup a Berkeley database.
1280     
1281     :param setup_path: Setup path function.
1282     :param dbdir: Database directory."""
1283     if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
1284         os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700)
1285         if not os.path.isdir(os.path.join(dbdir, "tmp")):
1286             os.makedirs(os.path.join(dbdir, "tmp"), 0700)
1287
1288     setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
1289                {"LDAPDBDIR": dbdir})
1290     
1291 class ProvisionBackend(object):
1292     def __init__(self, paths=None, setup_path=None, lp=None, credentials=None, 
1293                  names=None, message=None, 
1294                  hostname=None, root=None, 
1295                  schema=None, ldapadminpass=None,
1296                  ldap_backend_type=None, ldap_backend_extra_port=None,
1297                  ol_mmr_urls=None, 
1298                  setup_ds_path=None, slapd_path=None, 
1299                  nosync=False, ldap_dryrun_mode=False):
1300         """Provision an LDAP backend for samba4
1301         
1302         This works for OpenLDAP and Fedora DS
1303         """
1304
1305         self.ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.ldapdir, "ldapi"), safe="")
1306         
1307         if not os.path.isdir(paths.ldapdir):
1308             os.makedirs(paths.ldapdir, 0700)
1309             
1310         if ldap_backend_type == "existing":
1311             #Check to see that this 'existing' LDAP backend in fact exists
1312             ldapi_db = Ldb(self.ldapi_uri, credentials=credentials)
1313             search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1314                                                 expression="(objectClass=OpenLDAProotDSE)")
1315
1316             # If we have got here, then we must have a valid connection to the LDAP server, with valid credentials supplied
1317             # This caused them to be set into the long-term database later in the script.
1318             self.credentials = credentials
1319             self.ldap_backend_type = "openldap" #For now, assume existing backends at least emulate OpenLDAP
1320             return
1321     
1322         # we will shortly start slapd with ldapi for final provisioning. first check with ldapsearch -> rootDSE via self.ldapi_uri
1323         # if another instance of slapd is already running 
1324         try:
1325             ldapi_db = Ldb(self.ldapi_uri)
1326             search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1327                                                 expression="(objectClass=OpenLDAProotDSE)");
1328             try:
1329                 f = open(paths.slapdpid, "r")
1330                 p = f.read()
1331                 f.close()
1332                 message("Check for slapd Process with PID: " + str(p) + " and terminate it manually.")
1333             except:
1334                 pass
1335             
1336             raise("Warning: Another slapd Instance seems already running on this host, listening to " + self.ldapi_uri + ". Please shut it down before you continue. ")
1337         
1338         except LdbError, e:
1339             pass
1340
1341         # Try to print helpful messages when the user has not specified the path to slapd
1342         if slapd_path is None:
1343             raise("Warning: LDAP-Backend must be setup with path to slapd, e.g. --slapd-path=\"/usr/local/libexec/slapd\"!")
1344         if not os.path.exists(slapd_path):
1345             message (slapd_path)
1346             raise("Warning: Given Path to slapd does not exist!")
1347
1348         schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
1349         try:
1350             os.unlink(schemadb_path)
1351         except OSError:
1352             pass
1353
1354
1355         # Put the LDIF of the schema into a database so we can search on
1356         # it to generate schema-dependent configurations in Fedora DS and
1357         # OpenLDAP
1358         os.path.join(paths.ldapdir, "schema-tmp.ldb")
1359         schema.ldb.connect(schemadb_path)
1360         schema.ldb.transaction_start()
1361     
1362         # These bits of LDIF are supplied when the Schema object is created
1363         schema.ldb.add_ldif(schema.schema_dn_add)
1364         schema.ldb.modify_ldif(schema.schema_dn_modify)
1365         schema.ldb.add_ldif(schema.schema_data)
1366         schema.ldb.transaction_commit()
1367
1368         self.credentials = Credentials()
1369         self.credentials.guess(lp)
1370         #Kerberos to an ldapi:// backend makes no sense
1371         self.credentials.set_kerberos_state(DONT_USE_KERBEROS)
1372         self.ldap_backend_type = ldap_backend_type
1373
1374         if ldap_backend_type == "fedora-ds":
1375             provision_fds_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                                   setup_ds_path=setup_ds_path, slapd_path=slapd_path,
1379                                   nosync=nosync, ldap_dryrun_mode=ldap_dryrun_mode)
1380             
1381         elif ldap_backend_type == "openldap":
1382             provision_openldap_backend(self, paths=paths, setup_path=setup_path, names=names, message=message, 
1383                                        hostname=hostname, ldapadminpass=ldapadminpass, root=root, 
1384                                        schema=schema, ldap_backend_extra_port=ldap_backend_extra_port, 
1385                                        ol_mmr_urls=ol_mmr_urls, 
1386                                        slapd_path=slapd_path,
1387                                        nosync=nosync, ldap_dryrun_mode=ldap_dryrun_mode)
1388         else:
1389             raise("Unknown LDAP backend type selected")
1390
1391         self.credentials.set_password(ldapadminpass)
1392
1393         # Now start the slapd, so we can provision onto it.  We keep the
1394         # subprocess context around, to kill this off at the successful
1395         # end of the script
1396         self.slapd = subprocess.Popen(self.slapd_provision_command, close_fds=True, shell=False)
1397     
1398         while self.slapd.poll() is None:
1399             # Wait until the socket appears
1400             try:
1401                 ldapi_db = Ldb(self.ldapi_uri, lp=lp, credentials=self.credentials)
1402                 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1403                                                     expression="(objectClass=OpenLDAProotDSE)")
1404                 # If we have got here, then we must have a valid connection to the LDAP server!
1405                 return
1406             except LdbError, e:
1407                 time.sleep(1)
1408                 pass
1409         
1410         raise "slapd died before we could make a connection to it"
1411
1412
1413 def provision_openldap_backend(result, paths=None, setup_path=None, names=None, message=None, 
1414                                hostname=None, ldapadminpass=None, root=None, 
1415                                schema=None, 
1416                                ldap_backend_extra_port=None,
1417                                ol_mmr_urls=None, 
1418                                slapd_path=None, nosync=False,
1419                                ldap_dryrun_mode=False):
1420
1421     #Allow the test scripts to turn off fsync() for OpenLDAP as for TDB and LDB
1422     nosync_config = ""
1423     if nosync:
1424         nosync_config = "dbnosync"
1425         
1426         
1427     attrs = ["linkID", "lDAPDisplayName"]
1428     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)
1429
1430     memberof_config = "# Generated from Samba4 schema\n"
1431     refint_attributes = ""
1432     for i in range (0, len(res)):
1433         expression = "(&(objectclass=attributeSchema)(linkID=%d)(attributeSyntax=2.5.5.1))" % (int(res[i]["linkID"][0])+1)
1434         target = schema.ldb.searchone(basedn=names.schemadn, 
1435                                       expression=expression, 
1436                                       attribute="lDAPDisplayName", 
1437                                       scope=SCOPE_SUBTREE)
1438         if target is not None:
1439             refint_attributes = refint_attributes + " " + res[i]["lDAPDisplayName"][0]
1440             
1441             memberof_config += read_and_sub_file(setup_path("memberof.conf"),
1442                                                  { "MEMBER_ATTR" : str(res[i]["lDAPDisplayName"][0]),
1443                                                    "MEMBEROF_ATTR" : str(target) })
1444             
1445     refint_config = read_and_sub_file(setup_path("refint.conf"),
1446                                       { "LINK_ATTRS" : refint_attributes})
1447     
1448     res = schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
1449     index_config = ""
1450     for i in range (0, len(res)):
1451         index_attr = res[i]["lDAPDisplayName"][0]
1452         if index_attr == "objectGUID":
1453             index_attr = "entryUUID"
1454             
1455         index_config += "index " + index_attr + " eq\n"
1456
1457 # generate serverids, ldap-urls and syncrepl-blocks for mmr hosts
1458     mmr_on_config = ""
1459     mmr_replicator_acl = ""
1460     mmr_serverids_config = ""
1461     mmr_syncrepl_schema_config = "" 
1462     mmr_syncrepl_config_config = "" 
1463     mmr_syncrepl_user_config = "" 
1464        
1465     
1466     if ol_mmr_urls is not None:
1467         # For now, make these equal
1468         mmr_pass = ldapadminpass
1469         
1470         url_list=filter(None,ol_mmr_urls.split(' ')) 
1471         if (len(url_list) == 1):
1472             url_list=filter(None,ol_mmr_urls.split(',')) 
1473                      
1474             
1475             mmr_on_config = "MirrorMode On"
1476             mmr_replicator_acl = "  by dn=cn=replicator,cn=samba read"
1477             serverid=0
1478             for url in url_list:
1479                 serverid=serverid+1
1480                 mmr_serverids_config += read_and_sub_file(setup_path("mmr_serverids.conf"),
1481                                                           { "SERVERID" : str(serverid),
1482                                                             "LDAPSERVER" : url })
1483                 rid=serverid*10
1484                 rid=rid+1
1485                 mmr_syncrepl_schema_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1486                                                                 {  "RID" : str(rid),
1487                                                                    "MMRDN": names.schemadn,
1488                                                                    "LDAPSERVER" : url,
1489                                                                    "MMR_PASSWORD": mmr_pass})
1490                 
1491                 rid=rid+1
1492                 mmr_syncrepl_config_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1493                                                                 {  "RID" : str(rid),
1494                                                                    "MMRDN": names.configdn,
1495                                                                    "LDAPSERVER" : url,
1496                                                                    "MMR_PASSWORD": mmr_pass})
1497                 
1498                 rid=rid+1
1499                 mmr_syncrepl_user_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1500                                                               {  "RID" : str(rid),
1501                                                                  "MMRDN": names.domaindn,
1502                                                                  "LDAPSERVER" : url,
1503                                                                  "MMR_PASSWORD": mmr_pass })
1504     # OpenLDAP cn=config initialisation
1505     olc_syncrepl_config = ""
1506     olc_mmr_config = "" 
1507     # if mmr = yes, generate cn=config-replication directives
1508     # and olc_seed.lif for the other mmr-servers
1509     if ol_mmr_urls is not None:
1510         serverid=0
1511         olc_serverids_config = ""
1512         olc_syncrepl_seed_config = ""
1513         olc_mmr_config += read_and_sub_file(setup_path("olc_mmr.conf"),{})
1514         rid=1000
1515         for url in url_list:
1516             serverid=serverid+1
1517             olc_serverids_config += read_and_sub_file(setup_path("olc_serverid.conf"),
1518                                                       { "SERVERID" : str(serverid),
1519                                                         "LDAPSERVER" : url })
1520             
1521             rid=rid+1
1522             olc_syncrepl_config += read_and_sub_file(setup_path("olc_syncrepl.conf"),
1523                                                      {  "RID" : str(rid),
1524                                                         "LDAPSERVER" : url,
1525                                                         "MMR_PASSWORD": mmr_pass})
1526             
1527             olc_syncrepl_seed_config += read_and_sub_file(setup_path("olc_syncrepl_seed.conf"),
1528                                                           {  "RID" : str(rid),
1529                                                              "LDAPSERVER" : url})
1530                 
1531         setup_file(setup_path("olc_seed.ldif"), paths.olcseedldif,
1532                    {"OLC_SERVER_ID_CONF": olc_serverids_config,
1533                     "OLC_PW": ldapadminpass,
1534                     "OLC_SYNCREPL_CONF": olc_syncrepl_seed_config})
1535     # end olc
1536                 
1537     setup_file(setup_path("slapd.conf"), paths.slapdconf,
1538                {"DNSDOMAIN": names.dnsdomain,
1539                 "LDAPDIR": paths.ldapdir,
1540                 "DOMAINDN": names.domaindn,
1541                 "CONFIGDN": names.configdn,
1542                 "SCHEMADN": names.schemadn,
1543                 "MEMBEROF_CONFIG": memberof_config,
1544                 "MIRRORMODE": mmr_on_config,
1545                 "REPLICATOR_ACL": mmr_replicator_acl,
1546                 "MMR_SERVERIDS_CONFIG": mmr_serverids_config,
1547                 "MMR_SYNCREPL_SCHEMA_CONFIG": mmr_syncrepl_schema_config,
1548                 "MMR_SYNCREPL_CONFIG_CONFIG": mmr_syncrepl_config_config,
1549                 "MMR_SYNCREPL_USER_CONFIG": mmr_syncrepl_user_config,
1550                 "OLC_SYNCREPL_CONFIG": olc_syncrepl_config,
1551                 "OLC_MMR_CONFIG": olc_mmr_config,
1552                 "REFINT_CONFIG": refint_config,
1553                 "INDEX_CONFIG": index_config,
1554                 "NOSYNC": nosync_config})
1555         
1556     setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "user"))
1557     setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "config"))
1558     setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "schema"))
1559     
1560     if not os.path.exists(os.path.join(paths.ldapdir, "db", "samba",  "cn=samba")):
1561         os.makedirs(os.path.join(paths.ldapdir, "db", "samba",  "cn=samba"), 0700)
1562         
1563     setup_file(setup_path("cn=samba.ldif"), 
1564                os.path.join(paths.ldapdir, "db", "samba",  "cn=samba.ldif"),
1565                { "UUID": str(uuid.uuid4()), 
1566                  "LDAPTIME": timestring(int(time.time()))} )
1567     setup_file(setup_path("cn=samba-admin.ldif"), 
1568                os.path.join(paths.ldapdir, "db", "samba",  "cn=samba", "cn=samba-admin.ldif"),
1569                {"LDAPADMINPASS_B64": b64encode(ldapadminpass),
1570                 "UUID": str(uuid.uuid4()), 
1571                 "LDAPTIME": timestring(int(time.time()))} )
1572     
1573     if ol_mmr_urls is not None:
1574         setup_file(setup_path("cn=replicator.ldif"),
1575                    os.path.join(paths.ldapdir, "db", "samba",  "cn=samba", "cn=replicator.ldif"),
1576                    {"MMR_PASSWORD_B64": b64encode(mmr_pass),
1577                     "UUID": str(uuid.uuid4()),
1578                     "LDAPTIME": timestring(int(time.time()))} )
1579         
1580
1581     mapping = "schema-map-openldap-2.3"
1582     backend_schema = "backend-schema.schema"
1583
1584     backend_schema_data = schema.ldb.convert_schema_to_openldap("openldap", open(setup_path(mapping), 'r').read())
1585     assert backend_schema_data is not None
1586     open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1587
1588     # now we generate the needed strings to start slapd automatically,
1589     # first ldapi_uri...
1590     if ldap_backend_extra_port is not None:
1591         # When we use MMR, we can't use 0.0.0.0 as it uses the name
1592         # specified there as part of it's clue as to it's own name,
1593         # and not to replicate to itself
1594         if ol_mmr_urls is None:
1595             server_port_string = "ldap://0.0.0.0:%d" % ldap_backend_extra_port
1596         else:
1597             server_port_string = "ldap://" + names.hostname + "." + names.dnsdomain +":%d" % ldap_backend_extra_port
1598     else:
1599         server_port_string = ""
1600
1601     # Prepare the 'result' information - the commands to return in particular
1602     result.slapd_provision_command = [slapd_path]
1603
1604     result.slapd_provision_command.append("-F" + paths.olcdir)
1605
1606     result.slapd_provision_command.append("-h")
1607
1608     # copy this command so we have two version, one with -d0 and only ldapi, and one with all the listen commands
1609     result.slapd_command = list(result.slapd_provision_command)
1610     
1611     result.slapd_provision_command.append(result.ldapi_uri)
1612     result.slapd_provision_command.append("-d0")
1613
1614     uris = result.ldapi_uri
1615     if server_port_string is not "":
1616         uris = uris + " " + server_port_string
1617
1618     result.slapd_command.append(uris)
1619
1620     # Set the username - done here because Fedora DS still uses the admin DN and simple bind
1621     result.credentials.set_username("samba-admin")
1622     
1623     # If we were just looking for crashes up to this point, it's a
1624     # good time to exit before we realise we don't have OpenLDAP on
1625     # this system
1626     if ldap_dryrun_mode:
1627         sys.exit(0)
1628
1629     # Finally, convert the configuration into cn=config style!
1630     if not os.path.isdir(paths.olcdir):
1631         os.makedirs(paths.olcdir, 0770)
1632
1633         retcode = subprocess.call([slapd_path, "-Ttest", "-f", paths.slapdconf, "-F", paths.olcdir], close_fds=True, shell=False)
1634
1635 #        We can't do this, as OpenLDAP is strange.  It gives an error
1636 #        output to the above, but does the conversion sucessfully...
1637 #
1638 #        if retcode != 0:
1639 #            raise("conversion from slapd.conf to cn=config failed")
1640
1641         if not os.path.exists(os.path.join(paths.olcdir, "cn=config.ldif")):
1642             raise("conversion from slapd.conf to cn=config failed")
1643
1644         # Don't confuse the admin by leaving the slapd.conf around
1645         os.remove(paths.slapdconf)        
1646           
1647
1648 def provision_fds_backend(result, paths=None, setup_path=None, names=None, message=None, 
1649                           hostname=None, ldapadminpass=None, root=None, 
1650                           schema=None,
1651                           ldap_backend_extra_port=None,
1652                           setup_ds_path=None,
1653                           slapd_path=None,
1654                           nosync=False, 
1655                           ldap_dryrun_mode=False):
1656
1657     if ldap_backend_extra_port is not None:
1658         serverport = "ServerPort=%d" % ldap_backend_extra_port
1659     else:
1660         serverport = ""
1661         
1662     setup_file(setup_path("fedorads.inf"), paths.fedoradsinf, 
1663                {"ROOT": root,
1664                 "HOSTNAME": hostname,
1665                 "DNSDOMAIN": names.dnsdomain,
1666                 "LDAPDIR": paths.ldapdir,
1667                 "DOMAINDN": names.domaindn,
1668                 "LDAPMANAGERDN": names.ldapmanagerdn,
1669                 "LDAPMANAGERPASS": ldapadminpass, 
1670                 "SERVERPORT": serverport})
1671
1672     setup_file(setup_path("fedorads-partitions.ldif"), paths.fedoradspartitions, 
1673                {"CONFIGDN": names.configdn,
1674                 "SCHEMADN": names.schemadn,
1675                 })
1676
1677     mapping = "schema-map-fedora-ds-1.0"
1678     backend_schema = "99_ad.ldif"
1679     
1680     # Build a schema file in Fedora DS format
1681     backend_schema_data = schema.ldb.convert_schema_to_openldap("fedora-ds", open(setup_path(mapping), 'r').read())
1682     assert backend_schema_data is not None
1683     open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1684
1685     result.credentials.set_bind_dn(names.ldapmanagerdn)
1686
1687     # Destory the target directory, or else setup-ds.pl will complain
1688     fedora_ds_dir = os.path.join(paths.ldapdir, "slapd-samba4")
1689     shutil.rmtree(fedora_ds_dir, True)
1690
1691     result.slapd_provision_command = [slapd_path, "-D", fedora_ds_dir, "-i", paths.slapdpid];
1692     #In the 'provision' command line, stay in the foreground so we can easily kill it
1693     result.slapd_provision_command.append("-d0")
1694
1695     #the command for the final run is the normal script
1696     result.slapd_command = [os.path.join(paths.ldapdir, "slapd-samba4", "start-slapd")]
1697
1698     # If we were just looking for crashes up to this point, it's a
1699     # good time to exit before we realise we don't have Fedora DS on
1700     if ldap_dryrun_mode:
1701         sys.exit(0)
1702
1703     # Try to print helpful messages when the user has not specified the path to the setup-ds tool
1704     if setup_ds_path is None:
1705         raise("Warning: Fedora DS LDAP-Backend must be setup with path to setup-ds, e.g. --setup-ds-path=\"/usr/sbin/setup-ds.pl\"!")
1706     if not os.path.exists(setup_ds_path):
1707         message (setup_ds_path)
1708         raise("Warning: Given Path to slapd does not exist!")
1709
1710     # Run the Fedora DS setup utility
1711     retcode = subprocess.call([setup_ds_path, "--silent", "--file", paths.fedoradsinf], close_fds=True, shell=False)
1712     if retcode != 0:
1713         raise("setup-ds failed")
1714
1715 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1716     """Create a PHP LDAP admin configuration file.
1717
1718     :param path: Path to write the configuration to.
1719     :param setup_path: Function to generate setup paths.
1720     """
1721     setup_file(setup_path("phpldapadmin-config.php"), path, 
1722             {"S4_LDAPI_URI": ldapi_uri})
1723
1724
1725 def create_zone_file(path, setup_path, dnsdomain, domaindn, 
1726                      hostip, hostip6, hostname, dnspass, realm, domainguid, hostguid):
1727     """Write out a DNS zone file, from the info in the current database.
1728
1729     :param path: Path of the new zone file.
1730     :param setup_path: Setup path function.
1731     :param dnsdomain: DNS Domain name
1732     :param domaindn: DN of the Domain
1733     :param hostip: Local IPv4 IP
1734     :param hostip6: Local IPv6 IP
1735     :param hostname: Local hostname
1736     :param dnspass: Password for DNS
1737     :param realm: Realm name
1738     :param domainguid: GUID of the domain.
1739     :param hostguid: GUID of the host.
1740     """
1741     assert isinstance(domainguid, str)
1742
1743     if hostip6 is not None:
1744         hostip6_base_line = "            IN AAAA    " + hostip6
1745         hostip6_host_line = hostname + "        IN AAAA    " + hostip6
1746     else:
1747         hostip6_base_line = ""
1748         hostip6_host_line = ""
1749
1750     if hostip is not None:
1751         hostip_base_line = "            IN A    " + hostip
1752         hostip_host_line = hostname + "        IN A    " + hostip
1753     else:
1754         hostip_base_line = ""
1755         hostip_host_line = ""
1756
1757     setup_file(setup_path("provision.zone"), path, {
1758             "DNSPASS_B64": b64encode(dnspass),
1759             "HOSTNAME": hostname,
1760             "DNSDOMAIN": dnsdomain,
1761             "REALM": realm,
1762             "HOSTIP_BASE_LINE": hostip_base_line,
1763             "HOSTIP_HOST_LINE": hostip_host_line,
1764             "DOMAINGUID": domainguid,
1765             "DATESTRING": time.strftime("%Y%m%d%H"),
1766             "DEFAULTSITE": DEFAULTSITE,
1767             "HOSTGUID": hostguid,
1768             "HOSTIP6_BASE_LINE": hostip6_base_line,
1769             "HOSTIP6_HOST_LINE": hostip6_host_line,
1770         })
1771
1772
1773 def create_named_conf(path, setup_path, realm, dnsdomain,
1774                       private_dir):
1775     """Write out a file containing zone statements suitable for inclusion in a
1776     named.conf file (including GSS-TSIG configuration).
1777     
1778     :param path: Path of the new named.conf file.
1779     :param setup_path: Setup path function.
1780     :param realm: Realm name
1781     :param dnsdomain: DNS Domain name
1782     :param private_dir: Path to private directory
1783     :param keytab_name: File name of DNS keytab file
1784     """
1785
1786     setup_file(setup_path("named.conf"), path, {
1787             "DNSDOMAIN": dnsdomain,
1788             "REALM": realm,
1789             "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1790             "PRIVATE_DIR": private_dir
1791             })
1792
1793 def create_named_txt(path, setup_path, realm, dnsdomain,
1794                       private_dir, keytab_name):
1795     """Write out a file containing zone statements suitable for inclusion in a
1796     named.conf file (including GSS-TSIG configuration).
1797     
1798     :param path: Path of the new named.conf file.
1799     :param setup_path: Setup path function.
1800     :param realm: Realm name
1801     :param dnsdomain: DNS Domain name
1802     :param private_dir: Path to private directory
1803     :param keytab_name: File name of DNS keytab file
1804     """
1805
1806     setup_file(setup_path("named.txt"), path, {
1807             "DNSDOMAIN": dnsdomain,
1808             "REALM": realm,
1809             "DNS_KEYTAB": keytab_name,
1810             "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1811             "PRIVATE_DIR": private_dir
1812         })
1813
1814 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1815     """Write out a file containing zone statements suitable for inclusion in a
1816     named.conf file (including GSS-TSIG configuration).
1817     
1818     :param path: Path of the new named.conf file.
1819     :param setup_path: Setup path function.
1820     :param dnsdomain: DNS Domain name
1821     :param hostname: Local hostname
1822     :param realm: Realm name
1823     """
1824
1825     setup_file(setup_path("krb5.conf"), path, {
1826             "DNSDOMAIN": dnsdomain,
1827             "HOSTNAME": hostname,
1828             "REALM": realm,
1829         })
1830
1831
1832
1833  
1834