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