more fixups from provision changes
[metze/samba/wip.git] / source4 / scripting / python / samba / provision.py
1 #
2 # Unix SMB/CIFS implementation.
3 # backend code for provisioning a Samba4 server
4
5 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
6 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
7 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
8 #
9 # Based on the original in EJS:
10 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
11 #
12 # This program is free software; you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
14 # the Free Software Foundation; either version 3 of the License, or
15 # (at your option) any later version.
16 #   
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 # GNU General Public License for more details.
21 #   
22 # You should have received a copy of the GNU General Public License
23 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
24 #
25
26 """Functions for setting up a Samba configuration."""
27
28 from base64 import b64encode
29 import os
30 import sys
31 import pwd
32 import grp
33 import time
34 import uuid, glue
35 import socket
36 import param
37 import registry
38 import samba
39 import subprocess
40
41 import shutil
42 from credentials import Credentials, DONT_USE_KERBEROS
43 from auth import system_session
44 from samba import version, Ldb, substitute_var, valid_netbios_name, check_all_substituted, \
45   DS_BEHAVIOR_WIN2008
46 from samba.samdb import SamDB
47 from samba.idmap import IDmapDB
48 from samba.dcerpc import security
49 import urllib
50 from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError, timestring
51 from ms_schema import read_ms_schema
52 from signal import SIGTERM
53
54 __docformat__ = "restructuredText"
55
56
57 def find_setup_dir():
58     """Find the setup directory used by provision."""
59     dirname = os.path.dirname(__file__)
60     if "/site-packages/" in dirname:
61         prefix = "/".join(dirname[:dirname.index("/site-packages/")].split("/")[:-2])
62         for suffix in ["share/setup", "share/samba/setup", "setup"]:
63             ret = os.path.join(prefix, suffix)
64             if os.path.isdir(ret):
65                 return ret
66     # In source tree
67     ret = os.path.join(dirname, "../../../setup")
68     if os.path.isdir(ret):
69         return ret
70     raise Exception("Unable to find setup directory.")
71
72
73 DEFAULTSITE = "Default-First-Site-Name"
74
75 class InvalidNetbiosName(Exception):
76     """A specified name was not a valid NetBIOS name."""
77     def __init__(self, name):
78         super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)
79
80
81 class ProvisionPaths(object):
82     def __init__(self):
83         self.shareconf = None
84         self.hklm = None
85         self.hkcu = None
86         self.hkcr = None
87         self.hku = None
88         self.hkpd = None
89         self.hkpt = None
90         self.samdb = None
91         self.idmapdb = None
92         self.secrets = None
93         self.keytab = None
94         self.dns_keytab = None
95         self.dns = None
96         self.winsdb = None
97         self.private_dir = None
98         self.ldapdir = None
99         self.slapdconf = None
100         self.modulesconf = None
101         self.memberofconf = None
102         self.fedoradsinf = None
103         self.fedoradspartitions = None
104         self.olmmron = None
105         self.olmmrserveridsconf = None
106         self.olmmrsyncreplconf = None
107         self.olcdir = None
108         self.olslapd = None
109         self.olcseedldif = None
110
111
112 class ProvisionNames(object):
113     def __init__(self):
114         self.rootdn = None
115         self.domaindn = None
116         self.configdn = None
117         self.schemadn = None
118         self.ldapmanagerdn = None
119         self.dnsdomain = None
120         self.realm = None
121         self.netbiosname = None
122         self.domain = None
123         self.hostname = None
124         self.sitename = None
125         self.smbconf = None
126     
127
128 class ProvisionResult(object):
129     def __init__(self):
130         self.paths = None
131         self.domaindn = None
132         self.lp = None
133         self.samdb = None
134         
135 class Schema(object):
136     def __init__(self, setup_path, schemadn=None, 
137                  serverdn=None):
138         """Load schema for the SamDB from the AD schema files and samba4_schema.ldif
139         
140         :param samdb: Load a schema into a SamDB.
141         :param setup_path: Setup path function.
142         :param schemadn: DN of the schema
143         :param serverdn: DN of the server
144         
145         Returns the schema data loaded, to avoid double-parsing when then needing to add it to the db
146         """
147         
148         self.ldb = Ldb()
149         self.schema_data = read_ms_schema(setup_path('ad-schema/MS-AD_Schema_2K8_Attributes.txt'),
150                                           setup_path('ad-schema/MS-AD_Schema_2K8_Classes.txt'))
151         self.schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
152         self.schema_data = substitute_var(self.schema_data, {"SCHEMADN": schemadn})
153         check_all_substituted(self.schema_data)
154         prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
155         prefixmap = b64encode(prefixmap)
156         
157         self.schema_dn_modify = read_and_sub_file(setup_path("provision_schema_basedn_modify.ldif"),
158                                                   {"SCHEMADN": schemadn,
159                                                    "PREFIXMAP_B64": prefixmap,
160                                                    "SERVERDN": serverdn,
161                                                    })
162         self.schema_dn_add = read_and_sub_file(setup_path("provision_schema_basedn.ldif"),
163                                                {"SCHEMADN": schemadn
164                                                 })
165         self.ldb.set_schema_from_ldif(self.schema_dn_modify, self.schema_data)
166
167     
168 def check_install(lp, session_info, credentials):
169     """Check whether the current install seems ok.
170     
171     :param lp: Loadparm context
172     :param session_info: Session information
173     :param credentials: Credentials
174     """
175     if lp.get("realm") == "":
176         raise Exception("Realm empty")
177     ldb = Ldb(lp.get("sam database"), session_info=session_info, 
178             credentials=credentials, lp=lp)
179     if len(ldb.search("(cn=Administrator)")) != 1:
180         raise "No administrator account found"
181
182
183 def findnss(nssfn, names):
184     """Find a user or group from a list of possibilities.
185     
186     :param nssfn: NSS Function to try (should raise KeyError if not found)
187     :param names: Names to check.
188     :return: Value return by first names list.
189     """
190     for name in names:
191         try:
192             return nssfn(name)
193         except KeyError:
194             pass
195     raise KeyError("Unable to find user/group %r" % names)
196
197
198 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
199 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
200
201
202 def read_and_sub_file(file, subst_vars):
203     """Read a file and sub in variables found in it
204     
205     :param file: File to be read (typically from setup directory)
206      param subst_vars: Optional variables to subsitute in the file.
207     """
208     data = open(file, 'r').read()
209     if subst_vars is not None:
210         data = substitute_var(data, subst_vars)
211     check_all_substituted(data)
212     return data
213
214
215 def setup_add_ldif(ldb, ldif_path, subst_vars=None):
216     """Setup a ldb in the private dir.
217     
218     :param ldb: LDB file to import data into
219     :param ldif_path: Path of the LDIF file to load
220     :param subst_vars: Optional variables to subsitute in LDIF.
221     """
222     assert isinstance(ldif_path, str)
223
224     data = read_and_sub_file(ldif_path, subst_vars)
225     ldb.add_ldif(data)
226
227
228 def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
229     """Modify a ldb in the private dir.
230     
231     :param ldb: LDB object.
232     :param ldif_path: LDIF file path.
233     :param subst_vars: Optional dictionary with substitution variables.
234     """
235     data = read_and_sub_file(ldif_path, subst_vars)
236
237     ldb.modify_ldif(data)
238
239
240 def setup_ldb(ldb, ldif_path, subst_vars):
241     """Import a LDIF a file into a LDB handle, optionally substituting variables.
242
243     :note: Either all LDIF data will be added or none (using transactions).
244
245     :param ldb: LDB file to import into.
246     :param ldif_path: Path to the LDIF file.
247     :param subst_vars: Dictionary with substitution variables.
248     """
249     assert ldb is not None
250     ldb.transaction_start()
251     try:
252         setup_add_ldif(ldb, ldif_path, subst_vars)
253     except:
254         ldb.transaction_cancel()
255         raise
256     ldb.transaction_commit()
257
258
259 def setup_file(template, fname, subst_vars):
260     """Setup a file in the private dir.
261
262     :param template: Path of the template file.
263     :param fname: Path of the file to create.
264     :param subst_vars: Substitution variables.
265     """
266     f = fname
267
268     if os.path.exists(f):
269         os.unlink(f)
270
271     data = read_and_sub_file(template, subst_vars)
272     open(f, 'w').write(data)
273
274
275 def provision_paths_from_lp(lp, dnsdomain):
276     """Set the default paths for provisioning.
277
278     :param lp: Loadparm context.
279     :param dnsdomain: DNS Domain name
280     """
281     paths = ProvisionPaths()
282     paths.private_dir = lp.get("private dir")
283     paths.keytab = "secrets.keytab"
284     paths.dns_keytab = "dns.keytab"
285
286     paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
287     paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
288     paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
289     paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
290     paths.templates = os.path.join(paths.private_dir, "templates.ldb")
291     paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
292     paths.namedconf = os.path.join(paths.private_dir, "named.conf")
293     paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
294     paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
295     paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
296     paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
297     paths.phpldapadminconfig = os.path.join(paths.private_dir, 
298                                             "phpldapadmin-config.php")
299     paths.ldapdir = os.path.join(paths.private_dir, 
300                                  "ldap")
301     paths.slapdconf = os.path.join(paths.ldapdir, 
302                                    "slapd.conf")
303     paths.slapdpid = os.path.join(paths.ldapdir, 
304                                    "slapd.pid")
305     paths.modulesconf = os.path.join(paths.ldapdir, 
306                                      "modules.conf")
307     paths.memberofconf = os.path.join(paths.ldapdir, 
308                                       "memberof.conf")
309     paths.fedoradsinf = os.path.join(paths.ldapdir, 
310                                      "fedorads.inf")
311     paths.fedoradspartitions = os.path.join(paths.ldapdir, 
312                                             "fedorads-partitions.ldif")
313     paths.olmmrserveridsconf = os.path.join(paths.ldapdir, 
314                                             "mmr_serverids.conf")
315     paths.olmmrsyncreplconf = os.path.join(paths.ldapdir, 
316                                            "mmr_syncrepl.conf")
317     paths.olcdir = os.path.join(paths.ldapdir, 
318                                  "slapd.d")
319     paths.olcseedldif = os.path.join(paths.ldapdir, 
320                                  "olc_seed.ldif")
321     paths.hklm = "hklm.ldb"
322     paths.hkcr = "hkcr.ldb"
323     paths.hkcu = "hkcu.ldb"
324     paths.hku = "hku.ldb"
325     paths.hkpd = "hkpd.ldb"
326     paths.hkpt = "hkpt.ldb"
327
328     paths.sysvol = lp.get("path", "sysvol")
329
330     paths.netlogon = lp.get("path", "netlogon")
331
332     paths.smbconf = lp.configfile
333
334     return paths
335
336
337 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None, serverrole=None,
338                 rootdn=None, domaindn=None, configdn=None, schemadn=None, serverdn=None, 
339                 sitename=None):
340     """Guess configuration settings to use."""
341
342     if hostname is None:
343         hostname = socket.gethostname().split(".")[0].lower()
344
345     netbiosname = hostname.upper()
346     if not valid_netbios_name(netbiosname):
347         raise InvalidNetbiosName(netbiosname)
348
349     hostname = hostname.lower()
350
351     if dnsdomain is None:
352         dnsdomain = lp.get("realm")
353
354     if serverrole is None:
355         serverrole = lp.get("server role")
356
357     assert dnsdomain is not None
358     realm = dnsdomain.upper()
359
360     if lp.get("realm").upper() != realm:
361         raise Exception("realm '%s' in %s must match chosen realm '%s'" %
362                         (lp.get("realm"), lp.configfile, realm))
363     
364     dnsdomain = dnsdomain.lower()
365
366     if serverrole == "domain controller":
367         if domain is None:
368             domain = lp.get("workgroup")
369         if domaindn is None:
370             domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
371         if lp.get("workgroup").upper() != domain.upper():
372             raise Exception("workgroup '%s' in smb.conf must match chosen domain '%s'",
373                         lp.get("workgroup"), domain)
374     else:
375         domain = netbiosname
376         if domaindn is None:
377             domaindn = "CN=" + netbiosname
378         
379     assert domain is not None
380     domain = domain.upper()
381     if not valid_netbios_name(domain):
382         raise InvalidNetbiosName(domain)
383         
384     if rootdn is None:
385        rootdn = domaindn
386        
387     if configdn is None:
388         configdn = "CN=Configuration," + rootdn
389     if schemadn is None:
390         schemadn = "CN=Schema," + configdn
391
392     if sitename is None:
393         sitename=DEFAULTSITE
394
395     names = ProvisionNames()
396     names.rootdn = rootdn
397     names.domaindn = domaindn
398     names.configdn = configdn
399     names.schemadn = schemadn
400     names.ldapmanagerdn = "CN=Manager," + rootdn
401     names.dnsdomain = dnsdomain
402     names.domain = domain
403     names.realm = realm
404     names.netbiosname = netbiosname
405     names.hostname = hostname
406     names.sitename = sitename
407     names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
408  
409     return names
410     
411
412 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, 
413                  targetdir):
414     """Create a new smb.conf file based on a couple of basic settings.
415     """
416     assert smbconf is not None
417     if hostname is None:
418         hostname = socket.gethostname().split(".")[0].lower()
419
420     if serverrole is None:
421         serverrole = "standalone"
422
423     assert serverrole in ("domain controller", "member server", "standalone")
424     if serverrole == "domain controller":
425         smbconfsuffix = "dc"
426     elif serverrole == "member server":
427         smbconfsuffix = "member"
428     elif serverrole == "standalone":
429         smbconfsuffix = "standalone"
430
431     assert domain is not None
432     assert realm is not None
433
434     default_lp = param.LoadParm()
435     #Load non-existant file
436     if os.path.exists(smbconf):
437         default_lp.load(smbconf)
438     
439     if targetdir is not None:
440         privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
441         lockdir_line = "lock dir = " + os.path.abspath(targetdir)
442
443         default_lp.set("lock dir", os.path.abspath(targetdir))
444     else:
445         privatedir_line = ""
446         lockdir_line = ""
447
448     sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
449     netlogon = os.path.join(sysvol, realm.lower(), "scripts")
450
451     setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix), 
452                smbconf, {
453             "HOSTNAME": hostname,
454             "DOMAIN": domain,
455             "REALM": realm,
456             "SERVERROLE": serverrole,
457             "NETLOGONPATH": netlogon,
458             "SYSVOLPATH": sysvol,
459             "PRIVATEDIR_LINE": privatedir_line,
460             "LOCKDIR_LINE": lockdir_line
461             })
462
463
464 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
465                         users_gid, wheel_gid):
466     """setup reasonable name mappings for sam names to unix names.
467
468     :param samdb: SamDB object.
469     :param idmap: IDmap db object.
470     :param sid: The domain sid.
471     :param domaindn: The domain DN.
472     :param root_uid: uid of the UNIX root user.
473     :param nobody_uid: uid of the UNIX nobody user.
474     :param users_gid: gid of the UNIX users group.
475     :param wheel_gid: gid of the UNIX wheel group."""
476
477 def add_foreign(self, domaindn, sid, desc):
478     """Add a foreign security principle."""
479     add = """
480 dn: CN=%s,CN=ForeignSecurityPrincipals,%s
481 objectClass: top
482 objectClass: foreignSecurityPrincipal
483 description: %s
484 """ % (sid, domaindn, desc)
485     # deliberately ignore errors from this, as the records may
486     # already exist
487     for msg in self.parse_ldif(add):
488         self.add(msg[1])
489
490 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info, 
491                            credentials, names,
492                            serverrole, ldap_backend=None, 
493                            erase=False):
494     """Setup the partitions for the SAM database. 
495     
496     Alternatively, provision() may call this, and then populate the database.
497     
498     :note: This will wipe the Sam Database!
499     
500     :note: This function always removes the local SAM LDB file. The erase 
501         parameter controls whether to erase the existing data, which 
502         may not be stored locally but in LDAP.
503     """
504     assert session_info is not None
505
506     # We use options=["modules:"] to stop the modules loading - we
507     # just want to wipe and re-initialise the database, not start it up
508
509     try:
510         samdb = Ldb(url=samdb_path, session_info=session_info, 
511                       credentials=credentials, lp=lp, options=["modules:"])
512         # Wipes the database
513         samdb.erase_except_schema_controlled()
514     except LdbError:
515         os.unlink(samdb_path)
516         samdb = Ldb(url=samdb_path, session_info=session_info, 
517                       credentials=credentials, lp=lp, options=["modules:"])
518          # Wipes the database
519         samdb.erase_except_schema_controlled()
520         
521
522     #Add modules to the list to activate them by default
523     #beware often order is important
524     #
525     # Some Known ordering constraints:
526     # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
527     # - objectclass must be before password_hash, because password_hash checks
528     #   that the objectclass is of type person (filled in by objectclass
529     #   module when expanding the objectclass list)
530     # - partition must be last
531     # - each partition has its own module list then
532     modules_list = ["rootdse",
533                     "paged_results",
534                     "ranged_results",
535                     "anr",
536                     "server_sort",
537                     "asq",
538                     "extended_dn_store",
539                     "extended_dn_in",
540                     "rdn_name",
541                     "objectclass",
542                     "samldb",
543                     "kludge_acl",
544                     "password_hash",
545                     "operational"]
546     tdb_modules_list = [
547                     "subtree_rename",
548                     "subtree_delete",
549                     "linked_attributes",
550                     "extended_dn_out_ldb"]
551     modules_list2 = ["show_deleted",
552                     "partition"]
553  
554     domaindn_ldb = "users.ldb"
555     configdn_ldb = "configuration.ldb"
556     schemadn_ldb = "schema.ldb"
557     if ldap_backend is not None:
558         domaindn_ldb = ldap_backend.ldapi_uri
559         configdn_ldb = ldap_backend.ldapi_uri
560         schemadn_ldb = ldap_backend.ldapi_uri
561         
562         if ldap_backend.ldap_backend_type == "fedora-ds":
563             backend_modules = ["nsuniqueid", "paged_searches"]
564             # We can handle linked attributes here, as we don't have directory-side subtree operations
565             tdb_modules_list = ["linked_attributes", "extended_dn_out_dereference"]
566         elif ldap_backend.ldap_backend_type == "openldap":
567             backend_modules = ["entryuuid", "paged_searches"]
568             # OpenLDAP handles subtree renames, so we don't want to do any of these things
569             tdb_modules_list = ["extended_dn_out_dereference"]
570
571     elif serverrole == "domain controller":
572         backend_modules = ["repl_meta_data"]
573     else:
574         backend_modules = ["objectguid"]
575
576     if tdb_modules_list is None:
577         tdb_modules_list_as_string = ""
578     else:
579         tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
580         
581     samdb.transaction_start()
582     try:
583         message("Setting up sam.ldb partitions and settings")
584         setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
585                 "SCHEMADN": names.schemadn, 
586                 "SCHEMADN_LDB": schemadn_ldb,
587                 "SCHEMADN_MOD2": ",objectguid",
588                 "CONFIGDN": names.configdn,
589                 "CONFIGDN_LDB": configdn_ldb,
590                 "DOMAINDN": names.domaindn,
591                 "DOMAINDN_LDB": domaindn_ldb,
592                 "SCHEMADN_MOD": "schema_fsmo,instancetype",
593                 "CONFIGDN_MOD": "naming_fsmo,instancetype",
594                 "DOMAINDN_MOD": "pdc_fsmo,instancetype",
595                 "MODULES_LIST": ",".join(modules_list),
596                 "TDB_MODULES_LIST": tdb_modules_list_as_string,
597                 "MODULES_LIST2": ",".join(modules_list2),
598                 "BACKEND_MOD": ",".join(backend_modules),
599         })
600
601         samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
602
603         message("Setting up sam.ldb rootDSE")
604         setup_samdb_rootdse(samdb, setup_path, names)
605
606     except:
607         samdb.transaction_cancel()
608         raise
609
610     samdb.transaction_commit()
611     
612
613
614 def secretsdb_become_dc(secretsdb, setup_path, domain, realm, dnsdomain, 
615                         netbiosname, domainsid, keytab_path, samdb_url, 
616                         dns_keytab_path, dnspass, machinepass):
617     """Add DC-specific bits to a secrets database.
618     
619     :param secretsdb: Ldb Handle to the secrets database
620     :param setup_path: Setup path function
621     :param machinepass: Machine password
622     """
623     setup_ldb(secretsdb, setup_path("secrets_dc.ldif"), { 
624             "MACHINEPASS_B64": b64encode(machinepass),
625             "DOMAIN": domain,
626             "REALM": realm,
627             "DNSDOMAIN": dnsdomain,
628             "DOMAINSID": str(domainsid),
629             "SECRETS_KEYTAB": keytab_path,
630             "NETBIOSNAME": netbiosname,
631             "SAM_LDB": samdb_url,
632             "DNS_KEYTAB": dns_keytab_path,
633             "DNSPASS_B64": b64encode(dnspass),
634             })
635
636
637 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
638     """Setup the secrets database.
639
640     :param path: Path to the secrets database.
641     :param setup_path: Get the path to a setup file.
642     :param session_info: Session info.
643     :param credentials: Credentials
644     :param lp: Loadparm context
645     :return: LDB handle for the created secrets database
646     """
647     if os.path.exists(path):
648         os.unlink(path)
649     secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
650                       lp=lp)
651     secrets_ldb.erase()
652     secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
653     secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
654                       lp=lp)
655     secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
656
657     if credentials is not None and credentials.authentication_requested():
658         if credentials.get_bind_dn() is not None:
659             setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
660                     "LDAPMANAGERDN": credentials.get_bind_dn(),
661                     "LDAPMANAGERPASS_B64": b64encode(credentials.get_password())
662                     })
663         else:
664             setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
665                     "LDAPADMINUSER": credentials.get_username(),
666                     "LDAPADMINREALM": credentials.get_realm(),
667                     "LDAPADMINPASS_B64": b64encode(credentials.get_password())
668                     })
669
670     return secrets_ldb
671
672
673 def setup_templatesdb(path, setup_path, session_info, lp):
674     """Setup the templates database.
675
676     :param path: Path to the database.
677     :param setup_path: Function for obtaining the path to setup files.
678     :param session_info: Session info
679     :param credentials: Credentials
680     :param lp: Loadparm context
681     """
682     templates_ldb = Ldb(url=path, session_info=session_info,
683                         lp=lp)
684     # Wipes the database
685     try:
686         templates_ldb.erase()
687     # This should be 'except LdbError', but on a re-provision the assert in ldb.erase fires, and we need to catch that too
688     except:
689         os.unlink(path)
690
691     templates_ldb.load_ldif_file_add(setup_path("provision_templates_init.ldif"))
692
693     templates_ldb = Ldb(url=path, session_info=session_info,
694                         lp=lp)
695
696     templates_ldb.load_ldif_file_add(setup_path("provision_templates.ldif"))
697
698
699 def setup_registry(path, setup_path, session_info, lp):
700     """Setup the registry.
701     
702     :param path: Path to the registry database
703     :param setup_path: Function that returns the path to a setup.
704     :param session_info: Session information
705     :param credentials: Credentials
706     :param lp: Loadparm context
707     """
708     reg = registry.Registry()
709     hive = registry.open_ldb(path, session_info=session_info, 
710                          lp_ctx=lp)
711     reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
712     provision_reg = setup_path("provision.reg")
713     assert os.path.exists(provision_reg)
714     reg.diff_apply(provision_reg)
715
716
717 def setup_idmapdb(path, setup_path, session_info, lp):
718     """Setup the idmap database.
719
720     :param path: path to the idmap database
721     :param setup_path: Function that returns a path to a setup file
722     :param session_info: Session information
723     :param credentials: Credentials
724     :param lp: Loadparm context
725     """
726     if os.path.exists(path):
727         os.unlink(path)
728
729     idmap_ldb = IDmapDB(path, session_info=session_info,
730                         lp=lp)
731
732     idmap_ldb.erase()
733     idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
734     return idmap_ldb
735
736
737 def setup_samdb_rootdse(samdb, setup_path, names):
738     """Setup the SamDB rootdse.
739
740     :param samdb: Sam Database handle
741     :param setup_path: Obtain setup path
742     """
743     setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
744         "SCHEMADN": names.schemadn, 
745         "NETBIOSNAME": names.netbiosname,
746         "DNSDOMAIN": names.dnsdomain,
747         "REALM": names.realm,
748         "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
749         "DOMAINDN": names.domaindn,
750         "ROOTDN": names.rootdn,
751         "CONFIGDN": names.configdn,
752         "SERVERDN": names.serverdn,
753         })
754         
755
756 def setup_self_join(samdb, names,
757                     machinepass, dnspass, 
758                     domainsid, invocationid, setup_path,
759                     policyguid, domainControllerFunctionality):
760     """Join a host to its own domain."""
761     assert isinstance(invocationid, str)
762     setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), { 
763               "CONFIGDN": names.configdn, 
764               "SCHEMADN": names.schemadn,
765               "DOMAINDN": names.domaindn,
766               "SERVERDN": names.serverdn,
767               "INVOCATIONID": invocationid,
768               "NETBIOSNAME": names.netbiosname,
769               "DEFAULTSITE": names.sitename,
770               "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
771               "MACHINEPASS_B64": b64encode(machinepass),
772               "DNSPASS_B64": b64encode(dnspass),
773               "REALM": names.realm,
774               "DOMAIN": names.domain,
775               "DNSDOMAIN": names.dnsdomain,
776               "SAMBA_VERSION_STRING": version,
777               "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
778     setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), { 
779               "POLICYGUID": policyguid,
780               "DNSDOMAIN": names.dnsdomain,
781               "DOMAINSID": str(domainsid),
782               "DOMAINDN": names.domaindn})
783
784
785 def setup_samdb(path, setup_path, session_info, credentials, lp, 
786                 names, message, 
787                 domainsid, domainguid, policyguid, 
788                 fill, adminpass, krbtgtpass, 
789                 machinepass, invocationid, dnspass,
790                 serverrole, schema=None, ldap_backend=None):
791     """Setup a complete SAM Database.
792     
793     :note: This will wipe the main SAM database file!
794     """
795
796     domainFunctionality = DS_BEHAVIOR_WIN2008
797     forestFunctionality = DS_BEHAVIOR_WIN2008
798     domainControllerFunctionality = DS_BEHAVIOR_WIN2008
799
800     # Also wipes the database
801     setup_samdb_partitions(path, setup_path, message=message, lp=lp,
802                            credentials=credentials, session_info=session_info,
803                            names=names, 
804                            ldap_backend=ldap_backend, serverrole=serverrole)
805
806     if (schema == None):
807         schema = Schema(setup_path, schemadn=names.schemadn, serverdn=names.serverdn)
808
809     # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
810     samdb = Ldb(session_info=session_info, 
811                 credentials=credentials, lp=lp)
812
813     message("Pre-loading the Samba 4 and AD schema")
814
815     # Load the schema from the one we computed earlier
816     samdb.set_schema_from_ldb(schema.ldb)
817
818     # And now we can connect to the DB - the schema won't be loaded from the DB
819     samdb.connect(path)
820     if fill == FILL_DRS:
821         return samdb
822
823     samdb.transaction_start()
824     try:
825         message("Erasing data from partitions")
826         # Load the schema (again).  This time it will force a reindex,
827         # and will therefore make the erase_partitions() below
828         # computationally sane
829         samdb.set_schema_from_ldb(schema.ldb)
830         samdb.erase_partitions()
831     
832         # Set the domain functionality levels onto the database.
833         # Various module (the password_hash module in particular) need
834         # to know what level of AD we are emulating.
835
836         # These will be fixed into the database via the database
837         # modifictions below, but we need them set from the start.
838         samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
839         samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
840         samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
841
842         samdb.set_domain_sid(str(domainsid))
843         if serverrole == "domain controller":
844             samdb.set_invocation_id(invocationid)
845
846         message("Adding DomainDN: %s" % names.domaindn)
847         if serverrole == "domain controller":
848             domain_oc = "domainDNS"
849         else:
850             domain_oc = "samba4LocalDomain"
851
852         setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
853                 "DOMAINDN": names.domaindn,
854                 "DOMAIN_OC": domain_oc
855                 })
856
857         message("Modifying DomainDN: " + names.domaindn + "")
858         if domainguid is not None:
859             domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
860         else:
861             domainguid_mod = ""
862
863         setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
864             "LDAPTIME": timestring(int(time.time())),
865             "DOMAINSID": str(domainsid),
866             "SCHEMADN": names.schemadn, 
867             "NETBIOSNAME": names.netbiosname,
868             "DEFAULTSITE": names.sitename,
869             "CONFIGDN": names.configdn,
870             "SERVERDN": names.serverdn,
871             "POLICYGUID": policyguid,
872             "DOMAINDN": names.domaindn,
873             "DOMAINGUID_MOD": domainguid_mod,
874             "DOMAIN_FUNCTIONALITY": str(domainFunctionality)
875             })
876
877         message("Adding configuration container")
878         setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
879             "CONFIGDN": names.configdn, 
880             })
881         message("Modifying configuration container")
882         setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
883             "CONFIGDN": names.configdn, 
884             "SCHEMADN": names.schemadn,
885             })
886
887         # The LDIF here was created when the Schema object was constructed
888         message("Setting up sam.ldb schema")
889         samdb.add_ldif(schema.schema_dn_add)
890         samdb.modify_ldif(schema.schema_dn_modify)
891         samdb.add_ldif(schema.schema_data)
892         setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"), 
893                        {"SCHEMADN": names.schemadn})
894
895         message("Setting up sam.ldb configuration data")
896         setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
897             "CONFIGDN": names.configdn,
898             "NETBIOSNAME": names.netbiosname,
899             "DEFAULTSITE": names.sitename,
900             "DNSDOMAIN": names.dnsdomain,
901             "DOMAIN": names.domain,
902             "SCHEMADN": names.schemadn,
903             "DOMAINDN": names.domaindn,
904             "SERVERDN": names.serverdn,
905             "FOREST_FUNCTIONALALITY": str(forestFunctionality)
906             })
907
908         message("Setting up display specifiers")
909         setup_add_ldif(samdb, setup_path("display_specifiers.ldif"), 
910                        {"CONFIGDN": names.configdn})
911
912         message("Adding users container")
913         setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
914                 "DOMAINDN": names.domaindn})
915         message("Modifying users container")
916         setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
917                 "DOMAINDN": names.domaindn})
918         message("Adding computers container")
919         setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
920                 "DOMAINDN": names.domaindn})
921         message("Modifying computers container")
922         setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
923                 "DOMAINDN": names.domaindn})
924         message("Setting up sam.ldb data")
925         setup_add_ldif(samdb, setup_path("provision.ldif"), {
926             "DOMAINDN": names.domaindn,
927             "NETBIOSNAME": names.netbiosname,
928             "DEFAULTSITE": names.sitename,
929             "CONFIGDN": names.configdn,
930             "SERVERDN": names.serverdn
931             })
932
933         if fill == FILL_FULL:
934             message("Setting up sam.ldb users and groups")
935             setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
936                 "DOMAINDN": names.domaindn,
937                 "DOMAINSID": str(domainsid),
938                 "CONFIGDN": names.configdn,
939                 "ADMINPASS_B64": b64encode(adminpass),
940                 "KRBTGTPASS_B64": b64encode(krbtgtpass),
941                 })
942
943             if serverrole == "domain controller":
944                 message("Setting up self join")
945                 setup_self_join(samdb, names=names, invocationid=invocationid, 
946                                 dnspass=dnspass,  
947                                 machinepass=machinepass, 
948                                 domainsid=domainsid, policyguid=policyguid,
949                                 setup_path=setup_path, domainControllerFunctionality=domainControllerFunctionality)
950
951     except:
952         samdb.transaction_cancel()
953         raise
954
955     samdb.transaction_commit()
956     return samdb
957
958
959 FILL_FULL = "FULL"
960 FILL_NT4SYNC = "NT4SYNC"
961 FILL_DRS = "DRS"
962
963
964 def provision(setup_dir, message, session_info, 
965               credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL, realm=None, 
966               rootdn=None, domaindn=None, schemadn=None, configdn=None, 
967               serverdn=None,
968               domain=None, hostname=None, hostip=None, hostip6=None, 
969               domainsid=None, adminpass=None, ldapadminpass=None, 
970               krbtgtpass=None, domainguid=None, 
971               policyguid=None, invocationid=None, machinepass=None, 
972               dnspass=None, root=None, nobody=None, users=None, 
973               wheel=None, backup=None, aci=None, serverrole=None, 
974               ldap_backend_extra_port=None, ldap_backend_type=None, sitename=None,
975               ol_mmr_urls=None, ol_olc=None, 
976               setup_ds_path=None, slapd_path=None, nosync=False,
977               ldap_dryrun_mode=False):
978     """Provision samba4
979     
980     :note: caution, this wipes all existing data!
981     """
982
983     def setup_path(file):
984         return os.path.join(setup_dir, file)
985
986     if domainsid is None:
987         domainsid = security.random_sid()
988
989     if policyguid is None:
990         policyguid = str(uuid.uuid4())
991     if adminpass is None:
992         adminpass = glue.generate_random_str(12)
993     if krbtgtpass is None:
994         krbtgtpass = glue.generate_random_str(12)
995     if machinepass is None:
996         machinepass  = glue.generate_random_str(12)
997     if dnspass is None:
998         dnspass = glue.generate_random_str(12)
999     if ldapadminpass is None:
1000         #Make a new, random password between Samba and it's LDAP server
1001         ldapadminpass=glue.generate_random_str(12)        
1002
1003
1004     root_uid = findnss_uid([root or "root"])
1005     nobody_uid = findnss_uid([nobody or "nobody"])
1006     users_gid = findnss_gid([users or "users"])
1007     if wheel is None:
1008         wheel_gid = findnss_gid(["wheel", "adm"])
1009     else:
1010         wheel_gid = findnss_gid([wheel])
1011
1012     if targetdir is not None:
1013         if (not os.path.exists(os.path.join(targetdir, "etc"))):
1014             os.makedirs(os.path.join(targetdir, "etc"))
1015         smbconf = os.path.join(targetdir, "etc", "smb.conf")
1016     elif smbconf is None:
1017         smbconf = param.default_path()
1018
1019     # only install a new smb.conf if there isn't one there already
1020     if not os.path.exists(smbconf):
1021         make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, 
1022                      targetdir)
1023
1024     lp = param.LoadParm()
1025     lp.load(smbconf)
1026
1027     names = guess_names(lp=lp, hostname=hostname, domain=domain, 
1028                         dnsdomain=realm, serverrole=serverrole, sitename=sitename,
1029                         rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1030                         serverdn=serverdn)
1031
1032     paths = provision_paths_from_lp(lp, names.dnsdomain)
1033
1034     if hostip is None:
1035         try:
1036             hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1037         except socket.gaierror, (socket.EAI_NODATA, msg):
1038             hostip = None
1039
1040     if hostip6 is None:
1041         try:
1042             hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1043         except socket.gaierror, (socket.EAI_NODATA, msg): 
1044             hostip6 = None
1045
1046     if serverrole is None:
1047         serverrole = lp.get("server role")
1048
1049     assert serverrole in ("domain controller", "member server", "standalone")
1050     if invocationid is None and serverrole == "domain controller":
1051         invocationid = str(uuid.uuid4())
1052
1053     if not os.path.exists(paths.private_dir):
1054         os.mkdir(paths.private_dir)
1055
1056     ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1057     
1058     schema = Schema(setup_path, schemadn=names.schemadn, serverdn=names.serverdn)
1059     
1060     provision_backend = None
1061     if ldap_backend_type:
1062         # We only support an LDAP backend over ldapi://
1063
1064         provision_backend = ProvisionBackend(paths=paths, setup_path=setup_path, lp=lp, credentials=credentials, 
1065                                              names=names,
1066                                              message=message, hostname=hostname, 
1067                                              root=root, schema=schema, ldap_backend_type=ldap_backend_type,
1068                                              ldapadminpass=ldapadminpass,
1069                                              ldap_backend_extra_port=ldap_backend_extra_port,
1070                                              ol_mmr_urls=ol_mmr_urls, 
1071                                              slapd_path=slapd_path,
1072                                              setup_ds_path=setup_ds_path,
1073                                              ldap_dryrun_mode=ldap_dryrun_mode)
1074
1075         # Now use the backend credentials to access the databases
1076         credentials = provision_backend.credentials
1077
1078     # only install a new shares config db if there is none
1079     if not os.path.exists(paths.shareconf):
1080         message("Setting up share.ldb")
1081         share_ldb = Ldb(paths.shareconf, session_info=session_info, 
1082                         credentials=credentials, lp=lp)
1083         share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1084
1085      
1086     message("Setting up secrets.ldb")
1087     secrets_ldb = setup_secretsdb(paths.secrets, setup_path, 
1088                                   session_info=session_info, 
1089                                   credentials=credentials, lp=lp)
1090
1091     message("Setting up the registry")
1092     setup_registry(paths.hklm, setup_path, session_info, 
1093                    lp=lp)
1094
1095     message("Setting up templates db")
1096     setup_templatesdb(paths.templates, setup_path, session_info=session_info, 
1097                       lp=lp)
1098
1099     message("Setting up idmap db")
1100     idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1101                           lp=lp)
1102
1103     message("Setting up SAM db")
1104     samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info, 
1105                         credentials=credentials, lp=lp, names=names,
1106                         message=message, 
1107                         domainsid=domainsid, 
1108                         schema=schema, domainguid=domainguid, policyguid=policyguid, 
1109                         fill=samdb_fill, 
1110                         adminpass=adminpass, krbtgtpass=krbtgtpass,
1111                         invocationid=invocationid, 
1112                         machinepass=machinepass, dnspass=dnspass,
1113                         serverrole=serverrole, ldap_backend=provision_backend)
1114
1115     if serverrole == "domain controller":
1116         if paths.netlogon is None:
1117             message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1118             message("Please either remove %s or see the template at %s" % 
1119                     ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1120             assert(paths.netlogon is not None)
1121
1122         if paths.sysvol is None:
1123             message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1124             message("Please either remove %s or see the template at %s" % 
1125                     (paths.smbconf, setup_path("provision.smb.conf.dc")))
1126             assert(paths.sysvol is not None)            
1127             
1128         policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies", 
1129                                    "{" + policyguid + "}")
1130         os.makedirs(policy_path, 0755)
1131         open(os.path.join(policy_path, "GPT.INI"), 'w').write("")
1132         os.makedirs(os.path.join(policy_path, "Machine"), 0755)
1133         os.makedirs(os.path.join(policy_path, "User"), 0755)
1134         if not os.path.isdir(paths.netlogon):
1135             os.makedirs(paths.netlogon, 0755)
1136
1137     if samdb_fill == FILL_FULL:
1138         setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1139                             root_uid=root_uid, nobody_uid=nobody_uid,
1140                             users_gid=users_gid, wheel_gid=wheel_gid)
1141
1142         message("Setting up sam.ldb rootDSE marking as synchronized")
1143         setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1144
1145         # Only make a zone file on the first DC, it should be replicated with DNS replication
1146         if serverrole == "domain controller":
1147             secrets_ldb = Ldb(paths.secrets, session_info=session_info, 
1148                               credentials=credentials, lp=lp)
1149             secretsdb_become_dc(secrets_ldb, setup_path, domain=domain, realm=names.realm,
1150                                 netbiosname=names.netbiosname, domainsid=domainsid, 
1151                                 keytab_path=paths.keytab, samdb_url=paths.samdb, 
1152                                 dns_keytab_path=paths.dns_keytab, dnspass=dnspass, 
1153                                 machinepass=machinepass, dnsdomain=names.dnsdomain)
1154
1155             domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1156             assert isinstance(domainguid, str)
1157             hostguid = samdb.searchone(basedn=domaindn, attribute="objectGUID",
1158                                        expression="(&(objectClass=computer)(cn=%s))" % names.hostname,
1159                                        scope=SCOPE_SUBTREE)
1160             assert isinstance(hostguid, str)
1161
1162             create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1163                              domaindn=names.domaindn, hostip=hostip,
1164                              hostip6=hostip6, hostname=names.hostname,
1165                              dnspass=dnspass, realm=names.realm,
1166                              domainguid=domainguid, hostguid=hostguid)
1167
1168             create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1169                               dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1170
1171             create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1172                               dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1173                               keytab_name=paths.dns_keytab)
1174             message("See %s for an example configuration include file for BIND" % paths.namedconf)
1175             message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1176
1177             create_krb5_conf(paths.krb5conf, setup_path, dnsdomain=names.dnsdomain,
1178                              hostname=names.hostname, realm=names.realm)
1179             message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1180
1181
1182     # if backend is openldap, terminate slapd after final provision and check its proper termination
1183     if provision_backend is not None and provision_backend.slapd is not None:
1184         if provision_backend.slapd.poll() is None:
1185             #Kill the slapd
1186             if hasattr(provision_backend.slapd, "terminate"):
1187                 provision_backend.slapd.terminate()
1188             else:
1189                 import signal
1190                 os.kill(provision_backend.slapd.pid, signal.SIGTERM)
1191             
1192             #and now wait for it to die
1193             provision_backend.slapd.communicate()
1194             
1195     # now display slapd_command_file.txt to show how slapd must be started next time
1196         message("Use later the following commandline to start slapd, then Samba:")
1197         slapd_command = "\'" + "\' \'".join(provision_backend.slapd_command) + "\'"
1198         message(slapd_command)
1199         message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh")
1200
1201         setup_file(setup_path("ldap_backend_startup.sh"), paths.ldapdir + "/ldap_backend_startup.sh", {
1202                 "SLAPD_COMMAND" : slapd_command})
1203
1204     
1205     create_phpldapadmin_config(paths.phpldapadminconfig, setup_path, 
1206                                ldapi_url)
1207
1208     message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1209
1210     message("Once the above files are installed, your Samba4 server will be ready to use")
1211     message("Server Role:           %s" % serverrole)
1212     message("Hostname:              %s" % names.hostname)
1213     message("NetBIOS Domain:        %s" % names.domain)
1214     message("DNS Domain:            %s" % names.dnsdomain)
1215     message("DOMAIN SID:            %s" % str(domainsid))
1216     if samdb_fill == FILL_FULL:
1217         message("Admin password:    %s" % adminpass)
1218     if provision_backend:
1219         if provision_backend.credentials.get_bind_dn() is not None:
1220             message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1221         else:
1222             message("LDAP Admin User:       %s" % provision_backend.credentials.get_username())
1223
1224         message("LDAP Admin Password:   %s" % provision_backend.credentials.get_password())
1225   
1226     result = ProvisionResult()
1227     result.domaindn = domaindn
1228     result.paths = paths
1229     result.lp = lp
1230     result.samdb = samdb
1231     return result
1232
1233
1234
1235 def provision_become_dc(setup_dir=None,
1236                         smbconf=None, targetdir=None, realm=None, 
1237                         rootdn=None, domaindn=None, schemadn=None, configdn=None,
1238                         serverdn=None,
1239                         domain=None, hostname=None, domainsid=None, 
1240                         adminpass=None, krbtgtpass=None, domainguid=None, 
1241                         policyguid=None, invocationid=None, machinepass=None, 
1242                         dnspass=None, root=None, nobody=None, users=None, 
1243                         wheel=None, backup=None, serverrole=None, 
1244                         ldap_backend=None, ldap_backend_type=None, sitename=None):
1245
1246     def message(text):
1247         """print a message if quiet is not set."""
1248         print text
1249
1250     return provision(setup_dir, message, system_session(), None,
1251               smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS, realm=realm, 
1252               rootdn=rootdn, domaindn=domaindn, schemadn=schemadn, configdn=configdn, serverdn=serverdn,
1253               domain=domain, hostname=hostname, hostip="127.0.0.1", domainsid=domainsid, machinepass=machinepass, serverrole="domain controller", sitename=sitename)
1254     
1255
1256 def setup_db_config(setup_path, dbdir):
1257     """Setup a Berkeley database.
1258     
1259     :param setup_path: Setup path function.
1260     :param dbdir: Database directory."""
1261     if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
1262         os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700)
1263         if not os.path.isdir(os.path.join(dbdir, "tmp")):
1264             os.makedirs(os.path.join(dbdir, "tmp"), 0700)
1265
1266     setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
1267                {"LDAPDBDIR": dbdir})
1268     
1269 class ProvisionBackend(object):
1270     def __init__(self, paths=None, setup_path=None, lp=None, credentials=None, 
1271                  names=None, message=None, 
1272                  hostname=None, root=None, 
1273                  schema=None, ldapadminpass=None,
1274                  ldap_backend_type=None, ldap_backend_extra_port=None,
1275                  ol_mmr_urls=None, 
1276                  setup_ds_path=None, slapd_path=None, 
1277                  nosync=False, ldap_dryrun_mode=False):
1278         """Provision an LDAP backend for samba4
1279         
1280         This works for OpenLDAP and Fedora DS
1281         """
1282
1283         self.ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.ldapdir, "ldapi"), safe="")
1284         
1285         if not os.path.isdir(paths.ldapdir):
1286             os.makedirs(paths.ldapdir, 0700)
1287             
1288         if ldap_backend_type == "existing":
1289             #Check to see that this 'existing' LDAP backend in fact exists
1290             ldapi_db = Ldb(self.ldapi_uri, credentials=credentials)
1291             search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1292                                                 expression="(objectClass=OpenLDAProotDSE)")
1293
1294             # If we have got here, then we must have a valid connection to the LDAP server, with valid credentials supplied
1295             # This caused them to be set into the long-term database later in the script.
1296             self.credentials = credentials
1297             self.ldap_backend_type = "openldap" #For now, assume existing backends at least emulate OpenLDAP
1298             return
1299     
1300         # we will shortly start slapd with ldapi for final provisioning. first check with ldapsearch -> rootDSE via self.ldapi_uri
1301         # if another instance of slapd is already running 
1302         try:
1303             ldapi_db = Ldb(self.ldapi_uri)
1304             search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1305                                                 expression="(objectClass=OpenLDAProotDSE)");
1306             try:
1307                 f = open(paths.slapdpid, "r")
1308                 p = f.read()
1309                 f.close()
1310                 message("Check for slapd Process with PID: " + str(p) + " and terminate it manually.")
1311             except:
1312                 pass
1313             
1314             raise("Warning: Another slapd Instance seems already running on this host, listening to " + self.ldapi_uri + ". Please shut it down before you continue. ")
1315         
1316         except LdbError, e:
1317             pass
1318
1319         # Try to print helpful messages when the user has not specified the path to slapd
1320         if slapd_path is None:
1321             raise("Warning: LDAP-Backend must be setup with path to slapd, e.g. --slapd-path=\"/usr/local/libexec/slapd\"!")
1322         if not os.path.exists(slapd_path):
1323             message (slapd_path)
1324             raise("Warning: Given Path to slapd does not exist!")
1325
1326         schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
1327         try:
1328             os.unlink(schemadb_path)
1329         except OSError:
1330             pass
1331
1332
1333         # Put the LDIF of the schema into a database so we can search on
1334         # it to generate schema-dependent configurations in Fedora DS and
1335         # OpenLDAP
1336         os.path.join(paths.ldapdir, "schema-tmp.ldb")
1337         schema.ldb.connect(schemadb_path)
1338         schema.ldb.transaction_start()
1339     
1340         # These bits of LDIF are supplied when the Schema object is created
1341         schema.ldb.add_ldif(schema.schema_dn_add)
1342         schema.ldb.modify_ldif(schema.schema_dn_modify)
1343         schema.ldb.add_ldif(schema.schema_data)
1344         schema.ldb.transaction_commit()
1345
1346         self.credentials = Credentials()
1347         self.credentials.guess(lp)
1348         #Kerberos to an ldapi:// backend makes no sense
1349         self.credentials.set_kerberos_state(DONT_USE_KERBEROS)
1350         self.ldap_backend_type = ldap_backend_type
1351
1352         if ldap_backend_type == "fedora-ds":
1353             provision_fds_backend(self, paths=paths, setup_path=setup_path, names=names, message=message, 
1354                                   hostname=hostname, ldapadminpass=ldapadminpass, root=root, 
1355                                   schema=schema, ldap_backend_extra_port=ldap_backend_extra_port, 
1356                                   setup_ds_path=setup_ds_path, slapd_path=slapd_path,
1357                                   nosync=nosync, ldap_dryrun_mode=ldap_dryrun_mode)
1358             
1359         elif ldap_backend_type == "openldap":
1360             provision_openldap_backend(self, paths=paths, setup_path=setup_path, names=names, message=message, 
1361                                        hostname=hostname, ldapadminpass=ldapadminpass, root=root, 
1362                                        schema=schema, ldap_backend_extra_port=ldap_backend_extra_port, 
1363                                        ol_mmr_urls=ol_mmr_urls, 
1364                                        slapd_path=slapd_path,
1365                                        nosync=nosync, ldap_dryrun_mode=ldap_dryrun_mode)
1366         else:
1367             raise("Unknown LDAP backend type selected")
1368
1369         self.credentials.set_password(ldapadminpass)
1370
1371         # Now start the slapd, so we can provision onto it.  We keep the
1372         # subprocess context around, to kill this off at the successful
1373         # end of the script
1374         self.slapd = subprocess.Popen(self.slapd_provision_command, close_fds=True, shell=False)
1375     
1376         while self.slapd.poll() is None:
1377             # Wait until the socket appears
1378             try:
1379                 ldapi_db = Ldb(self.ldapi_uri, lp=lp, credentials=self.credentials)
1380                 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1381                                                     expression="(objectClass=OpenLDAProotDSE)")
1382                 # If we have got here, then we must have a valid connection to the LDAP server!
1383                 return
1384             except LdbError, e:
1385                 time.sleep(1)
1386                 pass
1387         
1388         raise "slapd died before we could make a connection to it"
1389
1390
1391 def provision_openldap_backend(result, paths=None, setup_path=None, names=None, message=None, 
1392                                hostname=None, ldapadminpass=None, root=None, 
1393                                schema=None, 
1394                                ldap_backend_extra_port=None,
1395                                ol_mmr_urls=None, 
1396                                slapd_path=None, nosync=False,
1397                                ldap_dryrun_mode=False):
1398
1399     #Allow the test scripts to turn off fsync() for OpenLDAP as for TDB and LDB
1400     nosync_config = ""
1401     if nosync:
1402         nosync_config = "dbnosync"
1403         
1404         
1405     attrs = ["linkID", "lDAPDisplayName"]
1406     res = schema.ldb.search(expression="(&(linkID=*)(!(linkID:1.2.840.113556.1.4.803:=1))(objectclass=attributeSchema)(attributeSyntax=2.5.5.1))", base=names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
1407
1408     memberof_config = "# Generated from Samba4 schema\n"
1409     refint_attributes = ""
1410     for i in range (0, len(res)):
1411         expression = "(&(objectclass=attributeSchema)(linkID=%d)(attributeSyntax=2.5.5.1))" % (int(res[i]["linkID"][0])+1)
1412         target = schema.ldb.searchone(basedn=names.schemadn, 
1413                                       expression=expression, 
1414                                       attribute="lDAPDisplayName", 
1415                                       scope=SCOPE_SUBTREE)
1416         if target is not None:
1417             refint_attributes = refint_attributes + " " + res[i]["lDAPDisplayName"][0]
1418             
1419             memberof_config += read_and_sub_file(setup_path("memberof.conf"),
1420                                                  { "MEMBER_ATTR" : str(res[i]["lDAPDisplayName"][0]),
1421                                                    "MEMBEROF_ATTR" : str(target) })
1422             
1423     refint_config = read_and_sub_file(setup_path("refint.conf"),
1424                                       { "LINK_ATTRS" : refint_attributes})
1425     
1426     res = schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
1427     index_config = ""
1428     for i in range (0, len(res)):
1429         index_attr = res[i]["lDAPDisplayName"][0]
1430         if index_attr == "objectGUID":
1431             index_attr = "entryUUID"
1432             
1433         index_config += "index " + index_attr + " eq\n"
1434
1435 # generate serverids, ldap-urls and syncrepl-blocks for mmr hosts
1436     mmr_on_config = ""
1437     mmr_replicator_acl = ""
1438     mmr_serverids_config = ""
1439     mmr_syncrepl_schema_config = "" 
1440     mmr_syncrepl_config_config = "" 
1441     mmr_syncrepl_user_config = "" 
1442        
1443     
1444     if ol_mmr_urls is not None:
1445         # For now, make these equal
1446         mmr_pass = ldapadminpass
1447         
1448         url_list=filter(None,ol_mmr_urls.split(' ')) 
1449         if (len(url_list) == 1):
1450             url_list=filter(None,ol_mmr_urls.split(',')) 
1451                      
1452             
1453             mmr_on_config = "MirrorMode On"
1454             mmr_replicator_acl = "  by dn=cn=replicator,cn=samba read"
1455             serverid=0
1456             for url in url_list:
1457                 serverid=serverid+1
1458                 mmr_serverids_config += read_and_sub_file(setup_path("mmr_serverids.conf"),
1459                                                           { "SERVERID" : str(serverid),
1460                                                             "LDAPSERVER" : url })
1461                 rid=serverid*10
1462                 rid=rid+1
1463                 mmr_syncrepl_schema_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1464                                                                 {  "RID" : str(rid),
1465                                                                    "MMRDN": names.schemadn,
1466                                                                    "LDAPSERVER" : url,
1467                                                                    "MMR_PASSWORD": mmr_pass})
1468                 
1469                 rid=rid+1
1470                 mmr_syncrepl_config_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1471                                                                 {  "RID" : str(rid),
1472                                                                    "MMRDN": names.configdn,
1473                                                                    "LDAPSERVER" : url,
1474                                                                    "MMR_PASSWORD": mmr_pass})
1475                 
1476                 rid=rid+1
1477                 mmr_syncrepl_user_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1478                                                               {  "RID" : str(rid),
1479                                                                  "MMRDN": names.domaindn,
1480                                                                  "LDAPSERVER" : url,
1481                                                                  "MMR_PASSWORD": mmr_pass })
1482     # OpenLDAP cn=config initialisation
1483     olc_syncrepl_config = ""
1484     olc_mmr_config = "" 
1485     # if mmr = yes, generate cn=config-replication directives
1486     # and olc_seed.lif for the other mmr-servers
1487     if ol_mmr_urls is not None:
1488         serverid=0
1489         olc_serverids_config = ""
1490         olc_syncrepl_seed_config = ""
1491         olc_mmr_config += read_and_sub_file(setup_path("olc_mmr.conf"),{})
1492         rid=1000
1493         for url in url_list:
1494             serverid=serverid+1
1495             olc_serverids_config += read_and_sub_file(setup_path("olc_serverid.conf"),
1496                                                       { "SERVERID" : str(serverid),
1497                                                         "LDAPSERVER" : url })
1498             
1499             rid=rid+1
1500             olc_syncrepl_config += read_and_sub_file(setup_path("olc_syncrepl.conf"),
1501                                                      {  "RID" : str(rid),
1502                                                         "LDAPSERVER" : url,
1503                                                         "MMR_PASSWORD": mmr_pass})
1504             
1505             olc_syncrepl_seed_config += read_and_sub_file(setup_path("olc_syncrepl_seed.conf"),
1506                                                           {  "RID" : str(rid),
1507                                                              "LDAPSERVER" : url})
1508                 
1509         setup_file(setup_path("olc_seed.ldif"), paths.olcseedldif,
1510                    {"OLC_SERVER_ID_CONF": olc_serverids_config,
1511                     "OLC_PW": ldapadminpass,
1512                     "OLC_SYNCREPL_CONF": olc_syncrepl_seed_config})
1513     # end olc
1514                 
1515     setup_file(setup_path("slapd.conf"), paths.slapdconf,
1516                {"DNSDOMAIN": names.dnsdomain,
1517                 "LDAPDIR": paths.ldapdir,
1518                 "DOMAINDN": names.domaindn,
1519                 "CONFIGDN": names.configdn,
1520                 "SCHEMADN": names.schemadn,
1521                 "MEMBEROF_CONFIG": memberof_config,
1522                 "MIRRORMODE": mmr_on_config,
1523                 "REPLICATOR_ACL": mmr_replicator_acl,
1524                 "MMR_SERVERIDS_CONFIG": mmr_serverids_config,
1525                 "MMR_SYNCREPL_SCHEMA_CONFIG": mmr_syncrepl_schema_config,
1526                 "MMR_SYNCREPL_CONFIG_CONFIG": mmr_syncrepl_config_config,
1527                 "MMR_SYNCREPL_USER_CONFIG": mmr_syncrepl_user_config,
1528                 "OLC_SYNCREPL_CONFIG": olc_syncrepl_config,
1529                 "OLC_MMR_CONFIG": olc_mmr_config,
1530                 "REFINT_CONFIG": refint_config,
1531                 "INDEX_CONFIG": index_config,
1532                 "NOSYNC": nosync_config})
1533         
1534     setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "user"))
1535     setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "config"))
1536     setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "schema"))
1537     
1538     if not os.path.exists(os.path.join(paths.ldapdir, "db", "samba",  "cn=samba")):
1539         os.makedirs(os.path.join(paths.ldapdir, "db", "samba",  "cn=samba"), 0700)
1540         
1541     setup_file(setup_path("cn=samba.ldif"), 
1542                os.path.join(paths.ldapdir, "db", "samba",  "cn=samba.ldif"),
1543                { "UUID": str(uuid.uuid4()), 
1544                  "LDAPTIME": timestring(int(time.time()))} )
1545     setup_file(setup_path("cn=samba-admin.ldif"), 
1546                os.path.join(paths.ldapdir, "db", "samba",  "cn=samba", "cn=samba-admin.ldif"),
1547                {"LDAPADMINPASS_B64": b64encode(ldapadminpass),
1548                 "UUID": str(uuid.uuid4()), 
1549                 "LDAPTIME": timestring(int(time.time()))} )
1550     
1551     if ol_mmr_urls is not None:
1552         setup_file(setup_path("cn=replicator.ldif"),
1553                    os.path.join(paths.ldapdir, "db", "samba",  "cn=samba", "cn=replicator.ldif"),
1554                    {"MMR_PASSWORD_B64": b64encode(mmr_pass),
1555                     "UUID": str(uuid.uuid4()),
1556                     "LDAPTIME": timestring(int(time.time()))} )
1557         
1558
1559     mapping = "schema-map-openldap-2.3"
1560     backend_schema = "backend-schema.schema"
1561
1562     backend_schema_data = schema.ldb.convert_schema_to_openldap("openldap", open(setup_path(mapping), 'r').read())
1563     assert backend_schema_data is not None
1564     open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1565
1566     # now we generate the needed strings to start slapd automatically,
1567     # first ldapi_uri...
1568     if ldap_backend_extra_port is not None:
1569         # When we use MMR, we can't use 0.0.0.0 as it uses the name
1570         # specified there as part of it's clue as to it's own name,
1571         # and not to replicate to itself
1572         if ol_mmr_urls is None:
1573             server_port_string = "ldap://0.0.0.0:%d" % ldap_backend_extra_port
1574         else:
1575             server_port_string = "ldap://" + names.hostname + "." + names.dnsdomain +":%d" % ldap_backend_extra_port
1576     else:
1577         server_port_string = ""
1578
1579     # Prepare the 'result' information - the commands to return in particular
1580     result.slapd_provision_command = [slapd_path]
1581
1582     result.slapd_provision_command.append("-F" + paths.olcdir)
1583
1584     result.slapd_provision_command.append("-h")
1585
1586     # copy this command so we have two version, one with -d0 and only ldapi, and one with all the listen commands
1587     result.slapd_command = list(result.slapd_provision_command)
1588     
1589     result.slapd_provision_command.append(result.ldapi_uri)
1590     result.slapd_provision_command.append("-d0")
1591
1592     uris = result.ldapi_uri
1593     if server_port_string is not "":
1594         uris = uris + " " + server_port_string
1595
1596     result.slapd_command.append(uris)
1597
1598     # Set the username - done here because Fedora DS still uses the admin DN and simple bind
1599     result.credentials.set_username("samba-admin")
1600     
1601     # If we were just looking for crashes up to this point, it's a
1602     # good time to exit before we realise we don't have OpenLDAP on
1603     # this system
1604     if ldap_dryrun_mode:
1605         sys.exit(0)
1606
1607     # Finally, convert the configuration into cn=config style!
1608     if not os.path.isdir(paths.olcdir):
1609         os.makedirs(paths.olcdir, 0770)
1610
1611         retcode = subprocess.call([slapd_path, "-Ttest", "-f", paths.slapdconf, "-F", paths.olcdir], close_fds=True, shell=False)
1612
1613 #        We can't do this, as OpenLDAP is strange.  It gives an error
1614 #        output to the above, but does the conversion sucessfully...
1615 #
1616 #        if retcode != 0:
1617 #            raise("conversion from slapd.conf to cn=config failed")
1618
1619         if not os.path.exists(os.path.join(paths.olcdir, "cn=config.ldif")):
1620             raise("conversion from slapd.conf to cn=config failed")
1621
1622         # Don't confuse the admin by leaving the slapd.conf around
1623         os.remove(paths.slapdconf)        
1624           
1625
1626 def provision_fds_backend(result, paths=None, setup_path=None, names=None, message=None, 
1627                           hostname=None, ldapadminpass=None, root=None, 
1628                           schema=None,
1629                           ldap_backend_extra_port=None,
1630                           setup_ds_path=None,
1631                           slapd_path=None,
1632                           nosync=False, 
1633                           ldap_dryrun_mode=False):
1634
1635     if ldap_backend_extra_port is not None:
1636         serverport = "ServerPort=%d" % ldap_backend_extra_port
1637     else:
1638         serverport = ""
1639         
1640     setup_file(setup_path("fedorads.inf"), paths.fedoradsinf, 
1641                {"ROOT": root,
1642                 "HOSTNAME": hostname,
1643                 "DNSDOMAIN": names.dnsdomain,
1644                 "LDAPDIR": paths.ldapdir,
1645                 "DOMAINDN": names.domaindn,
1646                 "LDAPMANAGERDN": names.ldapmanagerdn,
1647                 "LDAPMANAGERPASS": ldapadminpass, 
1648                 "SERVERPORT": serverport})
1649
1650     setup_file(setup_path("fedorads-partitions.ldif"), paths.fedoradspartitions, 
1651                {"CONFIGDN": names.configdn,
1652                 "SCHEMADN": names.schemadn,
1653                 })
1654
1655     mapping = "schema-map-fedora-ds-1.0"
1656     backend_schema = "99_ad.ldif"
1657     
1658     # Build a schema file in Fedora DS format
1659     backend_schema_data = schema.ldb.convert_schema_to_openldap("fedora-ds", open(setup_path(mapping), 'r').read())
1660     assert backend_schema_data is not None
1661     open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1662
1663     result.credentials.set_bind_dn(names.ldapmanagerdn)
1664
1665     # Destory the target directory, or else setup-ds.pl will complain
1666     fedora_ds_dir = os.path.join(paths.ldapdir, "slapd-samba4")
1667     shutil.rmtree(fedora_ds_dir, True)
1668
1669     result.slapd_provision_command = [slapd_path, "-D", fedora_ds_dir, "-i", paths.slapdpid];
1670     #In the 'provision' command line, stay in the foreground so we can easily kill it
1671     result.slapd_provision_command.append("-d0")
1672
1673     #the command for the final run is the normal script
1674     result.slapd_command = [os.path.join(paths.ldapdir, "slapd-samba4", "start-slapd")]
1675
1676     # If we were just looking for crashes up to this point, it's a
1677     # good time to exit before we realise we don't have Fedora DS on
1678     if ldap_dryrun_mode:
1679         sys.exit(0)
1680
1681     # Try to print helpful messages when the user has not specified the path to the setup-ds tool
1682     if setup_ds_path is None:
1683         raise("Warning: Fedora DS LDAP-Backend must be setup with path to setup-ds, e.g. --setup-ds-path=\"/usr/sbin/setup-ds.pl\"!")
1684     if not os.path.exists(setup_ds_path):
1685         message (setup_ds_path)
1686         raise("Warning: Given Path to slapd does not exist!")
1687
1688     # Run the Fedora DS setup utility
1689     retcode = subprocess.call([setup_ds_path, "--silent", "--file", paths.fedoradsinf], close_fds=True, shell=False)
1690     if retcode != 0:
1691         raise("setup-ds failed")
1692
1693 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1694     """Create a PHP LDAP admin configuration file.
1695
1696     :param path: Path to write the configuration to.
1697     :param setup_path: Function to generate setup paths.
1698     """
1699     setup_file(setup_path("phpldapadmin-config.php"), path, 
1700             {"S4_LDAPI_URI": ldapi_uri})
1701
1702
1703 def create_zone_file(path, setup_path, dnsdomain, domaindn, 
1704                      hostip, hostip6, hostname, dnspass, realm, domainguid, hostguid):
1705     """Write out a DNS zone file, from the info in the current database.
1706
1707     :param path: Path of the new zone file.
1708     :param setup_path: Setup path function.
1709     :param dnsdomain: DNS Domain name
1710     :param domaindn: DN of the Domain
1711     :param hostip: Local IPv4 IP
1712     :param hostip6: Local IPv6 IP
1713     :param hostname: Local hostname
1714     :param dnspass: Password for DNS
1715     :param realm: Realm name
1716     :param domainguid: GUID of the domain.
1717     :param hostguid: GUID of the host.
1718     """
1719     assert isinstance(domainguid, str)
1720
1721     if hostip6 is not None:
1722         hostip6_base_line = "            IN AAAA    " + hostip6
1723         hostip6_host_line = hostname + "        IN AAAA    " + hostip6
1724     else:
1725         hostip6_base_line = ""
1726         hostip6_host_line = ""
1727
1728     if hostip is not None:
1729         hostip_base_line = "            IN A    " + hostip
1730         hostip_host_line = hostname + "        IN A    " + hostip
1731     else:
1732         hostip_base_line = ""
1733         hostip_host_line = ""
1734
1735     setup_file(setup_path("provision.zone"), path, {
1736             "DNSPASS_B64": b64encode(dnspass),
1737             "HOSTNAME": hostname,
1738             "DNSDOMAIN": dnsdomain,
1739             "REALM": realm,
1740             "HOSTIP_BASE_LINE": hostip_base_line,
1741             "HOSTIP_HOST_LINE": hostip_host_line,
1742             "DOMAINGUID": domainguid,
1743             "DATESTRING": time.strftime("%Y%m%d%H"),
1744             "DEFAULTSITE": DEFAULTSITE,
1745             "HOSTGUID": hostguid,
1746             "HOSTIP6_BASE_LINE": hostip6_base_line,
1747             "HOSTIP6_HOST_LINE": hostip6_host_line,
1748         })
1749
1750
1751 def create_named_conf(path, setup_path, realm, dnsdomain,
1752                       private_dir):
1753     """Write out a file containing zone statements suitable for inclusion in a
1754     named.conf file (including GSS-TSIG configuration).
1755     
1756     :param path: Path of the new named.conf file.
1757     :param setup_path: Setup path function.
1758     :param realm: Realm name
1759     :param dnsdomain: DNS Domain name
1760     :param private_dir: Path to private directory
1761     :param keytab_name: File name of DNS keytab file
1762     """
1763
1764     setup_file(setup_path("named.conf"), path, {
1765             "DNSDOMAIN": dnsdomain,
1766             "REALM": realm,
1767             "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1768             "PRIVATE_DIR": private_dir
1769             })
1770
1771 def create_named_txt(path, setup_path, realm, dnsdomain,
1772                       private_dir, keytab_name):
1773     """Write out a file containing zone statements suitable for inclusion in a
1774     named.conf file (including GSS-TSIG configuration).
1775     
1776     :param path: Path of the new named.conf file.
1777     :param setup_path: Setup path function.
1778     :param realm: Realm name
1779     :param dnsdomain: DNS Domain name
1780     :param private_dir: Path to private directory
1781     :param keytab_name: File name of DNS keytab file
1782     """
1783
1784     setup_file(setup_path("named.txt"), path, {
1785             "DNSDOMAIN": dnsdomain,
1786             "REALM": realm,
1787             "DNS_KEYTAB": keytab_name,
1788             "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1789             "PRIVATE_DIR": private_dir
1790         })
1791
1792 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1793     """Write out a file containing zone statements suitable for inclusion in a
1794     named.conf file (including GSS-TSIG configuration).
1795     
1796     :param path: Path of the new named.conf file.
1797     :param setup_path: Setup path function.
1798     :param dnsdomain: DNS Domain name
1799     :param hostname: Local hostname
1800     :param realm: Realm name
1801     """
1802
1803     setup_file(setup_path("krb5.conf"), path, {
1804             "DNSDOMAIN": dnsdomain,
1805             "HOSTNAME": hostname,
1806             "REALM": realm,
1807         })
1808
1809
1810
1811  
1812