s4:provision Remove LDB backend files in provision
[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
43 from auth import system_session, admin_session
44 from samba import version, Ldb, substitute_var, valid_netbios_name, setup_file
45 from samba import check_all_substituted, read_and_sub_file
46 from samba import DS_DOMAIN_FUNCTION_2003, DS_DOMAIN_FUNCTION_2008, DS_DC_FUNCTION_2008
47 from samba.samdb import SamDB
48 from samba.idmap import IDmapDB
49 from samba.dcerpc import security
50 from samba.ndr import ndr_pack
51 import urllib
52 from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError
53 from ms_display_specifiers import read_ms_ldif
54 from schema import Schema
55 from provisionbackend import ProvisionBackend
56 from signal import SIGTERM
57 from dcerpc.misc import SEC_CHAN_BDC, SEC_CHAN_WKSTA
58
59 __docformat__ = "restructuredText"
60
61 def find_setup_dir():
62     """Find the setup directory used by provision."""
63     dirname = os.path.dirname(__file__)
64     if "/site-packages/" in dirname:
65         prefix = "/".join(dirname[:dirname.index("/site-packages/")].split("/")[:-2])
66         for suffix in ["share/setup", "share/samba/setup", "setup"]:
67             ret = os.path.join(prefix, suffix)
68             if os.path.isdir(ret):
69                 return ret
70     # In source tree
71     ret = os.path.join(dirname, "../../../setup")
72     if os.path.isdir(ret):
73         return ret
74     raise Exception("Unable to find setup directory.")
75
76 def get_config_descriptor(domain_sid):
77     sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
78            "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
79            "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
80            "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
81            "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
82            "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
83            "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
84            "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
85            "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
86            "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
87            "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
88            "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
89            "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;S-1-5-21-3191434175-1265308384-3577286990-498)" \
90            "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
91            "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
92     sec = security.descriptor.from_sddl(sddl, domain_sid)
93     return b64encode(ndr_pack(sec))
94
95
96 DEFAULTSITE = "Default-First-Site-Name"
97
98 # Exception classes
99
100 class ProvisioningError(Exception):
101     """A generic provision error."""
102
103 class InvalidNetbiosName(Exception):
104     """A specified name was not a valid NetBIOS name."""
105     def __init__(self, name):
106         super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)
107
108
109 class ProvisionPaths(object):
110     def __init__(self):
111         self.shareconf = None
112         self.hklm = None
113         self.hkcu = None
114         self.hkcr = None
115         self.hku = None
116         self.hkpd = None
117         self.hkpt = None
118         self.samdb = None
119         self.idmapdb = None
120         self.secrets = None
121         self.keytab = None
122         self.dns_keytab = None
123         self.dns = None
124         self.winsdb = None
125         self.private_dir = None
126         self.ldapdir = None
127         self.slapdconf = None
128         self.modulesconf = None
129         self.memberofconf = None
130         self.fedoradsinf = None
131         self.fedoradspartitions = None
132         self.fedoradssasl = None
133         self.fedoradsdna = None
134         self.fedoradspam = None
135         self.fedoradsrefint = None
136         self.fedoradslinkedattributes = None
137         self.fedoradsindex = None
138         self.fedoradssamba = None
139         self.olmmron = None
140         self.olmmrserveridsconf = None
141         self.olmmrsyncreplconf = None
142         self.olcdir = None
143         self.olslapd = None
144         self.olcseedldif = None
145
146
147 class ProvisionNames(object):
148     def __init__(self):
149         self.rootdn = None
150         self.domaindn = None
151         self.configdn = None
152         self.schemadn = None
153         self.sambadn = None
154         self.ldapmanagerdn = None
155         self.dnsdomain = None
156         self.realm = None
157         self.netbiosname = None
158         self.domain = None
159         self.hostname = None
160         self.sitename = None
161         self.smbconf = None
162     
163
164 class ProvisionResult(object):
165     def __init__(self):
166         self.paths = None
167         self.domaindn = None
168         self.lp = None
169         self.samdb = None
170
171 def check_install(lp, session_info, credentials):
172     """Check whether the current install seems ok.
173     
174     :param lp: Loadparm context
175     :param session_info: Session information
176     :param credentials: Credentials
177     """
178     if lp.get("realm") == "":
179         raise Exception("Realm empty")
180     ldb = Ldb(lp.get("sam database"), session_info=session_info, 
181             credentials=credentials, lp=lp)
182     if len(ldb.search("(cn=Administrator)")) != 1:
183         raise ProvisioningError("No administrator account found")
184
185
186 def findnss(nssfn, names):
187     """Find a user or group from a list of possibilities.
188     
189     :param nssfn: NSS Function to try (should raise KeyError if not found)
190     :param names: Names to check.
191     :return: Value return by first names list.
192     """
193     for name in names:
194         try:
195             return nssfn(name)
196         except KeyError:
197             pass
198     raise KeyError("Unable to find user/group %r" % names)
199
200
201 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
202 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
203
204
205 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
206     """Setup a ldb in the private dir.
207     
208     :param ldb: LDB file to import data into
209     :param ldif_path: Path of the LDIF file to load
210     :param subst_vars: Optional variables to subsitute in LDIF.
211     :param nocontrols: Optional list of controls, can be None for no controls
212     """
213     assert isinstance(ldif_path, str)
214     data = read_and_sub_file(ldif_path, subst_vars)
215     ldb.add_ldif(data,controls)
216
217
218 def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
219     """Modify a ldb in the private dir.
220     
221     :param ldb: LDB object.
222     :param ldif_path: LDIF file path.
223     :param subst_vars: Optional dictionary with substitution variables.
224     """
225     data = read_and_sub_file(ldif_path, subst_vars)
226
227     ldb.modify_ldif(data)
228
229
230 def setup_ldb(ldb, ldif_path, subst_vars):
231     """Import a LDIF a file into a LDB handle, optionally substituting variables.
232
233     :note: Either all LDIF data will be added or none (using transactions).
234
235     :param ldb: LDB file to import into.
236     :param ldif_path: Path to the LDIF file.
237     :param subst_vars: Dictionary with substitution variables.
238     """
239     assert ldb is not None
240     ldb.transaction_start()
241     try:
242         setup_add_ldif(ldb, ldif_path, subst_vars)
243     except:
244         ldb.transaction_cancel()
245         raise
246     ldb.transaction_commit()
247
248
249 def provision_paths_from_lp(lp, dnsdomain):
250     """Set the default paths for provisioning.
251
252     :param lp: Loadparm context.
253     :param dnsdomain: DNS Domain name
254     """
255     paths = ProvisionPaths()
256     paths.private_dir = lp.get("private dir")
257     paths.dns_keytab = "dns.keytab"
258
259     paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
260     paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
261     paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
262     paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
263     paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
264     paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
265     paths.namedconf = os.path.join(paths.private_dir, "named.conf")
266     paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
267     paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
268     paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
269     paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
270     paths.phpldapadminconfig = os.path.join(paths.private_dir, 
271                                             "phpldapadmin-config.php")
272     paths.ldapdir = os.path.join(paths.private_dir, 
273                                  "ldap")
274     paths.slapdconf = os.path.join(paths.ldapdir, 
275                                    "slapd.conf")
276     paths.slapdpid = os.path.join(paths.ldapdir, 
277                                    "slapd.pid")
278     paths.modulesconf = os.path.join(paths.ldapdir, 
279                                      "modules.conf")
280     paths.memberofconf = os.path.join(paths.ldapdir, 
281                                       "memberof.conf")
282     paths.fedoradsinf = os.path.join(paths.ldapdir, 
283                                      "fedorads.inf")
284     paths.fedoradspartitions = os.path.join(paths.ldapdir, 
285                                             "fedorads-partitions.ldif")
286     paths.fedoradssasl = os.path.join(paths.ldapdir, 
287                                       "fedorads-sasl.ldif")
288     paths.fedoradsdna = os.path.join(paths.ldapdir, 
289                                      "fedorads-dna.ldif")
290     paths.fedoradspam = os.path.join(paths.ldapdir,
291                                      "fedorads-pam.ldif")
292     paths.fedoradsrefint = os.path.join(paths.ldapdir,
293                                         "fedorads-refint.ldif")
294     paths.fedoradslinkedattributes = os.path.join(paths.ldapdir,
295                                                   "fedorads-linked-attributes.ldif")
296     paths.fedoradsindex = os.path.join(paths.ldapdir,
297                                        "fedorads-index.ldif")
298     paths.fedoradssamba = os.path.join(paths.ldapdir, 
299                                        "fedorads-samba.ldif")
300     paths.olmmrserveridsconf = os.path.join(paths.ldapdir, 
301                                             "mmr_serverids.conf")
302     paths.olmmrsyncreplconf = os.path.join(paths.ldapdir, 
303                                            "mmr_syncrepl.conf")
304     paths.olcdir = os.path.join(paths.ldapdir, 
305                                  "slapd.d")
306     paths.olcseedldif = os.path.join(paths.ldapdir, 
307                                  "olc_seed.ldif")
308     paths.hklm = "hklm.ldb"
309     paths.hkcr = "hkcr.ldb"
310     paths.hkcu = "hkcu.ldb"
311     paths.hku = "hku.ldb"
312     paths.hkpd = "hkpd.ldb"
313     paths.hkpt = "hkpt.ldb"
314
315     paths.sysvol = lp.get("path", "sysvol")
316
317     paths.netlogon = lp.get("path", "netlogon")
318
319     paths.smbconf = lp.configfile
320
321     return paths
322
323
324 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
325                 serverrole=None, rootdn=None, domaindn=None, configdn=None,
326                 schemadn=None, serverdn=None, sitename=None, sambadn=None):
327     """Guess configuration settings to use."""
328
329     if hostname is None:
330         hostname = socket.gethostname().split(".")[0]
331
332     netbiosname = lp.get("netbios name")
333     if netbiosname is None:
334         netbiosname = hostname
335     assert netbiosname is not None
336     netbiosname = netbiosname.upper()
337     if not valid_netbios_name(netbiosname):
338         raise InvalidNetbiosName(netbiosname)
339
340     if dnsdomain is None:
341         dnsdomain = lp.get("realm")
342     assert dnsdomain is not None
343     dnsdomain = dnsdomain.lower()
344
345     if serverrole is None:
346         serverrole = lp.get("server role")
347     assert serverrole is not None
348     serverrole = serverrole.lower()
349
350     realm = dnsdomain.upper()
351
352     if lp.get("realm").upper() != realm:
353         raise ProvisioningError("guess_names: Realm '%s' in smb.conf must match chosen realm '%s'!", lp.get("realm").upper(), realm)
354
355     if serverrole == "domain controller":
356         if domain is None:
357             domain = lp.get("workgroup")
358         assert domain is not None
359         domain = domain.upper()
360
361         if lp.get("workgroup").upper() != domain:
362             raise ProvisioningError("guess_names: Workgroup '%s' in smb.conf must match chosen domain '%s'!", lp.get("workgroup").upper(), domain)
363
364         if domaindn is None:
365             domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
366     else:
367         domain = netbiosname
368         if domaindn is None:
369             domaindn = "DC=" + netbiosname
370         
371     if not valid_netbios_name(domain):
372         raise InvalidNetbiosName(domain)
373         
374     if hostname.upper() == realm:
375         raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!", realm, hostname)
376     if netbiosname == realm:
377         raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!", realm, netbiosname)
378     if domain == realm:
379         raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!", realm, domain)
380
381     if rootdn is None:
382        rootdn = domaindn
383        
384     if configdn is None:
385         configdn = "CN=Configuration," + rootdn
386     if schemadn is None:
387         schemadn = "CN=Schema," + configdn
388     if sambadn is None:
389         sambadn = "CN=Samba"
390
391     if sitename is None:
392         sitename=DEFAULTSITE
393
394     names = ProvisionNames()
395     names.rootdn = rootdn
396     names.domaindn = domaindn
397     names.configdn = configdn
398     names.schemadn = schemadn
399     names.sambadn = sambadn
400     names.ldapmanagerdn = "CN=Manager," + rootdn
401     names.dnsdomain = dnsdomain
402     names.domain = domain
403     names.realm = realm
404     names.netbiosname = netbiosname
405     names.hostname = hostname
406     names.sitename = sitename
407     names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
408  
409     return names
410     
411
412 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, 
413                  targetdir, sid_generator):
414     """Create a new smb.conf file based on a couple of basic settings.
415     """
416     assert smbconf is not None
417     if hostname is None:
418         hostname = socket.gethostname().split(".")[0]
419     netbiosname = hostname.upper()
420
421     if serverrole is None:
422         serverrole = "standalone"
423
424     assert serverrole in ("domain controller", "member server", "standalone")
425     if serverrole == "domain controller":
426         smbconfsuffix = "dc"
427     elif serverrole == "member server":
428         smbconfsuffix = "member"
429     elif serverrole == "standalone":
430         smbconfsuffix = "standalone"
431
432     if sid_generator is None:
433         sid_generator = "internal"
434
435     assert domain is not None
436     domain = domain.upper()
437
438     assert realm is not None
439     realm = realm.upper()
440
441     default_lp = param.LoadParm()
442     #Load non-existant file
443     if os.path.exists(smbconf):
444         default_lp.load(smbconf)
445     
446     if targetdir is not None:
447         privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
448         lockdir_line = "lock dir = " + os.path.abspath(targetdir)
449
450         default_lp.set("lock dir", os.path.abspath(targetdir))
451     else:
452         privatedir_line = ""
453         lockdir_line = ""
454
455     if sid_generator == "internal":
456         sid_generator_line = ""
457     else:
458         sid_generator_line = "sid generator = " + sid_generator
459
460     sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
461     netlogon = os.path.join(sysvol, realm.lower(), "scripts")
462
463     setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix), 
464                smbconf, {
465             "NETBIOS_NAME": netbiosname,
466             "DOMAIN": domain,
467             "REALM": realm,
468             "SERVERROLE": serverrole,
469             "NETLOGONPATH": netlogon,
470             "SYSVOLPATH": sysvol,
471             "SIDGENERATOR_LINE": sid_generator_line,
472             "PRIVATEDIR_LINE": privatedir_line,
473             "LOCKDIR_LINE": lockdir_line
474             })
475
476
477 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
478                         users_gid, wheel_gid):
479     """setup reasonable name mappings for sam names to unix names.
480
481     :param samdb: SamDB object.
482     :param idmap: IDmap db object.
483     :param sid: The domain sid.
484     :param domaindn: The domain DN.
485     :param root_uid: uid of the UNIX root user.
486     :param nobody_uid: uid of the UNIX nobody user.
487     :param users_gid: gid of the UNIX users group.
488     :param wheel_gid: gid of the UNIX wheel group."""
489
490     idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
491     idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
492     
493     idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
494     idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
495
496 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info, 
497                            provision_backend, names, schema,
498                            serverrole, 
499                            erase=False):
500     """Setup the partitions for the SAM database. 
501     
502     Alternatively, provision() may call this, and then populate the database.
503     
504     :note: This will wipe the Sam Database!
505     
506     :note: This function always removes the local SAM LDB file. The erase 
507         parameter controls whether to erase the existing data, which 
508         may not be stored locally but in LDAP.
509
510     """
511     assert session_info is not None
512
513     old_partitions = None
514     new_partitions = None
515
516     # We use options=["modules:"] to stop the modules loading - we
517     # just want to wipe and re-initialise the database, not start it up
518
519     try:
520         os.unlink(samdb_path)
521     except OSError:
522         pass
523
524     samdb = Ldb(url=samdb_path, session_info=session_info, 
525                 lp=lp, options=["modules:"])
526
527     #Add modules to the list to activate them by default
528     #beware often order is important
529     #
530     # Some Known ordering constraints:
531     # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
532     # - objectclass must be before password_hash, because password_hash checks
533     #   that the objectclass is of type person (filled in by objectclass
534     #   module when expanding the objectclass list)
535     # - partition must be last
536     # - each partition has its own module list then
537     modules_list = ["resolve_oids",
538                     "rootdse",
539                     "lazy_commit",
540                     "acl",
541                     "paged_results",
542                     "ranged_results",
543                     "anr",
544                     "server_sort",
545                     "asq",
546                     "extended_dn_store",
547                     "extended_dn_in",
548                     "rdn_name",
549                     "objectclass",
550                     "descriptor",
551                     "samldb",
552                     "password_hash",
553                     "operational",
554                     "kludge_acl", 
555                     "instancetype"]
556     tdb_modules_list = [
557                     "subtree_rename",
558                     "subtree_delete",
559                     "linked_attributes",
560                     "extended_dn_out_ldb"]
561     modules_list2 = ["show_deleted",
562                      "schema_load",
563                      "new_partition",
564                      "partition"]
565
566     ldap_backend_line = "# No LDAP backend"
567     if provision_backend.type is not "ldb":
568         ldap_backend_line = "ldapBackend: %s" % provision_backend.ldapi_uri
569         
570         if provision_backend.ldap_backend_type == "fedora-ds":
571             backend_modules = ["nsuniqueid", "paged_searches"]
572             # We can handle linked attributes here, as we don't have directory-side subtree operations
573             tdb_modules_list = ["extended_dn_out_fds"]
574         elif ldap_backend.ldap_backend_type == "openldap":
575             backend_modules = ["entryuuid", "paged_searches"]
576             # OpenLDAP handles subtree renames, so we don't want to do any of these things
577             tdb_modules_list = ["extended_dn_out_openldap"]
578
579     elif serverrole == "domain controller":
580         tdb_modules_list.insert(0, "repl_meta_data")
581         backend_modules = []
582     else:
583         backend_modules = ["objectguid"]
584
585     if tdb_modules_list is None:
586         tdb_modules_list_as_string = ""
587     else:
588         tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
589         
590     samdb.transaction_start()
591     try:
592         message("Setting up sam.ldb partitions and settings")
593         setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
594                 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(), 
595                 "SCHEMADN_MOD2": ",objectguid",
596                 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
597                 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
598                 "SCHEMADN_MOD": "schema_data",
599                 "CONFIGDN_MOD": "naming_fsmo",
600                 "DOMAINDN_MOD": "pdc_fsmo",
601                 "MODULES_LIST": ",".join(modules_list),
602                 "TDB_MODULES_LIST": tdb_modules_list_as_string,
603                 "MODULES_LIST2": ",".join(modules_list2),
604                 "BACKEND_MOD": ",".join(backend_modules),
605                 "LDAP_BACKEND_LINE": ldap_backend_line,
606         })
607
608         
609         samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
610
611         message("Setting up sam.ldb rootDSE")
612         setup_samdb_rootdse(samdb, setup_path, names)
613
614     except:
615         samdb.transaction_cancel()
616         raise
617
618     samdb.transaction_commit()
619
620         
621 def secretsdb_self_join(secretsdb, domain, 
622                         netbiosname, domainsid, machinepass, 
623                         realm=None, dnsdomain=None,
624                         keytab_path=None, 
625                         key_version_number=1,
626                         secure_channel_type=SEC_CHAN_WKSTA):
627     """Add domain join-specific bits to a secrets database.
628     
629     :param secretsdb: Ldb Handle to the secrets database
630     :param machinepass: Machine password
631     """
632     attrs=["whenChanged",
633            "secret",
634            "priorSecret",
635            "priorChanged",
636            "krb5Keytab",
637            "privateKeytab"]
638     
639
640     msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain));
641     msg["secureChannelType"] = str(secure_channel_type)
642     msg["flatname"] = [domain]
643     msg["objectClass"] = ["top", "primaryDomain"]
644     if realm is not None:
645       if dnsdomain is None:
646         dnsdomain = realm.lower()
647       msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
648       msg["realm"] = realm
649       msg["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())
650       msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
651       msg["privateKeytab"] = ["secrets.keytab"];
652
653
654     msg["secret"] = [machinepass]
655     msg["samAccountName"] = ["%s$" % netbiosname]
656     msg["secureChannelType"] = [str(secure_channel_type)]
657     msg["objectSid"] = [ndr_pack(domainsid)]
658     
659     res = secretsdb.search(base="cn=Primary Domains", 
660                            attrs=attrs, 
661                            expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))), 
662                            scope=SCOPE_ONELEVEL)
663     
664     for del_msg in res:
665       if del_msg.dn is not msg.dn:
666         secretsdb.delete(del_msg.dn)
667
668     res = secretsdb.search(base=msg.dn, attrs=attrs, scope=SCOPE_BASE)
669
670     if len(res) == 1:
671       msg["priorSecret"] = res[0]["secret"]
672       msg["priorWhenChanged"] = res[0]["whenChanged"]
673
674       if res["privateKeytab"] is not None:
675         msg["privateKeytab"] = res[0]["privateKeytab"]
676
677       if res["krb5Keytab"] is not None:
678         msg["krb5Keytab"] = res[0]["krb5Keytab"]
679
680       for el in msg:
681         el.set_flags(ldb.FLAG_MOD_REPLACE)
682         secretsdb.modify(msg)
683     else:
684       secretsdb.add(msg)
685
686
687 def secretsdb_setup_dns(secretsdb, setup_path, realm, dnsdomain, 
688                         dns_keytab_path, dnspass):
689     """Add DNS specific bits to a secrets database.
690     
691     :param secretsdb: Ldb Handle to the secrets database
692     :param setup_path: Setup path function
693     :param machinepass: Machine password
694     """
695     setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), { 
696             "REALM": realm,
697             "DNSDOMAIN": dnsdomain,
698             "DNS_KEYTAB": dns_keytab_path,
699             "DNSPASS_B64": b64encode(dnspass),
700             })
701
702
703 def setup_secretsdb(path, setup_path, session_info, backend_credentials, lp):
704     """Setup the secrets database.
705
706     :param path: Path to the secrets database.
707     :param setup_path: Get the path to a setup file.
708     :param session_info: Session info.
709     :param credentials: Credentials
710     :param lp: Loadparm context
711     :return: LDB handle for the created secrets database
712     """
713     if os.path.exists(path):
714         os.unlink(path)
715     secrets_ldb = Ldb(path, session_info=session_info, 
716                       lp=lp)
717     secrets_ldb.erase()
718     secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
719     secrets_ldb = Ldb(path, session_info=session_info, 
720                       lp=lp)
721     secrets_ldb.transaction_start()
722     secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
723
724     if backend_credentials is not None and backend_credentials.authentication_requested():
725         if backend_credentials.get_bind_dn() is not None:
726             setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
727                     "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
728                     "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
729                     })
730         else:
731             setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
732                     "LDAPADMINUSER": backend_credentials.get_username(),
733                     "LDAPADMINREALM": backend_credentials.get_realm(),
734                     "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
735                     })
736
737     return secrets_ldb
738
739 def setup_privileges(path, setup_path, session_info, lp):
740     """Setup the privileges database.
741
742     :param path: Path to the privileges database.
743     :param setup_path: Get the path to a setup file.
744     :param session_info: Session info.
745     :param credentials: Credentials
746     :param lp: Loadparm context
747     :return: LDB handle for the created secrets database
748     """
749     if os.path.exists(path):
750         os.unlink(path)
751     privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
752     privilege_ldb.erase()
753     privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
754
755
756 def setup_registry(path, setup_path, session_info, lp):
757     """Setup the registry.
758     
759     :param path: Path to the registry database
760     :param setup_path: Function that returns the path to a setup.
761     :param session_info: Session information
762     :param credentials: Credentials
763     :param lp: Loadparm context
764     """
765     reg = registry.Registry()
766     hive = registry.open_ldb(path, session_info=session_info, 
767                          lp_ctx=lp)
768     reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
769     provision_reg = setup_path("provision.reg")
770     assert os.path.exists(provision_reg)
771     reg.diff_apply(provision_reg)
772
773
774 def setup_idmapdb(path, setup_path, session_info, lp):
775     """Setup the idmap database.
776
777     :param path: path to the idmap database
778     :param setup_path: Function that returns a path to a setup file
779     :param session_info: Session information
780     :param credentials: Credentials
781     :param lp: Loadparm context
782     """
783     if os.path.exists(path):
784         os.unlink(path)
785
786     idmap_ldb = IDmapDB(path, session_info=session_info,
787                         lp=lp)
788
789     idmap_ldb.erase()
790     idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
791     return idmap_ldb
792
793
794 def setup_samdb_rootdse(samdb, setup_path, names):
795     """Setup the SamDB rootdse.
796
797     :param samdb: Sam Database handle
798     :param setup_path: Obtain setup path
799     """
800     setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
801         "SCHEMADN": names.schemadn, 
802         "NETBIOSNAME": names.netbiosname,
803         "DNSDOMAIN": names.dnsdomain,
804         "REALM": names.realm,
805         "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
806         "DOMAINDN": names.domaindn,
807         "ROOTDN": names.rootdn,
808         "CONFIGDN": names.configdn,
809         "SERVERDN": names.serverdn,
810         })
811         
812
813 def setup_self_join(samdb, names,
814                     machinepass, dnspass, 
815                     domainsid, invocationid, setup_path,
816                     policyguid, policyguid_dc, domainControllerFunctionality,
817                     ntdsguid):
818     """Join a host to its own domain."""
819     assert isinstance(invocationid, str)
820     if ntdsguid is not None:
821         ntdsguid_line = "objectGUID: %s\n"%ntdsguid
822     else:
823         ntdsguid_line = ""
824     setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), { 
825               "CONFIGDN": names.configdn, 
826               "SCHEMADN": names.schemadn,
827               "DOMAINDN": names.domaindn,
828               "SERVERDN": names.serverdn,
829               "INVOCATIONID": invocationid,
830               "NETBIOSNAME": names.netbiosname,
831               "DEFAULTSITE": names.sitename,
832               "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
833               "MACHINEPASS_B64": b64encode(machinepass),
834               "DNSPASS_B64": b64encode(dnspass),
835               "REALM": names.realm,
836               "DOMAIN": names.domain,
837               "DNSDOMAIN": names.dnsdomain,
838               "SAMBA_VERSION_STRING": version,
839               "NTDSGUID": ntdsguid_line,
840               "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
841
842     setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), { 
843               "POLICYGUID": policyguid,
844               "POLICYGUID_DC": policyguid_dc,
845               "DNSDOMAIN": names.dnsdomain,
846               "DOMAINSID": str(domainsid),
847               "DOMAINDN": names.domaindn})
848     
849     # add the NTDSGUID based SPNs
850     ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
851     names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
852                                      expression="", scope=SCOPE_BASE)
853     assert isinstance(names.ntdsguid, str)
854
855     # Setup fSMORoleOwner entries to point at the newly created DC entry
856     setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
857               "DOMAIN": names.domain,
858               "DNSDOMAIN": names.dnsdomain,
859               "DOMAINDN": names.domaindn,
860               "CONFIGDN": names.configdn,
861               "SCHEMADN": names.schemadn, 
862               "DEFAULTSITE": names.sitename,
863               "SERVERDN": names.serverdn,
864               "NETBIOSNAME": names.netbiosname,
865               "NTDSGUID": names.ntdsguid
866               })
867
868
869 def setup_samdb(path, setup_path, session_info, provision_backend, lp, 
870                 names, message, 
871                 domainsid, domainguid, policyguid, policyguid_dc,
872                 fill, adminpass, krbtgtpass, 
873                 machinepass, invocationid, dnspass, ntdsguid,
874                 serverrole, dom_for_fun_level=None,
875                 schema=None):
876     """Setup a complete SAM Database.
877     
878     :note: This will wipe the main SAM database file!
879     """
880
881     # ATTENTION: Do NOT change these default values without discussion with the
882     # team and/or release manager. They have a big impact on the whole program!
883     domainControllerFunctionality = DS_DC_FUNCTION_2008
884
885     if dom_for_fun_level is None:
886         dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
887     if dom_for_fun_level < DS_DOMAIN_FUNCTION_2003:
888         raise ProvisioningError("You want to run SAMBA 4 on a domain and forest function level lower than Windows 2003 (Native). This isn't supported!")
889
890     if dom_for_fun_level > domainControllerFunctionality:
891         raise ProvisioningError("You want to run SAMBA 4 on a domain and forest function level which itself is higher than its actual DC function level (2008). This won't work!")
892
893     domainFunctionality = dom_for_fun_level
894     forestFunctionality = dom_for_fun_level
895
896     # Also wipes the database
897     setup_samdb_partitions(path, setup_path, message=message, lp=lp,
898                            provision_backend=provision_backend, session_info=session_info,
899                            names=names, 
900                            serverrole=serverrole, schema=schema)
901
902     if (schema == None):
903         schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn,
904                         sambadn=names.sambadn)
905
906     # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
907     samdb = Ldb(session_info=session_info, 
908                 credentials=provision_backend.credentials, lp=lp)
909
910     message("Pre-loading the Samba 4 and AD schema")
911
912     # Load the schema from the one we computed earlier
913     samdb.set_schema_from_ldb(schema.ldb)
914
915     # And now we can connect to the DB - the schema won't be loaded from the DB
916     samdb.connect(path)
917
918     if fill == FILL_DRS:
919         return samdb
920         
921     samdb.transaction_start()
922     try:
923         # Set the domain functionality levels onto the database.
924         # Various module (the password_hash module in particular) need
925         # to know what level of AD we are emulating.
926
927         # These will be fixed into the database via the database
928         # modifictions below, but we need them set from the start.
929         samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
930         samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
931         samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
932
933         samdb.set_domain_sid(str(domainsid))
934         if serverrole == "domain controller":
935             samdb.set_invocation_id(invocationid)
936
937         message("Adding DomainDN: %s" % names.domaindn)
938
939 #impersonate domain admin
940         admin_session_info = admin_session(lp, str(domainsid))
941         samdb.set_session_info(admin_session_info)
942         if domainguid is not None:
943             domainguid_line = "objectGUID: %s\n-" % domainguid
944         else:
945             domainguid_line = ""
946         setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
947                 "DOMAINDN": names.domaindn,
948                 "DOMAINGUID": domainguid_line
949                 })
950
951
952         setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
953             "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
954             "DOMAINSID": str(domainsid),
955             "SCHEMADN": names.schemadn, 
956             "NETBIOSNAME": names.netbiosname,
957             "DEFAULTSITE": names.sitename,
958             "CONFIGDN": names.configdn,
959             "SERVERDN": names.serverdn,
960             "POLICYGUID": policyguid,
961             "DOMAINDN": names.domaindn,
962             "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
963             "SAMBA_VERSION_STRING": version
964             })
965
966         message("Adding configuration container")
967         descr = get_config_descriptor(domainsid);
968         setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
969             "CONFIGDN": names.configdn, 
970             "DESCRIPTOR": descr,
971             })
972         message("Modifying configuration container")
973         setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
974             "CONFIGDN": names.configdn, 
975             "SCHEMADN": names.schemadn,
976             })
977
978         # The LDIF here was created when the Schema object was constructed
979         message("Setting up sam.ldb schema")
980         samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
981         samdb.modify_ldif(schema.schema_dn_modify)
982         samdb.write_prefixes_from_schema()
983         samdb.add_ldif(schema.schema_data, controls=["relax:0"])
984         setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"), 
985                        {"SCHEMADN": names.schemadn})
986
987         message("Setting up sam.ldb configuration data")
988         setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
989             "CONFIGDN": names.configdn,
990             "NETBIOSNAME": names.netbiosname,
991             "DEFAULTSITE": names.sitename,
992             "DNSDOMAIN": names.dnsdomain,
993             "DOMAIN": names.domain,
994             "SCHEMADN": names.schemadn,
995             "DOMAINDN": names.domaindn,
996             "SERVERDN": names.serverdn,
997             "FOREST_FUNCTIONALALITY": str(forestFunctionality)
998             })
999
1000         message("Setting up display specifiers")
1001         display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1002         display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
1003         check_all_substituted(display_specifiers_ldif)
1004         samdb.add_ldif(display_specifiers_ldif)
1005
1006         message("Adding users container")
1007         setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1008                 "DOMAINDN": names.domaindn})
1009         message("Modifying users container")
1010         setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1011                 "DOMAINDN": names.domaindn})
1012         message("Adding computers container")
1013         setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1014                 "DOMAINDN": names.domaindn})
1015         message("Modifying computers container")
1016         setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1017                 "DOMAINDN": names.domaindn})
1018         message("Setting up sam.ldb data")
1019         setup_add_ldif(samdb, setup_path("provision.ldif"), {
1020             "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1021             "DOMAINDN": names.domaindn,
1022             "NETBIOSNAME": names.netbiosname,
1023             "DEFAULTSITE": names.sitename,
1024             "CONFIGDN": names.configdn,
1025             "SERVERDN": names.serverdn,
1026             "POLICYGUID_DC": policyguid_dc
1027             })
1028
1029         if fill == FILL_FULL:
1030             message("Setting up sam.ldb users and groups")
1031             setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1032                 "DOMAINDN": names.domaindn,
1033                 "DOMAINSID": str(domainsid),
1034                 "CONFIGDN": names.configdn,
1035                 "ADMINPASS_B64": b64encode(adminpass),
1036                 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1037                 })
1038
1039             if serverrole == "domain controller":
1040                 message("Setting up self join")
1041                 setup_self_join(samdb, names=names, invocationid=invocationid, 
1042                                 dnspass=dnspass,  
1043                                 machinepass=machinepass, 
1044                                 domainsid=domainsid, policyguid=policyguid,
1045                                 policyguid_dc=policyguid_dc,
1046                                 setup_path=setup_path,
1047                                 domainControllerFunctionality=domainControllerFunctionality,
1048                                 ntdsguid=ntdsguid)
1049
1050                 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
1051                 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1052                   attribute="objectGUID", expression="", scope=SCOPE_BASE)
1053                 assert isinstance(names.ntdsguid, str)
1054
1055     except:
1056         samdb.transaction_cancel()
1057         raise
1058
1059     samdb.transaction_commit()
1060     return samdb
1061
1062
1063 FILL_FULL = "FULL"
1064 FILL_NT4SYNC = "NT4SYNC"
1065 FILL_DRS = "DRS"
1066
1067
1068 def provision(setup_dir, message, session_info, 
1069               credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1070               realm=None, 
1071               rootdn=None, domaindn=None, schemadn=None, configdn=None, 
1072               serverdn=None,
1073               domain=None, hostname=None, hostip=None, hostip6=None, 
1074               domainsid=None, adminpass=None, ldapadminpass=None, 
1075               krbtgtpass=None, domainguid=None, 
1076               policyguid=None, policyguid_dc=None, invocationid=None,
1077               machinepass=None, ntdsguid=None,
1078               dnspass=None, root=None, nobody=None, users=None, 
1079               wheel=None, backup=None, aci=None, serverrole=None,
1080               dom_for_fun_level=None,
1081               ldap_backend_extra_port=None, backend_type=None,
1082               sitename=None,
1083               ol_mmr_urls=None, ol_olc=None, 
1084               setup_ds_path=None, slapd_path=None, nosync=False,
1085               ldap_dryrun_mode=False):
1086     """Provision samba4
1087     
1088     :note: caution, this wipes all existing data!
1089     """
1090
1091     def setup_path(file):
1092       return os.path.join(setup_dir, file)
1093
1094     if domainsid is None:
1095       domainsid = security.random_sid()
1096     else:
1097       domainsid = security.dom_sid(domainsid)
1098
1099     # create/adapt the group policy GUIDs
1100     if policyguid is None:
1101         policyguid = str(uuid.uuid4())
1102     policyguid = policyguid.upper()
1103     if policyguid_dc is None:
1104         policyguid_dc = str(uuid.uuid4())
1105     policyguid_dc = policyguid_dc.upper()
1106
1107     if adminpass is None:
1108         adminpass = glue.generate_random_str(12)
1109     if krbtgtpass is None:
1110         krbtgtpass = glue.generate_random_str(12)
1111     if machinepass is None:
1112         machinepass  = glue.generate_random_str(12)
1113     if dnspass is None:
1114         dnspass = glue.generate_random_str(12)
1115     if ldapadminpass is None:
1116         #Make a new, random password between Samba and it's LDAP server
1117         ldapadminpass=glue.generate_random_str(12)        
1118
1119     if backend_type is None:
1120         backend_type = "ldb"
1121
1122     sid_generator = "internal"
1123     if backend_type == "fedora-ds":
1124         sid_generator = "backend"
1125
1126     root_uid = findnss_uid([root or "root"])
1127     nobody_uid = findnss_uid([nobody or "nobody"])
1128     users_gid = findnss_gid([users or "users"])
1129     if wheel is None:
1130         wheel_gid = findnss_gid(["wheel", "adm"])
1131     else:
1132         wheel_gid = findnss_gid([wheel])
1133
1134     if targetdir is not None:
1135         if (not os.path.exists(os.path.join(targetdir, "etc"))):
1136             os.makedirs(os.path.join(targetdir, "etc"))
1137         smbconf = os.path.join(targetdir, "etc", "smb.conf")
1138     elif smbconf is None:
1139         smbconf = param.default_path()
1140
1141     # only install a new smb.conf if there isn't one there already
1142     if not os.path.exists(smbconf):
1143         make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, 
1144                      targetdir, sid_generator)
1145
1146     lp = param.LoadParm()
1147     lp.load(smbconf)
1148
1149     names = guess_names(lp=lp, hostname=hostname, domain=domain,
1150                         dnsdomain=realm, serverrole=serverrole,
1151                         domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1152                         serverdn=serverdn, sitename=sitename)
1153
1154     paths = provision_paths_from_lp(lp, names.dnsdomain)
1155
1156     if hostip is None:
1157         try:
1158             hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1159         except socket.gaierror, (socket.EAI_NODATA, msg):
1160             hostip = None
1161
1162     if hostip6 is None:
1163         try:
1164             hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1165         except socket.gaierror, (socket.EAI_NODATA, msg): 
1166             hostip6 = None
1167
1168     if serverrole is None:
1169         serverrole = lp.get("server role")
1170
1171     assert serverrole in ("domain controller", "member server", "standalone")
1172     if invocationid is None and serverrole == "domain controller":
1173         invocationid = str(uuid.uuid4())
1174
1175     if not os.path.exists(paths.private_dir):
1176         os.mkdir(paths.private_dir)
1177
1178     ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1179     
1180     schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn,
1181                     sambadn=names.sambadn)
1182     
1183     provision_backend = ProvisionBackend(backend_type,
1184                                          paths=paths, setup_path=setup_path,
1185                                          lp=lp, credentials=credentials, 
1186                                          names=names,
1187                                          message=message, hostname=hostname,
1188                                          root=root, schema=schema,
1189                                          ldapadminpass=ldapadminpass,
1190                                          ldap_backend_extra_port=ldap_backend_extra_port,
1191                                          ol_mmr_urls=ol_mmr_urls, 
1192                                          slapd_path=slapd_path,
1193                                          setup_ds_path=setup_ds_path,
1194                                          ldap_dryrun_mode=ldap_dryrun_mode,
1195                                          domainsid=domainsid)
1196
1197     # only install a new shares config db if there is none
1198     if not os.path.exists(paths.shareconf):
1199         message("Setting up share.ldb")
1200         share_ldb = Ldb(paths.shareconf, session_info=session_info, 
1201                         lp=lp)
1202         share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1203
1204      
1205     message("Setting up secrets.ldb")
1206     secrets_ldb = setup_secretsdb(paths.secrets, setup_path, 
1207                                   session_info=session_info, 
1208                                   backend_credentials=provision_backend.secrets_credentials, lp=lp)
1209
1210     message("Setting up the registry")
1211     setup_registry(paths.hklm, setup_path, session_info, 
1212                    lp=lp)
1213
1214     message("Setting up the privileges database")
1215     setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1216
1217     message("Setting up idmap db")
1218     idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1219                           lp=lp)
1220
1221     message("Setting up SAM db")
1222     samdb = setup_samdb(paths.samdb, setup_path, session_info, 
1223                         provision_backend, lp, names,
1224                         message, 
1225                         domainsid=domainsid, 
1226                         schema=schema, domainguid=domainguid,
1227                         policyguid=policyguid, policyguid_dc=policyguid_dc,
1228                         fill=samdb_fill, 
1229                         adminpass=adminpass, krbtgtpass=krbtgtpass,
1230                         invocationid=invocationid, 
1231                         machinepass=machinepass, dnspass=dnspass, 
1232                         ntdsguid=ntdsguid, serverrole=serverrole,
1233                         dom_for_fun_level=dom_for_fun_level)
1234
1235     if serverrole == "domain controller":
1236         if paths.netlogon is None:
1237             message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1238             message("Please either remove %s or see the template at %s" % 
1239                     ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1240             assert(paths.netlogon is not None)
1241
1242         if paths.sysvol is None:
1243             message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1244             message("Please either remove %s or see the template at %s" % 
1245                     (paths.smbconf, setup_path("provision.smb.conf.dc")))
1246             assert(paths.sysvol is not None)            
1247             
1248         # Set up group policies (domain policy and domain controller policy)
1249
1250         policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1251                                    "{" + policyguid + "}")
1252         os.makedirs(policy_path, 0755)
1253         open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1254                                    "[General]\r\nVersion=65543")
1255         os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
1256         os.makedirs(os.path.join(policy_path, "USER"), 0755)
1257
1258         policy_path_dc = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1259                                    "{" + policyguid_dc + "}")
1260         os.makedirs(policy_path_dc, 0755)
1261         open(os.path.join(policy_path_dc, "GPT.INI"), 'w').write(
1262                                    "[General]\r\nVersion=2")
1263         os.makedirs(os.path.join(policy_path_dc, "MACHINE"), 0755)
1264         os.makedirs(os.path.join(policy_path_dc, "USER"), 0755)
1265
1266         if not os.path.isdir(paths.netlogon):
1267             os.makedirs(paths.netlogon, 0755)
1268
1269     if samdb_fill == FILL_FULL:
1270         setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1271                             root_uid=root_uid, nobody_uid=nobody_uid,
1272                             users_gid=users_gid, wheel_gid=wheel_gid)
1273
1274         message("Setting up sam.ldb rootDSE marking as synchronized")
1275         setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1276
1277         # Only make a zone file on the first DC, it should be replicated with DNS replication
1278         if serverrole == "domain controller":
1279             secretsdb_self_join(secrets_ldb, domain=domain,
1280                                 realm=names.realm,
1281                                 dnsdomain=names.dnsdomain,
1282                                 netbiosname=names.netbiosname,
1283                                 domainsid=domainsid, 
1284                                 machinepass=machinepass,
1285                                 secure_channel_type=SEC_CHAN_BDC)
1286
1287             secretsdb_setup_dns(secrets_ldb, setup_path, 
1288                                 realm=names.realm, dnsdomain=names.dnsdomain,
1289                                 dns_keytab_path=paths.dns_keytab,
1290                                 dnspass=dnspass)
1291
1292             domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1293             assert isinstance(domainguid, str)
1294
1295             create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1296                              hostip=hostip,
1297                              hostip6=hostip6, hostname=names.hostname,
1298                              realm=names.realm,
1299                              domainguid=domainguid, ntdsguid=names.ntdsguid)
1300
1301             create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1302                               dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1303
1304             create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1305                               dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1306                               keytab_name=paths.dns_keytab)
1307             message("See %s for an example configuration include file for BIND" % paths.namedconf)
1308             message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1309
1310             create_krb5_conf(paths.krb5conf, setup_path,
1311                              dnsdomain=names.dnsdomain, hostname=names.hostname,
1312                              realm=names.realm)
1313             message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1314
1315     if provision_backend.post_setup is not None:
1316         provision_backend.post_setup()
1317
1318     if provision_backend.shutdown is not None:
1319         provision_backend.shutdown()
1320     
1321     create_phpldapadmin_config(paths.phpldapadminconfig, setup_path, 
1322                                ldapi_url)
1323
1324     #Now commit the secrets.ldb to disk
1325     secrets_ldb.transaction_commit()
1326
1327     message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1328
1329     message("Once the above files are installed, your Samba4 server will be ready to use")
1330     message("Server Role:           %s" % serverrole)
1331     message("Hostname:              %s" % names.hostname)
1332     message("NetBIOS Domain:        %s" % names.domain)
1333     message("DNS Domain:            %s" % names.dnsdomain)
1334     message("DOMAIN SID:            %s" % str(domainsid))
1335     if samdb_fill == FILL_FULL:
1336         message("Admin password:    %s" % adminpass)
1337     if provision_backend.type is not "ldb":
1338         if provision_backend.credentials.get_bind_dn() is not None:
1339             message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1340         else:
1341             message("LDAP Admin User:       %s" % provision_backend.credentials.get_username())
1342
1343         message("LDAP Admin Password:   %s" % provision_backend.credentials.get_password())
1344
1345         if provision_backend.slapd_command_escaped is not None:
1346             # now display slapd_command_file.txt to show how slapd must be started next time
1347             message("Use later the following commandline to start slapd, then Samba:")
1348             message(provision_backend.slapd_command_escaped)
1349             message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh")
1350
1351
1352     result = ProvisionResult()
1353     result.domaindn = domaindn
1354     result.paths = paths
1355     result.lp = lp
1356     result.samdb = samdb
1357     return result
1358
1359
1360
1361 def provision_become_dc(setup_dir=None,
1362                         smbconf=None, targetdir=None, realm=None, 
1363                         rootdn=None, domaindn=None, schemadn=None,
1364                         configdn=None, serverdn=None,
1365                         domain=None, hostname=None, domainsid=None, 
1366                         adminpass=None, krbtgtpass=None, domainguid=None, 
1367                         policyguid=None, policyguid_dc=None, invocationid=None,
1368                         machinepass=None, 
1369                         dnspass=None, root=None, nobody=None, users=None, 
1370                         wheel=None, backup=None, serverrole=None, 
1371                         ldap_backend=None, ldap_backend_type=None,
1372                         sitename=None, debuglevel=1):
1373
1374     def message(text):
1375         """print a message if quiet is not set."""
1376         print text
1377
1378     glue.set_debug_level(debuglevel)
1379
1380     return provision(setup_dir, message, system_session(), None,
1381               smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1382               realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1383               configdn=configdn, serverdn=serverdn, domain=domain,
1384               hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1385               machinepass=machinepass, serverrole="domain controller",
1386               sitename=sitename)
1387
1388
1389 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1390     """Create a PHP LDAP admin configuration file.
1391
1392     :param path: Path to write the configuration to.
1393     :param setup_path: Function to generate setup paths.
1394     """
1395     setup_file(setup_path("phpldapadmin-config.php"), path, 
1396             {"S4_LDAPI_URI": ldapi_uri})
1397
1398
1399 def create_zone_file(path, setup_path, dnsdomain, 
1400                      hostip, hostip6, hostname, realm, domainguid,
1401                      ntdsguid):
1402     """Write out a DNS zone file, from the info in the current database.
1403
1404     :param path: Path of the new zone file.
1405     :param setup_path: Setup path function.
1406     :param dnsdomain: DNS Domain name
1407     :param domaindn: DN of the Domain
1408     :param hostip: Local IPv4 IP
1409     :param hostip6: Local IPv6 IP
1410     :param hostname: Local hostname
1411     :param realm: Realm name
1412     :param domainguid: GUID of the domain.
1413     :param ntdsguid: GUID of the hosts nTDSDSA record.
1414     """
1415     assert isinstance(domainguid, str)
1416
1417     if hostip6 is not None:
1418         hostip6_base_line = "            IN AAAA    " + hostip6
1419         hostip6_host_line = hostname + "        IN AAAA    " + hostip6
1420     else:
1421         hostip6_base_line = ""
1422         hostip6_host_line = ""
1423
1424     if hostip is not None:
1425         hostip_base_line = "            IN A    " + hostip
1426         hostip_host_line = hostname + "        IN A    " + hostip
1427     else:
1428         hostip_base_line = ""
1429         hostip_host_line = ""
1430
1431     setup_file(setup_path("provision.zone"), path, {
1432             "HOSTNAME": hostname,
1433             "DNSDOMAIN": dnsdomain,
1434             "REALM": realm,
1435             "HOSTIP_BASE_LINE": hostip_base_line,
1436             "HOSTIP_HOST_LINE": hostip_host_line,
1437             "DOMAINGUID": domainguid,
1438             "DATESTRING": time.strftime("%Y%m%d%H"),
1439             "DEFAULTSITE": DEFAULTSITE,
1440             "NTDSGUID": ntdsguid,
1441             "HOSTIP6_BASE_LINE": hostip6_base_line,
1442             "HOSTIP6_HOST_LINE": hostip6_host_line,
1443         })
1444
1445
1446 def create_named_conf(path, setup_path, realm, dnsdomain,
1447                       private_dir):
1448     """Write out a file containing zone statements suitable for inclusion in a
1449     named.conf file (including GSS-TSIG configuration).
1450     
1451     :param path: Path of the new named.conf file.
1452     :param setup_path: Setup path function.
1453     :param realm: Realm name
1454     :param dnsdomain: DNS Domain name
1455     :param private_dir: Path to private directory
1456     :param keytab_name: File name of DNS keytab file
1457     """
1458
1459     setup_file(setup_path("named.conf"), path, {
1460             "DNSDOMAIN": dnsdomain,
1461             "REALM": realm,
1462             "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1463             "PRIVATE_DIR": private_dir
1464             })
1465
1466 def create_named_txt(path, setup_path, realm, dnsdomain,
1467                       private_dir, keytab_name):
1468     """Write out a file containing zone statements suitable for inclusion in a
1469     named.conf file (including GSS-TSIG configuration).
1470     
1471     :param path: Path of the new named.conf file.
1472     :param setup_path: Setup path function.
1473     :param realm: Realm name
1474     :param dnsdomain: DNS Domain name
1475     :param private_dir: Path to private directory
1476     :param keytab_name: File name of DNS keytab file
1477     """
1478
1479     setup_file(setup_path("named.txt"), path, {
1480             "DNSDOMAIN": dnsdomain,
1481             "REALM": realm,
1482             "DNS_KEYTAB": keytab_name,
1483             "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1484             "PRIVATE_DIR": private_dir
1485         })
1486
1487 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1488     """Write out a file containing zone statements suitable for inclusion in a
1489     named.conf file (including GSS-TSIG configuration).
1490     
1491     :param path: Path of the new named.conf file.
1492     :param setup_path: Setup path function.
1493     :param dnsdomain: DNS Domain name
1494     :param hostname: Local hostname
1495     :param realm: Realm name
1496     """
1497
1498     setup_file(setup_path("krb5.conf"), path, {
1499             "DNSDOMAIN": dnsdomain,
1500             "HOSTNAME": hostname,
1501             "REALM": realm,
1502         })
1503
1504