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