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