25316e888a2e96882df64b336d45348ed125241f
[samba.git] / source4 / scripting / python / samba / provision.py
1 #
2 # Unix SMB/CIFS implementation.
3 # backend code for provisioning a Samba4 server
4
5 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
6 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008
7 #
8 # Based on the original in EJS:
9 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
10 #
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 3 of the License, or
14 # (at your option) any later version.
15 #   
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 # GNU General Public License for more details.
20 #   
21 # You should have received a copy of the GNU General Public License
22 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
23 #
24
25 from base64 import b64encode
26 import os
27 import pwd
28 import grp
29 import time
30 import uuid, misc
31 from socket import gethostname, gethostbyname
32 import param
33 import registry
34 import samba
35 from auth import system_session
36 from samba import Ldb, substitute_var, valid_netbios_name, check_all_substituted
37 from samba.samdb import SamDB
38 import security
39 import urllib
40 from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError, \
41         LDB_ERR_NO_SUCH_OBJECT, timestring, CHANGETYPE_MODIFY, CHANGETYPE_NONE
42
43 """Functions for setting up a Samba configuration."""
44
45 DEFAULTSITE = "Default-First-Site-Name"
46
47 class InvalidNetbiosName(Exception):
48     def __init__(self, name):
49         super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)
50
51
52 class ProvisionPaths:
53     def __init__(self):
54         self.shareconf = None
55         self.hklm = None
56         self.hkcu = None
57         self.hkcr = None
58         self.hku = None
59         self.hkpd = None
60         self.hkpt = None
61         self.samdb = None
62         self.idmapdb = None
63         self.secrets = None
64         self.keytab = None
65         self.dns_keytab = None
66         self.dns = None
67         self.winsdb = None
68         self.private_dir = None
69         self.ldapdir = None
70         self.slapdconf = None
71         self.modulesconf = None
72         self.memberofconf = None
73         self.fedoradsinf = None
74         self.fedoradspartitions = None
75  
76 class ProvisionNames:
77     def __init__(self):
78         self.rootdn = None
79         self.domaindn = None
80         self.configdn = None
81         self.schemadn = None
82         self.ldapmanagerdn = None
83         self.dnsdomain = None
84         self.realm = None
85         self.netbiosname = None
86         self.domain = None
87         self.hostname = None
88         self.sitename = None
89     
90 class ProvisionResult:
91     def __init__(self):
92         self.paths = None
93         self.domaindn = None
94         self.lp = None
95         self.samdb = None
96
97 def check_install(lp, session_info, credentials):
98     """Check whether the current install seems ok.
99     
100     :param lp: Loadparm context
101     :param session_info: Session information
102     :param credentials: Credentials
103     """
104     if lp.get("realm") == "":
105         raise Error("Realm empty")
106     ldb = Ldb(lp.get("sam database"), session_info=session_info, 
107             credentials=credentials, lp=lp)
108     if len(ldb.search("(cn=Administrator)")) != 1:
109         raise "No administrator account found"
110
111
112 def findnss(nssfn, names):
113     """Find a user or group from a list of possibilities.
114     
115     :param nssfn: NSS Function to try (should raise KeyError if not found)
116     :param names: Names to check.
117     :return: Value return by first names list.
118     """
119     for name in names:
120         try:
121             return nssfn(name)
122         except KeyError:
123             pass
124     raise KeyError("Unable to find user/group %r" % names)
125
126
127 def open_ldb(session_info, credentials, lp, dbname):
128     """Open a LDB, thrashing it if it is corrupt.
129
130     :param session_info: auth session information
131     :param credentials: credentials
132     :param lp: Loadparm context
133     :param dbname: Path of the database to open.
134     :return: a Ldb object
135     """
136     assert session_info is not None
137     try:
138         return Ldb(dbname, session_info=session_info, credentials=credentials, 
139                    lp=lp)
140     except LdbError, e:
141         print e
142         os.unlink(dbname)
143         return Ldb(dbname, session_info=session_info, credentials=credentials,
144                    lp=lp)
145
146
147 def setup_add_ldif(ldb, ldif_path, subst_vars=None):
148     """Setup a ldb in the private dir.
149     
150     :param ldb: LDB file to import data into
151     :param ldif_path: Path of the LDIF file to load
152     :param subst_vars: Optional variables to subsitute in LDIF.
153     """
154     assert isinstance(ldif_path, str)
155
156     data = open(ldif_path, 'r').read()
157     if subst_vars is not None:
158         data = substitute_var(data, subst_vars)
159
160     check_all_substituted(data)
161
162     ldb.add_ldif(data)
163
164
165 def setup_modify_ldif(ldb, ldif_path, substvars=None):
166     """Modify a ldb in the private dir.
167     
168     :param ldb: LDB object.
169     :param ldif_path: LDIF file path.
170     :param substvars: Optional dictionary with substitution variables.
171     """
172     data = open(ldif_path, 'r').read()
173     if substvars is not None:
174         data = substitute_var(data, substvars)
175
176     check_all_substituted(data)
177
178     ldb.modify_ldif(data)
179
180
181 def setup_ldb(ldb, ldif_path, subst_vars):
182     """Import a LDIF a file into a LDB handle, optionally substituting variables.
183
184     :note: Either all LDIF data will be added or none (using transactions).
185
186     :param ldb: LDB file to import into.
187     :param ldif_path: Path to the LDIF file.
188     :param subst_vars: Dictionary with substitution variables.
189     """
190     assert ldb is not None
191     ldb.transaction_start()
192     try:
193         setup_add_ldif(ldb, ldif_path, subst_vars)
194     except:
195         ldb.transaction_cancel()
196         raise
197     ldb.transaction_commit()
198
199
200 def setup_file(template, fname, substvars):
201     """Setup a file in the private dir.
202
203     :param template: Path of the template file.
204     :param fname: Path of the file to create.
205     :param substvars: Substitution variables.
206     """
207     f = fname
208
209     if os.path.exists(f):
210         os.unlink(f)
211
212     data = open(template, 'r').read()
213     if substvars:
214         data = substitute_var(data, substvars)
215     check_all_substituted(data)
216
217     open(f, 'w').write(data)
218
219
220 def provision_paths_from_lp(lp, dnsdomain):
221     """Set the default paths for provisioning.
222
223     :param lp: Loadparm context.
224     :param dnsdomain: DNS Domain name
225     """
226     paths = ProvisionPaths()
227     paths.private_dir = lp.get("private dir")
228     paths.keytab = "secrets.keytab"
229     paths.dns_keytab = "dns.keytab"
230
231     paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
232     paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
233     paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
234     paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
235     paths.templates = os.path.join(paths.private_dir, "templates.ldb")
236     paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
237     paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
238     paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
239     paths.phpldapadminconfig = os.path.join(paths.private_dir, 
240                                             "phpldapadmin-config.php")
241     paths.ldapdir = os.path.join(paths.private_dir, 
242                                  "ldap")
243     paths.slapdconf = os.path.join(paths.ldapdir, 
244                                    "slapd.conf")
245     paths.modulesconf = os.path.join(paths.ldapdir, 
246                                      "modules.conf")
247     paths.memberofconf = os.path.join(paths.ldapdir, 
248                                       "memberof.conf")
249     paths.fedoradsinf = os.path.join(paths.ldapdir, 
250                                    "fedorads.inf")
251     paths.fedoradspartitions = os.path.join(paths.ldapdir, 
252                                             "fedorads-partitions.ldif")
253     paths.hklm = "hklm.ldb"
254     paths.hkcr = "hkcr.ldb"
255     paths.hkcu = "hkcu.ldb"
256     paths.hku = "hku.ldb"
257     paths.hkpd = "hkpd.ldb"
258     paths.hkpt = "hkpt.ldb"
259
260     paths.sysvol = lp.get("path", "sysvol")
261
262     paths.netlogon = lp.get("path", "netlogon")
263
264     return paths
265
266 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None, serverrole=None,
267               rootdn=None, domaindn=None, configdn=None, schemadn=None, sitename=None):
268
269     if hostname is None:
270         hostname = gethostname().split(".")[0].lower()
271
272     netbiosname = hostname.upper()
273     if not valid_netbios_name(netbiosname):
274         raise InvalidNetbiosName(netbiosname)
275
276     hostname = hostname.lower()
277
278     if dnsdomain is None:
279         dnsdomain = lp.get("realm")
280
281     if serverrole is None:
282         serverrole = lp.get("server role")
283
284     assert dnsdomain is not None
285     realm = dnsdomain.upper()
286
287     if lp.get("realm").upper() != realm:
288         raise Exception("realm '%s' in %s must match chosen realm '%s'" %
289                         (lp.get("realm"), smbconf, realm))
290     
291     dnsdomain = dnsdomain.lower()
292
293     if (serverrole == "domain controller"):
294         if domain is None:
295             domain = lp.get("workgroup")
296         if domaindn is None:
297             domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
298         if lp.get("workgroup").upper() != domain.upper():
299             raise Error("workgroup '%s' in smb.conf must match chosen domain '%s'",
300                         lp.get("workgroup"), domain)
301     else:
302         domain = netbiosname
303         if domaindn is None:
304             domaindn = "CN=" + netbiosname
305         
306     assert domain is not None
307     domain = domain.upper()
308     if not valid_netbios_name(domain):
309         raise InvalidNetbiosName(domain)
310         
311     if rootdn is None:
312        rootdn = domaindn
313        
314     if configdn is None:
315         configdn = "CN=Configuration," + rootdn
316     if schemadn is None:
317         schemadn = "CN=Schema," + configdn
318
319     if sitename is None:
320         sitename=DEFAULTSITE
321
322     names = ProvisionNames()
323     names.rootdn = rootdn
324     names.domaindn = domaindn
325     names.configdn = configdn
326     names.schemadn = schemadn
327     names.ldapmanagerdn = "CN=Manager," + rootdn
328     names.dnsdomain = dnsdomain
329     names.domain = domain
330     names.realm = realm
331     names.netbiosname = netbiosname
332     names.hostname = hostname
333     names.sitename = sitename
334     
335     return names
336     
337
338 def load_or_make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, targetdir):
339     if targetdir is not None:
340         if not os.path.exists(targetdir):
341             os.mkdir(targetdir)
342         if not os.path.exists(os.path.join(targetdir, "etc")):
343            os.mkdir(os.path.join(targetdir, "etc"))
344
345         smbconf = os.path.join(targetdir, "etc", "smb.conf")
346
347     # only install a new smb.conf if there isn't one there already
348
349     if not os.path.exists(smbconf):
350         if hostname is None:
351             hostname = gethostname().split(".")[0].lower()
352
353         if serverrole is None:
354             serverrole = "standalone"
355
356         assert serverrole in ("domain controller", "member server", "standalone")
357         if serverrole == "domain controller":
358             smbconfsuffix = "dc"
359         elif serverrole == "member server":
360             smbconfsuffix = "member"
361         elif serverrole == "standalone":
362             smbconfsuffix = "standalone"
363
364         assert domain is not None
365         assert realm is not None
366
367         default_lp = param.LoadParm()
368         #Load non-existant file
369         default_lp.load(smbconf)
370         
371         if targetdir is not None:
372             privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
373             lockdir_line = "lock dir = " + os.path.abspath(targetdir)
374
375             default_lp.set("lock dir", os.path.abspath(targetdir))
376             
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         if serverrole == "domain controller":
767             domain_oc = "domainDNS"
768         else:
769             domain_oc = "samba4LocalDomain"
770
771         setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
772             "DOMAINDN": names.domaindn,
773             "ACI": aci,
774             "DOMAIN_OC": domain_oc
775             })
776
777         message("Modifying DomainDN: " + names.domaindn + "")
778         if domainguid is not None:
779             domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
780         else:
781             domainguid_mod = ""
782
783         setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
784             "LDAPTIME": timestring(int(time.time())),
785             "DOMAINSID": str(domainsid),
786             "SCHEMADN": names.schemadn, 
787             "NETBIOSNAME": names.netbiosname,
788             "DEFAULTSITE": names.sitename,
789             "CONFIGDN": names.configdn,
790             "POLICYGUID": policyguid,
791             "DOMAINDN": names.domaindn,
792             "DOMAINGUID_MOD": domainguid_mod,
793             })
794
795         message("Adding configuration container (permitted to fail)")
796         setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
797             "CONFIGDN": names.configdn, 
798             "ACI": aci,
799             "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb",
800             })
801         message("Modifying configuration container")
802         setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
803             "CONFIGDN": names.configdn, 
804             "SCHEMADN": names.schemadn,
805             })
806
807         message("Adding schema container (permitted to fail)")
808         setup_add_ldif(samdb, setup_path("provision_schema_basedn.ldif"), {
809             "SCHEMADN": names.schemadn,
810             "ACI": aci,
811             "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb"
812             })
813         message("Modifying schema container")
814         setup_modify_ldif(samdb, 
815             setup_path("provision_schema_basedn_modify.ldif"), {
816             "SCHEMADN": names.schemadn,
817             "NETBIOSNAME": names.netbiosname,
818             "DEFAULTSITE": names.sitename,
819             "CONFIGDN": names.configdn,
820             })
821
822         message("Setting up sam.ldb Samba4 schema")
823         setup_add_ldif(samdb, setup_path("schema_samba4.ldif"), 
824                        {"SCHEMADN": names.schemadn })
825         message("Setting up sam.ldb AD schema")
826         setup_add_ldif(samdb, setup_path("schema.ldif"), 
827                        {"SCHEMADN": names.schemadn})
828
829         message("Setting up sam.ldb configuration data")
830         setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
831             "CONFIGDN": names.configdn,
832             "NETBIOSNAME": names.netbiosname,
833             "DEFAULTSITE": names.sitename,
834             "DNSDOMAIN": names.dnsdomain,
835             "DOMAIN": names.domain,
836             "SCHEMADN": names.schemadn,
837             "DOMAINDN": names.domaindn,
838             })
839
840         message("Setting up display specifiers")
841         setup_add_ldif(samdb, setup_path("display_specifiers.ldif"), 
842                        {"CONFIGDN": names.configdn})
843
844         message("Adding users container (permitted to fail)")
845         setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
846                 "DOMAINDN": names.domaindn})
847         message("Modifying users container")
848         setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
849                 "DOMAINDN": names.domaindn})
850         message("Adding computers container (permitted to fail)")
851         setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
852                 "DOMAINDN": names.domaindn})
853         message("Modifying computers container")
854         setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
855                 "DOMAINDN": names.domaindn})
856         message("Setting up sam.ldb data")
857         setup_add_ldif(samdb, setup_path("provision.ldif"), {
858             "DOMAINDN": names.domaindn,
859             "NETBIOSNAME": names.netbiosname,
860             "DEFAULTSITE": names.sitename,
861             "CONFIGDN": names.configdn,
862             })
863
864         if fill == FILL_FULL:
865             message("Setting up sam.ldb users and groups")
866             setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
867                 "DOMAINDN": names.domaindn,
868                 "DOMAINSID": str(domainsid),
869                 "CONFIGDN": names.configdn,
870                 "ADMINPASS_B64": b64encode(adminpass),
871                 "KRBTGTPASS_B64": b64encode(krbtgtpass),
872                 })
873
874             if serverrole == "domain controller":
875                 message("Setting up self join")
876                 setup_self_join(samdb, names=names, invocationid=invocationid, 
877                                 dnspass=dnspass,  
878                                 machinepass=machinepass, 
879                                 domainsid=domainsid, policyguid=policyguid,
880                                 hostguid=hostguid, 
881                                 setup_path=setup_path)
882
883     #We want to setup the index last, as adds are faster unindexed
884         message("Setting up sam.ldb index")
885         samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
886     except:
887         samdb.transaction_cancel()
888         raise
889
890     samdb.transaction_commit()
891     return samdb
892
893
894 FILL_FULL = "FULL"
895 FILL_NT4SYNC = "NT4SYNC"
896 FILL_DRS = "DRS"
897
898 def provision(setup_dir, message, session_info, 
899               credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL, realm=None, 
900               rootdn=None, domaindn=None, schemadn=None, configdn=None,
901               domain=None, hostname=None, hostip=None, domainsid=None, 
902               hostguid=None, adminpass=None, krbtgtpass=None, domainguid=None, 
903               policyguid=None, invocationid=None, machinepass=None, 
904               dnspass=None, root=None, nobody=None, nogroup=None, users=None, 
905               wheel=None, backup=None, aci=None, serverrole=None, 
906               ldap_backend=None, ldap_backend_type=None, sitename=None):
907     """Provision samba4
908     
909     :note: caution, this wipes all existing data!
910     """
911
912     def setup_path(file):
913         return os.path.join(setup_dir, file)
914
915     if domainsid is None:
916         domainsid = security.random_sid()
917     else:
918         domainsid = security.Sid(domainsid)
919
920     if policyguid is None:
921         policyguid = uuid.random()
922     if adminpass is None:
923         adminpass = misc.random_password(12)
924     if krbtgtpass is None:
925         krbtgtpass = misc.random_password(12)
926     if machinepass is None:
927         machinepass  = misc.random_password(12)
928     if dnspass is None:
929         dnspass = misc.random_password(12)
930     if root is None:
931         root = findnss(pwd.getpwnam, ["root"])[0]
932     if nobody is None:
933         nobody = findnss(pwd.getpwnam, ["nobody"])[0]
934     if nogroup is None:
935         nogroup = findnss(grp.getgrnam, ["nogroup", "nobody"])[0]
936     if users is None:
937         users = findnss(grp.getgrnam, ["users", "guest", "other", "unknown", 
938                         "usr"])[0]
939     if wheel is None:
940         wheel = findnss(grp.getgrnam, ["wheel", "root", "staff", "adm"])[0]
941     if backup is None:
942         backup = findnss(grp.getgrnam, ["backup", "wheel", "root", "staff"])[0]
943     if aci is None:
944         aci = "# no aci for local ldb"
945
946     lp = load_or_make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, targetdir)
947
948     names = guess_names(lp=lp, hostname=hostname, domain=domain, 
949                         dnsdomain=realm, serverrole=serverrole, sitename=sitename,
950                         rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn)
951
952     paths = provision_paths_from_lp(lp, names.dnsdomain)
953
954     if hostip is None:
955         hostip = gethostbyname(names.hostname)
956
957     if serverrole is None:
958         serverrole = lp.get("server role")
959
960     assert serverrole in ("domain controller", "member server", "standalone")
961     if invocationid is None and serverrole == "domain controller":
962         invocationid = uuid.random()
963
964     if not os.path.exists(paths.private_dir):
965         os.mkdir(paths.private_dir)
966
967     ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
968     
969     if ldap_backend is not None:
970         if ldap_backend == "ldapi":
971             # provision-backend will set this path suggested slapd command line / fedorads.inf
972             ldap_backend = "ldapi://" % urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
973              
974     message("set DOMAIN SID: %s" % str(domainsid))
975     message("Provisioning for %s in realm %s" % (names.domain, realm))
976     message("Using administrator password: %s" % adminpass)
977
978     # only install a new shares config db if there is none
979     if not os.path.exists(paths.shareconf):
980         message("Setting up share.ldb")
981         share_ldb = Ldb(paths.shareconf, session_info=session_info, 
982                         credentials=credentials, lp=lp)
983         share_ldb.load_ldif_file_add(setup_path("share.ldif"))
984
985      
986     message("Setting up secrets.ldb")
987     secrets_ldb = setup_secretsdb(paths.secrets, setup_path, 
988                                   session_info=session_info, 
989                                   credentials=credentials, lp=lp)
990
991     message("Setting up the registry")
992     setup_registry(paths.hklm, setup_path, session_info, 
993                    credentials=credentials, lp=lp)
994
995     message("Setting up templates db")
996     setup_templatesdb(paths.templates, setup_path, session_info=session_info, 
997                       credentials=credentials, lp=lp)
998
999     message("Setting up idmap db")
1000     setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1001                   credentials=credentials, lp=lp)
1002
1003     samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info, 
1004                         credentials=credentials, lp=lp, names=names,
1005                         message=message, 
1006                         domainsid=domainsid, 
1007                         aci=aci, domainguid=domainguid, policyguid=policyguid, 
1008                         fill=samdb_fill, 
1009                         adminpass=adminpass, krbtgtpass=krbtgtpass,
1010                         hostguid=hostguid, invocationid=invocationid, 
1011                         machinepass=machinepass, dnspass=dnspass,
1012                         serverrole=serverrole, ldap_backend=ldap_backend, 
1013                         ldap_backend_type=ldap_backend_type)
1014
1015     if lp.get("server role") == "domain controller":
1016        policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies", 
1017                                   "{" + policyguid + "}")
1018        os.makedirs(policy_path, 0755)
1019        os.makedirs(os.path.join(policy_path, "Machine"), 0755)
1020        os.makedirs(os.path.join(policy_path, "User"), 0755)
1021        if not os.path.isdir(paths.netlogon):
1022             os.makedirs(paths.netlogon, 0755)
1023        secrets_ldb = Ldb(paths.secrets, session_info=session_info, 
1024                          credentials=credentials, lp=lp)
1025        secretsdb_become_dc(secrets_ldb, setup_path, domain=domain, realm=names.realm,
1026                            netbiosname=names.netbiosname, domainsid=domainsid, 
1027                            keytab_path=paths.keytab, samdb_url=paths.samdb, 
1028                            dns_keytab_path=paths.dns_keytab, dnspass=dnspass, 
1029                            machinepass=machinepass, dnsdomain=names.dnsdomain)
1030
1031     if samdb_fill == FILL_FULL:
1032         setup_name_mappings(samdb, str(domainsid), names.domaindn, root=root, 
1033                             nobody=nobody, nogroup=nogroup, wheel=wheel, 
1034                             users=users, backup=backup)
1035    
1036         message("Setting up sam.ldb rootDSE marking as synchronized")
1037         setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1038
1039         # Only make a zone file on the first DC, it should be replicated with DNS replication
1040         if serverrole == "domain controller":
1041             samdb = SamDB(paths.samdb, session_info=session_info, 
1042                       credentials=credentials, lp=lp)
1043
1044             domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1045             assert isinstance(domainguid, str)
1046             hostguid = samdb.searchone(basedn=domaindn, attribute="objectGUID",
1047                                        expression="(&(objectClass=computer)(cn=%s))" % names.hostname,
1048                                        scope=SCOPE_SUBTREE)
1049             assert isinstance(hostguid, str)
1050             
1051             message("Setting up DNS zone: %s" % names.dnsdomain)
1052             create_zone_file(paths.dns, setup_path, samdb, 
1053                              hostname=names.hostname, hostip=hostip, dnsdomain=names.dnsdomain,
1054                              domaindn=names.domaindn, dnspass=dnspass, realm=names.realm, 
1055                              domainguid=domainguid, hostguid=hostguid)
1056             message("Please install the zone located in %s into your DNS server" % paths.dns)
1057             
1058     message("Setting up phpLDAPadmin configuration")
1059     create_phpldapadmin_config(paths.phpldapadminconfig, setup_path, 
1060                                ldapi_url)
1061
1062     message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1063
1064     result = ProvisionResult()
1065     result.domaindn = domaindn
1066     result.paths = paths
1067     result.lp = lp
1068     result.samdb = samdb
1069     return result
1070
1071 def provision_become_dc(setup_dir=None,
1072                         smbconf=None, targetdir=None, realm=None, 
1073                         rootdn=None, domaindn=None, schemadn=None, configdn=None,
1074                         domain=None, hostname=None, domainsid=None, 
1075                         hostguid=None, adminpass=None, krbtgtpass=None, domainguid=None, 
1076                         policyguid=None, invocationid=None, machinepass=None, 
1077                         dnspass=None, root=None, nobody=None, nogroup=None, users=None, 
1078                         wheel=None, backup=None, aci=None, serverrole=None, 
1079                         ldap_backend=None, ldap_backend_type=None, sitename=DEFAULTSITE):
1080
1081     def message(text):
1082         """print a message if quiet is not set."""
1083         print text
1084
1085     provision(setup_dir, message, system_session(), None,
1086               smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS, realm=realm, 
1087               rootdn=rootdn, domaindn=domaindn, schemadn=schemadn, configdn=configdn, 
1088               domain=domain, hostname=hostname, hostip="127.0.0.1", domainsid=domainsid, machinepass=machinepass, serverrole="domain controller", sitename=sitename);
1089     
1090
1091 def setup_db_config(setup_path, file, dbdir):
1092     if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
1093         os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700);
1094     if not os.path.isdir(os.path.join(dbdir, "tmp")):
1095         os.makedirs(os.path.join(dbdir, "tmp"), 0700);
1096     
1097     setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
1098                {"LDAPDBDIR": dbdir})
1099     
1100
1101
1102 def provision_backend(setup_dir=None, message=None,
1103                       smbconf=None, targetdir=None, realm=None, 
1104                       rootdn=None, domaindn=None, schemadn=None, configdn=None,
1105                       domain=None, hostname=None, adminpass=None, root=None, serverrole=None, 
1106                       ldap_backend_type=None):
1107
1108     def setup_path(file):
1109         return os.path.join(setup_dir, file)
1110
1111     if hostname is None:
1112         hostname = gethostname().split(".")[0].lower()
1113
1114     if root is None:
1115         root = findnss(pwd.getpwnam, ["root"])[0]
1116
1117     lp = load_or_make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, targetdir)
1118
1119     names = guess_names(lp=lp, hostname=hostname, domain=domain, 
1120                         dnsdomain=realm, serverrole=serverrole, 
1121                         rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn)
1122
1123     paths = provision_paths_from_lp(lp, names.dnsdomain)
1124
1125     if not os.path.isdir(paths.ldapdir):
1126         os.makedirs(paths.ldapdir)
1127     schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
1128     try:
1129         os.unlink(schemadb_path)
1130     except:
1131         pass
1132
1133     schemadb = Ldb(schemadb_path, lp=lp)
1134  
1135     setup_add_ldif(schemadb, setup_path("provision_schema_basedn.ldif"), 
1136                    {"SCHEMADN": names.schemadn,
1137                     "ACI": "#",
1138                     "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb"
1139                     })
1140     setup_modify_ldif(schemadb, 
1141                       setup_path("provision_schema_basedn_modify.ldif"), \
1142                           {"SCHEMADN": names.schemadn,
1143                            "NETBIOSNAME": names.netbiosname,
1144                            "DEFAULTSITE": DEFAULTSITE,
1145                            "CONFIGDN": names.configdn,
1146                            })
1147     
1148     setup_add_ldif(schemadb, setup_path("schema_samba4.ldif"), 
1149                    {"SCHEMADN": names.schemadn })
1150     setup_add_ldif(schemadb, setup_path("schema.ldif"), 
1151                    {"SCHEMADN": names.schemadn})
1152
1153     if ldap_backend_type == "fedora-ds":
1154         setup_file(setup_path("fedora-ds.inf"), paths.fedoradsinf, 
1155                    {"ROOT": root,
1156                     "HOSTNAME": hostname,
1157                     "DNSDOMAIN": names.dnsdomain,
1158                     "LDAPDIR": paths.ldapdir,
1159                     "DOMAINDN": names.domaindn,
1160                     "LDAPMANAGERDN": names.ldapmanagerdn,
1161                     "LDAPMANAGERPASS": adminpass, 
1162                     "SERVERPORT": ""})
1163         
1164         setup_file(setup_path("fedora-partitions.ldif"), paths.fedoradspartitions, 
1165                    {"CONFIGDN": names.configdn,
1166                     "SCHEMADN": names.schemadn,
1167                     })
1168         
1169         setup_file(setup_path("fedora-partitions.ldif"), paths.fedoradspartitions, 
1170                    {"CONFIGDN": names.configdn,
1171                     "SCHEMADN": names.schemadn,
1172                     })
1173         mapping = "schema-map-fedora-ds-1.0"
1174         backend_schema = "99_ad.ldif"
1175     elif ldap_backend_type == "openldap":
1176         setup_file(setup_path("slapd.conf"), paths.slapdconf,
1177                    {"DNSDOMAIN": names.dnsdomain,
1178                     "LDAPDIR": paths.ldapdir,
1179                     "DOMAINDN": names.domaindn,
1180                     "CONFIGDN": names.configdn,
1181                     "SCHEMADN": names.schemadn,
1182                     "LDAPMANAGERDN": names.ldapmanagerdn,
1183                     "LDAPMANAGERPASS": adminpass})
1184         setup_file(setup_path("modules.conf"), paths.modulesconf,
1185                    {"REALM": names.realm})
1186         
1187         setup_db_config(setup_path, file, os.path.join(paths.ldapdir, "db", "user"))
1188         setup_db_config(setup_path, file, os.path.join(paths.ldapdir, "db", "config"))
1189         setup_db_config(setup_path, file, os.path.join(paths.ldapdir, "db", "schema"))
1190         mapping = "schema-map-openldap-2.3"
1191         backend_schema = "backend-schema.schema"
1192         
1193         attrs = ["linkID", "lDAPDisplayName"]
1194         res = schemadb.search(expression="(&(&(linkID=*)(!(linkID:1.2.840.113556.1.4.803:=1)))(objectclass=attributeSchema))", base=names.schemadn, scope=SCOPE_SUBTREE, attrs=attrs);
1195
1196         memberof_config = "# This is a generated file, do not edit!\n";
1197         refint_attributes = "";
1198         for i in range (0, len(res)):
1199             linkid = res[i]["linkID"][0]
1200             linkid = str(int(linkid) + 1)
1201             expression = "(&(objectclass=attributeSchema)(linkID=" + (linkid) + "))"
1202             target = schemadb.searchone(basedn=names.schemadn, 
1203                                         expression=expression, 
1204                                         attribute="lDAPDisplayName", 
1205                                         scope=SCOPE_SUBTREE);
1206             if target is not None:
1207                 refint_attributes = refint_attributes + " " + target + " " + res[i]["lDAPDisplayName"][0];
1208                 memberof_config = memberof_config + """overlay memberof
1209 memberof-dangling error
1210 memberof-refint TRUE
1211 memberof-group-oc top
1212 memberof-member-ad """ + res[i]["lDAPDisplayName"][0] + """
1213 memberof-memberof-ad """ + target + """
1214 memberof-dangling-error 32
1215
1216 """;
1217
1218         memberof_config = memberof_config + """
1219 overlay refint
1220 refint_attributes""" + refint_attributes + "\n";
1221         
1222         if os.path.exists(paths.memberofconf):
1223             os.unlink(paths.memberof.conf)
1224
1225         open(paths.memberofconf, 'w').write(memberof_config)
1226
1227         ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
1228         message("Start slapd with: slapd -f " + paths.ldapdir + "/slapd.conf -h " + ldapi_uri)
1229                 
1230
1231     schema_command = "bin/ad2oLschema --option=convert:target=" + ldap_backend_type + " -I " + setup_path(mapping) + " -H tdb://" + schemadb_path + " -O " + os.path.join(paths.ldapdir, backend_schema);
1232
1233     os.system(schema_command)
1234
1235
1236
1237 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1238     """Create a PHP LDAP admin configuration file.
1239
1240     :param path: Path to write the configuration to.
1241     :param setup_path: Function to generate setup paths.
1242     """
1243     setup_file(setup_path("phpldapadmin-config.php"), path, 
1244             {"S4_LDAPI_URI": ldapi_uri})
1245
1246
1247 def create_zone_file(path, setup_path, samdb, dnsdomain, domaindn, 
1248                   hostip, hostname, dnspass, realm, domainguid, hostguid):
1249     """Write out a DNS zone file, from the info in the current database.
1250     
1251     :param path: Path of the new file.
1252     :param setup_path": Setup path function.
1253     :param samdb: SamDB object
1254     :param dnsdomain: DNS Domain name
1255     :param domaindn: DN of the Domain
1256     :param hostip: Local IP
1257     :param hostname: Local hostname
1258     :param dnspass: Password for DNS
1259     :param realm: Realm name
1260     :param domainguid: GUID of the domain.
1261     :param hostguid: GUID of the host.
1262     """
1263     assert isinstance(domainguid, str)
1264
1265     setup_file(setup_path("provision.zone"), path, {
1266             "DNSPASS_B64": b64encode(dnspass),
1267             "HOSTNAME": hostname,
1268             "DNSDOMAIN": dnsdomain,
1269             "REALM": realm,
1270             "HOSTIP": hostip,
1271             "DOMAINGUID": domainguid,
1272             "DATESTRING": time.strftime("%Y%m%d%H"),
1273             "DEFAULTSITE": DEFAULTSITE,
1274             "HOSTGUID": hostguid,
1275         })
1276
1277 def load_schema(setup_path, samdb, schemadn, netbiosname, configdn, sitename):
1278     """Load schema for the SamDB.
1279     
1280     :param samdb: Load a schema into a SamDB.
1281     :param setup_path: Setup path function.
1282     :param schemadn: DN of the schema
1283     :param netbiosname: NetBIOS name of the host.
1284     :param configdn: DN of the configuration
1285     """
1286     schema_data = open(setup_path("schema.ldif"), 'r').read()
1287     schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
1288     schema_data = substitute_var(schema_data, {"SCHEMADN": schemadn})
1289     head_data = open(setup_path("provision_schema_basedn_modify.ldif"), 'r').read()
1290     head_data = substitute_var(head_data, {
1291                     "SCHEMADN": schemadn,
1292                     "NETBIOSNAME": netbiosname,
1293                     "CONFIGDN": configdn,
1294                     "DEFAULTSITE":sitename 
1295     })
1296     samdb.attach_schema_from_ldif(head_data, schema_data)
1297