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