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