963410e03d9bc991c91c88de4baffa8841773640
[abartlet/samba.git/.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     samdb = SamDB(samdb_path, session_info=session_info, 
436                   credentials=credentials, lp=lp)
437
438     # Wipes the database
439     try:
440         samdb.erase()
441     except:
442         os.unlink(samdb_path)
443
444     samdb = SamDB(samdb_path, session_info=session_info, 
445                   credentials=credentials, lp=lp)
446
447     #Add modules to the list to activate them by default
448     #beware often order is important
449     #
450     # Some Known ordering constraints:
451     # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
452     # - objectclass must be before password_hash, because password_hash checks
453     #   that the objectclass is of type person (filled in by objectclass
454     #   module when expanding the objectclass list)
455     # - partition must be last
456     # - each partition has its own module list then
457     modules_list = ["rootdse",
458                     "paged_results",
459                     "ranged_results",
460                     "anr",
461                     "server_sort",
462                     "asq",
463                     "extended_dn_store",
464                     "extended_dn_in",
465                     "rdn_name",
466                     "objectclass",
467                     "samldb",
468                     "kludge_acl",
469                     "password_hash",
470                     "operational"]
471     tdb_modules_list = [
472                     "subtree_rename",
473                     "subtree_delete",
474                     "linked_attributes",
475                     "extended_dn_out_ldb"]
476     modules_list2 = ["show_deleted",
477                      "partition"]
478  
479     domaindn_ldb = "users.ldb"
480     if ldap_backend is not None:
481         domaindn_ldb = ldap_backend
482     configdn_ldb = "configuration.ldb"
483     if ldap_backend is not None:
484         configdn_ldb = ldap_backend
485     schemadn_ldb = "schema.ldb"
486     if ldap_backend is not None:
487         schema_ldb = ldap_backend
488         schemadn_ldb = ldap_backend
489         
490     if ldap_backend_type == "fedora-ds":
491         backend_modules = ["nsuniqueid", "paged_searches"]
492         # We can handle linked attributes here, as we don't have directory-side subtree operations
493         tdb_modules_list = ["linked_attributes", "extended_dn_out_dereference"]
494     elif ldap_backend_type == "openldap":
495         backend_modules = ["normalise", "entryuuid", "paged_searches"]
496         # OpenLDAP handles subtree renames, so we don't want to do any of these things
497         tdb_modules_list = ["extended_dn_out_dereference"]
498     elif ldap_backend is not None:
499         raise "LDAP Backend specified, but LDAP Backend Type not specified"
500     elif serverrole == "domain controller":
501         backend_modules = ["repl_meta_data"]
502     else:
503         backend_modules = ["objectguid"]
504
505     if tdb_modules_list is None:
506         tdb_modules_list_as_string = ""
507     else:
508         tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
509         
510     samdb.transaction_start()
511     try:
512         setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
513                 "SCHEMADN": names.schemadn, 
514                 "SCHEMADN_LDB": schemadn_ldb,
515                 "SCHEMADN_MOD2": ",objectguid",
516                 "CONFIGDN": names.configdn,
517                 "CONFIGDN_LDB": configdn_ldb,
518                 "DOMAINDN": names.domaindn,
519                 "DOMAINDN_LDB": domaindn_ldb,
520                 "SCHEMADN_MOD": "schema_fsmo,instancetype",
521                 "CONFIGDN_MOD": "naming_fsmo,instancetype",
522                 "DOMAINDN_MOD": "pdc_fsmo,instancetype",
523                 "MODULES_LIST": ",".join(modules_list),
524                 "TDB_MODULES_LIST": tdb_modules_list_as_string,
525                 "MODULES_LIST2": ",".join(modules_list2),
526                 "BACKEND_MOD": ",".join(backend_modules),
527         })
528
529     except:
530         samdb.transaction_cancel()
531         raise
532
533     samdb.transaction_commit()
534     
535     samdb = SamDB(samdb_path, session_info=session_info, 
536                   credentials=credentials, lp=lp)
537
538     samdb.transaction_start()
539     try:
540         message("Setting up sam.ldb attributes")
541         samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
542
543         message("Setting up sam.ldb rootDSE")
544         setup_samdb_rootdse(samdb, setup_path, names)
545
546         if erase:
547             message("Erasing data from partitions")
548             samdb.erase_partitions()
549
550     except:
551         samdb.transaction_cancel()
552         raise
553
554     samdb.transaction_commit()
555     
556     return samdb
557
558
559 def secretsdb_become_dc(secretsdb, setup_path, domain, realm, dnsdomain, 
560                         netbiosname, domainsid, keytab_path, samdb_url, 
561                         dns_keytab_path, dnspass, machinepass):
562     """Add DC-specific bits to a secrets database.
563     
564     :param secretsdb: Ldb Handle to the secrets database
565     :param setup_path: Setup path function
566     :param machinepass: Machine password
567     """
568     setup_ldb(secretsdb, setup_path("secrets_dc.ldif"), { 
569             "MACHINEPASS_B64": b64encode(machinepass),
570             "DOMAIN": domain,
571             "REALM": realm,
572             "DNSDOMAIN": dnsdomain,
573             "DOMAINSID": str(domainsid),
574             "SECRETS_KEYTAB": keytab_path,
575             "NETBIOSNAME": netbiosname,
576             "SAM_LDB": samdb_url,
577             "DNS_KEYTAB": dns_keytab_path,
578             "DNSPASS_B64": b64encode(dnspass),
579             })
580
581
582 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
583     """Setup the secrets database.
584
585     :param path: Path to the secrets database.
586     :param setup_path: Get the path to a setup file.
587     :param session_info: Session info.
588     :param credentials: Credentials
589     :param lp: Loadparm context
590     :return: LDB handle for the created secrets database
591     """
592     if os.path.exists(path):
593         os.unlink(path)
594     secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
595                       lp=lp)
596     secrets_ldb.erase()
597     secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
598     secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
599                       lp=lp)
600     secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
601
602     if credentials is not None and credentials.authentication_requested():
603         if credentials.get_bind_dn() is not None:
604             setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
605                     "LDAPMANAGERDN": credentials.get_bind_dn(),
606                     "LDAPMANAGERPASS_B64": b64encode(credentials.get_password())
607                     })
608         else:
609             setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
610                     "LDAPADMINUSER": credentials.get_username(),
611                     "LDAPADMINREALM": credentials.get_realm(),
612                     "LDAPADMINPASS_B64": b64encode(credentials.get_password())
613                     })
614
615     return secrets_ldb
616
617
618 def setup_templatesdb(path, setup_path, session_info, credentials, lp):
619     """Setup the templates database.
620
621     :param path: Path to the database.
622     :param setup_path: Function for obtaining the path to setup files.
623     :param session_info: Session info
624     :param credentials: Credentials
625     :param lp: Loadparm context
626     """
627     templates_ldb = SamDB(path, session_info=session_info,
628                           credentials=credentials, lp=lp)
629     # Wipes the database
630     try:
631         templates_ldb.erase()
632     except:
633         os.unlink(path)
634
635     templates_ldb.load_ldif_file_add(setup_path("provision_templates_init.ldif"))
636
637     templates_ldb = SamDB(path, session_info=session_info,
638                           credentials=credentials, lp=lp)
639
640     templates_ldb.load_ldif_file_add(setup_path("provision_templates.ldif"))
641
642
643 def setup_registry(path, setup_path, session_info, credentials, lp):
644     """Setup the registry.
645     
646     :param path: Path to the registry database
647     :param setup_path: Function that returns the path to a setup.
648     :param session_info: Session information
649     :param credentials: Credentials
650     :param lp: Loadparm context
651     """
652     reg = registry.Registry()
653     hive = registry.open_ldb(path, session_info=session_info, 
654                          credentials=credentials, lp_ctx=lp)
655     reg.mount_hive(hive, "HKEY_LOCAL_MACHINE")
656     provision_reg = setup_path("provision.reg")
657     assert os.path.exists(provision_reg)
658     reg.diff_apply(provision_reg)
659
660
661 def setup_idmapdb(path, setup_path, session_info, credentials, lp):
662     """Setup the idmap database.
663
664     :param path: path to the idmap database
665     :param setup_path: Function that returns a path to a setup file
666     :param session_info: Session information
667     :param credentials: Credentials
668     :param lp: Loadparm context
669     """
670     if os.path.exists(path):
671         os.unlink(path)
672
673     idmap_ldb = IDmapDB(path, session_info=session_info,
674                         credentials=credentials, lp=lp)
675
676     idmap_ldb.erase()
677     idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
678     return idmap_ldb
679
680
681 def setup_samdb_rootdse(samdb, setup_path, names):
682     """Setup the SamDB rootdse.
683
684     :param samdb: Sam Database handle
685     :param setup_path: Obtain setup path
686     """
687     setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
688         "SCHEMADN": names.schemadn, 
689         "NETBIOSNAME": names.netbiosname,
690         "DNSDOMAIN": names.dnsdomain,
691         "REALM": names.realm,
692         "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
693         "DOMAINDN": names.domaindn,
694         "ROOTDN": names.rootdn,
695         "CONFIGDN": names.configdn,
696         "SERVERDN": names.serverdn,
697         })
698         
699
700 def setup_self_join(samdb, names,
701                     machinepass, dnspass, 
702                     domainsid, invocationid, setup_path,
703                     policyguid):
704     """Join a host to its own domain."""
705     assert isinstance(invocationid, str)
706     setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), { 
707               "CONFIGDN": names.configdn, 
708               "SCHEMADN": names.schemadn,
709               "DOMAINDN": names.domaindn,
710               "SERVERDN": names.serverdn,
711               "INVOCATIONID": invocationid,
712               "NETBIOSNAME": names.netbiosname,
713               "DEFAULTSITE": names.sitename,
714               "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
715               "MACHINEPASS_B64": b64encode(machinepass),
716               "DNSPASS_B64": b64encode(dnspass),
717               "REALM": names.realm,
718               "DOMAIN": names.domain,
719               "DNSDOMAIN": names.dnsdomain})
720     setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), { 
721               "POLICYGUID": policyguid,
722               "DNSDOMAIN": names.dnsdomain,
723               "DOMAINSID": str(domainsid),
724               "DOMAINDN": names.domaindn})
725
726
727 def setup_samdb(path, setup_path, session_info, credentials, lp, 
728                 names, message, 
729                 domainsid, aci, domainguid, policyguid, 
730                 fill, adminpass, krbtgtpass, 
731                 machinepass, invocationid, dnspass,
732                 serverrole, ldap_backend=None, 
733                 ldap_backend_type=None):
734     """Setup a complete SAM Database.
735     
736     :note: This will wipe the main SAM database file!
737     """
738
739     erase = (fill != FILL_DRS)
740
741     # Also wipes the database
742     setup_samdb_partitions(path, setup_path, message=message, lp=lp,
743                            credentials=credentials, session_info=session_info,
744                            names=names, 
745                            ldap_backend=ldap_backend, serverrole=serverrole,
746                            ldap_backend_type=ldap_backend_type, erase=erase)
747
748     samdb = SamDB(path, session_info=session_info, 
749                   credentials=credentials, lp=lp)
750     if fill == FILL_DRS:
751         return samdb
752
753     message("Pre-loading the Samba 4 and AD schema")
754     samdb.set_domain_sid(domainsid)
755     if serverrole == "domain controller":
756         samdb.set_invocation_id(invocationid)
757
758     load_schema(setup_path, samdb, names.schemadn, names.netbiosname, 
759                 names.configdn, names.sitename, names.serverdn,
760                 names.hostname)
761
762     samdb.transaction_start()
763         
764     try:
765         message("Adding DomainDN: %s (permitted to fail)" % names.domaindn)
766         if serverrole == "domain controller":
767             domain_oc = "domainDNS"
768         else:
769             domain_oc = "samba4LocalDomain"
770
771         setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
772                 "DOMAINDN": names.domaindn,
773                 "ACI": aci,
774                 "DOMAIN_OC": domain_oc
775                 })
776
777         message("Modifying DomainDN: " + names.domaindn + "")
778         if domainguid is not None:
779             domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
780         else:
781             domainguid_mod = ""
782
783         setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
784             "LDAPTIME": timestring(int(time.time())),
785             "DOMAINSID": str(domainsid),
786             "SCHEMADN": names.schemadn, 
787             "NETBIOSNAME": names.netbiosname,
788             "DEFAULTSITE": names.sitename,
789             "CONFIGDN": names.configdn,
790             "SERVERDN": names.serverdn,
791             "POLICYGUID": policyguid,
792             "DOMAINDN": names.domaindn,
793             "DOMAINGUID_MOD": domainguid_mod,
794             })
795
796         message("Adding configuration container (permitted to fail)")
797         setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
798             "CONFIGDN": names.configdn, 
799             "ACI": aci,
800             })
801         message("Modifying configuration container")
802         setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
803             "CONFIGDN": names.configdn, 
804             "SCHEMADN": names.schemadn,
805             })
806
807         message("Adding schema container (permitted to fail)")
808         setup_add_ldif(samdb, setup_path("provision_schema_basedn.ldif"), {
809             "SCHEMADN": names.schemadn,
810             "ACI": aci,
811             })
812         message("Modifying schema container")
813
814         prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
815
816         setup_modify_ldif(samdb, 
817             setup_path("provision_schema_basedn_modify.ldif"), {
818             "SCHEMADN": names.schemadn,
819             "NETBIOSNAME": names.netbiosname,
820             "DEFAULTSITE": names.sitename,
821             "CONFIGDN": names.configdn,
822             "SERVERDN": names.serverdn,
823             "PREFIXMAP_B64": b64encode(prefixmap)
824             })
825
826         message("Setting up sam.ldb Samba4 schema")
827         setup_add_ldif(samdb, setup_path("schema_samba4.ldif"), 
828                        {"SCHEMADN": names.schemadn })
829         message("Setting up sam.ldb AD schema")
830         setup_add_ldif(samdb, setup_path("schema.ldif"), 
831                        {"SCHEMADN": names.schemadn})
832
833         message("Setting up sam.ldb configuration data")
834         setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
835             "CONFIGDN": names.configdn,
836             "NETBIOSNAME": names.netbiosname,
837             "DEFAULTSITE": names.sitename,
838             "DNSDOMAIN": names.dnsdomain,
839             "DOMAIN": names.domain,
840             "SCHEMADN": names.schemadn,
841             "DOMAINDN": names.domaindn,
842             "SERVERDN": names.serverdn
843             })
844
845         message("Setting up display specifiers")
846         setup_add_ldif(samdb, setup_path("display_specifiers.ldif"), 
847                        {"CONFIGDN": names.configdn})
848
849         message("Adding users container (permitted to fail)")
850         setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
851                 "DOMAINDN": names.domaindn})
852         message("Modifying users container")
853         setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
854                 "DOMAINDN": names.domaindn})
855         message("Adding computers container (permitted to fail)")
856         setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
857                 "DOMAINDN": names.domaindn})
858         message("Modifying computers container")
859         setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
860                 "DOMAINDN": names.domaindn})
861         message("Setting up sam.ldb data")
862         setup_add_ldif(samdb, setup_path("provision.ldif"), {
863             "DOMAINDN": names.domaindn,
864             "NETBIOSNAME": names.netbiosname,
865             "DEFAULTSITE": names.sitename,
866             "CONFIGDN": names.configdn,
867             "SERVERDN": names.serverdn
868             })
869
870         if fill == FILL_FULL:
871             message("Setting up sam.ldb users and groups")
872             setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
873                 "DOMAINDN": names.domaindn,
874                 "DOMAINSID": str(domainsid),
875                 "CONFIGDN": names.configdn,
876                 "ADMINPASS_B64": b64encode(adminpass),
877                 "KRBTGTPASS_B64": b64encode(krbtgtpass),
878                 })
879
880             if serverrole == "domain controller":
881                 message("Setting up self join")
882                 setup_self_join(samdb, names=names, invocationid=invocationid, 
883                                 dnspass=dnspass,  
884                                 machinepass=machinepass, 
885                                 domainsid=domainsid, policyguid=policyguid,
886                                 setup_path=setup_path)
887
888     except:
889         samdb.transaction_cancel()
890         raise
891
892     samdb.transaction_commit()
893     return samdb
894
895
896 FILL_FULL = "FULL"
897 FILL_NT4SYNC = "NT4SYNC"
898 FILL_DRS = "DRS"
899
900 def provision(setup_dir, message, session_info, 
901               credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL, realm=None, 
902               rootdn=None, domaindn=None, schemadn=None, configdn=None, 
903               serverdn=None,
904               domain=None, hostname=None, hostip=None, hostip6=None, 
905               domainsid=None, adminpass=None, krbtgtpass=None, domainguid=None, 
906               policyguid=None, invocationid=None, machinepass=None, 
907               dnspass=None, root=None, nobody=None, nogroup=None, users=None, 
908               wheel=None, backup=None, aci=None, serverrole=None, 
909               ldap_backend=None, ldap_backend_type=None, sitename=None):
910     """Provision samba4
911     
912     :note: caution, this wipes all existing data!
913     """
914
915     def setup_path(file):
916         return os.path.join(setup_dir, file)
917
918     if domainsid is None:
919         domainsid = security.random_sid()
920     else:
921         domainsid = security.Sid(domainsid)
922
923     if policyguid is None:
924         policyguid = str(uuid.uuid4())
925     if adminpass is None:
926         adminpass = misc.random_password(12)
927     if krbtgtpass is None:
928         krbtgtpass = misc.random_password(12)
929     if machinepass is None:
930         machinepass  = misc.random_password(12)
931     if dnspass is None:
932         dnspass = misc.random_password(12)
933     root_uid = findnss_uid([root or "root"])
934     nobody_uid = findnss_uid([nobody or "nobody"])
935     users_gid = findnss_gid([users or "users"])
936     if wheel is None:
937         wheel_gid = findnss_gid(["wheel", "adm"])
938     else:
939         wheel_gid = findnss_gid([wheel])
940     if aci is None:
941         aci = "# no aci for local ldb"
942
943     if targetdir is not None:
944         if (not os.path.exists(os.path.join(targetdir, "etc"))):
945             os.makedirs(os.path.join(targetdir, "etc"))
946         smbconf = os.path.join(targetdir, "etc", "smb.conf")
947
948     # only install a new smb.conf if there isn't one there already
949     if not os.path.exists(smbconf):
950         make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, 
951                      targetdir)
952
953     lp = param.LoadParm()
954     lp.load(smbconf)
955
956     names = guess_names(lp=lp, hostname=hostname, domain=domain, 
957                         dnsdomain=realm, serverrole=serverrole, sitename=sitename,
958                         rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn,
959                         serverdn=serverdn)
960
961     paths = provision_paths_from_lp(lp, names.dnsdomain)
962
963     if hostip is None:
964         try:
965             hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
966         except socket.gaierror, (socket.EAI_NODATA, msg):
967             hostip = None
968
969     if hostip6 is None:
970         try:
971             hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
972         except socket.gaierror, (socket.EAI_NODATA, msg): 
973             hostip6 = None
974
975     if serverrole is None:
976         serverrole = lp.get("server role")
977
978     assert serverrole in ("domain controller", "member server", "standalone")
979     if invocationid is None and serverrole == "domain controller":
980         invocationid = str(uuid.uuid4())
981
982     if not os.path.exists(paths.private_dir):
983         os.mkdir(paths.private_dir)
984
985     ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
986     
987     if ldap_backend is not None:
988         if ldap_backend == "ldapi":
989             # provision-backend will set this path suggested slapd command line / fedorads.inf
990             ldap_backend = "ldapi://%s" % urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
991              
992     # only install a new shares config db if there is none
993     if not os.path.exists(paths.shareconf):
994         message("Setting up share.ldb")
995         share_ldb = Ldb(paths.shareconf, session_info=session_info, 
996                         credentials=credentials, lp=lp)
997         share_ldb.load_ldif_file_add(setup_path("share.ldif"))
998
999      
1000     message("Setting up secrets.ldb")
1001     secrets_ldb = setup_secretsdb(paths.secrets, setup_path, 
1002                                   session_info=session_info, 
1003                                   credentials=credentials, lp=lp)
1004
1005     message("Setting up the registry")
1006     setup_registry(paths.hklm, setup_path, session_info, 
1007                    credentials=credentials, lp=lp)
1008
1009     message("Setting up templates db")
1010     setup_templatesdb(paths.templates, setup_path, session_info=session_info, 
1011                       credentials=credentials, lp=lp)
1012
1013     message("Setting up idmap db")
1014     idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1015                           credentials=credentials, lp=lp)
1016
1017     samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info, 
1018                         credentials=credentials, lp=lp, names=names,
1019                         message=message, 
1020                         domainsid=domainsid, 
1021                         aci=aci, domainguid=domainguid, policyguid=policyguid, 
1022                         fill=samdb_fill, 
1023                         adminpass=adminpass, krbtgtpass=krbtgtpass,
1024                         invocationid=invocationid, 
1025                         machinepass=machinepass, dnspass=dnspass,
1026                         serverrole=serverrole, ldap_backend=ldap_backend, 
1027                         ldap_backend_type=ldap_backend_type)
1028
1029     if lp.get("server role") == "domain controller":
1030         if paths.netlogon is None:
1031             message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1032             message("Please either remove %s or see the template at %s" % 
1033                     ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1034             assert(paths.netlogon is not None)
1035
1036         if paths.sysvol is None:
1037             message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1038             message("Please either remove %s or see the template at %s" % 
1039                     (paths.smbconf, setup_path("provision.smb.conf.dc")))
1040             assert(paths.sysvol is not None)            
1041             
1042         policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies", 
1043                                    "{" + policyguid + "}")
1044         os.makedirs(policy_path, 0755)
1045         open(os.path.join(policy_path, "GPT.INI"), 'w').write("")
1046         os.makedirs(os.path.join(policy_path, "Machine"), 0755)
1047         os.makedirs(os.path.join(policy_path, "User"), 0755)
1048         if not os.path.isdir(paths.netlogon):
1049             os.makedirs(paths.netlogon, 0755)
1050
1051     if samdb_fill == FILL_FULL:
1052         setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1053                             root_uid=root_uid, nobody_uid=nobody_uid,
1054                             users_gid=users_gid, wheel_gid=wheel_gid)
1055
1056         message("Setting up sam.ldb rootDSE marking as synchronized")
1057         setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1058
1059         # Only make a zone file on the first DC, it should be replicated with DNS replication
1060         if serverrole == "domain controller":
1061             secrets_ldb = Ldb(paths.secrets, session_info=session_info, 
1062                               credentials=credentials, lp=lp)
1063             secretsdb_become_dc(secrets_ldb, setup_path, domain=domain, realm=names.realm,
1064                                 netbiosname=names.netbiosname, domainsid=domainsid, 
1065                                 keytab_path=paths.keytab, samdb_url=paths.samdb, 
1066                                 dns_keytab_path=paths.dns_keytab, dnspass=dnspass, 
1067                                 machinepass=machinepass, dnsdomain=names.dnsdomain)
1068
1069             samdb = SamDB(paths.samdb, session_info=session_info, 
1070                       credentials=credentials, lp=lp)
1071
1072             domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1073             assert isinstance(domainguid, str)
1074             hostguid = samdb.searchone(basedn=domaindn, attribute="objectGUID",
1075                                        expression="(&(objectClass=computer)(cn=%s))" % names.hostname,
1076                                        scope=SCOPE_SUBTREE)
1077             assert isinstance(hostguid, str)
1078
1079             create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1080                              domaindn=names.domaindn, hostip=hostip,
1081                              hostip6=hostip6, hostname=names.hostname,
1082                              dnspass=dnspass, realm=names.realm,
1083                              domainguid=domainguid, hostguid=hostguid)
1084
1085             create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1086                               dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1087
1088             create_named_txt(paths.namedtxt, 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 an example configuration include file for BIND" % paths.namedconf)
1092             message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1093
1094             create_krb5_conf(paths.krb5conf, setup_path, dnsdomain=names.dnsdomain,
1095                              hostname=names.hostname, realm=names.realm)
1096             message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1097
1098     create_phpldapadmin_config(paths.phpldapadminconfig, setup_path, 
1099                                ldapi_url)
1100
1101     message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1102
1103     message("Once the above files are installed, your Samba4 server will be ready to use")
1104     message("Server Role:    %s" % serverrole)
1105     message("Hostname:       %s" % names.hostname)
1106     message("NetBIOS Domain: %s" % names.domain)
1107     message("DNS Domain:     %s" % names.dnsdomain)
1108     message("DOMAIN SID:     %s" % str(domainsid))
1109     message("Admin password: %s" % adminpass)
1110
1111     result = ProvisionResult()
1112     result.domaindn = domaindn
1113     result.paths = paths
1114     result.lp = lp
1115     result.samdb = samdb
1116     return result
1117
1118
1119 def provision_become_dc(setup_dir=None,
1120                         smbconf=None, targetdir=None, realm=None, 
1121                         rootdn=None, domaindn=None, schemadn=None, configdn=None,
1122                         serverdn=None,
1123                         domain=None, hostname=None, domainsid=None, 
1124                         adminpass=None, krbtgtpass=None, domainguid=None, 
1125                         policyguid=None, invocationid=None, machinepass=None, 
1126                         dnspass=None, root=None, nobody=None, nogroup=None, users=None, 
1127                         wheel=None, backup=None, aci=None, serverrole=None, 
1128                         ldap_backend=None, ldap_backend_type=None, sitename=None):
1129
1130     def message(text):
1131         """print a message if quiet is not set."""
1132         print text
1133
1134     return provision(setup_dir, message, system_session(), None,
1135               smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS, realm=realm, 
1136               rootdn=rootdn, domaindn=domaindn, schemadn=schemadn, configdn=configdn, serverdn=serverdn,
1137               domain=domain, hostname=hostname, hostip="127.0.0.1", domainsid=domainsid, machinepass=machinepass, serverrole="domain controller", sitename=sitename)
1138     
1139
1140 def setup_db_config(setup_path, dbdir):
1141     """Setup a Berkeley database.
1142     
1143     :param setup_path: Setup path function.
1144     :param dbdir: Database directory."""
1145     if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
1146         os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700)
1147     if not os.path.isdir(os.path.join(dbdir, "tmp")):
1148         os.makedirs(os.path.join(dbdir, "tmp"), 0700)
1149     
1150     setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
1151                {"LDAPDBDIR": dbdir})
1152     
1153
1154
1155 def provision_backend(setup_dir=None, message=None,
1156                       smbconf=None, targetdir=None, realm=None, 
1157                       rootdn=None, domaindn=None, schemadn=None, configdn=None,
1158                       domain=None, hostname=None, adminpass=None, root=None, serverrole=None, 
1159                       ldap_backend_type=None, ldap_backend_port=None,
1160                       ol_mmr_urls=None):
1161
1162     def setup_path(file):
1163         return os.path.join(setup_dir, file)
1164
1165     if hostname is None:
1166         hostname = socket.gethostname().split(".")[0].lower()
1167
1168     if root is None:
1169         root = findnss(pwd.getpwnam, ["root"])[0]
1170
1171     if adminpass is None:
1172         adminpass = misc.random_password(12)
1173
1174     if targetdir is not None:
1175         if (not os.path.exists(os.path.join(targetdir, "etc"))):
1176             os.makedirs(os.path.join(targetdir, "etc"))
1177         smbconf = os.path.join(targetdir, "etc", "smb.conf")
1178
1179     # only install a new smb.conf if there isn't one there already
1180     if not os.path.exists(smbconf):
1181         make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, 
1182                      targetdir)
1183
1184     lp = param.LoadParm()
1185     lp.load(smbconf)
1186
1187     names = guess_names(lp=lp, hostname=hostname, domain=domain, 
1188                         dnsdomain=realm, serverrole=serverrole, 
1189                         rootdn=rootdn, domaindn=domaindn, configdn=configdn, 
1190                         schemadn=schemadn)
1191
1192     paths = provision_paths_from_lp(lp, names.dnsdomain)
1193
1194     if not os.path.isdir(paths.ldapdir):
1195         os.makedirs(paths.ldapdir, 0700)
1196     schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
1197     try:
1198         os.unlink(schemadb_path)
1199     except:
1200         pass
1201
1202     schemadb = Ldb(schemadb_path, lp=lp)
1203  
1204     prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
1205
1206     setup_add_ldif(schemadb, setup_path("provision_schema_basedn.ldif"), 
1207                    {"SCHEMADN": names.schemadn,
1208                     "ACI": "#",
1209                     })
1210     setup_modify_ldif(schemadb, 
1211                       setup_path("provision_schema_basedn_modify.ldif"), \
1212                           {"SCHEMADN": names.schemadn,
1213                            "NETBIOSNAME": names.netbiosname,
1214                            "DEFAULTSITE": DEFAULTSITE,
1215                            "CONFIGDN": names.configdn,
1216                            "SERVERDN": names.serverdn,
1217                            "PREFIXMAP_B64": b64encode(prefixmap)
1218                            })
1219     
1220     setup_add_ldif(schemadb, setup_path("schema_samba4.ldif"), 
1221                    {"SCHEMADN": names.schemadn })
1222     setup_add_ldif(schemadb, setup_path("schema.ldif"), 
1223                    {"SCHEMADN": names.schemadn})
1224
1225     if ldap_backend_type == "fedora-ds":
1226         if ldap_backend_port is not None:
1227             serverport = "ServerPort=%d" % ldap_backend_port
1228         else:
1229             serverport = ""
1230
1231         setup_file(setup_path("fedorads.inf"), paths.fedoradsinf, 
1232                    {"ROOT": root,
1233                     "HOSTNAME": hostname,
1234                     "DNSDOMAIN": names.dnsdomain,
1235                     "LDAPDIR": paths.ldapdir,
1236                     "DOMAINDN": names.domaindn,
1237                     "LDAPMANAGERDN": names.ldapmanagerdn,
1238                     "LDAPMANAGERPASS": adminpass, 
1239                     "SERVERPORT": serverport})
1240         
1241         setup_file(setup_path("fedorads-partitions.ldif"), paths.fedoradspartitions, 
1242                    {"CONFIGDN": names.configdn,
1243                     "SCHEMADN": names.schemadn,
1244                     })
1245         
1246         mapping = "schema-map-fedora-ds-1.0"
1247         backend_schema = "99_ad.ldif"
1248         
1249         slapdcommand="Initailise Fedora DS with: setup-ds.pl --file=%s" % paths.fedoradsinf
1250        
1251         ldapuser = "--simple-bind-dn=" + names.ldapmanagerdn
1252
1253     elif ldap_backend_type == "openldap":
1254         attrs = ["linkID", "lDAPDisplayName"]
1255         res = schemadb.search(expression="(&(&(linkID=*)(!(linkID:1.2.840.113556.1.4.803:=1)))(objectclass=attributeSchema))", base=names.schemadn, scope=SCOPE_SUBTREE, attrs=attrs)
1256
1257         memberof_config = "# Generated from schema in %s\n" % schemadb_path
1258         refint_attributes = ""
1259         for i in range (0, len(res)):
1260             expression = "(&(objectclass=attributeSchema)(linkID=%d))" % (int(res[i]["linkID"][0])+1)
1261             target = schemadb.searchone(basedn=names.schemadn, 
1262                                         expression=expression, 
1263                                         attribute="lDAPDisplayName", 
1264                                         scope=SCOPE_SUBTREE)
1265             if target is not None:
1266                 refint_attributes = refint_attributes + " " + target + " " + res[i]["lDAPDisplayName"][0]
1267             
1268                 memberof_config += read_and_sub_file(setup_path("memberof.conf"),
1269                                                      { "MEMBER_ATTR" : str(res[i]["lDAPDisplayName"][0]),
1270                                                        "MEMBEROF_ATTR" : str(target) })
1271
1272         refint_config = read_and_sub_file(setup_path("refint.conf"),
1273                                             { "LINK_ATTRS" : refint_attributes})
1274
1275 # generate serverids, ldap-urls and syncrepl-blocks for mmr hosts
1276         mmr_on_config = ""
1277         mmr_replicator_acl = ""
1278         mmr_serverids_config = ""
1279         mmr_syncrepl_schema_config = "" 
1280         mmr_syncrepl_config_config = "" 
1281         mmr_syncrepl_user_config = "" 
1282         
1283         if ol_mmr_urls is not None:
1284                 # For now, make these equal
1285                 mmr_pass = adminpass
1286
1287                 url_list=filter(None,ol_mmr_urls.split(' ')) 
1288                 if (len(url_list) == 1):
1289                     url_list=filter(None,ol_mmr_urls.split(',')) 
1290                      
1291
1292                 mmr_on_config = "MirrorMode On"
1293                 mmr_replicator_acl = "  by dn=cn=replicator,cn=samba read"
1294                 serverid=0
1295                 for url in url_list:
1296                         serverid=serverid+1
1297                         mmr_serverids_config += read_and_sub_file(setup_path("mmr_serverids.conf"),
1298                                                                      { "SERVERID" : str(serverid),
1299                                                                        "LDAPSERVER" : url })
1300                         rid=serverid*10
1301                         rid=rid+1
1302                         mmr_syncrepl_schema_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1303                                                                      {  "RID" : str(rid),
1304                                                                         "MMRDN": names.schemadn,
1305                                                                         "LDAPSERVER" : url,
1306                                                                         "MMR_PASSWORD": mmr_pass})
1307
1308                         rid=rid+1
1309                         mmr_syncrepl_config_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1310                                                                      {  "RID" : str(rid),
1311                                                                         "MMRDN": names.configdn,
1312                                                                         "LDAPSERVER" : url,
1313                                                                         "MMR_PASSWORD": mmr_pass})
1314
1315                         rid=rid+1
1316                         mmr_syncrepl_user_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1317                                                                      {  "RID" : str(rid),
1318                                                                         "MMRDN": names.domaindn,
1319                                                                         "LDAPSERVER" : url,
1320                                                                         "MMR_PASSWORD": mmr_pass })
1321
1322
1323         setup_file(setup_path("slapd.conf"), paths.slapdconf,
1324                    {"DNSDOMAIN": names.dnsdomain,
1325                     "LDAPDIR": paths.ldapdir,
1326                     "DOMAINDN": names.domaindn,
1327                     "CONFIGDN": names.configdn,
1328                     "SCHEMADN": names.schemadn,
1329                     "MEMBEROF_CONFIG": memberof_config,
1330                     "MIRRORMODE": mmr_on_config,
1331                     "REPLICATOR_ACL": mmr_replicator_acl,
1332                     "MMR_SERVERIDS_CONFIG": mmr_serverids_config,
1333                     "MMR_SYNCREPL_SCHEMA_CONFIG": mmr_syncrepl_schema_config,
1334                     "MMR_SYNCREPL_CONFIG_CONFIG": mmr_syncrepl_config_config,
1335                     "MMR_SYNCREPL_USER_CONFIG": mmr_syncrepl_user_config,
1336                     "REFINT_CONFIG": refint_config})
1337         setup_file(setup_path("modules.conf"), paths.modulesconf,
1338                    {"REALM": names.realm})
1339         
1340         setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "user"))
1341         setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "config"))
1342         setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "schema"))
1343
1344         if not os.path.exists(os.path.join(paths.ldapdir, "db", "samba",  "cn=samba")):
1345             os.makedirs(os.path.join(paths.ldapdir, "db", "samba",  "cn=samba"), 0700)
1346
1347         setup_file(setup_path("cn=samba.ldif"), 
1348                    os.path.join(paths.ldapdir, "db", "samba",  "cn=samba.ldif"),
1349                    { "UUID": str(uuid.uuid4()), 
1350                      "LDAPTIME": timestring(int(time.time()))} )
1351         setup_file(setup_path("cn=samba-admin.ldif"), 
1352                               os.path.join(paths.ldapdir, "db", "samba",  "cn=samba", "cn=samba-admin.ldif"),
1353                               {"LDAPADMINPASS_B64": b64encode(adminpass),
1354                                "UUID": str(uuid.uuid4()), 
1355                                "LDAPTIME": timestring(int(time.time()))} )
1356         
1357         if ol_mmr_urls is not None:
1358            setup_file(setup_path("cn=replicator.ldif"),
1359                               os.path.join(paths.ldapdir, "db", "samba",  "cn=samba", "cn=replicator.ldif"),
1360                               {"MMR_PASSWORD_B64": b64encode(mmr_pass),
1361                                "UUID": str(uuid.uuid4()),
1362                                "LDAPTIME": timestring(int(time.time()))} )
1363
1364
1365
1366         mapping = "schema-map-openldap-2.3"
1367         backend_schema = "backend-schema.schema"
1368
1369         ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
1370         if ldap_backend_port is not None:
1371             server_port_string = " -h ldap://0.0.0.0:%d" % ldap_backend_port
1372         else:
1373             server_port_string = ""
1374
1375         slapdcommand="Start slapd with:    slapd -f " + paths.ldapdir + "/slapd.conf -h " + ldapi_uri + server_port_string
1376
1377         ldapuser = "--username=samba-admin"
1378
1379             
1380     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)
1381             
1382     os.system(schema_command)
1383
1384     message("Your %s Backend for Samba4 is now configured, and is ready to be started" % ldap_backend_type)
1385     message("Server Role:         %s" % serverrole)
1386     message("Hostname:            %s" % names.hostname)
1387     message("DNS Domain:          %s" % names.dnsdomain)
1388     message("Base DN:             %s" % names.domaindn)
1389
1390     if ldap_backend_type == "openldap":
1391         message("LDAP admin user:     samba-admin")
1392     else:
1393         message("LDAP admin DN:       %s" % names.ldapmanagerdn)
1394
1395     message("LDAP admin password: %s" % adminpass)
1396     message(slapdcommand)
1397     message("Run provision with:  --ldap-backend=ldapi --ldap-backend-type=" + ldap_backend_type + " --password=" + adminpass + " " + ldapuser)
1398
1399 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1400     """Create a PHP LDAP admin configuration file.
1401
1402     :param path: Path to write the configuration to.
1403     :param setup_path: Function to generate setup paths.
1404     """
1405     setup_file(setup_path("phpldapadmin-config.php"), path, 
1406             {"S4_LDAPI_URI": ldapi_uri})
1407
1408
1409 def create_zone_file(path, setup_path, dnsdomain, domaindn, 
1410                      hostip, hostip6, hostname, dnspass, realm, domainguid, hostguid):
1411     """Write out a DNS zone file, from the info in the current database.
1412
1413     :param path: Path of the new zone file.
1414     :param setup_path: Setup path function.
1415     :param dnsdomain: DNS Domain name
1416     :param domaindn: DN of the Domain
1417     :param hostip: Local IPv4 IP
1418     :param hostip6: Local IPv6 IP
1419     :param hostname: Local hostname
1420     :param dnspass: Password for DNS
1421     :param realm: Realm name
1422     :param domainguid: GUID of the domain.
1423     :param hostguid: GUID of the host.
1424     """
1425     assert isinstance(domainguid, str)
1426
1427     if hostip6 is not None:
1428         hostip6_base_line = "            IN AAAA    " + hostip6
1429         hostip6_host_line = hostname + "        IN AAAA    " + hostip6
1430     else:
1431         hostip6_base_line = ""
1432         hostip6_host_line = ""
1433
1434     if hostip is not None:
1435         hostip_base_line = "            IN A    " + hostip
1436         hostip_host_line = hostname + "        IN A    " + hostip
1437     else:
1438         hostip_base_line = ""
1439         hostip_host_line = ""
1440
1441     setup_file(setup_path("provision.zone"), path, {
1442             "DNSPASS_B64": b64encode(dnspass),
1443             "HOSTNAME": hostname,
1444             "DNSDOMAIN": dnsdomain,
1445             "REALM": realm,
1446             "HOSTIP_BASE_LINE": hostip_base_line,
1447             "HOSTIP_HOST_LINE": hostip_host_line,
1448             "DOMAINGUID": domainguid,
1449             "DATESTRING": time.strftime("%Y%m%d%H"),
1450             "DEFAULTSITE": DEFAULTSITE,
1451             "HOSTGUID": hostguid,
1452             "HOSTIP6_BASE_LINE": hostip6_base_line,
1453             "HOSTIP6_HOST_LINE": hostip6_host_line,
1454         })
1455
1456
1457 def create_named_conf(path, setup_path, realm, dnsdomain,
1458                       private_dir):
1459     """Write out a file containing zone statements suitable for inclusion in a
1460     named.conf file (including GSS-TSIG configuration).
1461     
1462     :param path: Path of the new named.conf file.
1463     :param setup_path: Setup path function.
1464     :param realm: Realm name
1465     :param dnsdomain: DNS Domain name
1466     :param private_dir: Path to private directory
1467     :param keytab_name: File name of DNS keytab file
1468     """
1469
1470     setup_file(setup_path("named.conf"), path, {
1471             "DNSDOMAIN": dnsdomain,
1472             "REALM": realm,
1473             "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1474             "PRIVATE_DIR": private_dir
1475             })
1476
1477 def create_named_txt(path, setup_path, realm, dnsdomain,
1478                       private_dir, keytab_name):
1479     """Write out a file containing zone statements suitable for inclusion in a
1480     named.conf file (including GSS-TSIG configuration).
1481     
1482     :param path: Path of the new named.conf file.
1483     :param setup_path: Setup path function.
1484     :param realm: Realm name
1485     :param dnsdomain: DNS Domain name
1486     :param private_dir: Path to private directory
1487     :param keytab_name: File name of DNS keytab file
1488     """
1489
1490     setup_file(setup_path("named.txt"), path, {
1491             "DNSDOMAIN": dnsdomain,
1492             "REALM": realm,
1493             "DNS_KEYTAB": keytab_name,
1494             "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1495             "PRIVATE_DIR": private_dir
1496         })
1497
1498 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1499     """Write out a file containing zone statements suitable for inclusion in a
1500     named.conf file (including GSS-TSIG configuration).
1501     
1502     :param path: Path of the new named.conf file.
1503     :param setup_path: Setup path function.
1504     :param dnsdomain: DNS Domain name
1505     :param hostname: Local hostname
1506     :param realm: Realm name
1507     """
1508
1509     setup_file(setup_path("krb5.conf"), path, {
1510             "DNSDOMAIN": dnsdomain,
1511             "HOSTNAME": hostname,
1512             "REALM": realm,
1513         })
1514
1515
1516 def load_schema(setup_path, samdb, schemadn, netbiosname, configdn, sitename,
1517                 serverdn, servername):
1518     """Load schema for the SamDB.
1519     
1520     :param samdb: Load a schema into a SamDB.
1521     :param setup_path: Setup path function.
1522     :param schemadn: DN of the schema
1523     :param netbiosname: NetBIOS name of the host.
1524     :param configdn: DN of the configuration
1525     :param serverdn: DN of the server
1526     :param servername: Host name of the server
1527     """
1528     schema_data = open(setup_path("schema.ldif"), 'r').read()
1529     schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
1530     schema_data = substitute_var(schema_data, {"SCHEMADN": schemadn})
1531     check_all_substituted(schema_data)
1532     prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
1533     prefixmap = b64encode(prefixmap)
1534
1535     head_data = open(setup_path("provision_schema_basedn_modify.ldif"), 'r').read()
1536     head_data = substitute_var(head_data, {
1537                     "SCHEMADN": schemadn,
1538                     "NETBIOSNAME": netbiosname,
1539                     "CONFIGDN": configdn,
1540                     "DEFAULTSITE": sitename,
1541                     "PREFIXMAP_B64": prefixmap,
1542                     "SERVERDN": serverdn,
1543                     "SERVERNAME": servername,
1544     })
1545     check_all_substituted(head_data)
1546     samdb.attach_schema_from_ldif(head_data, schema_data)
1547