cbb747b64f2b515bcdd3384815ad04c69af4cd75
[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             "LDAPTIME": timestring(int(time.time())),
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             })
927
928         message("Adding configuration container")
929         setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
930             "CONFIGDN": names.configdn, 
931             })
932         message("Modifying configuration container")
933         setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
934             "CONFIGDN": names.configdn, 
935             "SCHEMADN": names.schemadn,
936             })
937
938         # The LDIF here was created when the Schema object was constructed
939         message("Setting up sam.ldb schema")
940         samdb.add_ldif(schema.schema_dn_add)
941         samdb.modify_ldif(schema.schema_dn_modify)
942         samdb.write_prefixes_from_schema()
943         samdb.add_ldif(schema.schema_data)
944         setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"), 
945                        {"SCHEMADN": names.schemadn})
946
947         message("Setting up sam.ldb configuration data")
948         setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
949             "CONFIGDN": names.configdn,
950             "NETBIOSNAME": names.netbiosname,
951             "DEFAULTSITE": names.sitename,
952             "DNSDOMAIN": names.dnsdomain,
953             "DOMAIN": names.domain,
954             "SCHEMADN": names.schemadn,
955             "DOMAINDN": names.domaindn,
956             "SERVERDN": names.serverdn,
957             "FOREST_FUNCTIONALALITY": str(forestFunctionality)
958             })
959
960         message("Setting up display specifiers")
961         display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
962         display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
963         check_all_substituted(display_specifiers_ldif)
964         samdb.add_ldif(display_specifiers_ldif)
965
966         message("Adding users container")
967         setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
968                 "DOMAINDN": names.domaindn})
969         message("Modifying users container")
970         setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
971                 "DOMAINDN": names.domaindn})
972         message("Adding computers container")
973         setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
974                 "DOMAINDN": names.domaindn})
975         message("Modifying computers container")
976         setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
977                 "DOMAINDN": names.domaindn})
978         message("Setting up sam.ldb data")
979         setup_add_ldif(samdb, setup_path("provision.ldif"), {
980             "DOMAINDN": names.domaindn,
981             "NETBIOSNAME": names.netbiosname,
982             "DEFAULTSITE": names.sitename,
983             "CONFIGDN": names.configdn,
984             "SERVERDN": names.serverdn,
985             "POLICYGUID_DC": policyguid_dc
986             })
987
988         if fill == FILL_FULL:
989             message("Setting up sam.ldb users and groups")
990             setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
991                 "DOMAINDN": names.domaindn,
992                 "DOMAINSID": str(domainsid),
993                 "CONFIGDN": names.configdn,
994                 "ADMINPASS_B64": b64encode(adminpass),
995                 "KRBTGTPASS_B64": b64encode(krbtgtpass),
996                 })
997
998             if serverrole == "domain controller":
999                 message("Setting up self join")
1000                 setup_self_join(samdb, names=names, invocationid=invocationid, 
1001                                 dnspass=dnspass,  
1002                                 machinepass=machinepass, 
1003                                 domainsid=domainsid, policyguid=policyguid,
1004                                 policyguid_dc=policyguid_dc,
1005                                 setup_path=setup_path,
1006                                 domainControllerFunctionality=domainControllerFunctionality)
1007                 # add the NTDSGUID based SPNs
1008                 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
1009                 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
1010                                                  expression="", scope=SCOPE_BASE)
1011                 assert isinstance(names.ntdsguid, str)
1012
1013     except:
1014         samdb.transaction_cancel()
1015         raise
1016
1017     samdb.transaction_commit()
1018     return samdb
1019
1020
1021 FILL_FULL = "FULL"
1022 FILL_NT4SYNC = "NT4SYNC"
1023 FILL_DRS = "DRS"
1024
1025
1026 def provision(setup_dir, message, session_info, 
1027               credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1028               realm=None, 
1029               rootdn=None, domaindn=None, schemadn=None, configdn=None, 
1030               serverdn=None,
1031               domain=None, hostname=None, hostip=None, hostip6=None, 
1032               domainsid=None, adminpass=None, ldapadminpass=None, 
1033               krbtgtpass=None, domainguid=None, 
1034               policyguid=None, policyguid_dc=None, invocationid=None,
1035               machinepass=None, 
1036               dnspass=None, root=None, nobody=None, users=None, 
1037               wheel=None, backup=None, aci=None, serverrole=None, 
1038               ldap_backend_extra_port=None, ldap_backend_type=None,
1039               sitename=None,
1040               ol_mmr_urls=None, ol_olc=None, 
1041               setup_ds_path=None, slapd_path=None, nosync=False,
1042               ldap_dryrun_mode=False):
1043     """Provision samba4
1044     
1045     :note: caution, this wipes all existing data!
1046     """
1047
1048     def setup_path(file):
1049         return os.path.join(setup_dir, file)
1050
1051     if domainsid is None:
1052         domainsid = security.random_sid()
1053
1054     # create/adapt the group policy GUIDs
1055     if policyguid is None:
1056         policyguid = str(uuid.uuid4())
1057     policyguid = policyguid.upper()
1058     if policyguid_dc is None:
1059         policyguid_dc = str(uuid.uuid4())
1060     policyguid_dc = policyguid_dc.upper()
1061
1062     if adminpass is None:
1063         adminpass = glue.generate_random_str(12)
1064     if krbtgtpass is None:
1065         krbtgtpass = glue.generate_random_str(12)
1066     if machinepass is None:
1067         machinepass  = glue.generate_random_str(12)
1068     if dnspass is None:
1069         dnspass = glue.generate_random_str(12)
1070     if ldapadminpass is None:
1071         #Make a new, random password between Samba and it's LDAP server
1072         ldapadminpass=glue.generate_random_str(12)        
1073
1074
1075     root_uid = findnss_uid([root or "root"])
1076     nobody_uid = findnss_uid([nobody or "nobody"])
1077     users_gid = findnss_gid([users or "users"])
1078     if wheel is None:
1079         wheel_gid = findnss_gid(["wheel", "adm"])
1080     else:
1081         wheel_gid = findnss_gid([wheel])
1082
1083     if targetdir is not None:
1084         if (not os.path.exists(os.path.join(targetdir, "etc"))):
1085             os.makedirs(os.path.join(targetdir, "etc"))
1086         smbconf = os.path.join(targetdir, "etc", "smb.conf")
1087     elif smbconf is None:
1088         smbconf = param.default_path()
1089
1090     # only install a new smb.conf if there isn't one there already
1091     if not os.path.exists(smbconf):
1092         make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, 
1093                      targetdir)
1094
1095     lp = param.LoadParm()
1096     lp.load(smbconf)
1097
1098     names = guess_names(lp=lp, hostname=hostname, domain=domain, 
1099                         dnsdomain=realm, serverrole=serverrole, sitename=sitename,
1100                         rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1101                         serverdn=serverdn)
1102
1103     paths = provision_paths_from_lp(lp, names.dnsdomain)
1104
1105     if hostip is None:
1106         try:
1107             hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1108         except socket.gaierror, (socket.EAI_NODATA, msg):
1109             hostip = None
1110
1111     if hostip6 is None:
1112         try:
1113             hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1114         except socket.gaierror, (socket.EAI_NODATA, msg): 
1115             hostip6 = None
1116
1117     if serverrole is None:
1118         serverrole = lp.get("server role")
1119
1120     assert serverrole in ("domain controller", "member server", "standalone")
1121     if invocationid is None and serverrole == "domain controller":
1122         invocationid = str(uuid.uuid4())
1123
1124     if not os.path.exists(paths.private_dir):
1125         os.mkdir(paths.private_dir)
1126
1127     ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1128     
1129     schema = Schema(setup_path, schemadn=names.schemadn, serverdn=names.serverdn,
1130         sambadn=names.sambadn, ldap_backend_type=ldap_backend_type)
1131     
1132     secrets_credentials = credentials
1133     provision_backend = None
1134     if ldap_backend_type:
1135         # We only support an LDAP backend over ldapi://
1136
1137         provision_backend = ProvisionBackend(paths=paths, setup_path=setup_path,
1138                                              lp=lp, credentials=credentials, 
1139                                              names=names,
1140                                              message=message, hostname=hostname,
1141                                              root=root, schema=schema,
1142                                              ldap_backend_type=ldap_backend_type,
1143                                              ldapadminpass=ldapadminpass,
1144                                              ldap_backend_extra_port=ldap_backend_extra_port,
1145                                              ol_mmr_urls=ol_mmr_urls, 
1146                                              slapd_path=slapd_path,
1147                                              setup_ds_path=setup_ds_path,
1148                                              ldap_dryrun_mode=ldap_dryrun_mode)
1149
1150         # Now use the backend credentials to access the databases
1151         credentials = provision_backend.credentials
1152         secrets_credentials = provision_backend.adminCredentials
1153         ldapi_url = provision_backend.ldapi_uri
1154
1155     # only install a new shares config db if there is none
1156     if not os.path.exists(paths.shareconf):
1157         message("Setting up share.ldb")
1158         share_ldb = Ldb(paths.shareconf, session_info=session_info, 
1159                         credentials=credentials, lp=lp)
1160         share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1161
1162      
1163     message("Setting up secrets.ldb")
1164     secrets_ldb = setup_secretsdb(paths.secrets, setup_path, 
1165                                   session_info=session_info, 
1166                                   credentials=secrets_credentials, lp=lp)
1167
1168     message("Setting up the registry")
1169     setup_registry(paths.hklm, setup_path, session_info, 
1170                    lp=lp)
1171
1172     message("Setting up idmap db")
1173     idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1174                           lp=lp)
1175
1176     message("Setting up SAM db")
1177     samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info, 
1178                         credentials=credentials, lp=lp, names=names,
1179                         message=message, 
1180                         domainsid=domainsid, 
1181                         schema=schema, domainguid=domainguid,
1182                         policyguid=policyguid, policyguid_dc=policyguid_dc,
1183                         fill=samdb_fill, 
1184                         adminpass=adminpass, krbtgtpass=krbtgtpass,
1185                         invocationid=invocationid, 
1186                         machinepass=machinepass, dnspass=dnspass,
1187                         serverrole=serverrole, ldap_backend=provision_backend)
1188
1189     if serverrole == "domain controller":
1190         if paths.netlogon is None:
1191             message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1192             message("Please either remove %s or see the template at %s" % 
1193                     ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1194             assert(paths.netlogon is not None)
1195
1196         if paths.sysvol is None:
1197             message("Existing smb.conf does not have a [sysvol] 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.sysvol is not None)            
1201             
1202         # Set up group policies (domain policy and domain controller policy)
1203
1204         policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1205                                    "{" + policyguid + "}")
1206         os.makedirs(policy_path, 0755)
1207         open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1208                                    "[General]\r\nVersion=65544")
1209         os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
1210         os.makedirs(os.path.join(policy_path, "USER"), 0755)
1211
1212         policy_path_dc = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1213                                    "{" + policyguid_dc + "}")
1214         os.makedirs(policy_path_dc, 0755)
1215         open(os.path.join(policy_path_dc, "GPT.INI"), 'w').write(
1216                                    "[General]\r\nVersion=2")
1217         os.makedirs(os.path.join(policy_path_dc, "MACHINE"), 0755)
1218         os.makedirs(os.path.join(policy_path_dc, "USER"), 0755)
1219
1220         if not os.path.isdir(paths.netlogon):
1221             os.makedirs(paths.netlogon, 0755)
1222
1223     if samdb_fill == FILL_FULL:
1224         setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1225                             root_uid=root_uid, nobody_uid=nobody_uid,
1226                             users_gid=users_gid, wheel_gid=wheel_gid)
1227
1228         message("Setting up sam.ldb rootDSE marking as synchronized")
1229         setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1230
1231         # Only make a zone file on the first DC, it should be replicated with DNS replication
1232         if serverrole == "domain controller":
1233             secrets_ldb = Ldb(paths.secrets, session_info=session_info, 
1234                               credentials=credentials, lp=lp)
1235             secretsdb_become_dc(secrets_ldb, setup_path, domain=domain,
1236                                 realm=names.realm,
1237                                 netbiosname=names.netbiosname,
1238                                 domainsid=domainsid, 
1239                                 keytab_path=paths.keytab, samdb_url=paths.samdb,
1240                                 dns_keytab_path=paths.dns_keytab,
1241                                 dnspass=dnspass, machinepass=machinepass,
1242                                 dnsdomain=names.dnsdomain)
1243
1244             domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1245             assert isinstance(domainguid, str)
1246
1247             create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1248                              domaindn=names.domaindn, hostip=hostip,
1249                              hostip6=hostip6, hostname=names.hostname,
1250                              dnspass=dnspass, realm=names.realm,
1251                              domainguid=domainguid, ntdsguid=names.ntdsguid)
1252
1253             create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1254                               dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1255
1256             create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1257                               dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1258                               keytab_name=paths.dns_keytab)
1259             message("See %s for an example configuration include file for BIND" % paths.namedconf)
1260             message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1261
1262             create_krb5_conf(paths.krb5conf, setup_path,
1263                              dnsdomain=names.dnsdomain, hostname=names.hostname,
1264                              realm=names.realm)
1265             message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1266
1267
1268     if provision_backend is not None: 
1269       if ldap_backend_type == "fedora-ds":
1270         ldapi_db = Ldb(provision_backend.ldapi_uri, lp=lp, credentials=credentials)
1271
1272         # delete default SASL mappings
1273         res = ldapi_db.search(expression="(!(cn=samba-admin mapping))", base="cn=mapping,cn=sasl,cn=config", scope=SCOPE_ONELEVEL, attrs=["dn"])
1274
1275         # configure in-directory access control on Fedora DS via the aci attribute (over a direct ldapi:// socket)
1276         for i in range (0, len(res)):
1277           dn = str(res[i]["dn"])
1278           ldapi_db.delete(dn)
1279
1280           aci = """(targetattr = "*") (version 3.0;acl "full access to all by samba-admin";allow (all)(userdn = "ldap:///CN=samba-admin,%s");)""" % names.sambadn
1281
1282           m = ldb.Message()
1283           m["aci"] = ldb.MessageElement([aci], ldb.FLAG_MOD_REPLACE, "aci")
1284         
1285           m.dn = ldb.Dn(1, names.domaindn)
1286           ldapi_db.modify(m)
1287
1288           m.dn = ldb.Dn(1, names.configdn)
1289           ldapi_db.modify(m)
1290
1291           m.dn = ldb.Dn(1, names.schemadn)
1292           ldapi_db.modify(m)
1293
1294       # if an LDAP backend is in use, terminate slapd after final provision and check its proper termination
1295       if provision_backend.slapd.poll() is None:
1296         #Kill the slapd
1297         if hasattr(provision_backend.slapd, "terminate"):
1298           provision_backend.slapd.terminate()
1299         else:
1300           # Older python versions don't have .terminate()
1301           import signal
1302           os.kill(provision_backend.slapd.pid, signal.SIGTERM)
1303             
1304         #and now wait for it to die
1305         provision_backend.slapd.communicate()
1306             
1307     # now display slapd_command_file.txt to show how slapd must be started next time
1308         message("Use later the following commandline to start slapd, then Samba:")
1309         slapd_command = "\'" + "\' \'".join(provision_backend.slapd_command) + "\'"
1310         message(slapd_command)
1311         message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh")
1312
1313         setup_file(setup_path("ldap_backend_startup.sh"), paths.ldapdir + "/ldap_backend_startup.sh", {
1314                 "SLAPD_COMMAND" : slapd_command})
1315
1316     
1317     create_phpldapadmin_config(paths.phpldapadminconfig, setup_path, 
1318                                ldapi_url)
1319
1320     message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1321
1322     message("Once the above files are installed, your Samba4 server will be ready to use")
1323     message("Server Role:           %s" % serverrole)
1324     message("Hostname:              %s" % names.hostname)
1325     message("NetBIOS Domain:        %s" % names.domain)
1326     message("DNS Domain:            %s" % names.dnsdomain)
1327     message("DOMAIN SID:            %s" % str(domainsid))
1328     if samdb_fill == FILL_FULL:
1329         message("Admin password:    %s" % adminpass)
1330     if provision_backend:
1331         if provision_backend.credentials.get_bind_dn() is not None:
1332             message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1333         else:
1334             message("LDAP Admin User:       %s" % provision_backend.credentials.get_username())
1335
1336         message("LDAP Admin Password:   %s" % provision_backend.credentials.get_password())
1337   
1338     result = ProvisionResult()
1339     result.domaindn = domaindn
1340     result.paths = paths
1341     result.lp = lp
1342     result.samdb = samdb
1343     return result
1344
1345
1346
1347 def provision_become_dc(setup_dir=None,
1348                         smbconf=None, targetdir=None, realm=None, 
1349                         rootdn=None, domaindn=None, schemadn=None,
1350                         configdn=None, serverdn=None,
1351                         domain=None, hostname=None, domainsid=None, 
1352                         adminpass=None, krbtgtpass=None, domainguid=None, 
1353                         policyguid=None, policyguid_dc=None, invocationid=None,
1354                         machinepass=None, 
1355                         dnspass=None, root=None, nobody=None, users=None, 
1356                         wheel=None, backup=None, serverrole=None, 
1357                         ldap_backend=None, ldap_backend_type=None,
1358                         sitename=None, debuglevel=1):
1359
1360     def message(text):
1361         """print a message if quiet is not set."""
1362         print text
1363
1364     glue.set_debug_level(debuglevel)
1365
1366     return provision(setup_dir, message, system_session(), None,
1367               smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1368               realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1369               configdn=configdn, serverdn=serverdn, domain=domain,
1370               hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1371               machinepass=machinepass, serverrole="domain controller",
1372               sitename=sitename)
1373
1374
1375 def setup_db_config(setup_path, dbdir):
1376     """Setup a Berkeley database.
1377     
1378     :param setup_path: Setup path function.
1379     :param dbdir: Database directory."""
1380     if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
1381         os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700)
1382         if not os.path.isdir(os.path.join(dbdir, "tmp")):
1383             os.makedirs(os.path.join(dbdir, "tmp"), 0700)
1384
1385     setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
1386                {"LDAPDBDIR": dbdir})
1387     
1388 class ProvisionBackend(object):
1389     def __init__(self, paths=None, setup_path=None, lp=None, credentials=None, 
1390                  names=None, message=None, 
1391                  hostname=None, root=None, 
1392                  schema=None, ldapadminpass=None,
1393                  ldap_backend_type=None, ldap_backend_extra_port=None,
1394                  ol_mmr_urls=None, 
1395                  setup_ds_path=None, slapd_path=None, 
1396                  nosync=False, ldap_dryrun_mode=False):
1397         """Provision an LDAP backend for samba4
1398         
1399         This works for OpenLDAP and Fedora DS
1400         """
1401
1402         self.ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.ldapdir, "ldapi"), safe="")
1403         
1404         if not os.path.isdir(paths.ldapdir):
1405             os.makedirs(paths.ldapdir, 0700)
1406             
1407         if ldap_backend_type == "existing":
1408             #Check to see that this 'existing' LDAP backend in fact exists
1409             ldapi_db = Ldb(self.ldapi_uri, credentials=credentials)
1410             search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1411                                                 expression="(objectClass=OpenLDAProotDSE)")
1412
1413             # If we have got here, then we must have a valid connection to the LDAP server, with valid credentials supplied
1414             # This caused them to be set into the long-term database later in the script.
1415             self.credentials = credentials
1416             self.ldap_backend_type = "openldap" #For now, assume existing backends at least emulate OpenLDAP
1417             return
1418     
1419         # we will shortly start slapd with ldapi for final provisioning. first check with ldapsearch -> rootDSE via self.ldapi_uri
1420         # if another instance of slapd is already running 
1421         try:
1422             ldapi_db = Ldb(self.ldapi_uri)
1423             search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1424                                                 expression="(objectClass=OpenLDAProotDSE)");
1425             try:
1426                 f = open(paths.slapdpid, "r")
1427                 p = f.read()
1428                 f.close()
1429                 message("Check for slapd Process with PID: " + str(p) + " and terminate it manually.")
1430             except:
1431                 pass
1432             
1433             raise ProvisioningError("Warning: Another slapd Instance seems already running on this host, listening to " + self.ldapi_uri + ". Please shut it down before you continue. ")
1434         
1435         except LdbError, e:
1436             pass
1437
1438         # Try to print helpful messages when the user has not specified the path to slapd
1439         if slapd_path is None:
1440             raise ProvisioningError("Warning: LDAP-Backend must be setup with path to slapd, e.g. --slapd-path=\"/usr/local/libexec/slapd\"!")
1441         if not os.path.exists(slapd_path):
1442             message (slapd_path)
1443             raise ProvisioningError("Warning: Given Path to slapd does not exist!")
1444
1445         schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
1446         try:
1447             os.unlink(schemadb_path)
1448         except OSError:
1449             pass
1450
1451
1452         # Put the LDIF of the schema into a database so we can search on
1453         # it to generate schema-dependent configurations in Fedora DS and
1454         # OpenLDAP
1455         os.path.join(paths.ldapdir, "schema-tmp.ldb")
1456         schema.ldb.connect(schemadb_path)
1457         schema.ldb.transaction_start()
1458     
1459         # These bits of LDIF are supplied when the Schema object is created
1460         schema.ldb.add_ldif(schema.schema_dn_add)
1461         schema.ldb.modify_ldif(schema.schema_dn_modify)
1462         schema.ldb.add_ldif(schema.schema_data)
1463         schema.ldb.transaction_commit()
1464
1465         self.credentials = Credentials()
1466         self.credentials.guess(lp)
1467         #Kerberos to an ldapi:// backend makes no sense
1468         self.credentials.set_kerberos_state(DONT_USE_KERBEROS)
1469
1470         self.adminCredentials = Credentials()
1471         self.adminCredentials.guess(lp)
1472         #Kerberos to an ldapi:// backend makes no sense
1473         self.adminCredentials.set_kerberos_state(DONT_USE_KERBEROS)
1474
1475         self.ldap_backend_type = ldap_backend_type
1476
1477         if ldap_backend_type == "fedora-ds":
1478             provision_fds_backend(self, paths=paths, setup_path=setup_path,
1479                                   names=names, message=message, 
1480                                   hostname=hostname,
1481                                   ldapadminpass=ldapadminpass, root=root, 
1482                                   schema=schema,
1483                                   ldap_backend_extra_port=ldap_backend_extra_port, 
1484                                   setup_ds_path=setup_ds_path,
1485                                   slapd_path=slapd_path,
1486                                   nosync=nosync,
1487                                   ldap_dryrun_mode=ldap_dryrun_mode)
1488             
1489         elif ldap_backend_type == "openldap":
1490             provision_openldap_backend(self, paths=paths, setup_path=setup_path,
1491                                        names=names, message=message, 
1492                                        hostname=hostname,
1493                                        ldapadminpass=ldapadminpass, root=root, 
1494                                        schema=schema,
1495                                        ldap_backend_extra_port=ldap_backend_extra_port, 
1496                                        ol_mmr_urls=ol_mmr_urls, 
1497                                        slapd_path=slapd_path,
1498                                        nosync=nosync,
1499                                        ldap_dryrun_mode=ldap_dryrun_mode)
1500         else:
1501             raise ProvisioningError("Unknown LDAP backend type selected")
1502
1503         self.credentials.set_password(ldapadminpass)
1504         self.adminCredentials.set_username("samba-admin")
1505         self.adminCredentials.set_password(ldapadminpass)
1506
1507         # Now start the slapd, so we can provision onto it.  We keep the
1508         # subprocess context around, to kill this off at the successful
1509         # end of the script
1510         self.slapd = subprocess.Popen(self.slapd_provision_command, close_fds=True, shell=False)
1511     
1512         while self.slapd.poll() is None:
1513             # Wait until the socket appears
1514             try:
1515                 ldapi_db = Ldb(self.ldapi_uri, lp=lp, credentials=self.credentials)
1516                 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1517                                                     expression="(objectClass=OpenLDAProotDSE)")
1518                 # If we have got here, then we must have a valid connection to the LDAP server!
1519                 return
1520             except LdbError, e:
1521                 time.sleep(1)
1522                 pass
1523         
1524         raise ProvisioningError("slapd died before we could make a connection to it")
1525
1526
1527 def provision_openldap_backend(result, paths=None, setup_path=None, names=None,
1528                                message=None, 
1529                                hostname=None, ldapadminpass=None, root=None, 
1530                                schema=None, 
1531                                ldap_backend_extra_port=None,
1532                                ol_mmr_urls=None, 
1533                                slapd_path=None, nosync=False,
1534                                ldap_dryrun_mode=False):
1535
1536     #Allow the test scripts to turn off fsync() for OpenLDAP as for TDB and LDB
1537     nosync_config = ""
1538     if nosync:
1539         nosync_config = "dbnosync"
1540         
1541     lnkattr = get_linked_attributes(names.schemadn,schema.ldb)
1542     refint_attributes = ""
1543     memberof_config = "# Generated from Samba4 schema\n"
1544     for att in  lnkattr.keys():
1545         if lnkattr[att] is not None:
1546             refint_attributes = refint_attributes + " " + att 
1547             
1548             memberof_config += read_and_sub_file(setup_path("memberof.conf"),
1549                                                  { "MEMBER_ATTR" : att ,
1550                                                    "MEMBEROF_ATTR" : lnkattr[att] })
1551             
1552     refint_config = read_and_sub_file(setup_path("refint.conf"),
1553                                       { "LINK_ATTRS" : refint_attributes})
1554     
1555     attrs = ["linkID", "lDAPDisplayName"]
1556     res = schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
1557     index_config = ""
1558     for i in range (0, len(res)):
1559         index_attr = res[i]["lDAPDisplayName"][0]
1560         if index_attr == "objectGUID":
1561             index_attr = "entryUUID"
1562             
1563         index_config += "index " + index_attr + " eq\n"
1564
1565 # generate serverids, ldap-urls and syncrepl-blocks for mmr hosts
1566     mmr_on_config = ""
1567     mmr_replicator_acl = ""
1568     mmr_serverids_config = ""
1569     mmr_syncrepl_schema_config = "" 
1570     mmr_syncrepl_config_config = "" 
1571     mmr_syncrepl_user_config = "" 
1572        
1573     
1574     if ol_mmr_urls is not None:
1575         # For now, make these equal
1576         mmr_pass = ldapadminpass
1577         
1578         url_list=filter(None,ol_mmr_urls.split(' ')) 
1579         if (len(url_list) == 1):
1580             url_list=filter(None,ol_mmr_urls.split(',')) 
1581                      
1582             
1583             mmr_on_config = "MirrorMode On"
1584             mmr_replicator_acl = "  by dn=cn=replicator,cn=samba read"
1585             serverid=0
1586             for url in url_list:
1587                 serverid=serverid+1
1588                 mmr_serverids_config += read_and_sub_file(setup_path("mmr_serverids.conf"),
1589                                                           { "SERVERID" : str(serverid),
1590                                                             "LDAPSERVER" : url })
1591                 rid=serverid*10
1592                 rid=rid+1
1593                 mmr_syncrepl_schema_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1594                                                                 {  "RID" : str(rid),
1595                                                                    "MMRDN": names.schemadn,
1596                                                                    "LDAPSERVER" : url,
1597                                                                    "MMR_PASSWORD": mmr_pass})
1598                 
1599                 rid=rid+1
1600                 mmr_syncrepl_config_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1601                                                                 {  "RID" : str(rid),
1602                                                                    "MMRDN": names.configdn,
1603                                                                    "LDAPSERVER" : url,
1604                                                                    "MMR_PASSWORD": mmr_pass})
1605                 
1606                 rid=rid+1
1607                 mmr_syncrepl_user_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1608                                                               {  "RID" : str(rid),
1609                                                                  "MMRDN": names.domaindn,
1610                                                                  "LDAPSERVER" : url,
1611                                                                  "MMR_PASSWORD": mmr_pass })
1612     # OpenLDAP cn=config initialisation
1613     olc_syncrepl_config = ""
1614     olc_mmr_config = "" 
1615     # if mmr = yes, generate cn=config-replication directives
1616     # and olc_seed.lif for the other mmr-servers
1617     if ol_mmr_urls is not None:
1618         serverid=0
1619         olc_serverids_config = ""
1620         olc_syncrepl_seed_config = ""
1621         olc_mmr_config += read_and_sub_file(setup_path("olc_mmr.conf"),{})
1622         rid=1000
1623         for url in url_list:
1624             serverid=serverid+1
1625             olc_serverids_config += read_and_sub_file(setup_path("olc_serverid.conf"),
1626                                                       { "SERVERID" : str(serverid),
1627                                                         "LDAPSERVER" : url })
1628             
1629             rid=rid+1
1630             olc_syncrepl_config += read_and_sub_file(setup_path("olc_syncrepl.conf"),
1631                                                      {  "RID" : str(rid),
1632                                                         "LDAPSERVER" : url,
1633                                                         "MMR_PASSWORD": mmr_pass})
1634             
1635             olc_syncrepl_seed_config += read_and_sub_file(setup_path("olc_syncrepl_seed.conf"),
1636                                                           {  "RID" : str(rid),
1637                                                              "LDAPSERVER" : url})
1638                 
1639         setup_file(setup_path("olc_seed.ldif"), paths.olcseedldif,
1640                    {"OLC_SERVER_ID_CONF": olc_serverids_config,
1641                     "OLC_PW": ldapadminpass,
1642                     "OLC_SYNCREPL_CONF": olc_syncrepl_seed_config})
1643     # end olc
1644                 
1645     setup_file(setup_path("slapd.conf"), paths.slapdconf,
1646                {"DNSDOMAIN": names.dnsdomain,
1647                 "LDAPDIR": paths.ldapdir,
1648                 "DOMAINDN": names.domaindn,
1649                 "CONFIGDN": names.configdn,
1650                 "SCHEMADN": names.schemadn,
1651                 "MEMBEROF_CONFIG": memberof_config,
1652                 "MIRRORMODE": mmr_on_config,
1653                 "REPLICATOR_ACL": mmr_replicator_acl,
1654                 "MMR_SERVERIDS_CONFIG": mmr_serverids_config,
1655                 "MMR_SYNCREPL_SCHEMA_CONFIG": mmr_syncrepl_schema_config,
1656                 "MMR_SYNCREPL_CONFIG_CONFIG": mmr_syncrepl_config_config,
1657                 "MMR_SYNCREPL_USER_CONFIG": mmr_syncrepl_user_config,
1658                 "OLC_SYNCREPL_CONFIG": olc_syncrepl_config,
1659                 "OLC_MMR_CONFIG": olc_mmr_config,
1660                 "REFINT_CONFIG": refint_config,
1661                 "INDEX_CONFIG": index_config,
1662                 "NOSYNC": nosync_config})
1663         
1664     setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "user"))
1665     setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "config"))
1666     setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "schema"))
1667     
1668     if not os.path.exists(os.path.join(paths.ldapdir, "db", "samba",  "cn=samba")):
1669         os.makedirs(os.path.join(paths.ldapdir, "db", "samba",  "cn=samba"), 0700)
1670         
1671     setup_file(setup_path("cn=samba.ldif"), 
1672                os.path.join(paths.ldapdir, "db", "samba",  "cn=samba.ldif"),
1673                { "UUID": str(uuid.uuid4()), 
1674                  "LDAPTIME": timestring(int(time.time()))} )
1675     setup_file(setup_path("cn=samba-admin.ldif"), 
1676                os.path.join(paths.ldapdir, "db", "samba",  "cn=samba", "cn=samba-admin.ldif"),
1677                {"LDAPADMINPASS_B64": b64encode(ldapadminpass),
1678                 "UUID": str(uuid.uuid4()), 
1679                 "LDAPTIME": timestring(int(time.time()))} )
1680     
1681     if ol_mmr_urls is not None:
1682         setup_file(setup_path("cn=replicator.ldif"),
1683                    os.path.join(paths.ldapdir, "db", "samba",  "cn=samba", "cn=replicator.ldif"),
1684                    {"MMR_PASSWORD_B64": b64encode(mmr_pass),
1685                     "UUID": str(uuid.uuid4()),
1686                     "LDAPTIME": timestring(int(time.time()))} )
1687         
1688
1689     mapping = "schema-map-openldap-2.3"
1690     backend_schema = "backend-schema.schema"
1691
1692     backend_schema_data = schema.ldb.convert_schema_to_openldap("openldap", open(setup_path(mapping), 'r').read())
1693     assert backend_schema_data is not None
1694     open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1695
1696     # now we generate the needed strings to start slapd automatically,
1697     # first ldapi_uri...
1698     if ldap_backend_extra_port is not None:
1699         # When we use MMR, we can't use 0.0.0.0 as it uses the name
1700         # specified there as part of it's clue as to it's own name,
1701         # and not to replicate to itself
1702         if ol_mmr_urls is None:
1703             server_port_string = "ldap://0.0.0.0:%d" % ldap_backend_extra_port
1704         else:
1705             server_port_string = "ldap://" + names.hostname + "." + names.dnsdomain +":%d" % ldap_backend_extra_port
1706     else:
1707         server_port_string = ""
1708
1709     # Prepare the 'result' information - the commands to return in particular
1710     result.slapd_provision_command = [slapd_path]
1711
1712     result.slapd_provision_command.append("-F" + paths.olcdir)
1713
1714     result.slapd_provision_command.append("-h")
1715
1716     # copy this command so we have two version, one with -d0 and only ldapi, and one with all the listen commands
1717     result.slapd_command = list(result.slapd_provision_command)
1718     
1719     result.slapd_provision_command.append(result.ldapi_uri)
1720     result.slapd_provision_command.append("-d0")
1721
1722     uris = result.ldapi_uri
1723     if server_port_string is not "":
1724         uris = uris + " " + server_port_string
1725
1726     result.slapd_command.append(uris)
1727
1728     # Set the username - done here because Fedora DS still uses the admin DN and simple bind
1729     result.credentials.set_username("samba-admin")
1730     
1731     # If we were just looking for crashes up to this point, it's a
1732     # good time to exit before we realise we don't have OpenLDAP on
1733     # this system
1734     if ldap_dryrun_mode:
1735         sys.exit(0)
1736
1737     # Finally, convert the configuration into cn=config style!
1738     if not os.path.isdir(paths.olcdir):
1739         os.makedirs(paths.olcdir, 0770)
1740
1741         retcode = subprocess.call([slapd_path, "-Ttest", "-f", paths.slapdconf, "-F", paths.olcdir], close_fds=True, shell=False)
1742
1743 #        We can't do this, as OpenLDAP is strange.  It gives an error
1744 #        output to the above, but does the conversion sucessfully...
1745 #
1746 #        if retcode != 0:
1747 #            raise ProvisioningError("conversion from slapd.conf to cn=config failed")
1748
1749         if not os.path.exists(os.path.join(paths.olcdir, "cn=config.ldif")):
1750             raise ProvisioningError("conversion from slapd.conf to cn=config failed")
1751
1752         # Don't confuse the admin by leaving the slapd.conf around
1753         os.remove(paths.slapdconf)        
1754           
1755
1756 def provision_fds_backend(result, paths=None, setup_path=None, names=None,
1757                           message=None, 
1758                           hostname=None, ldapadminpass=None, root=None, 
1759                           schema=None,
1760                           ldap_backend_extra_port=None,
1761                           setup_ds_path=None,
1762                           slapd_path=None,
1763                           nosync=False, 
1764                           ldap_dryrun_mode=False):
1765
1766     if ldap_backend_extra_port is not None:
1767         serverport = "ServerPort=%d" % ldap_backend_extra_port
1768     else:
1769         serverport = ""
1770         
1771     setup_file(setup_path("fedorads.inf"), paths.fedoradsinf, 
1772                {"ROOT": root,
1773                 "HOSTNAME": hostname,
1774                 "DNSDOMAIN": names.dnsdomain,
1775                 "LDAPDIR": paths.ldapdir,
1776                 "DOMAINDN": names.domaindn,
1777                 "LDAPMANAGERDN": names.ldapmanagerdn,
1778                 "LDAPMANAGERPASS": ldapadminpass, 
1779                 "SERVERPORT": serverport})
1780
1781     setup_file(setup_path("fedorads-partitions.ldif"), paths.fedoradspartitions, 
1782                {"CONFIGDN": names.configdn,
1783                 "SCHEMADN": names.schemadn,
1784                 "SAMBADN": names.sambadn,
1785                 })
1786
1787     setup_file(setup_path("fedorads-sasl.ldif"), paths.fedoradssasl, 
1788                {"SAMBADN": names.sambadn,
1789                 })
1790
1791     setup_file(setup_path("fedorads-samba.ldif"), paths.fedoradssamba,
1792                 {"SAMBADN": names.sambadn, 
1793                  "LDAPADMINPASS": ldapadminpass
1794                 })
1795
1796     mapping = "schema-map-fedora-ds-1.0"
1797     backend_schema = "99_ad.ldif"
1798     
1799     # Build a schema file in Fedora DS format
1800     backend_schema_data = schema.ldb.convert_schema_to_openldap("fedora-ds", open(setup_path(mapping), 'r').read())
1801     assert backend_schema_data is not None
1802     open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1803
1804     result.credentials.set_bind_dn(names.ldapmanagerdn)
1805
1806     # Destory the target directory, or else setup-ds.pl will complain
1807     fedora_ds_dir = os.path.join(paths.ldapdir, "slapd-samba4")
1808     shutil.rmtree(fedora_ds_dir, True)
1809
1810     result.slapd_provision_command = [slapd_path, "-D", fedora_ds_dir, "-i", paths.slapdpid];
1811     #In the 'provision' command line, stay in the foreground so we can easily kill it
1812     result.slapd_provision_command.append("-d0")
1813
1814     #the command for the final run is the normal script
1815     result.slapd_command = [os.path.join(paths.ldapdir, "slapd-samba4", "start-slapd")]
1816
1817     # If we were just looking for crashes up to this point, it's a
1818     # good time to exit before we realise we don't have Fedora DS on
1819     if ldap_dryrun_mode:
1820         sys.exit(0)
1821
1822     # Try to print helpful messages when the user has not specified the path to the setup-ds tool
1823     if setup_ds_path is None:
1824         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\"!")
1825     if not os.path.exists(setup_ds_path):
1826         message (setup_ds_path)
1827         raise ProvisioningError("Warning: Given Path to slapd does not exist!")
1828
1829     # Run the Fedora DS setup utility
1830     retcode = subprocess.call([setup_ds_path, "--silent", "--file", paths.fedoradsinf], close_fds=True, shell=False)
1831     if retcode != 0:
1832         raise ProvisioningError("setup-ds failed")
1833
1834     # Load samba-admin
1835     retcode = subprocess.call([
1836         os.path.join(paths.ldapdir, "slapd-samba4", "ldif2db"), "-s", names.sambadn, "-i", paths.fedoradssamba],
1837         close_fds=True, shell=False)
1838     if retcode != 0:
1839         raise("ldib2db failed")
1840
1841 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1842     """Create a PHP LDAP admin configuration file.
1843
1844     :param path: Path to write the configuration to.
1845     :param setup_path: Function to generate setup paths.
1846     """
1847     setup_file(setup_path("phpldapadmin-config.php"), path, 
1848             {"S4_LDAPI_URI": ldapi_uri})
1849
1850
1851 def create_zone_file(path, setup_path, dnsdomain, domaindn, 
1852                      hostip, hostip6, hostname, dnspass, realm, domainguid,
1853                      ntdsguid):
1854     """Write out a DNS zone file, from the info in the current database.
1855
1856     :param path: Path of the new zone file.
1857     :param setup_path: Setup path function.
1858     :param dnsdomain: DNS Domain name
1859     :param domaindn: DN of the Domain
1860     :param hostip: Local IPv4 IP
1861     :param hostip6: Local IPv6 IP
1862     :param hostname: Local hostname
1863     :param dnspass: Password for DNS
1864     :param realm: Realm name
1865     :param domainguid: GUID of the domain.
1866     :param ntdsguid: GUID of the hosts nTDSDSA record.
1867     """
1868     assert isinstance(domainguid, str)
1869
1870     if hostip6 is not None:
1871         hostip6_base_line = "            IN AAAA    " + hostip6
1872         hostip6_host_line = hostname + "        IN AAAA    " + hostip6
1873     else:
1874         hostip6_base_line = ""
1875         hostip6_host_line = ""
1876
1877     if hostip is not None:
1878         hostip_base_line = "            IN A    " + hostip
1879         hostip_host_line = hostname + "        IN A    " + hostip
1880     else:
1881         hostip_base_line = ""
1882         hostip_host_line = ""
1883
1884     setup_file(setup_path("provision.zone"), path, {
1885             "DNSPASS_B64": b64encode(dnspass),
1886             "HOSTNAME": hostname,
1887             "DNSDOMAIN": dnsdomain,
1888             "REALM": realm,
1889             "HOSTIP_BASE_LINE": hostip_base_line,
1890             "HOSTIP_HOST_LINE": hostip_host_line,
1891             "DOMAINGUID": domainguid,
1892             "DATESTRING": time.strftime("%Y%m%d%H"),
1893             "DEFAULTSITE": DEFAULTSITE,
1894             "NTDSGUID": ntdsguid,
1895             "HOSTIP6_BASE_LINE": hostip6_base_line,
1896             "HOSTIP6_HOST_LINE": hostip6_host_line,
1897         })
1898
1899
1900 def create_named_conf(path, setup_path, realm, dnsdomain,
1901                       private_dir):
1902     """Write out a file containing zone statements suitable for inclusion in a
1903     named.conf file (including GSS-TSIG configuration).
1904     
1905     :param path: Path of the new named.conf file.
1906     :param setup_path: Setup path function.
1907     :param realm: Realm name
1908     :param dnsdomain: DNS Domain name
1909     :param private_dir: Path to private directory
1910     :param keytab_name: File name of DNS keytab file
1911     """
1912
1913     setup_file(setup_path("named.conf"), path, {
1914             "DNSDOMAIN": dnsdomain,
1915             "REALM": realm,
1916             "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1917             "PRIVATE_DIR": private_dir
1918             })
1919
1920 def create_named_txt(path, setup_path, realm, dnsdomain,
1921                       private_dir, keytab_name):
1922     """Write out a file containing zone statements suitable for inclusion in a
1923     named.conf file (including GSS-TSIG configuration).
1924     
1925     :param path: Path of the new named.conf file.
1926     :param setup_path: Setup path function.
1927     :param realm: Realm name
1928     :param dnsdomain: DNS Domain name
1929     :param private_dir: Path to private directory
1930     :param keytab_name: File name of DNS keytab file
1931     """
1932
1933     setup_file(setup_path("named.txt"), path, {
1934             "DNSDOMAIN": dnsdomain,
1935             "REALM": realm,
1936             "DNS_KEYTAB": keytab_name,
1937             "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1938             "PRIVATE_DIR": private_dir
1939         })
1940
1941 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1942     """Write out a file containing zone statements suitable for inclusion in a
1943     named.conf file (including GSS-TSIG configuration).
1944     
1945     :param path: Path of the new named.conf file.
1946     :param setup_path: Setup path function.
1947     :param dnsdomain: DNS Domain name
1948     :param hostname: Local hostname
1949     :param realm: Realm name
1950     """
1951
1952     setup_file(setup_path("krb5.conf"), path, {
1953             "DNSDOMAIN": dnsdomain,
1954             "HOSTNAME": hostname,
1955             "REALM": realm,
1956         })
1957
1958