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