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