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