Initial implementation of security descriptor creation in DS
[ira/wip.git] / source4 / scripting / python / samba / provision.py
1 #
2 # Unix SMB/CIFS implementation.
3 # backend code for provisioning a Samba4 server
4
5 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
6 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
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 import subprocess
40 import ldb
41
42 import shutil
43 from credentials import Credentials, DONT_USE_KERBEROS
44 from auth import system_session, admin_session
45 from samba import version, Ldb, substitute_var, valid_netbios_name
46 from samba import check_all_substituted
47 from samba import DS_DOMAIN_FUNCTION_2000, DS_DC_FUNCTION_2008_R2
48 from samba.samdb import SamDB
49 from samba.idmap import IDmapDB
50 from samba.dcerpc import security
51 from samba.ndr import ndr_pack
52 import urllib
53 from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError, timestring
54 from ms_schema import read_ms_schema
55 from ms_display_specifiers import read_ms_ldif
56 from signal import SIGTERM
57
58 __docformat__ = "restructuredText"
59
60
61 class ProvisioningError(ValueError):
62   pass
63
64
65 def find_setup_dir():
66     """Find the setup directory used by provision."""
67     dirname = os.path.dirname(__file__)
68     if "/site-packages/" in dirname:
69         prefix = "/".join(dirname[:dirname.index("/site-packages/")].split("/")[:-2])
70         for suffix in ["share/setup", "share/samba/setup", "setup"]:
71             ret = os.path.join(prefix, suffix)
72             if os.path.isdir(ret):
73                 return ret
74     # In source tree
75     ret = os.path.join(dirname, "../../../setup")
76     if os.path.isdir(ret):
77         return ret
78     raise Exception("Unable to find setup directory.")
79
80 def get_schema_descriptor(domain_sid):
81     sddl = "O:SAG:SAD:(A;CI;RPLCLORC;;;AU)(A;CI;RPWPCRCCLCLORCWOWDSW;;;SA)" \
82            "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
83            "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
84            "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
85            "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
86            "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
87            "S:(AU;SA;WPCCDCWOWDSDDTSW;;;WD)" \
88            "(AU;CISA;WP;;;WD)(AU;SA;CR;;;BA)" \
89            "(AU;SA;CR;;;DU)(OU;SA;CR;e12b56b6-0a95-11d1-adbb-00c04fd8d5cd;;WD)" \
90            "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
91     sec = security.descriptor.from_sddl(sddl, domain_sid)
92     return b64encode(ndr_pack(sec))
93
94 def get_config_descriptor(domain_sid):
95     sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
96            "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
97            "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
98            "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
99            "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
100            "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
101            "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
102            "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
103            "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
104            "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
105            "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
106            "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
107            "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;S-1-5-21-3191434175-1265308384-3577286990-498)" \
108            "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
109            "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
110     sec = security.descriptor.from_sddl(sddl, domain_sid)
111     return b64encode(ndr_pack(sec))
112
113
114 DEFAULTSITE = "Default-First-Site-Name"
115
116 class InvalidNetbiosName(Exception):
117     """A specified name was not a valid NetBIOS name."""
118     def __init__(self, name):
119         super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)
120
121
122 class ProvisionPaths(object):
123     def __init__(self):
124         self.shareconf = None
125         self.hklm = None
126         self.hkcu = None
127         self.hkcr = None
128         self.hku = None
129         self.hkpd = None
130         self.hkpt = None
131         self.samdb = None
132         self.idmapdb = None
133         self.secrets = None
134         self.keytab = None
135         self.dns_keytab = None
136         self.dns = None
137         self.winsdb = None
138         self.private_dir = None
139         self.ldapdir = None
140         self.slapdconf = None
141         self.modulesconf = None
142         self.memberofconf = None
143         self.fedoradsinf = None
144         self.fedoradspartitions = None
145         self.fedoradssasl = None
146         self.olmmron = None
147         self.olmmrserveridsconf = None
148         self.olmmrsyncreplconf = None
149         self.olcdir = None
150         self.olslapd = None
151         self.olcseedldif = None
152
153
154 class ProvisionNames(object):
155     def __init__(self):
156         self.rootdn = None
157         self.domaindn = None
158         self.configdn = None
159         self.schemadn = None
160         self.sambadn = None
161         self.ldapmanagerdn = None
162         self.dnsdomain = None
163         self.realm = None
164         self.netbiosname = None
165         self.domain = None
166         self.hostname = None
167         self.sitename = None
168         self.smbconf = None
169     
170
171 class ProvisionResult(object):
172     def __init__(self):
173         self.paths = None
174         self.domaindn = None
175         self.lp = None
176         self.samdb = None
177         
178 class Schema(object):
179     def __init__(self, setup_path, domain_sid, schemadn=None,
180                  serverdn=None, sambadn=None, ldap_backend_type=None):
181         """Load schema for the SamDB from the AD schema files and samba4_schema.ldif
182         
183         :param samdb: Load a schema into a SamDB.
184         :param setup_path: Setup path function.
185         :param schemadn: DN of the schema
186         :param serverdn: DN of the server
187         
188         Returns the schema data loaded, to avoid double-parsing when then needing to add it to the db
189         """
190         
191         self.ldb = Ldb()
192         self.schema_data = read_ms_schema(setup_path('ad-schema/MS-AD_Schema_2K8_Attributes.txt'),
193                                           setup_path('ad-schema/MS-AD_Schema_2K8_Classes.txt'))
194         self.schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
195         self.schema_data = substitute_var(self.schema_data, {"SCHEMADN": schemadn})
196         check_all_substituted(self.schema_data)
197
198         self.schema_dn_modify = read_and_sub_file(setup_path("provision_schema_basedn_modify.ldif"),
199                                                   {"SCHEMADN": schemadn,
200                                                    "SERVERDN": serverdn,
201                                                    })
202
203         descr = get_schema_descriptor(domain_sid)
204         self.schema_dn_add = read_and_sub_file(setup_path("provision_schema_basedn.ldif"),
205                                                {"SCHEMADN": schemadn,
206                                                 "DESCRIPTOR": descr
207                                                 })
208
209         prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
210         prefixmap = b64encode(prefixmap)
211
212         # We don't actually add this ldif, just parse it
213         prefixmap_ldif = "dn: cn=schema\nprefixMap:: %s\n\n" % prefixmap
214         self.ldb.set_schema_from_ldif(prefixmap_ldif, self.schema_data)
215
216
217 # Return a hash with the forward attribute as a key and the back as the value 
218 def get_linked_attributes(schemadn,schemaldb):
219     attrs = ["linkID", "lDAPDisplayName"]
220     res = schemaldb.search(expression="(&(linkID=*)(!(linkID:1.2.840.113556.1.4.803:=1))(objectclass=attributeSchema)(attributeSyntax=2.5.5.1))", base=schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
221     attributes = {}
222     for i in range (0, len(res)):
223         expression = "(&(objectclass=attributeSchema)(linkID=%d)(attributeSyntax=2.5.5.1))" % (int(res[i]["linkID"][0])+1)
224         target = schemaldb.searchone(basedn=schemadn, 
225                                      expression=expression, 
226                                      attribute="lDAPDisplayName", 
227                                      scope=SCOPE_SUBTREE)
228         if target is not None:
229             attributes[str(res[i]["lDAPDisplayName"])]=str(target)
230             
231     return attributes
232
233 def get_dnsyntax_attributes(schemadn,schemaldb):
234     attrs = ["linkID", "lDAPDisplayName"]
235     res = schemaldb.search(expression="(&(!(linkID=*))(objectclass=attributeSchema)(attributeSyntax=2.5.5.1))", base=schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
236     attributes = []
237     for i in range (0, len(res)):
238         attributes.append(str(res[i]["lDAPDisplayName"]))
239         
240     return attributes
241     
242     
243 def check_install(lp, session_info, credentials):
244     """Check whether the current install seems ok.
245     
246     :param lp: Loadparm context
247     :param session_info: Session information
248     :param credentials: Credentials
249     """
250     if lp.get("realm") == "":
251         raise Exception("Realm empty")
252     ldb = Ldb(lp.get("sam database"), session_info=session_info, 
253             credentials=credentials, lp=lp)
254     if len(ldb.search("(cn=Administrator)")) != 1:
255         raise ProvisioningError("No administrator account found")
256
257
258 def findnss(nssfn, names):
259     """Find a user or group from a list of possibilities.
260     
261     :param nssfn: NSS Function to try (should raise KeyError if not found)
262     :param names: Names to check.
263     :return: Value return by first names list.
264     """
265     for name in names:
266         try:
267             return nssfn(name)
268         except KeyError:
269             pass
270     raise KeyError("Unable to find user/group %r" % names)
271
272
273 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
274 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
275
276
277 def read_and_sub_file(file, subst_vars):
278     """Read a file and sub in variables found in it
279     
280     :param file: File to be read (typically from setup directory)
281      param subst_vars: Optional variables to subsitute in the file.
282     """
283     data = open(file, 'r').read()
284     if subst_vars is not None:
285         data = substitute_var(data, subst_vars)
286     check_all_substituted(data)
287     return data
288
289
290 def setup_add_ldif(ldb, ldif_path, subst_vars=None):
291     """Setup a ldb in the private dir.
292     
293     :param ldb: LDB file to import data into
294     :param ldif_path: Path of the LDIF file to load
295     :param subst_vars: Optional variables to subsitute in LDIF.
296     """
297     assert isinstance(ldif_path, str)
298
299     data = read_and_sub_file(ldif_path, subst_vars)
300     ldb.add_ldif(data)
301
302
303 def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
304     """Modify a ldb in the private dir.
305     
306     :param ldb: LDB object.
307     :param ldif_path: LDIF file path.
308     :param subst_vars: Optional dictionary with substitution variables.
309     """
310     data = read_and_sub_file(ldif_path, subst_vars)
311
312     ldb.modify_ldif(data)
313
314
315 def setup_ldb(ldb, ldif_path, subst_vars):
316     """Import a LDIF a file into a LDB handle, optionally substituting variables.
317
318     :note: Either all LDIF data will be added or none (using transactions).
319
320     :param ldb: LDB file to import into.
321     :param ldif_path: Path to the LDIF file.
322     :param subst_vars: Dictionary with substitution variables.
323     """
324     assert ldb is not None
325     ldb.transaction_start()
326     try:
327         setup_add_ldif(ldb, ldif_path, subst_vars)
328     except:
329         ldb.transaction_cancel()
330         raise
331     ldb.transaction_commit()
332
333
334 def setup_file(template, fname, subst_vars):
335     """Setup a file in the private dir.
336
337     :param template: Path of the template file.
338     :param fname: Path of the file to create.
339     :param subst_vars: Substitution variables.
340     """
341     f = fname
342
343     if os.path.exists(f):
344         os.unlink(f)
345
346     data = read_and_sub_file(template, subst_vars)
347     open(f, 'w').write(data)
348
349
350 def provision_paths_from_lp(lp, dnsdomain):
351     """Set the default paths for provisioning.
352
353     :param lp: Loadparm context.
354     :param dnsdomain: DNS Domain name
355     """
356     paths = ProvisionPaths()
357     paths.private_dir = lp.get("private dir")
358     paths.keytab = "secrets.keytab"
359     paths.dns_keytab = "dns.keytab"
360
361     paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
362     paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
363     paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
364     paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
365     paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
366     paths.namedconf = os.path.join(paths.private_dir, "named.conf")
367     paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
368     paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
369     paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
370     paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
371     paths.phpldapadminconfig = os.path.join(paths.private_dir, 
372                                             "phpldapadmin-config.php")
373     paths.ldapdir = os.path.join(paths.private_dir, 
374                                  "ldap")
375     paths.slapdconf = os.path.join(paths.ldapdir, 
376                                    "slapd.conf")
377     paths.slapdpid = os.path.join(paths.ldapdir, 
378                                    "slapd.pid")
379     paths.modulesconf = os.path.join(paths.ldapdir, 
380                                      "modules.conf")
381     paths.memberofconf = os.path.join(paths.ldapdir, 
382                                       "memberof.conf")
383     paths.fedoradsinf = os.path.join(paths.ldapdir, 
384                                      "fedorads.inf")
385     paths.fedoradspartitions = os.path.join(paths.ldapdir, 
386                                             "fedorads-partitions.ldif")
387     paths.fedoradssasl = os.path.join(paths.ldapdir, 
388                                       "fedorads-sasl.ldif")
389     paths.fedoradssamba = os.path.join(paths.ldapdir, 
390                                         "fedorads-samba.ldif")
391     paths.olmmrserveridsconf = os.path.join(paths.ldapdir, 
392                                             "mmr_serverids.conf")
393     paths.olmmrsyncreplconf = os.path.join(paths.ldapdir, 
394                                            "mmr_syncrepl.conf")
395     paths.olcdir = os.path.join(paths.ldapdir, 
396                                  "slapd.d")
397     paths.olcseedldif = os.path.join(paths.ldapdir, 
398                                  "olc_seed.ldif")
399     paths.hklm = "hklm.ldb"
400     paths.hkcr = "hkcr.ldb"
401     paths.hkcu = "hkcu.ldb"
402     paths.hku = "hku.ldb"
403     paths.hkpd = "hkpd.ldb"
404     paths.hkpt = "hkpt.ldb"
405
406     paths.sysvol = lp.get("path", "sysvol")
407
408     paths.netlogon = lp.get("path", "netlogon")
409
410     paths.smbconf = lp.configfile
411
412     return paths
413
414
415 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
416                 serverrole=None, rootdn=None, domaindn=None, configdn=None,
417                 schemadn=None, serverdn=None, sitename=None, sambadn=None):
418     """Guess configuration settings to use."""
419
420     if hostname is None:
421         hostname = socket.gethostname().split(".")[0].lower()
422
423     netbiosname = hostname.upper()
424     if not valid_netbios_name(netbiosname):
425         raise InvalidNetbiosName(netbiosname)
426
427     hostname = hostname.lower()
428
429     if dnsdomain is None:
430         dnsdomain = lp.get("realm")
431
432     if serverrole is None:
433         serverrole = lp.get("server role")
434
435     assert dnsdomain is not None
436     realm = dnsdomain.upper()
437
438     if lp.get("realm").upper() != realm:
439         raise Exception("realm '%s' in %s must match chosen realm '%s'" %
440                         (lp.get("realm"), lp.configfile, realm))
441     
442     dnsdomain = dnsdomain.lower()
443
444     if serverrole == "domain controller":
445         if domain is None:
446             domain = lp.get("workgroup")
447         if domaindn is None:
448             domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
449         if lp.get("workgroup").upper() != domain.upper():
450             raise Exception("workgroup '%s' in smb.conf must match chosen domain '%s'",
451                         lp.get("workgroup"), domain)
452     else:
453         domain = netbiosname
454         if domaindn is None:
455             domaindn = "CN=" + netbiosname
456         
457     assert domain is not None
458     domain = domain.upper()
459     if not valid_netbios_name(domain):
460         raise InvalidNetbiosName(domain)
461         
462     if netbiosname.upper() == realm.upper():
463         raise Exception("realm %s must not be equal to netbios domain name %s", realm, netbiosname)
464         
465     if hostname.upper() == realm.upper():
466         raise Exception("realm %s must not be equal to hostname %s", realm, hostname)
467         
468     if domain.upper() == realm.upper():
469         raise Exception("realm %s must not be equal to domain name %s", realm, domain)
470
471     if rootdn is None:
472        rootdn = domaindn
473        
474     if configdn is None:
475         configdn = "CN=Configuration," + rootdn
476     if schemadn is None:
477         schemadn = "CN=Schema," + configdn
478     if sambadn is None:
479         sambadn = "CN=Samba"
480
481     if sitename is None:
482         sitename=DEFAULTSITE
483
484     names = ProvisionNames()
485     names.rootdn = rootdn
486     names.domaindn = domaindn
487     names.configdn = configdn
488     names.schemadn = schemadn
489     names.sambadn = sambadn
490     names.ldapmanagerdn = "CN=Manager," + rootdn
491     names.dnsdomain = dnsdomain
492     names.domain = domain
493     names.realm = realm
494     names.netbiosname = netbiosname
495     names.hostname = hostname
496     names.sitename = sitename
497     names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
498  
499     return names
500     
501
502 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, 
503                  targetdir):
504     """Create a new smb.conf file based on a couple of basic settings.
505     """
506     assert smbconf is not None
507     if hostname is None:
508         hostname = socket.gethostname().split(".")[0].lower()
509
510     if serverrole is None:
511         serverrole = "standalone"
512
513     assert serverrole in ("domain controller", "member server", "standalone")
514     if serverrole == "domain controller":
515         smbconfsuffix = "dc"
516     elif serverrole == "member server":
517         smbconfsuffix = "member"
518     elif serverrole == "standalone":
519         smbconfsuffix = "standalone"
520
521     assert domain is not None
522     assert realm is not None
523
524     default_lp = param.LoadParm()
525     #Load non-existant file
526     if os.path.exists(smbconf):
527         default_lp.load(smbconf)
528     
529     if targetdir is not None:
530         privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
531         lockdir_line = "lock dir = " + os.path.abspath(targetdir)
532
533         default_lp.set("lock dir", os.path.abspath(targetdir))
534     else:
535         privatedir_line = ""
536         lockdir_line = ""
537
538     sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
539     netlogon = os.path.join(sysvol, realm.lower(), "scripts")
540
541     setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix), 
542                smbconf, {
543             "HOSTNAME": hostname,
544             "DOMAIN": domain,
545             "REALM": realm,
546             "SERVERROLE": serverrole,
547             "NETLOGONPATH": netlogon,
548             "SYSVOLPATH": sysvol,
549             "PRIVATEDIR_LINE": privatedir_line,
550             "LOCKDIR_LINE": lockdir_line
551             })
552
553
554 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
555                         users_gid, wheel_gid):
556     """setup reasonable name mappings for sam names to unix names.
557
558     :param samdb: SamDB object.
559     :param idmap: IDmap db object.
560     :param sid: The domain sid.
561     :param domaindn: The domain DN.
562     :param root_uid: uid of the UNIX root user.
563     :param nobody_uid: uid of the UNIX nobody user.
564     :param users_gid: gid of the UNIX users group.
565     :param wheel_gid: gid of the UNIX wheel group."""
566
567     idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
568     idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
569     
570     idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
571     idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
572
573 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info, 
574                            credentials, names,
575                            serverrole, ldap_backend=None, 
576                            erase=False):
577     """Setup the partitions for the SAM database. 
578     
579     Alternatively, provision() may call this, and then populate the database.
580     
581     :note: This will wipe the Sam Database!
582     
583     :note: This function always removes the local SAM LDB file. The erase 
584         parameter controls whether to erase the existing data, which 
585         may not be stored locally but in LDAP.
586     """
587     assert session_info is not None
588
589     # We use options=["modules:"] to stop the modules loading - we
590     # just want to wipe and re-initialise the database, not start it up
591
592     try:
593         samdb = Ldb(url=samdb_path, session_info=session_info, 
594                       credentials=credentials, lp=lp, options=["modules:"])
595         # Wipes the database
596         samdb.erase_except_schema_controlled()
597     except LdbError:
598         os.unlink(samdb_path)
599         samdb = Ldb(url=samdb_path, session_info=session_info, 
600                       credentials=credentials, lp=lp, options=["modules:"])
601          # Wipes the database
602         samdb.erase_except_schema_controlled()
603         
604
605     #Add modules to the list to activate them by default
606     #beware often order is important
607     #
608     # Some Known ordering constraints:
609     # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
610     # - objectclass must be before password_hash, because password_hash checks
611     #   that the objectclass is of type person (filled in by objectclass
612     #   module when expanding the objectclass list)
613     # - partition must be last
614     # - each partition has its own module list then
615     modules_list = ["resolve_oids",
616                     "rootdse",
617                     "paged_results",
618                     "ranged_results",
619                     "anr",
620                     "server_sort",
621                     "asq",
622                     "extended_dn_store",
623                     "extended_dn_in",
624                     "rdn_name",
625                     "objectclass",
626                     "descriptor",
627                     "samldb",
628                     "password_hash",
629                     "operational",
630                     "kludge_acl"]
631     tdb_modules_list = [
632                     "subtree_rename",
633                     "subtree_delete",
634                     "linked_attributes",
635                     "extended_dn_out_ldb"]
636     modules_list2 = ["show_deleted",
637                     "partition"]
638  
639     domaindn_ldb = "users.ldb"
640     configdn_ldb = "configuration.ldb"
641     schemadn_ldb = "schema.ldb"
642     if ldap_backend is not None:
643         domaindn_ldb = ldap_backend.ldapi_uri
644         configdn_ldb = ldap_backend.ldapi_uri
645         schemadn_ldb = ldap_backend.ldapi_uri
646         
647         if ldap_backend.ldap_backend_type == "fedora-ds":
648             backend_modules = ["nsuniqueid", "paged_searches"]
649             # We can handle linked attributes here, as we don't have directory-side subtree operations
650             tdb_modules_list = ["linked_attributes", "extended_dn_out_dereference"]
651         elif ldap_backend.ldap_backend_type == "openldap":
652             backend_modules = ["entryuuid", "paged_searches"]
653             # OpenLDAP handles subtree renames, so we don't want to do any of these things
654             tdb_modules_list = ["extended_dn_out_dereference"]
655
656     elif serverrole == "domain controller":
657         tdb_modules_list.insert(0, "repl_meta_data")
658         backend_modules = []
659     else:
660         backend_modules = ["objectguid"]
661
662     if tdb_modules_list is None:
663         tdb_modules_list_as_string = ""
664     else:
665         tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
666         
667     samdb.transaction_start()
668     try:
669         message("Setting up sam.ldb partitions and settings")
670         setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
671                 "SCHEMADN": names.schemadn, 
672                 "SCHEMADN_LDB": schemadn_ldb,
673                 "SCHEMADN_MOD2": ",objectguid",
674                 "CONFIGDN": names.configdn,
675                 "CONFIGDN_LDB": configdn_ldb,
676                 "DOMAINDN": names.domaindn,
677                 "DOMAINDN_LDB": domaindn_ldb,
678                 "SCHEMADN_MOD": "schema_fsmo,instancetype",
679                 "CONFIGDN_MOD": "naming_fsmo,instancetype",
680                 "DOMAINDN_MOD": "pdc_fsmo,instancetype",
681                 "MODULES_LIST": ",".join(modules_list),
682                 "TDB_MODULES_LIST": tdb_modules_list_as_string,
683                 "MODULES_LIST2": ",".join(modules_list2),
684                 "BACKEND_MOD": ",".join(backend_modules),
685         })
686
687         samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
688
689         message("Setting up sam.ldb rootDSE")
690         setup_samdb_rootdse(samdb, setup_path, names)
691
692     except:
693         samdb.transaction_cancel()
694         raise
695
696     samdb.transaction_commit()
697     
698
699
700 def secretsdb_become_dc(secretsdb, setup_path, domain, realm, dnsdomain, 
701                         netbiosname, domainsid, keytab_path, samdb_url, 
702                         dns_keytab_path, dnspass, machinepass):
703     """Add DC-specific bits to a secrets database.
704     
705     :param secretsdb: Ldb Handle to the secrets database
706     :param setup_path: Setup path function
707     :param machinepass: Machine password
708     """
709     setup_ldb(secretsdb, setup_path("secrets_dc.ldif"), { 
710             "MACHINEPASS_B64": b64encode(machinepass),
711             "DOMAIN": domain,
712             "REALM": realm,
713             "DNSDOMAIN": dnsdomain,
714             "DOMAINSID": str(domainsid),
715             "SECRETS_KEYTAB": keytab_path,
716             "NETBIOSNAME": netbiosname,
717             "SAM_LDB": samdb_url,
718             "DNS_KEYTAB": dns_keytab_path,
719             "DNSPASS_B64": b64encode(dnspass),
720             })
721
722
723 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
724     """Setup the secrets database.
725
726     :param path: Path to the secrets database.
727     :param setup_path: Get the path to a setup file.
728     :param session_info: Session info.
729     :param credentials: Credentials
730     :param lp: Loadparm context
731     :return: LDB handle for the created secrets database
732     """
733     if os.path.exists(path):
734         os.unlink(path)
735     secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
736                       lp=lp)
737     secrets_ldb.erase()
738     secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
739     secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
740                       lp=lp)
741     secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
742
743     if credentials is not None and credentials.authentication_requested():
744         if credentials.get_bind_dn() is not None:
745             setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
746                     "LDAPMANAGERDN": credentials.get_bind_dn(),
747                     "LDAPMANAGERPASS_B64": b64encode(credentials.get_password())
748                     })
749         else:
750             setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
751                     "LDAPADMINUSER": credentials.get_username(),
752                     "LDAPADMINREALM": credentials.get_realm(),
753                     "LDAPADMINPASS_B64": b64encode(credentials.get_password())
754                     })
755
756     return secrets_ldb
757
758 def setup_registry(path, setup_path, session_info, lp):
759     """Setup the registry.
760     
761     :param path: Path to the registry database
762     :param setup_path: Function that returns the path to a setup.
763     :param session_info: Session information
764     :param credentials: Credentials
765     :param lp: Loadparm context
766     """
767     reg = registry.Registry()
768     hive = registry.open_ldb(path, session_info=session_info, 
769                          lp_ctx=lp)
770     reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
771     provision_reg = setup_path("provision.reg")
772     assert os.path.exists(provision_reg)
773     reg.diff_apply(provision_reg)
774
775
776 def setup_idmapdb(path, setup_path, session_info, lp):
777     """Setup the idmap database.
778
779     :param path: path to the idmap database
780     :param setup_path: Function that returns a path to a setup file
781     :param session_info: Session information
782     :param credentials: Credentials
783     :param lp: Loadparm context
784     """
785     if os.path.exists(path):
786         os.unlink(path)
787
788     idmap_ldb = IDmapDB(path, session_info=session_info,
789                         lp=lp)
790
791     idmap_ldb.erase()
792     idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
793     return idmap_ldb
794
795
796 def setup_samdb_rootdse(samdb, setup_path, names):
797     """Setup the SamDB rootdse.
798
799     :param samdb: Sam Database handle
800     :param setup_path: Obtain setup path
801     """
802     setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
803         "SCHEMADN": names.schemadn, 
804         "NETBIOSNAME": names.netbiosname,
805         "DNSDOMAIN": names.dnsdomain,
806         "REALM": names.realm,
807         "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
808         "DOMAINDN": names.domaindn,
809         "ROOTDN": names.rootdn,
810         "CONFIGDN": names.configdn,
811         "SERVERDN": names.serverdn,
812         })
813         
814
815 def setup_self_join(samdb, names,
816                     machinepass, dnspass, 
817                     domainsid, invocationid, setup_path,
818                     policyguid, policyguid_dc, domainControllerFunctionality):
819     """Join a host to its own domain."""
820     assert isinstance(invocationid, str)
821     setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), { 
822               "CONFIGDN": names.configdn, 
823               "SCHEMADN": names.schemadn,
824               "DOMAINDN": names.domaindn,
825               "SERVERDN": names.serverdn,
826               "INVOCATIONID": invocationid,
827               "NETBIOSNAME": names.netbiosname,
828               "DEFAULTSITE": names.sitename,
829               "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
830               "MACHINEPASS_B64": b64encode(machinepass),
831               "DNSPASS_B64": b64encode(dnspass),
832               "REALM": names.realm,
833               "DOMAIN": names.domain,
834               "DNSDOMAIN": names.dnsdomain,
835               "SAMBA_VERSION_STRING": version,
836               "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
837
838     setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), { 
839               "POLICYGUID": policyguid,
840               "POLICYGUID_DC": policyguid_dc,
841               "DNSDOMAIN": names.dnsdomain,
842               "DOMAINSID": str(domainsid),
843               "DOMAINDN": names.domaindn})
844     
845     # add the NTDSGUID based SPNs
846     ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
847     names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
848                                      expression="", scope=SCOPE_BASE)
849     assert isinstance(names.ntdsguid, str)
850
851     # Setup fSMORoleOwner entries to point at the newly created DC entry
852     setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
853               "DOMAIN": names.domain,
854               "DNSDOMAIN": names.dnsdomain,
855               "DOMAINDN": names.domaindn,
856               "CONFIGDN": names.configdn,
857               "SCHEMADN": names.schemadn, 
858               "DEFAULTSITE": names.sitename,
859               "SERVERDN": names.serverdn,
860               "NETBIOSNAME": names.netbiosname,
861               "NTDSGUID": names.ntdsguid
862               })
863
864
865 def setup_samdb(path, setup_path, session_info, credentials, lp, 
866                 names, message, 
867                 domainsid, domainguid, policyguid, policyguid_dc,
868                 fill, adminpass, krbtgtpass, 
869                 machinepass, invocationid, dnspass,
870                 serverrole, schema=None, ldap_backend=None):
871     """Setup a complete SAM Database.
872     
873     :note: This will wipe the main SAM database file!
874     """
875
876     domainFunctionality = DS_DOMAIN_FUNCTION_2000
877     forestFunctionality = DS_DOMAIN_FUNCTION_2000
878     domainControllerFunctionality = DS_DC_FUNCTION_2008_R2
879
880     # Also wipes the database
881     setup_samdb_partitions(path, setup_path, message=message, lp=lp,
882                            credentials=credentials, session_info=session_info,
883                            names=names, 
884                            ldap_backend=ldap_backend, serverrole=serverrole)
885
886     if (schema == None):
887         schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn,
888             sambadn=names.sambadn, ldap_backend_type=ldap_backend.ldap_backend_type)
889
890     # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
891     samdb = Ldb(session_info=session_info, 
892                 credentials=credentials, lp=lp)
893
894     message("Pre-loading the Samba 4 and AD schema")
895
896     # Load the schema from the one we computed earlier
897     samdb.set_schema_from_ldb(schema.ldb)
898
899     # And now we can connect to the DB - the schema won't be loaded from the DB
900     samdb.connect(path)
901
902     # Load @OPTIONS
903     samdb.load_ldif_file_add(setup_path("provision_options.ldif"))
904
905     if fill == FILL_DRS:
906         return samdb
907
908     samdb.transaction_start()
909     try:
910         message("Erasing data from partitions")
911         # Load the schema (again).  This time it will force a reindex,
912         # and will therefore make the erase_partitions() below
913         # computationally sane
914         samdb.set_schema_from_ldb(schema.ldb)
915         samdb.erase_partitions()
916     
917         # Set the domain functionality levels onto the database.
918         # Various module (the password_hash module in particular) need
919         # to know what level of AD we are emulating.
920
921         # These will be fixed into the database via the database
922         # modifictions below, but we need them set from the start.
923         samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
924         samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
925         samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
926
927         samdb.set_domain_sid(str(domainsid))
928         if serverrole == "domain controller":
929             samdb.set_invocation_id(invocationid)
930
931         message("Adding DomainDN: %s" % names.domaindn)
932         if serverrole == "domain controller":
933             domain_oc = "domainDNS"
934         else:
935             domain_oc = "samba4LocalDomain"
936
937 #impersonate domain admin
938         admin_session_info = admin_session(lp, str(domainsid))
939         samdb.set_session_info(admin_session_info)
940
941         setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
942                 "DOMAINDN": names.domaindn,
943                 "DOMAIN_OC": domain_oc
944                 })
945
946         message("Modifying DomainDN: " + names.domaindn + "")
947         if domainguid is not None:
948             domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
949         else:
950             domainguid_mod = ""
951
952         setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
953             "CREATTIME": str(int(time.time()) * 1e7), # seconds -> ticks
954             "DOMAINSID": str(domainsid),
955             "SCHEMADN": names.schemadn, 
956             "NETBIOSNAME": names.netbiosname,
957             "DEFAULTSITE": names.sitename,
958             "CONFIGDN": names.configdn,
959             "SERVERDN": names.serverdn,
960             "POLICYGUID": policyguid,
961             "DOMAINDN": names.domaindn,
962             "DOMAINGUID_MOD": domainguid_mod,
963             "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
964             "SAMBA_VERSION_STRING": version
965             })
966
967         message("Adding configuration container")
968         descr = get_config_descriptor(domainsid);
969         setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
970             "CONFIGDN": names.configdn, 
971             "DESCRIPTOR": descr,
972             })
973         message("Modifying configuration container")
974         setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
975             "CONFIGDN": names.configdn, 
976             "SCHEMADN": names.schemadn,
977             })
978
979         # The LDIF here was created when the Schema object was constructed
980         message("Setting up sam.ldb schema")
981         samdb.add_ldif(schema.schema_dn_add)
982         samdb.modify_ldif(schema.schema_dn_modify)
983         samdb.write_prefixes_from_schema()
984         samdb.add_ldif(schema.schema_data)
985         setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"), 
986                        {"SCHEMADN": names.schemadn})
987
988         message("Setting up sam.ldb configuration data")
989         setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
990             "CONFIGDN": names.configdn,
991             "NETBIOSNAME": names.netbiosname,
992             "DEFAULTSITE": names.sitename,
993             "DNSDOMAIN": names.dnsdomain,
994             "DOMAIN": names.domain,
995             "SCHEMADN": names.schemadn,
996             "DOMAINDN": names.domaindn,
997             "SERVERDN": names.serverdn,
998             "FOREST_FUNCTIONALALITY": str(forestFunctionality)
999             })
1000
1001         message("Setting up display specifiers")
1002         display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1003         display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
1004         check_all_substituted(display_specifiers_ldif)
1005         samdb.add_ldif(display_specifiers_ldif)
1006
1007         message("Adding users container")
1008         setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1009                 "DOMAINDN": names.domaindn})
1010         message("Modifying users container")
1011         setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1012                 "DOMAINDN": names.domaindn})
1013         message("Adding computers container")
1014         setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1015                 "DOMAINDN": names.domaindn})
1016         message("Modifying computers container")
1017         setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1018                 "DOMAINDN": names.domaindn})
1019         message("Setting up sam.ldb data")
1020         setup_add_ldif(samdb, setup_path("provision.ldif"), {
1021             "CREATTIME": str(int(time.time()) * 1e7), # seconds -> ticks
1022             "DOMAINDN": names.domaindn,
1023             "NETBIOSNAME": names.netbiosname,
1024             "DEFAULTSITE": names.sitename,
1025             "CONFIGDN": names.configdn,
1026             "SERVERDN": names.serverdn,
1027             "POLICYGUID_DC": policyguid_dc
1028             })
1029
1030         if fill == FILL_FULL:
1031             message("Setting up sam.ldb users and groups")
1032             setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1033                 "DOMAINDN": names.domaindn,
1034                 "DOMAINSID": str(domainsid),
1035                 "CONFIGDN": names.configdn,
1036                 "ADMINPASS_B64": b64encode(adminpass),
1037                 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1038                 })
1039
1040             if serverrole == "domain controller":
1041                 message("Setting up self join")
1042                 setup_self_join(samdb, names=names, invocationid=invocationid, 
1043                                 dnspass=dnspass,  
1044                                 machinepass=machinepass, 
1045                                 domainsid=domainsid, policyguid=policyguid,
1046                                 policyguid_dc=policyguid_dc,
1047                                 setup_path=setup_path,
1048                                 domainControllerFunctionality=domainControllerFunctionality)
1049
1050                 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
1051                 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1052                   attribute="objectGUID", expression="", scope=SCOPE_BASE)
1053                 assert isinstance(names.ntdsguid, str)
1054
1055     except:
1056         samdb.transaction_cancel()
1057         raise
1058
1059     samdb.transaction_commit()
1060     return samdb
1061
1062
1063 FILL_FULL = "FULL"
1064 FILL_NT4SYNC = "NT4SYNC"
1065 FILL_DRS = "DRS"
1066
1067
1068 def provision(setup_dir, message, session_info, 
1069               credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1070               realm=None, 
1071               rootdn=None, domaindn=None, schemadn=None, configdn=None, 
1072               serverdn=None,
1073               domain=None, hostname=None, hostip=None, hostip6=None, 
1074               domainsid=None, adminpass=None, ldapadminpass=None, 
1075               krbtgtpass=None, domainguid=None, 
1076               policyguid=None, policyguid_dc=None, invocationid=None,
1077               machinepass=None, 
1078               dnspass=None, root=None, nobody=None, users=None, 
1079               wheel=None, backup=None, aci=None, serverrole=None, 
1080               ldap_backend_extra_port=None, ldap_backend_type=None,
1081               sitename=None,
1082               ol_mmr_urls=None, ol_olc=None, 
1083               setup_ds_path=None, slapd_path=None, nosync=False,
1084               ldap_dryrun_mode=False):
1085     """Provision samba4
1086     
1087     :note: caution, this wipes all existing data!
1088     """
1089
1090     def setup_path(file):
1091       return os.path.join(setup_dir, file)
1092
1093     if domainsid is None:
1094       domainsid = security.random_sid()
1095     else:
1096       domainsid = security.dom_sid(domainsid)
1097
1098
1099     # create/adapt the group policy GUIDs
1100     if policyguid is None:
1101         policyguid = str(uuid.uuid4())
1102     policyguid = policyguid.upper()
1103     if policyguid_dc is None:
1104         policyguid_dc = str(uuid.uuid4())
1105     policyguid_dc = policyguid_dc.upper()
1106
1107     if adminpass is None:
1108         adminpass = glue.generate_random_str(12)
1109     if krbtgtpass is None:
1110         krbtgtpass = glue.generate_random_str(12)
1111     if machinepass is None:
1112         machinepass  = glue.generate_random_str(12)
1113     if dnspass is None:
1114         dnspass = glue.generate_random_str(12)
1115     if ldapadminpass is None:
1116         #Make a new, random password between Samba and it's LDAP server
1117         ldapadminpass=glue.generate_random_str(12)        
1118
1119
1120     root_uid = findnss_uid([root or "root"])
1121     nobody_uid = findnss_uid([nobody or "nobody"])
1122     users_gid = findnss_gid([users or "users"])
1123     if wheel is None:
1124         wheel_gid = findnss_gid(["wheel", "adm"])
1125     else:
1126         wheel_gid = findnss_gid([wheel])
1127
1128     if targetdir is not None:
1129         if (not os.path.exists(os.path.join(targetdir, "etc"))):
1130             os.makedirs(os.path.join(targetdir, "etc"))
1131         smbconf = os.path.join(targetdir, "etc", "smb.conf")
1132     elif smbconf is None:
1133         smbconf = param.default_path()
1134
1135     # only install a new smb.conf if there isn't one there already
1136     if not os.path.exists(smbconf):
1137         make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, 
1138                      targetdir)
1139
1140     lp = param.LoadParm()
1141     lp.load(smbconf)
1142
1143     names = guess_names(lp=lp, hostname=hostname, domain=domain, 
1144                         dnsdomain=realm, serverrole=serverrole, sitename=sitename,
1145                         rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1146                         serverdn=serverdn)
1147
1148     paths = provision_paths_from_lp(lp, names.dnsdomain)
1149
1150     if hostip is None:
1151         try:
1152             hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1153         except socket.gaierror, (socket.EAI_NODATA, msg):
1154             hostip = None
1155
1156     if hostip6 is None:
1157         try:
1158             hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1159         except socket.gaierror, (socket.EAI_NODATA, msg): 
1160             hostip6 = None
1161
1162     if serverrole is None:
1163         serverrole = lp.get("server role")
1164
1165     assert serverrole in ("domain controller", "member server", "standalone")
1166     if invocationid is None and serverrole == "domain controller":
1167         invocationid = str(uuid.uuid4())
1168
1169     if not os.path.exists(paths.private_dir):
1170         os.mkdir(paths.private_dir)
1171
1172     ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1173     
1174     schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn,
1175         sambadn=names.sambadn, ldap_backend_type=ldap_backend_type)
1176     
1177     secrets_credentials = credentials
1178     provision_backend = None
1179     if ldap_backend_type:
1180         # We only support an LDAP backend over ldapi://
1181
1182         provision_backend = ProvisionBackend(paths=paths, setup_path=setup_path,
1183                                              lp=lp, credentials=credentials, 
1184                                              names=names,
1185                                              message=message, hostname=hostname,
1186                                              root=root, schema=schema,
1187                                              ldap_backend_type=ldap_backend_type,
1188                                              ldapadminpass=ldapadminpass,
1189                                              ldap_backend_extra_port=ldap_backend_extra_port,
1190                                              ol_mmr_urls=ol_mmr_urls, 
1191                                              slapd_path=slapd_path,
1192                                              setup_ds_path=setup_ds_path,
1193                                              ldap_dryrun_mode=ldap_dryrun_mode)
1194
1195         # Now use the backend credentials to access the databases
1196         credentials = provision_backend.credentials
1197         secrets_credentials = provision_backend.adminCredentials
1198         ldapi_url = provision_backend.ldapi_uri
1199
1200     # only install a new shares config db if there is none
1201     if not os.path.exists(paths.shareconf):
1202         message("Setting up share.ldb")
1203         share_ldb = Ldb(paths.shareconf, session_info=session_info, 
1204                         credentials=credentials, lp=lp)
1205         share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1206
1207      
1208     message("Setting up secrets.ldb")
1209     secrets_ldb = setup_secretsdb(paths.secrets, setup_path, 
1210                                   session_info=session_info, 
1211                                   credentials=secrets_credentials, lp=lp)
1212
1213     message("Setting up the registry")
1214     setup_registry(paths.hklm, setup_path, session_info, 
1215                    lp=lp)
1216
1217     message("Setting up idmap db")
1218     idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1219                           lp=lp)
1220
1221     message("Setting up SAM db")
1222     samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info, 
1223                         credentials=credentials, lp=lp, names=names,
1224                         message=message, 
1225                         domainsid=domainsid, 
1226                         schema=schema, domainguid=domainguid,
1227                         policyguid=policyguid, policyguid_dc=policyguid_dc,
1228                         fill=samdb_fill, 
1229                         adminpass=adminpass, krbtgtpass=krbtgtpass,
1230                         invocationid=invocationid, 
1231                         machinepass=machinepass, dnspass=dnspass,
1232                         serverrole=serverrole, ldap_backend=provision_backend)
1233
1234     if serverrole == "domain controller":
1235         if paths.netlogon is None:
1236             message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1237             message("Please either remove %s or see the template at %s" % 
1238                     ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1239             assert(paths.netlogon is not None)
1240
1241         if paths.sysvol is None:
1242             message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1243             message("Please either remove %s or see the template at %s" % 
1244                     (paths.smbconf, setup_path("provision.smb.conf.dc")))
1245             assert(paths.sysvol is not None)            
1246             
1247         # Set up group policies (domain policy and domain controller policy)
1248
1249         policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1250                                    "{" + policyguid + "}")
1251         os.makedirs(policy_path, 0755)
1252         open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1253                                    "[General]\r\nVersion=65543")
1254         os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
1255         os.makedirs(os.path.join(policy_path, "USER"), 0755)
1256
1257         policy_path_dc = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1258                                    "{" + policyguid_dc + "}")
1259         os.makedirs(policy_path_dc, 0755)
1260         open(os.path.join(policy_path_dc, "GPT.INI"), 'w').write(
1261                                    "[General]\r\nVersion=2")
1262         os.makedirs(os.path.join(policy_path_dc, "MACHINE"), 0755)
1263         os.makedirs(os.path.join(policy_path_dc, "USER"), 0755)
1264
1265         if not os.path.isdir(paths.netlogon):
1266             os.makedirs(paths.netlogon, 0755)
1267
1268     if samdb_fill == FILL_FULL:
1269         setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1270                             root_uid=root_uid, nobody_uid=nobody_uid,
1271                             users_gid=users_gid, wheel_gid=wheel_gid)
1272
1273         message("Setting up sam.ldb rootDSE marking as synchronized")
1274         setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1275
1276         # Only make a zone file on the first DC, it should be replicated with DNS replication
1277         if serverrole == "domain controller":
1278             secrets_ldb = Ldb(paths.secrets, session_info=session_info, 
1279                               credentials=credentials, lp=lp)
1280             secretsdb_become_dc(secrets_ldb, setup_path, domain=domain,
1281                                 realm=names.realm,
1282                                 netbiosname=names.netbiosname,
1283                                 domainsid=domainsid, 
1284                                 keytab_path=paths.keytab, samdb_url=paths.samdb,
1285                                 dns_keytab_path=paths.dns_keytab,
1286                                 dnspass=dnspass, machinepass=machinepass,
1287                                 dnsdomain=names.dnsdomain)
1288
1289             domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1290             assert isinstance(domainguid, str)
1291
1292             create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1293                              domaindn=names.domaindn, hostip=hostip,
1294                              hostip6=hostip6, hostname=names.hostname,
1295                              dnspass=dnspass, realm=names.realm,
1296                              domainguid=domainguid, ntdsguid=names.ntdsguid)
1297
1298             create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1299                               dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1300
1301             create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1302                               dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1303                               keytab_name=paths.dns_keytab)
1304             message("See %s for an example configuration include file for BIND" % paths.namedconf)
1305             message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1306
1307             create_krb5_conf(paths.krb5conf, setup_path,
1308                              dnsdomain=names.dnsdomain, hostname=names.hostname,
1309                              realm=names.realm)
1310             message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1311
1312
1313     if provision_backend is not None: 
1314       if ldap_backend_type == "fedora-ds":
1315         ldapi_db = Ldb(provision_backend.ldapi_uri, lp=lp, credentials=credentials)
1316
1317         # delete default SASL mappings
1318         res = ldapi_db.search(expression="(!(cn=samba-admin mapping))", base="cn=mapping,cn=sasl,cn=config", scope=SCOPE_ONELEVEL, attrs=["dn"])
1319
1320         # configure in-directory access control on Fedora DS via the aci attribute (over a direct ldapi:// socket)
1321         for i in range (0, len(res)):
1322           dn = str(res[i]["dn"])
1323           ldapi_db.delete(dn)
1324
1325           aci = """(targetattr = "*") (version 3.0;acl "full access to all by samba-admin";allow (all)(userdn = "ldap:///CN=samba-admin,%s");)""" % names.sambadn
1326
1327           m = ldb.Message()
1328           m["aci"] = ldb.MessageElement([aci], ldb.FLAG_MOD_REPLACE, "aci")
1329         
1330           m.dn = ldb.Dn(1, names.domaindn)
1331           ldapi_db.modify(m)
1332
1333           m.dn = ldb.Dn(1, names.configdn)
1334           ldapi_db.modify(m)
1335
1336           m.dn = ldb.Dn(1, names.schemadn)
1337           ldapi_db.modify(m)
1338
1339       # if an LDAP backend is in use, terminate slapd after final provision and check its proper termination
1340       if provision_backend.slapd.poll() is None:
1341         #Kill the slapd
1342         if hasattr(provision_backend.slapd, "terminate"):
1343           provision_backend.slapd.terminate()
1344         else:
1345           # Older python versions don't have .terminate()
1346           import signal
1347           os.kill(provision_backend.slapd.pid, signal.SIGTERM)
1348             
1349         #and now wait for it to die
1350         provision_backend.slapd.communicate()
1351             
1352     # now display slapd_command_file.txt to show how slapd must be started next time
1353         message("Use later the following commandline to start slapd, then Samba:")
1354         slapd_command = "\'" + "\' \'".join(provision_backend.slapd_command) + "\'"
1355         message(slapd_command)
1356         message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh")
1357
1358         setup_file(setup_path("ldap_backend_startup.sh"), paths.ldapdir + "/ldap_backend_startup.sh", {
1359                 "SLAPD_COMMAND" : slapd_command})
1360
1361     
1362     create_phpldapadmin_config(paths.phpldapadminconfig, setup_path, 
1363                                ldapi_url)
1364
1365     message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1366
1367     message("Once the above files are installed, your Samba4 server will be ready to use")
1368     message("Server Role:           %s" % serverrole)
1369     message("Hostname:              %s" % names.hostname)
1370     message("NetBIOS Domain:        %s" % names.domain)
1371     message("DNS Domain:            %s" % names.dnsdomain)
1372     message("DOMAIN SID:            %s" % str(domainsid))
1373     if samdb_fill == FILL_FULL:
1374         message("Admin password:    %s" % adminpass)
1375     if provision_backend:
1376         if provision_backend.credentials.get_bind_dn() is not None:
1377             message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1378         else:
1379             message("LDAP Admin User:       %s" % provision_backend.credentials.get_username())
1380
1381         message("LDAP Admin Password:   %s" % provision_backend.credentials.get_password())
1382   
1383     result = ProvisionResult()
1384     result.domaindn = domaindn
1385     result.paths = paths
1386     result.lp = lp
1387     result.samdb = samdb
1388     return result
1389
1390
1391
1392 def provision_become_dc(setup_dir=None,
1393                         smbconf=None, targetdir=None, realm=None, 
1394                         rootdn=None, domaindn=None, schemadn=None,
1395                         configdn=None, serverdn=None,
1396                         domain=None, hostname=None, domainsid=None, 
1397                         adminpass=None, krbtgtpass=None, domainguid=None, 
1398                         policyguid=None, policyguid_dc=None, invocationid=None,
1399                         machinepass=None, 
1400                         dnspass=None, root=None, nobody=None, users=None, 
1401                         wheel=None, backup=None, serverrole=None, 
1402                         ldap_backend=None, ldap_backend_type=None,
1403                         sitename=None, debuglevel=1):
1404
1405     def message(text):
1406         """print a message if quiet is not set."""
1407         print text
1408
1409     glue.set_debug_level(debuglevel)
1410
1411     return provision(setup_dir, message, system_session(), None,
1412               smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1413               realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1414               configdn=configdn, serverdn=serverdn, domain=domain,
1415               hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1416               machinepass=machinepass, serverrole="domain controller",
1417               sitename=sitename)
1418
1419
1420 def setup_db_config(setup_path, dbdir):
1421     """Setup a Berkeley database.
1422     
1423     :param setup_path: Setup path function.
1424     :param dbdir: Database directory."""
1425     if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
1426         os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700)
1427         if not os.path.isdir(os.path.join(dbdir, "tmp")):
1428             os.makedirs(os.path.join(dbdir, "tmp"), 0700)
1429
1430     setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
1431                {"LDAPDBDIR": dbdir})
1432     
1433 class ProvisionBackend(object):
1434     def __init__(self, paths=None, setup_path=None, lp=None, credentials=None, 
1435                  names=None, message=None, 
1436                  hostname=None, root=None, 
1437                  schema=None, ldapadminpass=None,
1438                  ldap_backend_type=None, ldap_backend_extra_port=None,
1439                  ol_mmr_urls=None, 
1440                  setup_ds_path=None, slapd_path=None, 
1441                  nosync=False, ldap_dryrun_mode=False):
1442         """Provision an LDAP backend for samba4
1443         
1444         This works for OpenLDAP and Fedora DS
1445         """
1446
1447         self.ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.ldapdir, "ldapi"), safe="")
1448         
1449         if not os.path.isdir(paths.ldapdir):
1450             os.makedirs(paths.ldapdir, 0700)
1451             
1452         if ldap_backend_type == "existing":
1453             #Check to see that this 'existing' LDAP backend in fact exists
1454             ldapi_db = Ldb(self.ldapi_uri, credentials=credentials)
1455             search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1456                                                 expression="(objectClass=OpenLDAProotDSE)")
1457
1458             # If we have got here, then we must have a valid connection to the LDAP server, with valid credentials supplied
1459             # This caused them to be set into the long-term database later in the script.
1460             self.credentials = credentials
1461             self.ldap_backend_type = "openldap" #For now, assume existing backends at least emulate OpenLDAP
1462             return
1463     
1464         # we will shortly start slapd with ldapi for final provisioning. first check with ldapsearch -> rootDSE via self.ldapi_uri
1465         # if another instance of slapd is already running 
1466         try:
1467             ldapi_db = Ldb(self.ldapi_uri)
1468             search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1469                                                 expression="(objectClass=OpenLDAProotDSE)");
1470             try:
1471                 f = open(paths.slapdpid, "r")
1472                 p = f.read()
1473                 f.close()
1474                 message("Check for slapd Process with PID: " + str(p) + " and terminate it manually.")
1475             except:
1476                 pass
1477             
1478             raise ProvisioningError("Warning: Another slapd Instance seems already running on this host, listening to " + self.ldapi_uri + ". Please shut it down before you continue. ")
1479         
1480         except LdbError, e:
1481             pass
1482
1483         # Try to print helpful messages when the user has not specified the path to slapd
1484         if slapd_path is None:
1485             raise ProvisioningError("Warning: LDAP-Backend must be setup with path to slapd, e.g. --slapd-path=\"/usr/local/libexec/slapd\"!")
1486         if not os.path.exists(slapd_path):
1487             message (slapd_path)
1488             raise ProvisioningError("Warning: Given Path to slapd does not exist!")
1489
1490         schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
1491         try:
1492             os.unlink(schemadb_path)
1493         except OSError:
1494             pass
1495
1496
1497         # Put the LDIF of the schema into a database so we can search on
1498         # it to generate schema-dependent configurations in Fedora DS and
1499         # OpenLDAP
1500         os.path.join(paths.ldapdir, "schema-tmp.ldb")
1501         schema.ldb.connect(schemadb_path)
1502         schema.ldb.transaction_start()
1503     
1504         # These bits of LDIF are supplied when the Schema object is created
1505         schema.ldb.add_ldif(schema.schema_dn_add)
1506         schema.ldb.modify_ldif(schema.schema_dn_modify)
1507         schema.ldb.add_ldif(schema.schema_data)
1508         schema.ldb.transaction_commit()
1509
1510         self.credentials = Credentials()
1511         self.credentials.guess(lp)
1512         #Kerberos to an ldapi:// backend makes no sense
1513         self.credentials.set_kerberos_state(DONT_USE_KERBEROS)
1514
1515         self.adminCredentials = Credentials()
1516         self.adminCredentials.guess(lp)
1517         #Kerberos to an ldapi:// backend makes no sense
1518         self.adminCredentials.set_kerberos_state(DONT_USE_KERBEROS)
1519
1520         self.ldap_backend_type = ldap_backend_type
1521
1522         if ldap_backend_type == "fedora-ds":
1523             provision_fds_backend(self, paths=paths, setup_path=setup_path,
1524                                   names=names, message=message, 
1525                                   hostname=hostname,
1526                                   ldapadminpass=ldapadminpass, root=root, 
1527                                   schema=schema,
1528                                   ldap_backend_extra_port=ldap_backend_extra_port, 
1529                                   setup_ds_path=setup_ds_path,
1530                                   slapd_path=slapd_path,
1531                                   nosync=nosync,
1532                                   ldap_dryrun_mode=ldap_dryrun_mode)
1533             
1534         elif ldap_backend_type == "openldap":
1535             provision_openldap_backend(self, paths=paths, setup_path=setup_path,
1536                                        names=names, message=message, 
1537                                        hostname=hostname,
1538                                        ldapadminpass=ldapadminpass, root=root, 
1539                                        schema=schema,
1540                                        ldap_backend_extra_port=ldap_backend_extra_port, 
1541                                        ol_mmr_urls=ol_mmr_urls, 
1542                                        slapd_path=slapd_path,
1543                                        nosync=nosync,
1544                                        ldap_dryrun_mode=ldap_dryrun_mode)
1545         else:
1546             raise ProvisioningError("Unknown LDAP backend type selected")
1547
1548         self.credentials.set_password(ldapadminpass)
1549         self.adminCredentials.set_username("samba-admin")
1550         self.adminCredentials.set_password(ldapadminpass)
1551
1552         # Now start the slapd, so we can provision onto it.  We keep the
1553         # subprocess context around, to kill this off at the successful
1554         # end of the script
1555         self.slapd = subprocess.Popen(self.slapd_provision_command, close_fds=True, shell=False)
1556     
1557         while self.slapd.poll() is None:
1558             # Wait until the socket appears
1559             try:
1560                 ldapi_db = Ldb(self.ldapi_uri, lp=lp, credentials=self.credentials)
1561                 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1562                                                     expression="(objectClass=OpenLDAProotDSE)")
1563                 # If we have got here, then we must have a valid connection to the LDAP server!
1564                 return
1565             except LdbError, e:
1566                 time.sleep(1)
1567                 pass
1568         
1569         raise ProvisioningError("slapd died before we could make a connection to it")
1570
1571
1572 def provision_openldap_backend(result, paths=None, setup_path=None, names=None,
1573                                message=None, 
1574                                hostname=None, ldapadminpass=None, root=None, 
1575                                schema=None, 
1576                                ldap_backend_extra_port=None,
1577                                ol_mmr_urls=None, 
1578                                slapd_path=None, nosync=False,
1579                                ldap_dryrun_mode=False):
1580
1581     #Allow the test scripts to turn off fsync() for OpenLDAP as for TDB and LDB
1582     nosync_config = ""
1583     if nosync:
1584         nosync_config = "dbnosync"
1585         
1586     lnkattr = get_linked_attributes(names.schemadn,schema.ldb)
1587     refint_attributes = ""
1588     memberof_config = "# Generated from Samba4 schema\n"
1589     for att in  lnkattr.keys():
1590         if lnkattr[att] is not None:
1591             refint_attributes = refint_attributes + " " + att 
1592             
1593             memberof_config += read_and_sub_file(setup_path("memberof.conf"),
1594                                                  { "MEMBER_ATTR" : att ,
1595                                                    "MEMBEROF_ATTR" : lnkattr[att] })
1596             
1597     refint_config = read_and_sub_file(setup_path("refint.conf"),
1598                                       { "LINK_ATTRS" : refint_attributes})
1599     
1600     attrs = ["linkID", "lDAPDisplayName"]
1601     res = schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
1602     index_config = ""
1603     for i in range (0, len(res)):
1604         index_attr = res[i]["lDAPDisplayName"][0]
1605         if index_attr == "objectGUID":
1606             index_attr = "entryUUID"
1607             
1608         index_config += "index " + index_attr + " eq\n"
1609
1610 # generate serverids, ldap-urls and syncrepl-blocks for mmr hosts
1611     mmr_on_config = ""
1612     mmr_replicator_acl = ""
1613     mmr_serverids_config = ""
1614     mmr_syncrepl_schema_config = "" 
1615     mmr_syncrepl_config_config = "" 
1616     mmr_syncrepl_user_config = "" 
1617        
1618     
1619     if ol_mmr_urls is not None:
1620         # For now, make these equal
1621         mmr_pass = ldapadminpass
1622         
1623         url_list=filter(None,ol_mmr_urls.split(' ')) 
1624         if (len(url_list) == 1):
1625             url_list=filter(None,ol_mmr_urls.split(',')) 
1626                      
1627             
1628             mmr_on_config = "MirrorMode On"
1629             mmr_replicator_acl = "  by dn=cn=replicator,cn=samba read"
1630             serverid=0
1631             for url in url_list:
1632                 serverid=serverid+1
1633                 mmr_serverids_config += read_and_sub_file(setup_path("mmr_serverids.conf"),
1634                                                           { "SERVERID" : str(serverid),
1635                                                             "LDAPSERVER" : url })
1636                 rid=serverid*10
1637                 rid=rid+1
1638                 mmr_syncrepl_schema_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1639                                                                 {  "RID" : str(rid),
1640                                                                    "MMRDN": names.schemadn,
1641                                                                    "LDAPSERVER" : url,
1642                                                                    "MMR_PASSWORD": mmr_pass})
1643                 
1644                 rid=rid+1
1645                 mmr_syncrepl_config_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1646                                                                 {  "RID" : str(rid),
1647                                                                    "MMRDN": names.configdn,
1648                                                                    "LDAPSERVER" : url,
1649                                                                    "MMR_PASSWORD": mmr_pass})
1650                 
1651                 rid=rid+1
1652                 mmr_syncrepl_user_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1653                                                               {  "RID" : str(rid),
1654                                                                  "MMRDN": names.domaindn,
1655                                                                  "LDAPSERVER" : url,
1656                                                                  "MMR_PASSWORD": mmr_pass })
1657     # OpenLDAP cn=config initialisation
1658     olc_syncrepl_config = ""
1659     olc_mmr_config = "" 
1660     # if mmr = yes, generate cn=config-replication directives
1661     # and olc_seed.lif for the other mmr-servers
1662     if ol_mmr_urls is not None:
1663         serverid=0
1664         olc_serverids_config = ""
1665         olc_syncrepl_seed_config = ""
1666         olc_mmr_config += read_and_sub_file(setup_path("olc_mmr.conf"),{})
1667         rid=1000
1668         for url in url_list:
1669             serverid=serverid+1
1670             olc_serverids_config += read_and_sub_file(setup_path("olc_serverid.conf"),
1671                                                       { "SERVERID" : str(serverid),
1672                                                         "LDAPSERVER" : url })
1673             
1674             rid=rid+1
1675             olc_syncrepl_config += read_and_sub_file(setup_path("olc_syncrepl.conf"),
1676                                                      {  "RID" : str(rid),
1677                                                         "LDAPSERVER" : url,
1678                                                         "MMR_PASSWORD": mmr_pass})
1679             
1680             olc_syncrepl_seed_config += read_and_sub_file(setup_path("olc_syncrepl_seed.conf"),
1681                                                           {  "RID" : str(rid),
1682                                                              "LDAPSERVER" : url})
1683                 
1684         setup_file(setup_path("olc_seed.ldif"), paths.olcseedldif,
1685                    {"OLC_SERVER_ID_CONF": olc_serverids_config,
1686                     "OLC_PW": ldapadminpass,
1687                     "OLC_SYNCREPL_CONF": olc_syncrepl_seed_config})
1688     # end olc
1689                 
1690     setup_file(setup_path("slapd.conf"), paths.slapdconf,
1691                {"DNSDOMAIN": names.dnsdomain,
1692                 "LDAPDIR": paths.ldapdir,
1693                 "DOMAINDN": names.domaindn,
1694                 "CONFIGDN": names.configdn,
1695                 "SCHEMADN": names.schemadn,
1696                 "MEMBEROF_CONFIG": memberof_config,
1697                 "MIRRORMODE": mmr_on_config,
1698                 "REPLICATOR_ACL": mmr_replicator_acl,
1699                 "MMR_SERVERIDS_CONFIG": mmr_serverids_config,
1700                 "MMR_SYNCREPL_SCHEMA_CONFIG": mmr_syncrepl_schema_config,
1701                 "MMR_SYNCREPL_CONFIG_CONFIG": mmr_syncrepl_config_config,
1702                 "MMR_SYNCREPL_USER_CONFIG": mmr_syncrepl_user_config,
1703                 "OLC_SYNCREPL_CONFIG": olc_syncrepl_config,
1704                 "OLC_MMR_CONFIG": olc_mmr_config,
1705                 "REFINT_CONFIG": refint_config,
1706                 "INDEX_CONFIG": index_config,
1707                 "NOSYNC": nosync_config})
1708         
1709     setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "user"))
1710     setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "config"))
1711     setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "schema"))
1712     
1713     if not os.path.exists(os.path.join(paths.ldapdir, "db", "samba",  "cn=samba")):
1714         os.makedirs(os.path.join(paths.ldapdir, "db", "samba",  "cn=samba"), 0700)
1715         
1716     setup_file(setup_path("cn=samba.ldif"), 
1717                os.path.join(paths.ldapdir, "db", "samba",  "cn=samba.ldif"),
1718                { "UUID": str(uuid.uuid4()), 
1719                  "LDAPTIME": timestring(int(time.time()))} )
1720     setup_file(setup_path("cn=samba-admin.ldif"), 
1721                os.path.join(paths.ldapdir, "db", "samba",  "cn=samba", "cn=samba-admin.ldif"),
1722                {"LDAPADMINPASS_B64": b64encode(ldapadminpass),
1723                 "UUID": str(uuid.uuid4()), 
1724                 "LDAPTIME": timestring(int(time.time()))} )
1725     
1726     if ol_mmr_urls is not None:
1727         setup_file(setup_path("cn=replicator.ldif"),
1728                    os.path.join(paths.ldapdir, "db", "samba",  "cn=samba", "cn=replicator.ldif"),
1729                    {"MMR_PASSWORD_B64": b64encode(mmr_pass),
1730                     "UUID": str(uuid.uuid4()),
1731                     "LDAPTIME": timestring(int(time.time()))} )
1732         
1733
1734     mapping = "schema-map-openldap-2.3"
1735     backend_schema = "backend-schema.schema"
1736
1737     backend_schema_data = schema.ldb.convert_schema_to_openldap("openldap", open(setup_path(mapping), 'r').read())
1738     assert backend_schema_data is not None
1739     open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1740
1741     # now we generate the needed strings to start slapd automatically,
1742     # first ldapi_uri...
1743     if ldap_backend_extra_port is not None:
1744         # When we use MMR, we can't use 0.0.0.0 as it uses the name
1745         # specified there as part of it's clue as to it's own name,
1746         # and not to replicate to itself
1747         if ol_mmr_urls is None:
1748             server_port_string = "ldap://0.0.0.0:%d" % ldap_backend_extra_port
1749         else:
1750             server_port_string = "ldap://" + names.hostname + "." + names.dnsdomain +":%d" % ldap_backend_extra_port
1751     else:
1752         server_port_string = ""
1753
1754     # Prepare the 'result' information - the commands to return in particular
1755     result.slapd_provision_command = [slapd_path]
1756
1757     result.slapd_provision_command.append("-F" + paths.olcdir)
1758
1759     result.slapd_provision_command.append("-h")
1760
1761     # copy this command so we have two version, one with -d0 and only ldapi, and one with all the listen commands
1762     result.slapd_command = list(result.slapd_provision_command)
1763     
1764     result.slapd_provision_command.append(result.ldapi_uri)
1765     result.slapd_provision_command.append("-d0")
1766
1767     uris = result.ldapi_uri
1768     if server_port_string is not "":
1769         uris = uris + " " + server_port_string
1770
1771     result.slapd_command.append(uris)
1772
1773     # Set the username - done here because Fedora DS still uses the admin DN and simple bind
1774     result.credentials.set_username("samba-admin")
1775     
1776     # If we were just looking for crashes up to this point, it's a
1777     # good time to exit before we realise we don't have OpenLDAP on
1778     # this system
1779     if ldap_dryrun_mode:
1780         sys.exit(0)
1781
1782     # Finally, convert the configuration into cn=config style!
1783     if not os.path.isdir(paths.olcdir):
1784         os.makedirs(paths.olcdir, 0770)
1785
1786         retcode = subprocess.call([slapd_path, "-Ttest", "-f", paths.slapdconf, "-F", paths.olcdir], close_fds=True, shell=False)
1787
1788 #        We can't do this, as OpenLDAP is strange.  It gives an error
1789 #        output to the above, but does the conversion sucessfully...
1790 #
1791 #        if retcode != 0:
1792 #            raise ProvisioningError("conversion from slapd.conf to cn=config failed")
1793
1794         if not os.path.exists(os.path.join(paths.olcdir, "cn=config.ldif")):
1795             raise ProvisioningError("conversion from slapd.conf to cn=config failed")
1796
1797         # Don't confuse the admin by leaving the slapd.conf around
1798         os.remove(paths.slapdconf)        
1799           
1800
1801 def provision_fds_backend(result, paths=None, setup_path=None, names=None,
1802                           message=None, 
1803                           hostname=None, ldapadminpass=None, root=None, 
1804                           schema=None,
1805                           ldap_backend_extra_port=None,
1806                           setup_ds_path=None,
1807                           slapd_path=None,
1808                           nosync=False, 
1809                           ldap_dryrun_mode=False):
1810
1811     if ldap_backend_extra_port is not None:
1812         serverport = "ServerPort=%d" % ldap_backend_extra_port
1813     else:
1814         serverport = ""
1815         
1816     setup_file(setup_path("fedorads.inf"), paths.fedoradsinf, 
1817                {"ROOT": root,
1818                 "HOSTNAME": hostname,
1819                 "DNSDOMAIN": names.dnsdomain,
1820                 "LDAPDIR": paths.ldapdir,
1821                 "DOMAINDN": names.domaindn,
1822                 "LDAPMANAGERDN": names.ldapmanagerdn,
1823                 "LDAPMANAGERPASS": ldapadminpass, 
1824                 "SERVERPORT": serverport})
1825
1826     setup_file(setup_path("fedorads-partitions.ldif"), paths.fedoradspartitions, 
1827                {"CONFIGDN": names.configdn,
1828                 "SCHEMADN": names.schemadn,
1829                 "SAMBADN": names.sambadn,
1830                 })
1831
1832     setup_file(setup_path("fedorads-sasl.ldif"), paths.fedoradssasl, 
1833                {"SAMBADN": names.sambadn,
1834                 })
1835
1836     setup_file(setup_path("fedorads-samba.ldif"), paths.fedoradssamba,
1837                 {"SAMBADN": names.sambadn, 
1838                  "LDAPADMINPASS": ldapadminpass
1839                 })
1840
1841     mapping = "schema-map-fedora-ds-1.0"
1842     backend_schema = "99_ad.ldif"
1843     
1844     # Build a schema file in Fedora DS format
1845     backend_schema_data = schema.ldb.convert_schema_to_openldap("fedora-ds", open(setup_path(mapping), 'r').read())
1846     assert backend_schema_data is not None
1847     open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1848
1849     result.credentials.set_bind_dn(names.ldapmanagerdn)
1850
1851     # Destory the target directory, or else setup-ds.pl will complain
1852     fedora_ds_dir = os.path.join(paths.ldapdir, "slapd-samba4")
1853     shutil.rmtree(fedora_ds_dir, True)
1854
1855     result.slapd_provision_command = [slapd_path, "-D", fedora_ds_dir, "-i", paths.slapdpid];
1856     #In the 'provision' command line, stay in the foreground so we can easily kill it
1857     result.slapd_provision_command.append("-d0")
1858
1859     #the command for the final run is the normal script
1860     result.slapd_command = [os.path.join(paths.ldapdir, "slapd-samba4", "start-slapd")]
1861
1862     # If we were just looking for crashes up to this point, it's a
1863     # good time to exit before we realise we don't have Fedora DS on
1864     if ldap_dryrun_mode:
1865         sys.exit(0)
1866
1867     # Try to print helpful messages when the user has not specified the path to the setup-ds tool
1868     if setup_ds_path is None:
1869         raise ProvisioningError("Warning: Fedora DS LDAP-Backend must be setup with path to setup-ds, e.g. --setup-ds-path=\"/usr/sbin/setup-ds.pl\"!")
1870     if not os.path.exists(setup_ds_path):
1871         message (setup_ds_path)
1872         raise ProvisioningError("Warning: Given Path to slapd does not exist!")
1873
1874     # Run the Fedora DS setup utility
1875     retcode = subprocess.call([setup_ds_path, "--silent", "--file", paths.fedoradsinf], close_fds=True, shell=False)
1876     if retcode != 0:
1877         raise ProvisioningError("setup-ds failed")
1878
1879     # Load samba-admin
1880     retcode = subprocess.call([
1881         os.path.join(paths.ldapdir, "slapd-samba4", "ldif2db"), "-s", names.sambadn, "-i", paths.fedoradssamba],
1882         close_fds=True, shell=False)
1883     if retcode != 0:
1884         raise("ldib2db failed")
1885
1886 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1887     """Create a PHP LDAP admin configuration file.
1888
1889     :param path: Path to write the configuration to.
1890     :param setup_path: Function to generate setup paths.
1891     """
1892     setup_file(setup_path("phpldapadmin-config.php"), path, 
1893             {"S4_LDAPI_URI": ldapi_uri})
1894
1895
1896 def create_zone_file(path, setup_path, dnsdomain, domaindn, 
1897                      hostip, hostip6, hostname, dnspass, realm, domainguid,
1898                      ntdsguid):
1899     """Write out a DNS zone file, from the info in the current database.
1900
1901     :param path: Path of the new zone file.
1902     :param setup_path: Setup path function.
1903     :param dnsdomain: DNS Domain name
1904     :param domaindn: DN of the Domain
1905     :param hostip: Local IPv4 IP
1906     :param hostip6: Local IPv6 IP
1907     :param hostname: Local hostname
1908     :param dnspass: Password for DNS
1909     :param realm: Realm name
1910     :param domainguid: GUID of the domain.
1911     :param ntdsguid: GUID of the hosts nTDSDSA record.
1912     """
1913     assert isinstance(domainguid, str)
1914
1915     if hostip6 is not None:
1916         hostip6_base_line = "            IN AAAA    " + hostip6
1917         hostip6_host_line = hostname + "        IN AAAA    " + hostip6
1918     else:
1919         hostip6_base_line = ""
1920         hostip6_host_line = ""
1921
1922     if hostip is not None:
1923         hostip_base_line = "            IN A    " + hostip
1924         hostip_host_line = hostname + "        IN A    " + hostip
1925     else:
1926         hostip_base_line = ""
1927         hostip_host_line = ""
1928
1929     setup_file(setup_path("provision.zone"), path, {
1930             "DNSPASS_B64": b64encode(dnspass),
1931             "HOSTNAME": hostname,
1932             "DNSDOMAIN": dnsdomain,
1933             "REALM": realm,
1934             "HOSTIP_BASE_LINE": hostip_base_line,
1935             "HOSTIP_HOST_LINE": hostip_host_line,
1936             "DOMAINGUID": domainguid,
1937             "DATESTRING": time.strftime("%Y%m%d%H"),
1938             "DEFAULTSITE": DEFAULTSITE,
1939             "NTDSGUID": ntdsguid,
1940             "HOSTIP6_BASE_LINE": hostip6_base_line,
1941             "HOSTIP6_HOST_LINE": hostip6_host_line,
1942         })
1943
1944
1945 def create_named_conf(path, setup_path, realm, dnsdomain,
1946                       private_dir):
1947     """Write out a file containing zone statements suitable for inclusion in a
1948     named.conf file (including GSS-TSIG configuration).
1949     
1950     :param path: Path of the new named.conf file.
1951     :param setup_path: Setup path function.
1952     :param realm: Realm name
1953     :param dnsdomain: DNS Domain name
1954     :param private_dir: Path to private directory
1955     :param keytab_name: File name of DNS keytab file
1956     """
1957
1958     setup_file(setup_path("named.conf"), path, {
1959             "DNSDOMAIN": dnsdomain,
1960             "REALM": realm,
1961             "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1962             "PRIVATE_DIR": private_dir
1963             })
1964
1965 def create_named_txt(path, setup_path, realm, dnsdomain,
1966                       private_dir, keytab_name):
1967     """Write out a file containing zone statements suitable for inclusion in a
1968     named.conf file (including GSS-TSIG configuration).
1969     
1970     :param path: Path of the new named.conf file.
1971     :param setup_path: Setup path function.
1972     :param realm: Realm name
1973     :param dnsdomain: DNS Domain name
1974     :param private_dir: Path to private directory
1975     :param keytab_name: File name of DNS keytab file
1976     """
1977
1978     setup_file(setup_path("named.txt"), path, {
1979             "DNSDOMAIN": dnsdomain,
1980             "REALM": realm,
1981             "DNS_KEYTAB": keytab_name,
1982             "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1983             "PRIVATE_DIR": private_dir
1984         })
1985
1986 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1987     """Write out a file containing zone statements suitable for inclusion in a
1988     named.conf file (including GSS-TSIG configuration).
1989     
1990     :param path: Path of the new named.conf file.
1991     :param setup_path: Setup path function.
1992     :param dnsdomain: DNS Domain name
1993     :param hostname: Local hostname
1994     :param realm: Realm name
1995     """
1996
1997     setup_file(setup_path("krb5.conf"), path, {
1998             "DNSDOMAIN": dnsdomain,
1999             "HOSTNAME": hostname,
2000             "REALM": realm,
2001         })
2002
2003