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