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