provision: Add support for IPv6 (bz #4593).
[sfrench/samba-autobuild/.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
7 #
8 # Based on the original in EJS:
9 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
10 #
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 3 of the License, or
14 # (at your option) any later version.
15 #   
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 # GNU General Public License for more details.
20 #   
21 # You should have received a copy of the GNU General Public License
22 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
23 #
24
25 from base64 import b64encode
26 import os
27 import pwd
28 import grp
29 import time
30 import uuid, misc
31 import socket
32 import param
33 import registry
34 import samba
35 from auth import system_session
36 from samba import Ldb, substitute_var, valid_netbios_name, check_all_substituted
37 from samba.samdb import SamDB
38 import security
39 import urllib
40 from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError, \
41         LDB_ERR_NO_SUCH_OBJECT, timestring, CHANGETYPE_MODIFY, CHANGETYPE_NONE
42
43 """Functions for setting up a Samba configuration."""
44
45 DEFAULTSITE = "Default-First-Site-Name"
46
47 class InvalidNetbiosName(Exception):
48     def __init__(self, name):
49         super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)
50
51
52 class ProvisionPaths:
53     def __init__(self):
54         self.shareconf = None
55         self.hklm = None
56         self.hkcu = None
57         self.hkcr = None
58         self.hku = None
59         self.hkpd = None
60         self.hkpt = None
61         self.samdb = None
62         self.idmapdb = None
63         self.secrets = None
64         self.keytab = None
65         self.dns_keytab = None
66         self.dns = None
67         self.winsdb = None
68         self.private_dir = None
69         self.ldapdir = None
70         self.slapdconf = None
71         self.modulesconf = None
72         self.memberofconf = None
73         self.fedoradsinf = None
74         self.fedoradspartitions = None
75  
76 class ProvisionNames:
77     def __init__(self):
78         self.rootdn = None
79         self.domaindn = None
80         self.configdn = None
81         self.schemadn = None
82         self.ldapmanagerdn = None
83         self.dnsdomain = None
84         self.realm = None
85         self.netbiosname = None
86         self.domain = None
87         self.hostname = None
88         self.sitename = None
89     
90 class ProvisionResult:
91     def __init__(self):
92         self.paths = None
93         self.domaindn = None
94         self.lp = None
95         self.samdb = None
96
97 def check_install(lp, session_info, credentials):
98     """Check whether the current install seems ok.
99     
100     :param lp: Loadparm context
101     :param session_info: Session information
102     :param credentials: Credentials
103     """
104     if lp.get("realm") == "":
105         raise Error("Realm empty")
106     ldb = Ldb(lp.get("sam database"), session_info=session_info, 
107             credentials=credentials, lp=lp)
108     if len(ldb.search("(cn=Administrator)")) != 1:
109         raise "No administrator account found"
110
111
112 def findnss(nssfn, names):
113     """Find a user or group from a list of possibilities.
114     
115     :param nssfn: NSS Function to try (should raise KeyError if not found)
116     :param names: Names to check.
117     :return: Value return by first names list.
118     """
119     for name in names:
120         try:
121             return nssfn(name)
122         except KeyError:
123             pass
124     raise KeyError("Unable to find user/group %r" % names)
125
126
127 def open_ldb(session_info, credentials, lp, dbname):
128     """Open a LDB, thrashing it if it is corrupt.
129
130     :param session_info: auth session information
131     :param credentials: credentials
132     :param lp: Loadparm context
133     :param dbname: Path of the database to open.
134     :return: a Ldb object
135     """
136     assert session_info is not None
137     try:
138         return Ldb(dbname, session_info=session_info, credentials=credentials, 
139                    lp=lp)
140     except LdbError, e:
141         print e
142         os.unlink(dbname)
143         return Ldb(dbname, session_info=session_info, credentials=credentials,
144                    lp=lp)
145
146
147 def setup_add_ldif(ldb, ldif_path, subst_vars=None):
148     """Setup a ldb in the private dir.
149     
150     :param ldb: LDB file to import data into
151     :param ldif_path: Path of the LDIF file to load
152     :param subst_vars: Optional variables to subsitute in LDIF.
153     """
154     assert isinstance(ldif_path, str)
155
156     data = open(ldif_path, 'r').read()
157     if subst_vars is not None:
158         data = substitute_var(data, subst_vars)
159
160     check_all_substituted(data)
161
162     ldb.add_ldif(data)
163
164
165 def setup_modify_ldif(ldb, ldif_path, substvars=None):
166     """Modify a ldb in the private dir.
167     
168     :param ldb: LDB object.
169     :param ldif_path: LDIF file path.
170     :param substvars: Optional dictionary with substitution variables.
171     """
172     data = open(ldif_path, 'r').read()
173     if substvars is not None:
174         data = substitute_var(data, substvars)
175
176     check_all_substituted(data)
177
178     ldb.modify_ldif(data)
179
180
181 def setup_ldb(ldb, ldif_path, subst_vars):
182     """Import a LDIF a file into a LDB handle, optionally substituting variables.
183
184     :note: Either all LDIF data will be added or none (using transactions).
185
186     :param ldb: LDB file to import into.
187     :param ldif_path: Path to the LDIF file.
188     :param subst_vars: Dictionary with substitution variables.
189     """
190     assert ldb is not None
191     ldb.transaction_start()
192     try:
193         setup_add_ldif(ldb, ldif_path, subst_vars)
194     except:
195         ldb.transaction_cancel()
196         raise
197     ldb.transaction_commit()
198
199
200 def setup_file(template, fname, substvars):
201     """Setup a file in the private dir.
202
203     :param template: Path of the template file.
204     :param fname: Path of the file to create.
205     :param substvars: Substitution variables.
206     """
207     f = fname
208
209     if os.path.exists(f):
210         os.unlink(f)
211
212     data = open(template, 'r').read()
213     if substvars:
214         data = substitute_var(data, substvars)
215     check_all_substituted(data)
216
217     open(f, 'w').write(data)
218
219
220 def provision_paths_from_lp(lp, dnsdomain):
221     """Set the default paths for provisioning.
222
223     :param lp: Loadparm context.
224     :param dnsdomain: DNS Domain name
225     """
226     paths = ProvisionPaths()
227     paths.private_dir = lp.get("private dir")
228     paths.keytab = "secrets.keytab"
229     paths.dns_keytab = "dns.keytab"
230
231     paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
232     paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
233     paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
234     paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
235     paths.templates = os.path.join(paths.private_dir, "templates.ldb")
236     paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
237     paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
238     paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
239     paths.phpldapadminconfig = os.path.join(paths.private_dir, 
240                                             "phpldapadmin-config.php")
241     paths.ldapdir = os.path.join(paths.private_dir, 
242                                  "ldap")
243     paths.slapdconf = os.path.join(paths.ldapdir, 
244                                    "slapd.conf")
245     paths.modulesconf = os.path.join(paths.ldapdir, 
246                                      "modules.conf")
247     paths.memberofconf = os.path.join(paths.ldapdir, 
248                                       "memberof.conf")
249     paths.fedoradsinf = os.path.join(paths.ldapdir, 
250                                    "fedorads.inf")
251     paths.fedoradspartitions = os.path.join(paths.ldapdir, 
252                                             "fedorads-partitions.ldif")
253     paths.hklm = "hklm.ldb"
254     paths.hkcr = "hkcr.ldb"
255     paths.hkcu = "hkcu.ldb"
256     paths.hku = "hku.ldb"
257     paths.hkpd = "hkpd.ldb"
258     paths.hkpt = "hkpt.ldb"
259
260     paths.sysvol = lp.get("path", "sysvol")
261
262     paths.netlogon = lp.get("path", "netlogon")
263
264     return paths
265
266 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None, serverrole=None,
267               rootdn=None, domaindn=None, configdn=None, schemadn=None, sitename=None):
268
269     if hostname is None:
270         hostname = socket.gethostname().split(".")[0].lower()
271
272     netbiosname = hostname.upper()
273     if not valid_netbios_name(netbiosname):
274         raise InvalidNetbiosName(netbiosname)
275
276     hostname = hostname.lower()
277
278     if dnsdomain is None:
279         dnsdomain = lp.get("realm")
280
281     if serverrole is None:
282         serverrole = lp.get("server role")
283
284     assert dnsdomain is not None
285     realm = dnsdomain.upper()
286
287     if lp.get("realm").upper() != realm:
288         raise Exception("realm '%s' in %s must match chosen realm '%s'" %
289                         (lp.get("realm"), smbconf, realm))
290     
291     dnsdomain = dnsdomain.lower()
292
293     if (serverrole == "domain controller"):
294         if domain is None:
295             domain = lp.get("workgroup")
296         if domaindn is None:
297             domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
298         if lp.get("workgroup").upper() != domain.upper():
299             raise Error("workgroup '%s' in smb.conf must match chosen domain '%s'",
300                         lp.get("workgroup"), domain)
301     else:
302         domain = netbiosname
303         if domaindn is None:
304             domaindn = "CN=" + netbiosname
305         
306     assert domain is not None
307     domain = domain.upper()
308     if not valid_netbios_name(domain):
309         raise InvalidNetbiosName(domain)
310         
311     if rootdn is None:
312        rootdn = domaindn
313        
314     if configdn is None:
315         configdn = "CN=Configuration," + rootdn
316     if schemadn is None:
317         schemadn = "CN=Schema," + configdn
318
319     if sitename is None:
320         sitename=DEFAULTSITE
321
322     names = ProvisionNames()
323     names.rootdn = rootdn
324     names.domaindn = domaindn
325     names.configdn = configdn
326     names.schemadn = schemadn
327     names.ldapmanagerdn = "CN=Manager," + rootdn
328     names.dnsdomain = dnsdomain
329     names.domain = domain
330     names.realm = realm
331     names.netbiosname = netbiosname
332     names.hostname = hostname
333     names.sitename = sitename
334     
335     return names
336     
337
338 def load_or_make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, targetdir):
339     if targetdir is not None:
340         if not os.path.exists(targetdir):
341             os.mkdir(targetdir)
342         if not os.path.exists(os.path.join(targetdir, "etc")):
343            os.mkdir(os.path.join(targetdir, "etc"))
344
345         smbconf = os.path.join(targetdir, "etc", "smb.conf")
346
347     # only install a new smb.conf if there isn't one there already
348
349     if not os.path.exists(smbconf):
350         if hostname is None:
351             hostname = socket.gethostname().split(".")[0].lower()
352
353         if serverrole is None:
354             serverrole = "standalone"
355
356         assert serverrole in ("domain controller", "member server", "standalone")
357         if serverrole == "domain controller":
358             smbconfsuffix = "dc"
359         elif serverrole == "member server":
360             smbconfsuffix = "member"
361         elif serverrole == "standalone":
362             smbconfsuffix = "standalone"
363
364         assert domain is not None
365         assert realm is not None
366
367         default_lp = param.LoadParm()
368         #Load non-existant file
369         default_lp.load(smbconf)
370         
371         if targetdir is not None:
372             privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
373             lockdir_line = "lock dir = " + os.path.abspath(targetdir)
374
375             default_lp.set("lock dir", os.path.abspath(targetdir))
376         else:
377             privatedir_line = ""
378             lockdir_line = ""
379
380         sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
381         netlogon = os.path.join(sysvol, realm.lower(), "scripts")
382
383         setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix), 
384                    smbconf, {
385                 "HOSTNAME": hostname,
386                 "DOMAIN": domain,
387                 "REALM": realm,
388                 "SERVERROLE": serverrole,
389                 "NETLOGONPATH": netlogon,
390                 "SYSVOLPATH": sysvol,
391                 "PRIVATEDIR_LINE": privatedir_line,
392                 "LOCKDIR_LINE": lockdir_line
393                 })
394
395     lp = param.LoadParm()
396     lp.load(smbconf)
397
398     return lp
399
400 def setup_name_mappings(ldb, sid, domaindn, root, nobody, nogroup, users, 
401                         wheel, backup):
402     """setup reasonable name mappings for sam names to unix names.
403     
404     :param ldb: SamDB object.
405     :param sid: The domain sid.
406     :param domaindn: The domain DN.
407     :param root: Name of the UNIX root user.
408     :param nobody: Name of the UNIX nobody user.
409     :param nogroup: Name of the unix nobody group.
410     :param users: Name of the unix users group.
411     :param wheel: Name of the wheel group (users that can become root).
412     :param backup: Name of the backup group."""
413     # add some foreign sids if they are not present already
414     ldb.add_foreign(domaindn, "S-1-5-7", "Anonymous")
415     ldb.add_foreign(domaindn, "S-1-1-0", "World")
416     ldb.add_foreign(domaindn, "S-1-5-2", "Network")
417     ldb.add_foreign(domaindn, "S-1-5-18", "System")
418     ldb.add_foreign(domaindn, "S-1-5-11", "Authenticated Users")
419
420     # some well known sids
421     ldb.setup_name_mapping(domaindn, "S-1-5-7", nobody)
422     ldb.setup_name_mapping(domaindn, "S-1-1-0", nogroup)
423     ldb.setup_name_mapping(domaindn, "S-1-5-2", nogroup)
424     ldb.setup_name_mapping(domaindn, "S-1-5-18", root)
425     ldb.setup_name_mapping(domaindn, "S-1-5-11", users)
426     ldb.setup_name_mapping(domaindn, "S-1-5-32-544", wheel)
427     ldb.setup_name_mapping(domaindn, "S-1-5-32-545", users)
428     ldb.setup_name_mapping(domaindn, "S-1-5-32-546", nogroup)
429     ldb.setup_name_mapping(domaindn, "S-1-5-32-551", backup)
430
431     # and some well known domain rids
432     ldb.setup_name_mapping(domaindn, sid + "-500", root)
433     ldb.setup_name_mapping(domaindn, sid + "-518", wheel)
434     ldb.setup_name_mapping(domaindn, sid + "-519", wheel)
435     ldb.setup_name_mapping(domaindn, sid + "-512", wheel)
436     ldb.setup_name_mapping(domaindn, sid + "-513", users)
437     ldb.setup_name_mapping(domaindn, sid + "-520", wheel)
438
439
440 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info, 
441                            credentials, names,
442                            serverrole, ldap_backend=None, 
443                            ldap_backend_type=None, erase=False):
444     """Setup the partitions for the SAM database. 
445     
446     Alternatively, provision() may call this, and then populate the database.
447     
448     :note: This will wipe the Sam Database!
449     
450     :note: This function always removes the local SAM LDB file. The erase 
451         parameter controls whether to erase the existing data, which 
452         may not be stored locally but in LDAP.
453     """
454     assert session_info is not None
455
456     samdb = SamDB(samdb_path, session_info=session_info, 
457                   credentials=credentials, lp=lp)
458
459     # Wipes the database
460     try:
461         samdb.erase()
462     except:
463         os.unlink(samdb_path)
464
465     samdb = SamDB(samdb_path, session_info=session_info, 
466                   credentials=credentials, lp=lp)
467
468     #Add modules to the list to activate them by default
469     #beware often order is important
470     #
471     # Some Known ordering constraints:
472     # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
473     # - objectclass must be before password_hash, because password_hash checks
474     #   that the objectclass is of type person (filled in by objectclass
475     #   module when expanding the objectclass list)
476     # - partition must be last
477     # - each partition has its own module list then
478     modules_list = ["rootdse",
479                     "paged_results",
480                     "ranged_results",
481                     "anr",
482                     "server_sort",
483                     "extended_dn",
484                     "asq",
485                     "rdn_name",
486                     "objectclass",
487                     "samldb",
488                     "kludge_acl",
489                     "operational"]
490     tdb_modules_list = [
491                     "subtree_rename",
492                     "subtree_delete",
493                     "linked_attributes"]
494     modules_list2 = ["show_deleted",
495                     "partition"]
496  
497     domaindn_ldb = "users.ldb"
498     if ldap_backend is not None:
499         domaindn_ldb = ldap_backend
500     configdn_ldb = "configuration.ldb"
501     if ldap_backend is not None:
502         configdn_ldb = ldap_backend
503     schemadn_ldb = "schema.ldb"
504     if ldap_backend is not None:
505         schema_ldb = ldap_backend
506         schemadn_ldb = ldap_backend
507         
508     if ldap_backend_type == "fedora-ds":
509         backend_modules = ["nsuniqueid", "paged_searches"]
510         # We can handle linked attributes here, as we don't have directory-side subtree operations
511         tdb_modules_list = ["linked_attributes"]
512     elif ldap_backend_type == "openldap":
513         backend_modules = ["normalise", "entryuuid", "paged_searches"]
514         # OpenLDAP handles subtree renames, so we don't want to do any of these things
515         tdb_modules_list = None
516     elif serverrole == "domain controller":
517         backend_modules = ["repl_meta_data"]
518     else:
519         backend_modules = ["objectguid"]
520
521     if tdb_modules_list is None:
522         tdb_modules_list_as_string = ""
523     else:
524         tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
525         
526     samdb.transaction_start()
527     try:
528         setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
529                 "SCHEMADN": names.schemadn, 
530                 "SCHEMADN_LDB": schemadn_ldb,
531                 "SCHEMADN_MOD2": ",objectguid",
532                 "CONFIGDN": names.configdn,
533                 "CONFIGDN_LDB": configdn_ldb,
534                 "DOMAINDN": names.domaindn,
535                 "DOMAINDN_LDB": domaindn_ldb,
536                 "SCHEMADN_MOD": "schema_fsmo,instancetype",
537                 "CONFIGDN_MOD": "naming_fsmo,instancetype",
538                 "DOMAINDN_MOD": "pdc_fsmo,password_hash,instancetype",
539                 "MODULES_LIST": ",".join(modules_list),
540                 "TDB_MODULES_LIST": tdb_modules_list_as_string,
541                 "MODULES_LIST2": ",".join(modules_list2),
542                 "BACKEND_MOD": ",".join(backend_modules),
543         })
544
545     except:
546         samdb.transaction_cancel()
547         raise
548
549     samdb.transaction_commit()
550     
551     samdb = SamDB(samdb_path, session_info=session_info, 
552                   credentials=credentials, lp=lp)
553
554     samdb.transaction_start()
555     try:
556         message("Setting up sam.ldb attributes")
557         samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
558
559         message("Setting up sam.ldb rootDSE")
560         setup_samdb_rootdse(samdb, setup_path, names.schemadn, names.domaindn, names.hostname, 
561                             names.dnsdomain, names.realm, names.rootdn, names.configdn, names.netbiosname,
562                             names.sitename)
563
564         if erase:
565             message("Erasing data from partitions")
566             samdb.erase_partitions()
567
568     except:
569         samdb.transaction_cancel()
570         raise
571
572     samdb.transaction_commit()
573     
574     return samdb
575
576
577 def secretsdb_become_dc(secretsdb, setup_path, domain, realm, dnsdomain, 
578                         netbiosname, domainsid, keytab_path, samdb_url, 
579                         dns_keytab_path, dnspass, machinepass):
580     """Add DC-specific bits to a secrets database.
581     
582     :param secretsdb: Ldb Handle to the secrets database
583     :param setup_path: Setup path function
584     :param machinepass: Machine password
585     """
586     setup_ldb(secretsdb, setup_path("secrets_dc.ldif"), { 
587             "MACHINEPASS_B64": b64encode(machinepass),
588             "DOMAIN": domain,
589             "REALM": realm,
590             "DNSDOMAIN": dnsdomain,
591             "DOMAINSID": str(domainsid),
592             "SECRETS_KEYTAB": keytab_path,
593             "NETBIOSNAME": netbiosname,
594             "SAM_LDB": samdb_url,
595             "DNS_KEYTAB": dns_keytab_path,
596             "DNSPASS_B64": b64encode(dnspass),
597             })
598
599
600 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
601     """Setup the secrets database.
602
603     :param path: Path to the secrets database.
604     :param setup_path: Get the path to a setup file.
605     :param session_info: Session info.
606     :param credentials: Credentials
607     :param lp: Loadparm context
608     :return: LDB handle for the created secrets database
609     """
610     if os.path.exists(path):
611         os.unlink(path)
612     secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
613                       lp=lp)
614     secrets_ldb.erase()
615     secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
616     secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
617                       lp=lp)
618     secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
619     return secrets_ldb
620
621
622 def setup_templatesdb(path, setup_path, session_info, credentials, lp):
623     """Setup the templates database.
624
625     :param path: Path to the database.
626     :param setup_path: Function for obtaining the path to setup files.
627     :param session_info: Session info
628     :param credentials: Credentials
629     :param lp: Loadparm context
630     """
631     templates_ldb = SamDB(path, session_info=session_info,
632                           credentials=credentials, lp=lp)
633     templates_ldb.erase()
634     templates_ldb.load_ldif_file_add(setup_path("provision_templates.ldif"))
635
636
637 def setup_registry(path, setup_path, session_info, credentials, lp):
638     """Setup the registry.
639     
640     :param path: Path to the registry database
641     :param setup_path: Function that returns the path to a setup.
642     :param session_info: Session information
643     :param credentials: Credentials
644     :param lp: Loadparm context
645     """
646     reg = registry.Registry()
647     hive = registry.open_ldb(path, session_info=session_info, 
648                          credentials=credentials, lp_ctx=lp)
649     reg.mount_hive(hive, "HKEY_LOCAL_MACHINE")
650     provision_reg = setup_path("provision.reg")
651     assert os.path.exists(provision_reg)
652     reg.diff_apply(provision_reg)
653
654 def setup_idmapdb(path, setup_path, session_info, credentials, lp):
655     """Setup the idmap database.
656
657     :param path: path to the idmap database
658     :param setup_path: Function that returns a path to a setup file
659     :param session_info: Session information
660     :param credentials: Credentials
661     :param lp: Loadparm context
662     """
663     if os.path.exists(path):
664         os.unlink(path)
665
666     idmap_ldb = Ldb(path, session_info=session_info, credentials=credentials,
667                     lp=lp)
668
669     idmap_ldb.erase()
670     idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
671     return idmap_ldb
672
673 def setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, hostname, 
674                         dnsdomain, realm, rootdn, configdn, netbiosname,
675                         sitename):
676     """Setup the SamDB rootdse.
677
678     :param samdb: Sam Database handle
679     :param setup_path: Obtain setup path
680     """
681     setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
682         "SCHEMADN": schemadn, 
683         "NETBIOSNAME": netbiosname,
684         "DNSDOMAIN": dnsdomain,
685         "DEFAULTSITE": sitename,
686         "REALM": realm,
687         "DNSNAME": "%s.%s" % (hostname, dnsdomain),
688         "DOMAINDN": domaindn,
689         "ROOTDN": rootdn,
690         "CONFIGDN": configdn,
691         "VERSION": samba.version(),
692         })
693         
694
695 def setup_self_join(samdb, names,
696                     machinepass, dnspass, 
697                     domainsid, invocationid, setup_path,
698                     policyguid, hostguid=None):
699     """Join a host to its own domain."""
700     if hostguid is not None:
701         hostguid_add = "objectGUID: %s" % hostguid
702     else:
703         hostguid_add = ""
704
705     setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), { 
706               "CONFIGDN": names.configdn, 
707               "SCHEMADN": names.schemadn,
708               "DOMAINDN": names.domaindn,
709               "INVOCATIONID": invocationid,
710               "NETBIOSNAME": names.netbiosname,
711               "DEFAULTSITE": names.sitename,
712               "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
713               "MACHINEPASS_B64": b64encode(machinepass),
714               "DNSPASS_B64": b64encode(dnspass),
715               "REALM": names.realm,
716               "DOMAIN": names.domain,
717               "HOSTGUID_ADD": hostguid_add,
718               "DNSDOMAIN": names.dnsdomain})
719     setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), { 
720               "POLICYGUID": policyguid,
721               "DNSDOMAIN": names.dnsdomain,
722               "DOMAINSID": str(domainsid),
723               "DOMAINDN": names.domaindn})
724
725
726 def setup_samdb(path, setup_path, session_info, credentials, lp, 
727                 names, message, 
728                 domainsid, aci, domainguid, policyguid, 
729                 fill, adminpass, krbtgtpass, 
730                 machinepass, hostguid, invocationid, dnspass,
731                 serverrole, ldap_backend=None, 
732                 ldap_backend_type=None):
733     """Setup a complete SAM Database.
734     
735     :note: This will wipe the main SAM database file!
736     """
737
738     erase = (fill != FILL_DRS)
739
740     # Also wipes the database
741     setup_samdb_partitions(path, setup_path, message=message, lp=lp,
742                            credentials=credentials, session_info=session_info,
743                            names=names, 
744                            ldap_backend=ldap_backend, serverrole=serverrole,
745                            ldap_backend_type=ldap_backend_type, erase=erase)
746
747     samdb = SamDB(path, session_info=session_info, 
748                   credentials=credentials, lp=lp)
749
750     if fill == FILL_DRS:
751        # We want to finish here, but setup the index before we do so
752         message("Setting up sam.ldb index")
753         samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
754         return samdb
755
756     message("Pre-loading the Samba 4 and AD schema")
757     samdb = SamDB(path, session_info=session_info, 
758                   credentials=credentials, lp=lp)
759     samdb.set_domain_sid(domainsid)
760     if serverrole == "domain controller":
761         samdb.set_invocation_id(invocationid)
762
763     load_schema(setup_path, samdb, names.schemadn, names.netbiosname, names.configdn, names.sitename)
764
765     samdb.transaction_start()
766         
767     try:
768         message("Adding DomainDN: %s (permitted to fail)" % names.domaindn)
769         if serverrole == "domain controller":
770             domain_oc = "domainDNS"
771         else:
772             domain_oc = "samba4LocalDomain"
773
774         setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
775             "DOMAINDN": names.domaindn,
776             "ACI": aci,
777             "DOMAIN_OC": domain_oc
778             })
779
780         message("Modifying DomainDN: " + names.domaindn + "")
781         if domainguid is not None:
782             domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
783         else:
784             domainguid_mod = ""
785
786         setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
787             "LDAPTIME": timestring(int(time.time())),
788             "DOMAINSID": str(domainsid),
789             "SCHEMADN": names.schemadn, 
790             "NETBIOSNAME": names.netbiosname,
791             "DEFAULTSITE": names.sitename,
792             "CONFIGDN": names.configdn,
793             "POLICYGUID": policyguid,
794             "DOMAINDN": names.domaindn,
795             "DOMAINGUID_MOD": domainguid_mod,
796             })
797
798         message("Adding configuration container (permitted to fail)")
799         setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
800             "CONFIGDN": names.configdn, 
801             "ACI": aci,
802             "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb",
803             })
804         message("Modifying configuration container")
805         setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
806             "CONFIGDN": names.configdn, 
807             "SCHEMADN": names.schemadn,
808             })
809
810         message("Adding schema container (permitted to fail)")
811         setup_add_ldif(samdb, setup_path("provision_schema_basedn.ldif"), {
812             "SCHEMADN": names.schemadn,
813             "ACI": aci,
814             "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb"
815             })
816         message("Modifying schema container")
817         setup_modify_ldif(samdb, 
818             setup_path("provision_schema_basedn_modify.ldif"), {
819             "SCHEMADN": names.schemadn,
820             "NETBIOSNAME": names.netbiosname,
821             "DEFAULTSITE": names.sitename,
822             "CONFIGDN": names.configdn,
823             })
824
825         message("Setting up sam.ldb Samba4 schema")
826         setup_add_ldif(samdb, setup_path("schema_samba4.ldif"), 
827                        {"SCHEMADN": names.schemadn })
828         message("Setting up sam.ldb AD schema")
829         setup_add_ldif(samdb, setup_path("schema.ldif"), 
830                        {"SCHEMADN": names.schemadn})
831
832         message("Setting up sam.ldb configuration data")
833         setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
834             "CONFIGDN": names.configdn,
835             "NETBIOSNAME": names.netbiosname,
836             "DEFAULTSITE": names.sitename,
837             "DNSDOMAIN": names.dnsdomain,
838             "DOMAIN": names.domain,
839             "SCHEMADN": names.schemadn,
840             "DOMAINDN": names.domaindn,
841             })
842
843         message("Setting up display specifiers")
844         setup_add_ldif(samdb, setup_path("display_specifiers.ldif"), 
845                        {"CONFIGDN": names.configdn})
846
847         message("Adding users container (permitted to fail)")
848         setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
849                 "DOMAINDN": names.domaindn})
850         message("Modifying users container")
851         setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
852                 "DOMAINDN": names.domaindn})
853         message("Adding computers container (permitted to fail)")
854         setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
855                 "DOMAINDN": names.domaindn})
856         message("Modifying computers container")
857         setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
858                 "DOMAINDN": names.domaindn})
859         message("Setting up sam.ldb data")
860         setup_add_ldif(samdb, setup_path("provision.ldif"), {
861             "DOMAINDN": names.domaindn,
862             "NETBIOSNAME": names.netbiosname,
863             "DEFAULTSITE": names.sitename,
864             "CONFIGDN": names.configdn,
865             })
866
867         if fill == FILL_FULL:
868             message("Setting up sam.ldb users and groups")
869             setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
870                 "DOMAINDN": names.domaindn,
871                 "DOMAINSID": str(domainsid),
872                 "CONFIGDN": names.configdn,
873                 "ADMINPASS_B64": b64encode(adminpass),
874                 "KRBTGTPASS_B64": b64encode(krbtgtpass),
875                 })
876
877             if serverrole == "domain controller":
878                 message("Setting up self join")
879                 setup_self_join(samdb, names=names, invocationid=invocationid, 
880                                 dnspass=dnspass,  
881                                 machinepass=machinepass, 
882                                 domainsid=domainsid, policyguid=policyguid,
883                                 hostguid=hostguid, 
884                                 setup_path=setup_path)
885
886     #We want to setup the index last, as adds are faster unindexed
887         message("Setting up sam.ldb index")
888         samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
889     except:
890         samdb.transaction_cancel()
891         raise
892
893     samdb.transaction_commit()
894     return samdb
895
896
897 FILL_FULL = "FULL"
898 FILL_NT4SYNC = "NT4SYNC"
899 FILL_DRS = "DRS"
900
901 def provision(setup_dir, message, session_info, 
902               credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL, realm=None, 
903               rootdn=None, domaindn=None, schemadn=None, configdn=None,
904               domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None, 
905               hostguid=None, adminpass=None, krbtgtpass=None, domainguid=None, 
906               policyguid=None, invocationid=None, machinepass=None, 
907               dnspass=None, root=None, nobody=None, nogroup=None, users=None, 
908               wheel=None, backup=None, aci=None, serverrole=None, 
909               ldap_backend=None, ldap_backend_type=None, sitename=None):
910     """Provision samba4
911     
912     :note: caution, this wipes all existing data!
913     """
914
915     def setup_path(file):
916         return os.path.join(setup_dir, file)
917
918     if domainsid is None:
919         domainsid = security.random_sid()
920     else:
921         domainsid = security.Sid(domainsid)
922
923     if policyguid is None:
924         policyguid = uuid.random()
925     if adminpass is None:
926         adminpass = misc.random_password(12)
927     if krbtgtpass is None:
928         krbtgtpass = misc.random_password(12)
929     if machinepass is None:
930         machinepass  = misc.random_password(12)
931     if dnspass is None:
932         dnspass = misc.random_password(12)
933     if root is None:
934         root = findnss(pwd.getpwnam, ["root"])[0]
935     if nobody is None:
936         nobody = findnss(pwd.getpwnam, ["nobody"])[0]
937     if nogroup is None:
938         nogroup = findnss(grp.getgrnam, ["nogroup", "nobody"])[0]
939     if users is None:
940         users = findnss(grp.getgrnam, ["users", "guest", "other", "unknown", 
941                         "usr"])[0]
942     if wheel is None:
943         wheel = findnss(grp.getgrnam, ["wheel", "root", "staff", "adm"])[0]
944     if backup is None:
945         backup = findnss(grp.getgrnam, ["backup", "wheel", "root", "staff"])[0]
946     if aci is None:
947         aci = "# no aci for local ldb"
948
949     lp = load_or_make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, targetdir)
950
951     names = guess_names(lp=lp, hostname=hostname, domain=domain, 
952                         dnsdomain=realm, serverrole=serverrole, sitename=sitename,
953                         rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn)
954
955     paths = provision_paths_from_lp(lp, names.dnsdomain)
956
957     if hostip is None:
958         hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
959
960     if hostip6 is None:
961         try:
962             hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
963         except socket.gaierror: pass
964
965     if serverrole is None:
966         serverrole = lp.get("server role")
967
968     assert serverrole in ("domain controller", "member server", "standalone")
969     if invocationid is None and serverrole == "domain controller":
970         invocationid = uuid.random()
971
972     if not os.path.exists(paths.private_dir):
973         os.mkdir(paths.private_dir)
974
975     ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
976     
977     if ldap_backend is not None:
978         if ldap_backend == "ldapi":
979             # provision-backend will set this path suggested slapd command line / fedorads.inf
980             ldap_backend = "ldapi://" % urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
981              
982     # only install a new shares config db if there is none
983     if not os.path.exists(paths.shareconf):
984         message("Setting up share.ldb")
985         share_ldb = Ldb(paths.shareconf, session_info=session_info, 
986                         credentials=credentials, lp=lp)
987         share_ldb.load_ldif_file_add(setup_path("share.ldif"))
988
989      
990     message("Setting up secrets.ldb")
991     secrets_ldb = setup_secretsdb(paths.secrets, setup_path, 
992                                   session_info=session_info, 
993                                   credentials=credentials, lp=lp)
994
995     message("Setting up the registry")
996     setup_registry(paths.hklm, setup_path, session_info, 
997                    credentials=credentials, lp=lp)
998
999     message("Setting up templates db")
1000     setup_templatesdb(paths.templates, setup_path, session_info=session_info, 
1001                       credentials=credentials, lp=lp)
1002
1003     message("Setting up idmap db")
1004     setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1005                   credentials=credentials, lp=lp)
1006
1007     samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info, 
1008                         credentials=credentials, lp=lp, names=names,
1009                         message=message, 
1010                         domainsid=domainsid, 
1011                         aci=aci, domainguid=domainguid, policyguid=policyguid, 
1012                         fill=samdb_fill, 
1013                         adminpass=adminpass, krbtgtpass=krbtgtpass,
1014                         hostguid=hostguid, invocationid=invocationid, 
1015                         machinepass=machinepass, dnspass=dnspass,
1016                         serverrole=serverrole, ldap_backend=ldap_backend, 
1017                         ldap_backend_type=ldap_backend_type)
1018
1019     if lp.get("server role") == "domain controller":
1020        policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies", 
1021                                   "{" + policyguid + "}")
1022        os.makedirs(policy_path, 0755)
1023        os.makedirs(os.path.join(policy_path, "Machine"), 0755)
1024        os.makedirs(os.path.join(policy_path, "User"), 0755)
1025        if not os.path.isdir(paths.netlogon):
1026             os.makedirs(paths.netlogon, 0755)
1027        secrets_ldb = Ldb(paths.secrets, session_info=session_info, 
1028                          credentials=credentials, lp=lp)
1029        secretsdb_become_dc(secrets_ldb, setup_path, domain=domain, realm=names.realm,
1030                            netbiosname=names.netbiosname, domainsid=domainsid, 
1031                            keytab_path=paths.keytab, samdb_url=paths.samdb, 
1032                            dns_keytab_path=paths.dns_keytab, dnspass=dnspass, 
1033                            machinepass=machinepass, dnsdomain=names.dnsdomain)
1034
1035     if samdb_fill == FILL_FULL:
1036         setup_name_mappings(samdb, str(domainsid), names.domaindn, root=root, 
1037                             nobody=nobody, nogroup=nogroup, wheel=wheel, 
1038                             users=users, backup=backup)
1039    
1040         message("Compleating sam.ldb setup by marking as synchronized")
1041         setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1042
1043         # Only make a zone file on the first DC, it should be replicated with DNS replication
1044         if serverrole == "domain controller":
1045             samdb = SamDB(paths.samdb, session_info=session_info, 
1046                       credentials=credentials, lp=lp)
1047
1048             domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1049             assert isinstance(domainguid, str)
1050             hostguid = samdb.searchone(basedn=domaindn, attribute="objectGUID",
1051                                        expression="(&(objectClass=computer)(cn=%s))" % names.hostname,
1052                                        scope=SCOPE_SUBTREE)
1053             assert isinstance(hostguid, str)
1054             
1055             create_zone_file(paths.dns, setup_path, samdb, 
1056                              hostname=names.hostname, hostip=hostip,
1057                              hostip6=hostip6, dnsdomain=names.dnsdomain,
1058                              domaindn=names.domaindn, dnspass=dnspass, realm=names.realm, 
1059                              domainguid=domainguid, hostguid=hostguid)
1060             message("Please install the zone located in %s into your DNS server" % paths.dns)
1061             
1062     create_phpldapadmin_config(paths.phpldapadminconfig, setup_path, 
1063                                ldapi_url)
1064
1065     message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1066
1067     message("Once the above files are installed, your server will be ready to use")
1068     message("Server Type:    %s" % serverrole)
1069     message("Hostname:       %s" % names.hostname)
1070     message("NetBIOS Domain: %s" % names.domain)
1071     message("DNS Domain:     %s" % names.dnsdomain)
1072     message("DOMAIN SID:     %s" % str(domainsid))
1073     message("Admin password: %s" % adminpass)
1074
1075     result = ProvisionResult()
1076     result.domaindn = domaindn
1077     result.paths = paths
1078     result.lp = lp
1079     result.samdb = samdb
1080     return result
1081
1082 def provision_become_dc(setup_dir=None,
1083                         smbconf=None, targetdir=None, realm=None, 
1084                         rootdn=None, domaindn=None, schemadn=None, configdn=None,
1085                         domain=None, hostname=None, domainsid=None, 
1086                         hostguid=None, adminpass=None, krbtgtpass=None, domainguid=None, 
1087                         policyguid=None, invocationid=None, machinepass=None, 
1088                         dnspass=None, root=None, nobody=None, nogroup=None, users=None, 
1089                         wheel=None, backup=None, aci=None, serverrole=None, 
1090                         ldap_backend=None, ldap_backend_type=None, sitename=DEFAULTSITE):
1091
1092     def message(text):
1093         """print a message if quiet is not set."""
1094         print text
1095
1096     provision(setup_dir, message, system_session(), None,
1097               smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS, realm=realm, 
1098               rootdn=rootdn, domaindn=domaindn, schemadn=schemadn, configdn=configdn, 
1099               domain=domain, hostname=hostname, hostip="127.0.0.1", domainsid=domainsid, machinepass=machinepass, serverrole="domain controller", sitename=sitename);
1100     
1101
1102 def setup_db_config(setup_path, file, dbdir):
1103     if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
1104         os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700);
1105     if not os.path.isdir(os.path.join(dbdir, "tmp")):
1106         os.makedirs(os.path.join(dbdir, "tmp"), 0700);
1107     
1108     setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
1109                {"LDAPDBDIR": dbdir})
1110     
1111
1112
1113 def provision_backend(setup_dir=None, message=None,
1114                       smbconf=None, targetdir=None, realm=None, 
1115                       rootdn=None, domaindn=None, schemadn=None, configdn=None,
1116                       domain=None, hostname=None, adminpass=None, root=None, serverrole=None, 
1117                       ldap_backend_type=None):
1118
1119     def setup_path(file):
1120         return os.path.join(setup_dir, file)
1121
1122     if hostname is None:
1123         hostname = socket.gethostname().split(".")[0].lower()
1124
1125     if root is None:
1126         root = findnss(pwd.getpwnam, ["root"])[0]
1127
1128     lp = load_or_make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, targetdir)
1129
1130     names = guess_names(lp=lp, hostname=hostname, domain=domain, 
1131                         dnsdomain=realm, serverrole=serverrole, 
1132                         rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn)
1133
1134     paths = provision_paths_from_lp(lp, names.dnsdomain)
1135
1136     if not os.path.isdir(paths.ldapdir):
1137         os.makedirs(paths.ldapdir)
1138     schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
1139     try:
1140         os.unlink(schemadb_path)
1141     except:
1142         pass
1143
1144     schemadb = Ldb(schemadb_path, lp=lp)
1145  
1146     setup_add_ldif(schemadb, setup_path("provision_schema_basedn.ldif"), 
1147                    {"SCHEMADN": names.schemadn,
1148                     "ACI": "#",
1149                     "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb"
1150                     })
1151     setup_modify_ldif(schemadb, 
1152                       setup_path("provision_schema_basedn_modify.ldif"), \
1153                           {"SCHEMADN": names.schemadn,
1154                            "NETBIOSNAME": names.netbiosname,
1155                            "DEFAULTSITE": DEFAULTSITE,
1156                            "CONFIGDN": names.configdn,
1157                            })
1158     
1159     setup_add_ldif(schemadb, setup_path("schema_samba4.ldif"), 
1160                    {"SCHEMADN": names.schemadn })
1161     setup_add_ldif(schemadb, setup_path("schema.ldif"), 
1162                    {"SCHEMADN": names.schemadn})
1163
1164     if ldap_backend_type == "fedora-ds":
1165         setup_file(setup_path("fedora-ds.inf"), paths.fedoradsinf, 
1166                    {"ROOT": root,
1167                     "HOSTNAME": hostname,
1168                     "DNSDOMAIN": names.dnsdomain,
1169                     "LDAPDIR": paths.ldapdir,
1170                     "DOMAINDN": names.domaindn,
1171                     "LDAPMANAGERDN": names.ldapmanagerdn,
1172                     "LDAPMANAGERPASS": adminpass, 
1173                     "SERVERPORT": ""})
1174         
1175         setup_file(setup_path("fedora-partitions.ldif"), paths.fedoradspartitions, 
1176                    {"CONFIGDN": names.configdn,
1177                     "SCHEMADN": names.schemadn,
1178                     })
1179         
1180         setup_file(setup_path("fedora-partitions.ldif"), paths.fedoradspartitions, 
1181                    {"CONFIGDN": names.configdn,
1182                     "SCHEMADN": names.schemadn,
1183                     })
1184         mapping = "schema-map-fedora-ds-1.0"
1185         backend_schema = "99_ad.ldif"
1186     elif ldap_backend_type == "openldap":
1187         attrs = ["linkID", "lDAPDisplayName"]
1188         res = schemadb.search(expression="(&(&(linkID=*)(!(linkID:1.2.840.113556.1.4.803:=1)))(objectclass=attributeSchema))", base=names.schemadn, scope=SCOPE_SUBTREE, attrs=attrs);
1189
1190         memberof_config = "# Generated from schema in " + schemadb_path + "\n";
1191         refint_attributes = "";
1192         for i in range (0, len(res)):
1193             linkid = res[i]["linkID"][0]
1194             linkid = str(int(linkid) + 1)
1195             expression = "(&(objectclass=attributeSchema)(linkID=" + (linkid) + "))"
1196             target = schemadb.searchone(basedn=names.schemadn, 
1197                                         expression=expression, 
1198                                         attribute="lDAPDisplayName", 
1199                                         scope=SCOPE_SUBTREE);
1200             if target is not None:
1201                 refint_attributes = refint_attributes + " " + target + " " + res[i]["lDAPDisplayName"][0];
1202                 memberof_config = memberof_config + """overlay memberof
1203 memberof-dangling error
1204 memberof-refint TRUE
1205 memberof-group-oc top
1206 memberof-member-ad """ + res[i]["lDAPDisplayName"][0] + """
1207 memberof-memberof-ad """ + target + """
1208 memberof-dangling-error 32
1209
1210 """;
1211
1212         memberof_config = memberof_config + """
1213 overlay refint
1214 refint_attributes""" + refint_attributes + "\n";
1215         
1216         setup_file(setup_path("slapd.conf"), paths.slapdconf,
1217                    {"DNSDOMAIN": names.dnsdomain,
1218                     "LDAPDIR": paths.ldapdir,
1219                     "DOMAINDN": names.domaindn,
1220                     "CONFIGDN": names.configdn,
1221                     "SCHEMADN": names.schemadn,
1222                     "LDAPMANAGERDN": names.ldapmanagerdn,
1223                     "LDAPMANAGERPASS": adminpass,
1224                     "MEMBEROF_CONFIG": memberof_config})
1225         setup_file(setup_path("modules.conf"), paths.modulesconf,
1226                    {"REALM": names.realm})
1227         
1228         setup_db_config(setup_path, file, os.path.join(paths.ldapdir, "db", "user"))
1229         setup_db_config(setup_path, file, os.path.join(paths.ldapdir, "db", "config"))
1230         setup_db_config(setup_path, file, os.path.join(paths.ldapdir, "db", "schema"))
1231         mapping = "schema-map-openldap-2.3"
1232         backend_schema = "backend-schema.schema"
1233         
1234
1235         ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
1236         message("Start slapd with: slapd -f " + paths.ldapdir + "/slapd.conf -h " + ldapi_uri)
1237                 
1238
1239     schema_command = "bin/ad2oLschema --option=convert:target=" + ldap_backend_type + " -I " + setup_path(mapping) + " -H tdb://" + schemadb_path + " -O " + os.path.join(paths.ldapdir, backend_schema);
1240
1241     os.system(schema_command)
1242
1243
1244
1245 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1246     """Create a PHP LDAP admin configuration file.
1247
1248     :param path: Path to write the configuration to.
1249     :param setup_path: Function to generate setup paths.
1250     """
1251     setup_file(setup_path("phpldapadmin-config.php"), path, 
1252             {"S4_LDAPI_URI": ldapi_uri})
1253
1254
1255 def create_zone_file(path, setup_path, samdb, dnsdomain, domaindn, 
1256                   hostip, hostip6, hostname, dnspass, realm, domainguid, hostguid):
1257     """Write out a DNS zone file, from the info in the current database.
1258     
1259     :param path: Path of the new file.
1260     :param setup_path": Setup path function.
1261     :param samdb: SamDB object
1262     :param dnsdomain: DNS Domain name
1263     :param domaindn: DN of the Domain
1264     :param hostip: Local IPv4 IP
1265     :param hostip6: Local IPv6 IP
1266     :param hostname: Local hostname
1267     :param dnspass: Password for DNS
1268     :param realm: Realm name
1269     :param domainguid: GUID of the domain.
1270     :param hostguid: GUID of the host.
1271     """
1272     assert isinstance(domainguid, str)
1273
1274     hostip6_base_line = ""
1275     hostip6_host_line = ""
1276
1277     if hostip6 is not None:
1278         hostip6_base_line = "                   IN AAAA " + hostip6
1279         hostip6_host_line = hostname + "                IN AAAA " + hostip6
1280
1281     setup_file(setup_path("provision.zone"), path, {
1282             "DNSPASS_B64": b64encode(dnspass),
1283             "HOSTNAME": hostname,
1284             "DNSDOMAIN": dnsdomain,
1285             "REALM": realm,
1286             "HOSTIP": hostip,
1287             "DOMAINGUID": domainguid,
1288             "DATESTRING": time.strftime("%Y%m%d%H"),
1289             "DEFAULTSITE": DEFAULTSITE,
1290             "HOSTGUID": hostguid,
1291             "HOSTIP6_BASE_LINE": hostip6_base_line,
1292             "HOSTIP6_HOST_LINE": hostip6_host_line,
1293         })
1294
1295 def load_schema(setup_path, samdb, schemadn, netbiosname, configdn, sitename):
1296     """Load schema for the SamDB.
1297     
1298     :param samdb: Load a schema into a SamDB.
1299     :param setup_path: Setup path function.
1300     :param schemadn: DN of the schema
1301     :param netbiosname: NetBIOS name of the host.
1302     :param configdn: DN of the configuration
1303     """
1304     schema_data = open(setup_path("schema.ldif"), 'r').read()
1305     schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
1306     schema_data = substitute_var(schema_data, {"SCHEMADN": schemadn})
1307     head_data = open(setup_path("provision_schema_basedn_modify.ldif"), 'r').read()
1308     head_data = substitute_var(head_data, {
1309                     "SCHEMADN": schemadn,
1310                     "NETBIOSNAME": netbiosname,
1311                     "CONFIGDN": configdn,
1312                     "DEFAULTSITE":sitename 
1313     })
1314     samdb.attach_schema_from_ldif(head_data, schema_data)
1315