fe11b94d67546902ee2b0d6105633c1a7cda969d
[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 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 = ["resolve_oids",
579                     "rootdse",
580                     "paged_results",
581                     "ranged_results",
582                     "anr",
583                     "server_sort",
584                     "asq",
585                     "extended_dn_store",
586                     "extended_dn_in",
587                     "rdn_name",
588                     "objectclass",
589                     "descriptor",
590                     "samldb",
591                     "password_hash",
592                     "operational",
593                     "kludge_acl"]
594     tdb_modules_list = [
595                     "subtree_rename",
596                     "subtree_delete",
597                     "linked_attributes",
598                     "extended_dn_out_ldb"]
599     modules_list2 = ["show_deleted",
600                     "partition"]
601  
602     domaindn_ldb = "users.ldb"
603     configdn_ldb = "configuration.ldb"
604     schemadn_ldb = "schema.ldb"
605     if ldap_backend is not None:
606         domaindn_ldb = ldap_backend.ldapi_uri
607         configdn_ldb = ldap_backend.ldapi_uri
608         schemadn_ldb = ldap_backend.ldapi_uri
609         
610         if ldap_backend.ldap_backend_type == "fedora-ds":
611             backend_modules = ["nsuniqueid", "paged_searches"]
612             # We can handle linked attributes here, as we don't have directory-side subtree operations
613             tdb_modules_list = ["linked_attributes", "extended_dn_out_dereference"]
614         elif ldap_backend.ldap_backend_type == "openldap":
615             backend_modules = ["entryuuid", "paged_searches"]
616             # OpenLDAP handles subtree renames, so we don't want to do any of these things
617             tdb_modules_list = ["extended_dn_out_dereference"]
618
619     elif serverrole == "domain controller":
620         tdb_modules_list.insert(0, "repl_meta_data")
621         backend_modules = []
622     else:
623         backend_modules = ["objectguid"]
624
625     if tdb_modules_list is None:
626         tdb_modules_list_as_string = ""
627     else:
628         tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
629         
630     samdb.transaction_start()
631     try:
632         message("Setting up sam.ldb partitions and settings")
633         setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
634                 "SCHEMADN": names.schemadn, 
635                 "SCHEMADN_LDB": schemadn_ldb,
636                 "SCHEMADN_MOD2": ",objectguid",
637                 "CONFIGDN": names.configdn,
638                 "CONFIGDN_LDB": configdn_ldb,
639                 "DOMAINDN": names.domaindn,
640                 "DOMAINDN_LDB": domaindn_ldb,
641                 "SCHEMADN_MOD": "schema_fsmo,instancetype",
642                 "CONFIGDN_MOD": "naming_fsmo,instancetype",
643                 "DOMAINDN_MOD": "pdc_fsmo,instancetype",
644                 "MODULES_LIST": ",".join(modules_list),
645                 "TDB_MODULES_LIST": tdb_modules_list_as_string,
646                 "MODULES_LIST2": ",".join(modules_list2),
647                 "BACKEND_MOD": ",".join(backend_modules),
648         })
649
650         samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
651
652         message("Setting up sam.ldb rootDSE")
653         setup_samdb_rootdse(samdb, setup_path, names)
654
655     except:
656         samdb.transaction_cancel()
657         raise
658
659     samdb.transaction_commit()
660     
661
662
663 def secretsdb_become_dc(secretsdb, setup_path, domain, realm, dnsdomain, 
664                         netbiosname, domainsid, keytab_path, samdb_url, 
665                         dns_keytab_path, dnspass, machinepass):
666     """Add DC-specific bits to a secrets database.
667     
668     :param secretsdb: Ldb Handle to the secrets database
669     :param setup_path: Setup path function
670     :param machinepass: Machine password
671     """
672     setup_ldb(secretsdb, setup_path("secrets_dc.ldif"), { 
673             "MACHINEPASS_B64": b64encode(machinepass),
674             "DOMAIN": domain,
675             "REALM": realm,
676             "DNSDOMAIN": dnsdomain,
677             "DOMAINSID": str(domainsid),
678             "SECRETS_KEYTAB": keytab_path,
679             "NETBIOSNAME": netbiosname,
680             "SAM_LDB": samdb_url,
681             "DNS_KEYTAB": dns_keytab_path,
682             "DNSPASS_B64": b64encode(dnspass),
683             })
684
685
686 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
687     """Setup the secrets database.
688
689     :param path: Path to the secrets database.
690     :param setup_path: Get the path to a setup file.
691     :param session_info: Session info.
692     :param credentials: Credentials
693     :param lp: Loadparm context
694     :return: LDB handle for the created secrets database
695     """
696     if os.path.exists(path):
697         os.unlink(path)
698     secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
699                       lp=lp)
700     secrets_ldb.erase()
701     secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
702     secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
703                       lp=lp)
704     secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
705
706     if credentials is not None and credentials.authentication_requested():
707         if credentials.get_bind_dn() is not None:
708             setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
709                     "LDAPMANAGERDN": credentials.get_bind_dn(),
710                     "LDAPMANAGERPASS_B64": b64encode(credentials.get_password())
711                     })
712         else:
713             setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
714                     "LDAPADMINUSER": credentials.get_username(),
715                     "LDAPADMINREALM": credentials.get_realm(),
716                     "LDAPADMINPASS_B64": b64encode(credentials.get_password())
717                     })
718
719     return secrets_ldb
720
721 def setup_registry(path, setup_path, session_info, lp):
722     """Setup the registry.
723     
724     :param path: Path to the registry database
725     :param setup_path: Function that returns the path to a setup.
726     :param session_info: Session information
727     :param credentials: Credentials
728     :param lp: Loadparm context
729     """
730     reg = registry.Registry()
731     hive = registry.open_ldb(path, session_info=session_info, 
732                          lp_ctx=lp)
733     reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
734     provision_reg = setup_path("provision.reg")
735     assert os.path.exists(provision_reg)
736     reg.diff_apply(provision_reg)
737
738
739 def setup_idmapdb(path, setup_path, session_info, lp):
740     """Setup the idmap database.
741
742     :param path: path to the idmap database
743     :param setup_path: Function that returns a path to a setup file
744     :param session_info: Session information
745     :param credentials: Credentials
746     :param lp: Loadparm context
747     """
748     if os.path.exists(path):
749         os.unlink(path)
750
751     idmap_ldb = IDmapDB(path, session_info=session_info,
752                         lp=lp)
753
754     idmap_ldb.erase()
755     idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
756     return idmap_ldb
757
758
759 def setup_samdb_rootdse(samdb, setup_path, names):
760     """Setup the SamDB rootdse.
761
762     :param samdb: Sam Database handle
763     :param setup_path: Obtain setup path
764     """
765     setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
766         "SCHEMADN": names.schemadn, 
767         "NETBIOSNAME": names.netbiosname,
768         "DNSDOMAIN": names.dnsdomain,
769         "REALM": names.realm,
770         "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
771         "DOMAINDN": names.domaindn,
772         "ROOTDN": names.rootdn,
773         "CONFIGDN": names.configdn,
774         "SERVERDN": names.serverdn,
775         })
776         
777
778 def setup_self_join(samdb, names,
779                     machinepass, dnspass, 
780                     domainsid, invocationid, setup_path,
781                     policyguid, policyguid_dc, domainControllerFunctionality):
782     """Join a host to its own domain."""
783     assert isinstance(invocationid, str)
784     setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), { 
785               "CONFIGDN": names.configdn, 
786               "SCHEMADN": names.schemadn,
787               "DOMAINDN": names.domaindn,
788               "SERVERDN": names.serverdn,
789               "INVOCATIONID": invocationid,
790               "NETBIOSNAME": names.netbiosname,
791               "DEFAULTSITE": names.sitename,
792               "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
793               "MACHINEPASS_B64": b64encode(machinepass),
794               "DNSPASS_B64": b64encode(dnspass),
795               "REALM": names.realm,
796               "DOMAIN": names.domain,
797               "DNSDOMAIN": names.dnsdomain,
798               "SAMBA_VERSION_STRING": version,
799               "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
800
801     setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), { 
802               "POLICYGUID": policyguid,
803               "POLICYGUID_DC": policyguid_dc,
804               "DNSDOMAIN": names.dnsdomain,
805               "DOMAINSID": str(domainsid),
806               "DOMAINDN": names.domaindn})
807     
808     # add the NTDSGUID based SPNs
809     ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
810     names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
811                                      expression="", scope=SCOPE_BASE)
812     assert isinstance(names.ntdsguid, str)
813
814     # Setup fSMORoleOwner entries to point at the newly created DC entry
815     setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
816               "DOMAIN": names.domain,
817               "DNSDOMAIN": names.dnsdomain,
818               "DOMAINDN": names.domaindn,
819               "CONFIGDN": names.configdn,
820               "SCHEMADN": names.schemadn, 
821               "DEFAULTSITE": names.sitename,
822               "SERVERDN": names.serverdn,
823               "NETBIOSNAME": names.netbiosname,
824               "NTDSGUID": names.ntdsguid
825               })
826
827
828 def setup_samdb(path, setup_path, session_info, credentials, lp, 
829                 names, message, 
830                 domainsid, domainguid, policyguid, policyguid_dc,
831                 fill, adminpass, krbtgtpass, 
832                 machinepass, invocationid, dnspass,
833                 serverrole, schema=None, ldap_backend=None):
834     """Setup a complete SAM Database.
835     
836     :note: This will wipe the main SAM database file!
837     """
838
839     domainFunctionality = DS_DOMAIN_FUNCTION_2000
840     forestFunctionality = DS_DOMAIN_FUNCTION_2000
841     domainControllerFunctionality = DS_DC_FUNCTION_2008_R2
842
843     # Also wipes the database
844     setup_samdb_partitions(path, setup_path, message=message, lp=lp,
845                            credentials=credentials, session_info=session_info,
846                            names=names, 
847                            ldap_backend=ldap_backend, serverrole=serverrole)
848
849     if (schema == None):
850         schema = Schema(setup_path, schemadn=names.schemadn, serverdn=names.serverdn,
851             sambadn=names.sambadn, ldap_backend_type=ldap_backend.ldap_backend_type)
852
853     # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
854     samdb = Ldb(session_info=session_info, 
855                 credentials=credentials, lp=lp)
856
857     message("Pre-loading the Samba 4 and AD schema")
858
859     # Load the schema from the one we computed earlier
860     samdb.set_schema_from_ldb(schema.ldb)
861
862     # And now we can connect to the DB - the schema won't be loaded from the DB
863     samdb.connect(path)
864
865     # Load @OPTIONS
866     samdb.load_ldif_file_add(setup_path("provision_options.ldif"))
867
868     if fill == FILL_DRS:
869         return samdb
870
871     samdb.transaction_start()
872     try:
873         message("Erasing data from partitions")
874         # Load the schema (again).  This time it will force a reindex,
875         # and will therefore make the erase_partitions() below
876         # computationally sane
877         samdb.set_schema_from_ldb(schema.ldb)
878         samdb.erase_partitions()
879     
880         # Set the domain functionality levels onto the database.
881         # Various module (the password_hash module in particular) need
882         # to know what level of AD we are emulating.
883
884         # These will be fixed into the database via the database
885         # modifictions below, but we need them set from the start.
886         samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
887         samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
888         samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
889
890         samdb.set_domain_sid(str(domainsid))
891         if serverrole == "domain controller":
892             samdb.set_invocation_id(invocationid)
893
894         message("Adding DomainDN: %s" % names.domaindn)
895         if serverrole == "domain controller":
896             domain_oc = "domainDNS"
897         else:
898             domain_oc = "samba4LocalDomain"
899
900 #impersonate domain admin
901         admin_session_info = admin_session(lp, str(domainsid))
902         samdb.set_session_info(admin_session_info)
903
904         setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
905                 "DOMAINDN": names.domaindn,
906                 "DOMAIN_OC": domain_oc
907                 })
908
909         message("Modifying DomainDN: " + names.domaindn + "")
910         if domainguid is not None:
911             domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
912         else:
913             domainguid_mod = ""
914
915         setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
916             "CREATTIME": str(int(time.time()) * 1e7), # seconds -> ticks
917             "DOMAINSID": str(domainsid),
918             "SCHEMADN": names.schemadn, 
919             "NETBIOSNAME": names.netbiosname,
920             "DEFAULTSITE": names.sitename,
921             "CONFIGDN": names.configdn,
922             "SERVERDN": names.serverdn,
923             "POLICYGUID": policyguid,
924             "DOMAINDN": names.domaindn,
925             "DOMAINGUID_MOD": domainguid_mod,
926             "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
927             "SAMBA_VERSION_STRING": version
928             })
929
930         message("Adding configuration container")
931         setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
932             "CONFIGDN": names.configdn, 
933             })
934         message("Modifying configuration container")
935         setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
936             "CONFIGDN": names.configdn, 
937             "SCHEMADN": names.schemadn,
938             })
939
940         # The LDIF here was created when the Schema object was constructed
941         message("Setting up sam.ldb schema")
942         samdb.add_ldif(schema.schema_dn_add)
943         samdb.modify_ldif(schema.schema_dn_modify)
944         samdb.write_prefixes_from_schema()
945         samdb.add_ldif(schema.schema_data)
946         setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"), 
947                        {"SCHEMADN": names.schemadn})
948
949         message("Setting up sam.ldb configuration data")
950         setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
951             "CONFIGDN": names.configdn,
952             "NETBIOSNAME": names.netbiosname,
953             "DEFAULTSITE": names.sitename,
954             "DNSDOMAIN": names.dnsdomain,
955             "DOMAIN": names.domain,
956             "SCHEMADN": names.schemadn,
957             "DOMAINDN": names.domaindn,
958             "SERVERDN": names.serverdn,
959             "FOREST_FUNCTIONALALITY": str(forestFunctionality)
960             })
961
962         message("Setting up display specifiers")
963         display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
964         display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
965         check_all_substituted(display_specifiers_ldif)
966         samdb.add_ldif(display_specifiers_ldif)
967
968         message("Adding users container")
969         setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
970                 "DOMAINDN": names.domaindn})
971         message("Modifying users container")
972         setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
973                 "DOMAINDN": names.domaindn})
974         message("Adding computers container")
975         setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
976                 "DOMAINDN": names.domaindn})
977         message("Modifying computers container")
978         setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
979                 "DOMAINDN": names.domaindn})
980         message("Setting up sam.ldb data")
981         setup_add_ldif(samdb, setup_path("provision.ldif"), {
982             "CREATTIME": str(int(time.time()) * 1e7), # seconds -> ticks
983             "DOMAINDN": names.domaindn,
984             "NETBIOSNAME": names.netbiosname,
985             "DEFAULTSITE": names.sitename,
986             "CONFIGDN": names.configdn,
987             "SERVERDN": names.serverdn,
988             "POLICYGUID_DC": policyguid_dc
989             })
990
991         if fill == FILL_FULL:
992             message("Setting up sam.ldb users and groups")
993             setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
994                 "DOMAINDN": names.domaindn,
995                 "DOMAINSID": str(domainsid),
996                 "CONFIGDN": names.configdn,
997                 "ADMINPASS_B64": b64encode(adminpass),
998                 "KRBTGTPASS_B64": b64encode(krbtgtpass),
999                 })
1000
1001             if serverrole == "domain controller":
1002                 message("Setting up self join")
1003                 setup_self_join(samdb, names=names, invocationid=invocationid, 
1004                                 dnspass=dnspass,  
1005                                 machinepass=machinepass, 
1006                                 domainsid=domainsid, policyguid=policyguid,
1007                                 policyguid_dc=policyguid_dc,
1008                                 setup_path=setup_path,
1009                                 domainControllerFunctionality=domainControllerFunctionality)
1010
1011                 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
1012                 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1013                   attribute="objectGUID", expression="", scope=SCOPE_BASE)
1014                 assert isinstance(names.ntdsguid, str)
1015
1016     except:
1017         samdb.transaction_cancel()
1018         raise
1019
1020     samdb.transaction_commit()
1021     return samdb
1022
1023
1024 FILL_FULL = "FULL"
1025 FILL_NT4SYNC = "NT4SYNC"
1026 FILL_DRS = "DRS"
1027
1028
1029 def provision(setup_dir, message, session_info, 
1030               credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1031               realm=None, 
1032               rootdn=None, domaindn=None, schemadn=None, configdn=None, 
1033               serverdn=None,
1034               domain=None, hostname=None, hostip=None, hostip6=None, 
1035               domainsid=None, adminpass=None, ldapadminpass=None, 
1036               krbtgtpass=None, domainguid=None, 
1037               policyguid=None, policyguid_dc=None, invocationid=None,
1038               machinepass=None, 
1039               dnspass=None, root=None, nobody=None, users=None, 
1040               wheel=None, backup=None, aci=None, serverrole=None, 
1041               ldap_backend_extra_port=None, ldap_backend_type=None,
1042               sitename=None,
1043               ol_mmr_urls=None, ol_olc=None, 
1044               setup_ds_path=None, slapd_path=None, nosync=False,
1045               ldap_dryrun_mode=False):
1046     """Provision samba4
1047     
1048     :note: caution, this wipes all existing data!
1049     """
1050
1051     def setup_path(file):
1052         return os.path.join(setup_dir, file)
1053
1054     if domainsid is None:
1055       domainsid = security.random_sid()
1056     else:
1057       domainsid = security.dom_sid(domainsid)
1058
1059
1060     # create/adapt the group policy GUIDs
1061     if policyguid is None:
1062         policyguid = str(uuid.uuid4())
1063     policyguid = policyguid.upper()
1064     if policyguid_dc is None:
1065         policyguid_dc = str(uuid.uuid4())
1066     policyguid_dc = policyguid_dc.upper()
1067
1068     if adminpass is None:
1069         adminpass = glue.generate_random_str(12)
1070     if krbtgtpass is None:
1071         krbtgtpass = glue.generate_random_str(12)
1072     if machinepass is None:
1073         machinepass  = glue.generate_random_str(12)
1074     if dnspass is None:
1075         dnspass = glue.generate_random_str(12)
1076     if ldapadminpass is None:
1077         #Make a new, random password between Samba and it's LDAP server
1078         ldapadminpass=glue.generate_random_str(12)        
1079
1080
1081     root_uid = findnss_uid([root or "root"])
1082     nobody_uid = findnss_uid([nobody or "nobody"])
1083     users_gid = findnss_gid([users or "users"])
1084     if wheel is None:
1085         wheel_gid = findnss_gid(["wheel", "adm"])
1086     else:
1087         wheel_gid = findnss_gid([wheel])
1088
1089     if targetdir is not None:
1090         if (not os.path.exists(os.path.join(targetdir, "etc"))):
1091             os.makedirs(os.path.join(targetdir, "etc"))
1092         smbconf = os.path.join(targetdir, "etc", "smb.conf")
1093     elif smbconf is None:
1094         smbconf = param.default_path()
1095
1096     # only install a new smb.conf if there isn't one there already
1097     if not os.path.exists(smbconf):
1098         make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, 
1099                      targetdir)
1100
1101     lp = param.LoadParm()
1102     lp.load(smbconf)
1103
1104     names = guess_names(lp=lp, hostname=hostname, domain=domain, 
1105                         dnsdomain=realm, serverrole=serverrole, sitename=sitename,
1106                         rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1107                         serverdn=serverdn)
1108
1109     paths = provision_paths_from_lp(lp, names.dnsdomain)
1110
1111     if hostip is None:
1112         try:
1113             hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1114         except socket.gaierror, (socket.EAI_NODATA, msg):
1115             hostip = None
1116
1117     if hostip6 is None:
1118         try:
1119             hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1120         except socket.gaierror, (socket.EAI_NODATA, msg): 
1121             hostip6 = None
1122
1123     if serverrole is None:
1124         serverrole = lp.get("server role")
1125
1126     assert serverrole in ("domain controller", "member server", "standalone")
1127     if invocationid is None and serverrole == "domain controller":
1128         invocationid = str(uuid.uuid4())
1129
1130     if not os.path.exists(paths.private_dir):
1131         os.mkdir(paths.private_dir)
1132
1133     ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1134     
1135     schema = Schema(setup_path, schemadn=names.schemadn, serverdn=names.serverdn,
1136         sambadn=names.sambadn, ldap_backend_type=ldap_backend_type)
1137     
1138     secrets_credentials = credentials
1139     provision_backend = None
1140     if ldap_backend_type:
1141         # We only support an LDAP backend over ldapi://
1142
1143         provision_backend = ProvisionBackend(paths=paths, setup_path=setup_path,
1144                                              lp=lp, credentials=credentials, 
1145                                              names=names,
1146                                              message=message, hostname=hostname,
1147                                              root=root, schema=schema,
1148                                              ldap_backend_type=ldap_backend_type,
1149                                              ldapadminpass=ldapadminpass,
1150                                              ldap_backend_extra_port=ldap_backend_extra_port,
1151                                              ol_mmr_urls=ol_mmr_urls, 
1152                                              slapd_path=slapd_path,
1153                                              setup_ds_path=setup_ds_path,
1154                                              ldap_dryrun_mode=ldap_dryrun_mode)
1155
1156         # Now use the backend credentials to access the databases
1157         credentials = provision_backend.credentials
1158         secrets_credentials = provision_backend.adminCredentials
1159         ldapi_url = provision_backend.ldapi_uri
1160
1161     # only install a new shares config db if there is none
1162     if not os.path.exists(paths.shareconf):
1163         message("Setting up share.ldb")
1164         share_ldb = Ldb(paths.shareconf, session_info=session_info, 
1165                         credentials=credentials, lp=lp)
1166         share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1167
1168      
1169     message("Setting up secrets.ldb")
1170     secrets_ldb = setup_secretsdb(paths.secrets, setup_path, 
1171                                   session_info=session_info, 
1172                                   credentials=secrets_credentials, lp=lp)
1173
1174     message("Setting up the registry")
1175     setup_registry(paths.hklm, setup_path, session_info, 
1176                    lp=lp)
1177
1178     message("Setting up idmap db")
1179     idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1180                           lp=lp)
1181
1182     message("Setting up SAM db")
1183     samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info, 
1184                         credentials=credentials, lp=lp, names=names,
1185                         message=message, 
1186                         domainsid=domainsid, 
1187                         schema=schema, domainguid=domainguid,
1188                         policyguid=policyguid, policyguid_dc=policyguid_dc,
1189                         fill=samdb_fill, 
1190                         adminpass=adminpass, krbtgtpass=krbtgtpass,
1191                         invocationid=invocationid, 
1192                         machinepass=machinepass, dnspass=dnspass,
1193                         serverrole=serverrole, ldap_backend=provision_backend)
1194
1195     if serverrole == "domain controller":
1196         if paths.netlogon is None:
1197             message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1198             message("Please either remove %s or see the template at %s" % 
1199                     ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1200             assert(paths.netlogon is not None)
1201
1202         if paths.sysvol is None:
1203             message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1204             message("Please either remove %s or see the template at %s" % 
1205                     (paths.smbconf, setup_path("provision.smb.conf.dc")))
1206             assert(paths.sysvol is not None)            
1207             
1208         # Set up group policies (domain policy and domain controller policy)
1209
1210         policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1211                                    "{" + policyguid + "}")
1212         os.makedirs(policy_path, 0755)
1213         open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1214                                    "[General]\r\nVersion=65543")
1215         os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
1216         os.makedirs(os.path.join(policy_path, "USER"), 0755)
1217
1218         policy_path_dc = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1219                                    "{" + policyguid_dc + "}")
1220         os.makedirs(policy_path_dc, 0755)
1221         open(os.path.join(policy_path_dc, "GPT.INI"), 'w').write(
1222                                    "[General]\r\nVersion=2")
1223         os.makedirs(os.path.join(policy_path_dc, "MACHINE"), 0755)
1224         os.makedirs(os.path.join(policy_path_dc, "USER"), 0755)
1225
1226         if not os.path.isdir(paths.netlogon):
1227             os.makedirs(paths.netlogon, 0755)
1228
1229     if samdb_fill == FILL_FULL:
1230         setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1231                             root_uid=root_uid, nobody_uid=nobody_uid,
1232                             users_gid=users_gid, wheel_gid=wheel_gid)
1233
1234         message("Setting up sam.ldb rootDSE marking as synchronized")
1235         setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1236
1237         # Only make a zone file on the first DC, it should be replicated with DNS replication
1238         if serverrole == "domain controller":
1239             secrets_ldb = Ldb(paths.secrets, session_info=session_info, 
1240                               credentials=credentials, lp=lp)
1241             secretsdb_become_dc(secrets_ldb, setup_path, domain=domain,
1242                                 realm=names.realm,
1243                                 netbiosname=names.netbiosname,
1244                                 domainsid=domainsid, 
1245                                 keytab_path=paths.keytab, samdb_url=paths.samdb,
1246                                 dns_keytab_path=paths.dns_keytab,
1247                                 dnspass=dnspass, machinepass=machinepass,
1248                                 dnsdomain=names.dnsdomain)
1249
1250             domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1251             assert isinstance(domainguid, str)
1252
1253             create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1254                              domaindn=names.domaindn, hostip=hostip,
1255                              hostip6=hostip6, hostname=names.hostname,
1256                              dnspass=dnspass, realm=names.realm,
1257                              domainguid=domainguid, ntdsguid=names.ntdsguid)
1258
1259             create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1260                               dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1261
1262             create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1263                               dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1264                               keytab_name=paths.dns_keytab)
1265             message("See %s for an example configuration include file for BIND" % paths.namedconf)
1266             message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1267
1268             create_krb5_conf(paths.krb5conf, setup_path,
1269                              dnsdomain=names.dnsdomain, hostname=names.hostname,
1270                              realm=names.realm)
1271             message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1272
1273
1274     if provision_backend is not None: 
1275       if ldap_backend_type == "fedora-ds":
1276         ldapi_db = Ldb(provision_backend.ldapi_uri, lp=lp, credentials=credentials)
1277
1278         # delete default SASL mappings
1279         res = ldapi_db.search(expression="(!(cn=samba-admin mapping))", base="cn=mapping,cn=sasl,cn=config", scope=SCOPE_ONELEVEL, attrs=["dn"])
1280
1281         # configure in-directory access control on Fedora DS via the aci attribute (over a direct ldapi:// socket)
1282         for i in range (0, len(res)):
1283           dn = str(res[i]["dn"])
1284           ldapi_db.delete(dn)
1285
1286           aci = """(targetattr = "*") (version 3.0;acl "full access to all by samba-admin";allow (all)(userdn = "ldap:///CN=samba-admin,%s");)""" % names.sambadn
1287
1288           m = ldb.Message()
1289           m["aci"] = ldb.MessageElement([aci], ldb.FLAG_MOD_REPLACE, "aci")
1290         
1291           m.dn = ldb.Dn(1, names.domaindn)
1292           ldapi_db.modify(m)
1293
1294           m.dn = ldb.Dn(1, names.configdn)
1295           ldapi_db.modify(m)
1296
1297           m.dn = ldb.Dn(1, names.schemadn)
1298           ldapi_db.modify(m)
1299
1300       # if an LDAP backend is in use, terminate slapd after final provision and check its proper termination
1301       if provision_backend.slapd.poll() is None:
1302         #Kill the slapd
1303         if hasattr(provision_backend.slapd, "terminate"):
1304           provision_backend.slapd.terminate()
1305         else:
1306           # Older python versions don't have .terminate()
1307           import signal
1308           os.kill(provision_backend.slapd.pid, signal.SIGTERM)
1309             
1310         #and now wait for it to die
1311         provision_backend.slapd.communicate()
1312             
1313     # now display slapd_command_file.txt to show how slapd must be started next time
1314         message("Use later the following commandline to start slapd, then Samba:")
1315         slapd_command = "\'" + "\' \'".join(provision_backend.slapd_command) + "\'"
1316         message(slapd_command)
1317         message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh")
1318
1319         setup_file(setup_path("ldap_backend_startup.sh"), paths.ldapdir + "/ldap_backend_startup.sh", {
1320                 "SLAPD_COMMAND" : slapd_command})
1321
1322     
1323     create_phpldapadmin_config(paths.phpldapadminconfig, setup_path, 
1324                                ldapi_url)
1325
1326     message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1327
1328     message("Once the above files are installed, your Samba4 server will be ready to use")
1329     message("Server Role:           %s" % serverrole)
1330     message("Hostname:              %s" % names.hostname)
1331     message("NetBIOS Domain:        %s" % names.domain)
1332     message("DNS Domain:            %s" % names.dnsdomain)
1333     message("DOMAIN SID:            %s" % str(domainsid))
1334     if samdb_fill == FILL_FULL:
1335         message("Admin password:    %s" % adminpass)
1336     if provision_backend:
1337         if provision_backend.credentials.get_bind_dn() is not None:
1338             message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1339         else:
1340             message("LDAP Admin User:       %s" % provision_backend.credentials.get_username())
1341
1342         message("LDAP Admin Password:   %s" % provision_backend.credentials.get_password())
1343   
1344     result = ProvisionResult()
1345     result.domaindn = domaindn
1346     result.paths = paths
1347     result.lp = lp
1348     result.samdb = samdb
1349     return result
1350
1351
1352
1353 def provision_become_dc(setup_dir=None,
1354                         smbconf=None, targetdir=None, realm=None, 
1355                         rootdn=None, domaindn=None, schemadn=None,
1356                         configdn=None, serverdn=None,
1357                         domain=None, hostname=None, domainsid=None, 
1358                         adminpass=None, krbtgtpass=None, domainguid=None, 
1359                         policyguid=None, policyguid_dc=None, invocationid=None,
1360                         machinepass=None, 
1361                         dnspass=None, root=None, nobody=None, users=None, 
1362                         wheel=None, backup=None, serverrole=None, 
1363                         ldap_backend=None, ldap_backend_type=None,
1364                         sitename=None, debuglevel=1):
1365
1366     def message(text):
1367         """print a message if quiet is not set."""
1368         print text
1369
1370     glue.set_debug_level(debuglevel)
1371
1372     return provision(setup_dir, message, system_session(), None,
1373               smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1374               realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1375               configdn=configdn, serverdn=serverdn, domain=domain,
1376               hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1377               machinepass=machinepass, serverrole="domain controller",
1378               sitename=sitename)
1379
1380
1381 def setup_db_config(setup_path, dbdir):
1382     """Setup a Berkeley database.
1383     
1384     :param setup_path: Setup path function.
1385     :param dbdir: Database directory."""
1386     if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
1387         os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700)
1388         if not os.path.isdir(os.path.join(dbdir, "tmp")):
1389             os.makedirs(os.path.join(dbdir, "tmp"), 0700)
1390
1391     setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
1392                {"LDAPDBDIR": dbdir})
1393     
1394 class ProvisionBackend(object):
1395     def __init__(self, paths=None, setup_path=None, lp=None, credentials=None, 
1396                  names=None, message=None, 
1397                  hostname=None, root=None, 
1398                  schema=None, ldapadminpass=None,
1399                  ldap_backend_type=None, ldap_backend_extra_port=None,
1400                  ol_mmr_urls=None, 
1401                  setup_ds_path=None, slapd_path=None, 
1402                  nosync=False, ldap_dryrun_mode=False):
1403         """Provision an LDAP backend for samba4
1404         
1405         This works for OpenLDAP and Fedora DS
1406         """
1407
1408         self.ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.ldapdir, "ldapi"), safe="")
1409         
1410         if not os.path.isdir(paths.ldapdir):
1411             os.makedirs(paths.ldapdir, 0700)
1412             
1413         if ldap_backend_type == "existing":
1414             #Check to see that this 'existing' LDAP backend in fact exists
1415             ldapi_db = Ldb(self.ldapi_uri, credentials=credentials)
1416             search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1417                                                 expression="(objectClass=OpenLDAProotDSE)")
1418
1419             # If we have got here, then we must have a valid connection to the LDAP server, with valid credentials supplied
1420             # This caused them to be set into the long-term database later in the script.
1421             self.credentials = credentials
1422             self.ldap_backend_type = "openldap" #For now, assume existing backends at least emulate OpenLDAP
1423             return
1424     
1425         # we will shortly start slapd with ldapi for final provisioning. first check with ldapsearch -> rootDSE via self.ldapi_uri
1426         # if another instance of slapd is already running 
1427         try:
1428             ldapi_db = Ldb(self.ldapi_uri)
1429             search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1430                                                 expression="(objectClass=OpenLDAProotDSE)");
1431             try:
1432                 f = open(paths.slapdpid, "r")
1433                 p = f.read()
1434                 f.close()
1435                 message("Check for slapd Process with PID: " + str(p) + " and terminate it manually.")
1436             except:
1437                 pass
1438             
1439             raise ProvisioningError("Warning: Another slapd Instance seems already running on this host, listening to " + self.ldapi_uri + ". Please shut it down before you continue. ")
1440         
1441         except LdbError, e:
1442             pass
1443
1444         # Try to print helpful messages when the user has not specified the path to slapd
1445         if slapd_path is None:
1446             raise ProvisioningError("Warning: LDAP-Backend must be setup with path to slapd, e.g. --slapd-path=\"/usr/local/libexec/slapd\"!")
1447         if not os.path.exists(slapd_path):
1448             message (slapd_path)
1449             raise ProvisioningError("Warning: Given Path to slapd does not exist!")
1450
1451         schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
1452         try:
1453             os.unlink(schemadb_path)
1454         except OSError:
1455             pass
1456
1457
1458         # Put the LDIF of the schema into a database so we can search on
1459         # it to generate schema-dependent configurations in Fedora DS and
1460         # OpenLDAP
1461         os.path.join(paths.ldapdir, "schema-tmp.ldb")
1462         schema.ldb.connect(schemadb_path)
1463         schema.ldb.transaction_start()
1464     
1465         # These bits of LDIF are supplied when the Schema object is created
1466         schema.ldb.add_ldif(schema.schema_dn_add)
1467         schema.ldb.modify_ldif(schema.schema_dn_modify)
1468         schema.ldb.add_ldif(schema.schema_data)
1469         schema.ldb.transaction_commit()
1470
1471         self.credentials = Credentials()
1472         self.credentials.guess(lp)
1473         #Kerberos to an ldapi:// backend makes no sense
1474         self.credentials.set_kerberos_state(DONT_USE_KERBEROS)
1475
1476         self.adminCredentials = Credentials()
1477         self.adminCredentials.guess(lp)
1478         #Kerberos to an ldapi:// backend makes no sense
1479         self.adminCredentials.set_kerberos_state(DONT_USE_KERBEROS)
1480
1481         self.ldap_backend_type = ldap_backend_type
1482
1483         if ldap_backend_type == "fedora-ds":
1484             provision_fds_backend(self, paths=paths, setup_path=setup_path,
1485                                   names=names, message=message, 
1486                                   hostname=hostname,
1487                                   ldapadminpass=ldapadminpass, root=root, 
1488                                   schema=schema,
1489                                   ldap_backend_extra_port=ldap_backend_extra_port, 
1490                                   setup_ds_path=setup_ds_path,
1491                                   slapd_path=slapd_path,
1492                                   nosync=nosync,
1493                                   ldap_dryrun_mode=ldap_dryrun_mode)
1494             
1495         elif ldap_backend_type == "openldap":
1496             provision_openldap_backend(self, paths=paths, setup_path=setup_path,
1497                                        names=names, message=message, 
1498                                        hostname=hostname,
1499                                        ldapadminpass=ldapadminpass, root=root, 
1500                                        schema=schema,
1501                                        ldap_backend_extra_port=ldap_backend_extra_port, 
1502                                        ol_mmr_urls=ol_mmr_urls, 
1503                                        slapd_path=slapd_path,
1504                                        nosync=nosync,
1505                                        ldap_dryrun_mode=ldap_dryrun_mode)
1506         else:
1507             raise ProvisioningError("Unknown LDAP backend type selected")
1508
1509         self.credentials.set_password(ldapadminpass)
1510         self.adminCredentials.set_username("samba-admin")
1511         self.adminCredentials.set_password(ldapadminpass)
1512
1513         # Now start the slapd, so we can provision onto it.  We keep the
1514         # subprocess context around, to kill this off at the successful
1515         # end of the script
1516         self.slapd = subprocess.Popen(self.slapd_provision_command, close_fds=True, shell=False)
1517     
1518         while self.slapd.poll() is None:
1519             # Wait until the socket appears
1520             try:
1521                 ldapi_db = Ldb(self.ldapi_uri, lp=lp, credentials=self.credentials)
1522                 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1523                                                     expression="(objectClass=OpenLDAProotDSE)")
1524                 # If we have got here, then we must have a valid connection to the LDAP server!
1525                 return
1526             except LdbError, e:
1527                 time.sleep(1)
1528                 pass
1529         
1530         raise ProvisioningError("slapd died before we could make a connection to it")
1531
1532
1533 def provision_openldap_backend(result, paths=None, setup_path=None, names=None,
1534                                message=None, 
1535                                hostname=None, ldapadminpass=None, root=None, 
1536                                schema=None, 
1537                                ldap_backend_extra_port=None,
1538                                ol_mmr_urls=None, 
1539                                slapd_path=None, nosync=False,
1540                                ldap_dryrun_mode=False):
1541
1542     #Allow the test scripts to turn off fsync() for OpenLDAP as for TDB and LDB
1543     nosync_config = ""
1544     if nosync:
1545         nosync_config = "dbnosync"
1546         
1547     lnkattr = get_linked_attributes(names.schemadn,schema.ldb)
1548     refint_attributes = ""
1549     memberof_config = "# Generated from Samba4 schema\n"
1550     for att in  lnkattr.keys():
1551         if lnkattr[att] is not None:
1552             refint_attributes = refint_attributes + " " + att 
1553             
1554             memberof_config += read_and_sub_file(setup_path("memberof.conf"),
1555                                                  { "MEMBER_ATTR" : att ,
1556                                                    "MEMBEROF_ATTR" : lnkattr[att] })
1557             
1558     refint_config = read_and_sub_file(setup_path("refint.conf"),
1559                                       { "LINK_ATTRS" : refint_attributes})
1560     
1561     attrs = ["linkID", "lDAPDisplayName"]
1562     res = schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
1563     index_config = ""
1564     for i in range (0, len(res)):
1565         index_attr = res[i]["lDAPDisplayName"][0]
1566         if index_attr == "objectGUID":
1567             index_attr = "entryUUID"
1568             
1569         index_config += "index " + index_attr + " eq\n"
1570
1571 # generate serverids, ldap-urls and syncrepl-blocks for mmr hosts
1572     mmr_on_config = ""
1573     mmr_replicator_acl = ""
1574     mmr_serverids_config = ""
1575     mmr_syncrepl_schema_config = "" 
1576     mmr_syncrepl_config_config = "" 
1577     mmr_syncrepl_user_config = "" 
1578        
1579     
1580     if ol_mmr_urls is not None:
1581         # For now, make these equal
1582         mmr_pass = ldapadminpass
1583         
1584         url_list=filter(None,ol_mmr_urls.split(' ')) 
1585         if (len(url_list) == 1):
1586             url_list=filter(None,ol_mmr_urls.split(',')) 
1587                      
1588             
1589             mmr_on_config = "MirrorMode On"
1590             mmr_replicator_acl = "  by dn=cn=replicator,cn=samba read"
1591             serverid=0
1592             for url in url_list:
1593                 serverid=serverid+1
1594                 mmr_serverids_config += read_and_sub_file(setup_path("mmr_serverids.conf"),
1595                                                           { "SERVERID" : str(serverid),
1596                                                             "LDAPSERVER" : url })
1597                 rid=serverid*10
1598                 rid=rid+1
1599                 mmr_syncrepl_schema_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1600                                                                 {  "RID" : str(rid),
1601                                                                    "MMRDN": names.schemadn,
1602                                                                    "LDAPSERVER" : url,
1603                                                                    "MMR_PASSWORD": mmr_pass})
1604                 
1605                 rid=rid+1
1606                 mmr_syncrepl_config_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1607                                                                 {  "RID" : str(rid),
1608                                                                    "MMRDN": names.configdn,
1609                                                                    "LDAPSERVER" : url,
1610                                                                    "MMR_PASSWORD": mmr_pass})
1611                 
1612                 rid=rid+1
1613                 mmr_syncrepl_user_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1614                                                               {  "RID" : str(rid),
1615                                                                  "MMRDN": names.domaindn,
1616                                                                  "LDAPSERVER" : url,
1617                                                                  "MMR_PASSWORD": mmr_pass })
1618     # OpenLDAP cn=config initialisation
1619     olc_syncrepl_config = ""
1620     olc_mmr_config = "" 
1621     # if mmr = yes, generate cn=config-replication directives
1622     # and olc_seed.lif for the other mmr-servers
1623     if ol_mmr_urls is not None:
1624         serverid=0
1625         olc_serverids_config = ""
1626         olc_syncrepl_seed_config = ""
1627         olc_mmr_config += read_and_sub_file(setup_path("olc_mmr.conf"),{})
1628         rid=1000
1629         for url in url_list:
1630             serverid=serverid+1
1631             olc_serverids_config += read_and_sub_file(setup_path("olc_serverid.conf"),
1632                                                       { "SERVERID" : str(serverid),
1633                                                         "LDAPSERVER" : url })
1634             
1635             rid=rid+1
1636             olc_syncrepl_config += read_and_sub_file(setup_path("olc_syncrepl.conf"),
1637                                                      {  "RID" : str(rid),
1638                                                         "LDAPSERVER" : url,
1639                                                         "MMR_PASSWORD": mmr_pass})
1640             
1641             olc_syncrepl_seed_config += read_and_sub_file(setup_path("olc_syncrepl_seed.conf"),
1642                                                           {  "RID" : str(rid),
1643                                                              "LDAPSERVER" : url})
1644                 
1645         setup_file(setup_path("olc_seed.ldif"), paths.olcseedldif,
1646                    {"OLC_SERVER_ID_CONF": olc_serverids_config,
1647                     "OLC_PW": ldapadminpass,
1648                     "OLC_SYNCREPL_CONF": olc_syncrepl_seed_config})
1649     # end olc
1650                 
1651     setup_file(setup_path("slapd.conf"), paths.slapdconf,
1652                {"DNSDOMAIN": names.dnsdomain,
1653                 "LDAPDIR": paths.ldapdir,
1654                 "DOMAINDN": names.domaindn,
1655                 "CONFIGDN": names.configdn,
1656                 "SCHEMADN": names.schemadn,
1657                 "MEMBEROF_CONFIG": memberof_config,
1658                 "MIRRORMODE": mmr_on_config,
1659                 "REPLICATOR_ACL": mmr_replicator_acl,
1660                 "MMR_SERVERIDS_CONFIG": mmr_serverids_config,
1661                 "MMR_SYNCREPL_SCHEMA_CONFIG": mmr_syncrepl_schema_config,
1662                 "MMR_SYNCREPL_CONFIG_CONFIG": mmr_syncrepl_config_config,
1663                 "MMR_SYNCREPL_USER_CONFIG": mmr_syncrepl_user_config,
1664                 "OLC_SYNCREPL_CONFIG": olc_syncrepl_config,
1665                 "OLC_MMR_CONFIG": olc_mmr_config,
1666                 "REFINT_CONFIG": refint_config,
1667                 "INDEX_CONFIG": index_config,
1668                 "NOSYNC": nosync_config})
1669         
1670     setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "user"))
1671     setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "config"))
1672     setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "schema"))
1673     
1674     if not os.path.exists(os.path.join(paths.ldapdir, "db", "samba",  "cn=samba")):
1675         os.makedirs(os.path.join(paths.ldapdir, "db", "samba",  "cn=samba"), 0700)
1676         
1677     setup_file(setup_path("cn=samba.ldif"), 
1678                os.path.join(paths.ldapdir, "db", "samba",  "cn=samba.ldif"),
1679                { "UUID": str(uuid.uuid4()), 
1680                  "LDAPTIME": timestring(int(time.time()))} )
1681     setup_file(setup_path("cn=samba-admin.ldif"), 
1682                os.path.join(paths.ldapdir, "db", "samba",  "cn=samba", "cn=samba-admin.ldif"),
1683                {"LDAPADMINPASS_B64": b64encode(ldapadminpass),
1684                 "UUID": str(uuid.uuid4()), 
1685                 "LDAPTIME": timestring(int(time.time()))} )
1686     
1687     if ol_mmr_urls is not None:
1688         setup_file(setup_path("cn=replicator.ldif"),
1689                    os.path.join(paths.ldapdir, "db", "samba",  "cn=samba", "cn=replicator.ldif"),
1690                    {"MMR_PASSWORD_B64": b64encode(mmr_pass),
1691                     "UUID": str(uuid.uuid4()),
1692                     "LDAPTIME": timestring(int(time.time()))} )
1693         
1694
1695     mapping = "schema-map-openldap-2.3"
1696     backend_schema = "backend-schema.schema"
1697
1698     backend_schema_data = schema.ldb.convert_schema_to_openldap("openldap", open(setup_path(mapping), 'r').read())
1699     assert backend_schema_data is not None
1700     open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1701
1702     # now we generate the needed strings to start slapd automatically,
1703     # first ldapi_uri...
1704     if ldap_backend_extra_port is not None:
1705         # When we use MMR, we can't use 0.0.0.0 as it uses the name
1706         # specified there as part of it's clue as to it's own name,
1707         # and not to replicate to itself
1708         if ol_mmr_urls is None:
1709             server_port_string = "ldap://0.0.0.0:%d" % ldap_backend_extra_port
1710         else:
1711             server_port_string = "ldap://" + names.hostname + "." + names.dnsdomain +":%d" % ldap_backend_extra_port
1712     else:
1713         server_port_string = ""
1714
1715     # Prepare the 'result' information - the commands to return in particular
1716     result.slapd_provision_command = [slapd_path]
1717
1718     result.slapd_provision_command.append("-F" + paths.olcdir)
1719
1720     result.slapd_provision_command.append("-h")
1721
1722     # copy this command so we have two version, one with -d0 and only ldapi, and one with all the listen commands
1723     result.slapd_command = list(result.slapd_provision_command)
1724     
1725     result.slapd_provision_command.append(result.ldapi_uri)
1726     result.slapd_provision_command.append("-d0")
1727
1728     uris = result.ldapi_uri
1729     if server_port_string is not "":
1730         uris = uris + " " + server_port_string
1731
1732     result.slapd_command.append(uris)
1733
1734     # Set the username - done here because Fedora DS still uses the admin DN and simple bind
1735     result.credentials.set_username("samba-admin")
1736     
1737     # If we were just looking for crashes up to this point, it's a
1738     # good time to exit before we realise we don't have OpenLDAP on
1739     # this system
1740     if ldap_dryrun_mode:
1741         sys.exit(0)
1742
1743     # Finally, convert the configuration into cn=config style!
1744     if not os.path.isdir(paths.olcdir):
1745         os.makedirs(paths.olcdir, 0770)
1746
1747         retcode = subprocess.call([slapd_path, "-Ttest", "-f", paths.slapdconf, "-F", paths.olcdir], close_fds=True, shell=False)
1748
1749 #        We can't do this, as OpenLDAP is strange.  It gives an error
1750 #        output to the above, but does the conversion sucessfully...
1751 #
1752 #        if retcode != 0:
1753 #            raise ProvisioningError("conversion from slapd.conf to cn=config failed")
1754
1755         if not os.path.exists(os.path.join(paths.olcdir, "cn=config.ldif")):
1756             raise ProvisioningError("conversion from slapd.conf to cn=config failed")
1757
1758         # Don't confuse the admin by leaving the slapd.conf around
1759         os.remove(paths.slapdconf)        
1760           
1761
1762 def provision_fds_backend(result, paths=None, setup_path=None, names=None,
1763                           message=None, 
1764                           hostname=None, ldapadminpass=None, root=None, 
1765                           schema=None,
1766                           ldap_backend_extra_port=None,
1767                           setup_ds_path=None,
1768                           slapd_path=None,
1769                           nosync=False, 
1770                           ldap_dryrun_mode=False):
1771
1772     if ldap_backend_extra_port is not None:
1773         serverport = "ServerPort=%d" % ldap_backend_extra_port
1774     else:
1775         serverport = ""
1776         
1777     setup_file(setup_path("fedorads.inf"), paths.fedoradsinf, 
1778                {"ROOT": root,
1779                 "HOSTNAME": hostname,
1780                 "DNSDOMAIN": names.dnsdomain,
1781                 "LDAPDIR": paths.ldapdir,
1782                 "DOMAINDN": names.domaindn,
1783                 "LDAPMANAGERDN": names.ldapmanagerdn,
1784                 "LDAPMANAGERPASS": ldapadminpass, 
1785                 "SERVERPORT": serverport})
1786
1787     setup_file(setup_path("fedorads-partitions.ldif"), paths.fedoradspartitions, 
1788                {"CONFIGDN": names.configdn,
1789                 "SCHEMADN": names.schemadn,
1790                 "SAMBADN": names.sambadn,
1791                 })
1792
1793     setup_file(setup_path("fedorads-sasl.ldif"), paths.fedoradssasl, 
1794                {"SAMBADN": names.sambadn,
1795                 })
1796
1797     setup_file(setup_path("fedorads-samba.ldif"), paths.fedoradssamba,
1798                 {"SAMBADN": names.sambadn, 
1799                  "LDAPADMINPASS": ldapadminpass
1800                 })
1801
1802     mapping = "schema-map-fedora-ds-1.0"
1803     backend_schema = "99_ad.ldif"
1804     
1805     # Build a schema file in Fedora DS format
1806     backend_schema_data = schema.ldb.convert_schema_to_openldap("fedora-ds", open(setup_path(mapping), 'r').read())
1807     assert backend_schema_data is not None
1808     open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1809
1810     result.credentials.set_bind_dn(names.ldapmanagerdn)
1811
1812     # Destory the target directory, or else setup-ds.pl will complain
1813     fedora_ds_dir = os.path.join(paths.ldapdir, "slapd-samba4")
1814     shutil.rmtree(fedora_ds_dir, True)
1815
1816     result.slapd_provision_command = [slapd_path, "-D", fedora_ds_dir, "-i", paths.slapdpid];
1817     #In the 'provision' command line, stay in the foreground so we can easily kill it
1818     result.slapd_provision_command.append("-d0")
1819
1820     #the command for the final run is the normal script
1821     result.slapd_command = [os.path.join(paths.ldapdir, "slapd-samba4", "start-slapd")]
1822
1823     # If we were just looking for crashes up to this point, it's a
1824     # good time to exit before we realise we don't have Fedora DS on
1825     if ldap_dryrun_mode:
1826         sys.exit(0)
1827
1828     # Try to print helpful messages when the user has not specified the path to the setup-ds tool
1829     if setup_ds_path is None:
1830         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\"!")
1831     if not os.path.exists(setup_ds_path):
1832         message (setup_ds_path)
1833         raise ProvisioningError("Warning: Given Path to slapd does not exist!")
1834
1835     # Run the Fedora DS setup utility
1836     retcode = subprocess.call([setup_ds_path, "--silent", "--file", paths.fedoradsinf], close_fds=True, shell=False)
1837     if retcode != 0:
1838         raise ProvisioningError("setup-ds failed")
1839
1840     # Load samba-admin
1841     retcode = subprocess.call([
1842         os.path.join(paths.ldapdir, "slapd-samba4", "ldif2db"), "-s", names.sambadn, "-i", paths.fedoradssamba],
1843         close_fds=True, shell=False)
1844     if retcode != 0:
1845         raise("ldib2db failed")
1846
1847 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1848     """Create a PHP LDAP admin configuration file.
1849
1850     :param path: Path to write the configuration to.
1851     :param setup_path: Function to generate setup paths.
1852     """
1853     setup_file(setup_path("phpldapadmin-config.php"), path, 
1854             {"S4_LDAPI_URI": ldapi_uri})
1855
1856
1857 def create_zone_file(path, setup_path, dnsdomain, domaindn, 
1858                      hostip, hostip6, hostname, dnspass, realm, domainguid,
1859                      ntdsguid):
1860     """Write out a DNS zone file, from the info in the current database.
1861
1862     :param path: Path of the new zone file.
1863     :param setup_path: Setup path function.
1864     :param dnsdomain: DNS Domain name
1865     :param domaindn: DN of the Domain
1866     :param hostip: Local IPv4 IP
1867     :param hostip6: Local IPv6 IP
1868     :param hostname: Local hostname
1869     :param dnspass: Password for DNS
1870     :param realm: Realm name
1871     :param domainguid: GUID of the domain.
1872     :param ntdsguid: GUID of the hosts nTDSDSA record.
1873     """
1874     assert isinstance(domainguid, str)
1875
1876     if hostip6 is not None:
1877         hostip6_base_line = "            IN AAAA    " + hostip6
1878         hostip6_host_line = hostname + "        IN AAAA    " + hostip6
1879     else:
1880         hostip6_base_line = ""
1881         hostip6_host_line = ""
1882
1883     if hostip is not None:
1884         hostip_base_line = "            IN A    " + hostip
1885         hostip_host_line = hostname + "        IN A    " + hostip
1886     else:
1887         hostip_base_line = ""
1888         hostip_host_line = ""
1889
1890     setup_file(setup_path("provision.zone"), path, {
1891             "DNSPASS_B64": b64encode(dnspass),
1892             "HOSTNAME": hostname,
1893             "DNSDOMAIN": dnsdomain,
1894             "REALM": realm,
1895             "HOSTIP_BASE_LINE": hostip_base_line,
1896             "HOSTIP_HOST_LINE": hostip_host_line,
1897             "DOMAINGUID": domainguid,
1898             "DATESTRING": time.strftime("%Y%m%d%H"),
1899             "DEFAULTSITE": DEFAULTSITE,
1900             "NTDSGUID": ntdsguid,
1901             "HOSTIP6_BASE_LINE": hostip6_base_line,
1902             "HOSTIP6_HOST_LINE": hostip6_host_line,
1903         })
1904
1905
1906 def create_named_conf(path, setup_path, realm, dnsdomain,
1907                       private_dir):
1908     """Write out a file containing zone statements suitable for inclusion in a
1909     named.conf file (including GSS-TSIG configuration).
1910     
1911     :param path: Path of the new named.conf file.
1912     :param setup_path: Setup path function.
1913     :param realm: Realm name
1914     :param dnsdomain: DNS Domain name
1915     :param private_dir: Path to private directory
1916     :param keytab_name: File name of DNS keytab file
1917     """
1918
1919     setup_file(setup_path("named.conf"), path, {
1920             "DNSDOMAIN": dnsdomain,
1921             "REALM": realm,
1922             "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1923             "PRIVATE_DIR": private_dir
1924             })
1925
1926 def create_named_txt(path, setup_path, realm, dnsdomain,
1927                       private_dir, keytab_name):
1928     """Write out a file containing zone statements suitable for inclusion in a
1929     named.conf file (including GSS-TSIG configuration).
1930     
1931     :param path: Path of the new named.conf file.
1932     :param setup_path: Setup path function.
1933     :param realm: Realm name
1934     :param dnsdomain: DNS Domain name
1935     :param private_dir: Path to private directory
1936     :param keytab_name: File name of DNS keytab file
1937     """
1938
1939     setup_file(setup_path("named.txt"), path, {
1940             "DNSDOMAIN": dnsdomain,
1941             "REALM": realm,
1942             "DNS_KEYTAB": keytab_name,
1943             "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1944             "PRIVATE_DIR": private_dir
1945         })
1946
1947 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1948     """Write out a file containing zone statements suitable for inclusion in a
1949     named.conf file (including GSS-TSIG configuration).
1950     
1951     :param path: Path of the new named.conf file.
1952     :param setup_path: Setup path function.
1953     :param dnsdomain: DNS Domain name
1954     :param hostname: Local hostname
1955     :param realm: Realm name
1956     """
1957
1958     setup_file(setup_path("krb5.conf"), path, {
1959             "DNSDOMAIN": dnsdomain,
1960             "HOSTNAME": hostname,
1961             "REALM": realm,
1962         })
1963
1964