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