Clean up provision and rootdse module to hard-code less stuff.
[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 #
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 Error("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 def open_ldb(session_info, credentials, lp, dbname):
129     """Open a LDB, thrashing it if it is corrupt.
130
131     :param session_info: auth session information
132     :param credentials: credentials
133     :param lp: Loadparm context
134     :param dbname: Path of the database to open.
135     :return: a Ldb object
136     """
137     assert session_info is not None
138     try:
139         return Ldb(dbname, session_info=session_info, credentials=credentials, 
140                    lp=lp)
141     except LdbError, e:
142         print e
143         os.unlink(dbname)
144         return Ldb(dbname, session_info=session_info, credentials=credentials,
145                    lp=lp)
146
147
148 def setup_add_ldif(ldb, ldif_path, subst_vars=None):
149     """Setup a ldb in the private dir.
150     
151     :param ldb: LDB file to import data into
152     :param ldif_path: Path of the LDIF file to load
153     :param subst_vars: Optional variables to subsitute in LDIF.
154     """
155     assert isinstance(ldif_path, str)
156
157     data = open(ldif_path, 'r').read()
158     if subst_vars is not None:
159         data = substitute_var(data, subst_vars)
160
161     check_all_substituted(data)
162
163     ldb.add_ldif(data)
164
165
166 def setup_modify_ldif(ldb, ldif_path, substvars=None):
167     """Modify a ldb in the private dir.
168     
169     :param ldb: LDB object.
170     :param ldif_path: LDIF file path.
171     :param substvars: Optional dictionary with substitution variables.
172     """
173     data = open(ldif_path, 'r').read()
174     if substvars is not None:
175         data = substitute_var(data, substvars)
176
177     check_all_substituted(data)
178
179     ldb.modify_ldif(data)
180
181
182 def setup_ldb(ldb, ldif_path, subst_vars):
183     """Import a LDIF a file into a LDB handle, optionally substituting variables.
184
185     :note: Either all LDIF data will be added or none (using transactions).
186
187     :param ldb: LDB file to import into.
188     :param ldif_path: Path to the LDIF file.
189     :param subst_vars: Dictionary with substitution variables.
190     """
191     assert ldb is not None
192     ldb.transaction_start()
193     try:
194         setup_add_ldif(ldb, ldif_path, subst_vars)
195     except:
196         ldb.transaction_cancel()
197         raise
198     ldb.transaction_commit()
199
200
201 def setup_file(template, fname, substvars):
202     """Setup a file in the private dir.
203
204     :param template: Path of the template file.
205     :param fname: Path of the file to create.
206     :param substvars: Substitution variables.
207     """
208     f = fname
209
210     if os.path.exists(f):
211         os.unlink(f)
212
213     data = open(template, 'r').read()
214     if substvars:
215         data = substitute_var(data, substvars)
216     check_all_substituted(data)
217
218     open(f, 'w').write(data)
219
220
221 def provision_paths_from_lp(lp, dnsdomain):
222     """Set the default paths for provisioning.
223
224     :param lp: Loadparm context.
225     :param dnsdomain: DNS Domain name
226     """
227     paths = ProvisionPaths()
228     paths.private_dir = lp.get("private dir")
229     paths.keytab = "secrets.keytab"
230     paths.dns_keytab = "dns.keytab"
231
232     paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
233     paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
234     paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
235     paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
236     paths.templates = os.path.join(paths.private_dir, "templates.ldb")
237     paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
238     paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
239     paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
240     paths.phpldapadminconfig = os.path.join(paths.private_dir, 
241                                             "phpldapadmin-config.php")
242     paths.ldapdir = os.path.join(paths.private_dir, 
243                                  "ldap")
244     paths.slapdconf = os.path.join(paths.ldapdir, 
245                                    "slapd.conf")
246     paths.modulesconf = os.path.join(paths.ldapdir, 
247                                      "modules.conf")
248     paths.memberofconf = os.path.join(paths.ldapdir, 
249                                       "memberof.conf")
250     paths.fedoradsinf = os.path.join(paths.ldapdir, 
251                                    "fedorads.inf")
252     paths.fedoradspartitions = os.path.join(paths.ldapdir, 
253                                             "fedorads-partitions.ldif")
254     paths.hklm = "hklm.ldb"
255     paths.hkcr = "hkcr.ldb"
256     paths.hkcu = "hkcu.ldb"
257     paths.hku = "hku.ldb"
258     paths.hkpd = "hkpd.ldb"
259     paths.hkpt = "hkpt.ldb"
260
261     paths.sysvol = lp.get("path", "sysvol")
262
263     paths.netlogon = lp.get("path", "netlogon")
264
265     return paths
266
267 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None, serverrole=None,
268                 rootdn=None, domaindn=None, configdn=None, schemadn=None, serverdn=None, 
269                 sitename=None):
270
271     if hostname is None:
272         hostname = socket.gethostname().split(".")[0].lower()
273
274     netbiosname = hostname.upper()
275     if not valid_netbios_name(netbiosname):
276         raise InvalidNetbiosName(netbiosname)
277
278     hostname = hostname.lower()
279
280     if dnsdomain is None:
281         dnsdomain = lp.get("realm")
282
283     if serverrole is None:
284         serverrole = lp.get("server role")
285
286     assert dnsdomain is not None
287     realm = dnsdomain.upper()
288
289     if lp.get("realm").upper() != realm:
290         raise Exception("realm '%s' in %s must match chosen realm '%s'" %
291                         (lp.get("realm"), smbconf, realm))
292     
293     dnsdomain = dnsdomain.lower()
294
295     if (serverrole == "domain controller"):
296         if domain is None:
297             domain = lp.get("workgroup")
298         if domaindn is None:
299             domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
300         if lp.get("workgroup").upper() != domain.upper():
301             raise Error("workgroup '%s' in smb.conf must match chosen domain '%s'",
302                         lp.get("workgroup"), domain)
303     else:
304         domain = netbiosname
305         if domaindn is None:
306             domaindn = "CN=" + netbiosname
307         
308     assert domain is not None
309     domain = domain.upper()
310     if not valid_netbios_name(domain):
311         raise InvalidNetbiosName(domain)
312         
313     if rootdn is None:
314        rootdn = domaindn
315        
316     if configdn is None:
317         configdn = "CN=Configuration," + rootdn
318     if schemadn is None:
319         schemadn = "CN=Schema," + configdn
320
321     if sitename is None:
322         sitename=DEFAULTSITE
323
324     names = ProvisionNames()
325     names.rootdn = rootdn
326     names.domaindn = domaindn
327     names.configdn = configdn
328     names.schemadn = schemadn
329     names.ldapmanagerdn = "CN=Manager," + rootdn
330     names.dnsdomain = dnsdomain
331     names.domain = domain
332     names.realm = realm
333     names.netbiosname = netbiosname
334     names.hostname = hostname
335     names.sitename = sitename
336     names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
337     
338     return names
339     
340
341 def load_or_make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, targetdir):
342     if targetdir is not None:
343         if not os.path.exists(targetdir):
344             os.mkdir(targetdir)
345         if not os.path.exists(os.path.join(targetdir, "etc")):
346            os.mkdir(os.path.join(targetdir, "etc"))
347
348         smbconf = os.path.join(targetdir, "etc", "smb.conf")
349
350     # only install a new smb.conf if there isn't one there already
351
352     if not os.path.exists(smbconf):
353         if hostname is None:
354             hostname = socket.gethostname().split(".")[0].lower()
355
356         if serverrole is None:
357             serverrole = "standalone"
358
359         assert serverrole in ("domain controller", "member server", "standalone")
360         if serverrole == "domain controller":
361             smbconfsuffix = "dc"
362         elif serverrole == "member server":
363             smbconfsuffix = "member"
364         elif serverrole == "standalone":
365             smbconfsuffix = "standalone"
366
367         assert domain is not None
368         assert realm is not None
369
370         default_lp = param.LoadParm()
371         #Load non-existant file
372         default_lp.load(smbconf)
373         
374         if targetdir is not None:
375             privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
376             lockdir_line = "lock dir = " + os.path.abspath(targetdir)
377
378             default_lp.set("lock dir", os.path.abspath(targetdir))
379         else:
380             privatedir_line = ""
381             lockdir_line = ""
382
383         sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
384         netlogon = os.path.join(sysvol, realm.lower(), "scripts")
385
386         setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix), 
387                    smbconf, {
388                 "HOSTNAME": hostname,
389                 "DOMAIN": domain,
390                 "REALM": realm,
391                 "SERVERROLE": serverrole,
392                 "NETLOGONPATH": netlogon,
393                 "SYSVOLPATH": sysvol,
394                 "PRIVATEDIR_LINE": privatedir_line,
395                 "LOCKDIR_LINE": lockdir_line
396                 })
397
398     lp = param.LoadParm()
399     lp.load(smbconf)
400
401     return lp
402
403 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
404                         users_gid, wheel_gid):
405     """setup reasonable name mappings for sam names to unix names.
406
407     :param samdb: SamDB object.
408     :param idmap: IDmap db object.
409     :param sid: The domain sid.
410     :param domaindn: The domain DN.
411     :param root_uid: uid of the UNIX root user.
412     :param nobody_uid: uid of the UNIX nobody user.
413     :param users_gid: gid of the UNIX users group.
414     :param wheel_gid: gid of the UNIX wheel group."""
415     # add some foreign sids if they are not present already
416     samdb.add_foreign(domaindn, "S-1-5-7", "Anonymous")
417     samdb.add_foreign(domaindn, "S-1-1-0", "World")
418     samdb.add_foreign(domaindn, "S-1-5-2", "Network")
419     samdb.add_foreign(domaindn, "S-1-5-18", "System")
420     samdb.add_foreign(domaindn, "S-1-5-11", "Authenticated Users")
421
422     idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
423     idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
424
425     idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
426     idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
427
428 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info, 
429                            credentials, names,
430                            serverrole, ldap_backend=None, 
431                            ldap_backend_type=None, erase=False):
432     """Setup the partitions for the SAM database. 
433     
434     Alternatively, provision() may call this, and then populate the database.
435     
436     :note: This will wipe the Sam Database!
437     
438     :note: This function always removes the local SAM LDB file. The erase 
439         parameter controls whether to erase the existing data, which 
440         may not be stored locally but in LDAP.
441     """
442     assert session_info is not None
443
444     samdb = SamDB(samdb_path, session_info=session_info, 
445                   credentials=credentials, lp=lp)
446
447     # Wipes the database
448     try:
449         samdb.erase()
450     except:
451         os.unlink(samdb_path)
452
453     samdb = SamDB(samdb_path, session_info=session_info, 
454                   credentials=credentials, lp=lp)
455
456     #Add modules to the list to activate them by default
457     #beware often order is important
458     #
459     # Some Known ordering constraints:
460     # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
461     # - objectclass must be before password_hash, because password_hash checks
462     #   that the objectclass is of type person (filled in by objectclass
463     #   module when expanding the objectclass list)
464     # - partition must be last
465     # - each partition has its own module list then
466     modules_list = ["rootdse",
467                     "paged_results",
468                     "ranged_results",
469                     "anr",
470                     "server_sort",
471                     "extended_dn",
472                     "asq",
473                     "rdn_name",
474                     "objectclass",
475                     "samldb",
476                     "kludge_acl",
477                     "operational"]
478     tdb_modules_list = [
479                     "subtree_rename",
480                     "subtree_delete",
481                     "linked_attributes"]
482     modules_list2 = ["show_deleted",
483                     "partition"]
484  
485     domaindn_ldb = "users.ldb"
486     if ldap_backend is not None:
487         domaindn_ldb = ldap_backend
488     configdn_ldb = "configuration.ldb"
489     if ldap_backend is not None:
490         configdn_ldb = ldap_backend
491     schemadn_ldb = "schema.ldb"
492     if ldap_backend is not None:
493         schema_ldb = ldap_backend
494         schemadn_ldb = ldap_backend
495         
496     if ldap_backend_type == "fedora-ds":
497         backend_modules = ["nsuniqueid", "paged_searches"]
498         # We can handle linked attributes here, as we don't have directory-side subtree operations
499         tdb_modules_list = ["linked_attributes"]
500     elif ldap_backend_type == "openldap":
501         backend_modules = ["normalise", "entryuuid", "paged_searches"]
502         # OpenLDAP handles subtree renames, so we don't want to do any of these things
503         tdb_modules_list = None
504     elif serverrole == "domain controller":
505         backend_modules = ["repl_meta_data"]
506     else:
507         backend_modules = ["objectguid"]
508
509     if tdb_modules_list is None:
510         tdb_modules_list_as_string = ""
511     else:
512         tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
513         
514     samdb.transaction_start()
515     try:
516         setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
517                 "SCHEMADN": names.schemadn, 
518                 "SCHEMADN_LDB": schemadn_ldb,
519                 "SCHEMADN_MOD2": ",objectguid",
520                 "CONFIGDN": names.configdn,
521                 "CONFIGDN_LDB": configdn_ldb,
522                 "DOMAINDN": names.domaindn,
523                 "DOMAINDN_LDB": domaindn_ldb,
524                 "SCHEMADN_MOD": "schema_fsmo,instancetype",
525                 "CONFIGDN_MOD": "naming_fsmo,instancetype",
526                 "DOMAINDN_MOD": "pdc_fsmo,password_hash,instancetype",
527                 "MODULES_LIST": ",".join(modules_list),
528                 "TDB_MODULES_LIST": tdb_modules_list_as_string,
529                 "MODULES_LIST2": ",".join(modules_list2),
530                 "BACKEND_MOD": ",".join(backend_modules),
531         })
532
533     except:
534         samdb.transaction_cancel()
535         raise
536
537     samdb.transaction_commit()
538     
539     samdb = SamDB(samdb_path, session_info=session_info, 
540                   credentials=credentials, lp=lp)
541
542     samdb.transaction_start()
543     try:
544         message("Setting up sam.ldb attributes")
545         samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
546
547         message("Setting up sam.ldb rootDSE")
548         setup_samdb_rootdse(samdb, setup_path, names)
549
550         if erase:
551             message("Erasing data from partitions")
552             samdb.erase_partitions()
553
554     except:
555         samdb.transaction_cancel()
556         raise
557
558     samdb.transaction_commit()
559     
560     return samdb
561
562
563 def secretsdb_become_dc(secretsdb, setup_path, domain, realm, dnsdomain, 
564                         netbiosname, domainsid, keytab_path, samdb_url, 
565                         dns_keytab_path, dnspass, machinepass):
566     """Add DC-specific bits to a secrets database.
567     
568     :param secretsdb: Ldb Handle to the secrets database
569     :param setup_path: Setup path function
570     :param machinepass: Machine password
571     """
572     setup_ldb(secretsdb, setup_path("secrets_dc.ldif"), { 
573             "MACHINEPASS_B64": b64encode(machinepass),
574             "DOMAIN": domain,
575             "REALM": realm,
576             "DNSDOMAIN": dnsdomain,
577             "DOMAINSID": str(domainsid),
578             "SECRETS_KEYTAB": keytab_path,
579             "NETBIOSNAME": netbiosname,
580             "SAM_LDB": samdb_url,
581             "DNS_KEYTAB": dns_keytab_path,
582             "DNSPASS_B64": b64encode(dnspass),
583             })
584
585
586 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
587     """Setup the secrets database.
588
589     :param path: Path to the secrets database.
590     :param setup_path: Get the path to a setup file.
591     :param session_info: Session info.
592     :param credentials: Credentials
593     :param lp: Loadparm context
594     :return: LDB handle for the created secrets database
595     """
596     if os.path.exists(path):
597         os.unlink(path)
598     secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
599                       lp=lp)
600     secrets_ldb.erase()
601     secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
602     secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
603                       lp=lp)
604     secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
605     return secrets_ldb
606
607
608 def setup_templatesdb(path, setup_path, session_info, credentials, lp):
609     """Setup the templates database.
610
611     :param path: Path to the database.
612     :param setup_path: Function for obtaining the path to setup files.
613     :param session_info: Session info
614     :param credentials: Credentials
615     :param lp: Loadparm context
616     """
617     templates_ldb = SamDB(path, session_info=session_info,
618                           credentials=credentials, lp=lp)
619     templates_ldb.erase()
620     templates_ldb.load_ldif_file_add(setup_path("provision_templates.ldif"))
621
622
623 def setup_registry(path, setup_path, session_info, credentials, lp):
624     """Setup the registry.
625     
626     :param path: Path to the registry database
627     :param setup_path: Function that returns the path to a setup.
628     :param session_info: Session information
629     :param credentials: Credentials
630     :param lp: Loadparm context
631     """
632     reg = registry.Registry()
633     hive = registry.open_ldb(path, session_info=session_info, 
634                          credentials=credentials, lp_ctx=lp)
635     reg.mount_hive(hive, "HKEY_LOCAL_MACHINE")
636     provision_reg = setup_path("provision.reg")
637     assert os.path.exists(provision_reg)
638     reg.diff_apply(provision_reg)
639
640 def setup_idmapdb(path, setup_path, session_info, credentials, lp):
641     """Setup the idmap database.
642
643     :param path: path to the idmap database
644     :param setup_path: Function that returns a path to a setup file
645     :param session_info: Session information
646     :param credentials: Credentials
647     :param lp: Loadparm context
648     """
649     if os.path.exists(path):
650         os.unlink(path)
651
652     idmap_ldb = IDmapDB(path, session_info=session_info,
653                         credentials=credentials, lp=lp)
654
655     idmap_ldb.erase()
656     idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
657     return idmap_ldb
658
659 def setup_samdb_rootdse(samdb, setup_path, names):
660     """Setup the SamDB rootdse.
661
662     :param samdb: Sam Database handle
663     :param setup_path: Obtain setup path
664     """
665     setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
666         "SCHEMADN": names.schemadn, 
667         "NETBIOSNAME": names.netbiosname,
668         "DNSDOMAIN": names.dnsdomain,
669         "REALM": names.realm,
670         "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
671         "DOMAINDN": names.domaindn,
672         "ROOTDN": names.rootdn,
673         "CONFIGDN": names.configdn,
674         "SERVERDN": names.serverdn,
675         })
676         
677
678 def setup_self_join(samdb, names,
679                     machinepass, dnspass, 
680                     domainsid, invocationid, setup_path,
681                     policyguid):
682     """Join a host to its own domain."""
683     setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), { 
684               "CONFIGDN": names.configdn, 
685               "SCHEMADN": names.schemadn,
686               "DOMAINDN": names.domaindn,
687               "INVOCATIONID": invocationid,
688               "NETBIOSNAME": names.netbiosname,
689               "DEFAULTSITE": names.sitename,
690               "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
691               "MACHINEPASS_B64": b64encode(machinepass),
692               "DNSPASS_B64": b64encode(dnspass),
693               "REALM": names.realm,
694               "DOMAIN": names.domain,
695               "DNSDOMAIN": names.dnsdomain})
696     setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), { 
697               "POLICYGUID": policyguid,
698               "DNSDOMAIN": names.dnsdomain,
699               "DOMAINSID": str(domainsid),
700               "DOMAINDN": names.domaindn})
701
702
703 def setup_samdb(path, setup_path, session_info, credentials, lp, 
704                 names, message, 
705                 domainsid, aci, domainguid, policyguid, 
706                 fill, adminpass, krbtgtpass, 
707                 machinepass, invocationid, dnspass,
708                 serverrole, ldap_backend=None, 
709                 ldap_backend_type=None):
710     """Setup a complete SAM Database.
711     
712     :note: This will wipe the main SAM database file!
713     """
714
715     erase = (fill != FILL_DRS)
716
717     # Also wipes the database
718     setup_samdb_partitions(path, setup_path, message=message, lp=lp,
719                            credentials=credentials, session_info=session_info,
720                            names=names, 
721                            ldap_backend=ldap_backend, serverrole=serverrole,
722                            ldap_backend_type=ldap_backend_type, erase=erase)
723
724     samdb = SamDB(path, session_info=session_info, 
725                   credentials=credentials, lp=lp)
726
727     if fill == FILL_DRS:
728        # We want to finish here, but setup the index before we do so
729         message("Setting up sam.ldb index")
730         samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
731         return samdb
732
733     message("Pre-loading the Samba 4 and AD schema")
734     samdb = SamDB(path, session_info=session_info, 
735                   credentials=credentials, lp=lp)
736     samdb.set_domain_sid(domainsid)
737     if serverrole == "domain controller":
738         samdb.set_invocation_id(invocationid)
739
740     load_schema(setup_path, samdb, names.schemadn, names.netbiosname, 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, realm=None, 
879               rootdn=None, domaindn=None, schemadn=None, configdn=None, 
880               serverdn=None,
881               domain=None, hostname=None, hostip=None, hostip6=None, 
882               domainsid=None, adminpass=None, krbtgtpass=None, domainguid=None, 
883               policyguid=None, invocationid=None, machinepass=None, 
884               dnspass=None, root=None, nobody=None, nogroup=None, users=None, 
885               wheel=None, backup=None, aci=None, serverrole=None, 
886               ldap_backend=None, ldap_backend_type=None, 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     if root is None:
911         root_uid = findnss(pwd.getpwnam, ["root"])[2]
912     else:
913         root_uid = findnss(pwd.getpwnam, [root])[2]
914     if nobody is None:
915         nobody_uid = findnss(pwd.getpwnam, ["nobody"])[2]
916     else:
917         nobody_uid = findnss(pwd.getpwnam, [nobody])[2]
918     if users is None:
919         users_gid = findnss(grp.getgrnam, ["users"])[2]
920     else:
921         users_gid = findnss(grp.getgrnam, [users])[2]
922     if wheel is None:
923         wheel_gid = findnss(grp.getgrnam, ["wheel", "adm"])[2]
924     else:
925         wheel_gid = findnss(grp.getgrnam, [wheel])[2]
926     if aci is None:
927         aci = "# no aci for local ldb"
928
929     lp = load_or_make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, targetdir)
930
931     names = guess_names(lp=lp, hostname=hostname, domain=domain, 
932                         dnsdomain=realm, serverrole=serverrole, sitename=sitename,
933                         rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn,
934                         serverdn=serverdn)
935
936     paths = provision_paths_from_lp(lp, names.dnsdomain)
937
938     if hostip is None:
939         hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
940
941     if hostip6 is None:
942         try:
943             hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
944         except socket.gaierror: pass
945
946     if serverrole is None:
947         serverrole = lp.get("server role")
948
949     assert serverrole in ("domain controller", "member server", "standalone")
950     if invocationid is None and serverrole == "domain controller":
951         invocationid = uuid.random()
952
953     if not os.path.exists(paths.private_dir):
954         os.mkdir(paths.private_dir)
955
956     ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
957     
958     if ldap_backend is not None:
959         if ldap_backend == "ldapi":
960             # provision-backend will set this path suggested slapd command line / fedorads.inf
961             ldap_backend = "ldapi://" % urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
962              
963     # only install a new shares config db if there is none
964     if not os.path.exists(paths.shareconf):
965         message("Setting up share.ldb")
966         share_ldb = Ldb(paths.shareconf, session_info=session_info, 
967                         credentials=credentials, lp=lp)
968         share_ldb.load_ldif_file_add(setup_path("share.ldif"))
969
970      
971     message("Setting up secrets.ldb")
972     secrets_ldb = setup_secretsdb(paths.secrets, setup_path, 
973                                   session_info=session_info, 
974                                   credentials=credentials, lp=lp)
975
976     message("Setting up the registry")
977     setup_registry(paths.hklm, setup_path, session_info, 
978                    credentials=credentials, lp=lp)
979
980     message("Setting up templates db")
981     setup_templatesdb(paths.templates, setup_path, session_info=session_info, 
982                       credentials=credentials, lp=lp)
983
984     message("Setting up idmap db")
985     idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
986                           credentials=credentials, lp=lp)
987
988     samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info, 
989                         credentials=credentials, lp=lp, names=names,
990                         message=message, 
991                         domainsid=domainsid, 
992                         aci=aci, domainguid=domainguid, policyguid=policyguid, 
993                         fill=samdb_fill, 
994                         adminpass=adminpass, krbtgtpass=krbtgtpass,
995                         invocationid=invocationid, 
996                         machinepass=machinepass, dnspass=dnspass,
997                         serverrole=serverrole, ldap_backend=ldap_backend, 
998                         ldap_backend_type=ldap_backend_type)
999
1000     if lp.get("server role") == "domain controller":
1001        policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies", 
1002                                   "{" + policyguid + "}")
1003        os.makedirs(policy_path, 0755)
1004        os.makedirs(os.path.join(policy_path, "Machine"), 0755)
1005        os.makedirs(os.path.join(policy_path, "User"), 0755)
1006        if not os.path.isdir(paths.netlogon):
1007             os.makedirs(paths.netlogon, 0755)
1008        secrets_ldb = Ldb(paths.secrets, session_info=session_info, 
1009                          credentials=credentials, lp=lp)
1010        secretsdb_become_dc(secrets_ldb, setup_path, domain=domain, realm=names.realm,
1011                            netbiosname=names.netbiosname, domainsid=domainsid, 
1012                            keytab_path=paths.keytab, samdb_url=paths.samdb, 
1013                            dns_keytab_path=paths.dns_keytab, dnspass=dnspass, 
1014                            machinepass=machinepass, dnsdomain=names.dnsdomain)
1015
1016     if samdb_fill == FILL_FULL:
1017         setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1018                             root_uid=root_uid, nobody_uid=nobody_uid,
1019                             users_gid=users_gid, wheel_gid=wheel_gid)
1020
1021         message("Setting up sam.ldb rootDSE marking as synchronized")
1022         setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1023
1024         # Only make a zone file on the first DC, it should be replicated with DNS replication
1025         if serverrole == "domain controller":
1026             samdb = SamDB(paths.samdb, session_info=session_info, 
1027                       credentials=credentials, lp=lp)
1028
1029             domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1030             assert isinstance(domainguid, str)
1031             hostguid = samdb.searchone(basedn=domaindn, attribute="objectGUID",
1032                                        expression="(&(objectClass=computer)(cn=%s))" % names.hostname,
1033                                        scope=SCOPE_SUBTREE)
1034             assert isinstance(hostguid, str)
1035             
1036             create_zone_file(paths.dns, setup_path, samdb, 
1037                              hostname=names.hostname, hostip=hostip,
1038                              hostip6=hostip6, dnsdomain=names.dnsdomain,
1039                              domaindn=names.domaindn, dnspass=dnspass, realm=names.realm, 
1040                              domainguid=domainguid, hostguid=hostguid)
1041             message("Please install the zone located in %s into your DNS server" % paths.dns)
1042             
1043     create_phpldapadmin_config(paths.phpldapadminconfig, setup_path, 
1044                                ldapi_url)
1045
1046     message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1047
1048     message("Once the above files are installed, your server will be ready to use")
1049     message("Server Type:    %s" % serverrole)
1050     message("Hostname:       %s" % names.hostname)
1051     message("NetBIOS Domain: %s" % names.domain)
1052     message("DNS Domain:     %s" % names.dnsdomain)
1053     message("DOMAIN SID:     %s" % str(domainsid))
1054     message("Admin password: %s" % adminpass)
1055
1056     result = ProvisionResult()
1057     result.domaindn = domaindn
1058     result.paths = paths
1059     result.lp = lp
1060     result.samdb = samdb
1061     return result
1062
1063 def provision_become_dc(setup_dir=None,
1064                         smbconf=None, targetdir=None, realm=None, 
1065                         rootdn=None, domaindn=None, schemadn=None, configdn=None,
1066                         serverdn=None,
1067                         domain=None, hostname=None, domainsid=None, 
1068                         adminpass=None, krbtgtpass=None, domainguid=None, 
1069                         policyguid=None, invocationid=None, machinepass=None, 
1070                         dnspass=None, root=None, nobody=None, nogroup=None, users=None, 
1071                         wheel=None, backup=None, aci=None, serverrole=None, 
1072                         ldap_backend=None, ldap_backend_type=None, sitename=None):
1073
1074     def message(text):
1075         """print a message if quiet is not set."""
1076         print text
1077
1078     provision(setup_dir, message, system_session(), None,
1079               smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS, realm=realm, 
1080               rootdn=rootdn, domaindn=domaindn, schemadn=schemadn, configdn=configdn, serverdn=serverdn,
1081               domain=domain, hostname=hostname, hostip="127.0.0.1", domainsid=domainsid, machinepass=machinepass, serverrole="domain controller", sitename=sitename);
1082     
1083
1084 def setup_db_config(setup_path, file, dbdir):
1085     if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
1086         os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700);
1087     if not os.path.isdir(os.path.join(dbdir, "tmp")):
1088         os.makedirs(os.path.join(dbdir, "tmp"), 0700);
1089     
1090     setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
1091                {"LDAPDBDIR": dbdir})
1092     
1093
1094
1095 def provision_backend(setup_dir=None, message=None,
1096                       smbconf=None, targetdir=None, realm=None, 
1097                       rootdn=None, domaindn=None, schemadn=None, configdn=None,
1098                       domain=None, hostname=None, adminpass=None, root=None, serverrole=None, 
1099                       ldap_backend_type=None):
1100
1101     def setup_path(file):
1102         return os.path.join(setup_dir, file)
1103
1104     if hostname is None:
1105         hostname = socket.gethostname().split(".")[0].lower()
1106
1107     if root is None:
1108         root = findnss(pwd.getpwnam, ["root"])[0]
1109
1110     lp = load_or_make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, targetdir)
1111
1112     names = guess_names(lp=lp, hostname=hostname, domain=domain, 
1113                         dnsdomain=realm, serverrole=serverrole, 
1114                         rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn)
1115
1116     paths = provision_paths_from_lp(lp, names.dnsdomain)
1117
1118     if not os.path.isdir(paths.ldapdir):
1119         os.makedirs(paths.ldapdir)
1120     schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
1121     try:
1122         os.unlink(schemadb_path)
1123     except:
1124         pass
1125
1126     schemadb = Ldb(schemadb_path, lp=lp)
1127  
1128     setup_add_ldif(schemadb, setup_path("provision_schema_basedn.ldif"), 
1129                    {"SCHEMADN": names.schemadn,
1130                     "ACI": "#",
1131                     "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb"
1132                     })
1133     setup_modify_ldif(schemadb, 
1134                       setup_path("provision_schema_basedn_modify.ldif"), \
1135                           {"SCHEMADN": names.schemadn,
1136                            "NETBIOSNAME": names.netbiosname,
1137                            "DEFAULTSITE": DEFAULTSITE,
1138                            "CONFIGDN": names.configdn,
1139                            })
1140     
1141     setup_add_ldif(schemadb, setup_path("schema_samba4.ldif"), 
1142                    {"SCHEMADN": names.schemadn })
1143     setup_add_ldif(schemadb, setup_path("schema.ldif"), 
1144                    {"SCHEMADN": names.schemadn})
1145
1146     if ldap_backend_type == "fedora-ds":
1147         setup_file(setup_path("fedora-ds.inf"), paths.fedoradsinf, 
1148                    {"ROOT": root,
1149                     "HOSTNAME": hostname,
1150                     "DNSDOMAIN": names.dnsdomain,
1151                     "LDAPDIR": paths.ldapdir,
1152                     "DOMAINDN": names.domaindn,
1153                     "LDAPMANAGERDN": names.ldapmanagerdn,
1154                     "LDAPMANAGERPASS": adminpass, 
1155                     "SERVERPORT": ""})
1156         
1157         setup_file(setup_path("fedora-partitions.ldif"), paths.fedoradspartitions, 
1158                    {"CONFIGDN": names.configdn,
1159                     "SCHEMADN": names.schemadn,
1160                     })
1161         
1162         setup_file(setup_path("fedora-partitions.ldif"), paths.fedoradspartitions, 
1163                    {"CONFIGDN": names.configdn,
1164                     "SCHEMADN": names.schemadn,
1165                     })
1166         mapping = "schema-map-fedora-ds-1.0"
1167         backend_schema = "99_ad.ldif"
1168     elif ldap_backend_type == "openldap":
1169         attrs = ["linkID", "lDAPDisplayName"]
1170         res = schemadb.search(expression="(&(&(linkID=*)(!(linkID:1.2.840.113556.1.4.803:=1)))(objectclass=attributeSchema))", base=names.schemadn, scope=SCOPE_SUBTREE, attrs=attrs);
1171
1172         memberof_config = "# Generated from schema in " + schemadb_path + "\n";
1173         refint_attributes = "";
1174         for i in range (0, len(res)):
1175             linkid = res[i]["linkID"][0]
1176             linkid = str(int(linkid) + 1)
1177             expression = "(&(objectclass=attributeSchema)(linkID=" + (linkid) + "))"
1178             target = schemadb.searchone(basedn=names.schemadn, 
1179                                         expression=expression, 
1180                                         attribute="lDAPDisplayName", 
1181                                         scope=SCOPE_SUBTREE);
1182             if target is not None:
1183                 refint_attributes = refint_attributes + " " + target + " " + res[i]["lDAPDisplayName"][0];
1184                 memberof_config = memberof_config + """overlay memberof
1185 memberof-dangling error
1186 memberof-refint TRUE
1187 memberof-group-oc top
1188 memberof-member-ad """ + res[i]["lDAPDisplayName"][0] + """
1189 memberof-memberof-ad """ + target + """
1190 memberof-dangling-error 32
1191
1192 """;
1193
1194         memberof_config = memberof_config + """
1195 overlay refint
1196 refint_attributes""" + refint_attributes + "\n";
1197         
1198         setup_file(setup_path("slapd.conf"), paths.slapdconf,
1199                    {"DNSDOMAIN": names.dnsdomain,
1200                     "LDAPDIR": paths.ldapdir,
1201                     "DOMAINDN": names.domaindn,
1202                     "CONFIGDN": names.configdn,
1203                     "SCHEMADN": names.schemadn,
1204                     "LDAPMANAGERDN": names.ldapmanagerdn,
1205                     "LDAPMANAGERPASS": adminpass,
1206                     "MEMBEROF_CONFIG": memberof_config})
1207         setup_file(setup_path("modules.conf"), paths.modulesconf,
1208                    {"REALM": names.realm})
1209         
1210         setup_db_config(setup_path, file, os.path.join(paths.ldapdir, "db", "user"))
1211         setup_db_config(setup_path, file, os.path.join(paths.ldapdir, "db", "config"))
1212         setup_db_config(setup_path, file, os.path.join(paths.ldapdir, "db", "schema"))
1213         mapping = "schema-map-openldap-2.3"
1214         backend_schema = "backend-schema.schema"
1215         
1216
1217         ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
1218         message("Start slapd with: slapd -f " + paths.ldapdir + "/slapd.conf -h " + ldapi_uri)
1219                 
1220
1221     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);
1222
1223     os.system(schema_command)
1224
1225
1226
1227 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1228     """Create a PHP LDAP admin configuration file.
1229
1230     :param path: Path to write the configuration to.
1231     :param setup_path: Function to generate setup paths.
1232     """
1233     setup_file(setup_path("phpldapadmin-config.php"), path, 
1234             {"S4_LDAPI_URI": ldapi_uri})
1235
1236
1237 def create_zone_file(path, setup_path, samdb, dnsdomain, domaindn, 
1238                   hostip, hostip6, hostname, dnspass, realm, domainguid, hostguid):
1239     """Write out a DNS zone file, from the info in the current database.
1240     
1241     :param path: Path of the new file.
1242     :param setup_path": Setup path function.
1243     :param samdb: SamDB object
1244     :param dnsdomain: DNS Domain name
1245     :param domaindn: DN of the Domain
1246     :param hostip: Local IPv4 IP
1247     :param hostip6: Local IPv6 IP
1248     :param hostname: Local hostname
1249     :param dnspass: Password for DNS
1250     :param realm: Realm name
1251     :param domainguid: GUID of the domain.
1252     :param hostguid: GUID of the host.
1253     """
1254     assert isinstance(domainguid, str)
1255
1256     hostip6_base_line = ""
1257     hostip6_host_line = ""
1258
1259     if hostip6 is not None:
1260         hostip6_base_line = "                   IN AAAA " + hostip6
1261         hostip6_host_line = hostname + "                IN AAAA " + hostip6
1262
1263     setup_file(setup_path("provision.zone"), path, {
1264             "DNSPASS_B64": b64encode(dnspass),
1265             "HOSTNAME": hostname,
1266             "DNSDOMAIN": dnsdomain,
1267             "REALM": realm,
1268             "HOSTIP": hostip,
1269             "DOMAINGUID": domainguid,
1270             "DATESTRING": time.strftime("%Y%m%d%H"),
1271             "DEFAULTSITE": DEFAULTSITE,
1272             "HOSTGUID": hostguid,
1273             "HOSTIP6_BASE_LINE": hostip6_base_line,
1274             "HOSTIP6_HOST_LINE": hostip6_host_line,
1275         })
1276
1277 def load_schema(setup_path, samdb, schemadn, netbiosname, configdn, sitename):
1278     """Load schema for the SamDB.
1279     
1280     :param samdb: Load a schema into a SamDB.
1281     :param setup_path: Setup path function.
1282     :param schemadn: DN of the schema
1283     :param netbiosname: NetBIOS name of the host.
1284     :param configdn: DN of the configuration
1285     """
1286     schema_data = open(setup_path("schema.ldif"), 'r').read()
1287     schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
1288     schema_data = substitute_var(schema_data, {"SCHEMADN": schemadn})
1289     head_data = open(setup_path("provision_schema_basedn_modify.ldif"), 'r').read()
1290     head_data = substitute_var(head_data, {
1291                     "SCHEMADN": schemadn,
1292                     "NETBIOSNAME": netbiosname,
1293                     "CONFIGDN": configdn,
1294                     "DEFAULTSITE":sitename 
1295     })
1296     samdb.attach_schema_from_ldif(head_data, schema_data)
1297