python: use python3 style super statements
[samba.git] / python / samba / provision / __init__.py
blob56ca74964078861bda8573547717525a3a7c51ef
1 # Unix SMB/CIFS implementation.
2 # backend code for provisioning a Samba AD server
4 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2012
5 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
6 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
8 # Based on the original in EJS:
9 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 3 of the License, or
14 # (at your option) any later version.
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 """Functions for setting up a Samba configuration."""
27 __docformat__ = "restructuredText"
29 from base64 import b64encode
30 import errno
31 import os
32 import stat
33 import re
34 import pwd
35 import grp
36 import logging
37 import time
38 import uuid
39 import socket
40 import tempfile
41 import samba.dsdb
43 import ldb
45 from samba.auth import system_session, admin_session
46 from samba.auth_util import system_session_unix
47 import samba
48 from samba import auth
49 from samba.samba3 import smbd, passdb
50 from samba.samba3 import param as s3param
51 from samba import (
52 Ldb,
53 MAX_NETBIOS_NAME_LEN,
54 check_all_substituted,
55 is_valid_netbios_char,
56 setup_file,
57 substitute_var,
58 valid_netbios_name,
59 version,
60 is_heimdal_built,
62 from samba.dcerpc import security, misc
63 from samba.dcerpc.misc import (
64 SEC_CHAN_BDC,
65 SEC_CHAN_WKSTA,
67 from samba.dsdb import (
68 DS_DOMAIN_FUNCTION_2000,
69 DS_DOMAIN_FUNCTION_2008,
70 DS_DOMAIN_FUNCTION_2008_R2,
71 DS_DOMAIN_FUNCTION_2012,
72 DS_DOMAIN_FUNCTION_2012_R2,
73 DS_DOMAIN_FUNCTION_2016,
74 ENC_ALL_TYPES,
76 from samba.idmap import IDmapDB
77 from samba.ms_display_specifiers import read_ms_ldif
78 from samba.ntacls import setntacl, getntacl, dsacl2fsacl
79 from samba.ndr import ndr_pack, ndr_unpack
80 from samba.provision.backend import (
81 LDBBackend,
83 from samba.descriptor import (
84 get_deletedobjects_descriptor,
85 get_config_descriptor,
86 get_config_partitions_descriptor,
87 get_config_sites_descriptor,
88 get_config_ntds_quotas_descriptor,
89 get_config_delete_protected1_descriptor,
90 get_config_delete_protected1wd_descriptor,
91 get_config_delete_protected2_descriptor,
92 get_domain_descriptor,
93 get_domain_infrastructure_descriptor,
94 get_domain_builtin_descriptor,
95 get_domain_computers_descriptor,
96 get_domain_users_descriptor,
97 get_domain_controllers_descriptor,
98 get_domain_delete_protected1_descriptor,
99 get_domain_delete_protected2_descriptor,
100 get_managed_service_accounts_descriptor,
102 from samba.provision.common import (
103 setup_path,
104 setup_add_ldif,
105 setup_modify_ldif,
106 FILL_FULL,
107 FILL_SUBDOMAIN,
108 FILL_DRS
110 from samba.provision.sambadns import (
111 get_dnsadmins_sid,
112 setup_ad_dns,
113 create_dns_dir_keytab_link,
114 create_dns_update_list
117 import samba.param
118 import samba.registry
119 from samba.schema import Schema
120 from samba.samdb import SamDB
121 from samba.dbchecker import dbcheck
122 from samba.provision.kerberos import create_kdc_conf
123 from samba.samdb import get_default_backend_store
124 from samba import functional_level
126 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
127 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04FB984F9"
128 DEFAULTSITE = "Default-First-Site-Name"
129 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
131 DEFAULT_MIN_PWD_LENGTH = 7
134 class ProvisionPaths(object):
136 def __init__(self):
137 self.shareconf = None
138 self.hklm = None
139 self.hkcu = None
140 self.hkcr = None
141 self.hku = None
142 self.hkpd = None
143 self.hkpt = None
144 self.samdb = None
145 self.idmapdb = None
146 self.secrets = None
147 self.keytab = None
148 self.dns_keytab = None
149 self.dns = None
150 self.winsdb = None
151 self.private_dir = None
152 self.binddns_dir = None
153 self.state_dir = None
156 class ProvisionNames(object):
158 def __init__(self):
159 self.ncs = None
160 self.rootdn = None
161 self.domaindn = None
162 self.configdn = None
163 self.schemadn = None
164 self.dnsforestdn = None
165 self.dnsdomaindn = None
166 self.ldapmanagerdn = None
167 self.dnsdomain = None
168 self.realm = None
169 self.netbiosname = None
170 self.domain = None
171 self.hostname = None
172 self.sitename = None
173 self.smbconf = None
174 self.domainsid = None
175 self.forestsid = None
176 self.domainguid = None
177 self.name_map = {}
180 def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf,
181 lp):
182 """Get key provision parameters (realm, domain, ...) from a given provision
184 :param samdb: An LDB object connected to the sam.ldb file
185 :param secretsdb: An LDB object connected to the secrets.ldb file
186 :param idmapdb: An LDB object connected to the idmap.ldb file
187 :param paths: A list of path to provision object
188 :param smbconf: Path to the smb.conf file
189 :param lp: A LoadParm object
190 :return: A list of key provision parameters
192 names = ProvisionNames()
193 names.adminpass = None
195 # NT domain, kerberos realm, root dn, domain dn, domain dns name
196 names.domain = lp.get("workgroup").upper()
197 names.realm = lp.get("realm")
198 names.dnsdomain = names.realm.lower()
199 basedn = samba.dn_from_dns_name(names.dnsdomain)
200 names.realm = names.realm.upper()
201 # netbiosname
202 # Get the netbiosname first (could be obtained from smb.conf in theory)
203 res = secretsdb.search(expression="(flatname=%s)" %
204 names.domain, base="CN=Primary Domains",
205 scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"])
206 names.netbiosname = str(res[0]["sAMAccountName"]).replace("$", "")
208 names.smbconf = smbconf
210 # That's a bit simplistic but it's ok as long as we have only 3
211 # partitions
212 current = samdb.search(expression="(objectClass=*)",
213 base="", scope=ldb.SCOPE_BASE,
214 attrs=["defaultNamingContext", "schemaNamingContext",
215 "configurationNamingContext", "rootDomainNamingContext",
216 "namingContexts"])
218 names.configdn = str(current[0]["configurationNamingContext"][0])
219 names.schemadn = str(current[0]["schemaNamingContext"][0])
220 if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb,
221 current[0]["defaultNamingContext"][0].decode('utf8')))):
222 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
223 "is not the same ..." % (paths.samdb,
224 str(current[0]["defaultNamingContext"][0].decode('utf8')),
225 paths.smbconf, basedn)))
227 names.domaindn = str(current[0]["defaultNamingContext"][0])
228 names.rootdn = str(current[0]["rootDomainNamingContext"][0])
229 names.ncs = current[0]["namingContexts"]
230 names.dnsforestdn = None
231 names.dnsdomaindn = None
233 for i in range(0, len(names.ncs)):
234 nc = str(names.ncs[i])
236 dnsforestdn = "DC=ForestDnsZones,%s" % (str(names.rootdn))
237 if nc == dnsforestdn:
238 names.dnsforestdn = dnsforestdn
239 continue
241 dnsdomaindn = "DC=DomainDnsZones,%s" % (str(names.domaindn))
242 if nc == dnsdomaindn:
243 names.dnsdomaindn = dnsdomaindn
244 continue
246 # default site name
247 res3 = samdb.search(expression="(objectClass=site)",
248 base="CN=Sites," + str(names.configdn), scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
249 names.sitename = str(res3[0]["cn"])
251 # dns hostname and server dn
252 res4 = samdb.search(expression="(CN=%s)" % names.netbiosname,
253 base="OU=Domain Controllers,%s" % basedn,
254 scope=ldb.SCOPE_ONELEVEL, attrs=["dNSHostName"])
255 if len(res4) == 0:
256 raise ProvisioningError("Unable to find DC called CN=%s under OU=Domain Controllers,%s" % (names.netbiosname, basedn))
258 names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain, "")
260 server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
261 attrs=[], base=names.configdn)
262 names.serverdn = str(server_res[0].dn)
264 # invocation id/objectguid
265 res5 = samdb.search(expression="(objectClass=*)",
266 base="CN=NTDS Settings,%s" % str(names.serverdn),
267 scope=ldb.SCOPE_BASE,
268 attrs=["invocationID", "objectGUID"])
269 names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0]))
270 names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0]))
272 # domain guid/sid
273 res6 = samdb.search(expression="(objectClass=*)", base=basedn,
274 scope=ldb.SCOPE_BASE, attrs=["objectGUID",
275 "objectSid", "msDS-Behavior-Version"])
276 names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
277 names.domainsid = ndr_unpack(security.dom_sid, res6[0]["objectSid"][0])
278 names.forestsid = ndr_unpack(security.dom_sid, res6[0]["objectSid"][0])
279 if res6[0].get("msDS-Behavior-Version") is None or \
280 int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
281 names.domainlevel = DS_DOMAIN_FUNCTION_2000
282 else:
283 names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
285 # policy guid
286 res7 = samdb.search(expression="(name={%s})" % DEFAULT_POLICY_GUID,
287 base="CN=Policies,CN=System," + basedn,
288 scope=ldb.SCOPE_ONELEVEL, attrs=["cn", "displayName"])
289 names.policyid = str(res7[0]["cn"]).replace("{", "").replace("}", "")
290 # dc policy guid
291 res8 = samdb.search(expression="(name={%s})" % DEFAULT_DC_POLICY_GUID,
292 base="CN=Policies,CN=System," + basedn,
293 scope=ldb.SCOPE_ONELEVEL,
294 attrs=["cn", "displayName"])
295 if len(res8) == 1:
296 names.policyid_dc = str(res8[0]["cn"]).replace("{", "").replace("}", "")
297 else:
298 names.policyid_dc = None
300 res9 = idmapdb.search(expression="(cn=%s-%s)" %
301 (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR),
302 attrs=["xidNumber", "type"])
303 if len(res9) != 1:
304 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid (%s-%s" % (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR))
305 if str(res9[0]["type"][0]) == "ID_TYPE_BOTH":
306 names.root_gid = int(res9[0]["xidNumber"][0])
307 else:
308 names.root_gid = pwd.getpwuid(int(res9[0]["xidNumber"][0])).pw_gid
310 res10 = samdb.search(expression="(samaccountname=dns)",
311 scope=ldb.SCOPE_SUBTREE, attrs=["dn"],
312 controls=["search_options:1:2"])
313 if (len(res10) > 0):
314 has_legacy_dns_account = True
315 else:
316 has_legacy_dns_account = False
318 res11 = samdb.search(expression="(samaccountname=dns-%s)" % names.netbiosname,
319 scope=ldb.SCOPE_SUBTREE, attrs=["dn"],
320 controls=["search_options:1:2"])
321 if (len(res11) > 0):
322 has_dns_account = True
323 else:
324 has_dns_account = False
326 if names.dnsdomaindn is not None:
327 if has_dns_account:
328 names.dns_backend = 'BIND9_DLZ'
329 else:
330 names.dns_backend = 'SAMBA_INTERNAL'
331 elif has_dns_account or has_legacy_dns_account:
332 names.dns_backend = 'BIND9_FLATFILE'
333 else:
334 names.dns_backend = 'NONE'
336 dns_admins_sid = get_dnsadmins_sid(samdb, names.domaindn)
337 names.name_map['DnsAdmins'] = str(dns_admins_sid)
339 return names
342 def update_provision_usn(samdb, low, high, id, replace=False):
343 """Update the field provisionUSN in sam.ldb
345 This field is used to track range of USN modified by provision and
346 upgradeprovision.
347 This value is used afterward by next provision to figure out if
348 the field have been modified since last provision.
350 :param samdb: An LDB object connect to sam.ldb
351 :param low: The lowest USN modified by this upgrade
352 :param high: The highest USN modified by this upgrade
353 :param id: The invocation id of the samba's dc
354 :param replace: A boolean indicating if the range should replace any
355 existing one or appended (default)
358 tab = []
359 if not replace:
360 entry = samdb.search(base="@PROVISION",
361 scope=ldb.SCOPE_BASE,
362 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
363 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
364 if not re.search(';', str(e)):
365 e = "%s;%s" % (str(e), id)
366 tab.append(str(e))
368 tab.append("%s-%s;%s" % (low, high, id))
369 delta = ldb.Message()
370 delta.dn = ldb.Dn(samdb, "@PROVISION")
371 delta[LAST_PROVISION_USN_ATTRIBUTE] = \
372 ldb.MessageElement(tab,
373 ldb.FLAG_MOD_REPLACE,
374 LAST_PROVISION_USN_ATTRIBUTE)
375 entry = samdb.search(expression='provisionnerID=*',
376 base="@PROVISION", scope=ldb.SCOPE_BASE,
377 attrs=["provisionnerID"])
378 if len(entry) == 0 or len(entry[0]) == 0:
379 delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
380 samdb.modify(delta)
383 def set_provision_usn(samdb, low, high, id):
384 """Set the field provisionUSN in sam.ldb
385 This field is used to track range of USN modified by provision and
386 upgradeprovision.
387 This value is used afterward by next provision to figure out if
388 the field have been modified since last provision.
390 :param samdb: An LDB object connect to sam.ldb
391 :param low: The lowest USN modified by this upgrade
392 :param high: The highest USN modified by this upgrade
393 :param id: The invocationId of the provision"""
395 tab = []
396 tab.append("%s-%s;%s" % (low, high, id))
398 delta = ldb.Message()
399 delta.dn = ldb.Dn(samdb, "@PROVISION")
400 delta[LAST_PROVISION_USN_ATTRIBUTE] = \
401 ldb.MessageElement(tab,
402 ldb.FLAG_MOD_ADD,
403 LAST_PROVISION_USN_ATTRIBUTE)
404 samdb.add(delta)
407 def get_max_usn(samdb, basedn):
408 """ This function return the biggest USN present in the provision
410 :param samdb: A LDB object pointing to the sam.ldb
411 :param basedn: A string containing the base DN of the provision
412 (ie. DC=foo, DC=bar)
413 :return: The biggest USN in the provision"""
415 res = samdb.search(expression="objectClass=*", base=basedn,
416 scope=ldb.SCOPE_SUBTREE, attrs=["uSNChanged"],
417 controls=["search_options:1:2",
418 "server_sort:1:1:uSNChanged",
419 "paged_results:1:1"])
420 return res[0]["uSNChanged"]
423 def get_last_provision_usn(sam):
424 """Get USNs ranges modified by a provision or an upgradeprovision
426 :param sam: An LDB object pointing to the sam.ldb
427 :return: a dictionary which keys are invocation id and values are an array
428 of integer representing the different ranges
430 try:
431 entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE,
432 base="@PROVISION", scope=ldb.SCOPE_BASE,
433 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
434 except ldb.LdbError as e1:
435 (ecode, emsg) = e1.args
436 if ecode == ldb.ERR_NO_SUCH_OBJECT:
437 return None
438 raise
439 if len(entry) > 0:
440 myids = []
441 range = {}
442 p = re.compile(r'-')
443 if entry[0].get("provisionnerID"):
444 for e in entry[0]["provisionnerID"]:
445 myids.append(str(e))
446 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
447 tab1 = str(r).split(';')
448 if len(tab1) == 2:
449 id = tab1[1]
450 else:
451 id = "default"
452 if (len(myids) > 0 and id not in myids):
453 continue
454 tab2 = p.split(tab1[0])
455 if range.get(id) is None:
456 range[id] = []
457 range[id].append(tab2[0])
458 range[id].append(tab2[1])
459 return range
460 else:
461 return None
464 class ProvisionResult(object):
465 """Result of a provision.
467 :ivar server_role: The server role
468 :ivar paths: ProvisionPaths instance
469 :ivar domaindn: The domain dn, as string
472 def __init__(self):
473 self.server_role = None
474 self.paths = None
475 self.domaindn = None
476 self.lp = None
477 self.samdb = None
478 self.idmap = None
479 self.names = None
480 self.domainsid = None
481 self.adminpass_generated = None
482 self.adminpass = None
483 self.backend_result = None
485 def report_logger(self, logger):
486 """Report this provision result to a logger."""
487 logger.info(
488 "Once the above files are installed, your Samba AD server will "
489 "be ready to use")
490 if self.adminpass_generated:
491 logger.info("Admin password: %s", self.adminpass)
492 logger.info("Server Role: %s", self.server_role)
493 logger.info("Hostname: %s", self.names.hostname)
494 logger.info("NetBIOS Domain: %s", self.names.domain)
495 logger.info("DNS Domain: %s", self.names.dnsdomain)
496 logger.info("DOMAIN SID: %s", self.domainsid)
498 if self.backend_result:
499 self.backend_result.report_logger(logger)
502 def findnss(nssfn, names):
503 """Find a user or group from a list of possibilities.
505 :param nssfn: NSS Function to try (should raise KeyError if not found)
506 :param names: Names to check.
507 :return: Value return by first names list.
509 for name in names:
510 try:
511 return nssfn(name)
512 except KeyError:
513 pass
514 raise KeyError("Unable to find user/group in %r" % names)
517 def findnss_uid(names):
518 return findnss(pwd.getpwnam, names)[2]
521 def findnss_gid(names):
522 return findnss(grp.getgrnam, names)[2]
525 def get_root_uid(root, logger):
526 try:
527 root_uid = findnss_uid(root)
528 except KeyError as e:
529 logger.info(e)
530 logger.info("Assuming root user has UID zero")
531 root_uid = 0
532 return root_uid
535 def provision_paths_from_lp(lp, dnsdomain):
536 """Set the default paths for provisioning.
538 :param lp: Loadparm context.
539 :param dnsdomain: DNS Domain name
541 paths = ProvisionPaths()
542 paths.private_dir = lp.get("private dir")
543 paths.binddns_dir = lp.get("binddns dir")
544 paths.state_dir = lp.get("state directory")
546 # This is stored without path prefix for the "privateKeytab" attribute in
547 # "secrets_dns.ldif".
548 paths.dns_keytab = "dns.keytab"
549 paths.keytab = "secrets.keytab"
551 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
552 paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
553 paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
554 paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
555 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
556 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
557 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
558 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
559 paths.kdcconf = os.path.join(paths.private_dir, "kdc.conf")
560 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
561 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
562 paths.encrypted_secrets_key_path = os.path.join(
563 paths.private_dir,
564 "encrypted_secrets.key")
566 paths.dns = os.path.join(paths.binddns_dir, "dns", dnsdomain + ".zone")
567 paths.namedconf = os.path.join(paths.binddns_dir, "named.conf")
568 paths.namedconf_update = os.path.join(paths.binddns_dir, "named.conf.update")
569 paths.namedtxt = os.path.join(paths.binddns_dir, "named.txt")
571 paths.hklm = "hklm.ldb"
572 paths.hkcr = "hkcr.ldb"
573 paths.hkcu = "hkcu.ldb"
574 paths.hku = "hku.ldb"
575 paths.hkpd = "hkpd.ldb"
576 paths.hkpt = "hkpt.ldb"
577 paths.sysvol = lp.get("path", "sysvol")
578 paths.netlogon = lp.get("path", "netlogon")
579 paths.smbconf = lp.configfile
580 return paths
583 def determine_netbios_name(hostname):
584 """Determine a netbios name from a hostname."""
585 # remove forbidden chars and force the length to be <16
586 netbiosname = "".join([x for x in hostname if is_valid_netbios_char(x)])
587 return netbiosname[:MAX_NETBIOS_NAME_LEN].upper()
590 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
591 serverrole=None, rootdn=None, domaindn=None, configdn=None,
592 schemadn=None, serverdn=None, sitename=None,
593 domain_names_forced=False):
594 """Guess configuration settings to use."""
596 if hostname is None:
597 hostname = socket.gethostname().split(".")[0]
599 netbiosname = lp.get("netbios name")
600 if netbiosname is None:
601 netbiosname = determine_netbios_name(hostname)
602 netbiosname = netbiosname.upper()
603 if not valid_netbios_name(netbiosname):
604 raise InvalidNetbiosName(netbiosname)
606 if dnsdomain is None:
607 dnsdomain = lp.get("realm")
608 if dnsdomain is None or dnsdomain == "":
609 raise ProvisioningError(
610 "guess_names: 'realm' not specified in supplied %s!" %
611 lp.configfile)
613 dnsdomain = dnsdomain.lower()
615 if serverrole is None:
616 serverrole = lp.get("server role")
617 if serverrole is None:
618 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
620 serverrole = serverrole.lower()
622 realm = dnsdomain.upper()
624 if lp.get("realm") == "":
625 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
627 if lp.get("realm").upper() != realm:
628 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(), lp.configfile, realm))
630 if lp.get("server role").lower() != serverrole:
631 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"), lp.configfile, serverrole))
633 if serverrole == "active directory domain controller":
634 if domain is None:
635 # This will, for better or worse, default to 'WORKGROUP'
636 domain = lp.get("workgroup")
637 domain = domain.upper()
639 if lp.get("workgroup").upper() != domain:
640 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))
642 if domaindn is None:
643 domaindn = samba.dn_from_dns_name(dnsdomain)
645 if domain == netbiosname:
646 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
647 else:
648 domain = netbiosname
649 if domaindn is None:
650 domaindn = "DC=" + netbiosname
652 if not valid_netbios_name(domain):
653 raise InvalidNetbiosName(domain)
655 if hostname.upper() == realm:
656 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
657 if netbiosname.upper() == realm:
658 raise ProvisioningError("guess_names: Realm '%s' must not be equal to NetBIOS hostname '%s'!" % (realm, netbiosname))
659 if domain == realm and not domain_names_forced:
660 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
662 if serverrole != "active directory domain controller":
664 # This is the code path for a domain member
665 # where we provision the database as if we were
666 # on a domain controller, so we should not use
667 # the same dnsdomain as the domain controllers
668 # of our primary domain.
670 # This will be important if we start doing
671 # SID/name filtering and reject the local
672 # sid and names if they come from a domain
673 # controller.
675 realm = netbiosname
676 dnsdomain = netbiosname.lower()
678 if rootdn is None:
679 rootdn = domaindn
681 if configdn is None:
682 configdn = "CN=Configuration," + rootdn
683 if schemadn is None:
684 schemadn = "CN=Schema," + configdn
686 if sitename is None:
687 sitename = DEFAULTSITE
689 if serverdn is None:
690 serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
691 netbiosname, sitename, configdn)
693 names = ProvisionNames()
694 names.rootdn = rootdn
695 names.domaindn = domaindn
696 names.configdn = configdn
697 names.schemadn = schemadn
698 names.ldapmanagerdn = "CN=Manager," + rootdn
699 names.dnsdomain = dnsdomain
700 names.domain = domain
701 names.realm = realm
702 names.netbiosname = netbiosname
703 names.hostname = hostname
704 names.sitename = sitename
705 names.serverdn = serverdn
707 return names
710 def make_smbconf(smbconf, hostname, domain, realm, targetdir,
711 serverrole=None, eadb=False, use_ntvfs=False, lp=None,
712 global_param=None):
713 """Create a new smb.conf file based on a couple of basic settings.
715 assert smbconf is not None
717 if hostname is None:
718 hostname = socket.gethostname().split(".")[0]
720 netbiosname = determine_netbios_name(hostname)
722 if serverrole is None:
723 serverrole = "standalone server"
725 assert domain is not None
726 domain = domain.upper()
728 assert realm is not None
729 realm = realm.upper()
731 global_settings = {
732 "netbios name": netbiosname,
733 "workgroup": domain,
734 "realm": realm,
735 "server role": serverrole,
738 if lp is None:
739 lp = samba.param.LoadParm()
740 # Load non-existent file
741 if os.path.exists(smbconf):
742 lp.load(smbconf)
744 if global_param is not None:
745 for ent in global_param:
746 if global_param[ent] is not None:
747 global_settings[ent] = " ".join(global_param[ent])
749 if targetdir is not None:
750 global_settings["private dir"] = os.path.abspath(os.path.join(targetdir, "private"))
751 global_settings["lock dir"] = os.path.abspath(targetdir)
752 global_settings["state directory"] = os.path.abspath(os.path.join(targetdir, "state"))
753 global_settings["cache directory"] = os.path.abspath(os.path.join(targetdir, "cache"))
754 global_settings["binddns dir"] = os.path.abspath(os.path.join(targetdir, "bind-dns"))
756 lp.set("lock dir", os.path.abspath(targetdir))
757 lp.set("state directory", global_settings["state directory"])
758 lp.set("cache directory", global_settings["cache directory"])
759 lp.set("binddns dir", global_settings["binddns dir"])
761 if eadb:
762 if use_ntvfs:
763 if targetdir is not None:
764 privdir = os.path.join(targetdir, "private")
765 lp.set("posix:eadb",
766 os.path.abspath(os.path.join(privdir, "eadb.tdb")))
767 elif not lp.get("posix:eadb"):
768 privdir = lp.get("private dir")
769 lp.set("posix:eadb",
770 os.path.abspath(os.path.join(privdir, "eadb.tdb")))
771 else:
772 if targetdir is not None:
773 statedir = os.path.join(targetdir, "state")
774 lp.set("xattr_tdb:file",
775 os.path.abspath(os.path.join(statedir, "xattr.tdb")))
776 elif not lp.get("xattr_tdb:file"):
777 statedir = lp.get("state directory")
778 lp.set("xattr_tdb:file",
779 os.path.abspath(os.path.join(statedir, "xattr.tdb")))
781 shares = {}
782 if serverrole == "active directory domain controller":
783 shares["sysvol"] = os.path.join(lp.get("state directory"), "sysvol")
784 shares["netlogon"] = os.path.join(shares["sysvol"], realm.lower(),
785 "scripts")
786 else:
787 global_settings["passdb backend"] = "samba_dsdb"
789 f = open(smbconf, 'w')
790 try:
791 f.write("[globals]\n")
792 for key, val in global_settings.items():
793 f.write("\t%s = %s\n" % (key, val))
794 f.write("\n")
796 for name, path in shares.items():
797 f.write("[%s]\n" % name)
798 f.write("\tpath = %s\n" % path)
799 f.write("\tread only = no\n")
800 f.write("\n")
801 finally:
802 f.close()
803 # reload the smb.conf
804 lp.load(smbconf)
806 # and dump it without any values that are the default
807 # this ensures that any smb.conf parameters that were set
808 # on the provision/join command line are set in the resulting smb.conf
809 lp.dump(False, smbconf)
812 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
813 users_gid):
814 """setup reasonable name mappings for sam names to unix names.
816 :param samdb: SamDB object.
817 :param idmap: IDmap db object.
818 :param sid: The domain sid.
819 :param domaindn: The domain DN.
820 :param root_uid: uid of the UNIX root user.
821 :param nobody_uid: uid of the UNIX nobody user.
822 :param users_gid: gid of the UNIX users group.
824 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
826 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
827 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
830 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
831 provision_backend, names, serverrole,
832 plaintext_secrets=False,
833 backend_store=None):
834 """Setup the partitions for the SAM database.
836 Alternatively, provision() may call this, and then populate the database.
838 :note: This will wipe the Sam Database!
840 :note: This function always removes the local SAM LDB file. The erase
841 parameter controls whether to erase the existing data, which
842 may not be stored locally but in LDAP.
845 assert session_info is not None
847 # We use options=["modules:"] to stop the modules loading - we
848 # just want to wipe and re-initialise the database, not start it up
850 try:
851 os.unlink(samdb_path)
852 except OSError:
853 pass
855 samdb = Ldb(url=samdb_path, session_info=session_info,
856 lp=lp, options=["modules:"])
858 ldap_backend_line = "# No LDAP backend"
859 if provision_backend.type != "ldb":
860 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
862 required_features = None
863 if not plaintext_secrets:
864 required_features = "requiredFeatures: encryptedSecrets"
866 if backend_store is None:
867 backend_store = get_default_backend_store()
868 backend_store_line = "backendStore: %s" % backend_store
870 if backend_store == "mdb":
871 if required_features is not None:
872 required_features += "\n"
873 else:
874 required_features = ""
875 required_features += "requiredFeatures: lmdbLevelOne"
877 if required_features is None:
878 required_features = "# No required features"
880 samdb.transaction_start()
881 try:
882 logger.info("Setting up sam.ldb partitions and settings")
883 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
884 "LDAP_BACKEND_LINE": ldap_backend_line,
885 "BACKEND_STORE": backend_store_line
888 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
889 "BACKEND_TYPE": provision_backend.type,
890 "SERVER_ROLE": serverrole,
891 "REQUIRED_FEATURES": required_features
894 logger.info("Setting up sam.ldb rootDSE")
895 setup_samdb_rootdse(samdb, names)
896 except:
897 samdb.transaction_cancel()
898 raise
899 else:
900 samdb.transaction_commit()
903 def secretsdb_self_join(secretsdb, domain,
904 netbiosname, machinepass, domainsid=None,
905 realm=None, dnsdomain=None,
906 key_version_number=1,
907 secure_channel_type=SEC_CHAN_WKSTA):
908 """Add domain join-specific bits to a secrets database.
910 :param secretsdb: Ldb Handle to the secrets database
911 :param machinepass: Machine password
913 attrs = ["whenChanged",
914 "secret",
915 "priorSecret",
916 "priorChanged",
917 "krb5Keytab",
918 "privateKeytab"]
920 if realm is not None:
921 if dnsdomain is None:
922 dnsdomain = realm.lower()
923 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
924 else:
925 dnsname = None
926 shortname = netbiosname.lower()
928 # We don't need to set msg["flatname"] here, because rdn_name will handle
929 # it, and it causes problems for modifies anyway
930 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
931 msg["secureChannelType"] = [str(secure_channel_type)]
932 msg["objectClass"] = ["top", "primaryDomain"]
933 if dnsname is not None:
934 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
935 msg["realm"] = [realm]
936 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
937 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
938 msg["privateKeytab"] = ["secrets.keytab"]
940 msg["secret"] = [machinepass.encode('utf-8')]
941 msg["samAccountName"] = ["%s$" % netbiosname]
942 msg["secureChannelType"] = [str(secure_channel_type)]
943 if domainsid is not None:
944 msg["objectSid"] = [ndr_pack(domainsid)]
946 # This complex expression tries to ensure that we don't have more
947 # than one record for this SID, realm or netbios domain at a time,
948 # but we don't delete the old record that we are about to modify,
949 # because that would delete the keytab and previous password.
950 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
951 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
952 scope=ldb.SCOPE_ONELEVEL)
954 for del_msg in res:
955 secretsdb.delete(del_msg.dn)
957 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
959 if len(res) == 1:
960 msg["priorSecret"] = [res[0]["secret"][0]]
961 try:
962 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
963 except KeyError:
964 pass
966 try:
967 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
968 except KeyError:
969 pass
971 try:
972 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
973 except KeyError:
974 pass
976 for el in msg:
977 if el != 'dn':
978 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
979 secretsdb.modify(msg)
980 secretsdb.rename(res[0].dn, msg.dn)
981 else:
982 spn = ['HOST/%s' % shortname]
983 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
984 # if we are a domain controller then we add servicePrincipalName
985 # entries for the keytab code to update.
986 spn.extend(['HOST/%s' % dnsname])
987 msg["servicePrincipalName"] = spn
989 secretsdb.add(msg)
992 def setup_secretsdb(paths, session_info, lp):
993 """Setup the secrets database.
995 :note: This function does not handle exceptions and transaction on purpose,
996 it's up to the caller to do this job.
998 :param path: Path to the secrets database.
999 :param session_info: Session info.
1000 :param lp: Loadparm context
1001 :return: LDB handle for the created secrets database
1003 if os.path.exists(paths.secrets):
1004 os.unlink(paths.secrets)
1006 keytab_path = os.path.join(paths.private_dir, paths.keytab)
1007 if os.path.exists(keytab_path):
1008 os.unlink(keytab_path)
1010 bind_dns_keytab_path = os.path.join(paths.binddns_dir, paths.dns_keytab)
1011 if os.path.exists(bind_dns_keytab_path):
1012 os.unlink(bind_dns_keytab_path)
1014 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1015 if os.path.exists(dns_keytab_path):
1016 os.unlink(dns_keytab_path)
1018 path = paths.secrets
1020 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
1021 secrets_ldb.erase()
1022 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
1023 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
1024 secrets_ldb.transaction_start()
1025 try:
1026 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
1027 except:
1028 secrets_ldb.transaction_cancel()
1029 raise
1030 return secrets_ldb
1033 def setup_privileges(path, session_info, lp):
1034 """Setup the privileges database.
1036 :param path: Path to the privileges database.
1037 :param session_info: Session info.
1038 :param lp: Loadparm context
1039 :return: LDB handle for the created secrets database
1041 if os.path.exists(path):
1042 os.unlink(path)
1043 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
1044 privilege_ldb.erase()
1045 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
1048 def setup_encrypted_secrets_key(path):
1049 """Setup the encrypted secrets key file.
1051 Any existing key file will be deleted and a new random key generated.
1053 :param path: Path to the secrets key file.
1056 if os.path.exists(path):
1057 os.unlink(path)
1059 flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
1060 mode = stat.S_IRUSR | stat.S_IWUSR
1062 umask_original = os.umask(0)
1063 try:
1064 fd = os.open(path, flags, mode)
1065 finally:
1066 os.umask(umask_original)
1068 with os.fdopen(fd, 'wb') as f:
1069 key = samba.generate_random_bytes(16)
1070 f.write(key)
1073 def setup_registry(path, session_info, lp):
1074 """Setup the registry.
1076 :param path: Path to the registry database
1077 :param session_info: Session information
1078 :param lp: Loadparm context
1080 reg = samba.registry.Registry()
1081 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
1082 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
1083 provision_reg = setup_path("provision.reg")
1084 assert os.path.exists(provision_reg)
1085 reg.diff_apply(provision_reg)
1088 def setup_idmapdb(path, session_info, lp):
1089 """Setup the idmap database.
1091 :param path: path to the idmap database
1092 :param session_info: Session information
1093 :param lp: Loadparm context
1095 if os.path.exists(path):
1096 os.unlink(path)
1098 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
1099 idmap_ldb.erase()
1100 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
1101 return idmap_ldb
1104 def setup_samdb_rootdse(samdb, names):
1105 """Setup the SamDB rootdse.
1107 :param samdb: Sam Database handle
1109 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
1110 "SCHEMADN": names.schemadn,
1111 "DOMAINDN": names.domaindn,
1112 "ROOTDN": names.rootdn,
1113 "CONFIGDN": names.configdn,
1114 "SERVERDN": names.serverdn,
1118 def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
1119 dns_backend, dnspass, domainsid, next_rid, invocationid,
1120 policyguid, policyguid_dc,
1121 domainControllerFunctionality, ntdsguid=None, dc_rid=None):
1122 """Join a host to its own domain."""
1123 assert isinstance(invocationid, str)
1124 if ntdsguid is not None:
1125 ntdsguid_line = "objectGUID: %s\n" % ntdsguid
1126 else:
1127 ntdsguid_line = ""
1129 if dc_rid is None:
1130 dc_rid = next_rid
1132 # Some clients/applications (like exchange) make use of
1133 # the operatingSystemVersion attribute in order to
1134 # find if a DC is good enough.
1136 # So we better use a value matching a Windows DC
1137 # with the same domainControllerFunctionality level
1138 operatingSystemVersion = samba.dsdb.dc_operatingSystemVersion(domainControllerFunctionality)
1140 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
1141 "CONFIGDN": names.configdn,
1142 "SCHEMADN": names.schemadn,
1143 "DOMAINDN": names.domaindn,
1144 "SERVERDN": names.serverdn,
1145 "INVOCATIONID": invocationid,
1146 "NETBIOSNAME": names.netbiosname,
1147 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1148 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')).decode('utf8'),
1149 "DOMAINSID": str(domainsid),
1150 "DCRID": str(dc_rid),
1151 "OPERATING_SYSTEM": "Samba-%s" % version,
1152 "OPERATING_SYSTEM_VERSION": operatingSystemVersion,
1153 "NTDSGUID": ntdsguid_line,
1154 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1155 domainControllerFunctionality),
1156 "RIDALLOCATIONSTART": str(next_rid + 100),
1157 "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
1159 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1160 "POLICYGUID": policyguid,
1161 "POLICYGUID_DC": policyguid_dc,
1162 "DNSDOMAIN": names.dnsdomain,
1163 "DOMAINDN": names.domaindn})
1165 # If we are setting up a subdomain, then this has been replicated in, so we
1166 # don't need to add it
1167 if fill == FILL_FULL:
1168 setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
1169 "CONFIGDN": names.configdn,
1170 "SCHEMADN": names.schemadn,
1171 "DOMAINDN": names.domaindn,
1172 "SERVERDN": names.serverdn,
1173 "INVOCATIONID": invocationid,
1174 "NETBIOSNAME": names.netbiosname,
1175 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1176 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')).decode('utf8'),
1177 "DOMAINSID": str(domainsid),
1178 "DCRID": str(dc_rid),
1179 "SAMBA_VERSION_STRING": version,
1180 "NTDSGUID": ntdsguid_line,
1181 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1182 domainControllerFunctionality)})
1184 # Setup fSMORoleOwner entries to point at the newly created DC entry
1185 setup_modify_ldif(samdb,
1186 setup_path("provision_self_join_modify_schema.ldif"), {
1187 "SCHEMADN": names.schemadn,
1188 "SERVERDN": names.serverdn,
1190 controls=["provision:0", "relax:0"])
1191 setup_modify_ldif(samdb,
1192 setup_path("provision_self_join_modify_config.ldif"), {
1193 "CONFIGDN": names.configdn,
1194 "DEFAULTSITE": names.sitename,
1195 "NETBIOSNAME": names.netbiosname,
1196 "SERVERDN": names.serverdn,
1199 system_session_info = system_session()
1200 samdb.set_session_info(system_session_info)
1201 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1202 # modify a serverReference under cn=config when we are a subdomain, we must
1203 # be system due to ACLs
1204 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1205 "DOMAINDN": names.domaindn,
1206 "SERVERDN": names.serverdn,
1207 "NETBIOSNAME": names.netbiosname,
1210 samdb.set_session_info(admin_session_info)
1212 if dns_backend != "SAMBA_INTERNAL":
1213 # This is Samba4 specific and should be replaced by the correct
1214 # DNS AD-style setup
1215 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1216 "DNSDOMAIN": names.dnsdomain,
1217 "DOMAINDN": names.domaindn,
1218 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')).decode('utf8'),
1219 "HOSTNAME": names.hostname,
1220 "DNSNAME": '%s.%s' % (
1221 names.netbiosname.lower(), names.dnsdomain.lower())
1225 def getpolicypath(sysvolpath, dnsdomain, guid):
1226 """Return the physical path of policy given its guid.
1228 :param sysvolpath: Path to the sysvol folder
1229 :param dnsdomain: DNS name of the AD domain
1230 :param guid: The GUID of the policy
1231 :return: A string with the complete path to the policy folder
1233 if guid[0] != "{":
1234 guid = "{%s}" % guid
1235 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1236 return policy_path
1239 def create_gpo_struct(policy_path):
1240 if not os.path.exists(policy_path):
1241 os.makedirs(policy_path, 0o775)
1242 f = open(os.path.join(policy_path, "GPT.INI"), 'w')
1243 try:
1244 f.write("[General]\r\nVersion=0")
1245 finally:
1246 f.close()
1247 p = os.path.join(policy_path, "MACHINE")
1248 if not os.path.exists(p):
1249 os.makedirs(p, 0o775)
1250 p = os.path.join(policy_path, "USER")
1251 if not os.path.exists(p):
1252 os.makedirs(p, 0o775)
1255 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1256 """Create the default GPO for a domain
1258 :param sysvolpath: Physical path for the sysvol folder
1259 :param dnsdomain: DNS domain name of the AD domain
1260 :param policyguid: GUID of the default domain policy
1261 :param policyguid_dc: GUID of the default domain controller policy
1263 policy_path = getpolicypath(sysvolpath, dnsdomain, policyguid)
1264 create_gpo_struct(policy_path)
1266 policy_path = getpolicypath(sysvolpath, dnsdomain, policyguid_dc)
1267 create_gpo_struct(policy_path)
1270 # Default the database size to 8Gb
1271 DEFAULT_BACKEND_SIZE = 8 * 1024 * 1024 *1024
1273 def setup_samdb(path, session_info, provision_backend, lp, names,
1274 logger, serverrole, schema, am_rodc=False,
1275 plaintext_secrets=False, backend_store=None,
1276 backend_store_size=None, batch_mode=False):
1277 """Setup a complete SAM Database.
1279 :note: This will wipe the main SAM database file!
1282 # Also wipes the database
1283 setup_samdb_partitions(path, logger=logger, lp=lp,
1284 provision_backend=provision_backend, session_info=session_info,
1285 names=names, serverrole=serverrole, plaintext_secrets=plaintext_secrets,
1286 backend_store=backend_store)
1288 store_size = DEFAULT_BACKEND_SIZE
1289 if backend_store_size:
1290 store_size = backend_store_size
1292 options = []
1293 if backend_store == "mdb":
1294 options.append("lmdb_env_size:" + str(store_size))
1295 if batch_mode:
1296 options.append("batch_mode:1")
1297 if batch_mode:
1298 # Estimate the number of index records in the transaction_index_cache
1299 # Numbers chosen give the prime 202481 for the default backend size,
1300 # which works well for a 100,000 user database
1301 cache_size = int(store_size / 42423) + 1
1302 options.append("transaction_index_cache_size:" + str(cache_size))
1304 # Load the database, but don's load the global schema and don't connect
1305 # quite yet
1306 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1307 lp=lp,
1308 global_schema=False, am_rodc=am_rodc, options=options)
1310 logger.info("Pre-loading the Samba 4 and AD schema")
1312 # Load the schema from the one we computed earlier
1313 samdb.set_schema(schema, write_indices_and_attributes=False)
1315 # Set the NTDS settings DN manually - in order to have it already around
1316 # before the provisioned tree exists and we connect
1317 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1319 # And now we can connect to the DB - the schema won't be loaded from the
1320 # DB
1321 try:
1322 samdb.connect(path, options=options)
1323 except ldb.LdbError as e2:
1324 (num, string_error) = e2.args
1325 if (num == ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS):
1326 raise ProvisioningError("Permission denied connecting to %s, are you running as root?" % path)
1327 else:
1328 raise
1330 # But we have to give it one more kick to have it use the schema
1331 # during provision - it needs, now that it is connected, to write
1332 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1333 samdb.set_schema(schema, write_indices_and_attributes=True)
1335 return samdb
1338 def fill_samdb(samdb, lp, names, logger, policyguid,
1339 policyguid_dc, fill, adminpass, krbtgtpass, machinepass, dns_backend,
1340 dnspass, invocationid, ntdsguid,
1341 dom_for_fun_level=None, schema=None, next_rid=None, dc_rid=None):
1343 if next_rid is None:
1344 next_rid = 1000
1346 # Provision does not make much sense values larger than 1000000000
1347 # as the upper range of the rIDAvailablePool is 1073741823 and
1348 # we don't want to create a domain that cannot allocate rids.
1349 if next_rid < 1000 or next_rid > 1000000000:
1350 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1351 error += "the valid range is %u-%u. The default is %u." % (
1352 1000, 1000000000, 1000)
1353 raise ProvisioningError(error)
1355 domainControllerFunctionality = functional_level.dc_level_from_lp(lp)
1357 # ATTENTION: Do NOT change these default values without discussion with the
1358 # team and/or release manager. They have a big impact on the whole program!
1359 if dom_for_fun_level is None:
1360 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
1362 if dom_for_fun_level > domainControllerFunctionality:
1363 level = functional_level.level_to_string(domainControllerFunctionality)
1364 raise ProvisioningError(f"You want to run SAMBA 4 on a domain and forest function level which itself is higher than its actual DC function level ({level}). This won't work!")
1366 domainFunctionality = dom_for_fun_level
1367 forestFunctionality = dom_for_fun_level
1369 # Set the NTDS settings DN manually - in order to have it already around
1370 # before the provisioned tree exists and we connect
1371 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1373 # Set the domain functionality levels onto the database.
1374 # Various module (the password_hash module in particular) need
1375 # to know what level of AD we are emulating.
1377 # These will be fixed into the database via the database
1378 # modifictions below, but we need them set from the start.
1379 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1380 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1381 samdb.set_opaque_integer("domainControllerFunctionality",
1382 domainControllerFunctionality)
1384 samdb.set_domain_sid(str(names.domainsid))
1385 samdb.set_invocation_id(invocationid)
1387 logger.info("Adding DomainDN: %s" % names.domaindn)
1389 # impersonate domain admin
1390 admin_session_info = admin_session(lp, str(names.domainsid))
1391 samdb.set_session_info(admin_session_info)
1392 if names.domainguid is not None:
1393 domainguid_line = "objectGUID: %s\n-" % names.domainguid
1394 else:
1395 domainguid_line = ""
1397 descr = b64encode(get_domain_descriptor(names.domainsid)).decode('utf8')
1398 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1399 "DOMAINDN": names.domaindn,
1400 "DOMAINSID": str(names.domainsid),
1401 "DESCRIPTOR": descr,
1402 "DOMAINGUID": domainguid_line
1405 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1406 "DOMAINDN": names.domaindn,
1407 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1408 "NEXTRID": str(next_rid),
1409 "DEFAULTSITE": names.sitename,
1410 "CONFIGDN": names.configdn,
1411 "POLICYGUID": policyguid,
1412 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1413 "SAMBA_VERSION_STRING": version,
1414 "MIN_PWD_LENGTH": str(DEFAULT_MIN_PWD_LENGTH)
1417 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1418 if fill == FILL_FULL:
1419 logger.info("Adding configuration container")
1420 descr = b64encode(get_config_descriptor(names.domainsid)).decode('utf8')
1421 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1422 "CONFIGDN": names.configdn,
1423 "DESCRIPTOR": descr,
1426 # The LDIF here was created when the Schema object was constructed
1427 ignore_checks_oid = "local_oid:%s:0" % samba.dsdb.DSDB_CONTROL_SKIP_DUPLICATES_CHECK_OID
1428 schema_controls = [
1429 "provision:0",
1430 "relax:0",
1431 ignore_checks_oid
1434 logger.info("Setting up sam.ldb schema")
1435 samdb.add_ldif(schema.schema_dn_add, controls=schema_controls)
1436 samdb.modify_ldif(schema.schema_dn_modify, controls=schema_controls)
1437 samdb.write_prefixes_from_schema()
1438 samdb.add_ldif(schema.schema_data, controls=schema_controls)
1439 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1440 {"SCHEMADN": names.schemadn},
1441 controls=schema_controls)
1443 # Now register this container in the root of the forest
1444 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1445 msg["subRefs"] = ldb.MessageElement(names.configdn, ldb.FLAG_MOD_ADD,
1446 "subRefs")
1448 deletedobjects_descr = b64encode(get_deletedobjects_descriptor(names.domainsid)).decode('utf8')
1450 samdb.invocation_id = invocationid
1452 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1453 if fill == FILL_FULL:
1454 logger.info("Setting up sam.ldb configuration data")
1456 partitions_descr = b64encode(get_config_partitions_descriptor(names.domainsid)).decode('utf8')
1457 sites_descr = b64encode(get_config_sites_descriptor(names.domainsid)).decode('utf8')
1458 ntdsquotas_descr = b64encode(get_config_ntds_quotas_descriptor(names.domainsid)).decode('utf8')
1459 protected1_descr = b64encode(get_config_delete_protected1_descriptor(names.domainsid)).decode('utf8')
1460 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid)).decode('utf8')
1461 protected2_descr = b64encode(get_config_delete_protected2_descriptor(names.domainsid)).decode('utf8')
1463 if "2008" in schema.base_schema:
1464 # exclude 2012-specific changes if we're using a 2008 schema
1465 incl_2012 = "#"
1466 else:
1467 incl_2012 = ""
1469 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1470 "CONFIGDN": names.configdn,
1471 "NETBIOSNAME": names.netbiosname,
1472 "DEFAULTSITE": names.sitename,
1473 "DNSDOMAIN": names.dnsdomain,
1474 "DOMAIN": names.domain,
1475 "SCHEMADN": names.schemadn,
1476 "DOMAINDN": names.domaindn,
1477 "SERVERDN": names.serverdn,
1478 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1479 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1480 "NTDSQUOTAS_DESCRIPTOR": ntdsquotas_descr,
1481 "DELETEDOBJECTS_DESCRIPTOR": deletedobjects_descr,
1482 "LOSTANDFOUND_DESCRIPTOR": protected1wd_descr,
1483 "SERVICES_DESCRIPTOR": protected1_descr,
1484 "PHYSICALLOCATIONS_DESCRIPTOR": protected1wd_descr,
1485 "FORESTUPDATES_DESCRIPTOR": protected1wd_descr,
1486 "EXTENDEDRIGHTS_DESCRIPTOR": protected2_descr,
1487 "PARTITIONS_DESCRIPTOR": partitions_descr,
1488 "SITES_DESCRIPTOR": sites_descr,
1491 setup_add_ldif(samdb, setup_path("extended-rights.ldif"), {
1492 "CONFIGDN": names.configdn,
1493 "INC2012": incl_2012,
1496 logger.info("Setting up display specifiers")
1497 display_specifiers_ldif = read_ms_ldif(
1498 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1499 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1500 {"CONFIGDN": names.configdn})
1501 check_all_substituted(display_specifiers_ldif)
1502 samdb.add_ldif(display_specifiers_ldif)
1504 logger.info("Modifying display specifiers and extended rights")
1505 setup_modify_ldif(samdb,
1506 setup_path("provision_configuration_modify.ldif"), {
1507 "CONFIGDN": names.configdn,
1508 "DISPLAYSPECIFIERS_DESCRIPTOR": protected2_descr
1511 logger.info("Adding users container")
1512 users_desc = b64encode(get_domain_users_descriptor(names.domainsid)).decode('utf8')
1513 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1514 "DOMAINDN": names.domaindn,
1515 "USERS_DESCRIPTOR": users_desc
1517 logger.info("Modifying users container")
1518 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1519 "DOMAINDN": names.domaindn})
1520 logger.info("Adding computers container")
1521 computers_desc = b64encode(get_domain_computers_descriptor(names.domainsid)).decode('utf8')
1522 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1523 "DOMAINDN": names.domaindn,
1524 "COMPUTERS_DESCRIPTOR": computers_desc
1526 logger.info("Modifying computers container")
1527 setup_modify_ldif(samdb,
1528 setup_path("provision_computers_modify.ldif"), {
1529 "DOMAINDN": names.domaindn})
1530 logger.info("Setting up sam.ldb data")
1531 infrastructure_desc = b64encode(get_domain_infrastructure_descriptor(names.domainsid)).decode('utf8')
1532 lostandfound_desc = b64encode(get_domain_delete_protected2_descriptor(names.domainsid)).decode('utf8')
1533 system_desc = b64encode(get_domain_delete_protected1_descriptor(names.domainsid)).decode('utf8')
1534 builtin_desc = b64encode(get_domain_builtin_descriptor(names.domainsid)).decode('utf8')
1535 controllers_desc = b64encode(get_domain_controllers_descriptor(names.domainsid)).decode('utf8')
1536 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1537 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1538 "DOMAINDN": names.domaindn,
1539 "NETBIOSNAME": names.netbiosname,
1540 "DEFAULTSITE": names.sitename,
1541 "CONFIGDN": names.configdn,
1542 "SERVERDN": names.serverdn,
1543 "RIDAVAILABLESTART": str(next_rid + 600),
1544 "POLICYGUID_DC": policyguid_dc,
1545 "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc,
1546 "DELETEDOBJECTS_DESCRIPTOR": deletedobjects_descr,
1547 "LOSTANDFOUND_DESCRIPTOR": lostandfound_desc,
1548 "SYSTEM_DESCRIPTOR": system_desc,
1549 "BUILTIN_DESCRIPTOR": builtin_desc,
1550 "DOMAIN_CONTROLLERS_DESCRIPTOR": controllers_desc,
1553 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1554 if fill == FILL_FULL:
1555 managedservice_descr = b64encode(get_managed_service_accounts_descriptor(names.domainsid)).decode('utf8')
1556 setup_modify_ldif(samdb,
1557 setup_path("provision_configuration_references.ldif"), {
1558 "CONFIGDN": names.configdn,
1559 "SCHEMADN": names.schemadn})
1561 logger.info("Setting up well known security principals")
1562 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid)).decode('utf8')
1563 setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1564 "CONFIGDN": names.configdn,
1565 "WELLKNOWNPRINCIPALS_DESCRIPTOR": protected1wd_descr,
1566 }, controls=["relax:0", "provision:0"])
1568 if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1569 setup_modify_ldif(samdb,
1570 setup_path("provision_basedn_references.ldif"), {
1571 "DOMAINDN": names.domaindn,
1572 "MANAGEDSERVICE_DESCRIPTOR": managedservice_descr
1575 logger.info("Setting up sam.ldb users and groups")
1576 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1577 "DOMAINDN": names.domaindn,
1578 "DOMAINSID": str(names.domainsid),
1579 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')).decode('utf8'),
1580 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le')).decode('utf8')
1581 }, controls=["relax:0", "provision:0"])
1583 logger.info("Setting up self join")
1584 setup_self_join(samdb, admin_session_info, names=names, fill=fill,
1585 invocationid=invocationid,
1586 dns_backend=dns_backend,
1587 dnspass=dnspass,
1588 machinepass=machinepass,
1589 domainsid=names.domainsid,
1590 next_rid=next_rid,
1591 dc_rid=dc_rid,
1592 policyguid=policyguid,
1593 policyguid_dc=policyguid_dc,
1594 domainControllerFunctionality=domainControllerFunctionality,
1595 ntdsguid=ntdsguid)
1597 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1598 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1599 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE).decode('utf8')
1600 assert isinstance(names.ntdsguid, str)
1602 return samdb
1605 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;FA;;;BA)(A;OICI;0x1200a9;;;SO)(A;OICI;FA;;;SY)(A;OICI;0x1200a9;;;AU)"
1606 POLICIES_ACL = "O:LAG:BAD:P(A;OICI;FA;;;BA)(A;OICI;0x1200a9;;;SO)(A;OICI;FA;;;SY)(A;OICI;0x1200a9;;;AU)(A;OICI;0x1301bf;;;PA)"
1607 SYSVOL_SERVICE = "sysvol"
1610 def set_dir_acl(path, acl, lp, domsid, use_ntvfs, passdb, service=SYSVOL_SERVICE):
1611 session_info = system_session_unix()
1612 setntacl(lp, path, acl, domsid, session_info, use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1613 for root, dirs, files in os.walk(path, topdown=False):
1614 for name in files:
1615 setntacl(lp, os.path.join(root, name), acl, domsid, session_info,
1616 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1617 for name in dirs:
1618 setntacl(lp, os.path.join(root, name), acl, domsid, session_info,
1619 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1622 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb):
1623 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1624 folders beneath.
1626 :param sysvol: Physical path for the sysvol folder
1627 :param dnsdomain: The DNS name of the domain
1628 :param domainsid: The SID of the domain
1629 :param domaindn: The DN of the domain (ie. DC=...)
1630 :param samdb: An LDB object on the SAM db
1631 :param lp: an LP object
1634 # Set ACL for GPO root folder
1635 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1636 session_info = system_session_unix()
1638 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid), session_info,
1639 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=SYSVOL_SERVICE)
1641 res = samdb.search(base="CN=Policies,CN=System,%s" %(domaindn),
1642 attrs=["cn", "nTSecurityDescriptor"],
1643 expression="", scope=ldb.SCOPE_ONELEVEL)
1645 for policy in res:
1646 acl = ndr_unpack(security.descriptor,
1647 policy["nTSecurityDescriptor"][0]).as_sddl()
1648 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1649 set_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1650 str(domainsid), use_ntvfs,
1651 passdb=passdb)
1654 def setsysvolacl(samdb, sysvol, uid, gid, domainsid, dnsdomain,
1655 domaindn, lp, use_ntvfs):
1656 """Set the ACL for the sysvol share and the subfolders
1658 :param samdb: An LDB object on the SAM db
1659 :param sysvol: Physical path for the sysvol folder
1660 :param uid: The UID of the "Administrator" user
1661 :param gid: The GID of the "Domain administrators" group
1662 :param domainsid: The SID of the domain
1663 :param dnsdomain: The DNS name of the domain
1664 :param domaindn: The DN of the domain (ie. DC=...)
1666 s4_passdb = None
1668 if not use_ntvfs:
1669 s3conf = s3param.get_context()
1670 s3conf.load(lp.configfile)
1672 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(sysvol))
1673 try:
1674 try:
1675 smbd.set_simple_acl(file.name, 0o755, system_session_unix(), gid)
1676 except OSError:
1677 if not smbd.have_posix_acls():
1678 # This clue is only strictly correct for RPM and
1679 # Debian-like Linux systems, but hopefully other users
1680 # will get enough clue from it.
1681 raise ProvisioningError("Samba was compiled without the posix ACL support that s3fs requires. "
1682 "Try installing libacl1-dev or libacl-devel, then re-run configure and make.")
1684 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. "
1685 "Try the mounting the filesystem with the 'acl' option.")
1686 try:
1687 smbd.chown(file.name, uid, gid, system_session_unix())
1688 except OSError:
1689 raise ProvisioningError("Unable to chown a file on your filesystem. "
1690 "You may not be running provision as root.")
1691 finally:
1692 file.close()
1694 # This will ensure that the smbd code we are running when setting ACLs
1695 # is initialised with the smb.conf
1696 s3conf = s3param.get_context()
1697 s3conf.load(lp.configfile)
1698 # ensure we are using the right samba_dsdb passdb backend, no matter what
1699 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1700 passdb.reload_static_pdb()
1702 # ensure that we init the samba_dsdb backend, so the domain sid is
1703 # marked in secrets.tdb
1704 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1706 # now ensure everything matches correctly, to avoid weird issues
1707 if passdb.get_global_sam_sid() != domainsid:
1708 raise ProvisioningError('SID as seen by smbd [%s] does not match SID as seen by the provision script [%s]!' % (passdb.get_global_sam_sid(), domainsid))
1710 domain_info = s4_passdb.domain_info()
1711 if domain_info["dom_sid"] != domainsid:
1712 raise ProvisioningError('SID as seen by pdb_samba_dsdb [%s] does not match SID as seen by the provision script [%s]!' % (domain_info["dom_sid"], domainsid))
1714 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1715 raise ProvisioningError('Realm as seen by pdb_samba_dsdb [%s] does not match Realm as seen by the provision script [%s]!' % (domain_info["dns_domain"].upper(), dnsdomain.upper()))
1717 try:
1718 if use_ntvfs:
1719 os.chown(sysvol, -1, gid)
1720 except OSError:
1721 canchown = False
1722 else:
1723 canchown = True
1725 # use admin sid dn as user dn, since admin should own most of the files,
1726 # the operation will be much faster
1727 userdn = '<SID={}-{}>'.format(domainsid, security.DOMAIN_RID_ADMINISTRATOR)
1729 flags = (auth.AUTH_SESSION_INFO_DEFAULT_GROUPS |
1730 auth.AUTH_SESSION_INFO_AUTHENTICATED |
1731 auth.AUTH_SESSION_INFO_SIMPLE_PRIVILEGES)
1733 session_info = auth.user_session(samdb, lp_ctx=lp, dn=userdn,
1734 session_info_flags=flags)
1735 auth.session_info_set_unix(session_info,
1736 lp_ctx=lp,
1737 user_name="Administrator",
1738 uid=uid,
1739 gid=gid)
1741 def _setntacl(path):
1742 """A helper to reuse args"""
1743 return setntacl(
1744 lp, path, SYSVOL_ACL, str(domainsid), session_info,
1745 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=s4_passdb,
1746 service=SYSVOL_SERVICE)
1748 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1749 _setntacl(sysvol)
1750 for root, dirs, files in os.walk(sysvol, topdown=False):
1751 for name in files:
1752 if use_ntvfs and canchown:
1753 os.chown(os.path.join(root, name), -1, gid)
1754 _setntacl(os.path.join(root, name))
1755 for name in dirs:
1756 if use_ntvfs and canchown:
1757 os.chown(os.path.join(root, name), -1, gid)
1758 _setntacl(os.path.join(root, name))
1760 # Set acls on Policy folder and policies folders
1761 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb=s4_passdb)
1764 def acl_type(direct_db_access):
1765 if direct_db_access:
1766 return "DB"
1767 else:
1768 return "VFS"
1771 def check_dir_acl(path, acl, lp, domainsid, direct_db_access):
1772 session_info = system_session_unix()
1773 fsacl = getntacl(lp, path, session_info, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1774 fsacl_sddl = fsacl.as_sddl(domainsid)
1775 if fsacl_sddl != acl:
1776 raise ProvisioningError('%s ACL on GPO directory %s %s does not match expected value %s from GPO object' % (acl_type(direct_db_access), path, fsacl_sddl, acl))
1778 for root, dirs, files in os.walk(path, topdown=False):
1779 for name in files:
1780 fsacl = getntacl(lp, os.path.join(root, name), session_info,
1781 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1782 if fsacl is None:
1783 raise ProvisioningError('%s ACL on GPO file %s not found!' %
1784 (acl_type(direct_db_access),
1785 os.path.join(root, name)))
1786 fsacl_sddl = fsacl.as_sddl(domainsid)
1787 if fsacl_sddl != acl:
1788 raise ProvisioningError('%s ACL on GPO file %s %s does not match expected value %s from GPO object' % (acl_type(direct_db_access), os.path.join(root, name), fsacl_sddl, acl))
1790 for name in dirs:
1791 fsacl = getntacl(lp, os.path.join(root, name), session_info,
1792 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1793 if fsacl is None:
1794 raise ProvisioningError('%s ACL on GPO directory %s not found!'
1795 % (acl_type(direct_db_access),
1796 os.path.join(root, name)))
1797 fsacl_sddl = fsacl.as_sddl(domainsid)
1798 if fsacl_sddl != acl:
1799 raise ProvisioningError('%s ACL on GPO directory %s %s does not match expected value %s from GPO object' % (acl_type(direct_db_access), os.path.join(root, name), fsacl_sddl, acl))
1802 def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1803 direct_db_access):
1804 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1805 folders beneath.
1807 :param sysvol: Physical path for the sysvol folder
1808 :param dnsdomain: The DNS name of the domain
1809 :param domainsid: The SID of the domain
1810 :param domaindn: The DN of the domain (ie. DC=...)
1811 :param samdb: An LDB object on the SAM db
1812 :param lp: an LP object
1815 # Set ACL for GPO root folder
1816 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1817 session_info = system_session_unix()
1818 fsacl = getntacl(lp, root_policy_path, session_info,
1819 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1820 if fsacl is None:
1821 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access), root_policy_path))
1822 fsacl_sddl = fsacl.as_sddl(domainsid)
1823 if fsacl_sddl != POLICIES_ACL:
1824 raise ProvisioningError('%s ACL on policy root %s %s does not match expected value %s from provision' % (acl_type(direct_db_access), root_policy_path, fsacl_sddl, fsacl))
1825 res = samdb.search(base="CN=Policies,CN=System,%s" %(domaindn),
1826 attrs=["cn", "nTSecurityDescriptor"],
1827 expression="", scope=ldb.SCOPE_ONELEVEL)
1829 for policy in res:
1830 acl = ndr_unpack(security.descriptor,
1831 policy["nTSecurityDescriptor"][0]).as_sddl()
1832 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1833 check_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1834 domainsid, direct_db_access)
1837 def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn,
1838 lp):
1839 """Set the ACL for the sysvol share and the subfolders
1841 :param samdb: An LDB object on the SAM db
1842 :param netlogon: Physical path for the netlogon folder
1843 :param sysvol: Physical path for the sysvol folder
1844 :param uid: The UID of the "Administrator" user
1845 :param gid: The GID of the "Domain administrators" group
1846 :param domainsid: The SID of the domain
1847 :param dnsdomain: The DNS name of the domain
1848 :param domaindn: The DN of the domain (ie. DC=...)
1851 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1852 s3conf = s3param.get_context()
1853 s3conf.load(lp.configfile)
1854 # ensure we are using the right samba_dsdb passdb backend, no matter what
1855 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1856 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1857 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1859 # now ensure everything matches correctly, to avoid weird issues
1860 if passdb.get_global_sam_sid() != domainsid:
1861 raise ProvisioningError('SID as seen by smbd [%s] does not match SID as seen by the provision script [%s]!' % (passdb.get_global_sam_sid(), domainsid))
1863 domain_info = s4_passdb.domain_info()
1864 if domain_info["dom_sid"] != domainsid:
1865 raise ProvisioningError('SID as seen by pdb_samba_dsdb [%s] does not match SID as seen by the provision script [%s]!' % (domain_info["dom_sid"], domainsid))
1867 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1868 raise ProvisioningError('Realm as seen by pdb_samba_dsdb [%s] does not match Realm as seen by the provision script [%s]!' % (domain_info["dns_domain"].upper(), dnsdomain.upper()))
1870 # Ensure we can read this directly, and via the smbd VFS
1871 session_info = system_session_unix()
1872 for direct_db_access in [True, False]:
1873 # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1874 for dir_path in [os.path.join(sysvol, dnsdomain), netlogon]:
1875 fsacl = getntacl(lp, dir_path, session_info, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1876 if fsacl is None:
1877 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access), dir_path))
1878 fsacl_sddl = fsacl.as_sddl(domainsid)
1879 if fsacl_sddl != SYSVOL_ACL:
1880 raise ProvisioningError('%s ACL on sysvol directory %s %s does not match expected value %s from provision' % (acl_type(direct_db_access), dir_path, fsacl_sddl, SYSVOL_ACL))
1882 # Check acls on Policy folder and policies folders
1883 check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1884 direct_db_access)
1887 def interface_ips_v4(lp, all_interfaces=False):
1888 """return only IPv4 IPs"""
1889 ips = samba.interface_ips(lp, all_interfaces)
1890 ret = []
1891 for i in ips:
1892 if i.find(':') == -1:
1893 ret.append(i)
1894 return ret
1897 def interface_ips_v6(lp):
1898 """return only IPv6 IPs"""
1899 ips = samba.interface_ips(lp, False)
1900 ret = []
1901 for i in ips:
1902 if i.find(':') != -1:
1903 ret.append(i)
1904 return ret
1907 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1908 schema=None,
1909 samdb_fill=FILL_FULL,
1910 hostip=None, hostip6=None,
1911 next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1912 domainguid=None, policyguid=None, policyguid_dc=None,
1913 invocationid=None, machinepass=None, ntdsguid=None,
1914 dns_backend=None, dnspass=None,
1915 serverrole=None, dom_for_fun_level=None,
1916 lp=None, use_ntvfs=False,
1917 skip_sysvolacl=False):
1918 # create/adapt the group policy GUIDs
1919 # Default GUID for default policy are described at
1920 # "How Core Group Policy Works"
1921 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1922 if policyguid is None:
1923 policyguid = DEFAULT_POLICY_GUID
1924 policyguid = policyguid.upper()
1925 if policyguid_dc is None:
1926 policyguid_dc = DEFAULT_DC_POLICY_GUID
1927 policyguid_dc = policyguid_dc.upper()
1929 if invocationid is None:
1930 invocationid = str(uuid.uuid4())
1932 if krbtgtpass is None:
1933 # Note that the machinepass value is ignored
1934 # as the backend (password_hash.c) will generate its
1935 # own random values for the krbtgt keys
1936 krbtgtpass = samba.generate_random_machine_password(128, 255)
1937 if machinepass is None:
1938 machinepass = samba.generate_random_machine_password(120, 120)
1939 if dnspass is None:
1940 dnspass = samba.generate_random_password(120, 120)
1942 samdb.transaction_start()
1943 try:
1944 samdb = fill_samdb(samdb, lp, names, logger=logger,
1945 schema=schema,
1946 policyguid=policyguid, policyguid_dc=policyguid_dc,
1947 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1948 invocationid=invocationid, machinepass=machinepass,
1949 dns_backend=dns_backend, dnspass=dnspass,
1950 ntdsguid=ntdsguid,
1951 dom_for_fun_level=dom_for_fun_level,
1952 next_rid=next_rid, dc_rid=dc_rid)
1954 # Set up group policies (domain policy and domain controller
1955 # policy)
1956 if serverrole == "active directory domain controller":
1957 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1958 policyguid_dc)
1959 except:
1960 samdb.transaction_cancel()
1961 raise
1962 else:
1963 samdb.transaction_commit()
1965 if serverrole == "active directory domain controller":
1966 # Continue setting up sysvol for GPO. This appears to require being
1967 # outside a transaction.
1968 if not skip_sysvolacl:
1969 setsysvolacl(samdb, paths.sysvol, paths.root_uid,
1970 paths.root_gid, names.domainsid, names.dnsdomain,
1971 names.domaindn, lp, use_ntvfs)
1972 else:
1973 logger.info("Setting acl on sysvol skipped")
1975 secretsdb_self_join(secrets_ldb, domain=names.domain,
1976 realm=names.realm, dnsdomain=names.dnsdomain,
1977 netbiosname=names.netbiosname, domainsid=names.domainsid,
1978 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1980 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1981 # In future, this might be determined from some configuration
1982 kerberos_enctypes = str(ENC_ALL_TYPES)
1984 try:
1985 msg = ldb.Message(ldb.Dn(samdb,
1986 samdb.searchone("distinguishedName",
1987 expression="samAccountName=%s$" % names.netbiosname,
1988 scope=ldb.SCOPE_SUBTREE).decode('utf8')))
1989 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1990 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1991 name="msDS-SupportedEncryptionTypes")
1992 samdb.modify(msg)
1993 except ldb.LdbError as e:
1994 (enum, estr) = e.args
1995 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1996 # It might be that this attribute does not exist in this schema
1997 raise
1999 setup_ad_dns(samdb, secrets_ldb, names, paths, logger,
2000 hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
2001 dnspass=dnspass, os_level=dom_for_fun_level,
2002 fill_level=samdb_fill)
2004 domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
2005 attribute="objectGUID").decode('utf8')
2006 assert isinstance(domainguid, str)
2008 lastProvisionUSNs = get_last_provision_usn(samdb)
2009 maxUSN = get_max_usn(samdb, str(names.rootdn))
2010 if lastProvisionUSNs is not None:
2011 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
2012 else:
2013 set_provision_usn(samdb, 0, maxUSN, invocationid)
2015 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
2016 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
2017 {'NTDSGUID': names.ntdsguid})
2019 # fix any dangling GUIDs from the provision
2020 logger.info("Fixing provision GUIDs")
2021 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
2022 quiet=True)
2023 samdb.transaction_start()
2024 try:
2025 # a small number of GUIDs are missing because of ordering issues in the
2026 # provision code
2027 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
2028 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
2029 scope=ldb.SCOPE_BASE,
2030 attrs=['defaultObjectCategory'])
2031 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
2032 scope=ldb.SCOPE_ONELEVEL,
2033 attrs=['ipsecOwnersReference',
2034 'ipsecFilterReference',
2035 'ipsecISAKMPReference',
2036 'ipsecNegotiationPolicyReference',
2037 'ipsecNFAReference'])
2038 if chk.check_database(DN=names.schemadn, scope=ldb.SCOPE_SUBTREE,
2039 attrs=['attributeId', 'governsId']) != 0:
2040 raise ProvisioningError("Duplicate attributeId or governsId in schema. Must be fixed manually!!")
2041 except:
2042 samdb.transaction_cancel()
2043 raise
2044 else:
2045 samdb.transaction_commit()
2048 _ROLES_MAP = {
2049 "ROLE_STANDALONE": "standalone server",
2050 "ROLE_DOMAIN_MEMBER": "member server",
2051 "ROLE_DOMAIN_BDC": "active directory domain controller",
2052 "ROLE_DOMAIN_PDC": "active directory domain controller",
2053 "dc": "active directory domain controller",
2054 "member": "member server",
2055 "domain controller": "active directory domain controller",
2056 "active directory domain controller": "active directory domain controller",
2057 "member server": "member server",
2058 "standalone": "standalone server",
2059 "standalone server": "standalone server",
2063 def sanitize_server_role(role):
2064 """Sanitize a server role name.
2066 :param role: Server role
2067 :raise ValueError: If the role can not be interpreted
2068 :return: Sanitized server role (one of "member server",
2069 "active directory domain controller", "standalone server")
2071 try:
2072 return _ROLES_MAP[role]
2073 except KeyError:
2074 raise ValueError(role)
2077 def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain):
2078 """Create AD entries for the fake ypserver.
2080 This is needed for being able to manipulate posix attrs via ADUC.
2082 samdb.transaction_start()
2083 try:
2084 logger.info("Setting up fake yp server settings")
2085 setup_add_ldif(samdb, setup_path("ypServ30.ldif"), {
2086 "DOMAINDN": domaindn,
2087 "NETBIOSNAME": netbiosname,
2088 "NISDOMAIN": nisdomain,
2090 except:
2091 samdb.transaction_cancel()
2092 raise
2093 else:
2094 samdb.transaction_commit()
2097 def directory_create_or_exists(path, mode=0o755):
2098 if not os.path.exists(path):
2099 try:
2100 os.mkdir(path, mode)
2101 except OSError as e:
2102 if e.errno in [errno.EEXIST]:
2103 pass
2104 else:
2105 raise ProvisioningError("Failed to create directory %s: %s" % (path, e.strerror))
2108 def determine_host_ip(logger, lp, hostip=None):
2109 if hostip is None:
2110 logger.info("Looking up IPv4 addresses")
2111 hostips = interface_ips_v4(lp)
2112 if len(hostips) > 0:
2113 hostip = hostips[0]
2114 if len(hostips) > 1:
2115 logger.warning("More than one IPv4 address found. Using %s",
2116 hostip)
2117 if hostip == "127.0.0.1":
2118 hostip = None
2119 if hostip is None:
2120 logger.warning("No IPv4 address will be assigned")
2122 return hostip
2125 def determine_host_ip6(logger, lp, hostip6=None):
2126 if hostip6 is None:
2127 logger.info("Looking up IPv6 addresses")
2128 hostips = interface_ips_v6(lp)
2129 if hostips:
2130 hostip6 = hostips[0]
2131 if len(hostips) > 1:
2132 logger.warning("More than one IPv6 address found. Using %s", hostip6)
2133 if hostip6 is None:
2134 logger.warning("No IPv6 address will be assigned")
2136 return hostip6
2139 def provision(logger, session_info, smbconf=None,
2140 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
2141 domaindn=None, schemadn=None, configdn=None, serverdn=None,
2142 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
2143 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None,
2144 krbtgtpass=None, domainguid=None, policyguid=None, policyguid_dc=None,
2145 dns_backend=None, dns_forwarder=None, dnspass=None,
2146 invocationid=None, machinepass=None, ntdsguid=None,
2147 root=None, nobody=None, users=None,
2148 sitename=None, serverrole=None, dom_for_fun_level=None,
2149 useeadb=False, am_rodc=False, lp=None, use_ntvfs=False,
2150 use_rfc2307=False, skip_sysvolacl=True,
2151 base_schema="2019", adprep_level=DS_DOMAIN_FUNCTION_2016,
2152 plaintext_secrets=False, backend_store=None,
2153 backend_store_size=None, batch_mode=False):
2154 """Provision samba4
2156 :note: caution, this wipes all existing data!
2159 try:
2160 serverrole = sanitize_server_role(serverrole)
2161 except ValueError:
2162 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole)
2164 if dom_for_fun_level is None:
2165 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
2167 if base_schema in ["2008_R2", "2008_R2_old"]:
2168 max_adprep_level = DS_DOMAIN_FUNCTION_2008_R2
2169 elif base_schema in ["2012"]:
2170 max_adprep_level = DS_DOMAIN_FUNCTION_2012
2171 elif base_schema in ["2012_R2"]:
2172 max_adprep_level = DS_DOMAIN_FUNCTION_2012_R2
2173 else:
2174 max_adprep_level = DS_DOMAIN_FUNCTION_2016
2176 if max_adprep_level < dom_for_fun_level:
2177 raise ProvisioningError('dom_for_fun_level[%u] incompatible with base_schema[%s]' %
2178 (dom_for_fun_level, base_schema))
2180 if adprep_level is not None and max_adprep_level < adprep_level:
2181 raise ProvisioningError('base_schema[%s] incompatible with adprep_level[%u]' %
2182 (base_schema, adprep_level))
2184 if adprep_level is not None and adprep_level < dom_for_fun_level:
2185 raise ProvisioningError('dom_for_fun_level[%u] incompatible with adprep_level[%u]' %
2186 (dom_for_fun_level, adprep_level))
2188 if ldapadminpass is None:
2189 # Make a new, random password between Samba and it's LDAP server
2190 ldapadminpass = samba.generate_random_password(128, 255)
2192 if backend_store is None:
2193 backend_store = get_default_backend_store()
2195 if domainsid is None:
2196 domainsid = security.random_sid()
2198 root_uid = get_root_uid([root or "root"], logger)
2199 nobody_uid = findnss_uid([nobody or "nobody"])
2200 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
2201 root_gid = pwd.getpwuid(root_uid).pw_gid
2203 try:
2204 bind_gid = findnss_gid(["bind", "named"])
2205 except KeyError:
2206 bind_gid = None
2208 if targetdir is not None:
2209 smbconf = os.path.join(targetdir, "etc", "smb.conf")
2210 elif smbconf is None:
2211 smbconf = samba.param.default_path()
2212 if not os.path.exists(os.path.dirname(smbconf)):
2213 os.makedirs(os.path.dirname(smbconf))
2215 server_services = []
2216 global_param = {}
2217 if use_rfc2307:
2218 global_param["idmap_ldb:use rfc2307"] = ["yes"]
2220 if dns_backend != "SAMBA_INTERNAL":
2221 server_services.append("-dns")
2222 else:
2223 if dns_forwarder is not None:
2224 global_param["dns forwarder"] = [dns_forwarder]
2226 if use_ntvfs:
2227 server_services.append("+smb")
2228 server_services.append("-s3fs")
2229 global_param["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
2231 if len(server_services) > 0:
2232 global_param["server services"] = server_services
2234 # only install a new smb.conf if there isn't one there already
2235 if os.path.exists(smbconf):
2236 # if Samba Team members can't figure out the weird errors
2237 # loading an empty smb.conf gives, then we need to be smarter.
2238 # Pretend it just didn't exist --abartlet
2239 f = open(smbconf, 'r')
2240 try:
2241 data = f.read().lstrip()
2242 finally:
2243 f.close()
2244 if data is None or data == "":
2245 make_smbconf(smbconf, hostname, domain, realm,
2246 targetdir, serverrole=serverrole,
2247 eadb=useeadb, use_ntvfs=use_ntvfs,
2248 lp=lp, global_param=global_param)
2249 else:
2250 make_smbconf(smbconf, hostname, domain, realm, targetdir,
2251 serverrole=serverrole,
2252 eadb=useeadb, use_ntvfs=use_ntvfs, lp=lp, global_param=global_param)
2254 if lp is None:
2255 lp = samba.param.LoadParm()
2256 lp.load(smbconf)
2257 names = guess_names(lp=lp, hostname=hostname, domain=domain,
2258 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
2259 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
2260 sitename=sitename, rootdn=rootdn, domain_names_forced=(samdb_fill == FILL_DRS))
2261 paths = provision_paths_from_lp(lp, names.dnsdomain)
2263 paths.bind_gid = bind_gid
2264 paths.root_uid = root_uid
2265 paths.root_gid = root_gid
2267 hostip = determine_host_ip(logger, lp, hostip)
2268 hostip6 = determine_host_ip6(logger, lp, hostip6)
2269 names.hostip = hostip
2270 names.hostip6 = hostip6
2271 names.domainguid = domainguid
2272 names.domainsid = domainsid
2273 names.forestsid = domainsid
2275 if serverrole is None:
2276 serverrole = lp.get("server role")
2278 directory_create_or_exists(paths.private_dir, 0o700)
2279 directory_create_or_exists(paths.binddns_dir, 0o770)
2280 directory_create_or_exists(os.path.join(paths.private_dir, "tls"))
2281 directory_create_or_exists(paths.state_dir)
2282 if not plaintext_secrets:
2283 setup_encrypted_secrets_key(paths.encrypted_secrets_key_path)
2285 if paths.sysvol and not os.path.exists(paths.sysvol):
2286 os.makedirs(paths.sysvol, 0o775)
2288 schema = Schema(domainsid, invocationid=invocationid,
2289 schemadn=names.schemadn, base_schema=base_schema)
2291 provision_backend = LDBBackend(paths=paths,
2292 lp=lp,
2293 names=names, logger=logger)
2295 provision_backend.init()
2296 provision_backend.start()
2298 # only install a new shares config db if there is none
2299 if not os.path.exists(paths.shareconf):
2300 logger.info("Setting up share.ldb")
2301 share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
2302 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
2304 logger.info("Setting up secrets.ldb")
2305 secrets_ldb = setup_secretsdb(paths,
2306 session_info=session_info, lp=lp)
2308 try:
2309 logger.info("Setting up the registry")
2310 setup_registry(paths.hklm, session_info, lp=lp)
2312 logger.info("Setting up the privileges database")
2313 setup_privileges(paths.privilege, session_info, lp=lp)
2315 logger.info("Setting up idmap db")
2316 idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
2318 setup_name_mappings(idmap, sid=str(domainsid),
2319 root_uid=root_uid, nobody_uid=nobody_uid,
2320 users_gid=users_gid)
2322 logger.info("Setting up SAM db")
2323 samdb = setup_samdb(paths.samdb, session_info,
2324 provision_backend, lp, names, logger=logger,
2325 serverrole=serverrole,
2326 schema=schema, am_rodc=am_rodc,
2327 plaintext_secrets=plaintext_secrets,
2328 backend_store=backend_store,
2329 backend_store_size=backend_store_size,
2330 batch_mode=batch_mode)
2332 if serverrole == "active directory domain controller":
2333 if paths.netlogon is None:
2334 raise MissingShareError("netlogon", paths.smbconf)
2336 if paths.sysvol is None:
2337 raise MissingShareError("sysvol", paths.smbconf)
2339 if not os.path.isdir(paths.netlogon):
2340 os.makedirs(paths.netlogon, 0o755)
2342 if adminpass is None:
2343 adminpass = samba.generate_random_password(12, 32)
2344 adminpass_generated = True
2345 else:
2346 if isinstance(adminpass, bytes):
2347 adminpass = adminpass.decode('utf-8')
2348 adminpass_generated = False
2350 if samdb_fill == FILL_FULL:
2351 provision_fill(samdb, secrets_ldb, logger, names, paths,
2352 schema=schema, samdb_fill=samdb_fill,
2353 hostip=hostip, hostip6=hostip6,
2354 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
2355 krbtgtpass=krbtgtpass,
2356 policyguid=policyguid, policyguid_dc=policyguid_dc,
2357 invocationid=invocationid, machinepass=machinepass,
2358 ntdsguid=ntdsguid, dns_backend=dns_backend,
2359 dnspass=dnspass, serverrole=serverrole,
2360 dom_for_fun_level=dom_for_fun_level,
2361 lp=lp, use_ntvfs=use_ntvfs,
2362 skip_sysvolacl=skip_sysvolacl)
2364 if adprep_level is not None:
2365 updates_allowed_overridden = False
2366 if lp.get("dsdb:schema update allowed") is None:
2367 lp.set("dsdb:schema update allowed", "yes")
2368 print("Temporarily overriding 'dsdb:schema update allowed' setting")
2369 updates_allowed_overridden = True
2371 samdb.transaction_start()
2372 try:
2373 from samba.forest_update import ForestUpdate
2374 forest = ForestUpdate(samdb, fix=True)
2376 forest.check_updates_iterator([11, 54, 79, 80, 81, 82, 83])
2377 forest.check_updates_functional_level(adprep_level,
2378 DS_DOMAIN_FUNCTION_2008_R2,
2379 update_revision=True)
2381 samdb.transaction_commit()
2382 except Exception as e:
2383 samdb.transaction_cancel()
2384 raise e
2386 samdb.transaction_start()
2387 try:
2388 from samba.domain_update import DomainUpdate
2390 DomainUpdate(samdb, fix=True).check_updates_functional_level(
2391 adprep_level,
2392 DS_DOMAIN_FUNCTION_2008,
2393 update_revision=True,
2396 samdb.transaction_commit()
2397 except Exception as e:
2398 samdb.transaction_cancel()
2399 raise e
2401 if updates_allowed_overridden:
2402 lp.set("dsdb:schema update allowed", "no")
2404 if not is_heimdal_built():
2405 create_kdc_conf(paths.kdcconf, realm, domain, os.path.dirname(lp.get("log file")))
2406 logger.info("The Kerberos KDC configuration for Samba AD is "
2407 "located at %s", paths.kdcconf)
2409 create_krb5_conf(paths.krb5conf,
2410 dnsdomain=names.dnsdomain, hostname=names.hostname,
2411 realm=names.realm)
2412 logger.info("A Kerberos configuration suitable for Samba AD has been "
2413 "generated at %s", paths.krb5conf)
2414 logger.info("Merge the contents of this file with your system "
2415 "krb5.conf or replace it with this one. Do not create a "
2416 "symlink!")
2418 if serverrole == "active directory domain controller":
2419 create_dns_update_list(paths)
2421 backend_result = provision_backend.post_setup()
2422 provision_backend.shutdown()
2424 except:
2425 secrets_ldb.transaction_cancel()
2426 raise
2428 # Now commit the secrets.ldb to disk
2429 secrets_ldb.transaction_commit()
2431 # the commit creates the dns.keytab in the private directory
2432 create_dns_dir_keytab_link(logger, paths)
2434 result = ProvisionResult()
2435 result.server_role = serverrole
2436 result.domaindn = domaindn
2437 result.paths = paths
2438 result.names = names
2439 result.lp = lp
2440 result.samdb = samdb
2441 result.idmap = idmap
2442 result.domainsid = str(domainsid)
2444 if samdb_fill == FILL_FULL:
2445 result.adminpass_generated = adminpass_generated
2446 result.adminpass = adminpass
2447 else:
2448 result.adminpass_generated = False
2449 result.adminpass = None
2451 result.backend_result = backend_result
2453 if use_rfc2307:
2454 provision_fake_ypserver(logger=logger, samdb=samdb,
2455 domaindn=names.domaindn, netbiosname=names.netbiosname,
2456 nisdomain=names.domain.lower())
2458 return result
2461 def provision_become_dc(smbconf=None, targetdir=None, realm=None,
2462 rootdn=None, domaindn=None, schemadn=None,
2463 configdn=None, serverdn=None, domain=None,
2464 hostname=None, domainsid=None,
2465 machinepass=None, dnspass=None,
2466 dns_backend=None, sitename=None, debuglevel=1,
2467 use_ntvfs=False):
2469 logger = logging.getLogger("provision")
2470 samba.set_debug_level(debuglevel)
2472 res = provision(logger, system_session(),
2473 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
2474 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
2475 configdn=configdn, serverdn=serverdn, domain=domain,
2476 hostname=hostname, hostip=None, domainsid=domainsid,
2477 machinepass=machinepass,
2478 serverrole="active directory domain controller",
2479 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass,
2480 use_ntvfs=use_ntvfs)
2481 res.lp.set("debuglevel", str(debuglevel))
2482 return res
2485 def create_krb5_conf(path, dnsdomain, hostname, realm):
2486 """Write out a file containing a valid krb5.conf file
2488 :param path: Path of the new krb5.conf file.
2489 :param dnsdomain: DNS Domain name
2490 :param hostname: Local hostname
2491 :param realm: Realm name
2493 setup_file(setup_path("krb5.conf"), path, {
2494 "DNSDOMAIN": dnsdomain,
2495 "HOSTNAME": hostname,
2496 "REALM": realm,
2500 class ProvisioningError(Exception):
2501 """A generic provision error."""
2503 def __init__(self, value):
2504 self.value = value
2506 def __str__(self):
2507 return "ProvisioningError: " + self.value
2510 class InvalidNetbiosName(Exception):
2511 """A specified name was not a valid NetBIOS name."""
2513 def __init__(self, name):
2514 super().__init__(
2515 "The name '%r' is not a valid NetBIOS name" % name)
2518 class MissingShareError(ProvisioningError):
2520 def __init__(self, name, smbconf):
2521 super().__init__(
2522 "Existing smb.conf does not have a [%s] share, but you are "
2523 "configuring a DC. Please remove %s or add the share manually." %
2524 (name, smbconf))