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