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