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