s4:provision Handle machine account password changes while keeping keytab
[kamenim/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-2010
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 re
31 import pwd
32 import grp
33 import logging
34 import time
35 import uuid
36 import socket
37 import urllib
38 import shutil
39
40 import ldb
41
42 from samba.auth import system_session, admin_session
43 import samba
44 from samba import version, Ldb, substitute_var, valid_netbios_name
45 from samba import check_all_substituted, read_and_sub_file, setup_file
46 from samba.dsdb import DS_DOMAIN_FUNCTION_2003, DS_DOMAIN_FUNCTION_2008_R2, ENC_ALL_TYPES
47 from samba.dcerpc import security
48 from samba.dcerpc.misc import SEC_CHAN_BDC, SEC_CHAN_WKSTA
49 from samba.idmap import IDmapDB
50 from samba.ms_display_specifiers import read_ms_ldif
51 from samba.ntacls import setntacl, dsacl2fsacl
52 from samba.ndr import ndr_pack,ndr_unpack
53 from samba.provisionbackend import (
54     ExistingBackend,
55     FDSBackend,
56     LDBBackend,
57     OpenLDAPBackend,
58     )
59 import samba.param
60 import samba.registry
61 from samba.schema import Schema
62 from samba.samdb import SamDB
63
64 __docformat__ = "restructuredText"
65 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
66 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04fB984F9"
67
68 def find_setup_dir():
69     """Find the setup directory used by provision."""
70     import sys
71     for prefix in [sys.prefix,
72             os.path.join(os.path.dirname(__file__), "../../../..")]:
73         for suffix in ["share/setup", "share/samba/setup", "setup"]:
74             ret = os.path.join(prefix, suffix)
75             if os.path.isdir(ret):
76                 return ret
77     # In source tree
78     dirname = os.path.dirname(__file__)
79     ret = os.path.join(dirname, "../../../setup")
80     if os.path.isdir(ret):
81         return ret
82     raise Exception("Unable to find setup directory.")
83
84 # descriptors of the naming contexts
85 # hard coded at this point, but will probably be changed when
86 # we enable different fsmo roles
87
88
89 def get_config_descriptor(domain_sid):
90     sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
91            "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
92            "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
93            "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
94            "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
95            "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
96            "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
97            "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
98            "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
99            "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
100            "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
101            "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
102            "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
103            "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
104            "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
105     sec = security.descriptor.from_sddl(sddl, domain_sid)
106     return ndr_pack(sec)
107
108 def get_domain_descriptor(domain_sid):
109     sddl= "O:BAG:BAD:AI(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
110         "(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
111     "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
112     "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
113     "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
114     "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
115     "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
116     "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
117     "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
118     "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
119     "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
120     "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)" \
121     "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)" \
122     "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)" \
123     "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)" \
124     "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
125     "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
126     "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
127     "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
128     "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
129     "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
130     "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;IF)" \
131     "(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)" \
132     "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)" \
133     "(OA;CIIO;RPLCLORC;;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
134     "(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)" \
135     "(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
136     "(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)" \
137     "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
138     "(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)" \
139     "(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)" \
140     "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
141     "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
142     "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
143     "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
144     "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)" \
145     "(OA;CIIO;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \
146     "(A;;RPWPCRCCLCLORCWOWDSW;;;DA)" \
147     "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
148     "(A;;RPRC;;;RU)" \
149     "(A;CI;LC;;;RU)" \
150     "(A;CI;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
151     "(A;;RP;;;WD)" \
152     "(A;;RPLCLORC;;;ED)" \
153     "(A;;RPLCLORC;;;AU)" \
154     "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
155     "S:AI(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
156     "(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
157     "(AU;SA;CR;;;DU)(AU;SA;CR;;;BA)(AU;SA;WPWOWD;;;WD)"
158     sec = security.descriptor.from_sddl(sddl, domain_sid)
159     return ndr_pack(sec)
160
161 DEFAULTSITE = "Default-First-Site-Name"
162 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
163
164 class ProvisionPaths(object):
165
166     def __init__(self):
167         self.shareconf = None
168         self.hklm = None
169         self.hkcu = None
170         self.hkcr = None
171         self.hku = None
172         self.hkpd = None
173         self.hkpt = None
174         self.samdb = None
175         self.idmapdb = None
176         self.secrets = None
177         self.keytab = None
178         self.dns_keytab = None
179         self.dns = None
180         self.winsdb = None
181         self.private_dir = None
182
183
184 class ProvisionNames(object):
185
186     def __init__(self):
187         self.rootdn = None
188         self.domaindn = None
189         self.configdn = None
190         self.schemadn = None
191         self.ldapmanagerdn = None
192         self.dnsdomain = None
193         self.realm = None
194         self.netbiosname = None
195         self.domain = None
196         self.hostname = None
197         self.sitename = None
198         self.smbconf = None
199
200
201 def update_provision_usn(samdb, low, high, replace=False):
202     """Update the field provisionUSN in sam.ldb
203
204     This field is used to track range of USN modified by provision and 
205     upgradeprovision.
206     This value is used afterward by next provision to figure out if 
207     the field have been modified since last provision.
208
209     :param samdb: An LDB object connect to sam.ldb
210     :param low: The lowest USN modified by this upgrade
211     :param high: The highest USN modified by this upgrade
212     :param replace: A boolean indicating if the range should replace any 
213                     existing one or appended (default)
214     """
215
216     tab = []
217     if not replace:
218         entry = samdb.search(expression="(&(dn=@PROVISION)(%s=*))" % \
219                                 LAST_PROVISION_USN_ATTRIBUTE, base="", 
220                                 scope=ldb.SCOPE_SUBTREE,
221                                 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
222         for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
223             tab.append(str(e))
224
225     tab.append("%s-%s" % (low, high))
226     delta = ldb.Message()
227     delta.dn = ldb.Dn(samdb, "@PROVISION")
228     delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
229                                                     ldb.FLAG_MOD_REPLACE,
230                                                     LAST_PROVISION_USN_ATTRIBUTE)
231     samdb.modify(delta)
232
233
234 def set_provision_usn(samdb, low, high):
235     """Set the field provisionUSN in sam.ldb
236     This field is used to track range of USN modified by provision and
237     upgradeprovision.
238     This value is used afterward by next provision to figure out if
239     the field have been modified since last provision.
240
241     :param samdb: An LDB object connect to sam.ldb
242     :param low: The lowest USN modified by this upgrade
243     :param high: The highest USN modified by this upgrade"""
244     tab = []
245     tab.append("%s-%s" % (low, high))
246     delta = ldb.Message()
247     delta.dn = ldb.Dn(samdb, "@PROVISION")
248     delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
249                                                   ldb.FLAG_MOD_ADD,
250                                                   LAST_PROVISION_USN_ATTRIBUTE)
251     samdb.add(delta)
252
253
254 def get_max_usn(samdb,basedn):
255     """ This function return the biggest USN present in the provision
256
257     :param samdb: A LDB object pointing to the sam.ldb
258     :param basedn: A string containing the base DN of the provision
259                     (ie. DC=foo, DC=bar)
260     :return: The biggest USN in the provision"""
261
262     res = samdb.search(expression="objectClass=*",base=basedn,
263                          scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
264                          controls=["search_options:1:2",
265                                    "server_sort:1:1:uSNChanged",
266                                    "paged_results:1:1"])
267     return res[0]["uSNChanged"]
268     
269 def get_last_provision_usn(sam):
270     """Get the lastest USN modified by a provision or an upgradeprovision
271
272     :param sam: An LDB object pointing to the sam.ldb
273     :return an integer corresponding to the highest USN modified by 
274             (upgrade)provision, 0 is this value is unknown"""
275
276     entry = sam.search(expression="(&(dn=@PROVISION)(%s=*))" % \
277                         LAST_PROVISION_USN_ATTRIBUTE,
278                         base="", scope=ldb.SCOPE_SUBTREE,
279                         attrs=[LAST_PROVISION_USN_ATTRIBUTE])
280     if len(entry):
281         range = []
282         idx = 0
283         p = re.compile(r'-')
284         for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
285             tab = p.split(str(r))
286             range.append(tab[0])
287             range.append(tab[1])
288             idx = idx + 1
289         return range
290     else:
291         return None
292
293 class ProvisionResult(object):
294
295     def __init__(self):
296         self.paths = None
297         self.domaindn = None
298         self.lp = None
299         self.samdb = None
300
301
302 def check_install(lp, session_info, credentials):
303     """Check whether the current install seems ok.
304     
305     :param lp: Loadparm context
306     :param session_info: Session information
307     :param credentials: Credentials
308     """
309     if lp.get("realm") == "":
310         raise Exception("Realm empty")
311     samdb = Ldb(lp.get("sam database"), session_info=session_info, 
312             credentials=credentials, lp=lp)
313     if len(samdb.search("(cn=Administrator)")) != 1:
314         raise ProvisioningError("No administrator account found")
315
316
317 def findnss(nssfn, names):
318     """Find a user or group from a list of possibilities.
319     
320     :param nssfn: NSS Function to try (should raise KeyError if not found)
321     :param names: Names to check.
322     :return: Value return by first names list.
323     """
324     for name in names:
325         try:
326             return nssfn(name)
327         except KeyError:
328             pass
329     raise KeyError("Unable to find user/group in %r" % names)
330
331
332 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
333 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
334
335
336 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
337     """Setup a ldb in the private dir.
338     
339     :param ldb: LDB file to import data into
340     :param ldif_path: Path of the LDIF file to load
341     :param subst_vars: Optional variables to subsitute in LDIF.
342     :param nocontrols: Optional list of controls, can be None for no controls
343     """
344     assert isinstance(ldif_path, str)
345     data = read_and_sub_file(ldif_path, subst_vars)
346     ldb.add_ldif(data, controls)
347
348
349 def setup_modify_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
350     """Modify a ldb in the private dir.
351     
352     :param ldb: LDB object.
353     :param ldif_path: LDIF file path.
354     :param subst_vars: Optional dictionary with substitution variables.
355     """
356     data = read_and_sub_file(ldif_path, subst_vars)
357     ldb.modify_ldif(data, controls)
358
359
360 def setup_ldb(ldb, ldif_path, subst_vars):
361     """Import a LDIF a file into a LDB handle, optionally substituting variables.
362
363     :note: Either all LDIF data will be added or none (using transactions).
364
365     :param ldb: LDB file to import into.
366     :param ldif_path: Path to the LDIF file.
367     :param subst_vars: Dictionary with substitution variables.
368     """
369     assert ldb is not None
370     ldb.transaction_start()
371     try:
372         setup_add_ldif(ldb, ldif_path, subst_vars)
373     except:
374         ldb.transaction_cancel()
375         raise
376     else:
377         ldb.transaction_commit()
378
379
380 def provision_paths_from_lp(lp, dnsdomain):
381     """Set the default paths for provisioning.
382
383     :param lp: Loadparm context.
384     :param dnsdomain: DNS Domain name
385     """
386     paths = ProvisionPaths()
387     paths.private_dir = lp.get("private dir")
388
389     # This is stored without path prefix for the "privateKeytab" attribute in
390     # "secrets_dns.ldif".
391     paths.dns_keytab = "dns.keytab"
392
393     paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
394     paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
395     paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
396     paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
397     paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
398     paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
399     paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
400     paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
401     paths.namedconf = os.path.join(paths.private_dir, "named.conf")
402     paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
403     paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
404     paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
405     paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
406     paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
407     paths.phpldapadminconfig = os.path.join(paths.private_dir, 
408                                             "phpldapadmin-config.php")
409     paths.hklm = "hklm.ldb"
410     paths.hkcr = "hkcr.ldb"
411     paths.hkcu = "hkcu.ldb"
412     paths.hku = "hku.ldb"
413     paths.hkpd = "hkpd.ldb"
414     paths.hkpt = "hkpt.ldb"
415     paths.sysvol = lp.get("path", "sysvol")
416     paths.netlogon = lp.get("path", "netlogon")
417     paths.smbconf = lp.configfile
418     return paths
419
420
421 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
422                 serverrole=None, rootdn=None, domaindn=None, configdn=None,
423                 schemadn=None, serverdn=None, sitename=None):
424     """Guess configuration settings to use."""
425
426     if hostname is None:
427         hostname = socket.gethostname().split(".")[0]
428
429     netbiosname = lp.get("netbios name")
430     if netbiosname is None:
431         netbiosname = hostname
432     assert netbiosname is not None
433     netbiosname = netbiosname.upper()
434     if not valid_netbios_name(netbiosname):
435         raise InvalidNetbiosName(netbiosname)
436
437     if dnsdomain is None:
438         dnsdomain = lp.get("realm")
439         if dnsdomain is None or dnsdomain == "":
440             raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
441
442     dnsdomain = dnsdomain.lower()
443
444     if serverrole is None:
445         serverrole = lp.get("server role")
446         if serverrole is None:
447             raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
448
449     serverrole = serverrole.lower()
450
451     realm = dnsdomain.upper()
452
453     if lp.get("realm") == "":
454         raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s.  Please remove the smb.conf file and let provision generate it" % lp.configfile)
455
456     if lp.get("realm").upper() != realm:
457         raise ProvisioningError("guess_names: 'realm=%s' in %s must match chosen realm '%s'!  Please remove the smb.conf file and let provision generate it" % (lp.get("realm").upper(), realm, lp.configfile))
458
459     if lp.get("server role").lower() != serverrole:
460         raise ProvisioningError("guess_names: 'server role=%s' in %s must match chosen server role '%s'!  Please remove the smb.conf file and let provision generate it" % (lp.get("server role").upper(), serverrole, lp.configfile))
461
462     if serverrole == "domain controller":
463         if domain is None:
464             # This will, for better or worse, default to 'WORKGROUP'
465             domain = lp.get("workgroup")
466         domain = domain.upper()
467
468         if lp.get("workgroup").upper() != domain:
469             raise ProvisioningError("guess_names: Workgroup '%s' in %s must match chosen domain '%s'!  Please remove the %s file and let provision generate it" % (lp.get("workgroup").upper(), domain, lp.configfile))
470
471         if domaindn is None:
472             domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
473     else:
474         domain = netbiosname
475         if domaindn is None:
476             domaindn = "DC=" + netbiosname
477         
478     if not valid_netbios_name(domain):
479         raise InvalidNetbiosName(domain)
480         
481     if hostname.upper() == realm:
482         raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
483     if netbiosname == realm:
484         raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
485     if domain == realm:
486         raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
487
488     if rootdn is None:
489        rootdn = domaindn
490        
491     if configdn is None:
492         configdn = "CN=Configuration," + rootdn
493     if schemadn is None:
494         schemadn = "CN=Schema," + configdn
495
496     if sitename is None:
497         sitename=DEFAULTSITE
498
499     names = ProvisionNames()
500     names.rootdn = rootdn
501     names.domaindn = domaindn
502     names.configdn = configdn
503     names.schemadn = schemadn
504     names.ldapmanagerdn = "CN=Manager," + rootdn
505     names.dnsdomain = dnsdomain
506     names.domain = domain
507     names.realm = realm
508     names.netbiosname = netbiosname
509     names.hostname = hostname
510     names.sitename = sitename
511     names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
512  
513     return names
514     
515
516 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, 
517                  targetdir, sid_generator="internal", eadb=False):
518     """Create a new smb.conf file based on a couple of basic settings.
519     """
520     assert smbconf is not None
521     if hostname is None:
522         hostname = socket.gethostname().split(".")[0]
523     netbiosname = hostname.upper()
524
525     if serverrole is None:
526         serverrole = "standalone"
527
528     assert serverrole in ("domain controller", "member server", "standalone")
529     if serverrole == "domain controller":
530         smbconfsuffix = "dc"
531     elif serverrole == "member server":
532         smbconfsuffix = "member"
533     elif serverrole == "standalone":
534         smbconfsuffix = "standalone"
535
536     if sid_generator is None:
537         sid_generator = "internal"
538
539     assert domain is not None
540     domain = domain.upper()
541
542     assert realm is not None
543     realm = realm.upper()
544
545     default_lp = samba.param.LoadParm()
546     #Load non-existant file
547     if os.path.exists(smbconf):
548         default_lp.load(smbconf)
549     if eadb:
550         if targetdir is not None:
551             privdir = os.path.join(targetdir, "private")
552         else:
553             privdir = default_lp.get("private dir")
554         posixeadb_line = "posix:eadb = " + os.path.abspath(os.path.join(privdir, "eadb.tdb"))
555     else:
556         posixeadb_line = ""
557
558     if targetdir is not None:
559         privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
560         lockdir_line = "lock dir = " + os.path.abspath(targetdir)
561
562         default_lp.set("lock dir", os.path.abspath(targetdir))
563     else:
564         privatedir_line = ""
565         lockdir_line = ""
566
567     if sid_generator == "internal":
568         sid_generator_line = ""
569     else:
570         sid_generator_line = "sid generator = " + sid_generator
571
572     sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
573     netlogon = os.path.join(sysvol, realm.lower(), "scripts")
574
575     setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix), 
576                smbconf, {
577             "NETBIOS_NAME": netbiosname,
578             "DOMAIN": domain,
579             "REALM": realm,
580             "SERVERROLE": serverrole,
581             "NETLOGONPATH": netlogon,
582             "SYSVOLPATH": sysvol,
583             "SIDGENERATOR_LINE": sid_generator_line,
584             "PRIVATEDIR_LINE": privatedir_line,
585             "LOCKDIR_LINE": lockdir_line,
586             "POSIXEADB_LINE": posixeadb_line
587             })
588
589
590 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
591                         users_gid, wheel_gid):
592     """setup reasonable name mappings for sam names to unix names.
593
594     :param samdb: SamDB object.
595     :param idmap: IDmap db object.
596     :param sid: The domain sid.
597     :param domaindn: The domain DN.
598     :param root_uid: uid of the UNIX root user.
599     :param nobody_uid: uid of the UNIX nobody user.
600     :param users_gid: gid of the UNIX users group.
601     :param wheel_gid: gid of the UNIX wheel group."""
602     idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
603     idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
604     
605     idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
606     idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
607
608
609 def setup_samdb_partitions(samdb_path, setup_path, logger, lp, session_info, 
610                            provision_backend, names, schema, serverrole, 
611                            erase=False):
612     """Setup the partitions for the SAM database. 
613     
614     Alternatively, provision() may call this, and then populate the database.
615     
616     :note: This will wipe the Sam Database!
617     
618     :note: This function always removes the local SAM LDB file. The erase 
619         parameter controls whether to erase the existing data, which 
620         may not be stored locally but in LDAP.
621
622     """
623     assert session_info is not None
624
625     # We use options=["modules:"] to stop the modules loading - we
626     # just want to wipe and re-initialise the database, not start it up
627
628     try:
629         os.unlink(samdb_path)
630     except OSError:
631         pass
632
633     samdb = Ldb(url=samdb_path, session_info=session_info, 
634                 lp=lp, options=["modules:"])
635
636     ldap_backend_line = "# No LDAP backend"
637     if provision_backend.type is not "ldb":
638         ldap_backend_line = "ldapBackend: %s" % provision_backend.ldapi_uri
639
640     samdb.transaction_start()
641     try:
642         logger.info("Setting up sam.ldb partitions and settings")
643         setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
644                 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(), 
645                 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
646                 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
647                 "LDAP_BACKEND_LINE": ldap_backend_line,
648         })
649
650         
651         setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
652                 "BACKEND_TYPE": provision_backend.type,
653                 "SERVER_ROLE": serverrole
654                 })
655
656         logger.info("Setting up sam.ldb rootDSE")
657         setup_samdb_rootdse(samdb, setup_path, names)
658     except:
659         samdb.transaction_cancel()
660         raise
661     else:
662         samdb.transaction_commit()
663
664         
665 def secretsdb_self_join(secretsdb, domain, 
666                         netbiosname, machinepass, domainsid=None,
667                         realm=None, dnsdomain=None,
668                         keytab_path=None, 
669                         key_version_number=1,
670                         secure_channel_type=SEC_CHAN_WKSTA):
671     """Add domain join-specific bits to a secrets database.
672     
673     :param secretsdb: Ldb Handle to the secrets database
674     :param machinepass: Machine password
675     """
676     attrs=["whenChanged",
677            "secret",
678            "priorSecret",
679            "priorChanged",
680            "krb5Keytab",
681            "privateKeytab"]
682     
683     #We don't need to set msg["flatname"] here, because rdn_name will handle it, and it causes problems for modifies anyway
684     msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
685     msg["secureChannelType"] = [str(secure_channel_type)]
686     msg["objectClass"] = ["top", "primaryDomain"]
687     if realm is not None:
688       if dnsdomain is None:
689         dnsdomain = realm.lower()
690       msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
691       msg["realm"] = [realm]
692       msg["saltPrincipal"] = ["host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())]
693       msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
694       msg["privateKeytab"] = ["secrets.keytab"]
695
696
697     msg["secret"] = [machinepass]
698     msg["samAccountName"] = ["%s$" % netbiosname]
699     msg["secureChannelType"] = [str(secure_channel_type)]
700     if domainsid is not None:
701         msg["objectSid"] = [ndr_pack(domainsid)]
702     
703     # This complex expression tries to ensure that we don't have more
704     # than one record for this SID, realm or netbios domain at a time,
705     # but we don't delete the old record that we are about to modify,
706     # because that would delete the keytab and previous password.
707     res = secretsdb.search(base="cn=Primary Domains", 
708                            attrs=attrs, 
709                            expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(dn=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
710                            scope=ldb.SCOPE_ONELEVEL)
711     
712     for del_msg in res:
713         secretsdb.delete(del_msg.dn)
714
715     res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
716
717     if len(res) == 1:
718       msg["priorSecret"] = [res[0]["secret"][0]]
719       msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
720
721       try:
722         msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
723       except KeyError:
724         pass
725
726       try:
727         msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
728       except KeyError:
729         pass
730
731       for el in msg:
732           if el != 'dn':
733               msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
734       secretsdb.modify(msg)
735       secretsdb.rename(res[0].dn, msg.dn)
736     else:
737       secretsdb.add(msg)
738
739
740 def secretsdb_setup_dns(secretsdb, setup_path, private_dir,
741                         realm, dnsdomain,
742                         dns_keytab_path, dnspass):
743     """Add DNS specific bits to a secrets database.
744     
745     :param secretsdb: Ldb Handle to the secrets database
746     :param setup_path: Setup path function
747     :param machinepass: Machine password
748     """
749     try:
750         os.unlink(os.path.join(private_dir, dns_keytab_path))
751     except OSError:
752         pass
753
754     setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), { 
755             "REALM": realm,
756             "DNSDOMAIN": dnsdomain,
757             "DNS_KEYTAB": dns_keytab_path,
758             "DNSPASS_B64": b64encode(dnspass),
759             })
760
761
762 def setup_secretsdb(path, setup_path, session_info, backend_credentials, lp):
763     """Setup the secrets database.
764
765    :note: This function does not handle exceptions and transaction on purpose,
766    it's up to the caller to do this job.
767
768     :param path: Path to the secrets database.
769     :param setup_path: Get the path to a setup file.
770     :param session_info: Session info.
771     :param credentials: Credentials
772     :param lp: Loadparm context
773     :return: LDB handle for the created secrets database
774     """
775     if os.path.exists(path):
776         os.unlink(path)
777     secrets_ldb = Ldb(path, session_info=session_info, 
778                       lp=lp)
779     secrets_ldb.erase()
780     secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
781     secrets_ldb = Ldb(path, session_info=session_info, 
782                       lp=lp)
783     secrets_ldb.transaction_start()
784     try:
785         secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
786
787         if backend_credentials is not None and backend_credentials.authentication_requested():
788             if backend_credentials.get_bind_dn() is not None:
789                 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
790                         "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
791                         "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
792                         })
793             else:
794                 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
795                         "LDAPADMINUSER": backend_credentials.get_username(),
796                         "LDAPADMINREALM": backend_credentials.get_realm(),
797                         "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
798                         })
799
800         return secrets_ldb
801     except:
802         secrets_ldb.transaction_cancel()
803         raise
804
805 def setup_privileges(path, setup_path, session_info, lp):
806     """Setup the privileges database.
807
808     :param path: Path to the privileges database.
809     :param setup_path: Get the path to a setup file.
810     :param session_info: Session info.
811     :param credentials: Credentials
812     :param lp: Loadparm context
813     :return: LDB handle for the created secrets database
814     """
815     if os.path.exists(path):
816         os.unlink(path)
817     privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
818     privilege_ldb.erase()
819     privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
820
821
822 def setup_registry(path, setup_path, session_info, lp):
823     """Setup the registry.
824     
825     :param path: Path to the registry database
826     :param setup_path: Function that returns the path to a setup.
827     :param session_info: Session information
828     :param credentials: Credentials
829     :param lp: Loadparm context
830     """
831     reg = samba.registry.Registry()
832     hive = samba.registry.open_ldb(path, session_info=session_info, 
833                          lp_ctx=lp)
834     reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
835     provision_reg = setup_path("provision.reg")
836     assert os.path.exists(provision_reg)
837     reg.diff_apply(provision_reg)
838
839
840 def setup_idmapdb(path, setup_path, session_info, lp):
841     """Setup the idmap database.
842
843     :param path: path to the idmap database
844     :param setup_path: Function that returns a path to a setup file
845     :param session_info: Session information
846     :param credentials: Credentials
847     :param lp: Loadparm context
848     """
849     if os.path.exists(path):
850         os.unlink(path)
851
852     idmap_ldb = IDmapDB(path, session_info=session_info,
853                         lp=lp)
854
855     idmap_ldb.erase()
856     idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
857     return idmap_ldb
858
859
860 def setup_samdb_rootdse(samdb, setup_path, names):
861     """Setup the SamDB rootdse.
862
863     :param samdb: Sam Database handle
864     :param setup_path: Obtain setup path
865     """
866     setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
867         "SCHEMADN": names.schemadn, 
868         "NETBIOSNAME": names.netbiosname,
869         "DNSDOMAIN": names.dnsdomain,
870         "REALM": names.realm,
871         "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
872         "DOMAINDN": names.domaindn,
873         "ROOTDN": names.rootdn,
874         "CONFIGDN": names.configdn,
875         "SERVERDN": names.serverdn,
876         })
877         
878
879 def setup_self_join(samdb, names,
880                     machinepass, dnspass, 
881                     domainsid, next_rid, invocationid, setup_path,
882                     policyguid, policyguid_dc, domainControllerFunctionality,
883                     ntdsguid):
884     """Join a host to its own domain."""
885     assert isinstance(invocationid, str)
886     if ntdsguid is not None:
887         ntdsguid_line = "objectGUID: %s\n"%ntdsguid
888     else:
889         ntdsguid_line = ""
890     setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), { 
891               "CONFIGDN": names.configdn, 
892               "SCHEMADN": names.schemadn,
893               "DOMAINDN": names.domaindn,
894               "SERVERDN": names.serverdn,
895               "INVOCATIONID": invocationid,
896               "NETBIOSNAME": names.netbiosname,
897               "DEFAULTSITE": names.sitename,
898               "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
899               "MACHINEPASS_B64": b64encode(machinepass),
900               "REALM": names.realm,
901               "DOMAIN": names.domain,
902               "DOMAINSID": str(domainsid),
903               "DCRID": str(next_rid),
904               "DNSDOMAIN": names.dnsdomain,
905               "SAMBA_VERSION_STRING": version,
906               "NTDSGUID": ntdsguid_line,
907               "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
908
909     setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), { 
910               "POLICYGUID": policyguid,
911               "POLICYGUID_DC": policyguid_dc,
912               "DNSDOMAIN": names.dnsdomain,
913               "DOMAINSID": str(domainsid),
914               "DOMAINDN": names.domaindn})
915     
916     # add the NTDSGUID based SPNs
917     ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=%s,CN=Sites,CN=Configuration,%s" % (names.hostname, names.sitename, names.domaindn)
918     names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
919                                      expression="", scope=ldb.SCOPE_BASE)
920     assert isinstance(names.ntdsguid, str)
921
922     # Setup fSMORoleOwner entries to point at the newly created DC entry
923     setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
924               "DOMAIN": names.domain,
925               "DNSDOMAIN": names.dnsdomain,
926               "DOMAINDN": names.domaindn,
927               "CONFIGDN": names.configdn,
928               "SCHEMADN": names.schemadn, 
929               "DEFAULTSITE": names.sitename,
930               "SERVERDN": names.serverdn,
931               "NETBIOSNAME": names.netbiosname,
932               "NTDSGUID": names.ntdsguid,
933               "RIDALLOCATIONSTART": str(next_rid + 100),
934               "RIDALLOCATIONEND": str(next_rid + 100 + 499),
935               })
936
937     # This is partially Samba4 specific and should be replaced by the correct
938     # DNS AD-style setup
939     setup_add_ldif(samdb, setup_path("provision_dns_add.ldif"), {
940               "DNSDOMAIN": names.dnsdomain,
941               "DOMAINDN": names.domaindn,
942               "DNSPASS_B64": b64encode(dnspass),
943               })
944
945 def getpolicypath(sysvolpath, dnsdomain, guid):
946     if guid[0] != "{":
947         guid = "{%s}" % guid
948     policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
949     return policy_path
950
951 def create_gpo_struct(policy_path):
952     os.makedirs(policy_path, 0755)
953     open(os.path.join(policy_path, "GPT.INI"), 'w').write(
954                       "[General]\r\nVersion=65543")
955     os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
956     os.makedirs(os.path.join(policy_path, "USER"), 0755)
957
958
959 def setup_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
960     policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
961     create_gpo_struct(policy_path)
962
963     policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
964     create_gpo_struct(policy_path)
965
966
967 def setup_samdb(path, setup_path, session_info, provision_backend, lp, names,
968         logger, domainsid, domainguid, policyguid, policyguid_dc, fill,
969         adminpass, krbtgtpass, machinepass, invocationid, dnspass, ntdsguid,
970         serverrole, am_rodc=False, dom_for_fun_level=None, schema=None,
971         next_rid=1000):
972     """Setup a complete SAM Database.
973     
974     :note: This will wipe the main SAM database file!
975     """
976
977
978     # Provision does not make much sense values larger than 1000000000
979     # as the upper range of the rIDAvailablePool is 1073741823 and
980     # we don't want to create a domain that cannot allocate rids.
981     if next_rid < 1000 or next_rid > 1000000000:
982         error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
983         error += "the valid range is %u-%u. The default is %u." % (1000, 1000000000, 1000)
984         raise ProvisioningError(error)
985
986     # ATTENTION: Do NOT change these default values without discussion with the
987     # team and/or release manager. They have a big impact on the whole program!
988     domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
989
990     if dom_for_fun_level is None:
991         dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
992
993     if dom_for_fun_level > domainControllerFunctionality:
994         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_R2). This won't work!")
995
996     domainFunctionality = dom_for_fun_level
997     forestFunctionality = dom_for_fun_level
998
999     # Also wipes the database
1000     setup_samdb_partitions(path, setup_path, logger=logger, lp=lp,
1001         provision_backend=provision_backend, session_info=session_info,
1002         names=names, serverrole=serverrole, schema=schema)
1003
1004     if schema is None:
1005         schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
1006
1007     # Load the database, but don's load the global schema and don't connect quite yet
1008     samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1009                   credentials=provision_backend.credentials, lp=lp, global_schema=False,
1010                   am_rodc=am_rodc)
1011
1012     logger.info("Pre-loading the Samba 4 and AD schema")
1013
1014     # Load the schema from the one we computed earlier
1015     samdb.set_schema(schema)
1016
1017     # And now we can connect to the DB - the schema won't be loaded from the DB
1018     samdb.connect(path)
1019
1020     if fill == FILL_DRS:
1021         return samdb
1022         
1023     samdb.transaction_start()
1024     try:
1025         # Set the domain functionality levels onto the database.
1026         # Various module (the password_hash module in particular) need
1027         # to know what level of AD we are emulating.
1028
1029         # These will be fixed into the database via the database
1030         # modifictions below, but we need them set from the start.
1031         samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1032         samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1033         samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
1034
1035         samdb.set_domain_sid(str(domainsid))
1036         samdb.set_invocation_id(invocationid)
1037         samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1038
1039         logger.info("Adding DomainDN: %s" % names.domaindn)
1040
1041 #impersonate domain admin
1042         admin_session_info = admin_session(lp, str(domainsid))
1043         samdb.set_session_info(admin_session_info)
1044         if domainguid is not None:
1045             domainguid_line = "objectGUID: %s\n-" % domainguid
1046         else:
1047             domainguid_line = ""
1048
1049         descr = b64encode(get_domain_descriptor(domainsid))
1050         setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1051                 "DOMAINDN": names.domaindn,
1052                 "DOMAINGUID": domainguid_line,
1053                 "DESCRIPTOR": descr
1054                 })
1055
1056
1057         setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1058             "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1059             "DOMAINSID": str(domainsid),
1060             "NEXTRID": str(next_rid),
1061             "SCHEMADN": names.schemadn, 
1062             "NETBIOSNAME": names.netbiosname,
1063             "DEFAULTSITE": names.sitename,
1064             "CONFIGDN": names.configdn,
1065             "SERVERDN": names.serverdn,
1066             "POLICYGUID": policyguid,
1067             "DOMAINDN": names.domaindn,
1068             "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1069             "SAMBA_VERSION_STRING": version
1070             })
1071
1072         logger.info("Adding configuration container")
1073         descr = b64encode(get_config_descriptor(domainsid))
1074         setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1075             "CONFIGDN": names.configdn, 
1076             "DESCRIPTOR": descr,
1077             })
1078
1079         # The LDIF here was created when the Schema object was constructed
1080         logger.info("Setting up sam.ldb schema")
1081         samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1082         samdb.modify_ldif(schema.schema_dn_modify)
1083         samdb.write_prefixes_from_schema()
1084         samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1085         setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"), 
1086                        {"SCHEMADN": names.schemadn})
1087
1088         logger.info("Reopening sam.ldb with new schema")
1089     except:
1090         samdb.transaction_cancel()
1091         raise
1092     else:
1093         samdb.transaction_commit()
1094
1095     samdb = SamDB(session_info=admin_session_info,
1096                 credentials=provision_backend.credentials, lp=lp,
1097                 global_schema=False, am_rodc=am_rodc)
1098     samdb.connect(path)
1099     samdb.transaction_start()
1100     try:
1101         samdb.invocation_id = invocationid
1102
1103         logger.info("Setting up sam.ldb configuration data")
1104         setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1105             "CONFIGDN": names.configdn,
1106             "NETBIOSNAME": names.netbiosname,
1107             "DEFAULTSITE": names.sitename,
1108             "DNSDOMAIN": names.dnsdomain,
1109             "DOMAIN": names.domain,
1110             "SCHEMADN": names.schemadn,
1111             "DOMAINDN": names.domaindn,
1112             "SERVERDN": names.serverdn,
1113             "FOREST_FUNCTIONALITY": str(forestFunctionality),
1114             "DOMAIN_FUNCTIONALITY": str(domainFunctionality)
1115             })
1116
1117         logger.info("Setting up display specifiers")
1118         display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1119         display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
1120         check_all_substituted(display_specifiers_ldif)
1121         samdb.add_ldif(display_specifiers_ldif)
1122
1123         logger.info("Adding users container")
1124         setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1125                 "DOMAINDN": names.domaindn})
1126         logger.info("Modifying users container")
1127         setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1128                 "DOMAINDN": names.domaindn})
1129         logger.info("Adding computers container")
1130         setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1131                 "DOMAINDN": names.domaindn})
1132         logger.info("Modifying computers container")
1133         setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1134                 "DOMAINDN": names.domaindn})
1135         logger.info("Setting up sam.ldb data")
1136         setup_add_ldif(samdb, setup_path("provision.ldif"), {
1137             "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1138             "DOMAINDN": names.domaindn,
1139             "NETBIOSNAME": names.netbiosname,
1140             "DEFAULTSITE": names.sitename,
1141             "CONFIGDN": names.configdn,
1142             "SERVERDN": names.serverdn,
1143             "RIDAVAILABLESTART": str(next_rid + 600),
1144             "POLICYGUID_DC": policyguid_dc
1145             })
1146
1147         setup_modify_ldif(samdb, setup_path("provision_basedn_references.ldif"), {
1148                 "DOMAINDN": names.domaindn})
1149
1150         setup_modify_ldif(samdb, setup_path("provision_configuration_references.ldif"), {
1151                 "CONFIGDN": names.configdn,
1152                 "SCHEMADN": names.schemadn})
1153         if fill == FILL_FULL:
1154             logger.info("Setting up sam.ldb users and groups")
1155             setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1156                 "DOMAINDN": names.domaindn,
1157                 "DOMAINSID": str(domainsid),
1158                 "CONFIGDN": names.configdn,
1159                 "ADMINPASS_B64": b64encode(adminpass),
1160                 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1161                 })
1162
1163             logger.info("Setting up self join")
1164             setup_self_join(samdb, names=names, invocationid=invocationid,
1165                             dnspass=dnspass,
1166                             machinepass=machinepass,
1167                             domainsid=domainsid,
1168                             next_rid=next_rid,
1169                             policyguid=policyguid,
1170                             policyguid_dc=policyguid_dc,
1171                             setup_path=setup_path,
1172                             domainControllerFunctionality=domainControllerFunctionality,
1173                             ntdsguid=ntdsguid)
1174
1175             ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=%s,CN=Sites,CN=Configuration,%s" % (names.hostname, names.sitename, names.domaindn)
1176             names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1177                 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1178             assert isinstance(names.ntdsguid, str)
1179     except:
1180         samdb.transaction_cancel()
1181         raise
1182     else:
1183         samdb.transaction_commit()
1184         return samdb
1185
1186
1187 FILL_FULL = "FULL"
1188 FILL_NT4SYNC = "NT4SYNC"
1189 FILL_DRS = "DRS"
1190 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1191 POLICIES_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)(A;OICI;0x001301bf;;;PA)"
1192
1193 def set_dir_acl(path, acl, lp, domsid):
1194     setntacl(lp, path, acl, domsid)
1195     for root, dirs, files in os.walk(path, topdown=False):
1196         for name in files:
1197             setntacl(lp, os.path.join(root, name), acl, domsid)
1198         for name in dirs:
1199             setntacl(lp, os.path.join(root, name), acl, domsid)
1200
1201
1202 def set_gpo_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1203     # Set ACL for GPO
1204     policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1205     set_dir_acl(policy_path,dsacl2fsacl(POLICIES_ACL, str(domainsid)), 
1206         lp, str(domainsid))
1207     res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1208                         attrs=["cn", "nTSecurityDescriptor"],
1209                         expression="", scope=ldb.SCOPE_ONELEVEL)
1210     for policy in res:
1211         acl = ndr_unpack(security.descriptor, 
1212                          str(policy["nTSecurityDescriptor"])).as_sddl()
1213         policy_path = getpolicypath(sysvol,dnsdomain,str(policy["cn"]))
1214         set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp, 
1215                     str(domainsid))
1216
1217 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1218     lp):
1219     try:
1220         os.chown(sysvol,-1,gid)
1221     except:
1222         canchown = False
1223     else:
1224         canchown = True
1225
1226     setntacl(lp,sysvol,SYSVOL_ACL,str(domainsid))
1227     for root, dirs, files in os.walk(sysvol, topdown=False):
1228         for name in files:
1229             if canchown:
1230                 os.chown(os.path.join(root, name),-1,gid)
1231             setntacl(lp,os.path.join(root, name),SYSVOL_ACL,str(domainsid))
1232         for name in dirs:
1233             if canchown:
1234                 os.chown(os.path.join(root, name),-1,gid)
1235             setntacl(lp,os.path.join(root, name),SYSVOL_ACL,str(domainsid))
1236     set_gpo_acl(sysvol,dnsdomain,domainsid,domaindn,samdb,lp)
1237
1238
1239 def provision(setup_dir, logger, session_info, 
1240               credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1241               realm=None, 
1242               rootdn=None, domaindn=None, schemadn=None, configdn=None, 
1243               serverdn=None,
1244               domain=None, hostname=None, hostip=None, hostip6=None, 
1245               domainsid=None, next_rid=1000,
1246               adminpass=None, ldapadminpass=None,
1247               krbtgtpass=None, domainguid=None, 
1248               policyguid=None, policyguid_dc=None, invocationid=None,
1249               machinepass=None, ntdsguid=None,
1250               dnspass=None, root=None, nobody=None, users=None, 
1251               wheel=None, backup=None, aci=None, serverrole=None,
1252               dom_for_fun_level=None,
1253               ldap_backend_extra_port=None, backend_type=None,
1254               sitename=None,
1255               ol_mmr_urls=None, ol_olc=None, 
1256               setup_ds_path=None, slapd_path=None, nosync=False,
1257               ldap_dryrun_mode=False, useeadb=False, am_rodc=False):
1258     """Provision samba4
1259     
1260     :note: caution, this wipes all existing data!
1261     """
1262
1263     def setup_path(file):
1264       return os.path.join(setup_dir, file)
1265
1266     if domainsid is None:
1267       domainsid = security.random_sid()
1268     else:
1269       domainsid = security.dom_sid(domainsid)
1270
1271     # create/adapt the group policy GUIDs
1272     # Default GUID for default policy are described at
1273     # "How Core Group Policy Works"
1274     # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1275     if policyguid is None:
1276         policyguid = DEFAULT_POLICY_GUID
1277     policyguid = policyguid.upper()
1278     if policyguid_dc is None:
1279         policyguid_dc = DEFAULT_DC_POLICY_GUID
1280     policyguid_dc = policyguid_dc.upper()
1281
1282     if adminpass is None:
1283         adminpass = samba.generate_random_password(12, 32)
1284     if krbtgtpass is None:
1285         krbtgtpass = samba.generate_random_password(128, 255)
1286     if machinepass is None:
1287         machinepass  = samba.generate_random_password(128, 255)
1288     if dnspass is None:
1289         dnspass = samba.generate_random_password(128, 255)
1290     if ldapadminpass is None:
1291         #Make a new, random password between Samba and it's LDAP server
1292         ldapadminpass=samba.generate_random_password(128, 255)
1293
1294     if backend_type is None:
1295         backend_type = "ldb"
1296
1297     sid_generator = "internal"
1298     if backend_type == "fedora-ds":
1299         sid_generator = "backend"
1300
1301     root_uid = findnss_uid([root or "root"])
1302     nobody_uid = findnss_uid([nobody or "nobody"])
1303     users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1304     if wheel is None:
1305         wheel_gid = findnss_gid(["wheel", "adm"])
1306     else:
1307         wheel_gid = findnss_gid([wheel])
1308     try:
1309         bind_gid = findnss_gid(["bind", "named"])
1310     except KeyError:
1311         bind_gid = None
1312
1313     if targetdir is not None:
1314         smbconf = os.path.join(targetdir, "etc", "smb.conf")
1315     elif smbconf is None:
1316         smbconf = samba.param.default_path()
1317     if not os.path.exists(os.path.dirname(smbconf)):
1318         os.makedirs(os.path.dirname(smbconf))
1319
1320     # only install a new smb.conf if there isn't one there already
1321     if os.path.exists(smbconf):
1322         # if Samba Team members can't figure out the weird errors
1323         # loading an empty smb.conf gives, then we need to be smarter.
1324         # Pretend it just didn't exist --abartlet
1325         data = open(smbconf, 'r').read()
1326         data = data.lstrip()
1327         if data is None or data == "":
1328             make_smbconf(smbconf, setup_path, hostname, domain, realm,
1329                          serverrole, targetdir, sid_generator, useeadb)
1330     else: 
1331         make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, 
1332                      targetdir, sid_generator, useeadb)
1333
1334     lp = samba.param.LoadParm()
1335     lp.load(smbconf)
1336
1337     names = guess_names(lp=lp, hostname=hostname, domain=domain,
1338                         dnsdomain=realm, serverrole=serverrole,
1339                         domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1340                         serverdn=serverdn, sitename=sitename)
1341
1342     paths = provision_paths_from_lp(lp, names.dnsdomain)
1343
1344     paths.bind_gid = bind_gid
1345
1346     if hostip is None:
1347         hostips = samba.interface_ips(lp, False)
1348         if len(hostips) == 0:
1349             logger.warning("No external IPv4 address has been found. Using loopback.")
1350             hostip = '127.0.0.1'
1351         else:
1352             hostip = hostips[0]
1353             if len(hostips) > 1:
1354                 logger.warning("More than one IPv4 address found. Using %s.", hostip)
1355
1356     if hostip6 is None:
1357         try:
1358             for ip in socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP):
1359                 if hostip6 is None:
1360                     hostip6 = ip[-1][0]
1361                 if hostip6 == '::1' and ip[-1][0] != '::1':
1362                     hostip6 = ip[-1][0]
1363         except socket.gaierror, (socket.EAI_NODATA, msg): 
1364             hostip6 = None
1365
1366     if serverrole is None:
1367         serverrole = lp.get("server role")
1368
1369     assert serverrole in ("domain controller", "member server", "standalone")
1370     if invocationid is None:
1371         invocationid = str(uuid.uuid4())
1372
1373     if not os.path.exists(paths.private_dir):
1374         os.mkdir(paths.private_dir)
1375     if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1376         os.mkdir(os.path.join(paths.private_dir, "tls"))
1377
1378     ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1379  
1380     schema = Schema(setup_path, domainsid, invocationid=invocationid, schemadn=names.schemadn,
1381                     serverdn=names.serverdn)
1382
1383     if backend_type == "ldb":
1384         provision_backend = LDBBackend(backend_type,
1385                                          paths=paths, setup_path=setup_path,
1386                                          lp=lp, credentials=credentials, 
1387                                          names=names,
1388                                          logger=logger)
1389     elif backend_type == "existing":
1390         provision_backend = ExistingBackend(backend_type,
1391                                          paths=paths, setup_path=setup_path,
1392                                          lp=lp, credentials=credentials, 
1393                                          names=names,
1394                                          logger=logger,
1395                                          ldapi_url=ldapi_url)
1396     elif backend_type == "fedora-ds":
1397         provision_backend = FDSBackend(backend_type,
1398                                          paths=paths, setup_path=setup_path,
1399                                          lp=lp, credentials=credentials, 
1400                                          names=names,
1401                                          logger=logger,
1402                                          domainsid=domainsid,
1403                                          schema=schema,
1404                                          hostname=hostname,
1405                                          ldapadminpass=ldapadminpass,
1406                                          slapd_path=slapd_path,
1407                                          ldap_backend_extra_port=ldap_backend_extra_port,
1408                                          ldap_dryrun_mode=ldap_dryrun_mode,
1409                                          root=root,
1410                                          setup_ds_path=setup_ds_path)
1411     elif backend_type == "openldap":
1412         provision_backend = OpenLDAPBackend(backend_type,
1413                                          paths=paths, setup_path=setup_path,
1414                                          lp=lp, credentials=credentials, 
1415                                          names=names,
1416                                          logger=logger,
1417                                          domainsid=domainsid,
1418                                          schema=schema,
1419                                          hostname=hostname,
1420                                          ldapadminpass=ldapadminpass,
1421                                          slapd_path=slapd_path,
1422                                          ldap_backend_extra_port=ldap_backend_extra_port,
1423                                          ldap_dryrun_mode=ldap_dryrun_mode,
1424                                          ol_mmr_urls=ol_mmr_urls, 
1425                                          nosync=nosync)
1426     else:
1427         raise ValueError("Unknown LDAP backend type selected")
1428
1429     provision_backend.init()
1430     provision_backend.start()
1431
1432     # only install a new shares config db if there is none
1433     if not os.path.exists(paths.shareconf):
1434         logger.info("Setting up share.ldb")
1435         share_ldb = Ldb(paths.shareconf, session_info=session_info, 
1436                         lp=lp)
1437         share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1438
1439      
1440     logger.info("Setting up secrets.ldb")
1441     secrets_ldb = setup_secretsdb(paths.secrets, setup_path, 
1442         session_info=session_info,
1443         backend_credentials=provision_backend.secrets_credentials, lp=lp)
1444
1445     try:
1446         logger.info("Setting up the registry")
1447         setup_registry(paths.hklm, setup_path, session_info, 
1448                        lp=lp)
1449
1450         logger.info("Setting up the privileges database")
1451         setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1452
1453         logger.info("Setting up idmap db")
1454         idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1455                               lp=lp)
1456
1457         logger.info("Setting up SAM db")
1458         samdb = setup_samdb(paths.samdb, setup_path, session_info, 
1459                             provision_backend, lp, names,
1460                             logger=logger, 
1461                             domainsid=domainsid, 
1462                             schema=schema, domainguid=domainguid,
1463                             policyguid=policyguid, policyguid_dc=policyguid_dc,
1464                             fill=samdb_fill, 
1465                             adminpass=adminpass, krbtgtpass=krbtgtpass,
1466                             invocationid=invocationid, 
1467                             machinepass=machinepass, dnspass=dnspass, 
1468                             ntdsguid=ntdsguid, serverrole=serverrole,
1469                             dom_for_fun_level=dom_for_fun_level,
1470                             am_rodc=am_rodc, next_rid=next_rid)
1471
1472         if serverrole == "domain controller":
1473             if paths.netlogon is None:
1474                 logger.info("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1475                 logger.info("Please either remove %s or see the template at %s" % 
1476                         (paths.smbconf, setup_path("provision.smb.conf.dc")))
1477                 assert paths.netlogon is not None
1478
1479             if paths.sysvol is None:
1480                 logger.info("Existing smb.conf does not have a [sysvol] share, but you"
1481                         " are configuring a DC.")
1482                 logger.info("Please either remove %s or see the template at %s" % 
1483                         (paths.smbconf, setup_path("provision.smb.conf.dc")))
1484                 assert paths.sysvol is not None
1485
1486             if not os.path.isdir(paths.netlogon):
1487                 os.makedirs(paths.netlogon, 0755)
1488
1489         if samdb_fill == FILL_FULL:
1490             setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1491                                 root_uid=root_uid, nobody_uid=nobody_uid,
1492                                 users_gid=users_gid, wheel_gid=wheel_gid)
1493
1494             if serverrole == "domain controller":
1495                 # Set up group policies (domain policy and domain controller policy)
1496                 setup_gpo(paths.sysvol, names.dnsdomain, policyguid, policyguid_dc)
1497                 setsysvolacl(samdb, paths.netlogon, paths.sysvol, wheel_gid, 
1498                              domainsid, names.dnsdomain, names.domaindn, lp)
1499
1500             logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1501             setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1502
1503             secretsdb_self_join(secrets_ldb, domain=names.domain,
1504                                 realm=names.realm,
1505                                 dnsdomain=names.dnsdomain,
1506                                 netbiosname=names.netbiosname,
1507                                 domainsid=domainsid, 
1508                                 machinepass=machinepass,
1509                                 secure_channel_type=SEC_CHAN_BDC)
1510
1511             # Now set up the right msDS-SupportedEncryptionTypes into the DB
1512             # In future, this might be determined from some configuration
1513             kerberos_enctypes = str(ENC_ALL_TYPES)
1514
1515             try:
1516                 msg = ldb.Message(ldb.Dn(samdb, samdb.searchone("distinguishedName", expression="samAccountName=%s$" % names.netbiosname, scope=ldb.SCOPE_SUBTREE)))
1517                 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(elements=kerberos_enctypes, 
1518                                                                           flags=ldb.FLAG_MOD_REPLACE, 
1519                                                                           name="msDS-SupportedEncryptionTypes")
1520                 samdb.modify(msg)
1521             except ldb.LdbError, (ldb.ERR_NO_SUCH_ATTRIBUTE, _):
1522                 # It might be that this attribute does not exist in this schema
1523                 pass
1524
1525
1526             if serverrole == "domain controller":
1527                 secretsdb_setup_dns(secrets_ldb, setup_path,
1528                                     paths.private_dir,
1529                                     realm=names.realm, dnsdomain=names.dnsdomain,
1530                                     dns_keytab_path=paths.dns_keytab,
1531                                     dnspass=dnspass)
1532
1533                 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1534                 assert isinstance(domainguid, str)
1535
1536                 # Only make a zone file on the first DC, it should be replicated
1537                 # with DNS replication
1538                 create_zone_file(lp, logger, paths, targetdir, setup_path,
1539                     dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
1540                     hostname=names.hostname, realm=names.realm, 
1541                     domainguid=domainguid, ntdsguid=names.ntdsguid)
1542
1543                 create_named_conf(paths, setup_path, realm=names.realm,
1544                                   dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1545
1546                 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1547                                   dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1548                                   keytab_name=paths.dns_keytab)
1549                 logger.info("See %s for an example configuration include file for BIND", paths.namedconf)
1550                 logger.info("and %s for further documentation required for secure DNS "
1551                         "updates", paths.namedtxt)
1552
1553                 create_krb5_conf(paths.krb5conf, setup_path,
1554                                  dnsdomain=names.dnsdomain, hostname=names.hostname,
1555                                  realm=names.realm)
1556                 logger.info("A Kerberos configuration suitable for Samba 4 has been "
1557                         "generated at %s", paths.krb5conf)
1558
1559             lastProvisionUSNs = get_last_provision_usn(samdb)
1560             maxUSN = get_max_usn(samdb, str(names.rootdn))
1561             if lastProvisionUSNs is not None:
1562                 update_provision_usn(samdb, 0, maxUSN, 1)
1563             else:
1564                 set_provision_usn(samdb, 0, maxUSN)
1565
1566         if serverrole == "domain controller":
1567             create_dns_update_list(lp, logger, paths, setup_path)
1568
1569         provision_backend.post_setup()
1570         provision_backend.shutdown()
1571         
1572         create_phpldapadmin_config(paths.phpldapadminconfig, setup_path, 
1573                                    ldapi_url)
1574     except:
1575         secrets_ldb.transaction_cancel()
1576         raise
1577
1578     #Now commit the secrets.ldb to disk
1579     secrets_ldb.transaction_commit()
1580
1581     # the commit creates the dns.keytab, now chown it
1582     dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1583     if (os.path.isfile(dns_keytab_path) and paths.bind_gid is not None):
1584         try:
1585             os.chmod(dns_keytab_path, 0640)
1586             os.chown(dns_keytab_path, -1, paths.bind_gid)
1587         except OSError:
1588             logger.info("Failed to chown %s to bind gid %u", dns_keytab_path,
1589                 paths.bind_gid)
1590
1591
1592     logger.info("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php",
1593             paths.phpldapadminconfig)
1594
1595     logger.info("Once the above files are installed, your Samba4 server will be ready to use")
1596     logger.info("Server Role:           %s" % serverrole)
1597     logger.info("Hostname:              %s" % names.hostname)
1598     logger.info("NetBIOS Domain:        %s" % names.domain)
1599     logger.info("DNS Domain:            %s" % names.dnsdomain)
1600     logger.info("DOMAIN SID:            %s" % str(domainsid))
1601     if samdb_fill == FILL_FULL:
1602         logger.info("Admin password:        %s" % adminpass)
1603     if provision_backend.type is not "ldb":
1604         if provision_backend.credentials.get_bind_dn() is not None:
1605             logger.info("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1606         else:
1607             logger.info("LDAP Admin User:       %s" % provision_backend.credentials.get_username())
1608
1609         logger.info("LDAP Admin Password:   %s" % provision_backend.credentials.get_password())
1610
1611         if provision_backend.slapd_command_escaped is not None:
1612             # now display slapd_command_file.txt to show how slapd must be started next time
1613             logger.info("Use later the following commandline to start slapd, then Samba:")
1614             logger.info(provision_backend.slapd_command_escaped)
1615             logger.info("This slapd-Commandline is also stored under: %s/ldap_backend_startup.sh", 
1616                     provision_backend.ldapdir)
1617
1618     result = ProvisionResult()
1619     result.domaindn = domaindn
1620     result.paths = paths
1621     result.lp = lp
1622     result.samdb = samdb
1623     return result
1624
1625
1626 def provision_become_dc(setup_dir=None,
1627                         smbconf=None, targetdir=None, realm=None, 
1628                         rootdn=None, domaindn=None, schemadn=None,
1629                         configdn=None, serverdn=None,
1630                         domain=None, hostname=None, domainsid=None, 
1631                         adminpass=None, krbtgtpass=None, domainguid=None, 
1632                         policyguid=None, policyguid_dc=None, invocationid=None,
1633                         machinepass=None, 
1634                         dnspass=None, root=None, nobody=None, users=None, 
1635                         wheel=None, backup=None, serverrole=None, 
1636                         ldap_backend=None, ldap_backend_type=None,
1637                         sitename=None, debuglevel=1):
1638
1639     logger = logging.getLogger("provision")
1640     samba.set_debug_level(debuglevel)
1641
1642     return provision(setup_dir, logger, system_session(), None,
1643               smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1644               realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1645               configdn=configdn, serverdn=serverdn, domain=domain,
1646               hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1647               machinepass=machinepass, serverrole="domain controller",
1648               sitename=sitename)
1649
1650
1651 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1652     """Create a PHP LDAP admin configuration file.
1653
1654     :param path: Path to write the configuration to.
1655     :param setup_path: Function to generate setup paths.
1656     """
1657     setup_file(setup_path("phpldapadmin-config.php"), path, 
1658             {"S4_LDAPI_URI": ldapi_uri})
1659
1660
1661 def create_zone_file(lp, logger, paths, targetdir, setup_path, dnsdomain,
1662                      hostip, hostip6, hostname, realm, domainguid,
1663                      ntdsguid):
1664     """Write out a DNS zone file, from the info in the current database.
1665
1666     :param paths: paths object
1667     :param setup_path: Setup path function.
1668     :param dnsdomain: DNS Domain name
1669     :param domaindn: DN of the Domain
1670     :param hostip: Local IPv4 IP
1671     :param hostip6: Local IPv6 IP
1672     :param hostname: Local hostname
1673     :param realm: Realm name
1674     :param domainguid: GUID of the domain.
1675     :param ntdsguid: GUID of the hosts nTDSDSA record.
1676     """
1677     assert isinstance(domainguid, str)
1678
1679     if hostip6 is not None:
1680         hostip6_base_line = "            IN AAAA    " + hostip6
1681         hostip6_host_line = hostname + "        IN AAAA    " + hostip6
1682         gc_msdcs_ip6_line = "gc._msdcs               IN AAAA    " + hostip6
1683     else:
1684         hostip6_base_line = ""
1685         hostip6_host_line = ""
1686         gc_msdcs_ip6_line = ""
1687
1688     if hostip is not None:
1689         hostip_base_line = "            IN A    " + hostip
1690         hostip_host_line = hostname + "        IN A    " + hostip
1691         gc_msdcs_ip_line = "gc._msdcs               IN A    " + hostip
1692     else:
1693         hostip_base_line = ""
1694         hostip_host_line = ""
1695         gc_msdcs_ip_line = ""
1696
1697     dns_dir = os.path.dirname(paths.dns)
1698
1699     try:
1700         shutil.rmtree(dns_dir, True)
1701     except OSError:
1702         pass
1703
1704     os.mkdir(dns_dir, 0775)
1705
1706     # we need to freeze the zone while we update the contents
1707     if targetdir is None:
1708         rndc = ' '.join(lp.get("rndc command"))
1709         os.system(rndc + " freeze " + lp.get("realm"))
1710
1711     setup_file(setup_path("provision.zone"), paths.dns, {
1712             "HOSTNAME": hostname,
1713             "DNSDOMAIN": dnsdomain,
1714             "REALM": realm,
1715             "HOSTIP_BASE_LINE": hostip_base_line,
1716             "HOSTIP_HOST_LINE": hostip_host_line,
1717             "DOMAINGUID": domainguid,
1718             "DATESTRING": time.strftime("%Y%m%d%H"),
1719             "DEFAULTSITE": DEFAULTSITE,
1720             "NTDSGUID": ntdsguid,
1721             "HOSTIP6_BASE_LINE": hostip6_base_line,
1722             "HOSTIP6_HOST_LINE": hostip6_host_line,
1723             "GC_MSDCS_IP_LINE": gc_msdcs_ip_line,
1724             "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line,
1725         })
1726
1727     # note that we use no variable substitution on this file
1728     # the substitution is done at runtime by samba_dnsupdate
1729     setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1730
1731     # and the SPN update list
1732     setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1733
1734     if paths.bind_gid is not None:
1735         try:
1736             os.chown(dns_dir, -1, paths.bind_gid)
1737             os.chown(paths.dns, -1, paths.bind_gid)
1738             # chmod needed to cope with umask
1739             os.chmod(dns_dir, 0775)
1740             os.chmod(paths.dns, 0664)
1741         except OSError:
1742             logger.error("Failed to chown %s to bind gid %u" % (dns_dir, paths.bind_gid))
1743
1744     if targetdir is None:
1745         os.system(rndc + " unfreeze " + lp.get("realm"))
1746
1747
1748 def create_dns_update_list(lp, logger, paths, setup_path):
1749     """Write out a dns_update_list file"""
1750     # note that we use no variable substitution on this file
1751     # the substitution is done at runtime by samba_dnsupdate
1752     setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1753     setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1754
1755
1756 def create_named_conf(paths, setup_path, realm, dnsdomain,
1757                       private_dir):
1758     """Write out a file containing zone statements suitable for inclusion in a
1759     named.conf file (including GSS-TSIG configuration).
1760     
1761     :param paths: all paths
1762     :param setup_path: Setup path function.
1763     :param realm: Realm name
1764     :param dnsdomain: DNS Domain name
1765     :param private_dir: Path to private directory
1766     :param keytab_name: File name of DNS keytab file
1767     """
1768
1769     setup_file(setup_path("named.conf"), paths.namedconf, {
1770             "DNSDOMAIN": dnsdomain,
1771             "REALM": realm,
1772             "ZONE_FILE": paths.dns,
1773             "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1774             "NAMED_CONF": paths.namedconf,
1775             "NAMED_CONF_UPDATE": paths.namedconf_update
1776             })
1777
1778     setup_file(setup_path("named.conf.update"), paths.namedconf_update)
1779
1780
1781 def create_named_txt(path, setup_path, realm, dnsdomain,
1782                       private_dir, keytab_name):
1783     """Write out a file containing zone statements suitable for inclusion in a
1784     named.conf file (including GSS-TSIG configuration).
1785     
1786     :param path: Path of the new named.conf file.
1787     :param setup_path: Setup path function.
1788     :param realm: Realm name
1789     :param dnsdomain: DNS Domain name
1790     :param private_dir: Path to private directory
1791     :param keytab_name: File name of DNS keytab file
1792     """
1793
1794     setup_file(setup_path("named.txt"), path, {
1795             "DNSDOMAIN": dnsdomain,
1796             "REALM": realm,
1797             "DNS_KEYTAB": keytab_name,
1798             "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1799             "PRIVATE_DIR": private_dir
1800         })
1801
1802
1803 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1804     """Write out a file containing zone statements suitable for inclusion in a
1805     named.conf file (including GSS-TSIG configuration).
1806     
1807     :param path: Path of the new named.conf file.
1808     :param setup_path: Setup path function.
1809     :param dnsdomain: DNS Domain name
1810     :param hostname: Local hostname
1811     :param realm: Realm name
1812     """
1813     setup_file(setup_path("krb5.conf"), path, {
1814             "DNSDOMAIN": dnsdomain,
1815             "HOSTNAME": hostname,
1816             "REALM": realm,
1817         })
1818
1819
1820 class ProvisioningError(Exception):
1821     """A generic provision error."""
1822
1823     def __init__(self, value):
1824         self.value = value
1825
1826     def __str__(self):
1827         return "ProvisioningError: " + self.value
1828
1829
1830 class InvalidNetbiosName(Exception):
1831     """A specified name was not a valid NetBIOS name."""
1832     def __init__(self, name):
1833         super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)