More PEP8 compliancy.
[samba.git] / source / 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 #
8 # Based on the original in EJS:
9 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
10 #
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 3 of the License, or
14 # (at your option) any later version.
15 #   
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 # GNU General Public License for more details.
20 #   
21 # You should have received a copy of the GNU General Public License
22 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
23 #
24
25 from base64 import b64encode
26 import os
27 import pwd
28 import grp
29 import time
30 import uuid, misc
31 import socket
32 import param
33 import registry
34 import samba
35 from auth import system_session
36 from samba import Ldb, substitute_var, valid_netbios_name, check_all_substituted
37 from samba.samdb import SamDB
38 from samba.idmap import IDmapDB
39 import security
40 import urllib
41 from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError, \
42         LDB_ERR_NO_SUCH_OBJECT, timestring, CHANGETYPE_MODIFY, CHANGETYPE_NONE
43
44 """Functions for setting up a Samba configuration."""
45
46 DEFAULTSITE = "Default-First-Site-Name"
47
48 class InvalidNetbiosName(Exception):
49     def __init__(self, name):
50         super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)
51
52
53 class ProvisionPaths:
54     def __init__(self):
55         self.shareconf = None
56         self.hklm = None
57         self.hkcu = None
58         self.hkcr = None
59         self.hku = None
60         self.hkpd = None
61         self.hkpt = None
62         self.samdb = None
63         self.idmapdb = None
64         self.secrets = None
65         self.keytab = None
66         self.dns_keytab = None
67         self.dns = None
68         self.winsdb = None
69         self.private_dir = None
70         self.ldapdir = None
71         self.slapdconf = None
72         self.modulesconf = None
73         self.memberofconf = None
74         self.fedoradsinf = None
75         self.fedoradspartitions = None
76  
77 class ProvisionNames:
78     def __init__(self):
79         self.rootdn = None
80         self.domaindn = None
81         self.configdn = None
82         self.schemadn = None
83         self.ldapmanagerdn = None
84         self.dnsdomain = None
85         self.realm = None
86         self.netbiosname = None
87         self.domain = None
88         self.hostname = None
89         self.sitename = None
90     
91 class ProvisionResult:
92     def __init__(self):
93         self.paths = None
94         self.domaindn = None
95         self.lp = None
96         self.samdb = None
97
98 def check_install(lp, session_info, credentials):
99     """Check whether the current install seems ok.
100     
101     :param lp: Loadparm context
102     :param session_info: Session information
103     :param credentials: Credentials
104     """
105     if lp.get("realm") == "":
106         raise Exception("Realm empty")
107     ldb = Ldb(lp.get("sam database"), session_info=session_info, 
108             credentials=credentials, lp=lp)
109     if len(ldb.search("(cn=Administrator)")) != 1:
110         raise "No administrator account found"
111
112
113 def findnss(nssfn, names):
114     """Find a user or group from a list of possibilities.
115     
116     :param nssfn: NSS Function to try (should raise KeyError if not found)
117     :param names: Names to check.
118     :return: Value return by first names list.
119     """
120     for name in names:
121         try:
122             return nssfn(name)
123         except KeyError:
124             pass
125     raise KeyError("Unable to find user/group %r" % names)
126
127
128 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
129 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
130
131
132 def open_ldb(session_info, credentials, lp, dbname):
133     """Open a LDB, thrashing it if it is corrupt.
134
135     :param session_info: auth session information
136     :param credentials: credentials
137     :param lp: Loadparm context
138     :param dbname: Path of the database to open.
139     :return: a Ldb object
140     """
141     assert session_info is not None
142     try:
143         return Ldb(dbname, session_info=session_info, credentials=credentials, 
144                    lp=lp)
145     except LdbError, e:
146         print e
147         os.unlink(dbname)
148         return Ldb(dbname, session_info=session_info, credentials=credentials,
149                    lp=lp)
150
151
152 def setup_add_ldif(ldb, ldif_path, subst_vars=None):
153     """Setup a ldb in the private dir.
154     
155     :param ldb: LDB file to import data into
156     :param ldif_path: Path of the LDIF file to load
157     :param subst_vars: Optional variables to subsitute in LDIF.
158     """
159     assert isinstance(ldif_path, str)
160
161     data = open(ldif_path, 'r').read()
162     if subst_vars is not None:
163         data = substitute_var(data, subst_vars)
164
165     check_all_substituted(data)
166
167     ldb.add_ldif(data)
168
169
170 def setup_modify_ldif(ldb, ldif_path, substvars=None):
171     """Modify a ldb in the private dir.
172     
173     :param ldb: LDB object.
174     :param ldif_path: LDIF file path.
175     :param substvars: Optional dictionary with substitution variables.
176     """
177     data = open(ldif_path, 'r').read()
178     if substvars is not None:
179         data = substitute_var(data, substvars)
180
181     check_all_substituted(data)
182
183     ldb.modify_ldif(data)
184
185
186 def setup_ldb(ldb, ldif_path, subst_vars):
187     """Import a LDIF a file into a LDB handle, optionally substituting variables.
188
189     :note: Either all LDIF data will be added or none (using transactions).
190
191     :param ldb: LDB file to import into.
192     :param ldif_path: Path to the LDIF file.
193     :param subst_vars: Dictionary with substitution variables.
194     """
195     assert ldb is not None
196     ldb.transaction_start()
197     try:
198         setup_add_ldif(ldb, ldif_path, subst_vars)
199     except:
200         ldb.transaction_cancel()
201         raise
202     ldb.transaction_commit()
203
204
205 def setup_file(template, fname, substvars):
206     """Setup a file in the private dir.
207
208     :param template: Path of the template file.
209     :param fname: Path of the file to create.
210     :param substvars: Substitution variables.
211     """
212     f = fname
213
214     if os.path.exists(f):
215         os.unlink(f)
216
217     data = open(template, 'r').read()
218     if substvars:
219         data = substitute_var(data, substvars)
220     check_all_substituted(data)
221
222     open(f, 'w').write(data)
223
224
225 def provision_paths_from_lp(lp, dnsdomain):
226     """Set the default paths for provisioning.
227
228     :param lp: Loadparm context.
229     :param dnsdomain: DNS Domain name
230     """
231     paths = ProvisionPaths()
232     paths.private_dir = lp.get("private dir")
233     paths.keytab = "secrets.keytab"
234     paths.dns_keytab = "dns.keytab"
235
236     paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
237     paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
238     paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
239     paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
240     paths.templates = os.path.join(paths.private_dir, "templates.ldb")
241     paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
242     paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
243     paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
244     paths.phpldapadminconfig = os.path.join(paths.private_dir, 
245                                             "phpldapadmin-config.php")
246     paths.ldapdir = os.path.join(paths.private_dir, 
247                                  "ldap")
248     paths.slapdconf = os.path.join(paths.ldapdir, 
249                                    "slapd.conf")
250     paths.modulesconf = os.path.join(paths.ldapdir, 
251                                      "modules.conf")
252     paths.memberofconf = os.path.join(paths.ldapdir, 
253                                       "memberof.conf")
254     paths.fedoradsinf = os.path.join(paths.ldapdir, 
255                                    "fedorads.inf")
256     paths.fedoradspartitions = os.path.join(paths.ldapdir, 
257                                             "fedorads-partitions.ldif")
258     paths.hklm = "hklm.ldb"
259     paths.hkcr = "hkcr.ldb"
260     paths.hkcu = "hkcu.ldb"
261     paths.hku = "hku.ldb"
262     paths.hkpd = "hkpd.ldb"
263     paths.hkpt = "hkpt.ldb"
264
265     paths.sysvol = lp.get("path", "sysvol")
266
267     paths.netlogon = lp.get("path", "netlogon")
268
269     return paths
270
271
272 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None, 
273                 serverrole=None, rootdn=None, domaindn=None, configdn=None, 
274                 schemadn=None, sitename=None):
275
276     if hostname is None:
277         hostname = socket.gethostname().split(".")[0].lower()
278
279     netbiosname = hostname.upper()
280     if not valid_netbios_name(netbiosname):
281         raise InvalidNetbiosName(netbiosname)
282
283     hostname = hostname.lower()
284
285     if dnsdomain is None:
286         dnsdomain = lp.get("realm")
287
288     if serverrole is None:
289         serverrole = lp.get("server role")
290
291     assert dnsdomain is not None
292     realm = dnsdomain.upper()
293
294     if lp.get("realm").upper() != realm:
295         raise Exception("realm '%s' must match chosen realm '%s'" %
296                         (lp.get("realm"), realm))
297     
298     dnsdomain = dnsdomain.lower()
299
300     if serverrole == "domain controller":
301         if domain is None:
302             domain = lp.get("workgroup")
303         if domaindn is None:
304             domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
305         if lp.get("workgroup").upper() != domain.upper():
306             raise Exception("workgroup '%s' in smb.conf must match chosen domain '%s'",
307                         lp.get("workgroup"), domain)
308     else:
309         domain = netbiosname
310         if domaindn is None:
311             domaindn = "CN=" + netbiosname
312         
313     assert domain is not None
314     domain = domain.upper()
315     if not valid_netbios_name(domain):
316         raise InvalidNetbiosName(domain)
317         
318     if rootdn is None:
319        rootdn = domaindn
320        
321     if configdn is None:
322         configdn = "CN=Configuration," + rootdn
323     if schemadn is None:
324         schemadn = "CN=Schema," + configdn
325
326     if sitename is None:
327         sitename=DEFAULTSITE
328
329     names = ProvisionNames()
330     names.rootdn = rootdn
331     names.domaindn = domaindn
332     names.configdn = configdn
333     names.schemadn = schemadn
334     names.ldapmanagerdn = "CN=Manager," + rootdn
335     names.dnsdomain = dnsdomain
336     names.domain = domain
337     names.realm = realm
338     names.netbiosname = netbiosname
339     names.hostname = hostname
340     names.sitename = sitename
341     
342     return names
343     
344
345 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, 
346                  targetdir):
347     if hostname is None:
348         hostname = socket.gethostname().split(".")[0].lower()
349
350     if serverrole is None:
351         serverrole = "standalone"
352
353     assert serverrole in ("domain controller", "member server", "standalone")
354     if serverrole == "domain controller":
355         smbconfsuffix = "dc"
356     elif serverrole == "member server":
357         smbconfsuffix = "member"
358     elif serverrole == "standalone":
359         smbconfsuffix = "standalone"
360
361     assert domain is not None
362     assert realm is not None
363
364     default_lp = param.LoadParm()
365     #Load non-existant file
366     default_lp.load(smbconf)
367     
368     if targetdir is not None:
369         privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
370         lockdir_line = "lock dir = " + os.path.abspath(targetdir)
371
372         default_lp.set("lock dir", os.path.abspath(targetdir))
373     else:
374         privatedir_line = ""
375         lockdir_line = ""
376
377     sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
378     netlogon = os.path.join(sysvol, realm.lower(), "scripts")
379
380     setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix), 
381                smbconf, {
382             "HOSTNAME": hostname,
383             "DOMAIN": domain,
384             "REALM": realm,
385             "SERVERROLE": serverrole,
386             "NETLOGONPATH": netlogon,
387             "SYSVOLPATH": sysvol,
388             "PRIVATEDIR_LINE": privatedir_line,
389             "LOCKDIR_LINE": lockdir_line
390             })
391
392
393 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
394                         users_gid, wheel_gid):
395     """setup reasonable name mappings for sam names to unix names.
396
397     :param samdb: SamDB object.
398     :param idmap: IDmap db object.
399     :param sid: The domain sid.
400     :param domaindn: The domain DN.
401     :param root_uid: uid of the UNIX root user.
402     :param nobody_uid: uid of the UNIX nobody user.
403     :param users_gid: gid of the UNIX users group.
404     :param wheel_gid: gid of the UNIX wheel group."""
405     # add some foreign sids if they are not present already
406     samdb.add_foreign(domaindn, "S-1-5-7", "Anonymous")
407     samdb.add_foreign(domaindn, "S-1-1-0", "World")
408     samdb.add_foreign(domaindn, "S-1-5-2", "Network")
409     samdb.add_foreign(domaindn, "S-1-5-18", "System")
410     samdb.add_foreign(domaindn, "S-1-5-11", "Authenticated Users")
411
412     idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
413     idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
414
415     idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
416     idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
417
418
419 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info, 
420                            credentials, names,
421                            serverrole, ldap_backend=None, 
422                            ldap_backend_type=None, erase=False):
423     """Setup the partitions for the SAM database. 
424     
425     Alternatively, provision() may call this, and then populate the database.
426     
427     :note: This will wipe the Sam Database!
428     
429     :note: This function always removes the local SAM LDB file. The erase 
430         parameter controls whether to erase the existing data, which 
431         may not be stored locally but in LDAP.
432     """
433     assert session_info is not None
434
435     samdb = SamDB(samdb_path, session_info=session_info, 
436                   credentials=credentials, lp=lp)
437
438     # Wipes the database
439     try:
440         samdb.erase()
441     except:
442         os.unlink(samdb_path)
443
444     samdb = SamDB(samdb_path, session_info=session_info, 
445                   credentials=credentials, lp=lp)
446
447     #Add modules to the list to activate them by default
448     #beware often order is important
449     #
450     # Some Known ordering constraints:
451     # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
452     # - objectclass must be before password_hash, because password_hash checks
453     #   that the objectclass is of type person (filled in by objectclass
454     #   module when expanding the objectclass list)
455     # - partition must be last
456     # - each partition has its own module list then
457     modules_list = ["rootdse",
458                     "paged_results",
459                     "ranged_results",
460                     "anr",
461                     "server_sort",
462                     "extended_dn",
463                     "asq",
464                     "rdn_name",
465                     "objectclass",
466                     "samldb",
467                     "kludge_acl",
468                     "operational"]
469     tdb_modules_list = [
470                     "subtree_rename",
471                     "subtree_delete",
472                     "linked_attributes"]
473     modules_list2 = ["show_deleted",
474                     "partition"]
475  
476     domaindn_ldb = "users.ldb"
477     if ldap_backend is not None:
478         domaindn_ldb = ldap_backend
479     configdn_ldb = "configuration.ldb"
480     if ldap_backend is not None:
481         configdn_ldb = ldap_backend
482     schemadn_ldb = "schema.ldb"
483     if ldap_backend is not None:
484         schema_ldb = ldap_backend
485         schemadn_ldb = ldap_backend
486         
487     if ldap_backend_type == "fedora-ds":
488         backend_modules = ["nsuniqueid", "paged_searches"]
489         # We can handle linked attributes here, as we don't have directory-side subtree operations
490         tdb_modules_list = ["linked_attributes"]
491     elif ldap_backend_type == "openldap":
492         backend_modules = ["normalise", "entryuuid", "paged_searches"]
493         # OpenLDAP handles subtree renames, so we don't want to do any of these things
494         tdb_modules_list = None
495     elif serverrole == "domain controller":
496         backend_modules = ["repl_meta_data"]
497     else:
498         backend_modules = ["objectguid"]
499
500     if tdb_modules_list is None:
501         tdb_modules_list_as_string = ""
502     else:
503         tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
504         
505     samdb.transaction_start()
506     try:
507         setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
508                 "SCHEMADN": names.schemadn, 
509                 "SCHEMADN_LDB": schemadn_ldb,
510                 "SCHEMADN_MOD2": ",objectguid",
511                 "CONFIGDN": names.configdn,
512                 "CONFIGDN_LDB": configdn_ldb,
513                 "DOMAINDN": names.domaindn,
514                 "DOMAINDN_LDB": domaindn_ldb,
515                 "SCHEMADN_MOD": "schema_fsmo,instancetype",
516                 "CONFIGDN_MOD": "naming_fsmo,instancetype",
517                 "DOMAINDN_MOD": "pdc_fsmo,password_hash,instancetype",
518                 "MODULES_LIST": ",".join(modules_list),
519                 "TDB_MODULES_LIST": tdb_modules_list_as_string,
520                 "MODULES_LIST2": ",".join(modules_list2),
521                 "BACKEND_MOD": ",".join(backend_modules),
522         })
523
524     except:
525         samdb.transaction_cancel()
526         raise
527
528     samdb.transaction_commit()
529     
530     samdb = SamDB(samdb_path, session_info=session_info, 
531                   credentials=credentials, lp=lp)
532
533     samdb.transaction_start()
534     try:
535         message("Setting up sam.ldb attributes")
536         samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
537
538         message("Setting up sam.ldb rootDSE")
539         setup_samdb_rootdse(samdb, setup_path, names.schemadn, names.domaindn, 
540                             names.hostname, names.dnsdomain, names.realm, 
541                             names.rootdn, names.configdn, names.netbiosname,
542                             names.sitename)
543
544         if erase:
545             message("Erasing data from partitions")
546             samdb.erase_partitions()
547
548     except:
549         samdb.transaction_cancel()
550         raise
551
552     samdb.transaction_commit()
553     
554     return samdb
555
556
557 def secretsdb_become_dc(secretsdb, setup_path, domain, realm, dnsdomain, 
558                         netbiosname, domainsid, keytab_path, samdb_url, 
559                         dns_keytab_path, dnspass, machinepass):
560     """Add DC-specific bits to a secrets database.
561     
562     :param secretsdb: Ldb Handle to the secrets database
563     :param setup_path: Setup path function
564     :param machinepass: Machine password
565     """
566     setup_ldb(secretsdb, setup_path("secrets_dc.ldif"), { 
567             "MACHINEPASS_B64": b64encode(machinepass),
568             "DOMAIN": domain,
569             "REALM": realm,
570             "DNSDOMAIN": dnsdomain,
571             "DOMAINSID": str(domainsid),
572             "SECRETS_KEYTAB": keytab_path,
573             "NETBIOSNAME": netbiosname,
574             "SAM_LDB": samdb_url,
575             "DNS_KEYTAB": dns_keytab_path,
576             "DNSPASS_B64": b64encode(dnspass),
577             })
578
579
580 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
581     """Setup the secrets database.
582
583     :param path: Path to the secrets database.
584     :param setup_path: Get the path to a setup file.
585     :param session_info: Session info.
586     :param credentials: Credentials
587     :param lp: Loadparm context
588     :return: LDB handle for the created secrets database
589     """
590     if os.path.exists(path):
591         os.unlink(path)
592     secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
593                       lp=lp)
594     secrets_ldb.erase()
595     secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
596     secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
597                       lp=lp)
598     secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
599     return secrets_ldb
600
601
602 def setup_templatesdb(path, setup_path, session_info, credentials, lp):
603     """Setup the templates database.
604
605     :param path: Path to the database.
606     :param setup_path: Function for obtaining the path to setup files.
607     :param session_info: Session info
608     :param credentials: Credentials
609     :param lp: Loadparm context
610     """
611     templates_ldb = SamDB(path, session_info=session_info,
612                           credentials=credentials, lp=lp)
613     templates_ldb.erase()
614     templates_ldb.load_ldif_file_add(setup_path("provision_templates.ldif"))
615
616
617 def setup_registry(path, setup_path, session_info, credentials, lp):
618     """Setup the registry.
619     
620     :param path: Path to the registry database
621     :param setup_path: Function that returns the path to a setup.
622     :param session_info: Session information
623     :param credentials: Credentials
624     :param lp: Loadparm context
625     """
626     reg = registry.Registry()
627     hive = registry.open_ldb(path, session_info=session_info, 
628                          credentials=credentials, lp_ctx=lp)
629     reg.mount_hive(hive, "HKEY_LOCAL_MACHINE")
630     provision_reg = setup_path("provision.reg")
631     assert os.path.exists(provision_reg)
632     reg.diff_apply(provision_reg)
633
634
635 def setup_idmapdb(path, setup_path, session_info, credentials, lp):
636     """Setup the idmap database.
637
638     :param path: path to the idmap database
639     :param setup_path: Function that returns a path to a setup file
640     :param session_info: Session information
641     :param credentials: Credentials
642     :param lp: Loadparm context
643     """
644     if os.path.exists(path):
645         os.unlink(path)
646
647     idmap_ldb = IDmapDB(path, session_info=session_info,
648                         credentials=credentials, lp=lp)
649
650     idmap_ldb.erase()
651     idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
652     return idmap_ldb
653
654
655 def setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, hostname, 
656                         dnsdomain, realm, rootdn, configdn, netbiosname,
657                         sitename):
658     """Setup the SamDB rootdse.
659
660     :param samdb: Sam Database handle
661     :param setup_path: Obtain setup path
662     """
663     setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
664         "SCHEMADN": schemadn, 
665         "NETBIOSNAME": netbiosname,
666         "DNSDOMAIN": dnsdomain,
667         "DEFAULTSITE": sitename,
668         "REALM": realm,
669         "DNSNAME": "%s.%s" % (hostname, dnsdomain),
670         "DOMAINDN": domaindn,
671         "ROOTDN": rootdn,
672         "CONFIGDN": configdn,
673         "VERSION": samba.version(),
674         })
675         
676
677 def setup_self_join(samdb, names,
678                     machinepass, dnspass, 
679                     domainsid, invocationid, setup_path,
680                     policyguid):
681     """Join a host to its own domain."""
682     setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), { 
683               "CONFIGDN": names.configdn, 
684               "SCHEMADN": names.schemadn,
685               "DOMAINDN": names.domaindn,
686               "INVOCATIONID": invocationid,
687               "NETBIOSNAME": names.netbiosname,
688               "DEFAULTSITE": names.sitename,
689               "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
690               "MACHINEPASS_B64": b64encode(machinepass),
691               "DNSPASS_B64": b64encode(dnspass),
692               "REALM": names.realm,
693               "DOMAIN": names.domain,
694               "DNSDOMAIN": names.dnsdomain})
695     setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), { 
696               "POLICYGUID": policyguid,
697               "DNSDOMAIN": names.dnsdomain,
698               "DOMAINSID": str(domainsid),
699               "DOMAINDN": names.domaindn})
700
701
702 def setup_samdb(path, setup_path, session_info, credentials, lp, 
703                 names, message, 
704                 domainsid, aci, domainguid, policyguid, 
705                 fill, adminpass, krbtgtpass, 
706                 machinepass, invocationid, dnspass,
707                 serverrole, ldap_backend=None, 
708                 ldap_backend_type=None):
709     """Setup a complete SAM Database.
710     
711     :note: This will wipe the main SAM database file!
712     """
713
714     erase = (fill != FILL_DRS)
715
716     # Also wipes the database
717     setup_samdb_partitions(path, setup_path, message=message, lp=lp,
718                            credentials=credentials, session_info=session_info,
719                            names=names, 
720                            ldap_backend=ldap_backend, serverrole=serverrole,
721                            ldap_backend_type=ldap_backend_type, erase=erase)
722
723     samdb = SamDB(path, session_info=session_info, 
724                   credentials=credentials, lp=lp)
725
726     if fill == FILL_DRS:
727        # We want to finish here, but setup the index before we do so
728         message("Setting up sam.ldb index")
729         samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
730         return samdb
731
732     message("Pre-loading the Samba 4 and AD schema")
733     samdb = SamDB(path, session_info=session_info, 
734                   credentials=credentials, lp=lp)
735     samdb.set_domain_sid(domainsid)
736     if serverrole == "domain controller":
737         samdb.set_invocation_id(invocationid)
738
739     load_schema(setup_path, samdb, names.schemadn, names.netbiosname, 
740                 names.configdn, names.sitename)
741
742     samdb.transaction_start()
743         
744     try:
745         message("Adding DomainDN: %s (permitted to fail)" % names.domaindn)
746         if serverrole == "domain controller":
747             domain_oc = "domainDNS"
748         else:
749             domain_oc = "samba4LocalDomain"
750
751         setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
752             "DOMAINDN": names.domaindn,
753             "ACI": aci,
754             "DOMAIN_OC": domain_oc
755             })
756
757         message("Modifying DomainDN: " + names.domaindn + "")
758         if domainguid is not None:
759             domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
760         else:
761             domainguid_mod = ""
762
763         setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
764             "LDAPTIME": timestring(int(time.time())),
765             "DOMAINSID": str(domainsid),
766             "SCHEMADN": names.schemadn, 
767             "NETBIOSNAME": names.netbiosname,
768             "DEFAULTSITE": names.sitename,
769             "CONFIGDN": names.configdn,
770             "POLICYGUID": policyguid,
771             "DOMAINDN": names.domaindn,
772             "DOMAINGUID_MOD": domainguid_mod,
773             })
774
775         message("Adding configuration container (permitted to fail)")
776         setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
777             "CONFIGDN": names.configdn, 
778             "ACI": aci,
779             "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb",
780             })
781         message("Modifying configuration container")
782         setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
783             "CONFIGDN": names.configdn, 
784             "SCHEMADN": names.schemadn,
785             })
786
787         message("Adding schema container (permitted to fail)")
788         setup_add_ldif(samdb, setup_path("provision_schema_basedn.ldif"), {
789             "SCHEMADN": names.schemadn,
790             "ACI": aci,
791             "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb"
792             })
793         message("Modifying schema container")
794         setup_modify_ldif(samdb, 
795             setup_path("provision_schema_basedn_modify.ldif"), {
796             "SCHEMADN": names.schemadn,
797             "NETBIOSNAME": names.netbiosname,
798             "DEFAULTSITE": names.sitename,
799             "CONFIGDN": names.configdn,
800             })
801
802         message("Setting up sam.ldb Samba4 schema")
803         setup_add_ldif(samdb, setup_path("schema_samba4.ldif"), 
804                        {"SCHEMADN": names.schemadn })
805         message("Setting up sam.ldb AD schema")
806         setup_add_ldif(samdb, setup_path("schema.ldif"), 
807                        {"SCHEMADN": names.schemadn})
808
809         message("Setting up sam.ldb configuration data")
810         setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
811             "CONFIGDN": names.configdn,
812             "NETBIOSNAME": names.netbiosname,
813             "DEFAULTSITE": names.sitename,
814             "DNSDOMAIN": names.dnsdomain,
815             "DOMAIN": names.domain,
816             "SCHEMADN": names.schemadn,
817             "DOMAINDN": names.domaindn,
818             })
819
820         message("Setting up display specifiers")
821         setup_add_ldif(samdb, setup_path("display_specifiers.ldif"), 
822                        {"CONFIGDN": names.configdn})
823
824         message("Adding users container (permitted to fail)")
825         setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
826                 "DOMAINDN": names.domaindn})
827         message("Modifying users container")
828         setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
829                 "DOMAINDN": names.domaindn})
830         message("Adding computers container (permitted to fail)")
831         setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
832                 "DOMAINDN": names.domaindn})
833         message("Modifying computers container")
834         setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
835                 "DOMAINDN": names.domaindn})
836         message("Setting up sam.ldb data")
837         setup_add_ldif(samdb, setup_path("provision.ldif"), {
838             "DOMAINDN": names.domaindn,
839             "NETBIOSNAME": names.netbiosname,
840             "DEFAULTSITE": names.sitename,
841             "CONFIGDN": names.configdn,
842             })
843
844         if fill == FILL_FULL:
845             message("Setting up sam.ldb users and groups")
846             setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
847                 "DOMAINDN": names.domaindn,
848                 "DOMAINSID": str(domainsid),
849                 "CONFIGDN": names.configdn,
850                 "ADMINPASS_B64": b64encode(adminpass),
851                 "KRBTGTPASS_B64": b64encode(krbtgtpass),
852                 })
853
854             if serverrole == "domain controller":
855                 message("Setting up self join")
856                 setup_self_join(samdb, names=names, invocationid=invocationid, 
857                                 dnspass=dnspass,  
858                                 machinepass=machinepass, 
859                                 domainsid=domainsid, policyguid=policyguid,
860                                 setup_path=setup_path)
861
862     #We want to setup the index last, as adds are faster unindexed
863         message("Setting up sam.ldb index")
864         samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
865     except:
866         samdb.transaction_cancel()
867         raise
868
869     samdb.transaction_commit()
870     return samdb
871
872
873 FILL_FULL = "FULL"
874 FILL_NT4SYNC = "NT4SYNC"
875 FILL_DRS = "DRS"
876
877 def provision(setup_dir, message, session_info, 
878               credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL, 
879               realm=None, rootdn=None, domaindn=None, schemadn=None, 
880               configdn=None, domain=None, hostname=None, hostip=None, 
881               hostip6=None, domainsid=None, adminpass=None, krbtgtpass=None, 
882               domainguid=None, policyguid=None, invocationid=None, 
883               machinepass=None, dnspass=None, root=None, nobody=None, 
884               nogroup=None, users=None, wheel=None, backup=None, aci=None, 
885               serverrole=None, ldap_backend=None, ldap_backend_type=None, 
886               sitename=None):
887     """Provision samba4
888     
889     :note: caution, this wipes all existing data!
890     """
891
892     def setup_path(file):
893         return os.path.join(setup_dir, file)
894
895     if domainsid is None:
896         domainsid = security.random_sid()
897     else:
898         domainsid = security.Sid(domainsid)
899
900     if policyguid is None:
901         policyguid = uuid.random()
902     if adminpass is None:
903         adminpass = misc.random_password(12)
904     if krbtgtpass is None:
905         krbtgtpass = misc.random_password(12)
906     if machinepass is None:
907         machinepass  = misc.random_password(12)
908     if dnspass is None:
909         dnspass = misc.random_password(12)
910     root_uid = findnss_uid([root or "root"])
911     nobody_uid = findnss_uid([nobody or "nobody"])
912     users_gid = findnss_gid([users or "users"])
913     if wheel is None:
914         wheel_gid = findnss_gid(["wheel", "adm"])
915     else:
916         wheel_gid = findnss_gid([wheel])
917     if aci is None:
918         aci = "# no aci for local ldb"
919
920     if smbconf is None:
921         os.makedirs(os.path.join(targetdir, "etc"))
922         smbconf = os.path.join(targetdir, "etc", "smb.conf")
923
924     # only install a new smb.conf if there isn't one there already
925     if not os.path.exists(smbconf):
926         make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, 
927                      targetdir)
928
929     lp = param.LoadParm()
930     lp.load(smbconf)
931
932     names = guess_names(lp=lp, hostname=hostname, domain=domain, 
933                         dnsdomain=realm, serverrole=serverrole, 
934                         sitename=sitename, rootdn=rootdn, domaindn=domaindn, 
935                         configdn=configdn, schemadn=schemadn)
936
937     paths = provision_paths_from_lp(lp, names.dnsdomain)
938
939     if hostip is None:
940         hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
941
942     if hostip6 is None:
943         try:
944             hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
945         except socket.gaierror: 
946             pass
947
948     if serverrole is None:
949         serverrole = lp.get("server role")
950
951     assert serverrole in ("domain controller", "member server", "standalone")
952     if invocationid is None and serverrole == "domain controller":
953         invocationid = uuid.random()
954
955     if not os.path.exists(paths.private_dir):
956         os.mkdir(paths.private_dir)
957
958     ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
959     
960     if ldap_backend is not None:
961         if ldap_backend == "ldapi":
962             # provision-backend will set this path suggested slapd command line / fedorads.inf
963             ldap_backend = "ldapi://" % urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
964              
965     # only install a new shares config db if there is none
966     if not os.path.exists(paths.shareconf):
967         message("Setting up share.ldb")
968         share_ldb = Ldb(paths.shareconf, session_info=session_info, 
969                         credentials=credentials, lp=lp)
970         share_ldb.load_ldif_file_add(setup_path("share.ldif"))
971
972      
973     message("Setting up secrets.ldb")
974     secrets_ldb = setup_secretsdb(paths.secrets, setup_path, 
975                                   session_info=session_info, 
976                                   credentials=credentials, lp=lp)
977
978     message("Setting up the registry")
979     setup_registry(paths.hklm, setup_path, session_info, 
980                    credentials=credentials, lp=lp)
981
982     message("Setting up templates db")
983     setup_templatesdb(paths.templates, setup_path, session_info=session_info, 
984                       credentials=credentials, lp=lp)
985
986     message("Setting up idmap db")
987     idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
988                           credentials=credentials, lp=lp)
989
990     samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info, 
991                         credentials=credentials, lp=lp, names=names,
992                         message=message, 
993                         domainsid=domainsid, 
994                         aci=aci, domainguid=domainguid, policyguid=policyguid, 
995                         fill=samdb_fill, 
996                         adminpass=adminpass, krbtgtpass=krbtgtpass,
997                         invocationid=invocationid, 
998                         machinepass=machinepass, dnspass=dnspass,
999                         serverrole=serverrole, ldap_backend=ldap_backend, 
1000                         ldap_backend_type=ldap_backend_type)
1001
1002     if lp.get("server role") == "domain controller":
1003        policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies", 
1004                                   "{" + policyguid + "}")
1005        os.makedirs(policy_path, 0755)
1006        os.makedirs(os.path.join(policy_path, "Machine"), 0755)
1007        os.makedirs(os.path.join(policy_path, "User"), 0755)
1008        if not os.path.isdir(paths.netlogon):
1009             os.makedirs(paths.netlogon, 0755)
1010        secrets_ldb = Ldb(paths.secrets, session_info=session_info, 
1011                          credentials=credentials, lp=lp)
1012        secretsdb_become_dc(secrets_ldb, setup_path, domain=domain, realm=names.realm,
1013                            netbiosname=names.netbiosname, domainsid=domainsid, 
1014                            keytab_path=paths.keytab, samdb_url=paths.samdb, 
1015                            dns_keytab_path=paths.dns_keytab, dnspass=dnspass, 
1016                            machinepass=machinepass, dnsdomain=names.dnsdomain)
1017
1018     if samdb_fill == FILL_FULL:
1019         setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1020                             root_uid=root_uid, nobody_uid=nobody_uid,
1021                             users_gid=users_gid, wheel_gid=wheel_gid)
1022
1023         message("Setting up sam.ldb rootDSE marking as synchronized")
1024         setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1025
1026         # Only make a zone file on the first DC, it should be replicated with DNS replication
1027         if serverrole == "domain controller":
1028             samdb = SamDB(paths.samdb, session_info=session_info, 
1029                       credentials=credentials, lp=lp)
1030
1031             domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1032             assert isinstance(domainguid, str)
1033             hostguid = samdb.searchone(basedn=domaindn, attribute="objectGUID",
1034                                        expression="(&(objectClass=computer)(cn=%s))" % names.hostname,
1035                                        scope=SCOPE_SUBTREE)
1036             assert isinstance(hostguid, str)
1037             
1038             create_zone_file(paths.dns, setup_path, samdb, 
1039                              hostname=names.hostname, hostip=hostip,
1040                              hostip6=hostip6, dnsdomain=names.dnsdomain,
1041                              domaindn=names.domaindn, dnspass=dnspass, realm=names.realm, 
1042                              domainguid=domainguid, hostguid=hostguid)
1043             message("Please install the zone located in %s into your DNS server" % paths.dns)
1044             
1045     create_phpldapadmin_config(paths.phpldapadminconfig, setup_path, 
1046                                ldapi_url)
1047
1048     message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1049
1050     message("Once the above files are installed, your server will be ready to use")
1051     message("Server Type:    %s" % serverrole)
1052     message("Hostname:       %s" % names.hostname)
1053     message("NetBIOS Domain: %s" % names.domain)
1054     message("DNS Domain:     %s" % names.dnsdomain)
1055     message("DOMAIN SID:     %s" % str(domainsid))
1056     message("Admin password: %s" % adminpass)
1057
1058     result = ProvisionResult()
1059     result.domaindn = domaindn
1060     result.paths = paths
1061     result.lp = lp
1062     result.samdb = samdb
1063     return result
1064
1065 def provision_become_dc(setup_dir=None,
1066                         smbconf=None, targetdir=None, realm=None, 
1067                         rootdn=None, domaindn=None, schemadn=None, configdn=None,
1068                         domain=None, hostname=None, domainsid=None, 
1069                         adminpass=None, krbtgtpass=None, domainguid=None, 
1070                         policyguid=None, invocationid=None, machinepass=None, 
1071                         dnspass=None, root=None, nobody=None, nogroup=None, users=None, 
1072                         wheel=None, backup=None, aci=None, serverrole=None, 
1073                         ldap_backend=None, ldap_backend_type=None, sitename=DEFAULTSITE):
1074
1075     def message(text):
1076         """print a message if quiet is not set."""
1077         print text
1078
1079     provision(setup_dir, message, system_session(), None,
1080               smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS, realm=realm, 
1081               rootdn=rootdn, domaindn=domaindn, schemadn=schemadn, configdn=configdn, 
1082               domain=domain, hostname=hostname, hostip="127.0.0.1", domainsid=domainsid, machinepass=machinepass, serverrole="domain controller", sitename=sitename);
1083     
1084
1085 def setup_db_config(setup_path, file, dbdir):
1086     if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
1087         os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700);
1088     if not os.path.isdir(os.path.join(dbdir, "tmp")):
1089         os.makedirs(os.path.join(dbdir, "tmp"), 0700);
1090     
1091     setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
1092                {"LDAPDBDIR": dbdir})
1093     
1094
1095
1096 def provision_backend(setup_dir=None, message=None,
1097                       smbconf=None, targetdir=None, realm=None, 
1098                       rootdn=None, domaindn=None, schemadn=None, configdn=None,
1099                       domain=None, hostname=None, adminpass=None, root=None, serverrole=None, 
1100                       ldap_backend_type=None):
1101
1102     def setup_path(file):
1103         return os.path.join(setup_dir, file)
1104
1105     if hostname is None:
1106         hostname = socket.gethostname().split(".")[0].lower()
1107
1108     if root is None:
1109         root = findnss(pwd.getpwnam, ["root"])[0]
1110
1111     if smbconf is None:
1112         os.makedirs(os.path.join(targetdir, "etc"))
1113         smbconf = os.path.join(targetdir, "etc", "smb.conf")
1114
1115     # only install a new smb.conf if there isn't one there already
1116     if not os.path.exists(smbconf):
1117         make_smbconf(smbconf, setup_path, hostname, domain, realm, 
1118                               serverrole, targetdir)
1119
1120     lp = param.LoadParm()
1121     lp.load(smbconf)
1122
1123     names = guess_names(lp=lp, hostname=hostname, domain=domain, 
1124                         dnsdomain=realm, serverrole=serverrole, 
1125                         rootdn=rootdn, domaindn=domaindn, configdn=configdn, 
1126                         schemadn=schemadn)
1127
1128     paths = provision_paths_from_lp(lp, names.dnsdomain)
1129
1130     if not os.path.isdir(paths.ldapdir):
1131         os.makedirs(paths.ldapdir)
1132     schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
1133     try:
1134         os.unlink(schemadb_path)
1135     except:
1136         pass
1137
1138     schemadb = Ldb(schemadb_path, lp=lp)
1139  
1140     setup_add_ldif(schemadb, setup_path("provision_schema_basedn.ldif"), 
1141                    {"SCHEMADN": names.schemadn,
1142                     "ACI": "#",
1143                     "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb"
1144                     })
1145     setup_modify_ldif(schemadb, 
1146                       setup_path("provision_schema_basedn_modify.ldif"), \
1147                           {"SCHEMADN": names.schemadn,
1148                            "NETBIOSNAME": names.netbiosname,
1149                            "DEFAULTSITE": DEFAULTSITE,
1150                            "CONFIGDN": names.configdn,
1151                            })
1152     
1153     setup_add_ldif(schemadb, setup_path("schema_samba4.ldif"), 
1154                    {"SCHEMADN": names.schemadn })
1155     setup_add_ldif(schemadb, setup_path("schema.ldif"), 
1156                    {"SCHEMADN": names.schemadn})
1157
1158     if ldap_backend_type == "fedora-ds":
1159         setup_file(setup_path("fedora-ds.inf"), paths.fedoradsinf, 
1160                    {"ROOT": root,
1161                     "HOSTNAME": hostname,
1162                     "DNSDOMAIN": names.dnsdomain,
1163                     "LDAPDIR": paths.ldapdir,
1164                     "DOMAINDN": names.domaindn,
1165                     "LDAPMANAGERDN": names.ldapmanagerdn,
1166                     "LDAPMANAGERPASS": adminpass, 
1167                     "SERVERPORT": ""})
1168         
1169         setup_file(setup_path("fedora-partitions.ldif"), paths.fedoradspartitions, 
1170                    {"CONFIGDN": names.configdn,
1171                     "SCHEMADN": names.schemadn,
1172                     })
1173         
1174         setup_file(setup_path("fedora-partitions.ldif"), paths.fedoradspartitions, 
1175                    {"CONFIGDN": names.configdn,
1176                     "SCHEMADN": names.schemadn,
1177                     })
1178         mapping = "schema-map-fedora-ds-1.0"
1179         backend_schema = "99_ad.ldif"
1180     elif ldap_backend_type == "openldap":
1181         attrs = ["linkID", "lDAPDisplayName"]
1182     res = schemadb.search(expression="(&(&(linkID=*)(!(linkID:1.2.840.113556.1.4.803:=1)))(objectclass=attributeSchema))", base=names.schemadn, scope=SCOPE_SUBTREE, attrs=attrs);
1183
1184     memberof_config = "# Generated from schema in " + schemadb_path + "\n";
1185     refint_attributes = "";
1186     for i in range (0, len(res)):
1187             linkid = res[i]["linkID"][0]
1188             linkid = str(int(linkid) + 1)
1189             expression = "(&(objectclass=attributeSchema)(linkID=" + (linkid) + "))"
1190             target = schemadb.searchone(basedn=names.schemadn, 
1191                                         expression=expression, 
1192                                         attribute="lDAPDisplayName", 
1193                                         scope=SCOPE_SUBTREE);
1194             if target is not None:
1195                 refint_attributes = refint_attributes + " " + target + " " + res[i]["lDAPDisplayName"][0];
1196                 memberof_config = memberof_config + """overlay memberof
1197 memberof-dangling error
1198 memberof-refint TRUE
1199 memberof-group-oc top
1200 memberof-member-ad """ + res[i]["lDAPDisplayName"][0] + """
1201 memberof-memberof-ad """ + target + """
1202 memberof-dangling-error 32
1203
1204 """;
1205
1206     memberof_config = memberof_config + """
1207 overlay refint
1208 refint_attributes""" + refint_attributes + "\n";
1209     
1210     setup_file(setup_path("slapd.conf"), paths.slapdconf,
1211                    {"DNSDOMAIN": names.dnsdomain,
1212                     "LDAPDIR": paths.ldapdir,
1213                     "DOMAINDN": names.domaindn,
1214                     "CONFIGDN": names.configdn,
1215                     "SCHEMADN": names.schemadn,
1216                     "LDAPMANAGERDN": names.ldapmanagerdn,
1217                     "LDAPMANAGERPASS": adminpass,
1218                     "MEMBEROF_CONFIG": memberof_config})
1219     setup_file(setup_path("modules.conf"), paths.modulesconf,
1220                    {"REALM": names.realm})
1221         
1222     setup_db_config(setup_path, file, os.path.join(paths.ldapdir, "db", "user"))
1223     setup_db_config(setup_path, file, os.path.join(paths.ldapdir, "db", "config"))
1224     setup_db_config(setup_path, file, os.path.join(paths.ldapdir, "db", "schema"))
1225     mapping = "schema-map-openldap-2.3"
1226     backend_schema = "backend-schema.schema"
1227         
1228
1229     ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
1230     message("Start slapd with: slapd -f " + paths.ldapdir + "/slapd.conf -h " + ldapi_uri)
1231                 
1232
1233     schema_command = "bin/ad2oLschema --option=convert:target=" + ldap_backend_type + " -I " + setup_path(mapping) + " -H tdb://" + schemadb_path + " -O " + os.path.join(paths.ldapdir, backend_schema);
1234
1235     os.system(schema_command)
1236
1237
1238
1239 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1240     """Create a PHP LDAP admin configuration file.
1241
1242     :param path: Path to write the configuration to.
1243     :param setup_path: Function to generate setup paths.
1244     """
1245     setup_file(setup_path("phpldapadmin-config.php"), path, 
1246             {"S4_LDAPI_URI": ldapi_uri})
1247
1248
1249 def create_zone_file(path, setup_path, samdb, dnsdomain, domaindn, 
1250                   hostip, hostip6, hostname, dnspass, realm, domainguid, hostguid):
1251     """Write out a DNS zone file, from the info in the current database.
1252     
1253     :param path: Path of the new file.
1254     :param setup_path": Setup path function.
1255     :param samdb: SamDB object
1256     :param dnsdomain: DNS Domain name
1257     :param domaindn: DN of the Domain
1258     :param hostip: Local IPv4 IP
1259     :param hostip6: Local IPv6 IP
1260     :param hostname: Local hostname
1261     :param dnspass: Password for DNS
1262     :param realm: Realm name
1263     :param domainguid: GUID of the domain.
1264     :param hostguid: GUID of the host.
1265     """
1266     assert isinstance(domainguid, str)
1267
1268     hostip6_base_line = ""
1269     hostip6_host_line = ""
1270
1271     if hostip6 is not None:
1272         hostip6_base_line = "            IN AAAA    " + hostip6
1273         hostip6_host_line = hostname + "        IN AAAA    " + hostip6
1274
1275     setup_file(setup_path("provision.zone"), path, {
1276             "DNSPASS_B64": b64encode(dnspass),
1277             "HOSTNAME": hostname,
1278             "DNSDOMAIN": dnsdomain,
1279             "REALM": realm,
1280             "HOSTIP": hostip,
1281             "DOMAINGUID": domainguid,
1282             "DATESTRING": time.strftime("%Y%m%d%H"),
1283             "DEFAULTSITE": DEFAULTSITE,
1284             "HOSTGUID": hostguid,
1285             "HOSTIP6_BASE_LINE": hostip6_base_line,
1286             "HOSTIP6_HOST_LINE": hostip6_host_line,
1287         })
1288
1289 def load_schema(setup_path, samdb, schemadn, netbiosname, configdn, sitename):
1290     """Load schema for the SamDB.
1291     
1292     :param samdb: Load a schema into a SamDB.
1293     :param setup_path: Setup path function.
1294     :param schemadn: DN of the schema
1295     :param netbiosname: NetBIOS name of the host.
1296     :param configdn: DN of the configuration
1297     """
1298     schema_data = open(setup_path("schema.ldif"), 'r').read()
1299     schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
1300     schema_data = substitute_var(schema_data, {"SCHEMADN": schemadn})
1301     head_data = open(setup_path("provision_schema_basedn_modify.ldif"), 'r').read()
1302     head_data = substitute_var(head_data, {
1303                     "SCHEMADN": schemadn,
1304                     "NETBIOSNAME": netbiosname,
1305                     "CONFIGDN": configdn,
1306                     "DEFAULTSITE":sitename 
1307     })
1308     samdb.attach_schema_from_ldif(head_data, schema_data)
1309