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