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