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