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
45 from samba.auth import system_session, admin_session
46 from samba.auth_util import system_session_unix
48 from samba import auth
49 from samba.samba3 import smbd, passdb
50 from samba.samba3 import param as s3param
54 check_all_substituted,
55 is_valid_netbios_char,
62 from samba.dcerpc import security, misc
63 from samba.dcerpc.misc import (
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,
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 (
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 (
110 from samba.provision.sambadns import (
113 create_dns_dir_keytab_link,
114 create_dns_update_list
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):
137 self.shareconf = None
148 self.dns_keytab = None
151 self.private_dir = None
152 self.binddns_dir = None
153 self.state_dir = None
156 class ProvisionNames(object):
164 self.dnsforestdn = None
165 self.dnsdomaindn = None
166 self.ldapmanagerdn = None
167 self.dnsdomain = None
169 self.netbiosname = None
174 self.domainsid = None
175 self.forestsid = None
176 self.domainguid = None
180 def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf,
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()
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
212 current = samdb.search(expression="(objectClass=*)",
213 base="", scope=ldb.SCOPE_BASE,
214 attrs=["defaultNamingContext", "schemaNamingContext",
215 "configurationNamingContext", "rootDomainNamingContext",
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
241 dnsdomaindn = "DC=DomainDnsZones,%s" % (str(names.domaindn))
242 if nc == dnsdomaindn:
243 names.dnsdomaindn = dnsdomaindn
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"])
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]))
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
283 names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
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("}", "")
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"])
296 names.policyid_dc = str(res8[0]["cn"]).replace("{", "").replace("}", "")
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"])
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])
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"])
314 has_legacy_dns_account = True
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"])
322 has_dns_account = True
324 has_dns_account = False
326 if names.dnsdomaindn is not None:
328 names.dns_backend = 'BIND9_DLZ'
330 names.dns_backend = 'SAMBA_INTERNAL'
331 elif has_dns_account or has_legacy_dns_account:
332 names.dns_backend = 'BIND9_FLATFILE'
334 names.dns_backend = 'NONE'
336 dns_admins_sid = get_dnsadmins_sid(samdb, names.domaindn)
337 names.name_map['DnsAdmins'] = str(dns_admins_sid)
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
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)
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)
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")
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
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"""
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,
403 LAST_PROVISION_USN_ATTRIBUTE)
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
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
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:
443 if entry[0].get("provisionnerID"):
444 for e in entry[0]["provisionnerID"]:
446 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
447 tab1 = str(r).split(';')
452 if (len(myids) > 0 and id not in myids):
454 tab2 = p.split(tab1[0])
455 if range.get(id) is None:
457 range[id].append(tab2[0])
458 range[id].append(tab2[1])
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
473 self.server_role = 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."""
488 "Once the above files are installed, your Samba AD server will "
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.
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):
527 root_uid = findnss_uid(root)
528 except KeyError as e:
530 logger.info("Assuming root user has UID zero")
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(
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
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."""
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!" %
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":
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))
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))
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
676 dnsdomain = netbiosname.lower()
682 configdn = "CN=Configuration," + rootdn
684 schemadn = "CN=Schema," + configdn
687 sitename = DEFAULTSITE
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
702 names.netbiosname = netbiosname
703 names.hostname = hostname
704 names.sitename = sitename
705 names.serverdn = serverdn
710 def make_smbconf(smbconf, hostname, domain, realm, targetdir,
711 serverrole=None, eadb=False, use_ntvfs=False, lp=None,
713 """Create a new smb.conf file based on a couple of basic settings.
715 assert smbconf is not 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()
732 "netbios name": netbiosname,
735 "server role": serverrole,
739 lp = samba.param.LoadParm()
740 # Load non-existent file
741 if os.path.exists(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"])
763 if targetdir is not None:
764 privdir = os.path.join(targetdir, "private")
766 os.path.abspath(os.path.join(privdir, "eadb.tdb")))
767 elif not lp.get("posix:eadb"):
768 privdir = lp.get("private dir")
770 os.path.abspath(os.path.join(privdir, "eadb.tdb")))
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")))
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(),
787 global_settings["passdb backend"] = "samba_dsdb"
789 f = open(smbconf, 'w')
791 f.write("[globals]\n")
792 for key, val in global_settings.items():
793 f.write("\t%s = %s\n" % (key, val))
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")
803 # reload the smb.conf
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, root_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.
823 :param root_gid: gid of the UNIX root group.
825 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
827 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
828 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
831 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
832 provision_backend, names, serverrole,
833 erase=False, plaintext_secrets=False,
834 backend_store=None,backend_store_size=None):
835 """Setup the partitions for the SAM database.
837 Alternatively, provision() may call this, and then populate the database.
839 :note: This will wipe the Sam Database!
841 :note: This function always removes the local SAM LDB file. The erase
842 parameter controls whether to erase the existing data, which
843 may not be stored locally but in LDAP.
846 assert session_info is not None
848 # We use options=["modules:"] to stop the modules loading - we
849 # just want to wipe and re-initialise the database, not start it up
852 os.unlink(samdb_path)
856 samdb = Ldb(url=samdb_path, session_info=session_info,
857 lp=lp, options=["modules:"])
859 ldap_backend_line = "# No LDAP backend"
860 if provision_backend.type != "ldb":
861 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
863 required_features = None
864 if not plaintext_secrets:
865 required_features = "requiredFeatures: encryptedSecrets"
867 if backend_store is None:
868 backend_store = get_default_backend_store()
869 backend_store_line = "backendStore: %s" % backend_store
871 if backend_store == "mdb":
872 if required_features is not None:
873 required_features += "\n"
875 required_features = ""
876 required_features += "requiredFeatures: lmdbLevelOne"
878 if required_features is None:
879 required_features = "# No required features"
881 samdb.transaction_start()
883 logger.info("Setting up sam.ldb partitions and settings")
884 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
885 "LDAP_BACKEND_LINE": ldap_backend_line,
886 "BACKEND_STORE": backend_store_line
889 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
890 "BACKEND_TYPE": provision_backend.type,
891 "SERVER_ROLE": serverrole,
892 "REQUIRED_FEATURES": required_features
895 logger.info("Setting up sam.ldb rootDSE")
896 setup_samdb_rootdse(samdb, names)
898 samdb.transaction_cancel()
901 samdb.transaction_commit()
904 def secretsdb_self_join(secretsdb, domain,
905 netbiosname, machinepass, domainsid=None,
906 realm=None, dnsdomain=None,
908 key_version_number=1,
909 secure_channel_type=SEC_CHAN_WKSTA):
910 """Add domain join-specific bits to a secrets database.
912 :param secretsdb: Ldb Handle to the secrets database
913 :param machinepass: Machine password
915 attrs = ["whenChanged",
922 if realm is not None:
923 if dnsdomain is None:
924 dnsdomain = realm.lower()
925 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
928 shortname = netbiosname.lower()
930 # We don't need to set msg["flatname"] here, because rdn_name will handle
931 # it, and it causes problems for modifies anyway
932 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
933 msg["secureChannelType"] = [str(secure_channel_type)]
934 msg["objectClass"] = ["top", "primaryDomain"]
935 if dnsname is not None:
936 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
937 msg["realm"] = [realm]
938 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
939 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
940 msg["privateKeytab"] = ["secrets.keytab"]
942 msg["secret"] = [machinepass.encode('utf-8')]
943 msg["samAccountName"] = ["%s$" % netbiosname]
944 msg["secureChannelType"] = [str(secure_channel_type)]
945 if domainsid is not None:
946 msg["objectSid"] = [ndr_pack(domainsid)]
948 # This complex expression tries to ensure that we don't have more
949 # than one record for this SID, realm or netbios domain at a time,
950 # but we don't delete the old record that we are about to modify,
951 # because that would delete the keytab and previous password.
952 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
953 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
954 scope=ldb.SCOPE_ONELEVEL)
957 secretsdb.delete(del_msg.dn)
959 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
962 msg["priorSecret"] = [res[0]["secret"][0]]
964 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
969 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
974 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
980 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
981 secretsdb.modify(msg)
982 secretsdb.rename(res[0].dn, msg.dn)
984 spn = ['HOST/%s' % shortname]
985 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
986 # if we are a domain controller then we add servicePrincipalName
987 # entries for the keytab code to update.
988 spn.extend(['HOST/%s' % dnsname])
989 msg["servicePrincipalName"] = spn
994 def setup_secretsdb(paths, session_info, lp):
995 """Setup the secrets database.
997 :note: This function does not handle exceptions and transaction on purpose,
998 it's up to the caller to do this job.
1000 :param path: Path to the secrets database.
1001 :param session_info: Session info.
1002 :param credentials: Credentials
1003 :param lp: Loadparm context
1004 :return: LDB handle for the created secrets database
1006 if os.path.exists(paths.secrets):
1007 os.unlink(paths.secrets)
1009 keytab_path = os.path.join(paths.private_dir, paths.keytab)
1010 if os.path.exists(keytab_path):
1011 os.unlink(keytab_path)
1013 bind_dns_keytab_path = os.path.join(paths.binddns_dir, paths.dns_keytab)
1014 if os.path.exists(bind_dns_keytab_path):
1015 os.unlink(bind_dns_keytab_path)
1017 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1018 if os.path.exists(dns_keytab_path):
1019 os.unlink(dns_keytab_path)
1021 path = paths.secrets
1023 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
1025 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
1026 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
1027 secrets_ldb.transaction_start()
1029 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
1031 secrets_ldb.transaction_cancel()
1036 def setup_privileges(path, session_info, lp):
1037 """Setup the privileges database.
1039 :param path: Path to the privileges database.
1040 :param session_info: Session info.
1041 :param credentials: Credentials
1042 :param lp: Loadparm context
1043 :return: LDB handle for the created secrets database
1045 if os.path.exists(path):
1047 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
1048 privilege_ldb.erase()
1049 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
1052 def setup_encrypted_secrets_key(path):
1053 """Setup the encrypted secrets key file.
1055 Any existing key file will be deleted and a new random key generated.
1057 :param path: Path to the secrets key file.
1060 if os.path.exists(path):
1063 flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
1064 mode = stat.S_IRUSR | stat.S_IWUSR
1066 umask_original = os.umask(0)
1068 fd = os.open(path, flags, mode)
1070 os.umask(umask_original)
1072 with os.fdopen(fd, 'wb') as f:
1073 key = samba.generate_random_bytes(16)
1077 def setup_registry(path, session_info, lp):
1078 """Setup the registry.
1080 :param path: Path to the registry database
1081 :param session_info: Session information
1082 :param credentials: Credentials
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 credentials: Credentials
1099 :param lp: Loadparm context
1101 if os.path.exists(path):
1104 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
1106 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
1110 def setup_samdb_rootdse(samdb, names):
1111 """Setup the SamDB rootdse.
1113 :param samdb: Sam Database handle
1115 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
1116 "SCHEMADN": names.schemadn,
1117 "DOMAINDN": names.domaindn,
1118 "ROOTDN": names.rootdn,
1119 "CONFIGDN": names.configdn,
1120 "SERVERDN": names.serverdn,
1124 def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
1125 dns_backend, dnspass, domainsid, next_rid, invocationid,
1126 policyguid, policyguid_dc,
1127 domainControllerFunctionality, ntdsguid=None, dc_rid=None):
1128 """Join a host to its own domain."""
1129 assert isinstance(invocationid, str)
1130 if ntdsguid is not None:
1131 ntdsguid_line = "objectGUID: %s\n" % ntdsguid
1138 # Some clients/applications (like exchange) make use of
1139 # the operatingSystemVersion attribute in order to
1140 # find if a DC is good enough.
1142 # So we better use a value matching a Windows DC
1143 # with the same domainControllerFunctionality level
1144 operatingSystemVersion = samba.dsdb.dc_operatingSystemVersion(domainControllerFunctionality)
1146 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
1147 "CONFIGDN": names.configdn,
1148 "SCHEMADN": names.schemadn,
1149 "DOMAINDN": names.domaindn,
1150 "SERVERDN": names.serverdn,
1151 "INVOCATIONID": invocationid,
1152 "NETBIOSNAME": names.netbiosname,
1153 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1154 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')).decode('utf8'),
1155 "DOMAINSID": str(domainsid),
1156 "DCRID": str(dc_rid),
1157 "OPERATING_SYSTEM": "Samba-%s" % version,
1158 "OPERATING_SYSTEM_VERSION": operatingSystemVersion,
1159 "NTDSGUID": ntdsguid_line,
1160 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1161 domainControllerFunctionality),
1162 "RIDALLOCATIONSTART": str(next_rid + 100),
1163 "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
1165 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1166 "POLICYGUID": policyguid,
1167 "POLICYGUID_DC": policyguid_dc,
1168 "DNSDOMAIN": names.dnsdomain,
1169 "DOMAINDN": names.domaindn})
1171 # If we are setting up a subdomain, then this has been replicated in, so we
1172 # don't need to add it
1173 if fill == FILL_FULL:
1174 setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
1175 "CONFIGDN": names.configdn,
1176 "SCHEMADN": names.schemadn,
1177 "DOMAINDN": names.domaindn,
1178 "SERVERDN": names.serverdn,
1179 "INVOCATIONID": invocationid,
1180 "NETBIOSNAME": names.netbiosname,
1181 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1182 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')).decode('utf8'),
1183 "DOMAINSID": str(domainsid),
1184 "DCRID": str(dc_rid),
1185 "SAMBA_VERSION_STRING": version,
1186 "NTDSGUID": ntdsguid_line,
1187 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1188 domainControllerFunctionality)})
1190 # Setup fSMORoleOwner entries to point at the newly created DC entry
1191 setup_modify_ldif(samdb,
1192 setup_path("provision_self_join_modify_schema.ldif"), {
1193 "SCHEMADN": names.schemadn,
1194 "SERVERDN": names.serverdn,
1196 controls=["provision:0", "relax:0"])
1197 setup_modify_ldif(samdb,
1198 setup_path("provision_self_join_modify_config.ldif"), {
1199 "CONFIGDN": names.configdn,
1200 "DEFAULTSITE": names.sitename,
1201 "NETBIOSNAME": names.netbiosname,
1202 "SERVERDN": names.serverdn,
1205 system_session_info = system_session()
1206 samdb.set_session_info(system_session_info)
1207 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1208 # modify a serverReference under cn=config when we are a subdomain, we must
1209 # be system due to ACLs
1210 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1211 "DOMAINDN": names.domaindn,
1212 "SERVERDN": names.serverdn,
1213 "NETBIOSNAME": names.netbiosname,
1216 samdb.set_session_info(admin_session_info)
1218 if dns_backend != "SAMBA_INTERNAL":
1219 # This is Samba4 specific and should be replaced by the correct
1220 # DNS AD-style setup
1221 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1222 "DNSDOMAIN": names.dnsdomain,
1223 "DOMAINDN": names.domaindn,
1224 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')).decode('utf8'),
1225 "HOSTNAME": names.hostname,
1226 "DNSNAME": '%s.%s' % (
1227 names.netbiosname.lower(), names.dnsdomain.lower())
1231 def getpolicypath(sysvolpath, dnsdomain, guid):
1232 """Return the physical path of policy given its guid.
1234 :param sysvolpath: Path to the sysvol folder
1235 :param dnsdomain: DNS name of the AD domain
1236 :param guid: The GUID of the policy
1237 :return: A string with the complete path to the policy folder
1240 guid = "{%s}" % guid
1241 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1245 def create_gpo_struct(policy_path):
1246 if not os.path.exists(policy_path):
1247 os.makedirs(policy_path, 0o775)
1248 f = open(os.path.join(policy_path, "GPT.INI"), 'w')
1250 f.write("[General]\r\nVersion=0")
1253 p = os.path.join(policy_path, "MACHINE")
1254 if not os.path.exists(p):
1255 os.makedirs(p, 0o775)
1256 p = os.path.join(policy_path, "USER")
1257 if not os.path.exists(p):
1258 os.makedirs(p, 0o775)
1261 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1262 """Create the default GPO for a domain
1264 :param sysvolpath: Physical path for the sysvol folder
1265 :param dnsdomain: DNS domain name of the AD domain
1266 :param policyguid: GUID of the default domain policy
1267 :param policyguid_dc: GUID of the default domain controller policy
1269 policy_path = getpolicypath(sysvolpath, dnsdomain, policyguid)
1270 create_gpo_struct(policy_path)
1272 policy_path = getpolicypath(sysvolpath, dnsdomain, policyguid_dc)
1273 create_gpo_struct(policy_path)
1276 # Default the database size to 8Gb
1277 DEFAULT_BACKEND_SIZE = 8 * 1024 * 1024 *1024
1279 def setup_samdb(path, session_info, provision_backend, lp, names,
1280 logger, fill, serverrole, schema, am_rodc=False,
1281 plaintext_secrets=False, backend_store=None,
1282 backend_store_size=None, batch_mode=False):
1283 """Setup a complete SAM Database.
1285 :note: This will wipe the main SAM database file!
1288 # Also wipes the database
1289 setup_samdb_partitions(path, logger=logger, lp=lp,
1290 provision_backend=provision_backend, session_info=session_info,
1291 names=names, serverrole=serverrole, plaintext_secrets=plaintext_secrets,
1292 backend_store=backend_store,
1293 backend_store_size=backend_store_size)
1295 store_size = DEFAULT_BACKEND_SIZE
1296 if backend_store_size:
1297 store_size = backend_store_size
1300 if backend_store == "mdb":
1301 options.append("lmdb_env_size:" + str(store_size))
1303 options.append("batch_mode:1")
1305 # Estimate the number of index records in the transaction_index_cache
1306 # Numbers chosen give the prime 202481 for the default backend size,
1307 # which works well for a 100,000 user database
1308 cache_size = int(store_size / 42423) + 1
1309 options.append("transaction_index_cache_size:" + str(cache_size))
1311 # Load the database, but don's load the global schema and don't connect
1313 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1315 global_schema=False, am_rodc=am_rodc, options=options)
1317 logger.info("Pre-loading the Samba 4 and AD schema")
1319 # Load the schema from the one we computed earlier
1320 samdb.set_schema(schema, write_indices_and_attributes=False)
1322 # Set the NTDS settings DN manually - in order to have it already around
1323 # before the provisioned tree exists and we connect
1324 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1326 # And now we can connect to the DB - the schema won't be loaded from the
1329 samdb.connect(path, options=options)
1330 except ldb.LdbError as e2:
1331 (num, string_error) = e2.args
1332 if (num == ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS):
1333 raise ProvisioningError("Permission denied connecting to %s, are you running as root?" % path)
1337 # But we have to give it one more kick to have it use the schema
1338 # during provision - it needs, now that it is connected, to write
1339 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1340 samdb.set_schema(schema, write_indices_and_attributes=True)
1345 def fill_samdb(samdb, lp, names, logger, policyguid,
1346 policyguid_dc, fill, adminpass, krbtgtpass, machinepass, dns_backend,
1347 dnspass, invocationid, ntdsguid, serverrole, am_rodc=False,
1348 dom_for_fun_level=None, schema=None, next_rid=None, dc_rid=None,
1350 backend_store_size=None):
1352 if next_rid is None:
1355 # Provision does not make much sense values larger than 1000000000
1356 # as the upper range of the rIDAvailablePool is 1073741823 and
1357 # we don't want to create a domain that cannot allocate rids.
1358 if next_rid < 1000 or next_rid > 1000000000:
1359 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1360 error += "the valid range is %u-%u. The default is %u." % (
1361 1000, 1000000000, 1000)
1362 raise ProvisioningError(error)
1364 domainControllerFunctionality = functional_level.dc_level_from_lp(lp)
1366 # ATTENTION: Do NOT change these default values without discussion with the
1367 # team and/or release manager. They have a big impact on the whole program!
1368 if dom_for_fun_level is None:
1369 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
1371 if dom_for_fun_level > domainControllerFunctionality:
1372 level = functional_level.level_to_string(domainControllerFunctionality)
1373 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!")
1375 domainFunctionality = dom_for_fun_level
1376 forestFunctionality = dom_for_fun_level
1378 # Set the NTDS settings DN manually - in order to have it already around
1379 # before the provisioned tree exists and we connect
1380 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1382 # Set the domain functionality levels onto the database.
1383 # Various module (the password_hash module in particular) need
1384 # to know what level of AD we are emulating.
1386 # These will be fixed into the database via the database
1387 # modifictions below, but we need them set from the start.
1388 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1389 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1390 samdb.set_opaque_integer("domainControllerFunctionality",
1391 domainControllerFunctionality)
1393 samdb.set_domain_sid(str(names.domainsid))
1394 samdb.set_invocation_id(invocationid)
1396 logger.info("Adding DomainDN: %s" % names.domaindn)
1398 # impersonate domain admin
1399 admin_session_info = admin_session(lp, str(names.domainsid))
1400 samdb.set_session_info(admin_session_info)
1401 if names.domainguid is not None:
1402 domainguid_line = "objectGUID: %s\n-" % names.domainguid
1404 domainguid_line = ""
1406 descr = b64encode(get_domain_descriptor(names.domainsid)).decode('utf8')
1407 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1408 "DOMAINDN": names.domaindn,
1409 "DOMAINSID": str(names.domainsid),
1410 "DESCRIPTOR": descr,
1411 "DOMAINGUID": domainguid_line
1414 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1415 "DOMAINDN": names.domaindn,
1416 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1417 "NEXTRID": str(next_rid),
1418 "DEFAULTSITE": names.sitename,
1419 "CONFIGDN": names.configdn,
1420 "POLICYGUID": policyguid,
1421 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1422 "SAMBA_VERSION_STRING": version,
1423 "MIN_PWD_LENGTH": str(DEFAULT_MIN_PWD_LENGTH)
1426 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1427 if fill == FILL_FULL:
1428 logger.info("Adding configuration container")
1429 descr = b64encode(get_config_descriptor(names.domainsid)).decode('utf8')
1430 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1431 "CONFIGDN": names.configdn,
1432 "DESCRIPTOR": descr,
1435 # The LDIF here was created when the Schema object was constructed
1436 ignore_checks_oid = "local_oid:%s:0" % samba.dsdb.DSDB_CONTROL_SKIP_DUPLICATES_CHECK_OID
1443 logger.info("Setting up sam.ldb schema")
1444 samdb.add_ldif(schema.schema_dn_add, controls=schema_controls)
1445 samdb.modify_ldif(schema.schema_dn_modify, controls=schema_controls)
1446 samdb.write_prefixes_from_schema()
1447 samdb.add_ldif(schema.schema_data, controls=schema_controls)
1448 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1449 {"SCHEMADN": names.schemadn},
1450 controls=schema_controls)
1452 # Now register this container in the root of the forest
1453 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1454 msg["subRefs"] = ldb.MessageElement(names.configdn, ldb.FLAG_MOD_ADD,
1457 deletedobjects_descr = b64encode(get_deletedobjects_descriptor(names.domainsid)).decode('utf8')
1459 samdb.invocation_id = invocationid
1461 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1462 if fill == FILL_FULL:
1463 logger.info("Setting up sam.ldb configuration data")
1465 partitions_descr = b64encode(get_config_partitions_descriptor(names.domainsid)).decode('utf8')
1466 sites_descr = b64encode(get_config_sites_descriptor(names.domainsid)).decode('utf8')
1467 ntdsquotas_descr = b64encode(get_config_ntds_quotas_descriptor(names.domainsid)).decode('utf8')
1468 protected1_descr = b64encode(get_config_delete_protected1_descriptor(names.domainsid)).decode('utf8')
1469 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid)).decode('utf8')
1470 protected2_descr = b64encode(get_config_delete_protected2_descriptor(names.domainsid)).decode('utf8')
1472 if "2008" in schema.base_schema:
1473 # exclude 2012-specific changes if we're using a 2008 schema
1478 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1479 "CONFIGDN": names.configdn,
1480 "NETBIOSNAME": names.netbiosname,
1481 "DEFAULTSITE": names.sitename,
1482 "DNSDOMAIN": names.dnsdomain,
1483 "DOMAIN": names.domain,
1484 "SCHEMADN": names.schemadn,
1485 "DOMAINDN": names.domaindn,
1486 "SERVERDN": names.serverdn,
1487 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1488 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1489 "NTDSQUOTAS_DESCRIPTOR": ntdsquotas_descr,
1490 "DELETEDOBJECTS_DESCRIPTOR": deletedobjects_descr,
1491 "LOSTANDFOUND_DESCRIPTOR": protected1wd_descr,
1492 "SERVICES_DESCRIPTOR": protected1_descr,
1493 "PHYSICALLOCATIONS_DESCRIPTOR": protected1wd_descr,
1494 "FORESTUPDATES_DESCRIPTOR": protected1wd_descr,
1495 "EXTENDEDRIGHTS_DESCRIPTOR": protected2_descr,
1496 "PARTITIONS_DESCRIPTOR": partitions_descr,
1497 "SITES_DESCRIPTOR": sites_descr,
1500 setup_add_ldif(samdb, setup_path("extended-rights.ldif"), {
1501 "CONFIGDN": names.configdn,
1502 "INC2012": incl_2012,
1505 logger.info("Setting up display specifiers")
1506 display_specifiers_ldif = read_ms_ldif(
1507 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1508 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1509 {"CONFIGDN": names.configdn})
1510 check_all_substituted(display_specifiers_ldif)
1511 samdb.add_ldif(display_specifiers_ldif)
1513 logger.info("Modifying display specifiers and extended rights")
1514 setup_modify_ldif(samdb,
1515 setup_path("provision_configuration_modify.ldif"), {
1516 "CONFIGDN": names.configdn,
1517 "DISPLAYSPECIFIERS_DESCRIPTOR": protected2_descr
1520 logger.info("Adding users container")
1521 users_desc = b64encode(get_domain_users_descriptor(names.domainsid)).decode('utf8')
1522 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1523 "DOMAINDN": names.domaindn,
1524 "USERS_DESCRIPTOR": users_desc
1526 logger.info("Modifying users container")
1527 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1528 "DOMAINDN": names.domaindn})
1529 logger.info("Adding computers container")
1530 computers_desc = b64encode(get_domain_computers_descriptor(names.domainsid)).decode('utf8')
1531 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1532 "DOMAINDN": names.domaindn,
1533 "COMPUTERS_DESCRIPTOR": computers_desc
1535 logger.info("Modifying computers container")
1536 setup_modify_ldif(samdb,
1537 setup_path("provision_computers_modify.ldif"), {
1538 "DOMAINDN": names.domaindn})
1539 logger.info("Setting up sam.ldb data")
1540 infrastructure_desc = b64encode(get_domain_infrastructure_descriptor(names.domainsid)).decode('utf8')
1541 lostandfound_desc = b64encode(get_domain_delete_protected2_descriptor(names.domainsid)).decode('utf8')
1542 system_desc = b64encode(get_domain_delete_protected1_descriptor(names.domainsid)).decode('utf8')
1543 builtin_desc = b64encode(get_domain_builtin_descriptor(names.domainsid)).decode('utf8')
1544 controllers_desc = b64encode(get_domain_controllers_descriptor(names.domainsid)).decode('utf8')
1545 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1546 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1547 "DOMAINDN": names.domaindn,
1548 "NETBIOSNAME": names.netbiosname,
1549 "DEFAULTSITE": names.sitename,
1550 "CONFIGDN": names.configdn,
1551 "SERVERDN": names.serverdn,
1552 "RIDAVAILABLESTART": str(next_rid + 600),
1553 "POLICYGUID_DC": policyguid_dc,
1554 "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc,
1555 "DELETEDOBJECTS_DESCRIPTOR": deletedobjects_descr,
1556 "LOSTANDFOUND_DESCRIPTOR": lostandfound_desc,
1557 "SYSTEM_DESCRIPTOR": system_desc,
1558 "BUILTIN_DESCRIPTOR": builtin_desc,
1559 "DOMAIN_CONTROLLERS_DESCRIPTOR": controllers_desc,
1562 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1563 if fill == FILL_FULL:
1564 managedservice_descr = b64encode(get_managed_service_accounts_descriptor(names.domainsid)).decode('utf8')
1565 setup_modify_ldif(samdb,
1566 setup_path("provision_configuration_references.ldif"), {
1567 "CONFIGDN": names.configdn,
1568 "SCHEMADN": names.schemadn})
1570 logger.info("Setting up well known security principals")
1571 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid)).decode('utf8')
1572 setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1573 "CONFIGDN": names.configdn,
1574 "WELLKNOWNPRINCIPALS_DESCRIPTOR": protected1wd_descr,
1575 }, controls=["relax:0", "provision:0"])
1577 if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1578 setup_modify_ldif(samdb,
1579 setup_path("provision_basedn_references.ldif"), {
1580 "DOMAINDN": names.domaindn,
1581 "MANAGEDSERVICE_DESCRIPTOR": managedservice_descr
1584 logger.info("Setting up sam.ldb users and groups")
1585 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1586 "DOMAINDN": names.domaindn,
1587 "DOMAINSID": str(names.domainsid),
1588 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')).decode('utf8'),
1589 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le')).decode('utf8')
1590 }, controls=["relax:0", "provision:0"])
1592 logger.info("Setting up self join")
1593 setup_self_join(samdb, admin_session_info, names=names, fill=fill,
1594 invocationid=invocationid,
1595 dns_backend=dns_backend,
1597 machinepass=machinepass,
1598 domainsid=names.domainsid,
1601 policyguid=policyguid,
1602 policyguid_dc=policyguid_dc,
1603 domainControllerFunctionality=domainControllerFunctionality,
1606 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1607 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1608 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE).decode('utf8')
1609 assert isinstance(names.ntdsguid, str)
1614 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;FA;;;BA)(A;OICI;0x1200a9;;;SO)(A;OICI;FA;;;SY)(A;OICI;0x1200a9;;;AU)"
1615 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)"
1616 SYSVOL_SERVICE = "sysvol"
1619 def set_dir_acl(path, acl, lp, domsid, use_ntvfs, passdb, service=SYSVOL_SERVICE):
1620 session_info = system_session_unix()
1621 setntacl(lp, path, acl, domsid, session_info, use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1622 for root, dirs, files in os.walk(path, topdown=False):
1624 setntacl(lp, os.path.join(root, name), acl, domsid, session_info,
1625 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1627 setntacl(lp, os.path.join(root, name), acl, domsid, session_info,
1628 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1631 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb):
1632 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1635 :param sysvol: Physical path for the sysvol folder
1636 :param dnsdomain: The DNS name of the domain
1637 :param domainsid: The SID of the domain
1638 :param domaindn: The DN of the domain (ie. DC=...)
1639 :param samdb: An LDB object on the SAM db
1640 :param lp: an LP object
1643 # Set ACL for GPO root folder
1644 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1645 session_info = system_session_unix()
1647 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid), session_info,
1648 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=SYSVOL_SERVICE)
1650 res = samdb.search(base="CN=Policies,CN=System,%s" %(domaindn),
1651 attrs=["cn", "nTSecurityDescriptor"],
1652 expression="", scope=ldb.SCOPE_ONELEVEL)
1655 acl = ndr_unpack(security.descriptor,
1656 policy["nTSecurityDescriptor"][0]).as_sddl()
1657 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1658 set_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1659 str(domainsid), use_ntvfs,
1663 def setsysvolacl(samdb, sysvol, uid, gid, domainsid, dnsdomain,
1664 domaindn, lp, use_ntvfs):
1665 """Set the ACL for the sysvol share and the subfolders
1667 :param samdb: An LDB object on the SAM db
1668 :param sysvol: Physical path for the sysvol folder
1669 :param uid: The UID of the "Administrator" user
1670 :param gid: The GID of the "Domain administrators" group
1671 :param domainsid: The SID of the domain
1672 :param dnsdomain: The DNS name of the domain
1673 :param domaindn: The DN of the domain (ie. DC=...)
1678 s3conf = s3param.get_context()
1679 s3conf.load(lp.configfile)
1681 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(sysvol))
1684 smbd.set_simple_acl(file.name, 0o755, system_session_unix(), gid)
1686 if not smbd.have_posix_acls():
1687 # This clue is only strictly correct for RPM and
1688 # Debian-like Linux systems, but hopefully other users
1689 # will get enough clue from it.
1690 raise ProvisioningError("Samba was compiled without the posix ACL support that s3fs requires. "
1691 "Try installing libacl1-dev or libacl-devel, then re-run configure and make.")
1693 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. "
1694 "Try the mounting the filesystem with the 'acl' option.")
1696 smbd.chown(file.name, uid, gid, system_session_unix())
1698 raise ProvisioningError("Unable to chown a file on your filesystem. "
1699 "You may not be running provision as root.")
1703 # This will ensure that the smbd code we are running when setting ACLs
1704 # is initialised with the smb.conf
1705 s3conf = s3param.get_context()
1706 s3conf.load(lp.configfile)
1707 # ensure we are using the right samba_dsdb passdb backend, no matter what
1708 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1709 passdb.reload_static_pdb()
1711 # ensure that we init the samba_dsdb backend, so the domain sid is
1712 # marked in secrets.tdb
1713 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1715 # now ensure everything matches correctly, to avoid weird issues
1716 if passdb.get_global_sam_sid() != domainsid:
1717 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))
1719 domain_info = s4_passdb.domain_info()
1720 if domain_info["dom_sid"] != domainsid:
1721 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))
1723 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1724 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()))
1728 os.chown(sysvol, -1, gid)
1734 # use admin sid dn as user dn, since admin should own most of the files,
1735 # the operation will be much faster
1736 userdn = '<SID={}-{}>'.format(domainsid, security.DOMAIN_RID_ADMINISTRATOR)
1738 flags = (auth.AUTH_SESSION_INFO_DEFAULT_GROUPS |
1739 auth.AUTH_SESSION_INFO_AUTHENTICATED |
1740 auth.AUTH_SESSION_INFO_SIMPLE_PRIVILEGES)
1742 session_info = auth.user_session(samdb, lp_ctx=lp, dn=userdn,
1743 session_info_flags=flags)
1744 auth.session_info_set_unix(session_info,
1746 user_name="Administrator",
1750 def _setntacl(path):
1751 """A helper to reuse args"""
1753 lp, path, SYSVOL_ACL, str(domainsid), session_info,
1754 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=s4_passdb,
1755 service=SYSVOL_SERVICE)
1757 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1759 for root, dirs, files in os.walk(sysvol, topdown=False):
1761 if use_ntvfs and canchown:
1762 os.chown(os.path.join(root, name), -1, gid)
1763 _setntacl(os.path.join(root, name))
1765 if use_ntvfs and canchown:
1766 os.chown(os.path.join(root, name), -1, gid)
1767 _setntacl(os.path.join(root, name))
1769 # Set acls on Policy folder and policies folders
1770 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb=s4_passdb)
1773 def acl_type(direct_db_access):
1774 if direct_db_access:
1780 def check_dir_acl(path, acl, lp, domainsid, direct_db_access):
1781 session_info = system_session_unix()
1782 fsacl = getntacl(lp, path, session_info, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1783 fsacl_sddl = fsacl.as_sddl(domainsid)
1784 if fsacl_sddl != acl:
1785 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))
1787 for root, dirs, files in os.walk(path, topdown=False):
1789 fsacl = getntacl(lp, os.path.join(root, name), session_info,
1790 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1792 raise ProvisioningError('%s ACL on GPO file %s not found!' %
1793 (acl_type(direct_db_access),
1794 os.path.join(root, name)))
1795 fsacl_sddl = fsacl.as_sddl(domainsid)
1796 if fsacl_sddl != acl:
1797 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))
1800 fsacl = getntacl(lp, os.path.join(root, name), session_info,
1801 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1803 raise ProvisioningError('%s ACL on GPO directory %s not found!'
1804 % (acl_type(direct_db_access),
1805 os.path.join(root, name)))
1806 fsacl_sddl = fsacl.as_sddl(domainsid)
1807 if fsacl_sddl != acl:
1808 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))
1811 def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1813 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1816 :param sysvol: Physical path for the sysvol folder
1817 :param dnsdomain: The DNS name of the domain
1818 :param domainsid: The SID of the domain
1819 :param domaindn: The DN of the domain (ie. DC=...)
1820 :param samdb: An LDB object on the SAM db
1821 :param lp: an LP object
1824 # Set ACL for GPO root folder
1825 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1826 session_info = system_session_unix()
1827 fsacl = getntacl(lp, root_policy_path, session_info,
1828 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1830 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access), root_policy_path))
1831 fsacl_sddl = fsacl.as_sddl(domainsid)
1832 if fsacl_sddl != POLICIES_ACL:
1833 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))
1834 res = samdb.search(base="CN=Policies,CN=System,%s" %(domaindn),
1835 attrs=["cn", "nTSecurityDescriptor"],
1836 expression="", scope=ldb.SCOPE_ONELEVEL)
1839 acl = ndr_unpack(security.descriptor,
1840 policy["nTSecurityDescriptor"][0]).as_sddl()
1841 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1842 check_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1843 domainsid, direct_db_access)
1846 def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn,
1848 """Set the ACL for the sysvol share and the subfolders
1850 :param samdb: An LDB object on the SAM db
1851 :param netlogon: Physical path for the netlogon folder
1852 :param sysvol: Physical path for the sysvol folder
1853 :param uid: The UID of the "Administrator" user
1854 :param gid: The GID of the "Domain administrators" group
1855 :param domainsid: The SID of the domain
1856 :param dnsdomain: The DNS name of the domain
1857 :param domaindn: The DN of the domain (ie. DC=...)
1860 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1861 s3conf = s3param.get_context()
1862 s3conf.load(lp.configfile)
1863 # ensure we are using the right samba_dsdb passdb backend, no matter what
1864 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1865 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1866 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1868 # now ensure everything matches correctly, to avoid weird issues
1869 if passdb.get_global_sam_sid() != domainsid:
1870 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))
1872 domain_info = s4_passdb.domain_info()
1873 if domain_info["dom_sid"] != domainsid:
1874 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))
1876 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1877 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()))
1879 # Ensure we can read this directly, and via the smbd VFS
1880 session_info = system_session_unix()
1881 for direct_db_access in [True, False]:
1882 # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1883 for dir_path in [os.path.join(sysvol, dnsdomain), netlogon]:
1884 fsacl = getntacl(lp, dir_path, session_info, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1886 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access), dir_path))
1887 fsacl_sddl = fsacl.as_sddl(domainsid)
1888 if fsacl_sddl != SYSVOL_ACL:
1889 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))
1891 # Check acls on Policy folder and policies folders
1892 check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1896 def interface_ips_v4(lp, all_interfaces=False):
1897 """return only IPv4 IPs"""
1898 ips = samba.interface_ips(lp, all_interfaces)
1901 if i.find(':') == -1:
1906 def interface_ips_v6(lp):
1907 """return only IPv6 IPs"""
1908 ips = samba.interface_ips(lp, False)
1911 if i.find(':') != -1:
1916 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1918 samdb_fill=FILL_FULL,
1919 hostip=None, hostip6=None,
1920 next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1921 domainguid=None, policyguid=None, policyguid_dc=None,
1922 invocationid=None, machinepass=None, ntdsguid=None,
1923 dns_backend=None, dnspass=None,
1924 serverrole=None, dom_for_fun_level=None,
1925 am_rodc=False, lp=None, use_ntvfs=False,
1926 skip_sysvolacl=False, backend_store=None,
1927 backend_store_size=None):
1928 # create/adapt the group policy GUIDs
1929 # Default GUID for default policy are described at
1930 # "How Core Group Policy Works"
1931 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1932 if policyguid is None:
1933 policyguid = DEFAULT_POLICY_GUID
1934 policyguid = policyguid.upper()
1935 if policyguid_dc is None:
1936 policyguid_dc = DEFAULT_DC_POLICY_GUID
1937 policyguid_dc = policyguid_dc.upper()
1939 if invocationid is None:
1940 invocationid = str(uuid.uuid4())
1942 if krbtgtpass is None:
1943 # Note that the machinepass value is ignored
1944 # as the backend (password_hash.c) will generate its
1945 # own random values for the krbtgt keys
1946 krbtgtpass = samba.generate_random_machine_password(128, 255)
1947 if machinepass is None:
1948 machinepass = samba.generate_random_machine_password(120, 120)
1950 dnspass = samba.generate_random_password(120, 120)
1952 samdb.transaction_start()
1954 samdb = fill_samdb(samdb, lp, names, logger=logger,
1956 policyguid=policyguid, policyguid_dc=policyguid_dc,
1957 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1958 invocationid=invocationid, machinepass=machinepass,
1959 dns_backend=dns_backend, dnspass=dnspass,
1960 ntdsguid=ntdsguid, serverrole=serverrole,
1961 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1962 next_rid=next_rid, dc_rid=dc_rid,
1963 backend_store=backend_store,
1964 backend_store_size=backend_store_size)
1966 # Set up group policies (domain policy and domain controller
1968 if serverrole == "active directory domain controller":
1969 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1972 samdb.transaction_cancel()
1975 samdb.transaction_commit()
1977 if serverrole == "active directory domain controller":
1978 # Continue setting up sysvol for GPO. This appears to require being
1979 # outside a transaction.
1980 if not skip_sysvolacl:
1981 setsysvolacl(samdb, paths.sysvol, paths.root_uid,
1982 paths.root_gid, names.domainsid, names.dnsdomain,
1983 names.domaindn, lp, use_ntvfs)
1985 logger.info("Setting acl on sysvol skipped")
1987 secretsdb_self_join(secrets_ldb, domain=names.domain,
1988 realm=names.realm, dnsdomain=names.dnsdomain,
1989 netbiosname=names.netbiosname, domainsid=names.domainsid,
1990 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1992 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1993 # In future, this might be determined from some configuration
1994 kerberos_enctypes = str(ENC_ALL_TYPES)
1997 msg = ldb.Message(ldb.Dn(samdb,
1998 samdb.searchone("distinguishedName",
1999 expression="samAccountName=%s$" % names.netbiosname,
2000 scope=ldb.SCOPE_SUBTREE).decode('utf8')))
2001 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
2002 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
2003 name="msDS-SupportedEncryptionTypes")
2005 except ldb.LdbError as e:
2006 (enum, estr) = e.args
2007 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
2008 # It might be that this attribute does not exist in this schema
2011 setup_ad_dns(samdb, secrets_ldb, names, paths, logger,
2012 hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
2013 dnspass=dnspass, os_level=dom_for_fun_level,
2014 fill_level=samdb_fill)
2016 domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
2017 attribute="objectGUID").decode('utf8')
2018 assert isinstance(domainguid, str)
2020 lastProvisionUSNs = get_last_provision_usn(samdb)
2021 maxUSN = get_max_usn(samdb, str(names.rootdn))
2022 if lastProvisionUSNs is not None:
2023 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
2025 set_provision_usn(samdb, 0, maxUSN, invocationid)
2027 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
2028 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
2029 {'NTDSGUID': names.ntdsguid})
2031 # fix any dangling GUIDs from the provision
2032 logger.info("Fixing provision GUIDs")
2033 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
2035 samdb.transaction_start()
2037 # a small number of GUIDs are missing because of ordering issues in the
2039 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
2040 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
2041 scope=ldb.SCOPE_BASE,
2042 attrs=['defaultObjectCategory'])
2043 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
2044 scope=ldb.SCOPE_ONELEVEL,
2045 attrs=['ipsecOwnersReference',
2046 'ipsecFilterReference',
2047 'ipsecISAKMPReference',
2048 'ipsecNegotiationPolicyReference',
2049 'ipsecNFAReference'])
2050 if chk.check_database(DN=names.schemadn, scope=ldb.SCOPE_SUBTREE,
2051 attrs=['attributeId', 'governsId']) != 0:
2052 raise ProvisioningError("Duplicate attributeId or governsId in schema. Must be fixed manually!!")
2054 samdb.transaction_cancel()
2057 samdb.transaction_commit()
2061 "ROLE_STANDALONE": "standalone server",
2062 "ROLE_DOMAIN_MEMBER": "member server",
2063 "ROLE_DOMAIN_BDC": "active directory domain controller",
2064 "ROLE_DOMAIN_PDC": "active directory domain controller",
2065 "dc": "active directory domain controller",
2066 "member": "member server",
2067 "domain controller": "active directory domain controller",
2068 "active directory domain controller": "active directory domain controller",
2069 "member server": "member server",
2070 "standalone": "standalone server",
2071 "standalone server": "standalone server",
2075 def sanitize_server_role(role):
2076 """Sanitize a server role name.
2078 :param role: Server role
2079 :raise ValueError: If the role can not be interpreted
2080 :return: Sanitized server role (one of "member server",
2081 "active directory domain controller", "standalone server")
2084 return _ROLES_MAP[role]
2086 raise ValueError(role)
2089 def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain):
2090 """Create AD entries for the fake ypserver.
2092 This is needed for being able to manipulate posix attrs via ADUC.
2094 samdb.transaction_start()
2096 logger.info("Setting up fake yp server settings")
2097 setup_add_ldif(samdb, setup_path("ypServ30.ldif"), {
2098 "DOMAINDN": domaindn,
2099 "NETBIOSNAME": netbiosname,
2100 "NISDOMAIN": nisdomain,
2103 samdb.transaction_cancel()
2106 samdb.transaction_commit()
2109 def directory_create_or_exists(path, mode=0o755):
2110 if not os.path.exists(path):
2112 os.mkdir(path, mode)
2113 except OSError as e:
2114 if e.errno in [errno.EEXIST]:
2117 raise ProvisioningError("Failed to create directory %s: %s" % (path, e.strerror))
2120 def determine_host_ip(logger, lp, hostip=None):
2122 logger.info("Looking up IPv4 addresses")
2123 hostips = interface_ips_v4(lp)
2124 if len(hostips) > 0:
2126 if len(hostips) > 1:
2127 logger.warning("More than one IPv4 address found. Using %s",
2129 if hostip == "127.0.0.1":
2132 logger.warning("No IPv4 address will be assigned")
2137 def determine_host_ip6(logger, lp, hostip6=None):
2139 logger.info("Looking up IPv6 addresses")
2140 hostips = interface_ips_v6(lp)
2142 hostip6 = hostips[0]
2143 if len(hostips) > 1:
2144 logger.warning("More than one IPv6 address found. Using %s", hostip6)
2146 logger.warning("No IPv6 address will be assigned")
2151 def provision(logger, session_info, smbconf=None,
2152 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
2153 domaindn=None, schemadn=None, configdn=None, serverdn=None,
2154 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
2155 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None,
2156 krbtgtpass=None, domainguid=None, policyguid=None, policyguid_dc=None,
2157 dns_backend=None, dns_forwarder=None, dnspass=None,
2158 invocationid=None, machinepass=None, ntdsguid=None,
2159 root=None, nobody=None, users=None, backup=None,
2160 sitename=None, serverrole=None, dom_for_fun_level=None,
2161 useeadb=False, am_rodc=False, lp=None, use_ntvfs=False,
2162 use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True,
2163 base_schema="2019", adprep_level=DS_DOMAIN_FUNCTION_2016,
2164 plaintext_secrets=False, backend_store=None,
2165 backend_store_size=None, batch_mode=False):
2168 :note: caution, this wipes all existing data!
2172 serverrole = sanitize_server_role(serverrole)
2174 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole)
2176 if dom_for_fun_level is None:
2177 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
2179 if base_schema in ["2008_R2", "2008_R2_old"]:
2180 max_adprep_level = DS_DOMAIN_FUNCTION_2008_R2
2181 elif base_schema in ["2012"]:
2182 max_adprep_level = DS_DOMAIN_FUNCTION_2012
2183 elif base_schema in ["2012_R2"]:
2184 max_adprep_level = DS_DOMAIN_FUNCTION_2012_R2
2186 max_adprep_level = DS_DOMAIN_FUNCTION_2016
2188 if max_adprep_level < dom_for_fun_level:
2189 raise ProvisioningError('dom_for_fun_level[%u] incompatible with base_schema[%s]' %
2190 (dom_for_fun_level, base_schema))
2192 if adprep_level is not None and max_adprep_level < adprep_level:
2193 raise ProvisioningError('base_schema[%s] incompatible with adprep_level[%u]' %
2194 (base_schema, adprep_level))
2196 if adprep_level is not None and adprep_level < dom_for_fun_level:
2197 raise ProvisioningError('dom_for_fun_level[%u] incompatible with adprep_level[%u]' %
2198 (dom_for_fun_level, adprep_level))
2200 if ldapadminpass is None:
2201 # Make a new, random password between Samba and it's LDAP server
2202 ldapadminpass = samba.generate_random_password(128, 255)
2204 if backend_store is None:
2205 backend_store = get_default_backend_store()
2207 if domainsid is None:
2208 domainsid = security.random_sid()
2210 root_uid = get_root_uid([root or "root"], logger)
2211 nobody_uid = findnss_uid([nobody or "nobody"])
2212 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
2213 root_gid = pwd.getpwuid(root_uid).pw_gid
2216 bind_gid = findnss_gid(["bind", "named"])
2220 if targetdir is not None:
2221 smbconf = os.path.join(targetdir, "etc", "smb.conf")
2222 elif smbconf is None:
2223 smbconf = samba.param.default_path()
2224 if not os.path.exists(os.path.dirname(smbconf)):
2225 os.makedirs(os.path.dirname(smbconf))
2227 server_services = []
2230 global_param["idmap_ldb:use rfc2307"] = ["yes"]
2232 if dns_backend != "SAMBA_INTERNAL":
2233 server_services.append("-dns")
2235 if dns_forwarder is not None:
2236 global_param["dns forwarder"] = [dns_forwarder]
2239 server_services.append("+smb")
2240 server_services.append("-s3fs")
2241 global_param["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
2243 if len(server_services) > 0:
2244 global_param["server services"] = server_services
2246 # only install a new smb.conf if there isn't one there already
2247 if os.path.exists(smbconf):
2248 # if Samba Team members can't figure out the weird errors
2249 # loading an empty smb.conf gives, then we need to be smarter.
2250 # Pretend it just didn't exist --abartlet
2251 f = open(smbconf, 'r')
2253 data = f.read().lstrip()
2256 if data is None or data == "":
2257 make_smbconf(smbconf, hostname, domain, realm,
2258 targetdir, serverrole=serverrole,
2259 eadb=useeadb, use_ntvfs=use_ntvfs,
2260 lp=lp, global_param=global_param)
2262 make_smbconf(smbconf, hostname, domain, realm, targetdir,
2263 serverrole=serverrole,
2264 eadb=useeadb, use_ntvfs=use_ntvfs, lp=lp, global_param=global_param)
2267 lp = samba.param.LoadParm()
2269 names = guess_names(lp=lp, hostname=hostname, domain=domain,
2270 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
2271 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
2272 sitename=sitename, rootdn=rootdn, domain_names_forced=(samdb_fill == FILL_DRS))
2273 paths = provision_paths_from_lp(lp, names.dnsdomain)
2275 paths.bind_gid = bind_gid
2276 paths.root_uid = root_uid
2277 paths.root_gid = root_gid
2279 hostip = determine_host_ip(logger, lp, hostip)
2280 hostip6 = determine_host_ip6(logger, lp, hostip6)
2281 names.hostip = hostip
2282 names.hostip6 = hostip6
2283 names.domainguid = domainguid
2284 names.domainsid = domainsid
2285 names.forestsid = domainsid
2287 if serverrole is None:
2288 serverrole = lp.get("server role")
2290 directory_create_or_exists(paths.private_dir, 0o700)
2291 directory_create_or_exists(paths.binddns_dir, 0o770)
2292 directory_create_or_exists(os.path.join(paths.private_dir, "tls"))
2293 directory_create_or_exists(paths.state_dir)
2294 if not plaintext_secrets:
2295 setup_encrypted_secrets_key(paths.encrypted_secrets_key_path)
2297 if paths.sysvol and not os.path.exists(paths.sysvol):
2298 os.makedirs(paths.sysvol, 0o775)
2300 schema = Schema(domainsid, invocationid=invocationid,
2301 schemadn=names.schemadn, base_schema=base_schema)
2303 provision_backend = LDBBackend(paths=paths,
2305 names=names, logger=logger)
2307 provision_backend.init()
2308 provision_backend.start()
2310 # only install a new shares config db if there is none
2311 if not os.path.exists(paths.shareconf):
2312 logger.info("Setting up share.ldb")
2313 share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
2314 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
2316 logger.info("Setting up secrets.ldb")
2317 secrets_ldb = setup_secretsdb(paths,
2318 session_info=session_info, lp=lp)
2321 logger.info("Setting up the registry")
2322 setup_registry(paths.hklm, session_info, lp=lp)
2324 logger.info("Setting up the privileges database")
2325 setup_privileges(paths.privilege, session_info, lp=lp)
2327 logger.info("Setting up idmap db")
2328 idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
2330 setup_name_mappings(idmap, sid=str(domainsid),
2331 root_uid=root_uid, nobody_uid=nobody_uid,
2332 users_gid=users_gid, root_gid=root_gid)
2334 logger.info("Setting up SAM db")
2335 samdb = setup_samdb(paths.samdb, session_info,
2336 provision_backend, lp, names, logger=logger,
2337 serverrole=serverrole,
2338 schema=schema, fill=samdb_fill, am_rodc=am_rodc,
2339 plaintext_secrets=plaintext_secrets,
2340 backend_store=backend_store,
2341 backend_store_size=backend_store_size,
2342 batch_mode=batch_mode)
2344 if serverrole == "active directory domain controller":
2345 if paths.netlogon is None:
2346 raise MissingShareError("netlogon", paths.smbconf)
2348 if paths.sysvol is None:
2349 raise MissingShareError("sysvol", paths.smbconf)
2351 if not os.path.isdir(paths.netlogon):
2352 os.makedirs(paths.netlogon, 0o755)
2354 if adminpass is None:
2355 adminpass = samba.generate_random_password(12, 32)
2356 adminpass_generated = True
2358 if isinstance(adminpass, bytes):
2359 adminpass = adminpass.decode('utf-8')
2360 adminpass_generated = False
2362 if samdb_fill == FILL_FULL:
2363 provision_fill(samdb, secrets_ldb, logger, names, paths,
2364 schema=schema, samdb_fill=samdb_fill,
2365 hostip=hostip, hostip6=hostip6,
2366 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
2367 krbtgtpass=krbtgtpass,
2368 policyguid=policyguid, policyguid_dc=policyguid_dc,
2369 invocationid=invocationid, machinepass=machinepass,
2370 ntdsguid=ntdsguid, dns_backend=dns_backend,
2371 dnspass=dnspass, serverrole=serverrole,
2372 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
2373 lp=lp, use_ntvfs=use_ntvfs,
2374 skip_sysvolacl=skip_sysvolacl,
2375 backend_store=backend_store,
2376 backend_store_size=backend_store_size)
2378 if adprep_level is not None:
2379 updates_allowed_overridden = False
2380 if lp.get("dsdb:schema update allowed") is None:
2381 lp.set("dsdb:schema update allowed", "yes")
2382 print("Temporarily overriding 'dsdb:schema update allowed' setting")
2383 updates_allowed_overridden = True
2385 samdb.transaction_start()
2387 from samba.forest_update import ForestUpdate
2388 forest = ForestUpdate(samdb, fix=True)
2390 forest.check_updates_iterator([11, 54, 79, 80, 81, 82, 83])
2391 forest.check_updates_functional_level(adprep_level,
2392 DS_DOMAIN_FUNCTION_2008_R2,
2393 update_revision=True)
2395 samdb.transaction_commit()
2396 except Exception as e:
2397 samdb.transaction_cancel()
2400 samdb.transaction_start()
2402 from samba.domain_update import DomainUpdate
2404 DomainUpdate(samdb, fix=True).check_updates_functional_level(
2406 DS_DOMAIN_FUNCTION_2008,
2407 update_revision=True,
2410 samdb.transaction_commit()
2411 except Exception as e:
2412 samdb.transaction_cancel()
2415 if updates_allowed_overridden:
2416 lp.set("dsdb:schema update allowed", "no")
2418 if not is_heimdal_built():
2419 create_kdc_conf(paths.kdcconf, realm, domain, os.path.dirname(lp.get("log file")))
2420 logger.info("The Kerberos KDC configuration for Samba AD is "
2421 "located at %s", paths.kdcconf)
2423 create_krb5_conf(paths.krb5conf,
2424 dnsdomain=names.dnsdomain, hostname=names.hostname,
2426 logger.info("A Kerberos configuration suitable for Samba AD has been "
2427 "generated at %s", paths.krb5conf)
2428 logger.info("Merge the contents of this file with your system "
2429 "krb5.conf or replace it with this one. Do not create a "
2432 if serverrole == "active directory domain controller":
2433 create_dns_update_list(paths)
2435 backend_result = provision_backend.post_setup()
2436 provision_backend.shutdown()
2439 secrets_ldb.transaction_cancel()
2442 # Now commit the secrets.ldb to disk
2443 secrets_ldb.transaction_commit()
2445 # the commit creates the dns.keytab in the private directory
2446 create_dns_dir_keytab_link(logger, paths)
2448 result = ProvisionResult()
2449 result.server_role = serverrole
2450 result.domaindn = domaindn
2451 result.paths = paths
2452 result.names = names
2454 result.samdb = samdb
2455 result.idmap = idmap
2456 result.domainsid = str(domainsid)
2458 if samdb_fill == FILL_FULL:
2459 result.adminpass_generated = adminpass_generated
2460 result.adminpass = adminpass
2462 result.adminpass_generated = False
2463 result.adminpass = None
2465 result.backend_result = backend_result
2468 provision_fake_ypserver(logger=logger, samdb=samdb,
2469 domaindn=names.domaindn, netbiosname=names.netbiosname,
2470 nisdomain=names.domain.lower())
2475 def provision_become_dc(smbconf=None, targetdir=None, realm=None,
2476 rootdn=None, domaindn=None, schemadn=None,
2477 configdn=None, serverdn=None, domain=None,
2478 hostname=None, domainsid=None,
2479 machinepass=None, dnspass=None,
2480 dns_backend=None, sitename=None, debuglevel=1,
2483 logger = logging.getLogger("provision")
2484 samba.set_debug_level(debuglevel)
2486 res = provision(logger, system_session(),
2487 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
2488 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
2489 configdn=configdn, serverdn=serverdn, domain=domain,
2490 hostname=hostname, hostip=None, domainsid=domainsid,
2491 machinepass=machinepass,
2492 serverrole="active directory domain controller",
2493 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass,
2494 use_ntvfs=use_ntvfs)
2495 res.lp.set("debuglevel", str(debuglevel))
2499 def create_krb5_conf(path, dnsdomain, hostname, realm):
2500 """Write out a file containing a valid krb5.conf file
2502 :param path: Path of the new krb5.conf file.
2503 :param dnsdomain: DNS Domain name
2504 :param hostname: Local hostname
2505 :param realm: Realm name
2507 setup_file(setup_path("krb5.conf"), path, {
2508 "DNSDOMAIN": dnsdomain,
2509 "HOSTNAME": hostname,
2514 class ProvisioningError(Exception):
2515 """A generic provision error."""
2517 def __init__(self, value):
2521 return "ProvisioningError: " + self.value
2524 class InvalidNetbiosName(Exception):
2525 """A specified name was not a valid NetBIOS name."""
2527 def __init__(self, name):
2528 super(InvalidNetbiosName, self).__init__(
2529 "The name '%r' is not a valid NetBIOS name" % name)
2532 class MissingShareError(ProvisioningError):
2534 def __init__(self, name, smbconf):
2535 super(MissingShareError, self).__init__(
2536 "Existing smb.conf does not have a [%s] share, but you are "
2537 "configuring a DC. Please remove %s or add the share manually." %