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