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