s3:ntlm_auth: make logs more consistent with length check
[Samba.git] / python / samba / provision / __init__.py
blobdea50aa364e67849c674d1b63117db6ca62d7b13
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.gkdi import (
77 KEY_CYCLE_DURATION,
78 MAX_CLOCK_SKEW
80 from samba.idmap import IDmapDB
81 from samba.ms_display_specifiers import read_ms_ldif
82 from samba.ntacls import setntacl, getntacl, dsacl2fsacl
83 from samba.nt_time import nt_now
84 from samba.ndr import ndr_pack, ndr_unpack
85 from samba.provision.backend import (
86 LDBBackend,
88 from samba.descriptor import (
89 get_deletedobjects_descriptor,
90 get_config_descriptor,
91 get_config_partitions_descriptor,
92 get_config_sites_descriptor,
93 get_config_ntds_quotas_descriptor,
94 get_config_delete_protected1_descriptor,
95 get_config_delete_protected1wd_descriptor,
96 get_config_delete_protected2_descriptor,
97 get_domain_descriptor,
98 get_domain_infrastructure_descriptor,
99 get_domain_builtin_descriptor,
100 get_domain_computers_descriptor,
101 get_domain_users_descriptor,
102 get_domain_controllers_descriptor,
103 get_domain_delete_protected1_descriptor,
104 get_domain_delete_protected2_descriptor,
105 get_managed_service_accounts_descriptor,
107 from samba.provision.common import (
108 setup_path,
109 setup_add_ldif,
110 setup_modify_ldif,
111 FILL_FULL,
112 FILL_SUBDOMAIN,
113 FILL_DRS
115 from samba.provision.sambadns import (
116 get_dnsadmins_sid,
117 setup_ad_dns,
118 create_dns_dir_keytab_link,
119 create_dns_update_list
122 import samba.param
123 import samba.registry
124 from samba.schema import Schema
125 from samba.samdb import SamDB
126 from samba.dbchecker import dbcheck
127 from samba.provision.kerberos import create_kdc_conf
128 from samba.samdb import get_default_backend_store
129 from samba import functional_level
131 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
132 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04FB984F9"
133 DEFAULTSITE = "Default-First-Site-Name"
134 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
136 DEFAULT_MIN_PWD_LENGTH = 7
139 class ProvisionPaths(object):
141 def __init__(self):
142 self.shareconf = None
143 self.hklm = None
144 self.hkcu = None
145 self.hkcr = None
146 self.hku = None
147 self.hkpd = None
148 self.hkpt = None
149 self.samdb = None
150 self.idmapdb = None
151 self.secrets = None
152 self.keytab = None
153 self.dns_keytab = None
154 self.dns = None
155 self.winsdb = None
156 self.private_dir = None
157 self.binddns_dir = None
158 self.state_dir = None
161 class ProvisionNames(object):
163 def __init__(self):
164 self.ncs = None
165 self.rootdn = None
166 self.domaindn = None
167 self.configdn = None
168 self.schemadn = None
169 self.dnsforestdn = None
170 self.dnsdomaindn = None
171 self.ldapmanagerdn = None
172 self.dnsdomain = None
173 self.realm = None
174 self.netbiosname = None
175 self.domain = None
176 self.hostname = None
177 self.sitename = None
178 self.smbconf = None
179 self.domainsid = None
180 self.forestsid = None
181 self.domainguid = None
182 self.name_map = {}
185 def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf,
186 lp):
187 """Get key provision parameters (realm, domain, ...) from a given provision
189 :param samdb: An LDB object connected to the sam.ldb file
190 :param secretsdb: An LDB object connected to the secrets.ldb file
191 :param idmapdb: An LDB object connected to the idmap.ldb file
192 :param paths: A list of path to provision object
193 :param smbconf: Path to the smb.conf file
194 :param lp: A LoadParm object
195 :return: A list of key provision parameters
197 names = ProvisionNames()
198 names.adminpass = None
200 # NT domain, kerberos realm, root dn, domain dn, domain dns name
201 names.domain = lp.get("workgroup").upper()
202 names.realm = lp.get("realm")
203 names.dnsdomain = names.realm.lower()
204 basedn = samba.dn_from_dns_name(names.dnsdomain)
205 names.realm = names.realm.upper()
206 # netbiosname
207 # Get the netbiosname first (could be obtained from smb.conf in theory)
208 res = secretsdb.search(expression="(flatname=%s)" %
209 names.domain, base="CN=Primary Domains",
210 scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"])
211 names.netbiosname = str(res[0]["sAMAccountName"]).replace("$", "")
213 names.smbconf = smbconf
215 # That's a bit simplistic but it's ok as long as we have only 3
216 # partitions
217 current = samdb.search(expression="(objectClass=*)",
218 base="", scope=ldb.SCOPE_BASE,
219 attrs=["defaultNamingContext", "schemaNamingContext",
220 "configurationNamingContext", "rootDomainNamingContext",
221 "namingContexts"])
223 names.configdn = str(current[0]["configurationNamingContext"][0])
224 names.schemadn = str(current[0]["schemaNamingContext"][0])
225 if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb,
226 current[0]["defaultNamingContext"][0].decode('utf8')))):
227 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
228 "is not the same ..." % (paths.samdb,
229 str(current[0]["defaultNamingContext"][0].decode('utf8')),
230 paths.smbconf, basedn)))
232 names.domaindn = str(current[0]["defaultNamingContext"][0])
233 names.rootdn = str(current[0]["rootDomainNamingContext"][0])
234 names.ncs = current[0]["namingContexts"]
235 names.dnsforestdn = None
236 names.dnsdomaindn = None
238 for i in range(0, len(names.ncs)):
239 nc = str(names.ncs[i])
241 dnsforestdn = "DC=ForestDnsZones,%s" % (str(names.rootdn))
242 if nc == dnsforestdn:
243 names.dnsforestdn = dnsforestdn
244 continue
246 dnsdomaindn = "DC=DomainDnsZones,%s" % (str(names.domaindn))
247 if nc == dnsdomaindn:
248 names.dnsdomaindn = dnsdomaindn
249 continue
251 # default site name
252 res3 = samdb.search(expression="(objectClass=site)",
253 base="CN=Sites," + str(names.configdn), scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
254 names.sitename = str(res3[0]["cn"])
256 # dns hostname and server dn
257 res4 = samdb.search(expression="(CN=%s)" % names.netbiosname,
258 base="OU=Domain Controllers,%s" % basedn,
259 scope=ldb.SCOPE_ONELEVEL, attrs=["dNSHostName"])
260 if len(res4) == 0:
261 raise ProvisioningError("Unable to find DC called CN=%s under OU=Domain Controllers,%s" % (names.netbiosname, basedn))
263 names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain, "")
265 server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
266 attrs=[], base=names.configdn)
267 names.serverdn = str(server_res[0].dn)
269 # invocation id/objectguid
270 res5 = samdb.search(expression="(objectClass=*)",
271 base="CN=NTDS Settings,%s" % str(names.serverdn),
272 scope=ldb.SCOPE_BASE,
273 attrs=["invocationID", "objectGUID"])
274 names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0]))
275 names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0]))
277 # domain guid/sid
278 res6 = samdb.search(expression="(objectClass=*)", base=basedn,
279 scope=ldb.SCOPE_BASE, attrs=["objectGUID",
280 "objectSid", "msDS-Behavior-Version"])
281 names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
282 names.domainsid = ndr_unpack(security.dom_sid, res6[0]["objectSid"][0])
283 names.forestsid = ndr_unpack(security.dom_sid, res6[0]["objectSid"][0])
284 if res6[0].get("msDS-Behavior-Version") is None or \
285 int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
286 names.domainlevel = DS_DOMAIN_FUNCTION_2000
287 else:
288 names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
290 # policy guid
291 res7 = samdb.search(expression="(name={%s})" % DEFAULT_POLICY_GUID,
292 base="CN=Policies,CN=System," + basedn,
293 scope=ldb.SCOPE_ONELEVEL, attrs=["cn", "displayName"])
294 names.policyid = str(res7[0]["cn"]).replace("{", "").replace("}", "")
295 # dc policy guid
296 res8 = samdb.search(expression="(name={%s})" % DEFAULT_DC_POLICY_GUID,
297 base="CN=Policies,CN=System," + basedn,
298 scope=ldb.SCOPE_ONELEVEL,
299 attrs=["cn", "displayName"])
300 if len(res8) == 1:
301 names.policyid_dc = str(res8[0]["cn"]).replace("{", "").replace("}", "")
302 else:
303 names.policyid_dc = None
305 res9 = idmapdb.search(expression="(cn=%s-%s)" %
306 (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR),
307 attrs=["xidNumber", "type"])
308 if len(res9) != 1:
309 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid (%s-%s" % (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR))
310 if str(res9[0]["type"][0]) == "ID_TYPE_BOTH":
311 names.root_gid = int(res9[0]["xidNumber"][0])
312 else:
313 names.root_gid = pwd.getpwuid(int(res9[0]["xidNumber"][0])).pw_gid
315 res10 = samdb.search(expression="(samaccountname=dns)",
316 scope=ldb.SCOPE_SUBTREE, attrs=["dn"],
317 controls=["search_options:1:2"])
318 if (len(res10) > 0):
319 has_legacy_dns_account = True
320 else:
321 has_legacy_dns_account = False
323 res11 = samdb.search(expression="(samaccountname=dns-%s)" % names.netbiosname,
324 scope=ldb.SCOPE_SUBTREE, attrs=["dn"],
325 controls=["search_options:1:2"])
326 if (len(res11) > 0):
327 has_dns_account = True
328 else:
329 has_dns_account = False
331 if names.dnsdomaindn is not None:
332 if has_dns_account:
333 names.dns_backend = 'BIND9_DLZ'
334 else:
335 names.dns_backend = 'SAMBA_INTERNAL'
336 elif has_dns_account or has_legacy_dns_account:
337 names.dns_backend = 'BIND9_FLATFILE'
338 else:
339 names.dns_backend = 'NONE'
341 dns_admins_sid = get_dnsadmins_sid(samdb, names.domaindn)
342 names.name_map['DnsAdmins'] = str(dns_admins_sid)
344 return names
347 def update_provision_usn(samdb, low, high, id, replace=False):
348 """Update the field provisionUSN in sam.ldb
350 This field is used to track range of USN modified by provision and
351 upgradeprovision.
352 This value is used afterward by next provision to figure out if
353 the field have been modified since last provision.
355 :param samdb: An LDB object connect to sam.ldb
356 :param low: The lowest USN modified by this upgrade
357 :param high: The highest USN modified by this upgrade
358 :param id: The invocation id of the samba's dc
359 :param replace: A boolean indicating if the range should replace any
360 existing one or appended (default)
363 tab = []
364 if not replace:
365 entry = samdb.search(base="@PROVISION",
366 scope=ldb.SCOPE_BASE,
367 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
368 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
369 if not re.search(';', str(e)):
370 e = "%s;%s" % (str(e), id)
371 tab.append(str(e))
373 tab.append("%s-%s;%s" % (low, high, id))
374 delta = ldb.Message()
375 delta.dn = ldb.Dn(samdb, "@PROVISION")
376 delta[LAST_PROVISION_USN_ATTRIBUTE] = \
377 ldb.MessageElement(tab,
378 ldb.FLAG_MOD_REPLACE,
379 LAST_PROVISION_USN_ATTRIBUTE)
380 entry = samdb.search(expression='provisionnerID=*',
381 base="@PROVISION", scope=ldb.SCOPE_BASE,
382 attrs=["provisionnerID"])
383 if len(entry) == 0 or len(entry[0]) == 0:
384 delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
385 samdb.modify(delta)
388 def set_provision_usn(samdb, low, high, id):
389 """Set the field provisionUSN in sam.ldb
390 This field is used to track range of USN modified by provision and
391 upgradeprovision.
392 This value is used afterward by next provision to figure out if
393 the field have been modified since last provision.
395 :param samdb: An LDB object connect to sam.ldb
396 :param low: The lowest USN modified by this upgrade
397 :param high: The highest USN modified by this upgrade
398 :param id: The invocationId of the provision"""
400 tab = []
401 tab.append("%s-%s;%s" % (low, high, id))
403 delta = ldb.Message()
404 delta.dn = ldb.Dn(samdb, "@PROVISION")
405 delta[LAST_PROVISION_USN_ATTRIBUTE] = \
406 ldb.MessageElement(tab,
407 ldb.FLAG_MOD_ADD,
408 LAST_PROVISION_USN_ATTRIBUTE)
409 samdb.add(delta)
412 def get_max_usn(samdb, basedn):
413 """ This function return the biggest USN present in the provision
415 :param samdb: A LDB object pointing to the sam.ldb
416 :param basedn: A string containing the base DN of the provision
417 (ie. DC=foo, DC=bar)
418 :return: The biggest USN in the provision"""
420 res = samdb.search(expression="objectClass=*", base=basedn,
421 scope=ldb.SCOPE_SUBTREE, attrs=["uSNChanged"],
422 controls=["search_options:1:2",
423 "server_sort:1:1:uSNChanged",
424 "paged_results:1:1"])
425 return res[0]["uSNChanged"]
428 def get_last_provision_usn(sam):
429 """Get USNs ranges modified by a provision or an upgradeprovision
431 :param sam: An LDB object pointing to the sam.ldb
432 :return: a dictionary which keys are invocation id and values are an array
433 of integer representing the different ranges
435 try:
436 entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE,
437 base="@PROVISION", scope=ldb.SCOPE_BASE,
438 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
439 except ldb.LdbError as e1:
440 (ecode, emsg) = e1.args
441 if ecode == ldb.ERR_NO_SUCH_OBJECT:
442 return None
443 raise
444 if len(entry) > 0:
445 myids = []
446 range = {}
447 p = re.compile(r'-')
448 if entry[0].get("provisionnerID"):
449 for e in entry[0]["provisionnerID"]:
450 myids.append(str(e))
451 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
452 tab1 = str(r).split(';')
453 if len(tab1) == 2:
454 id = tab1[1]
455 else:
456 id = "default"
457 if (len(myids) > 0 and id not in myids):
458 continue
459 tab2 = p.split(tab1[0])
460 if range.get(id) is None:
461 range[id] = []
462 range[id].append(tab2[0])
463 range[id].append(tab2[1])
464 return range
465 else:
466 return None
469 class ProvisionResult(object):
470 """Result of a provision.
472 :ivar server_role: The server role
473 :ivar paths: ProvisionPaths instance
474 :ivar domaindn: The domain dn, as string
477 def __init__(self):
478 self.server_role = None
479 self.paths = None
480 self.domaindn = None
481 self.lp = None
482 self.samdb = None
483 self.idmap = None
484 self.names = None
485 self.domainsid = None
486 self.adminpass_generated = None
487 self.adminpass = None
488 self.backend_result = None
490 def report_logger(self, logger):
491 """Report this provision result to a logger."""
492 logger.info(
493 "Once the above files are installed, your Samba AD server will "
494 "be ready to use")
495 if self.adminpass_generated:
496 logger.info("Admin password: %s", self.adminpass)
497 logger.info("Server Role: %s", self.server_role)
498 logger.info("Hostname: %s", self.names.hostname)
499 logger.info("NetBIOS Domain: %s", self.names.domain)
500 logger.info("DNS Domain: %s", self.names.dnsdomain)
501 logger.info("DOMAIN SID: %s", self.domainsid)
503 if self.backend_result:
504 self.backend_result.report_logger(logger)
507 def findnss(nssfn, names):
508 """Find a user or group from a list of possibilities.
510 :param nssfn: NSS Function to try (should raise KeyError if not found)
511 :param names: Names to check.
512 :return: Value return by first names list.
514 for name in names:
515 try:
516 return nssfn(name)
517 except KeyError:
518 pass
519 raise KeyError("Unable to find user/group in %r" % names)
522 def findnss_uid(names):
523 return findnss(pwd.getpwnam, names)[2]
526 def findnss_gid(names):
527 return findnss(grp.getgrnam, names)[2]
530 def get_root_uid(root, logger):
531 try:
532 root_uid = findnss_uid(root)
533 except KeyError as e:
534 logger.info(e)
535 logger.info("Assuming root user has UID zero")
536 root_uid = 0
537 return root_uid
540 def provision_paths_from_lp(lp, dnsdomain):
541 """Set the default paths for provisioning.
543 :param lp: Loadparm context.
544 :param dnsdomain: DNS Domain name
546 paths = ProvisionPaths()
547 paths.private_dir = lp.get("private dir")
548 paths.binddns_dir = lp.get("binddns dir")
549 paths.state_dir = lp.get("state directory")
551 # This is stored without path prefix for the "privateKeytab" attribute in
552 # "secrets_dns.ldif".
553 paths.dns_keytab = "dns.keytab"
554 paths.keytab = "secrets.keytab"
556 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
557 paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
558 paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
559 paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
560 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
561 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
562 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
563 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
564 paths.kdcconf = os.path.join(paths.private_dir, "kdc.conf")
565 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
566 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
567 paths.encrypted_secrets_key_path = os.path.join(
568 paths.private_dir,
569 "encrypted_secrets.key")
571 paths.dns = os.path.join(paths.binddns_dir, "dns", dnsdomain + ".zone")
572 paths.namedconf = os.path.join(paths.binddns_dir, "named.conf")
573 paths.namedconf_update = os.path.join(paths.binddns_dir, "named.conf.update")
574 paths.namedtxt = os.path.join(paths.binddns_dir, "named.txt")
576 paths.hklm = "hklm.ldb"
577 paths.hkcr = "hkcr.ldb"
578 paths.hkcu = "hkcu.ldb"
579 paths.hku = "hku.ldb"
580 paths.hkpd = "hkpd.ldb"
581 paths.hkpt = "hkpt.ldb"
582 paths.sysvol = lp.get("path", "sysvol")
583 paths.netlogon = lp.get("path", "netlogon")
584 paths.smbconf = lp.configfile
585 return paths
588 def determine_netbios_name(hostname):
589 """Determine a netbios name from a hostname."""
590 # remove forbidden chars and force the length to be <16
591 netbiosname = "".join([x for x in hostname if is_valid_netbios_char(x)])
592 return netbiosname[:MAX_NETBIOS_NAME_LEN].upper()
595 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
596 serverrole=None, rootdn=None, domaindn=None, configdn=None,
597 schemadn=None, serverdn=None, sitename=None,
598 domain_names_forced=False):
599 """Guess configuration settings to use."""
601 if hostname is None:
602 hostname = socket.gethostname().split(".")[0]
604 netbiosname = lp.get("netbios name")
605 if netbiosname is None:
606 netbiosname = determine_netbios_name(hostname)
607 netbiosname = netbiosname.upper()
608 if not valid_netbios_name(netbiosname):
609 raise InvalidNetbiosName(netbiosname)
611 if dnsdomain is None:
612 dnsdomain = lp.get("realm")
613 if dnsdomain is None or dnsdomain == "":
614 raise ProvisioningError(
615 "guess_names: 'realm' not specified in supplied %s!" %
616 lp.configfile)
618 dnsdomain = dnsdomain.lower()
620 if serverrole is None:
621 serverrole = lp.get("server role")
622 if serverrole is None:
623 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
625 serverrole = serverrole.lower()
627 realm = dnsdomain.upper()
629 if lp.get("realm") == "":
630 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
632 if lp.get("realm").upper() != realm:
633 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))
635 if lp.get("server role").lower() != serverrole:
636 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))
638 if serverrole == "active directory domain controller":
639 if domain is None:
640 # This will, for better or worse, default to 'WORKGROUP'
641 domain = lp.get("workgroup")
642 domain = domain.upper()
644 if lp.get("workgroup").upper() != domain:
645 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))
647 if domaindn is None:
648 domaindn = samba.dn_from_dns_name(dnsdomain)
650 if domain == netbiosname:
651 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
652 else:
653 domain = netbiosname
654 if domaindn is None:
655 domaindn = "DC=" + netbiosname
657 if not valid_netbios_name(domain):
658 raise InvalidNetbiosName(domain)
660 if hostname.upper() == realm:
661 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
662 if netbiosname.upper() == realm:
663 raise ProvisioningError("guess_names: Realm '%s' must not be equal to NetBIOS hostname '%s'!" % (realm, netbiosname))
664 if domain == realm and not domain_names_forced:
665 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
667 if serverrole != "active directory domain controller":
669 # This is the code path for a domain member
670 # where we provision the database as if we were
671 # on a domain controller, so we should not use
672 # the same dnsdomain as the domain controllers
673 # of our primary domain.
675 # This will be important if we start doing
676 # SID/name filtering and reject the local
677 # sid and names if they come from a domain
678 # controller.
680 realm = netbiosname
681 dnsdomain = netbiosname.lower()
683 if rootdn is None:
684 rootdn = domaindn
686 if configdn is None:
687 configdn = "CN=Configuration," + rootdn
688 if schemadn is None:
689 schemadn = "CN=Schema," + configdn
691 if sitename is None:
692 sitename = DEFAULTSITE
694 if serverdn is None:
695 serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
696 netbiosname, sitename, configdn)
698 names = ProvisionNames()
699 names.rootdn = rootdn
700 names.domaindn = domaindn
701 names.configdn = configdn
702 names.schemadn = schemadn
703 names.ldapmanagerdn = "CN=Manager," + rootdn
704 names.dnsdomain = dnsdomain
705 names.domain = domain
706 names.realm = realm
707 names.netbiosname = netbiosname
708 names.hostname = hostname
709 names.sitename = sitename
710 names.serverdn = serverdn
712 return names
715 def make_smbconf(smbconf, hostname, domain, realm, targetdir,
716 serverrole=None, eadb=False, use_ntvfs=False, lp=None,
717 global_param=None):
718 """Create a new smb.conf file based on a couple of basic settings.
720 assert smbconf is not None
722 if hostname is None:
723 hostname = socket.gethostname().split(".")[0]
725 netbiosname = determine_netbios_name(hostname)
727 if serverrole is None:
728 serverrole = "standalone server"
730 assert domain is not None
731 domain = domain.upper()
733 assert realm is not None
734 realm = realm.upper()
736 global_settings = {
737 "netbios name": netbiosname,
738 "workgroup": domain,
739 "realm": realm,
740 "server role": serverrole,
743 if lp is None:
744 lp = samba.param.LoadParm()
745 # Load non-existent file
746 if os.path.exists(smbconf):
747 lp.load(smbconf)
749 if global_param is not None:
750 for ent in global_param:
751 if global_param[ent] is not None:
752 global_settings[ent] = " ".join(global_param[ent])
754 if targetdir is not None:
755 global_settings["private dir"] = os.path.abspath(os.path.join(targetdir, "private"))
756 global_settings["lock dir"] = os.path.abspath(targetdir)
757 global_settings["state directory"] = os.path.abspath(os.path.join(targetdir, "state"))
758 global_settings["cache directory"] = os.path.abspath(os.path.join(targetdir, "cache"))
759 global_settings["binddns dir"] = os.path.abspath(os.path.join(targetdir, "bind-dns"))
761 lp.set("lock dir", os.path.abspath(targetdir))
762 lp.set("state directory", global_settings["state directory"])
763 lp.set("cache directory", global_settings["cache directory"])
764 lp.set("binddns dir", global_settings["binddns dir"])
766 if eadb:
767 if use_ntvfs:
768 if targetdir is not None:
769 privdir = os.path.join(targetdir, "private")
770 lp.set("posix:eadb",
771 os.path.abspath(os.path.join(privdir, "eadb.tdb")))
772 elif not lp.get("posix:eadb"):
773 privdir = lp.get("private dir")
774 lp.set("posix:eadb",
775 os.path.abspath(os.path.join(privdir, "eadb.tdb")))
776 else:
777 if targetdir is not None:
778 statedir = os.path.join(targetdir, "state")
779 lp.set("xattr_tdb:file",
780 os.path.abspath(os.path.join(statedir, "xattr.tdb")))
781 elif not lp.get("xattr_tdb:file"):
782 statedir = lp.get("state directory")
783 lp.set("xattr_tdb:file",
784 os.path.abspath(os.path.join(statedir, "xattr.tdb")))
786 shares = {}
787 if serverrole == "active directory domain controller":
788 shares["sysvol"] = os.path.join(lp.get("state directory"), "sysvol")
789 shares["netlogon"] = os.path.join(shares["sysvol"], realm.lower(),
790 "scripts")
791 else:
792 global_settings["passdb backend"] = "samba_dsdb"
794 f = open(smbconf, 'w')
795 try:
796 f.write("[globals]\n")
797 for key, val in global_settings.items():
798 f.write("\t%s = %s\n" % (key, val))
799 f.write("\n")
801 for name, path in shares.items():
802 f.write("[%s]\n" % name)
803 f.write("\tpath = %s\n" % path)
804 f.write("\tread only = no\n")
805 f.write("\n")
806 finally:
807 f.close()
808 # reload the smb.conf
809 lp.load(smbconf)
811 # and dump it without any values that are the default
812 # this ensures that any smb.conf parameters that were set
813 # on the provision/join command line are set in the resulting smb.conf
814 lp.dump(False, smbconf)
817 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
818 users_gid):
819 """setup reasonable name mappings for sam names to unix names.
821 :param samdb: SamDB object.
822 :param idmap: IDmap db object.
823 :param sid: The domain sid.
824 :param domaindn: The domain DN.
825 :param root_uid: uid of the UNIX root user.
826 :param nobody_uid: uid of the UNIX nobody user.
827 :param users_gid: gid of the UNIX users group.
829 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
831 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
832 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
835 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
836 provision_backend, names, serverrole,
837 plaintext_secrets=False,
838 backend_store=None):
839 """Setup the partitions for the SAM database.
841 Alternatively, provision() may call this, and then populate the database.
843 :note: This will wipe the Sam Database!
845 :note: This function always removes the local SAM LDB file. The erase
846 parameter controls whether to erase the existing data, which
847 may not be stored locally but in LDAP.
850 assert session_info is not None
852 # We use options=["modules:"] to stop the modules loading - we
853 # just want to wipe and re-initialise the database, not start it up
855 try:
856 os.unlink(samdb_path)
857 except OSError:
858 pass
860 samdb = Ldb(url=samdb_path, session_info=session_info,
861 lp=lp, options=["modules:"])
863 ldap_backend_line = "# No LDAP backend"
864 if provision_backend.type != "ldb":
865 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
867 required_features = None
868 if not plaintext_secrets:
869 required_features = "requiredFeatures: encryptedSecrets"
871 if backend_store is None:
872 backend_store = get_default_backend_store()
873 backend_store_line = "backendStore: %s" % backend_store
875 if backend_store == "mdb":
876 if required_features is not None:
877 required_features += "\n"
878 else:
879 required_features = ""
880 required_features += "requiredFeatures: lmdbLevelOne"
882 if required_features is None:
883 required_features = "# No required features"
885 samdb.transaction_start()
886 try:
887 logger.info("Setting up sam.ldb partitions and settings")
888 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
889 "LDAP_BACKEND_LINE": ldap_backend_line,
890 "BACKEND_STORE": backend_store_line
893 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
894 "BACKEND_TYPE": provision_backend.type,
895 "SERVER_ROLE": serverrole,
896 "REQUIRED_FEATURES": required_features
899 logger.info("Setting up sam.ldb rootDSE")
900 setup_samdb_rootdse(samdb, names)
901 except:
902 samdb.transaction_cancel()
903 raise
904 else:
905 samdb.transaction_commit()
908 def secretsdb_self_join(secretsdb, domain,
909 netbiosname, machinepass, domainsid=None,
910 realm=None, dnsdomain=None,
911 key_version_number=1,
912 secure_channel_type=SEC_CHAN_WKSTA):
913 """Add domain join-specific bits to a secrets database.
915 :param secretsdb: Ldb Handle to the secrets database
916 :param machinepass: Machine password
918 attrs = ["whenChanged",
919 "secret",
920 "priorSecret",
921 "priorChanged",
922 "krb5Keytab",
923 "privateKeytab"]
925 if realm is not None:
926 if dnsdomain is None:
927 dnsdomain = realm.lower()
928 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
929 else:
930 dnsname = None
931 shortname = netbiosname.lower()
933 # We don't need to set msg["flatname"] here, because rdn_name will handle
934 # it, and it causes problems for modifies anyway
935 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
936 msg["secureChannelType"] = [str(secure_channel_type)]
937 msg["objectClass"] = ["top", "primaryDomain"]
938 if dnsname is not None:
939 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
940 msg["realm"] = [realm]
941 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
942 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
943 msg["privateKeytab"] = ["secrets.keytab"]
945 msg["secret"] = [machinepass.encode('utf-8')]
946 msg["samAccountName"] = ["%s$" % netbiosname]
947 msg["secureChannelType"] = [str(secure_channel_type)]
948 if domainsid is not None:
949 msg["objectSid"] = [ndr_pack(domainsid)]
951 # This complex expression tries to ensure that we don't have more
952 # than one record for this SID, realm or netbios domain at a time,
953 # but we don't delete the old record that we are about to modify,
954 # because that would delete the keytab and previous password.
955 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
956 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
957 scope=ldb.SCOPE_ONELEVEL)
959 for del_msg in res:
960 secretsdb.delete(del_msg.dn)
962 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
964 if len(res) == 1:
965 msg["priorSecret"] = [res[0]["secret"][0]]
966 try:
967 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
968 except KeyError:
969 pass
971 try:
972 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
973 except KeyError:
974 pass
976 try:
977 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
978 except KeyError:
979 pass
981 for el in msg:
982 if el != 'dn':
983 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
984 secretsdb.modify(msg)
985 secretsdb.rename(res[0].dn, msg.dn)
986 else:
987 spn = ['HOST/%s' % shortname]
988 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
989 # if we are a domain controller then we add servicePrincipalName
990 # entries for the keytab code to update.
991 spn.extend(['HOST/%s' % dnsname])
992 msg["servicePrincipalName"] = spn
994 secretsdb.add(msg)
997 def setup_secretsdb(paths, session_info, lp):
998 """Setup the secrets database.
1000 :note: This function does not handle exceptions and transaction on purpose,
1001 it's up to the caller to do this job.
1003 :param path: Path to the secrets database.
1004 :param session_info: Session info.
1005 :param lp: Loadparm context
1006 :return: LDB handle for the created secrets database
1008 if os.path.exists(paths.secrets):
1009 os.unlink(paths.secrets)
1011 keytab_path = os.path.join(paths.private_dir, paths.keytab)
1012 if os.path.exists(keytab_path):
1013 os.unlink(keytab_path)
1015 bind_dns_keytab_path = os.path.join(paths.binddns_dir, paths.dns_keytab)
1016 if os.path.exists(bind_dns_keytab_path):
1017 os.unlink(bind_dns_keytab_path)
1019 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1020 if os.path.exists(dns_keytab_path):
1021 os.unlink(dns_keytab_path)
1023 path = paths.secrets
1025 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
1026 secrets_ldb.erase()
1027 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
1028 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
1029 secrets_ldb.transaction_start()
1030 try:
1031 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
1032 except:
1033 secrets_ldb.transaction_cancel()
1034 raise
1035 return secrets_ldb
1038 def setup_privileges(path, session_info, lp):
1039 """Setup the privileges database.
1041 :param path: Path to the privileges database.
1042 :param session_info: Session info.
1043 :param lp: Loadparm context
1044 :return: LDB handle for the created secrets database
1046 if os.path.exists(path):
1047 os.unlink(path)
1048 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
1049 privilege_ldb.erase()
1050 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
1053 def setup_encrypted_secrets_key(path):
1054 """Setup the encrypted secrets key file.
1056 Any existing key file will be deleted and a new random key generated.
1058 :param path: Path to the secrets key file.
1061 if os.path.exists(path):
1062 os.unlink(path)
1064 flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
1065 mode = stat.S_IRUSR | stat.S_IWUSR
1067 umask_original = os.umask(0)
1068 try:
1069 fd = os.open(path, flags, mode)
1070 finally:
1071 os.umask(umask_original)
1073 with os.fdopen(fd, 'wb') as f:
1074 key = samba.generate_random_bytes(16)
1075 f.write(key)
1078 def setup_registry(path, session_info, lp):
1079 """Setup the registry.
1081 :param path: Path to the registry database
1082 :param session_info: Session information
1083 :param lp: Loadparm context
1085 reg = samba.registry.Registry()
1086 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
1087 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
1088 provision_reg = setup_path("provision.reg")
1089 assert os.path.exists(provision_reg)
1090 reg.diff_apply(provision_reg)
1093 def setup_idmapdb(path, session_info, lp):
1094 """Setup the idmap database.
1096 :param path: path to the idmap database
1097 :param session_info: Session information
1098 :param lp: Loadparm context
1100 if os.path.exists(path):
1101 os.unlink(path)
1103 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
1104 idmap_ldb.erase()
1105 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
1106 return idmap_ldb
1109 def setup_samdb_rootdse(samdb, names):
1110 """Setup the SamDB rootdse.
1112 :param samdb: Sam Database handle
1114 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
1115 "SCHEMADN": names.schemadn,
1116 "DOMAINDN": names.domaindn,
1117 "ROOTDN": names.rootdn,
1118 "CONFIGDN": names.configdn,
1119 "SERVERDN": names.serverdn,
1123 def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
1124 dns_backend, dnspass, domainsid, next_rid, invocationid,
1125 policyguid, policyguid_dc,
1126 domainControllerFunctionality, ntdsguid=None, dc_rid=None):
1127 """Join a host to its own domain."""
1128 assert isinstance(invocationid, str)
1129 if ntdsguid is not None:
1130 ntdsguid_line = "objectGUID: %s\n" % ntdsguid
1131 else:
1132 ntdsguid_line = ""
1134 if dc_rid is None:
1135 dc_rid = next_rid
1137 # Some clients/applications (like exchange) make use of
1138 # the operatingSystemVersion attribute in order to
1139 # find if a DC is good enough.
1141 # So we better use a value matching a Windows DC
1142 # with the same domainControllerFunctionality level
1143 operatingSystemVersion = samba.dsdb.dc_operatingSystemVersion(domainControllerFunctionality)
1145 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
1146 "CONFIGDN": names.configdn,
1147 "SCHEMADN": names.schemadn,
1148 "DOMAINDN": names.domaindn,
1149 "SERVERDN": names.serverdn,
1150 "INVOCATIONID": invocationid,
1151 "NETBIOSNAME": names.netbiosname,
1152 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1153 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')).decode('utf8'),
1154 "DOMAINSID": str(domainsid),
1155 "DCRID": str(dc_rid),
1156 "OPERATING_SYSTEM": "Samba-%s" % version,
1157 "OPERATING_SYSTEM_VERSION": operatingSystemVersion,
1158 "NTDSGUID": ntdsguid_line,
1159 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1160 domainControllerFunctionality),
1161 "RIDALLOCATIONSTART": str(next_rid + 100),
1162 "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
1164 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1165 "POLICYGUID": policyguid,
1166 "POLICYGUID_DC": policyguid_dc,
1167 "DNSDOMAIN": names.dnsdomain,
1168 "DOMAINDN": names.domaindn})
1170 # If we are setting up a subdomain, then this has been replicated in, so we
1171 # don't need to add it
1172 if fill == FILL_FULL:
1173 setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
1174 "CONFIGDN": names.configdn,
1175 "SCHEMADN": names.schemadn,
1176 "DOMAINDN": names.domaindn,
1177 "SERVERDN": names.serverdn,
1178 "INVOCATIONID": invocationid,
1179 "NETBIOSNAME": names.netbiosname,
1180 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1181 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')).decode('utf8'),
1182 "DOMAINSID": str(domainsid),
1183 "DCRID": str(dc_rid),
1184 "SAMBA_VERSION_STRING": version,
1185 "NTDSGUID": ntdsguid_line,
1186 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1187 domainControllerFunctionality)})
1189 # Setup fSMORoleOwner entries to point at the newly created DC entry
1190 setup_modify_ldif(samdb,
1191 setup_path("provision_self_join_modify_schema.ldif"), {
1192 "SCHEMADN": names.schemadn,
1193 "SERVERDN": names.serverdn,
1195 controls=["provision:0", "relax:0"])
1196 setup_modify_ldif(samdb,
1197 setup_path("provision_self_join_modify_config.ldif"), {
1198 "CONFIGDN": names.configdn,
1199 "DEFAULTSITE": names.sitename,
1200 "NETBIOSNAME": names.netbiosname,
1201 "SERVERDN": names.serverdn,
1204 system_session_info = system_session()
1205 samdb.set_session_info(system_session_info)
1206 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1207 # modify a serverReference under cn=config when we are a subdomain, we must
1208 # be system due to ACLs
1209 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1210 "DOMAINDN": names.domaindn,
1211 "SERVERDN": names.serverdn,
1212 "NETBIOSNAME": names.netbiosname,
1215 samdb.set_session_info(admin_session_info)
1217 if dns_backend != "SAMBA_INTERNAL":
1218 # This is Samba4 specific and should be replaced by the correct
1219 # DNS AD-style setup
1220 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1221 "DNSDOMAIN": names.dnsdomain,
1222 "DOMAINDN": names.domaindn,
1223 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')).decode('utf8'),
1224 "HOSTNAME": names.hostname,
1225 "DNSNAME": '%s.%s' % (
1226 names.netbiosname.lower(), names.dnsdomain.lower())
1230 def getpolicypath(sysvolpath, dnsdomain, guid):
1231 """Return the physical path of policy given its guid.
1233 :param sysvolpath: Path to the sysvol folder
1234 :param dnsdomain: DNS name of the AD domain
1235 :param guid: The GUID of the policy
1236 :return: A string with the complete path to the policy folder
1238 if guid[0] != "{":
1239 guid = "{%s}" % guid
1240 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1241 return policy_path
1244 def create_gpo_struct(policy_path):
1245 if not os.path.exists(policy_path):
1246 os.makedirs(policy_path, 0o775)
1247 f = open(os.path.join(policy_path, "GPT.INI"), 'w')
1248 try:
1249 f.write("[General]\r\nVersion=0")
1250 finally:
1251 f.close()
1252 p = os.path.join(policy_path, "MACHINE")
1253 if not os.path.exists(p):
1254 os.makedirs(p, 0o775)
1255 p = os.path.join(policy_path, "USER")
1256 if not os.path.exists(p):
1257 os.makedirs(p, 0o775)
1260 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1261 """Create the default GPO for a domain
1263 :param sysvolpath: Physical path for the sysvol folder
1264 :param dnsdomain: DNS domain name of the AD domain
1265 :param policyguid: GUID of the default domain policy
1266 :param policyguid_dc: GUID of the default domain controller policy
1268 policy_path = getpolicypath(sysvolpath, dnsdomain, policyguid)
1269 create_gpo_struct(policy_path)
1271 policy_path = getpolicypath(sysvolpath, dnsdomain, policyguid_dc)
1272 create_gpo_struct(policy_path)
1275 # Default the database size to 8Gb
1276 DEFAULT_BACKEND_SIZE = 8 * 1024 * 1024 *1024
1278 def setup_samdb(path, session_info, provision_backend, lp, names,
1279 logger, serverrole, schema, am_rodc=False,
1280 plaintext_secrets=False, backend_store=None,
1281 backend_store_size=None, batch_mode=False):
1282 """Setup a complete SAM Database.
1284 :note: This will wipe the main SAM database file!
1287 # Also wipes the database
1288 setup_samdb_partitions(path, logger=logger, lp=lp,
1289 provision_backend=provision_backend, session_info=session_info,
1290 names=names, serverrole=serverrole, plaintext_secrets=plaintext_secrets,
1291 backend_store=backend_store)
1293 store_size = DEFAULT_BACKEND_SIZE
1294 if backend_store_size:
1295 store_size = backend_store_size
1297 options = []
1298 if backend_store == "mdb":
1299 options.append("lmdb_env_size:" + str(store_size))
1300 if batch_mode:
1301 options.append("batch_mode:1")
1302 if batch_mode:
1303 # Estimate the number of index records in the transaction_index_cache
1304 # Numbers chosen give the prime 202481 for the default backend size,
1305 # which works well for a 100,000 user database
1306 cache_size = int(store_size / 42423) + 1
1307 options.append("transaction_index_cache_size:" + str(cache_size))
1309 # Load the database, but don's load the global schema and don't connect
1310 # quite yet
1311 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1312 lp=lp,
1313 global_schema=False, am_rodc=am_rodc, options=options)
1315 logger.info("Pre-loading the Samba 4 and AD schema")
1317 # Load the schema from the one we computed earlier
1318 samdb.set_schema(schema, write_indices_and_attributes=False)
1320 # Set the NTDS settings DN manually - in order to have it already around
1321 # before the provisioned tree exists and we connect
1322 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1324 # And now we can connect to the DB - the schema won't be loaded from the
1325 # DB
1326 try:
1327 samdb.connect(path, options=options)
1328 except ldb.LdbError as e2:
1329 (num, string_error) = e2.args
1330 if (num == ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS):
1331 raise ProvisioningError("Permission denied connecting to %s, are you running as root?" % path)
1332 else:
1333 raise
1335 # But we have to give it one more kick to have it use the schema
1336 # during provision - it needs, now that it is connected, to write
1337 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1338 samdb.set_schema(schema, write_indices_and_attributes=True)
1340 return samdb
1343 def fill_samdb(samdb, lp, names, logger, policyguid,
1344 policyguid_dc, fill, adminpass, krbtgtpass, machinepass, dns_backend,
1345 dnspass, invocationid, ntdsguid,
1346 dom_for_fun_level=None, schema=None, next_rid=None, dc_rid=None):
1348 if next_rid is None:
1349 next_rid = 1000
1351 # Provision does not make much sense values larger than 1000000000
1352 # as the upper range of the rIDAvailablePool is 1073741823 and
1353 # we don't want to create a domain that cannot allocate rids.
1354 if next_rid < 1000 or next_rid > 1000000000:
1355 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1356 error += "the valid range is %u-%u. The default is %u." % (
1357 1000, 1000000000, 1000)
1358 raise ProvisioningError(error)
1360 domainControllerFunctionality = functional_level.dc_level_from_lp(lp)
1362 # ATTENTION: Do NOT change these default values without discussion with the
1363 # team and/or release manager. They have a big impact on the whole program!
1364 if dom_for_fun_level is None:
1365 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
1367 if dom_for_fun_level > domainControllerFunctionality:
1368 level = functional_level.level_to_string(domainControllerFunctionality)
1369 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!")
1371 domainFunctionality = dom_for_fun_level
1372 forestFunctionality = dom_for_fun_level
1374 # Set the NTDS settings DN manually - in order to have it already around
1375 # before the provisioned tree exists and we connect
1376 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1378 # Set the domain functionality levels onto the database.
1379 # Various module (the password_hash module in particular) need
1380 # to know what level of AD we are emulating.
1382 # These will be fixed into the database via the database
1383 # modifictions below, but we need them set from the start.
1384 samdb.set_opaque("domainFunctionality", domainFunctionality)
1385 samdb.set_opaque("forestFunctionality", forestFunctionality)
1386 samdb.set_opaque("domainControllerFunctionality",
1387 domainControllerFunctionality)
1389 samdb.set_domain_sid(str(names.domainsid))
1390 samdb.set_invocation_id(invocationid)
1392 logger.info("Adding DomainDN: %s" % names.domaindn)
1394 # impersonate domain admin
1395 admin_session_info = admin_session(lp, str(names.domainsid))
1396 samdb.set_session_info(admin_session_info)
1397 if names.domainguid is not None:
1398 domainguid_line = "objectGUID: %s\n-" % names.domainguid
1399 else:
1400 domainguid_line = ""
1402 descr = b64encode(get_domain_descriptor(names.domainsid)).decode('utf8')
1403 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1404 "DOMAINDN": names.domaindn,
1405 "DOMAINSID": str(names.domainsid),
1406 "DESCRIPTOR": descr,
1407 "DOMAINGUID": domainguid_line
1410 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1411 "DOMAINDN": names.domaindn,
1412 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1413 "NEXTRID": str(next_rid),
1414 "DEFAULTSITE": names.sitename,
1415 "CONFIGDN": names.configdn,
1416 "POLICYGUID": policyguid,
1417 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1418 "SAMBA_VERSION_STRING": version,
1419 "MIN_PWD_LENGTH": str(DEFAULT_MIN_PWD_LENGTH)
1422 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1423 if fill == FILL_FULL:
1424 logger.info("Adding configuration container")
1425 descr = b64encode(get_config_descriptor(names.domainsid)).decode('utf8')
1426 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1427 "CONFIGDN": names.configdn,
1428 "DESCRIPTOR": descr,
1431 # The LDIF here was created when the Schema object was constructed
1432 ignore_checks_oid = "local_oid:%s:0" % samba.dsdb.DSDB_CONTROL_SKIP_DUPLICATES_CHECK_OID
1433 schema_controls = [
1434 "provision:0",
1435 "relax:0",
1436 ignore_checks_oid
1439 logger.info("Setting up sam.ldb schema")
1440 samdb.add_ldif(schema.schema_dn_add, controls=schema_controls)
1441 samdb.modify_ldif(schema.schema_dn_modify, controls=schema_controls)
1442 samdb.write_prefixes_from_schema()
1443 samdb.add_ldif(schema.schema_data, controls=schema_controls)
1444 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1445 {"SCHEMADN": names.schemadn},
1446 controls=schema_controls)
1448 # Now register this container in the root of the forest
1449 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1450 msg["subRefs"] = ldb.MessageElement(names.configdn, ldb.FLAG_MOD_ADD,
1451 "subRefs")
1453 deletedobjects_descr = b64encode(get_deletedobjects_descriptor(names.domainsid)).decode('utf8')
1455 samdb.invocation_id = invocationid
1457 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1458 if fill == FILL_FULL:
1459 logger.info("Setting up sam.ldb configuration data")
1461 partitions_descr = b64encode(get_config_partitions_descriptor(names.domainsid)).decode('utf8')
1462 sites_descr = b64encode(get_config_sites_descriptor(names.domainsid)).decode('utf8')
1463 ntdsquotas_descr = b64encode(get_config_ntds_quotas_descriptor(names.domainsid)).decode('utf8')
1464 protected1_descr = b64encode(get_config_delete_protected1_descriptor(names.domainsid)).decode('utf8')
1465 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid)).decode('utf8')
1466 protected2_descr = b64encode(get_config_delete_protected2_descriptor(names.domainsid)).decode('utf8')
1468 if "2008" in schema.base_schema:
1469 # exclude 2012-specific changes if we're using a 2008 schema
1470 incl_2012 = "#"
1471 else:
1472 incl_2012 = ""
1474 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1475 "CONFIGDN": names.configdn,
1476 "NETBIOSNAME": names.netbiosname,
1477 "DEFAULTSITE": names.sitename,
1478 "DNSDOMAIN": names.dnsdomain,
1479 "DOMAIN": names.domain,
1480 "SCHEMADN": names.schemadn,
1481 "DOMAINDN": names.domaindn,
1482 "SERVERDN": names.serverdn,
1483 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1484 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1485 "NTDSQUOTAS_DESCRIPTOR": ntdsquotas_descr,
1486 "DELETEDOBJECTS_DESCRIPTOR": deletedobjects_descr,
1487 "LOSTANDFOUND_DESCRIPTOR": protected1wd_descr,
1488 "SERVICES_DESCRIPTOR": protected1_descr,
1489 "PHYSICALLOCATIONS_DESCRIPTOR": protected1wd_descr,
1490 "FORESTUPDATES_DESCRIPTOR": protected1wd_descr,
1491 "EXTENDEDRIGHTS_DESCRIPTOR": protected2_descr,
1492 "PARTITIONS_DESCRIPTOR": partitions_descr,
1493 "SITES_DESCRIPTOR": sites_descr,
1496 setup_add_ldif(samdb, setup_path("extended-rights.ldif"), {
1497 "CONFIGDN": names.configdn,
1498 "INC2012": incl_2012,
1501 logger.info("Setting up display specifiers")
1502 display_specifiers_ldif = read_ms_ldif(
1503 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1504 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1505 {"CONFIGDN": names.configdn})
1506 check_all_substituted(display_specifiers_ldif)
1507 samdb.add_ldif(display_specifiers_ldif)
1509 logger.info("Modifying display specifiers and extended rights")
1510 setup_modify_ldif(samdb,
1511 setup_path("provision_configuration_modify.ldif"), {
1512 "CONFIGDN": names.configdn,
1513 "DISPLAYSPECIFIERS_DESCRIPTOR": protected2_descr
1516 logger.info("Adding users container")
1517 users_desc = b64encode(get_domain_users_descriptor(names.domainsid)).decode('utf8')
1518 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1519 "DOMAINDN": names.domaindn,
1520 "USERS_DESCRIPTOR": users_desc
1522 logger.info("Modifying users container")
1523 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1524 "DOMAINDN": names.domaindn})
1525 logger.info("Adding computers container")
1526 computers_desc = b64encode(get_domain_computers_descriptor(names.domainsid)).decode('utf8')
1527 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1528 "DOMAINDN": names.domaindn,
1529 "COMPUTERS_DESCRIPTOR": computers_desc
1531 logger.info("Modifying computers container")
1532 setup_modify_ldif(samdb,
1533 setup_path("provision_computers_modify.ldif"), {
1534 "DOMAINDN": names.domaindn})
1535 logger.info("Setting up sam.ldb data")
1536 infrastructure_desc = b64encode(get_domain_infrastructure_descriptor(names.domainsid)).decode('utf8')
1537 lostandfound_desc = b64encode(get_domain_delete_protected2_descriptor(names.domainsid)).decode('utf8')
1538 system_desc = b64encode(get_domain_delete_protected1_descriptor(names.domainsid)).decode('utf8')
1539 builtin_desc = b64encode(get_domain_builtin_descriptor(names.domainsid)).decode('utf8')
1540 controllers_desc = b64encode(get_domain_controllers_descriptor(names.domainsid)).decode('utf8')
1541 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1542 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1543 "DOMAINDN": names.domaindn,
1544 "NETBIOSNAME": names.netbiosname,
1545 "DEFAULTSITE": names.sitename,
1546 "CONFIGDN": names.configdn,
1547 "SERVERDN": names.serverdn,
1548 "RIDAVAILABLESTART": str(next_rid + 600),
1549 "POLICYGUID_DC": policyguid_dc,
1550 "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc,
1551 "DELETEDOBJECTS_DESCRIPTOR": deletedobjects_descr,
1552 "LOSTANDFOUND_DESCRIPTOR": lostandfound_desc,
1553 "SYSTEM_DESCRIPTOR": system_desc,
1554 "BUILTIN_DESCRIPTOR": builtin_desc,
1555 "DOMAIN_CONTROLLERS_DESCRIPTOR": controllers_desc,
1558 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1559 if fill == FILL_FULL:
1560 managedservice_descr = b64encode(get_managed_service_accounts_descriptor(names.domainsid)).decode('utf8')
1561 setup_modify_ldif(samdb,
1562 setup_path("provision_configuration_references.ldif"), {
1563 "CONFIGDN": names.configdn,
1564 "SCHEMADN": names.schemadn})
1566 logger.info("Setting up well known security principals")
1567 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid)).decode('utf8')
1568 setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1569 "CONFIGDN": names.configdn,
1570 "WELLKNOWNPRINCIPALS_DESCRIPTOR": protected1wd_descr,
1571 }, controls=["relax:0", "provision:0"])
1573 if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1574 setup_modify_ldif(samdb,
1575 setup_path("provision_basedn_references.ldif"), {
1576 "DOMAINDN": names.domaindn,
1577 "MANAGEDSERVICE_DESCRIPTOR": managedservice_descr
1580 logger.info("Setting up sam.ldb users and groups")
1581 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1582 "DOMAINDN": names.domaindn,
1583 "DOMAINSID": str(names.domainsid),
1584 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')).decode('utf8'),
1585 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le')).decode('utf8')
1586 }, controls=["relax:0", "provision:0"])
1588 logger.info("Setting up self join")
1589 setup_self_join(samdb, admin_session_info, names=names, fill=fill,
1590 invocationid=invocationid,
1591 dns_backend=dns_backend,
1592 dnspass=dnspass,
1593 machinepass=machinepass,
1594 domainsid=names.domainsid,
1595 next_rid=next_rid,
1596 dc_rid=dc_rid,
1597 policyguid=policyguid,
1598 policyguid_dc=policyguid_dc,
1599 domainControllerFunctionality=domainControllerFunctionality,
1600 ntdsguid=ntdsguid)
1602 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1603 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1604 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE).decode('utf8')
1605 assert isinstance(names.ntdsguid, str)
1607 return samdb
1610 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;FA;;;BA)(A;OICI;0x1200a9;;;SO)(A;OICI;FA;;;SY)(A;OICI;0x1200a9;;;AU)"
1611 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)"
1612 SYSVOL_SERVICE = "sysvol"
1615 def set_dir_acl(path, acl, lp, domsid, use_ntvfs, passdb, service=SYSVOL_SERVICE):
1616 session_info = system_session_unix()
1617 setntacl(lp, path, acl, domsid, session_info, use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1618 for root, dirs, files in os.walk(path, topdown=False):
1619 for name in files:
1620 setntacl(lp, os.path.join(root, name), acl, domsid, session_info,
1621 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1622 for name in dirs:
1623 setntacl(lp, os.path.join(root, name), acl, domsid, session_info,
1624 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1627 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb):
1628 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1629 folders beneath.
1631 :param sysvol: Physical path for the sysvol folder
1632 :param dnsdomain: The DNS name of the domain
1633 :param domainsid: The SID of the domain
1634 :param domaindn: The DN of the domain (ie. DC=...)
1635 :param samdb: An LDB object on the SAM db
1636 :param lp: an LP object
1639 # Set ACL for GPO root folder
1640 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1641 session_info = system_session_unix()
1643 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid), session_info,
1644 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=SYSVOL_SERVICE)
1646 res = samdb.search(base="CN=Policies,CN=System,%s" %(domaindn),
1647 attrs=["cn", "nTSecurityDescriptor"],
1648 expression="", scope=ldb.SCOPE_ONELEVEL)
1650 for policy in res:
1651 acl = ndr_unpack(security.descriptor,
1652 policy["nTSecurityDescriptor"][0]).as_sddl()
1653 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1654 set_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1655 str(domainsid), use_ntvfs,
1656 passdb=passdb)
1659 def setsysvolacl(samdb, sysvol, uid, gid, domainsid, dnsdomain,
1660 domaindn, lp, use_ntvfs):
1661 """Set the ACL for the sysvol share and the subfolders
1663 :param samdb: An LDB object on the SAM db
1664 :param sysvol: Physical path for the sysvol folder
1665 :param uid: The UID of the "Administrator" user
1666 :param gid: The GID of the "Domain administrators" group
1667 :param domainsid: The SID of the domain
1668 :param dnsdomain: The DNS name of the domain
1669 :param domaindn: The DN of the domain (ie. DC=...)
1671 s4_passdb = None
1673 if not use_ntvfs:
1674 s3conf = s3param.get_context()
1675 s3conf.load(lp.configfile)
1677 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(sysvol))
1678 try:
1679 try:
1680 smbd.set_simple_acl(file.name, 0o755, system_session_unix(), gid)
1681 except OSError:
1682 if not smbd.have_posix_acls():
1683 # This clue is only strictly correct for RPM and
1684 # Debian-like Linux systems, but hopefully other users
1685 # will get enough clue from it.
1686 raise ProvisioningError("Samba was compiled without the posix ACL support that s3fs requires. "
1687 "Try installing libacl1-dev or libacl-devel, then re-run configure and make.")
1689 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. "
1690 "Try the mounting the filesystem with the 'acl' option.")
1691 try:
1692 smbd.chown(file.name, uid, gid, system_session_unix())
1693 except OSError:
1694 raise ProvisioningError("Unable to chown a file on your filesystem. "
1695 "You may not be running provision as root.")
1696 finally:
1697 file.close()
1699 # This will ensure that the smbd code we are running when setting ACLs
1700 # is initialised with the smb.conf
1701 s3conf = s3param.get_context()
1702 s3conf.load(lp.configfile)
1703 # ensure we are using the right samba_dsdb passdb backend, no matter what
1704 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1705 passdb.reload_static_pdb()
1707 # ensure that we init the samba_dsdb backend, so the domain sid is
1708 # marked in secrets.tdb
1709 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1711 # now ensure everything matches correctly, to avoid weird issues
1712 if passdb.get_global_sam_sid() != domainsid:
1713 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))
1715 domain_info = s4_passdb.domain_info()
1716 if domain_info["dom_sid"] != domainsid:
1717 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))
1719 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1720 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()))
1722 try:
1723 if use_ntvfs:
1724 os.chown(sysvol, -1, gid)
1725 except OSError:
1726 canchown = False
1727 else:
1728 canchown = True
1730 # use admin sid dn as user dn, since admin should own most of the files,
1731 # the operation will be much faster
1732 userdn = '<SID={}-{}>'.format(domainsid, security.DOMAIN_RID_ADMINISTRATOR)
1734 flags = (auth.AUTH_SESSION_INFO_DEFAULT_GROUPS |
1735 auth.AUTH_SESSION_INFO_AUTHENTICATED |
1736 auth.AUTH_SESSION_INFO_SIMPLE_PRIVILEGES)
1738 session_info = auth.user_session(samdb, lp_ctx=lp, dn=userdn,
1739 session_info_flags=flags)
1740 auth.session_info_set_unix(session_info,
1741 lp_ctx=lp,
1742 user_name="Administrator",
1743 uid=uid,
1744 gid=gid)
1746 def _setntacl(path):
1747 """A helper to reuse args"""
1748 return setntacl(
1749 lp, path, SYSVOL_ACL, str(domainsid), session_info,
1750 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=s4_passdb,
1751 service=SYSVOL_SERVICE)
1753 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1754 _setntacl(sysvol)
1755 for root, dirs, files in os.walk(sysvol, topdown=False):
1756 for name in files:
1757 if use_ntvfs and canchown:
1758 os.chown(os.path.join(root, name), -1, gid)
1759 _setntacl(os.path.join(root, name))
1760 for name in dirs:
1761 if use_ntvfs and canchown:
1762 os.chown(os.path.join(root, name), -1, gid)
1763 _setntacl(os.path.join(root, name))
1765 # Set acls on Policy folder and policies folders
1766 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb=s4_passdb)
1769 def acl_type(direct_db_access):
1770 if direct_db_access:
1771 return "DB"
1772 else:
1773 return "VFS"
1776 def check_dir_acl(path, acl, lp, domainsid, direct_db_access):
1777 session_info = system_session_unix()
1778 fsacl = getntacl(lp, path, session_info, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1779 fsacl_sddl = fsacl.as_sddl(domainsid)
1780 if fsacl_sddl != acl:
1781 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))
1783 for root, dirs, files in os.walk(path, topdown=False):
1784 for name in files:
1785 fsacl = getntacl(lp, os.path.join(root, name), session_info,
1786 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1787 if fsacl is None:
1788 raise ProvisioningError('%s ACL on GPO file %s not found!' %
1789 (acl_type(direct_db_access),
1790 os.path.join(root, name)))
1791 fsacl_sddl = fsacl.as_sddl(domainsid)
1792 if fsacl_sddl != acl:
1793 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))
1795 for name in dirs:
1796 fsacl = getntacl(lp, os.path.join(root, name), session_info,
1797 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1798 if fsacl is None:
1799 raise ProvisioningError('%s ACL on GPO directory %s not found!'
1800 % (acl_type(direct_db_access),
1801 os.path.join(root, name)))
1802 fsacl_sddl = fsacl.as_sddl(domainsid)
1803 if fsacl_sddl != acl:
1804 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))
1807 def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1808 direct_db_access):
1809 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1810 folders beneath.
1812 :param sysvol: Physical path for the sysvol folder
1813 :param dnsdomain: The DNS name of the domain
1814 :param domainsid: The SID of the domain
1815 :param domaindn: The DN of the domain (ie. DC=...)
1816 :param samdb: An LDB object on the SAM db
1817 :param lp: an LP object
1820 # Set ACL for GPO root folder
1821 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1822 session_info = system_session_unix()
1823 fsacl = getntacl(lp, root_policy_path, session_info,
1824 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1825 if fsacl is None:
1826 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access), root_policy_path))
1827 fsacl_sddl = fsacl.as_sddl(domainsid)
1828 if fsacl_sddl != POLICIES_ACL:
1829 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))
1830 res = samdb.search(base="CN=Policies,CN=System,%s" %(domaindn),
1831 attrs=["cn", "nTSecurityDescriptor"],
1832 expression="", scope=ldb.SCOPE_ONELEVEL)
1834 for policy in res:
1835 acl = ndr_unpack(security.descriptor,
1836 policy["nTSecurityDescriptor"][0]).as_sddl()
1837 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1838 check_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1839 domainsid, direct_db_access)
1842 def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn,
1843 lp):
1844 """Set the ACL for the sysvol share and the subfolders
1846 :param samdb: An LDB object on the SAM db
1847 :param netlogon: Physical path for the netlogon folder
1848 :param sysvol: Physical path for the sysvol folder
1849 :param uid: The UID of the "Administrator" user
1850 :param gid: The GID of the "Domain administrators" group
1851 :param domainsid: The SID of the domain
1852 :param dnsdomain: The DNS name of the domain
1853 :param domaindn: The DN of the domain (ie. DC=...)
1856 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1857 s3conf = s3param.get_context()
1858 s3conf.load(lp.configfile)
1859 # ensure we are using the right samba_dsdb passdb backend, no matter what
1860 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1861 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1862 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1864 # now ensure everything matches correctly, to avoid weird issues
1865 if passdb.get_global_sam_sid() != domainsid:
1866 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))
1868 domain_info = s4_passdb.domain_info()
1869 if domain_info["dom_sid"] != domainsid:
1870 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))
1872 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1873 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()))
1875 # Ensure we can read this directly, and via the smbd VFS
1876 session_info = system_session_unix()
1877 for direct_db_access in [True, False]:
1878 # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1879 for dir_path in [os.path.join(sysvol, dnsdomain), netlogon]:
1880 fsacl = getntacl(lp, dir_path, session_info, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1881 if fsacl is None:
1882 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access), dir_path))
1883 fsacl_sddl = fsacl.as_sddl(domainsid)
1884 if fsacl_sddl != SYSVOL_ACL:
1885 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))
1887 # Check acls on Policy folder and policies folders
1888 check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1889 direct_db_access)
1892 def interface_ips_v4(lp, all_interfaces=False):
1893 """return only IPv4 IPs"""
1894 ips = samba.interface_ips(lp, all_interfaces)
1895 ret = []
1896 for i in ips:
1897 if i.find(':') == -1:
1898 ret.append(i)
1899 return ret
1902 def interface_ips_v6(lp):
1903 """return only IPv6 IPs"""
1904 ips = samba.interface_ips(lp, False)
1905 ret = []
1906 for i in ips:
1907 if i.find(':') != -1:
1908 ret.append(i)
1909 return ret
1912 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1913 schema=None,
1914 samdb_fill=FILL_FULL,
1915 hostip=None, hostip6=None,
1916 next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1917 domainguid=None, policyguid=None, policyguid_dc=None,
1918 invocationid=None, machinepass=None, ntdsguid=None,
1919 dns_backend=None, dnspass=None,
1920 serverrole=None, dom_for_fun_level=None,
1921 lp=None, use_ntvfs=False,
1922 skip_sysvolacl=False):
1923 # create/adapt the group policy GUIDs
1924 # Default GUID for default policy are described at
1925 # "How Core Group Policy Works"
1926 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1927 if policyguid is None:
1928 policyguid = DEFAULT_POLICY_GUID
1929 policyguid = policyguid.upper()
1930 if policyguid_dc is None:
1931 policyguid_dc = DEFAULT_DC_POLICY_GUID
1932 policyguid_dc = policyguid_dc.upper()
1934 if invocationid is None:
1935 invocationid = str(uuid.uuid4())
1937 if krbtgtpass is None:
1938 # Note that the machinepass value is ignored
1939 # as the backend (password_hash.c) will generate its
1940 # own random values for the krbtgt keys
1941 krbtgtpass = samba.generate_random_machine_password(128, 255)
1942 if machinepass is None:
1943 machinepass = samba.generate_random_machine_password(120, 120)
1944 if dnspass is None:
1945 dnspass = samba.generate_random_password(120, 120)
1947 samdb.transaction_start()
1948 try:
1949 samdb = fill_samdb(samdb, lp, names, logger=logger,
1950 schema=schema,
1951 policyguid=policyguid, policyguid_dc=policyguid_dc,
1952 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1953 invocationid=invocationid, machinepass=machinepass,
1954 dns_backend=dns_backend, dnspass=dnspass,
1955 ntdsguid=ntdsguid,
1956 dom_for_fun_level=dom_for_fun_level,
1957 next_rid=next_rid, dc_rid=dc_rid)
1959 # Set up group policies (domain policy and domain controller
1960 # policy)
1961 if serverrole == "active directory domain controller":
1962 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1963 policyguid_dc)
1964 except:
1965 samdb.transaction_cancel()
1966 raise
1967 else:
1968 samdb.transaction_commit()
1970 if serverrole == "active directory domain controller":
1971 # Continue setting up sysvol for GPO. This appears to require being
1972 # outside a transaction.
1973 if not skip_sysvolacl:
1974 setsysvolacl(samdb, paths.sysvol, paths.root_uid,
1975 paths.root_gid, names.domainsid, names.dnsdomain,
1976 names.domaindn, lp, use_ntvfs)
1977 else:
1978 logger.info("Setting acl on sysvol skipped")
1980 secretsdb_self_join(secrets_ldb, domain=names.domain,
1981 realm=names.realm, dnsdomain=names.dnsdomain,
1982 netbiosname=names.netbiosname, domainsid=names.domainsid,
1983 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1985 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1986 # In future, this might be determined from some configuration
1987 kerberos_enctypes = str(ENC_ALL_TYPES)
1989 try:
1990 msg = ldb.Message(ldb.Dn(samdb,
1991 samdb.searchone("distinguishedName",
1992 expression="samAccountName=%s$" % names.netbiosname,
1993 scope=ldb.SCOPE_SUBTREE).decode('utf8')))
1994 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1995 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1996 name="msDS-SupportedEncryptionTypes")
1997 samdb.modify(msg)
1998 except ldb.LdbError as e:
1999 (enum, estr) = e.args
2000 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
2001 # It might be that this attribute does not exist in this schema
2002 raise
2004 setup_ad_dns(samdb, secrets_ldb, names, paths, logger,
2005 hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
2006 dnspass=dnspass, os_level=dom_for_fun_level,
2007 fill_level=samdb_fill)
2009 domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
2010 attribute="objectGUID").decode('utf8')
2011 assert isinstance(domainguid, str)
2013 lastProvisionUSNs = get_last_provision_usn(samdb)
2014 maxUSN = get_max_usn(samdb, str(names.rootdn))
2015 if lastProvisionUSNs is not None:
2016 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
2017 else:
2018 set_provision_usn(samdb, 0, maxUSN, invocationid)
2020 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
2021 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
2022 {'NTDSGUID': names.ntdsguid})
2024 # fix any dangling GUIDs from the provision
2025 logger.info("Fixing provision GUIDs")
2026 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
2027 quiet=True)
2028 samdb.transaction_start()
2029 try:
2030 # a small number of GUIDs are missing because of ordering issues in the
2031 # provision code
2032 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
2033 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
2034 scope=ldb.SCOPE_BASE,
2035 attrs=['defaultObjectCategory'])
2036 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
2037 scope=ldb.SCOPE_ONELEVEL,
2038 attrs=['ipsecOwnersReference',
2039 'ipsecFilterReference',
2040 'ipsecISAKMPReference',
2041 'ipsecNegotiationPolicyReference',
2042 'ipsecNFAReference'])
2043 if chk.check_database(DN=names.schemadn, scope=ldb.SCOPE_SUBTREE,
2044 attrs=['attributeId', 'governsId']) != 0:
2045 raise ProvisioningError("Duplicate attributeId or governsId in schema. Must be fixed manually!!")
2046 except:
2047 samdb.transaction_cancel()
2048 raise
2049 else:
2050 samdb.transaction_commit()
2053 _ROLES_MAP = {
2054 "ROLE_STANDALONE": "standalone server",
2055 "ROLE_DOMAIN_MEMBER": "member server",
2056 "ROLE_DOMAIN_BDC": "active directory domain controller",
2057 "ROLE_DOMAIN_PDC": "active directory domain controller",
2058 "dc": "active directory domain controller",
2059 "member": "member server",
2060 "domain controller": "active directory domain controller",
2061 "active directory domain controller": "active directory domain controller",
2062 "member server": "member server",
2063 "standalone": "standalone server",
2064 "standalone server": "standalone server",
2068 def sanitize_server_role(role):
2069 """Sanitize a server role name.
2071 :param role: Server role
2072 :raise ValueError: If the role can not be interpreted
2073 :return: Sanitized server role (one of "member server",
2074 "active directory domain controller", "standalone server")
2076 try:
2077 return _ROLES_MAP[role]
2078 except KeyError:
2079 raise ValueError(role)
2082 def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain):
2083 """Create AD entries for the fake ypserver.
2085 This is needed for being able to manipulate posix attrs via ADUC.
2087 samdb.transaction_start()
2088 try:
2089 logger.info("Setting up fake yp server settings")
2090 setup_add_ldif(samdb, setup_path("ypServ30.ldif"), {
2091 "DOMAINDN": domaindn,
2092 "NETBIOSNAME": netbiosname,
2093 "NISDOMAIN": nisdomain,
2095 except:
2096 samdb.transaction_cancel()
2097 raise
2098 else:
2099 samdb.transaction_commit()
2102 def directory_create_or_exists(path, mode=0o755):
2103 if not os.path.exists(path):
2104 try:
2105 os.mkdir(path, mode)
2106 except OSError as e:
2107 if e.errno in [errno.EEXIST]:
2108 pass
2109 else:
2110 raise ProvisioningError("Failed to create directory %s: %s" % (path, e.strerror))
2113 def determine_host_ip(logger, lp, hostip=None):
2114 if hostip is None:
2115 logger.info("Looking up IPv4 addresses")
2116 hostips = interface_ips_v4(lp)
2117 if len(hostips) > 0:
2118 hostip = hostips[0]
2119 if len(hostips) > 1:
2120 logger.warning("More than one IPv4 address found. Using %s",
2121 hostip)
2122 if hostip == "127.0.0.1":
2123 hostip = None
2124 if hostip is None:
2125 logger.warning("No IPv4 address will be assigned")
2127 return hostip
2130 def determine_host_ip6(logger, lp, hostip6=None):
2131 if hostip6 is None:
2132 logger.info("Looking up IPv6 addresses")
2133 hostips = interface_ips_v6(lp)
2134 if hostips:
2135 hostip6 = hostips[0]
2136 if len(hostips) > 1:
2137 logger.warning("More than one IPv6 address found. Using %s", hostip6)
2138 if hostip6 is None:
2139 logger.warning("No IPv6 address will be assigned")
2141 return hostip6
2144 def provision(logger, session_info, smbconf=None,
2145 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
2146 domaindn=None, schemadn=None, configdn=None, serverdn=None,
2147 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
2148 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None,
2149 krbtgtpass=None, domainguid=None, policyguid=None, policyguid_dc=None,
2150 dns_backend=None, dns_forwarder=None, dnspass=None,
2151 invocationid=None, machinepass=None, ntdsguid=None,
2152 root=None, nobody=None, users=None,
2153 sitename=None, serverrole=None, dom_for_fun_level=None,
2154 useeadb=False, am_rodc=False, lp=None, use_ntvfs=False,
2155 use_rfc2307=False, skip_sysvolacl=True,
2156 base_schema="2019", adprep_level=DS_DOMAIN_FUNCTION_2016,
2157 plaintext_secrets=False, backend_store=None,
2158 backend_store_size=None, batch_mode=False):
2159 """Provision samba4
2161 :note: caution, this wipes all existing data!
2164 try:
2165 serverrole = sanitize_server_role(serverrole)
2166 except ValueError:
2167 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole)
2169 if dom_for_fun_level is None:
2170 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
2172 if base_schema in ["2008_R2", "2008_R2_old"]:
2173 max_adprep_level = DS_DOMAIN_FUNCTION_2008_R2
2174 elif base_schema in ["2012"]:
2175 max_adprep_level = DS_DOMAIN_FUNCTION_2012
2176 elif base_schema in ["2012_R2"]:
2177 max_adprep_level = DS_DOMAIN_FUNCTION_2012_R2
2178 else:
2179 max_adprep_level = DS_DOMAIN_FUNCTION_2016
2181 if max_adprep_level < dom_for_fun_level:
2182 raise ProvisioningError('dom_for_fun_level[%u] incompatible with base_schema[%s]' %
2183 (dom_for_fun_level, base_schema))
2185 if adprep_level is not None and max_adprep_level < adprep_level:
2186 raise ProvisioningError('base_schema[%s] incompatible with adprep_level[%u]' %
2187 (base_schema, adprep_level))
2189 if adprep_level is not None and adprep_level < dom_for_fun_level:
2190 raise ProvisioningError('dom_for_fun_level[%u] incompatible with adprep_level[%u]' %
2191 (dom_for_fun_level, adprep_level))
2193 if ldapadminpass is None:
2194 # Make a new, random password between Samba and it's LDAP server
2195 ldapadminpass = samba.generate_random_password(128, 255)
2197 if backend_store is None:
2198 backend_store = get_default_backend_store()
2200 if domainsid is None:
2201 domainsid = security.random_sid()
2203 root_uid = get_root_uid([root or "root"], logger)
2204 nobody_uid = findnss_uid([nobody or "nobody"])
2205 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
2206 root_gid = pwd.getpwuid(root_uid).pw_gid
2208 try:
2209 bind_gid = findnss_gid(["bind", "named"])
2210 except KeyError:
2211 bind_gid = None
2213 if targetdir is not None:
2214 smbconf = os.path.join(targetdir, "etc", "smb.conf")
2215 elif smbconf is None:
2216 smbconf = samba.param.default_path()
2217 if not os.path.exists(os.path.dirname(smbconf)):
2218 os.makedirs(os.path.dirname(smbconf))
2220 server_services = []
2221 global_param = {}
2222 if use_rfc2307:
2223 global_param["idmap_ldb:use rfc2307"] = ["yes"]
2225 if dns_backend != "SAMBA_INTERNAL":
2226 server_services.append("-dns")
2227 else:
2228 if dns_forwarder is not None:
2229 global_param["dns forwarder"] = [dns_forwarder]
2231 if use_ntvfs:
2232 server_services.append("+smb")
2233 server_services.append("-s3fs")
2234 global_param["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
2236 if len(server_services) > 0:
2237 global_param["server services"] = server_services
2239 # only install a new smb.conf if there isn't one there already
2240 if os.path.exists(smbconf):
2241 # if Samba Team members can't figure out the weird errors
2242 # loading an empty smb.conf gives, then we need to be smarter.
2243 # Pretend it just didn't exist --abartlet
2244 f = open(smbconf, 'r')
2245 try:
2246 data = f.read().lstrip()
2247 finally:
2248 f.close()
2249 if data is None or data == "":
2250 make_smbconf(smbconf, hostname, domain, realm,
2251 targetdir, serverrole=serverrole,
2252 eadb=useeadb, use_ntvfs=use_ntvfs,
2253 lp=lp, global_param=global_param)
2254 else:
2255 make_smbconf(smbconf, hostname, domain, realm, targetdir,
2256 serverrole=serverrole,
2257 eadb=useeadb, use_ntvfs=use_ntvfs, lp=lp, global_param=global_param)
2259 if lp is None:
2260 lp = samba.param.LoadParm()
2261 lp.load(smbconf)
2262 names = guess_names(lp=lp, hostname=hostname, domain=domain,
2263 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
2264 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
2265 sitename=sitename, rootdn=rootdn, domain_names_forced=(samdb_fill == FILL_DRS))
2266 paths = provision_paths_from_lp(lp, names.dnsdomain)
2268 paths.bind_gid = bind_gid
2269 paths.root_uid = root_uid
2270 paths.root_gid = root_gid
2272 hostip = determine_host_ip(logger, lp, hostip)
2273 hostip6 = determine_host_ip6(logger, lp, hostip6)
2274 names.hostip = hostip
2275 names.hostip6 = hostip6
2276 names.domainguid = domainguid
2277 names.domainsid = domainsid
2278 names.forestsid = domainsid
2280 if serverrole is None:
2281 serverrole = lp.get("server role")
2283 directory_create_or_exists(paths.private_dir, 0o700)
2284 directory_create_or_exists(paths.binddns_dir, 0o770)
2285 directory_create_or_exists(os.path.join(paths.private_dir, "tls"))
2286 directory_create_or_exists(paths.state_dir)
2287 if not plaintext_secrets:
2288 setup_encrypted_secrets_key(paths.encrypted_secrets_key_path)
2290 if paths.sysvol and not os.path.exists(paths.sysvol):
2291 os.makedirs(paths.sysvol, 0o775)
2293 schema = Schema(domainsid, invocationid=invocationid,
2294 schemadn=names.schemadn, base_schema=base_schema)
2296 provision_backend = LDBBackend(paths=paths,
2297 lp=lp,
2298 names=names, logger=logger)
2300 provision_backend.init()
2301 provision_backend.start()
2303 # only install a new shares config db if there is none
2304 if not os.path.exists(paths.shareconf):
2305 logger.info("Setting up share.ldb")
2306 share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
2307 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
2309 logger.info("Setting up secrets.ldb")
2310 secrets_ldb = setup_secretsdb(paths,
2311 session_info=session_info, lp=lp)
2313 try:
2314 logger.info("Setting up the registry")
2315 setup_registry(paths.hklm, session_info, lp=lp)
2317 logger.info("Setting up the privileges database")
2318 setup_privileges(paths.privilege, session_info, lp=lp)
2320 logger.info("Setting up idmap db")
2321 idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
2323 setup_name_mappings(idmap, sid=str(domainsid),
2324 root_uid=root_uid, nobody_uid=nobody_uid,
2325 users_gid=users_gid)
2327 logger.info("Setting up SAM db")
2328 samdb = setup_samdb(paths.samdb, session_info,
2329 provision_backend, lp, names, logger=logger,
2330 serverrole=serverrole,
2331 schema=schema, am_rodc=am_rodc,
2332 plaintext_secrets=plaintext_secrets,
2333 backend_store=backend_store,
2334 backend_store_size=backend_store_size,
2335 batch_mode=batch_mode)
2337 if serverrole == "active directory domain controller":
2338 if paths.netlogon is None:
2339 raise MissingShareError("netlogon", paths.smbconf)
2341 if paths.sysvol is None:
2342 raise MissingShareError("sysvol", paths.smbconf)
2344 if not os.path.isdir(paths.netlogon):
2345 os.makedirs(paths.netlogon, 0o755)
2347 if adminpass is None:
2348 adminpass = samba.generate_random_password(12, 32)
2349 adminpass_generated = True
2350 else:
2351 if isinstance(adminpass, bytes):
2352 adminpass = adminpass.decode('utf-8')
2353 adminpass_generated = False
2355 if samdb_fill == FILL_FULL:
2356 provision_fill(samdb, secrets_ldb, logger, names, paths,
2357 schema=schema, samdb_fill=samdb_fill,
2358 hostip=hostip, hostip6=hostip6,
2359 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
2360 krbtgtpass=krbtgtpass,
2361 policyguid=policyguid, policyguid_dc=policyguid_dc,
2362 invocationid=invocationid, machinepass=machinepass,
2363 ntdsguid=ntdsguid, dns_backend=dns_backend,
2364 dnspass=dnspass, serverrole=serverrole,
2365 dom_for_fun_level=dom_for_fun_level,
2366 lp=lp, use_ntvfs=use_ntvfs,
2367 skip_sysvolacl=skip_sysvolacl)
2369 if adprep_level is not None:
2370 updates_allowed_overridden = False
2371 if lp.get("dsdb:schema update allowed") is None:
2372 lp.set("dsdb:schema update allowed", "yes")
2373 print("Temporarily overriding 'dsdb:schema update allowed' setting")
2374 updates_allowed_overridden = True
2376 samdb.transaction_start()
2377 try:
2378 from samba.forest_update import ForestUpdate
2379 forest = ForestUpdate(samdb, fix=True)
2381 forest.check_updates_iterator([11, 54, 79, 80, 81, 82, 83])
2382 forest.check_updates_functional_level(adprep_level,
2383 DS_DOMAIN_FUNCTION_2008_R2,
2384 update_revision=True)
2386 samdb.transaction_commit()
2387 except Exception as e:
2388 samdb.transaction_cancel()
2389 raise e
2391 samdb.transaction_start()
2392 try:
2393 from samba.domain_update import DomainUpdate
2395 DomainUpdate(samdb,
2396 new_install=True,
2397 fix=True).check_updates_functional_level(
2398 adprep_level,
2399 DS_DOMAIN_FUNCTION_2008,
2400 update_revision=True,
2403 samdb.transaction_commit()
2404 except Exception as e:
2405 samdb.transaction_cancel()
2406 raise e
2408 if updates_allowed_overridden:
2409 lp.set("dsdb:schema update allowed", "no")
2411 current_time = nt_now()
2412 # We want the GKDI key to be instantly available for use
2413 use_start_time = current_time \
2414 - KEY_CYCLE_DURATION - MAX_CLOCK_SKEW
2415 gkdi_root_key_dn = samdb.new_gkdi_root_key(current_time=current_time,
2416 use_start_time=use_start_time)
2417 logger.info("gkdi/gmsa root key added with guid "
2418 f"{gkdi_root_key_dn.get_rdn_value()}")
2420 if not is_heimdal_built():
2421 create_kdc_conf(paths.kdcconf, realm, domain, os.path.dirname(lp.get("log file")))
2422 logger.info("The Kerberos KDC configuration for Samba AD is "
2423 "located at %s", paths.kdcconf)
2425 create_krb5_conf(paths.krb5conf,
2426 dnsdomain=names.dnsdomain, hostname=names.hostname,
2427 realm=names.realm)
2428 logger.info("A Kerberos configuration suitable for Samba AD has been "
2429 "generated at %s", paths.krb5conf)
2430 logger.info("Merge the contents of this file with your system "
2431 "krb5.conf or replace it with this one. Do not create a "
2432 "symlink!")
2434 if serverrole == "active directory domain controller":
2435 create_dns_update_list(paths)
2437 backend_result = provision_backend.post_setup()
2438 provision_backend.shutdown()
2440 except:
2441 secrets_ldb.transaction_cancel()
2442 raise
2444 # Now commit the secrets.ldb to disk
2445 secrets_ldb.transaction_commit()
2447 # the commit creates the dns.keytab in the private directory
2448 create_dns_dir_keytab_link(logger, paths)
2450 result = ProvisionResult()
2451 result.server_role = serverrole
2452 result.domaindn = domaindn
2453 result.paths = paths
2454 result.names = names
2455 result.lp = lp
2456 result.samdb = samdb
2457 result.idmap = idmap
2458 result.domainsid = str(domainsid)
2460 if samdb_fill == FILL_FULL:
2461 result.adminpass_generated = adminpass_generated
2462 result.adminpass = adminpass
2463 else:
2464 result.adminpass_generated = False
2465 result.adminpass = None
2467 result.backend_result = backend_result
2469 if use_rfc2307:
2470 provision_fake_ypserver(logger=logger, samdb=samdb,
2471 domaindn=names.domaindn, netbiosname=names.netbiosname,
2472 nisdomain=names.domain.lower())
2474 return result
2477 def provision_become_dc(smbconf=None, targetdir=None, realm=None,
2478 rootdn=None, domaindn=None, schemadn=None,
2479 configdn=None, serverdn=None, domain=None,
2480 hostname=None, domainsid=None,
2481 machinepass=None, dnspass=None,
2482 dns_backend=None, sitename=None, debuglevel=1,
2483 use_ntvfs=False):
2485 logger = logging.getLogger("provision")
2486 samba.set_debug_level(debuglevel)
2488 res = provision(logger, system_session(),
2489 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
2490 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
2491 configdn=configdn, serverdn=serverdn, domain=domain,
2492 hostname=hostname, hostip=None, domainsid=domainsid,
2493 machinepass=machinepass,
2494 serverrole="active directory domain controller",
2495 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass,
2496 use_ntvfs=use_ntvfs)
2497 res.lp.set("debuglevel", str(debuglevel))
2498 return res
2501 def create_krb5_conf(path, dnsdomain, hostname, realm):
2502 """Write out a file containing a valid krb5.conf file
2504 :param path: Path of the new krb5.conf file.
2505 :param dnsdomain: DNS Domain name
2506 :param hostname: Local hostname
2507 :param realm: Realm name
2509 setup_file(setup_path("krb5.conf"), path, {
2510 "DNSDOMAIN": dnsdomain,
2511 "HOSTNAME": hostname,
2512 "REALM": realm,
2516 class ProvisioningError(Exception):
2517 """A generic provision error."""
2519 def __init__(self, value):
2520 self.value = value
2522 def __str__(self):
2523 return "ProvisioningError: " + self.value
2526 class InvalidNetbiosName(Exception):
2527 """A specified name was not a valid NetBIOS name."""
2529 def __init__(self, name):
2530 super().__init__(
2531 "The name '%r' is not a valid NetBIOS name" % name)
2534 class MissingShareError(ProvisioningError):
2536 def __init__(self, name, smbconf):
2537 super().__init__(
2538 "Existing smb.conf does not have a [%s] share, but you are "
2539 "configuring a DC. Please remove %s or add the share manually." %
2540 (name, smbconf))