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