provision: Initialize uninitialized variables if "targetdir" is not
[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 = "private_dir = " + default_lp.get("private dir")
378             lockdir_line = "lock dir = " + default_lp.get("lock dir")
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, hostguid=None):
699     """Join a host to its own domain."""
700     if hostguid is not None:
701         hostguid_add = "objectGUID: %s" % hostguid
702     else:
703         hostguid_add = ""
704
705     setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), { 
706               "CONFIGDN": names.configdn, 
707               "SCHEMADN": names.schemadn,
708               "DOMAINDN": names.domaindn,
709               "INVOCATIONID": invocationid,
710               "NETBIOSNAME": names.netbiosname,
711               "DEFAULTSITE": names.sitename,
712               "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
713               "MACHINEPASS_B64": b64encode(machinepass),
714               "DNSPASS_B64": b64encode(dnspass),
715               "REALM": names.realm,
716               "DOMAIN": names.domain,
717               "HOSTGUID_ADD": hostguid_add,
718               "DNSDOMAIN": names.dnsdomain})
719     setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), { 
720               "POLICYGUID": policyguid,
721               "DNSDOMAIN": names.dnsdomain,
722               "DOMAINSID": str(domainsid),
723               "DOMAINDN": names.domaindn})
724
725
726 def setup_samdb(path, setup_path, session_info, credentials, lp, 
727                 names, message, 
728                 domainsid, aci, domainguid, policyguid, 
729                 fill, adminpass, krbtgtpass, 
730                 machinepass, hostguid, invocationid, dnspass,
731                 serverrole, ldap_backend=None, 
732                 ldap_backend_type=None):
733     """Setup a complete SAM Database.
734     
735     :note: This will wipe the main SAM database file!
736     """
737
738     erase = (fill != FILL_DRS)
739
740     # Also wipes the database
741     setup_samdb_partitions(path, setup_path, message=message, lp=lp,
742                            credentials=credentials, session_info=session_info,
743                            names=names, 
744                            ldap_backend=ldap_backend, serverrole=serverrole,
745                            ldap_backend_type=ldap_backend_type, erase=erase)
746
747     samdb = SamDB(path, session_info=session_info, 
748                   credentials=credentials, lp=lp)
749
750     if fill == FILL_DRS:
751        # We want to finish here, but setup the index before we do so
752         message("Setting up sam.ldb index")
753         samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
754         return samdb
755
756     message("Pre-loading the Samba 4 and AD schema")
757     samdb = SamDB(path, session_info=session_info, 
758                   credentials=credentials, lp=lp)
759     samdb.set_domain_sid(domainsid)
760     if serverrole == "domain controller":
761         samdb.set_invocation_id(invocationid)
762
763     load_schema(setup_path, samdb, names.schemadn, names.netbiosname, names.configdn, names.sitename)
764
765     samdb.transaction_start()
766         
767     try:
768         message("Adding DomainDN: %s (permitted to fail)" % names.domaindn)
769         if serverrole == "domain controller":
770             domain_oc = "domainDNS"
771         else:
772             domain_oc = "samba4LocalDomain"
773
774         setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
775             "DOMAINDN": names.domaindn,
776             "ACI": aci,
777             "DOMAIN_OC": domain_oc
778             })
779
780         message("Modifying DomainDN: " + names.domaindn + "")
781         if domainguid is not None:
782             domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
783         else:
784             domainguid_mod = ""
785
786         setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
787             "LDAPTIME": timestring(int(time.time())),
788             "DOMAINSID": str(domainsid),
789             "SCHEMADN": names.schemadn, 
790             "NETBIOSNAME": names.netbiosname,
791             "DEFAULTSITE": names.sitename,
792             "CONFIGDN": names.configdn,
793             "POLICYGUID": policyguid,
794             "DOMAINDN": names.domaindn,
795             "DOMAINGUID_MOD": domainguid_mod,
796             })
797
798         message("Adding configuration container (permitted to fail)")
799         setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
800             "CONFIGDN": names.configdn, 
801             "ACI": aci,
802             "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb",
803             })
804         message("Modifying configuration container")
805         setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
806             "CONFIGDN": names.configdn, 
807             "SCHEMADN": names.schemadn,
808             })
809
810         message("Adding schema container (permitted to fail)")
811         setup_add_ldif(samdb, setup_path("provision_schema_basedn.ldif"), {
812             "SCHEMADN": names.schemadn,
813             "ACI": aci,
814             "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb"
815             })
816         message("Modifying schema container")
817         setup_modify_ldif(samdb, 
818             setup_path("provision_schema_basedn_modify.ldif"), {
819             "SCHEMADN": names.schemadn,
820             "NETBIOSNAME": names.netbiosname,
821             "DEFAULTSITE": names.sitename,
822             "CONFIGDN": names.configdn,
823             })
824
825         message("Setting up sam.ldb Samba4 schema")
826         setup_add_ldif(samdb, setup_path("schema_samba4.ldif"), 
827                        {"SCHEMADN": names.schemadn })
828         message("Setting up sam.ldb AD schema")
829         setup_add_ldif(samdb, setup_path("schema.ldif"), 
830                        {"SCHEMADN": names.schemadn})
831
832         message("Setting up sam.ldb configuration data")
833         setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
834             "CONFIGDN": names.configdn,
835             "NETBIOSNAME": names.netbiosname,
836             "DEFAULTSITE": names.sitename,
837             "DNSDOMAIN": names.dnsdomain,
838             "DOMAIN": names.domain,
839             "SCHEMADN": names.schemadn,
840             "DOMAINDN": names.domaindn,
841             })
842
843         message("Setting up display specifiers")
844         setup_add_ldif(samdb, setup_path("display_specifiers.ldif"), 
845                        {"CONFIGDN": names.configdn})
846
847         message("Adding users container (permitted to fail)")
848         setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
849                 "DOMAINDN": names.domaindn})
850         message("Modifying users container")
851         setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
852                 "DOMAINDN": names.domaindn})
853         message("Adding computers container (permitted to fail)")
854         setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
855                 "DOMAINDN": names.domaindn})
856         message("Modifying computers container")
857         setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
858                 "DOMAINDN": names.domaindn})
859         message("Setting up sam.ldb data")
860         setup_add_ldif(samdb, setup_path("provision.ldif"), {
861             "DOMAINDN": names.domaindn,
862             "NETBIOSNAME": names.netbiosname,
863             "DEFAULTSITE": names.sitename,
864             "CONFIGDN": names.configdn,
865             })
866
867         if fill == FILL_FULL:
868             message("Setting up sam.ldb users and groups")
869             setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
870                 "DOMAINDN": names.domaindn,
871                 "DOMAINSID": str(domainsid),
872                 "CONFIGDN": names.configdn,
873                 "ADMINPASS_B64": b64encode(adminpass),
874                 "KRBTGTPASS_B64": b64encode(krbtgtpass),
875                 })
876
877             if serverrole == "domain controller":
878                 message("Setting up self join")
879                 setup_self_join(samdb, names=names, invocationid=invocationid, 
880                                 dnspass=dnspass,  
881                                 machinepass=machinepass, 
882                                 domainsid=domainsid, policyguid=policyguid,
883                                 hostguid=hostguid, 
884                                 setup_path=setup_path)
885
886     #We want to setup the index last, as adds are faster unindexed
887         message("Setting up sam.ldb index")
888         samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
889     except:
890         samdb.transaction_cancel()
891         raise
892
893     samdb.transaction_commit()
894     return samdb
895
896
897 FILL_FULL = "FULL"
898 FILL_NT4SYNC = "NT4SYNC"
899 FILL_DRS = "DRS"
900
901 def provision(setup_dir, message, session_info, 
902               credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL, realm=None, 
903               rootdn=None, domaindn=None, schemadn=None, configdn=None,
904               domain=None, hostname=None, hostip=None, domainsid=None, 
905               hostguid=None, adminpass=None, krbtgtpass=None, domainguid=None, 
906               policyguid=None, invocationid=None, machinepass=None, 
907               dnspass=None, root=None, nobody=None, nogroup=None, users=None, 
908               wheel=None, backup=None, aci=None, serverrole=None, 
909               ldap_backend=None, ldap_backend_type=None, sitename=None):
910     """Provision samba4
911     
912     :note: caution, this wipes all existing data!
913     """
914
915     def setup_path(file):
916         return os.path.join(setup_dir, file)
917
918     if domainsid is None:
919         domainsid = security.random_sid()
920     else:
921         domainsid = security.Sid(domainsid)
922
923     if policyguid is None:
924         policyguid = uuid.random()
925     if adminpass is None:
926         adminpass = misc.random_password(12)
927     if krbtgtpass is None:
928         krbtgtpass = misc.random_password(12)
929     if machinepass is None:
930         machinepass  = misc.random_password(12)
931     if dnspass is None:
932         dnspass = misc.random_password(12)
933     if root is None:
934         root = findnss(pwd.getpwnam, ["root"])[0]
935     if nobody is None:
936         nobody = findnss(pwd.getpwnam, ["nobody"])[0]
937     if nogroup is None:
938         nogroup = findnss(grp.getgrnam, ["nogroup", "nobody"])[0]
939     if users is None:
940         users = findnss(grp.getgrnam, ["users", "guest", "other", "unknown", 
941                         "usr"])[0]
942     if wheel is None:
943         wheel = findnss(grp.getgrnam, ["wheel", "root", "staff", "adm"])[0]
944     if backup is None:
945         backup = findnss(grp.getgrnam, ["backup", "wheel", "root", "staff"])[0]
946     if aci is None:
947         aci = "# no aci for local ldb"
948
949     lp = load_or_make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, targetdir)
950
951     names = guess_names(lp=lp, hostname=hostname, domain=domain, 
952                         dnsdomain=realm, serverrole=serverrole, sitename=sitename,
953                         rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn)
954
955     paths = provision_paths_from_lp(lp, names.dnsdomain)
956
957     if hostip is None:
958         hostip = gethostbyname(names.hostname)
959
960     if serverrole is None:
961         serverrole = lp.get("server role")
962
963     assert serverrole in ("domain controller", "member server", "standalone")
964     if invocationid is None and serverrole == "domain controller":
965         invocationid = uuid.random()
966
967     if not os.path.exists(paths.private_dir):
968         os.mkdir(paths.private_dir)
969
970     ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
971     
972     if ldap_backend is not None:
973         if ldap_backend == "ldapi":
974             # provision-backend will set this path suggested slapd command line / fedorads.inf
975             ldap_backend = "ldapi://" % urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
976              
977     message("set DOMAIN SID: %s" % str(domainsid))
978     message("Provisioning for %s in realm %s" % (names.domain, realm))
979     message("Using administrator password: %s" % adminpass)
980
981     # only install a new shares config db if there is none
982     if not os.path.exists(paths.shareconf):
983         message("Setting up share.ldb")
984         share_ldb = Ldb(paths.shareconf, session_info=session_info, 
985                         credentials=credentials, lp=lp)
986         share_ldb.load_ldif_file_add(setup_path("share.ldif"))
987
988      
989     message("Setting up secrets.ldb")
990     secrets_ldb = setup_secretsdb(paths.secrets, setup_path, 
991                                   session_info=session_info, 
992                                   credentials=credentials, lp=lp)
993
994     message("Setting up the registry")
995     setup_registry(paths.hklm, setup_path, session_info, 
996                    credentials=credentials, lp=lp)
997
998     message("Setting up templates db")
999     setup_templatesdb(paths.templates, setup_path, session_info=session_info, 
1000                       credentials=credentials, lp=lp)
1001
1002     message("Setting up idmap db")
1003     setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1004                   credentials=credentials, lp=lp)
1005
1006     samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info, 
1007                         credentials=credentials, lp=lp, names=names,
1008                         message=message, 
1009                         domainsid=domainsid, 
1010                         aci=aci, domainguid=domainguid, policyguid=policyguid, 
1011                         fill=samdb_fill, 
1012                         adminpass=adminpass, krbtgtpass=krbtgtpass,
1013                         hostguid=hostguid, invocationid=invocationid, 
1014                         machinepass=machinepass, dnspass=dnspass,
1015                         serverrole=serverrole, ldap_backend=ldap_backend, 
1016                         ldap_backend_type=ldap_backend_type)
1017
1018     if lp.get("server role") == "domain controller":
1019        policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies", 
1020                                   "{" + policyguid + "}")
1021        os.makedirs(policy_path, 0755)
1022        os.makedirs(os.path.join(policy_path, "Machine"), 0755)
1023        os.makedirs(os.path.join(policy_path, "User"), 0755)
1024        if not os.path.isdir(paths.netlogon):
1025             os.makedirs(paths.netlogon, 0755)
1026        secrets_ldb = Ldb(paths.secrets, session_info=session_info, 
1027                          credentials=credentials, lp=lp)
1028        secretsdb_become_dc(secrets_ldb, setup_path, domain=domain, realm=names.realm,
1029                            netbiosname=names.netbiosname, domainsid=domainsid, 
1030                            keytab_path=paths.keytab, samdb_url=paths.samdb, 
1031                            dns_keytab_path=paths.dns_keytab, dnspass=dnspass, 
1032                            machinepass=machinepass, dnsdomain=names.dnsdomain)
1033
1034     if samdb_fill == FILL_FULL:
1035         setup_name_mappings(samdb, str(domainsid), names.domaindn, root=root, 
1036                             nobody=nobody, nogroup=nogroup, wheel=wheel, 
1037                             users=users, backup=backup)
1038    
1039         message("Setting up sam.ldb rootDSE marking as synchronized")
1040         setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1041
1042         # Only make a zone file on the first DC, it should be replicated with DNS replication
1043         if serverrole == "domain controller":
1044             samdb = SamDB(paths.samdb, session_info=session_info, 
1045                       credentials=credentials, lp=lp)
1046
1047             domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1048             assert isinstance(domainguid, str)
1049             hostguid = samdb.searchone(basedn=domaindn, attribute="objectGUID",
1050                                        expression="(&(objectClass=computer)(cn=%s))" % names.hostname,
1051                                        scope=SCOPE_SUBTREE)
1052             assert isinstance(hostguid, str)
1053             
1054             message("Setting up DNS zone: %s" % names.dnsdomain)
1055             create_zone_file(paths.dns, setup_path, samdb, 
1056                              hostname=names.hostname, hostip=hostip, dnsdomain=names.dnsdomain,
1057                              domaindn=names.domaindn, dnspass=dnspass, realm=names.realm, 
1058                              domainguid=domainguid, hostguid=hostguid)
1059             message("Please install the zone located in %s into your DNS server" % paths.dns)
1060             
1061     message("Setting up phpLDAPadmin configuration")
1062     create_phpldapadmin_config(paths.phpldapadminconfig, setup_path, 
1063                                ldapi_url)
1064
1065     message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1066
1067     result = ProvisionResult()
1068     result.domaindn = domaindn
1069     result.paths = paths
1070     result.lp = lp
1071     result.samdb = samdb
1072     return result
1073
1074 def provision_become_dc(setup_dir=None,
1075                         smbconf=None, targetdir=None, realm=None, 
1076                         rootdn=None, domaindn=None, schemadn=None, configdn=None,
1077                         domain=None, hostname=None, domainsid=None, 
1078                         hostguid=None, adminpass=None, krbtgtpass=None, domainguid=None, 
1079                         policyguid=None, invocationid=None, machinepass=None, 
1080                         dnspass=None, root=None, nobody=None, nogroup=None, users=None, 
1081                         wheel=None, backup=None, aci=None, serverrole=None, 
1082                         ldap_backend=None, ldap_backend_type=None, sitename=DEFAULTSITE):
1083
1084     def message(text):
1085         """print a message if quiet is not set."""
1086         print text
1087
1088     provision(setup_dir, message, system_session(), None,
1089               smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS, realm=realm, 
1090               rootdn=rootdn, domaindn=domaindn, schemadn=schemadn, configdn=configdn, 
1091               domain=domain, hostname=hostname, hostip="127.0.0.1", domainsid=domainsid, machinepass=machinepass, serverrole="domain controller", sitename=sitename);
1092     
1093
1094 def setup_db_config(setup_path, file, dbdir):
1095     if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
1096         os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700);
1097     if not os.path.isdir(os.path.join(dbdir, "tmp")):
1098         os.makedirs(os.path.join(dbdir, "tmp"), 0700);
1099     
1100     setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
1101                {"LDAPDBDIR": dbdir})
1102     
1103
1104
1105 def provision_backend(setup_dir=None, message=None,
1106                       smbconf=None, targetdir=None, realm=None, 
1107                       rootdn=None, domaindn=None, schemadn=None, configdn=None,
1108                       domain=None, hostname=None, adminpass=None, root=None, serverrole=None, 
1109                       ldap_backend_type=None):
1110
1111     def setup_path(file):
1112         return os.path.join(setup_dir, file)
1113
1114     if hostname is None:
1115         hostname = gethostname().split(".")[0].lower()
1116
1117     if root is None:
1118         root = findnss(pwd.getpwnam, ["root"])[0]
1119
1120     lp = load_or_make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, targetdir)
1121
1122     names = guess_names(lp=lp, hostname=hostname, domain=domain, 
1123                         dnsdomain=realm, serverrole=serverrole, 
1124                         rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn)
1125
1126     paths = provision_paths_from_lp(lp, names.dnsdomain)
1127
1128     if not os.path.isdir(paths.ldapdir):
1129         os.makedirs(paths.ldapdir)
1130     schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
1131     try:
1132         os.unlink(schemadb_path)
1133     except:
1134         pass
1135
1136     schemadb = Ldb(schemadb_path, lp=lp)
1137  
1138     setup_add_ldif(schemadb, setup_path("provision_schema_basedn.ldif"), 
1139                    {"SCHEMADN": names.schemadn,
1140                     "ACI": "#",
1141                     "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb"
1142                     })
1143     setup_modify_ldif(schemadb, 
1144                       setup_path("provision_schema_basedn_modify.ldif"), \
1145                           {"SCHEMADN": names.schemadn,
1146                            "NETBIOSNAME": names.netbiosname,
1147                            "DEFAULTSITE": DEFAULTSITE,
1148                            "CONFIGDN": names.configdn,
1149                            })
1150     
1151     setup_add_ldif(schemadb, setup_path("schema_samba4.ldif"), 
1152                    {"SCHEMADN": names.schemadn })
1153     setup_add_ldif(schemadb, setup_path("schema.ldif"), 
1154                    {"SCHEMADN": names.schemadn})
1155
1156     if ldap_backend_type == "fedora-ds":
1157         setup_file(setup_path("fedora-ds.inf"), paths.fedoradsinf, 
1158                    {"ROOT": root,
1159                     "HOSTNAME": hostname,
1160                     "DNSDOMAIN": names.dnsdomain,
1161                     "LDAPDIR": paths.ldapdir,
1162                     "DOMAINDN": names.domaindn,
1163                     "LDAPMANAGERDN": names.ldapmanagerdn,
1164                     "LDAPMANAGERPASS": adminpass, 
1165                     "SERVERPORT": ""})
1166         
1167         setup_file(setup_path("fedora-partitions.ldif"), paths.fedoradspartitions, 
1168                    {"CONFIGDN": names.configdn,
1169                     "SCHEMADN": names.schemadn,
1170                     })
1171         
1172         setup_file(setup_path("fedora-partitions.ldif"), paths.fedoradspartitions, 
1173                    {"CONFIGDN": names.configdn,
1174                     "SCHEMADN": names.schemadn,
1175                     })
1176         mapping = "schema-map-fedora-ds-1.0"
1177         backend_schema = "99_ad.ldif"
1178     elif ldap_backend_type == "openldap":
1179         attrs = ["linkID", "lDAPDisplayName"]
1180         res = schemadb.search(expression="(&(&(linkID=*)(!(linkID:1.2.840.113556.1.4.803:=1)))(objectclass=attributeSchema))", base=names.schemadn, scope=SCOPE_SUBTREE, attrs=attrs);
1181
1182         memberof_config = "# Generated from schema in " + schemadb_path + "\n";
1183         refint_attributes = "";
1184         for i in range (0, len(res)):
1185             linkid = res[i]["linkID"][0]
1186             linkid = str(int(linkid) + 1)
1187             expression = "(&(objectclass=attributeSchema)(linkID=" + (linkid) + "))"
1188             target = schemadb.searchone(basedn=names.schemadn, 
1189                                         expression=expression, 
1190                                         attribute="lDAPDisplayName", 
1191                                         scope=SCOPE_SUBTREE);
1192             if target is not None:
1193                 refint_attributes = refint_attributes + " " + target + " " + res[i]["lDAPDisplayName"][0];
1194                 memberof_config = memberof_config + """overlay memberof
1195 memberof-dangling error
1196 memberof-refint TRUE
1197 memberof-group-oc top
1198 memberof-member-ad """ + res[i]["lDAPDisplayName"][0] + """
1199 memberof-memberof-ad """ + target + """
1200 memberof-dangling-error 32
1201
1202 """;
1203
1204         memberof_config = memberof_config + """
1205 overlay refint
1206 refint_attributes""" + refint_attributes + "\n";
1207         
1208         setup_file(setup_path("slapd.conf"), paths.slapdconf,
1209                    {"DNSDOMAIN": names.dnsdomain,
1210                     "LDAPDIR": paths.ldapdir,
1211                     "DOMAINDN": names.domaindn,
1212                     "CONFIGDN": names.configdn,
1213                     "SCHEMADN": names.schemadn,
1214                     "LDAPMANAGERDN": names.ldapmanagerdn,
1215                     "LDAPMANAGERPASS": adminpass,
1216                     "MEMBEROF_CONFIG": memberof_config})
1217         setup_file(setup_path("modules.conf"), paths.modulesconf,
1218                    {"REALM": names.realm})
1219         
1220         setup_db_config(setup_path, file, os.path.join(paths.ldapdir, "db", "user"))
1221         setup_db_config(setup_path, file, os.path.join(paths.ldapdir, "db", "config"))
1222         setup_db_config(setup_path, file, os.path.join(paths.ldapdir, "db", "schema"))
1223         mapping = "schema-map-openldap-2.3"
1224         backend_schema = "backend-schema.schema"
1225         
1226
1227         ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
1228         message("Start slapd with: slapd -f " + paths.ldapdir + "/slapd.conf -h " + ldapi_uri)
1229                 
1230
1231     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);
1232
1233     os.system(schema_command)
1234
1235
1236
1237 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1238     """Create a PHP LDAP admin configuration file.
1239
1240     :param path: Path to write the configuration to.
1241     :param setup_path: Function to generate setup paths.
1242     """
1243     setup_file(setup_path("phpldapadmin-config.php"), path, 
1244             {"S4_LDAPI_URI": ldapi_uri})
1245
1246
1247 def create_zone_file(path, setup_path, samdb, dnsdomain, domaindn, 
1248                   hostip, hostname, dnspass, realm, domainguid, hostguid):
1249     """Write out a DNS zone file, from the info in the current database.
1250     
1251     :param path: Path of the new file.
1252     :param setup_path": Setup path function.
1253     :param samdb: SamDB object
1254     :param dnsdomain: DNS Domain name
1255     :param domaindn: DN of the Domain
1256     :param hostip: Local IP
1257     :param hostname: Local hostname
1258     :param dnspass: Password for DNS
1259     :param realm: Realm name
1260     :param domainguid: GUID of the domain.
1261     :param hostguid: GUID of the host.
1262     """
1263     assert isinstance(domainguid, str)
1264
1265     setup_file(setup_path("provision.zone"), path, {
1266             "DNSPASS_B64": b64encode(dnspass),
1267             "HOSTNAME": hostname,
1268             "DNSDOMAIN": dnsdomain,
1269             "REALM": realm,
1270             "HOSTIP": hostip,
1271             "DOMAINGUID": domainguid,
1272             "DATESTRING": time.strftime("%Y%m%d%H"),
1273             "DEFAULTSITE": DEFAULTSITE,
1274             "HOSTGUID": hostguid,
1275         })
1276
1277 def load_schema(setup_path, samdb, schemadn, netbiosname, configdn, sitename):
1278     """Load schema for the SamDB.
1279     
1280     :param samdb: Load a schema into a SamDB.
1281     :param setup_path: Setup path function.
1282     :param schemadn: DN of the schema
1283     :param netbiosname: NetBIOS name of the host.
1284     :param configdn: DN of the configuration
1285     """
1286     schema_data = open(setup_path("schema.ldif"), 'r').read()
1287     schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
1288     schema_data = substitute_var(schema_data, {"SCHEMADN": schemadn})
1289     head_data = open(setup_path("provision_schema_basedn_modify.ldif"), 'r').read()
1290     head_data = substitute_var(head_data, {
1291                     "SCHEMADN": schemadn,
1292                     "NETBIOSNAME": netbiosname,
1293                     "CONFIGDN": configdn,
1294                     "DEFAULTSITE":sitename 
1295     })
1296     samdb.attach_schema_from_ldif(head_data, schema_data)
1297