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