Merge branch 'master' of git://git.samba.org/samba into minschema
[kai/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     if samdb_fill == FILL_FULL:
1140         message("Admin password: %s" % adminpass)
1141
1142     result = ProvisionResult()
1143     result.domaindn = domaindn
1144     result.paths = paths
1145     result.lp = lp
1146     result.samdb = samdb
1147     return result
1148
1149
1150 def provision_become_dc(setup_dir=None,
1151                         smbconf=None, targetdir=None, realm=None, 
1152                         rootdn=None, domaindn=None, schemadn=None, configdn=None,
1153                         serverdn=None,
1154                         domain=None, hostname=None, domainsid=None, 
1155                         adminpass=None, krbtgtpass=None, domainguid=None, 
1156                         policyguid=None, invocationid=None, machinepass=None, 
1157                         dnspass=None, root=None, nobody=None, nogroup=None, users=None, 
1158                         wheel=None, backup=None, aci=None, serverrole=None, 
1159                         ldap_backend=None, ldap_backend_type=None, sitename=None):
1160
1161     def message(text):
1162         """print a message if quiet is not set."""
1163         print text
1164
1165     return provision(setup_dir, message, system_session(), None,
1166               smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS, realm=realm, 
1167               rootdn=rootdn, domaindn=domaindn, schemadn=schemadn, configdn=configdn, serverdn=serverdn,
1168               domain=domain, hostname=hostname, hostip="127.0.0.1", domainsid=domainsid, machinepass=machinepass, serverrole="domain controller", sitename=sitename)
1169     
1170
1171 def setup_db_config(setup_path, dbdir):
1172     """Setup a Berkeley database.
1173     
1174     :param setup_path: Setup path function.
1175     :param dbdir: Database directory."""
1176     if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
1177         os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700)
1178     if not os.path.isdir(os.path.join(dbdir, "tmp")):
1179         os.makedirs(os.path.join(dbdir, "tmp"), 0700)
1180     
1181     setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
1182                {"LDAPDBDIR": dbdir})
1183     
1184
1185
1186 def provision_backend(setup_dir=None, message=None,
1187                       smbconf=None, targetdir=None, realm=None, 
1188                       rootdn=None, domaindn=None, schemadn=None, configdn=None,
1189                       domain=None, hostname=None, adminpass=None, root=None, serverrole=None, 
1190                       ldap_backend_type=None, ldap_backend_port=None,
1191                       ol_mmr_urls=None,ol_olc=None,ol_slaptest=None):
1192
1193     def setup_path(file):
1194         return os.path.join(setup_dir, file)
1195
1196     if hostname is None:
1197         hostname = socket.gethostname().split(".")[0].lower()
1198
1199     if root is None:
1200         root = findnss(pwd.getpwnam, ["root"])[0]
1201
1202     if adminpass is None:
1203         adminpass = glue.generate_random_str(12)
1204
1205     if targetdir is not None:
1206         if (not os.path.exists(os.path.join(targetdir, "etc"))):
1207             os.makedirs(os.path.join(targetdir, "etc"))
1208         smbconf = os.path.join(targetdir, "etc", "smb.conf")
1209     elif smbconf is None:
1210         smbconf = param.default_path()
1211         assert smbconf is not None
1212
1213     # only install a new smb.conf if there isn't one there already
1214     if not os.path.exists(smbconf):
1215         make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, 
1216                      targetdir)
1217
1218     # openldap-online-configuration: validation of olc and slaptest
1219     if ol_olc == "yes" and ol_slaptest is None: 
1220         sys.exit("Warning: OpenLDAP-Online-Configuration cant be setup without path to slaptest-Binary!")
1221
1222     if ol_olc == "yes" and ol_slaptest is not None:
1223         ol_slaptest = ol_slaptest + "/slaptest"
1224         if not os.path.exists(ol_slaptest):
1225             message (ol_slaptest)
1226             sys.exit("Warning: Given Path to slaptest-Binary does not exist!")
1227     ###
1228
1229
1230
1231     lp = param.LoadParm()
1232     lp.load(smbconf)
1233
1234     if serverrole is None:
1235         serverrole = lp.get("server role")
1236
1237     names = guess_names(lp=lp, hostname=hostname, domain=domain, 
1238                         dnsdomain=realm, serverrole=serverrole, 
1239                         rootdn=rootdn, domaindn=domaindn, configdn=configdn, 
1240                         schemadn=schemadn)
1241
1242     paths = provision_paths_from_lp(lp, names.dnsdomain)
1243
1244     if not os.path.isdir(paths.ldapdir):
1245         os.makedirs(paths.ldapdir, 0700)
1246     schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
1247     try:
1248         os.unlink(schemadb_path)
1249     except OSError:
1250         pass
1251
1252     schemadb = Ldb(schemadb_path, lp=lp)
1253  
1254     prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
1255
1256     setup_add_ldif(schemadb, setup_path("provision_schema_basedn.ldif"), 
1257                    {"SCHEMADN": names.schemadn,
1258                     "ACI": "#",
1259                     })
1260     setup_modify_ldif(schemadb, 
1261                       setup_path("provision_schema_basedn_modify.ldif"), \
1262                           {"SCHEMADN": names.schemadn,
1263                            "NETBIOSNAME": names.netbiosname,
1264                            "DEFAULTSITE": DEFAULTSITE,
1265                            "CONFIGDN": names.configdn,
1266                            "SERVERDN": names.serverdn,
1267                            "PREFIXMAP_B64": b64encode(prefixmap)
1268                            })
1269     
1270     setup_add_ldif(schemadb, setup_path("schema_samba4.ldif"), 
1271                    {"SCHEMADN": names.schemadn })
1272     setup_add_ldif(schemadb, setup_path("schema.ldif"), 
1273                    {"SCHEMADN": names.schemadn})
1274
1275     if ldap_backend_type == "fedora-ds":
1276         if ldap_backend_port is not None:
1277             serverport = "ServerPort=%d" % ldap_backend_port
1278         else:
1279             serverport = ""
1280
1281         setup_file(setup_path("fedorads.inf"), paths.fedoradsinf, 
1282                    {"ROOT": root,
1283                     "HOSTNAME": hostname,
1284                     "DNSDOMAIN": names.dnsdomain,
1285                     "LDAPDIR": paths.ldapdir,
1286                     "DOMAINDN": names.domaindn,
1287                     "LDAPMANAGERDN": names.ldapmanagerdn,
1288                     "LDAPMANAGERPASS": adminpass, 
1289                     "SERVERPORT": serverport})
1290         
1291         setup_file(setup_path("fedorads-partitions.ldif"), paths.fedoradspartitions, 
1292                    {"CONFIGDN": names.configdn,
1293                     "SCHEMADN": names.schemadn,
1294                     })
1295         
1296         mapping = "schema-map-fedora-ds-1.0"
1297         backend_schema = "99_ad.ldif"
1298         
1299         slapdcommand="Initialise Fedora DS with: setup-ds.pl --file=%s" % paths.fedoradsinf
1300        
1301         ldapuser = "--simple-bind-dn=" + names.ldapmanagerdn
1302
1303     elif ldap_backend_type == "openldap":
1304         attrs = ["linkID", "lDAPDisplayName"]
1305         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)
1306
1307         memberof_config = "# Generated from schema in %s\n" % schemadb_path
1308         refint_attributes = ""
1309         for i in range (0, len(res)):
1310             expression = "(&(objectclass=attributeSchema)(linkID=%d)(attributeSyntax=2.5.5.1))" % (int(res[i]["linkID"][0])+1)
1311             target = schemadb.searchone(basedn=names.schemadn, 
1312                                         expression=expression, 
1313                                         attribute="lDAPDisplayName", 
1314                                         scope=SCOPE_SUBTREE)
1315             if target is not None:
1316                 refint_attributes = refint_attributes + " " + target + " " + res[i]["lDAPDisplayName"][0]
1317             
1318                 memberof_config += read_and_sub_file(setup_path("memberof.conf"),
1319                                                      { "MEMBER_ATTR" : str(res[i]["lDAPDisplayName"][0]),
1320                                                        "MEMBEROF_ATTR" : str(target) })
1321
1322         refint_config = read_and_sub_file(setup_path("refint.conf"),
1323                                             { "LINK_ATTRS" : refint_attributes})
1324
1325 # generate serverids, ldap-urls and syncrepl-blocks for mmr hosts
1326         mmr_on_config = ""
1327         mmr_replicator_acl = ""
1328         mmr_serverids_config = ""
1329         mmr_syncrepl_schema_config = "" 
1330         mmr_syncrepl_config_config = "" 
1331         mmr_syncrepl_user_config = "" 
1332        
1333  
1334         if ol_mmr_urls is not None:
1335                 # For now, make these equal
1336                 mmr_pass = adminpass
1337
1338                 url_list=filter(None,ol_mmr_urls.split(' ')) 
1339                 if (len(url_list) == 1):
1340                     url_list=filter(None,ol_mmr_urls.split(',')) 
1341                      
1342
1343                 mmr_on_config = "MirrorMode On"
1344                 mmr_replicator_acl = "  by dn=cn=replicator,cn=samba read"
1345                 serverid=0
1346                 for url in url_list:
1347                         serverid=serverid+1
1348                         mmr_serverids_config += read_and_sub_file(setup_path("mmr_serverids.conf"),
1349                                                                      { "SERVERID" : str(serverid),
1350                                                                        "LDAPSERVER" : url })
1351                         rid=serverid*10
1352                         rid=rid+1
1353                         mmr_syncrepl_schema_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1354                                                                      {  "RID" : str(rid),
1355                                                                         "MMRDN": names.schemadn,
1356                                                                         "LDAPSERVER" : url,
1357                                                                         "MMR_PASSWORD": mmr_pass})
1358
1359                         rid=rid+1
1360                         mmr_syncrepl_config_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1361                                                                      {  "RID" : str(rid),
1362                                                                         "MMRDN": names.configdn,
1363                                                                         "LDAPSERVER" : url,
1364                                                                         "MMR_PASSWORD": mmr_pass})
1365
1366                         rid=rid+1
1367                         mmr_syncrepl_user_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1368                                                                      {  "RID" : str(rid),
1369                                                                         "MMRDN": names.domaindn,
1370                                                                         "LDAPSERVER" : url,
1371                                                                         "MMR_PASSWORD": mmr_pass })
1372         # olc = yes?
1373         olc_config_pass = ""
1374         olc_config_acl = ""
1375         olc_syncrepl_config = ""
1376         olc_mmr_config = "" 
1377         if ol_olc == "yes":
1378                 olc_config_pass += read_and_sub_file(setup_path("olc_pass.conf"),
1379                                                                 { "OLC_PW": adminpass })
1380                 olc_config_acl += read_and_sub_file(setup_path("olc_acl.conf"),{})
1381                 
1382             # if olc = yes + mmr = yes, generate cn=config-replication directives
1383             # and  olc_seed.lif for the other mmr-servers
1384                 if ol_olc == "yes" and ol_mmr_urls is not None:
1385                         serverid=0
1386                         olc_serverids_config = ""
1387                         olc_syncrepl_config = ""
1388                         olc_syncrepl_seed_config = ""
1389                         olc_mmr_config = "" 
1390                         olc_mmr_config += read_and_sub_file(setup_path("olc_mmr.conf"),{})
1391                         rid=1000
1392                         for url in url_list:
1393                                 serverid=serverid+1
1394                                 olc_serverids_config += read_and_sub_file(setup_path("olc_serverid.conf"),
1395                                                                      { "SERVERID" : str(serverid),
1396                                                                        "LDAPSERVER" : url })
1397                         
1398                                 rid=rid+1
1399                                 olc_syncrepl_config += read_and_sub_file(setup_path("olc_syncrepl.conf"),
1400                                                                      {  "RID" : str(rid),
1401                                                                         "LDAPSERVER" : url,
1402                                                                         "MMR_PASSWORD": adminpass})
1403
1404                                 olc_syncrepl_seed_config += read_and_sub_file(setup_path("olc_syncrepl_seed.conf"),
1405                                                                      {  "RID" : str(rid),
1406                                                                         "LDAPSERVER" : url})
1407
1408                                 setup_file(setup_path("olc_seed.ldif"), paths.olcseedldif,
1409                                                                      {"OLC_SERVER_ID_CONF": olc_serverids_config,
1410                                                                       "OLC_PW": adminpass,
1411                                                                       "OLC_SYNCREPL_CONF": olc_syncrepl_seed_config})
1412         
1413
1414                 # end olc
1415
1416         setup_file(setup_path("slapd.conf"), paths.slapdconf,
1417                    {"DNSDOMAIN": names.dnsdomain,
1418                     "LDAPDIR": paths.ldapdir,
1419                     "DOMAINDN": names.domaindn,
1420                     "CONFIGDN": names.configdn,
1421                     "SCHEMADN": names.schemadn,
1422                     "MEMBEROF_CONFIG": memberof_config,
1423                     "MIRRORMODE": mmr_on_config,
1424                     "REPLICATOR_ACL": mmr_replicator_acl,
1425                     "MMR_SERVERIDS_CONFIG": mmr_serverids_config,
1426                     "MMR_SYNCREPL_SCHEMA_CONFIG": mmr_syncrepl_schema_config,
1427                     "MMR_SYNCREPL_CONFIG_CONFIG": mmr_syncrepl_config_config,
1428                     "MMR_SYNCREPL_USER_CONFIG": mmr_syncrepl_user_config,
1429                     "OLC_CONFIG_PASS": olc_config_pass,
1430                     "OLC_SYNCREPL_CONFIG": olc_syncrepl_config,
1431                     "OLC_CONFIG_ACL": olc_config_acl,
1432                     "OLC_MMR_CONFIG": olc_mmr_config,
1433                     "REFINT_CONFIG": refint_config})
1434         setup_file(setup_path("modules.conf"), paths.modulesconf,
1435                    {"REALM": names.realm})
1436         
1437         setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "user"))
1438         setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "config"))
1439         setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "schema"))
1440
1441         if not os.path.exists(os.path.join(paths.ldapdir, "db", "samba",  "cn=samba")):
1442             os.makedirs(os.path.join(paths.ldapdir, "db", "samba",  "cn=samba"), 0700)
1443
1444         setup_file(setup_path("cn=samba.ldif"), 
1445                    os.path.join(paths.ldapdir, "db", "samba",  "cn=samba.ldif"),
1446                    { "UUID": str(uuid.uuid4()), 
1447                      "LDAPTIME": timestring(int(time.time()))} )
1448         setup_file(setup_path("cn=samba-admin.ldif"), 
1449                               os.path.join(paths.ldapdir, "db", "samba",  "cn=samba", "cn=samba-admin.ldif"),
1450                               {"LDAPADMINPASS_B64": b64encode(adminpass),
1451                                "UUID": str(uuid.uuid4()), 
1452                                "LDAPTIME": timestring(int(time.time()))} )
1453         
1454         if ol_mmr_urls is not None:
1455            setup_file(setup_path("cn=replicator.ldif"),
1456                               os.path.join(paths.ldapdir, "db", "samba",  "cn=samba", "cn=replicator.ldif"),
1457                               {"MMR_PASSWORD_B64": b64encode(mmr_pass),
1458                                "UUID": str(uuid.uuid4()),
1459                                "LDAPTIME": timestring(int(time.time()))} )
1460
1461
1462         mapping = "schema-map-openldap-2.3"
1463         backend_schema = "backend-schema.schema"
1464
1465         ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
1466         if ldap_backend_port is not None:
1467             server_port_string = " -h ldap://0.0.0.0:%d" % ldap_backend_port
1468         else:
1469             server_port_string = ""
1470
1471         if ol_olc != "yes" and ol_mmr_urls is None:
1472           slapdcommand="Start slapd with:    slapd -f " + paths.ldapdir + "/slapd.conf -h " + ldapi_uri + server_port_string
1473
1474         if ol_olc == "yes" and ol_mmr_urls is None:
1475           slapdcommand="Start slapd with:    slapd -F " + paths.olcdir + " -h \"" + ldapi_uri + " ldap://<FQHN>:<PORT>\"" 
1476
1477         if ol_olc != "yes" and ol_mmr_urls is not None:
1478           slapdcommand="Start slapd with:    slapd -f " + paths.ldapdir + "/slapd.conf -h \"" + ldapi_uri + " ldap://<FQHN>:<PORT>\""
1479
1480         if ol_olc == "yes" and ol_mmr_urls is not None:
1481           slapdcommand="Start slapd with:    slapd -F " + paths.olcdir + " -h \"" + ldapi_uri + " ldap://<FQHN>:<PORT>\""
1482
1483
1484         ldapuser = "--username=samba-admin"
1485
1486             
1487     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)
1488             
1489     os.system(schema_command)
1490
1491     message("Your %s Backend for Samba4 is now configured, and is ready to be started" % ldap_backend_type)
1492     message("Server Role:         %s" % serverrole)
1493     message("Hostname:            %s" % names.hostname)
1494     message("DNS Domain:          %s" % names.dnsdomain)
1495     message("Base DN:             %s" % names.domaindn)
1496
1497     if ldap_backend_type == "openldap":
1498         message("LDAP admin user:     samba-admin")
1499     else:
1500         message("LDAP admin DN:       %s" % names.ldapmanagerdn)
1501
1502     message("LDAP admin password: %s" % adminpass)
1503     message(slapdcommand)
1504     if ol_olc == "yes" or ol_mmr_urls is not None:
1505         message("Attention to slapd-Port: <PORT> must be different than 389!")
1506     assert isinstance(ldap_backend_type, str)
1507     assert isinstance(ldapuser, str)
1508     assert isinstance(adminpass, str)
1509     assert isinstance(names.dnsdomain, str)
1510     assert isinstance(names.domain, str)
1511     assert isinstance(serverrole, str)
1512     args = ["--ldap-backend=ldapi",
1513             "--ldap-backend-type=" + ldap_backend_type,
1514             "--password=" + adminpass,
1515             ldapuser,
1516             "--realm=" + names.dnsdomain,
1517             "--domain=" + names.domain,
1518             "--server-role='" + serverrole + "'"]
1519     message("Run provision with: " + " ".join(args))
1520
1521
1522     # if --ol-olc=yes, generate online-configuration in ../private/ldap/slapd.d 
1523     if ol_olc == "yes":
1524           if not os.path.isdir(paths.olcdir):
1525              os.makedirs(paths.olcdir, 0770)
1526           paths.olslaptest = str(ol_slaptest)
1527           olc_command = paths.olslaptest + " -f" + paths.slapdconf + " -F" +  paths.olcdir + " >/dev/null 2>&1"
1528           os.system(olc_command)
1529           os.remove(paths.slapdconf)        
1530           # use line below for debugging during olc-conversion with slaptest, instead of olc_command above 
1531           #olc_command = paths.olslaptest + " -f" + paths.slapdconf + " -F" +  paths.olcdir"
1532
1533
1534 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1535     """Create a PHP LDAP admin configuration file.
1536
1537     :param path: Path to write the configuration to.
1538     :param setup_path: Function to generate setup paths.
1539     """
1540     setup_file(setup_path("phpldapadmin-config.php"), path, 
1541             {"S4_LDAPI_URI": ldapi_uri})
1542
1543
1544 def create_zone_file(path, setup_path, dnsdomain, domaindn, 
1545                      hostip, hostip6, hostname, dnspass, realm, domainguid, hostguid):
1546     """Write out a DNS zone file, from the info in the current database.
1547
1548     :param path: Path of the new zone file.
1549     :param setup_path: Setup path function.
1550     :param dnsdomain: DNS Domain name
1551     :param domaindn: DN of the Domain
1552     :param hostip: Local IPv4 IP
1553     :param hostip6: Local IPv6 IP
1554     :param hostname: Local hostname
1555     :param dnspass: Password for DNS
1556     :param realm: Realm name
1557     :param domainguid: GUID of the domain.
1558     :param hostguid: GUID of the host.
1559     """
1560     assert isinstance(domainguid, str)
1561
1562     if hostip6 is not None:
1563         hostip6_base_line = "            IN AAAA    " + hostip6
1564         hostip6_host_line = hostname + "        IN AAAA    " + hostip6
1565     else:
1566         hostip6_base_line = ""
1567         hostip6_host_line = ""
1568
1569     if hostip is not None:
1570         hostip_base_line = "            IN A    " + hostip
1571         hostip_host_line = hostname + "        IN A    " + hostip
1572     else:
1573         hostip_base_line = ""
1574         hostip_host_line = ""
1575
1576     setup_file(setup_path("provision.zone"), path, {
1577             "DNSPASS_B64": b64encode(dnspass),
1578             "HOSTNAME": hostname,
1579             "DNSDOMAIN": dnsdomain,
1580             "REALM": realm,
1581             "HOSTIP_BASE_LINE": hostip_base_line,
1582             "HOSTIP_HOST_LINE": hostip_host_line,
1583             "DOMAINGUID": domainguid,
1584             "DATESTRING": time.strftime("%Y%m%d%H"),
1585             "DEFAULTSITE": DEFAULTSITE,
1586             "HOSTGUID": hostguid,
1587             "HOSTIP6_BASE_LINE": hostip6_base_line,
1588             "HOSTIP6_HOST_LINE": hostip6_host_line,
1589         })
1590
1591
1592 def create_named_conf(path, setup_path, realm, dnsdomain,
1593                       private_dir):
1594     """Write out a file containing zone statements suitable for inclusion in a
1595     named.conf file (including GSS-TSIG configuration).
1596     
1597     :param path: Path of the new named.conf file.
1598     :param setup_path: Setup path function.
1599     :param realm: Realm name
1600     :param dnsdomain: DNS Domain name
1601     :param private_dir: Path to private directory
1602     :param keytab_name: File name of DNS keytab file
1603     """
1604
1605     setup_file(setup_path("named.conf"), path, {
1606             "DNSDOMAIN": dnsdomain,
1607             "REALM": realm,
1608             "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1609             "PRIVATE_DIR": private_dir
1610             })
1611
1612 def create_named_txt(path, setup_path, realm, dnsdomain,
1613                       private_dir, keytab_name):
1614     """Write out a file containing zone statements suitable for inclusion in a
1615     named.conf file (including GSS-TSIG configuration).
1616     
1617     :param path: Path of the new named.conf file.
1618     :param setup_path: Setup path function.
1619     :param realm: Realm name
1620     :param dnsdomain: DNS Domain name
1621     :param private_dir: Path to private directory
1622     :param keytab_name: File name of DNS keytab file
1623     """
1624
1625     setup_file(setup_path("named.txt"), path, {
1626             "DNSDOMAIN": dnsdomain,
1627             "REALM": realm,
1628             "DNS_KEYTAB": keytab_name,
1629             "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1630             "PRIVATE_DIR": private_dir
1631         })
1632
1633 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1634     """Write out a file containing zone statements suitable for inclusion in a
1635     named.conf file (including GSS-TSIG configuration).
1636     
1637     :param path: Path of the new named.conf file.
1638     :param setup_path: Setup path function.
1639     :param dnsdomain: DNS Domain name
1640     :param hostname: Local hostname
1641     :param realm: Realm name
1642     """
1643
1644     setup_file(setup_path("krb5.conf"), path, {
1645             "DNSDOMAIN": dnsdomain,
1646             "HOSTNAME": hostname,
1647             "REALM": realm,
1648         })
1649
1650
1651 def load_schema(setup_path, samdb, schemadn, netbiosname, configdn, sitename,
1652                 serverdn, servername):
1653     """Load schema for the SamDB.
1654     
1655     :param samdb: Load a schema into a SamDB.
1656     :param setup_path: Setup path function.
1657     :param schemadn: DN of the schema
1658     :param netbiosname: NetBIOS name of the host.
1659     :param configdn: DN of the configuration
1660     :param serverdn: DN of the server
1661     :param servername: Host name of the server
1662     """
1663     schema_data = open(setup_path("schema.ldif"), 'r').read()
1664     schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
1665     schema_data = substitute_var(schema_data, {"SCHEMADN": schemadn})
1666     check_all_substituted(schema_data)
1667     prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
1668     prefixmap = b64encode(prefixmap)
1669
1670     head_data = open(setup_path("provision_schema_basedn_modify.ldif"), 'r').read()
1671     head_data = substitute_var(head_data, {
1672                     "SCHEMADN": schemadn,
1673                     "NETBIOSNAME": netbiosname,
1674                     "CONFIGDN": configdn,
1675                     "DEFAULTSITE": sitename,
1676                     "PREFIXMAP_B64": prefixmap,
1677                     "SERVERDN": serverdn,
1678                     "SERVERNAME": servername,
1679     })
1680     check_all_substituted(head_data)
1681     samdb.attach_schema_from_ldif(head_data, schema_data)
1682