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