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