Merge branch 'v4-0-test' of ssh://git.samba.org/data/git/samba into openchange
[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 import socket
32 import param
33 import registry
34 import samba
35 from auth import system_session
36 from samba import Ldb, substitute_var, valid_netbios_name, check_all_substituted
37 from samba.samdb import SamDB
38 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 = socket.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 = socket.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, hostip6=None, 
898               domainsid=None, 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 = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
952
953     if hostip6 is None:
954         try:
955             hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
956         except socket.gaierror: pass
957
958     if serverrole is None:
959         serverrole = lp.get("server role")
960
961     assert serverrole in ("domain controller", "member server", "standalone")
962     if invocationid is None and serverrole == "domain controller":
963         invocationid = uuid.random()
964
965     if not os.path.exists(paths.private_dir):
966         os.mkdir(paths.private_dir)
967
968     ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
969     
970     if ldap_backend is not None:
971         if ldap_backend == "ldapi":
972             # provision-backend will set this path suggested slapd command line / fedorads.inf
973             ldap_backend = "ldapi://" % urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
974              
975     # only install a new shares config db if there is none
976     if not os.path.exists(paths.shareconf):
977         message("Setting up share.ldb")
978         share_ldb = Ldb(paths.shareconf, session_info=session_info, 
979                         credentials=credentials, lp=lp)
980         share_ldb.load_ldif_file_add(setup_path("share.ldif"))
981
982      
983     message("Setting up secrets.ldb")
984     secrets_ldb = setup_secretsdb(paths.secrets, setup_path, 
985                                   session_info=session_info, 
986                                   credentials=credentials, lp=lp)
987
988     message("Setting up the registry")
989     setup_registry(paths.hklm, setup_path, session_info, 
990                    credentials=credentials, lp=lp)
991
992     message("Setting up templates db")
993     setup_templatesdb(paths.templates, setup_path, session_info=session_info, 
994                       credentials=credentials, lp=lp)
995
996     message("Setting up idmap db")
997     setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
998                   credentials=credentials, lp=lp)
999
1000     samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info, 
1001                         credentials=credentials, lp=lp, names=names,
1002                         message=message, 
1003                         domainsid=domainsid, 
1004                         aci=aci, domainguid=domainguid, policyguid=policyguid, 
1005                         fill=samdb_fill, 
1006                         adminpass=adminpass, krbtgtpass=krbtgtpass,
1007                         invocationid=invocationid, 
1008                         machinepass=machinepass, dnspass=dnspass,
1009                         serverrole=serverrole, ldap_backend=ldap_backend, 
1010                         ldap_backend_type=ldap_backend_type)
1011
1012     if lp.get("server role") == "domain controller":
1013        policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies", 
1014                                   "{" + policyguid + "}")
1015        os.makedirs(policy_path, 0755)
1016        os.makedirs(os.path.join(policy_path, "Machine"), 0755)
1017        os.makedirs(os.path.join(policy_path, "User"), 0755)
1018        if not os.path.isdir(paths.netlogon):
1019             os.makedirs(paths.netlogon, 0755)
1020        secrets_ldb = Ldb(paths.secrets, session_info=session_info, 
1021                          credentials=credentials, lp=lp)
1022        secretsdb_become_dc(secrets_ldb, setup_path, domain=domain, realm=names.realm,
1023                            netbiosname=names.netbiosname, domainsid=domainsid, 
1024                            keytab_path=paths.keytab, samdb_url=paths.samdb, 
1025                            dns_keytab_path=paths.dns_keytab, dnspass=dnspass, 
1026                            machinepass=machinepass, dnsdomain=names.dnsdomain)
1027
1028     if samdb_fill == FILL_FULL:
1029         setup_name_mappings(samdb, str(domainsid), names.domaindn, root=root, 
1030                             nobody=nobody, nogroup=nogroup, wheel=wheel, 
1031                             users=users, backup=backup)
1032    
1033         message("Compleating sam.ldb setup by marking as synchronized")
1034         setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1035
1036         # Only make a zone file on the first DC, it should be replicated with DNS replication
1037         if serverrole == "domain controller":
1038             samdb = SamDB(paths.samdb, session_info=session_info, 
1039                       credentials=credentials, lp=lp)
1040
1041             domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1042             assert isinstance(domainguid, str)
1043             hostguid = samdb.searchone(basedn=domaindn, attribute="objectGUID",
1044                                        expression="(&(objectClass=computer)(cn=%s))" % names.hostname,
1045                                        scope=SCOPE_SUBTREE)
1046             assert isinstance(hostguid, str)
1047             
1048             create_zone_file(paths.dns, setup_path, samdb, 
1049                              hostname=names.hostname, hostip=hostip,
1050                              hostip6=hostip6, dnsdomain=names.dnsdomain,
1051                              domaindn=names.domaindn, dnspass=dnspass, realm=names.realm, 
1052                              domainguid=domainguid, hostguid=hostguid)
1053             message("Please install the zone located in %s into your DNS server" % paths.dns)
1054             
1055     create_phpldapadmin_config(paths.phpldapadminconfig, setup_path, 
1056                                ldapi_url)
1057
1058     message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1059
1060     message("Once the above files are installed, your server will be ready to use")
1061     message("Server Type:    %s" % serverrole)
1062     message("Hostname:       %s" % names.hostname)
1063     message("NetBIOS Domain: %s" % names.domain)
1064     message("DNS Domain:     %s" % names.dnsdomain)
1065     message("DOMAIN SID:     %s" % str(domainsid))
1066     message("Admin password: %s" % adminpass)
1067
1068     result = ProvisionResult()
1069     result.domaindn = domaindn
1070     result.paths = paths
1071     result.lp = lp
1072     result.samdb = samdb
1073     return result
1074
1075 def provision_become_dc(setup_dir=None,
1076                         smbconf=None, targetdir=None, realm=None, 
1077                         rootdn=None, domaindn=None, schemadn=None, configdn=None,
1078                         domain=None, hostname=None, domainsid=None, 
1079                         adminpass=None, krbtgtpass=None, domainguid=None, 
1080                         policyguid=None, invocationid=None, machinepass=None, 
1081                         dnspass=None, root=None, nobody=None, nogroup=None, users=None, 
1082                         wheel=None, backup=None, aci=None, serverrole=None, 
1083                         ldap_backend=None, ldap_backend_type=None, sitename=DEFAULTSITE):
1084
1085     def message(text):
1086         """print a message if quiet is not set."""
1087         print text
1088
1089     provision(setup_dir, message, system_session(), None,
1090               smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS, realm=realm, 
1091               rootdn=rootdn, domaindn=domaindn, schemadn=schemadn, configdn=configdn, 
1092               domain=domain, hostname=hostname, hostip="127.0.0.1", domainsid=domainsid, machinepass=machinepass, serverrole="domain controller", sitename=sitename);
1093     
1094
1095 def setup_db_config(setup_path, file, dbdir):
1096     if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
1097         os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700);
1098     if not os.path.isdir(os.path.join(dbdir, "tmp")):
1099         os.makedirs(os.path.join(dbdir, "tmp"), 0700);
1100     
1101     setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
1102                {"LDAPDBDIR": dbdir})
1103     
1104
1105
1106 def provision_backend(setup_dir=None, message=None,
1107                       smbconf=None, targetdir=None, realm=None, 
1108                       rootdn=None, domaindn=None, schemadn=None, configdn=None,
1109                       domain=None, hostname=None, adminpass=None, root=None, serverrole=None, 
1110                       ldap_backend_type=None):
1111
1112     def setup_path(file):
1113         return os.path.join(setup_dir, file)
1114
1115     if hostname is None:
1116         hostname = socket.gethostname().split(".")[0].lower()
1117
1118     if root is None:
1119         root = findnss(pwd.getpwnam, ["root"])[0]
1120
1121     lp = load_or_make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, targetdir)
1122
1123     names = guess_names(lp=lp, hostname=hostname, domain=domain, 
1124                         dnsdomain=realm, serverrole=serverrole, 
1125                         rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn)
1126
1127     paths = provision_paths_from_lp(lp, names.dnsdomain)
1128
1129     if not os.path.isdir(paths.ldapdir):
1130         os.makedirs(paths.ldapdir)
1131     schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
1132     try:
1133         os.unlink(schemadb_path)
1134     except:
1135         pass
1136
1137     schemadb = Ldb(schemadb_path, lp=lp)
1138  
1139     setup_add_ldif(schemadb, setup_path("provision_schema_basedn.ldif"), 
1140                    {"SCHEMADN": names.schemadn,
1141                     "ACI": "#",
1142                     "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb"
1143                     })
1144     setup_modify_ldif(schemadb, 
1145                       setup_path("provision_schema_basedn_modify.ldif"), \
1146                           {"SCHEMADN": names.schemadn,
1147                            "NETBIOSNAME": names.netbiosname,
1148                            "DEFAULTSITE": DEFAULTSITE,
1149                            "CONFIGDN": names.configdn,
1150                            })
1151     
1152     setup_add_ldif(schemadb, setup_path("schema_samba4.ldif"), 
1153                    {"SCHEMADN": names.schemadn })
1154     setup_add_ldif(schemadb, setup_path("schema.ldif"), 
1155                    {"SCHEMADN": names.schemadn})
1156
1157     if ldap_backend_type == "fedora-ds":
1158         setup_file(setup_path("fedora-ds.inf"), paths.fedoradsinf, 
1159                    {"ROOT": root,
1160                     "HOSTNAME": hostname,
1161                     "DNSDOMAIN": names.dnsdomain,
1162                     "LDAPDIR": paths.ldapdir,
1163                     "DOMAINDN": names.domaindn,
1164                     "LDAPMANAGERDN": names.ldapmanagerdn,
1165                     "LDAPMANAGERPASS": adminpass, 
1166                     "SERVERPORT": ""})
1167         
1168         setup_file(setup_path("fedora-partitions.ldif"), paths.fedoradspartitions, 
1169                    {"CONFIGDN": names.configdn,
1170                     "SCHEMADN": names.schemadn,
1171                     })
1172         
1173         setup_file(setup_path("fedora-partitions.ldif"), paths.fedoradspartitions, 
1174                    {"CONFIGDN": names.configdn,
1175                     "SCHEMADN": names.schemadn,
1176                     })
1177         mapping = "schema-map-fedora-ds-1.0"
1178         backend_schema = "99_ad.ldif"
1179     elif ldap_backend_type == "openldap":
1180         attrs = ["linkID", "lDAPDisplayName"]
1181         res = schemadb.search(expression="(&(&(linkID=*)(!(linkID:1.2.840.113556.1.4.803:=1)))(objectclass=attributeSchema))", base=names.schemadn, scope=SCOPE_SUBTREE, attrs=attrs);
1182
1183         memberof_config = "# Generated from schema in " + schemadb_path + "\n";
1184         refint_attributes = "";
1185         for i in range (0, len(res)):
1186             linkid = res[i]["linkID"][0]
1187             linkid = str(int(linkid) + 1)
1188             expression = "(&(objectclass=attributeSchema)(linkID=" + (linkid) + "))"
1189             target = schemadb.searchone(basedn=names.schemadn, 
1190                                         expression=expression, 
1191                                         attribute="lDAPDisplayName", 
1192                                         scope=SCOPE_SUBTREE);
1193             if target is not None:
1194                 refint_attributes = refint_attributes + " " + target + " " + res[i]["lDAPDisplayName"][0];
1195                 memberof_config = memberof_config + """overlay memberof
1196 memberof-dangling error
1197 memberof-refint TRUE
1198 memberof-group-oc top
1199 memberof-member-ad """ + res[i]["lDAPDisplayName"][0] + """
1200 memberof-memberof-ad """ + target + """
1201 memberof-dangling-error 32
1202
1203 """;
1204
1205         memberof_config = memberof_config + """
1206 overlay refint
1207 refint_attributes""" + refint_attributes + "\n";
1208         
1209         setup_file(setup_path("slapd.conf"), paths.slapdconf,
1210                    {"DNSDOMAIN": names.dnsdomain,
1211                     "LDAPDIR": paths.ldapdir,
1212                     "DOMAINDN": names.domaindn,
1213                     "CONFIGDN": names.configdn,
1214                     "SCHEMADN": names.schemadn,
1215                     "LDAPMANAGERDN": names.ldapmanagerdn,
1216                     "LDAPMANAGERPASS": adminpass,
1217                     "MEMBEROF_CONFIG": memberof_config})
1218         setup_file(setup_path("modules.conf"), paths.modulesconf,
1219                    {"REALM": names.realm})
1220         
1221         setup_db_config(setup_path, file, os.path.join(paths.ldapdir, "db", "user"))
1222         setup_db_config(setup_path, file, os.path.join(paths.ldapdir, "db", "config"))
1223         setup_db_config(setup_path, file, os.path.join(paths.ldapdir, "db", "schema"))
1224         mapping = "schema-map-openldap-2.3"
1225         backend_schema = "backend-schema.schema"
1226         
1227
1228         ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
1229         message("Start slapd with: slapd -f " + paths.ldapdir + "/slapd.conf -h " + ldapi_uri)
1230                 
1231
1232     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);
1233
1234     os.system(schema_command)
1235
1236
1237
1238 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1239     """Create a PHP LDAP admin configuration file.
1240
1241     :param path: Path to write the configuration to.
1242     :param setup_path: Function to generate setup paths.
1243     """
1244     setup_file(setup_path("phpldapadmin-config.php"), path, 
1245             {"S4_LDAPI_URI": ldapi_uri})
1246
1247
1248 def create_zone_file(path, setup_path, samdb, dnsdomain, domaindn, 
1249                   hostip, hostip6, hostname, dnspass, realm, domainguid, hostguid):
1250     """Write out a DNS zone file, from the info in the current database.
1251     
1252     :param path: Path of the new file.
1253     :param setup_path": Setup path function.
1254     :param samdb: SamDB object
1255     :param dnsdomain: DNS Domain name
1256     :param domaindn: DN of the Domain
1257     :param hostip: Local IPv4 IP
1258     :param hostip6: Local IPv6 IP
1259     :param hostname: Local hostname
1260     :param dnspass: Password for DNS
1261     :param realm: Realm name
1262     :param domainguid: GUID of the domain.
1263     :param hostguid: GUID of the host.
1264     """
1265     assert isinstance(domainguid, str)
1266
1267     hostip6_base_line = ""
1268     hostip6_host_line = ""
1269
1270     if hostip6 is not None:
1271         hostip6_base_line = "                   IN AAAA " + hostip6
1272         hostip6_host_line = hostname + "                IN AAAA " + hostip6
1273
1274     setup_file(setup_path("provision.zone"), path, {
1275             "DNSPASS_B64": b64encode(dnspass),
1276             "HOSTNAME": hostname,
1277             "DNSDOMAIN": dnsdomain,
1278             "REALM": realm,
1279             "HOSTIP": hostip,
1280             "DOMAINGUID": domainguid,
1281             "DATESTRING": time.strftime("%Y%m%d%H"),
1282             "DEFAULTSITE": DEFAULTSITE,
1283             "HOSTGUID": hostguid,
1284             "HOSTIP6_BASE_LINE": hostip6_base_line,
1285             "HOSTIP6_HOST_LINE": hostip6_host_line,
1286         })
1287
1288 def load_schema(setup_path, samdb, schemadn, netbiosname, configdn, sitename):
1289     """Load schema for the SamDB.
1290     
1291     :param samdb: Load a schema into a SamDB.
1292     :param setup_path: Setup path function.
1293     :param schemadn: DN of the schema
1294     :param netbiosname: NetBIOS name of the host.
1295     :param configdn: DN of the configuration
1296     """
1297     schema_data = open(setup_path("schema.ldif"), 'r').read()
1298     schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
1299     schema_data = substitute_var(schema_data, {"SCHEMADN": schemadn})
1300     head_data = open(setup_path("provision_schema_basedn_modify.ldif"), 'r').read()
1301     head_data = substitute_var(head_data, {
1302                     "SCHEMADN": schemadn,
1303                     "NETBIOSNAME": netbiosname,
1304                     "CONFIGDN": configdn,
1305                     "DEFAULTSITE":sitename 
1306     })
1307     samdb.attach_schema_from_ldif(head_data, schema_data)
1308