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 samba.compat import urllib_quote
30 from samba.compat import string_types
31 from samba.compat import binary_type
32 from base64 import b64encode
48 from samba.auth import system_session, admin_session
50 from samba import auth
51 from samba.samba3 import smbd, passdb
52 from samba.samba3 import param as s3param
53 from samba.dsdb import DS_DOMAIN_FUNCTION_2000
57 check_all_substituted,
58 is_valid_netbios_char,
65 from samba.dcerpc import security, misc
66 from samba.dcerpc.misc import (
70 from samba.dsdb import (
71 DS_DOMAIN_FUNCTION_2003,
72 DS_DOMAIN_FUNCTION_2008_R2,
75 from samba.idmap import IDmapDB
76 from samba.ms_display_specifiers import read_ms_ldif
77 from samba.ntacls import setntacl, getntacl, dsacl2fsacl
78 from samba.ndr import ndr_pack, ndr_unpack
79 from samba.provision.backend import (
84 from samba.descriptor import (
86 get_config_descriptor,
87 get_config_partitions_descriptor,
88 get_config_sites_descriptor,
89 get_config_ntds_quotas_descriptor,
90 get_config_delete_protected1_descriptor,
91 get_config_delete_protected1wd_descriptor,
92 get_config_delete_protected2_descriptor,
93 get_domain_descriptor,
94 get_domain_infrastructure_descriptor,
95 get_domain_builtin_descriptor,
96 get_domain_computers_descriptor,
97 get_domain_users_descriptor,
98 get_domain_controllers_descriptor,
99 get_domain_delete_protected1_descriptor,
100 get_domain_delete_protected2_descriptor,
101 get_dns_partition_descriptor,
102 get_dns_forest_microsoft_dns_descriptor,
103 get_dns_domain_microsoft_dns_descriptor,
104 get_managed_service_accounts_descriptor,
106 from samba.provision.common import (
115 from samba.provision.sambadns import (
118 create_dns_update_list
122 import samba.registry
123 from samba.schema import Schema
124 from samba.samdb import SamDB
125 from samba.dbchecker import dbcheck
126 from samba.provision.kerberos import create_kdc_conf
127 from samba.samdb import get_default_backend_store
129 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
130 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04FB984F9"
131 DEFAULTSITE = "Default-First-Site-Name"
132 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
134 DEFAULT_MIN_PWD_LENGTH = 7
137 class ProvisionPaths(object):
140 self.shareconf = None
151 self.dns_keytab = None
154 self.private_dir = None
155 self.binddns_dir = None
156 self.state_dir = None
159 class ProvisionNames(object):
167 self.dnsforestdn = None
168 self.dnsdomaindn = None
169 self.ldapmanagerdn = None
170 self.dnsdomain = None
172 self.netbiosname = None
177 self.domainsid = None
178 self.forestsid = None
179 self.domainguid = None
183 def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf,
185 """Get key provision parameters (realm, domain, ...) from a given provision
187 :param samdb: An LDB object connected to the sam.ldb file
188 :param secretsdb: An LDB object connected to the secrets.ldb file
189 :param idmapdb: An LDB object connected to the idmap.ldb file
190 :param paths: A list of path to provision object
191 :param smbconf: Path to the smb.conf file
192 :param lp: A LoadParm object
193 :return: A list of key provision parameters
195 names = ProvisionNames()
196 names.adminpass = None
198 # NT domain, kerberos realm, root dn, domain dn, domain dns name
199 names.domain = lp.get("workgroup").upper()
200 names.realm = lp.get("realm")
201 names.dnsdomain = names.realm.lower()
202 basedn = samba.dn_from_dns_name(names.dnsdomain)
203 names.realm = names.realm.upper()
205 # Get the netbiosname first (could be obtained from smb.conf in theory)
206 res = secretsdb.search(expression="(flatname=%s)" %
207 names.domain, base="CN=Primary Domains",
208 scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"])
209 names.netbiosname = str(res[0]["sAMAccountName"]).replace("$", "")
211 names.smbconf = smbconf
213 # That's a bit simplistic but it's ok as long as we have only 3
215 current = samdb.search(expression="(objectClass=*)",
216 base="", scope=ldb.SCOPE_BASE,
217 attrs=["defaultNamingContext", "schemaNamingContext",
218 "configurationNamingContext", "rootDomainNamingContext",
221 names.configdn = str(current[0]["configurationNamingContext"][0])
222 names.schemadn = str(current[0]["schemaNamingContext"][0])
223 if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb,
224 current[0]["defaultNamingContext"][0].decode('utf8')))):
225 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
226 "is not the same ..." % (paths.samdb,
227 str(current[0]["defaultNamingContext"][0].decode('utf8')),
228 paths.smbconf, basedn)))
230 names.domaindn = str(current[0]["defaultNamingContext"][0])
231 names.rootdn = str(current[0]["rootDomainNamingContext"][0])
232 names.ncs = current[0]["namingContexts"]
233 names.dnsforestdn = None
234 names.dnsdomaindn = None
236 for i in range(0, len(names.ncs)):
237 nc = str(names.ncs[i])
239 dnsforestdn = "DC=ForestDnsZones,%s" % (str(names.rootdn))
240 if nc == dnsforestdn:
241 names.dnsforestdn = dnsforestdn
244 dnsdomaindn = "DC=DomainDnsZones,%s" % (str(names.domaindn))
245 if nc == dnsdomaindn:
246 names.dnsdomaindn = dnsdomaindn
250 res3 = samdb.search(expression="(objectClass=site)",
251 base="CN=Sites," + str(names.configdn), scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
252 names.sitename = str(res3[0]["cn"])
254 # dns hostname and server dn
255 res4 = samdb.search(expression="(CN=%s)" % names.netbiosname,
256 base="OU=Domain Controllers,%s" % basedn,
257 scope=ldb.SCOPE_ONELEVEL, attrs=["dNSHostName"])
259 raise ProvisioningError("Unable to find DC called CN=%s under OU=Domain Controllers,%s" % (names.netbiosname, basedn))
261 names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain, "")
263 server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
264 attrs=[], base=names.configdn)
265 names.serverdn = str(server_res[0].dn)
267 # invocation id/objectguid
268 res5 = samdb.search(expression="(objectClass=*)",
269 base="CN=NTDS Settings,%s" % str(names.serverdn),
270 scope=ldb.SCOPE_BASE,
271 attrs=["invocationID", "objectGUID"])
272 names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0]))
273 names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0]))
276 res6 = samdb.search(expression="(objectClass=*)", base=basedn,
277 scope=ldb.SCOPE_BASE, attrs=["objectGUID",
278 "objectSid", "msDS-Behavior-Version"])
279 names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
280 names.domainsid = ndr_unpack(security.dom_sid, res6[0]["objectSid"][0])
281 names.forestsid = ndr_unpack(security.dom_sid, res6[0]["objectSid"][0])
282 if res6[0].get("msDS-Behavior-Version") is None or \
283 int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
284 names.domainlevel = DS_DOMAIN_FUNCTION_2000
286 names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
289 res7 = samdb.search(expression="(name={%s})" % DEFAULT_POLICY_GUID,
290 base="CN=Policies,CN=System," + basedn,
291 scope=ldb.SCOPE_ONELEVEL, attrs=["cn", "displayName"])
292 names.policyid = str(res7[0]["cn"]).replace("{", "").replace("}", "")
294 res8 = samdb.search(expression="(name={%s})" % DEFAULT_DC_POLICY_GUID,
295 base="CN=Policies,CN=System," + basedn,
296 scope=ldb.SCOPE_ONELEVEL,
297 attrs=["cn", "displayName"])
299 names.policyid_dc = str(res8[0]["cn"]).replace("{", "").replace("}", "")
301 names.policyid_dc = None
303 res9 = idmapdb.search(expression="(cn=%s-%s)" %
304 (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR),
305 attrs=["xidNumber", "type"])
307 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid (%s-%s" % (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR))
308 if str(res9[0]["type"][0]) == "ID_TYPE_BOTH":
309 names.root_gid = int(res9[0]["xidNumber"][0])
311 names.root_gid = pwd.getpwuid(int(res9[0]["xidNumber"][0])).pw_gid
313 res10 = samdb.search(expression="(samaccountname=dns)",
314 scope=ldb.SCOPE_SUBTREE, attrs=["dn"],
315 controls=["search_options:1:2"])
317 has_legacy_dns_account = True
319 has_legacy_dns_account = False
321 res11 = samdb.search(expression="(samaccountname=dns-%s)" % names.netbiosname,
322 scope=ldb.SCOPE_SUBTREE, attrs=["dn"],
323 controls=["search_options:1:2"])
325 has_dns_account = True
327 has_dns_account = False
329 if names.dnsdomaindn is not None:
331 names.dns_backend = 'BIND9_DLZ'
333 names.dns_backend = 'SAMBA_INTERNAL'
334 elif has_dns_account or has_legacy_dns_account:
335 names.dns_backend = 'BIND9_FLATFILE'
337 names.dns_backend = 'NONE'
339 dns_admins_sid = get_dnsadmins_sid(samdb, names.domaindn)
340 names.name_map['DnsAdmins'] = str(dns_admins_sid)
345 def update_provision_usn(samdb, low, high, id, replace=False):
346 """Update the field provisionUSN in sam.ldb
348 This field is used to track range of USN modified by provision and
350 This value is used afterward by next provision to figure out if
351 the field have been modified since last provision.
353 :param samdb: An LDB object connect to sam.ldb
354 :param low: The lowest USN modified by this upgrade
355 :param high: The highest USN modified by this upgrade
356 :param id: The invocation id of the samba's dc
357 :param replace: A boolean indicating if the range should replace any
358 existing one or appended (default)
363 entry = samdb.search(base="@PROVISION",
364 scope=ldb.SCOPE_BASE,
365 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
366 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
367 if not re.search(';', str(e)):
368 e = "%s;%s" % (str(e), id)
371 tab.append("%s-%s;%s" % (low, high, id))
372 delta = ldb.Message()
373 delta.dn = ldb.Dn(samdb, "@PROVISION")
374 delta[LAST_PROVISION_USN_ATTRIBUTE] = \
375 ldb.MessageElement(tab,
376 ldb.FLAG_MOD_REPLACE,
377 LAST_PROVISION_USN_ATTRIBUTE)
378 entry = samdb.search(expression='provisionnerID=*',
379 base="@PROVISION", scope=ldb.SCOPE_BASE,
380 attrs=["provisionnerID"])
381 if len(entry) == 0 or len(entry[0]) == 0:
382 delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
386 def set_provision_usn(samdb, low, high, id):
387 """Set the field provisionUSN in sam.ldb
388 This field is used to track range of USN modified by provision and
390 This value is used afterward by next provision to figure out if
391 the field have been modified since last provision.
393 :param samdb: An LDB object connect to sam.ldb
394 :param low: The lowest USN modified by this upgrade
395 :param high: The highest USN modified by this upgrade
396 :param id: The invocationId of the provision"""
399 tab.append("%s-%s;%s" % (low, high, id))
401 delta = ldb.Message()
402 delta.dn = ldb.Dn(samdb, "@PROVISION")
403 delta[LAST_PROVISION_USN_ATTRIBUTE] = \
404 ldb.MessageElement(tab,
406 LAST_PROVISION_USN_ATTRIBUTE)
410 def get_max_usn(samdb, basedn):
411 """ This function return the biggest USN present in the provision
413 :param samdb: A LDB object pointing to the sam.ldb
414 :param basedn: A string containing the base DN of the provision
416 :return: The biggest USN in the provision"""
418 res = samdb.search(expression="objectClass=*", base=basedn,
419 scope=ldb.SCOPE_SUBTREE, attrs=["uSNChanged"],
420 controls=["search_options:1:2",
421 "server_sort:1:1:uSNChanged",
422 "paged_results:1:1"])
423 return res[0]["uSNChanged"]
426 def get_last_provision_usn(sam):
427 """Get USNs ranges modified by a provision or an upgradeprovision
429 :param sam: An LDB object pointing to the sam.ldb
430 :return: a dictionary which keys are invocation id and values are an array
431 of integer representing the different ranges
434 entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE,
435 base="@PROVISION", scope=ldb.SCOPE_BASE,
436 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
437 except ldb.LdbError as e1:
438 (ecode, emsg) = e1.args
439 if ecode == ldb.ERR_NO_SUCH_OBJECT:
446 if entry[0].get("provisionnerID"):
447 for e in entry[0]["provisionnerID"]:
449 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
450 tab1 = str(r).split(';')
455 if (len(myids) > 0 and id not in myids):
457 tab2 = p.split(tab1[0])
458 if range.get(id) is None:
460 range[id].append(tab2[0])
461 range[id].append(tab2[1])
467 class ProvisionResult(object):
468 """Result of a provision.
470 :ivar server_role: The server role
471 :ivar paths: ProvisionPaths instance
472 :ivar domaindn: The domain dn, as string
476 self.server_role = None
483 self.domainsid = None
484 self.adminpass_generated = None
485 self.adminpass = None
486 self.backend_result = None
488 def report_logger(self, logger):
489 """Report this provision result to a logger."""
491 "Once the above files are installed, your Samba AD server will "
493 if self.adminpass_generated:
494 logger.info("Admin password: %s", self.adminpass)
495 logger.info("Server Role: %s", self.server_role)
496 logger.info("Hostname: %s", self.names.hostname)
497 logger.info("NetBIOS Domain: %s", self.names.domain)
498 logger.info("DNS Domain: %s", self.names.dnsdomain)
499 logger.info("DOMAIN SID: %s", self.domainsid)
501 if self.backend_result:
502 self.backend_result.report_logger(logger)
505 def check_install(lp, session_info, credentials):
506 """Check whether the current install seems ok.
508 :param lp: Loadparm context
509 :param session_info: Session information
510 :param credentials: Credentials
512 if lp.get("realm") == "":
513 raise Exception("Realm empty")
514 samdb = Ldb(lp.samdb_url(), session_info=session_info,
515 credentials=credentials, lp=lp)
516 if len(samdb.search("(cn=Administrator)")) != 1:
517 raise ProvisioningError("No administrator account found")
520 def findnss(nssfn, names):
521 """Find a user or group from a list of possibilities.
523 :param nssfn: NSS Function to try (should raise KeyError if not found)
524 :param names: Names to check.
525 :return: Value return by first names list.
532 raise KeyError("Unable to find user/group in %r" % names)
535 def findnss_uid(names):
536 return findnss(pwd.getpwnam, names)[2]
539 def findnss_gid(names):
540 return findnss(grp.getgrnam, names)[2]
543 def provision_paths_from_lp(lp, dnsdomain):
544 """Set the default paths for provisioning.
546 :param lp: Loadparm context.
547 :param dnsdomain: DNS Domain name
549 paths = ProvisionPaths()
550 paths.private_dir = lp.get("private dir")
551 paths.binddns_dir = lp.get("binddns dir")
552 paths.state_dir = lp.get("state directory")
554 # This is stored without path prefix for the "privateKeytab" attribute in
555 # "secrets_dns.ldif".
556 paths.dns_keytab = "dns.keytab"
557 paths.keytab = "secrets.keytab"
559 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
560 paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
561 paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
562 paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
563 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
564 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
565 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
566 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
567 paths.kdcconf = os.path.join(paths.private_dir, "kdc.conf")
568 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
569 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
570 paths.encrypted_secrets_key_path = os.path.join(
572 "encrypted_secrets.key")
574 paths.dns = os.path.join(paths.binddns_dir, "dns", dnsdomain + ".zone")
575 paths.namedconf = os.path.join(paths.binddns_dir, "named.conf")
576 paths.namedconf_update = os.path.join(paths.binddns_dir, "named.conf.update")
577 paths.namedtxt = os.path.join(paths.binddns_dir, "named.txt")
579 paths.hklm = "hklm.ldb"
580 paths.hkcr = "hkcr.ldb"
581 paths.hkcu = "hkcu.ldb"
582 paths.hku = "hku.ldb"
583 paths.hkpd = "hkpd.ldb"
584 paths.hkpt = "hkpt.ldb"
585 paths.sysvol = lp.get("path", "sysvol")
586 paths.netlogon = lp.get("path", "netlogon")
587 paths.smbconf = lp.configfile
591 def determine_netbios_name(hostname):
592 """Determine a netbios name from a hostname."""
593 # remove forbidden chars and force the length to be <16
594 netbiosname = "".join([x for x in hostname if is_valid_netbios_char(x)])
595 return netbiosname[:MAX_NETBIOS_NAME_LEN].upper()
598 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
599 serverrole=None, rootdn=None, domaindn=None, configdn=None,
600 schemadn=None, serverdn=None, sitename=None,
601 domain_names_forced=False):
602 """Guess configuration settings to use."""
605 hostname = socket.gethostname().split(".")[0]
607 netbiosname = lp.get("netbios name")
608 if netbiosname is None:
609 netbiosname = determine_netbios_name(hostname)
610 netbiosname = netbiosname.upper()
611 if not valid_netbios_name(netbiosname):
612 raise InvalidNetbiosName(netbiosname)
614 if dnsdomain is None:
615 dnsdomain = lp.get("realm")
616 if dnsdomain is None or dnsdomain == "":
617 raise ProvisioningError(
618 "guess_names: 'realm' not specified in supplied %s!" %
621 dnsdomain = dnsdomain.lower()
623 if serverrole is None:
624 serverrole = lp.get("server role")
625 if serverrole is None:
626 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
628 serverrole = serverrole.lower()
630 realm = dnsdomain.upper()
632 if lp.get("realm") == "":
633 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
635 if lp.get("realm").upper() != realm:
636 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))
638 if lp.get("server role").lower() != serverrole:
639 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))
641 if serverrole == "active directory domain controller":
643 # This will, for better or worse, default to 'WORKGROUP'
644 domain = lp.get("workgroup")
645 domain = domain.upper()
647 if lp.get("workgroup").upper() != domain:
648 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))
651 domaindn = samba.dn_from_dns_name(dnsdomain)
653 if domain == netbiosname:
654 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
658 domaindn = "DC=" + netbiosname
660 if not valid_netbios_name(domain):
661 raise InvalidNetbiosName(domain)
663 if hostname.upper() == realm:
664 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
665 if netbiosname.upper() == realm:
666 raise ProvisioningError("guess_names: Realm '%s' must not be equal to NetBIOS hostname '%s'!" % (realm, netbiosname))
667 if domain == realm and not domain_names_forced:
668 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
670 if serverrole != "active directory domain controller":
672 # This is the code path for a domain member
673 # where we provision the database as if we where
674 # on a domain controller, so we should not use
675 # the same dnsdomain as the domain controllers
676 # of our primary domain.
678 # This will be important if we start doing
679 # SID/name filtering and reject the local
680 # sid and names if they come from a domain
684 dnsdomain = netbiosname.lower()
690 configdn = "CN=Configuration," + rootdn
692 schemadn = "CN=Schema," + configdn
695 sitename = DEFAULTSITE
697 names = ProvisionNames()
698 names.rootdn = rootdn
699 names.domaindn = domaindn
700 names.configdn = configdn
701 names.schemadn = schemadn
702 names.ldapmanagerdn = "CN=Manager," + rootdn
703 names.dnsdomain = dnsdomain
704 names.domain = domain
706 names.netbiosname = netbiosname
707 names.hostname = hostname
708 names.sitename = sitename
709 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
710 netbiosname, sitename, configdn)
715 def make_smbconf(smbconf, hostname, domain, realm, targetdir,
716 serverrole=None, eadb=False, use_ntvfs=False, lp=None,
718 """Create a new smb.conf file based on a couple of basic settings.
720 assert smbconf is not 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()
737 "netbios name": netbiosname,
740 "server role": serverrole,
744 lp = samba.param.LoadParm()
745 # Load non-existent file
746 if os.path.exists(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"])
767 if use_ntvfs and not lp.get("posix:eadb"):
768 if targetdir is not None:
769 privdir = os.path.join(targetdir, "private")
771 privdir = lp.get("private dir")
772 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
773 elif not use_ntvfs and not lp.get("xattr_tdb:file"):
774 if targetdir is not None:
775 statedir = os.path.join(targetdir, "state")
777 statedir = lp.get("state directory")
778 lp.set("xattr_tdb:file", os.path.abspath(os.path.join(statedir, "xattr.tdb")))
781 if serverrole == "active directory domain controller":
782 shares["sysvol"] = os.path.join(lp.get("state directory"), "sysvol")
783 shares["netlogon"] = os.path.join(shares["sysvol"], realm.lower(),
786 global_settings["passdb backend"] = "samba_dsdb"
788 f = open(smbconf, 'w')
790 f.write("[globals]\n")
791 for key, val in global_settings.items():
792 f.write("\t%s = %s\n" % (key, val))
795 for name, path in shares.items():
796 f.write("[%s]\n" % name)
797 f.write("\tpath = %s\n" % path)
798 f.write("\tread only = no\n")
802 # reload the smb.conf
805 # and dump it without any values that are the default
806 # this ensures that any smb.conf parameters that were set
807 # on the provision/join command line are set in the resulting smb.conf
808 lp.dump(False, smbconf)
811 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
812 users_gid, root_gid):
813 """setup reasonable name mappings for sam names to unix names.
815 :param samdb: SamDB object.
816 :param idmap: IDmap db object.
817 :param sid: The domain sid.
818 :param domaindn: The domain DN.
819 :param root_uid: uid of the UNIX root user.
820 :param nobody_uid: uid of the UNIX nobody user.
821 :param users_gid: gid of the UNIX users group.
822 :param root_gid: gid of the UNIX root group.
824 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
826 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
827 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
830 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
831 provision_backend, names, serverrole,
832 erase=False, plaintext_secrets=False,
834 """Setup the partitions for the SAM database.
836 Alternatively, provision() may call this, and then populate the database.
838 :note: This will wipe the Sam Database!
840 :note: This function always removes the local SAM LDB file. The erase
841 parameter controls whether to erase the existing data, which
842 may not be stored locally but in LDAP.
845 assert session_info is not None
847 # We use options=["modules:"] to stop the modules loading - we
848 # just want to wipe and re-initialise the database, not start it up
851 os.unlink(samdb_path)
855 samdb = Ldb(url=samdb_path, session_info=session_info,
856 lp=lp, options=["modules:"])
858 ldap_backend_line = "# No LDAP backend"
859 if provision_backend.type != "ldb":
860 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
862 required_features = None
863 if not plaintext_secrets:
864 required_features = "requiredFeatures: encryptedSecrets"
866 if backend_store is None:
867 backend_store = get_default_backend_store()
868 backend_store_line = "backendStore: %s" % backend_store
870 if backend_store == "mdb":
871 if required_features is not None:
872 required_features += "\n"
874 required_features = ""
875 required_features += "requiredFeatures: lmdbLevelOne"
877 if required_features is None:
878 required_features = "# No required features"
880 samdb.transaction_start()
882 logger.info("Setting up sam.ldb partitions and settings")
883 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
884 "LDAP_BACKEND_LINE": ldap_backend_line,
885 "BACKEND_STORE": backend_store_line
888 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
889 "BACKEND_TYPE": provision_backend.type,
890 "SERVER_ROLE": serverrole,
891 "REQUIRED_FEATURES": required_features
894 logger.info("Setting up sam.ldb rootDSE")
895 setup_samdb_rootdse(samdb, names)
897 samdb.transaction_cancel()
900 samdb.transaction_commit()
903 def secretsdb_self_join(secretsdb, domain,
904 netbiosname, machinepass, domainsid=None,
905 realm=None, dnsdomain=None,
907 key_version_number=1,
908 secure_channel_type=SEC_CHAN_WKSTA):
909 """Add domain join-specific bits to a secrets database.
911 :param secretsdb: Ldb Handle to the secrets database
912 :param machinepass: Machine password
914 attrs = ["whenChanged",
921 if realm is not None:
922 if dnsdomain is None:
923 dnsdomain = realm.lower()
924 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
927 shortname = netbiosname.lower()
929 # We don't need to set msg["flatname"] here, because rdn_name will handle
930 # it, and it causes problems for modifies anyway
931 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
932 msg["secureChannelType"] = [str(secure_channel_type)]
933 msg["objectClass"] = ["top", "primaryDomain"]
934 if dnsname is not None:
935 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
936 msg["realm"] = [realm]
937 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
938 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
939 msg["privateKeytab"] = ["secrets.keytab"]
941 msg["secret"] = [machinepass.encode('utf-8')]
942 msg["samAccountName"] = ["%s$" % netbiosname]
943 msg["secureChannelType"] = [str(secure_channel_type)]
944 if domainsid is not None:
945 msg["objectSid"] = [ndr_pack(domainsid)]
947 # This complex expression tries to ensure that we don't have more
948 # than one record for this SID, realm or netbios domain at a time,
949 # but we don't delete the old record that we are about to modify,
950 # because that would delete the keytab and previous password.
951 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
952 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
953 scope=ldb.SCOPE_ONELEVEL)
956 secretsdb.delete(del_msg.dn)
958 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
961 msg["priorSecret"] = [res[0]["secret"][0]]
963 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
968 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
973 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
979 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
980 secretsdb.modify(msg)
981 secretsdb.rename(res[0].dn, msg.dn)
983 spn = ['HOST/%s' % shortname]
984 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
985 # we are a domain controller then we add servicePrincipalName
986 # entries for the keytab code to update.
987 spn.extend(['HOST/%s' % dnsname])
988 msg["servicePrincipalName"] = spn
993 def setup_secretsdb(paths, session_info, backend_credentials, lp):
994 """Setup the secrets database.
996 :note: This function does not handle exceptions and transaction on purpose,
997 it's up to the caller to do this job.
999 :param path: Path to the secrets database.
1000 :param session_info: Session info.
1001 :param credentials: Credentials
1002 :param lp: Loadparm context
1003 :return: LDB handle for the created secrets database
1005 if os.path.exists(paths.secrets):
1006 os.unlink(paths.secrets)
1008 keytab_path = os.path.join(paths.private_dir, paths.keytab)
1009 if os.path.exists(keytab_path):
1010 os.unlink(keytab_path)
1012 bind_dns_keytab_path = os.path.join(paths.binddns_dir, paths.dns_keytab)
1013 if os.path.exists(bind_dns_keytab_path):
1014 os.unlink(bind_dns_keytab_path)
1016 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1017 if os.path.exists(dns_keytab_path):
1018 os.unlink(dns_keytab_path)
1020 path = paths.secrets
1022 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
1024 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
1025 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
1026 secrets_ldb.transaction_start()
1028 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
1030 if (backend_credentials is not None and
1031 backend_credentials.authentication_requested()):
1032 if backend_credentials.get_bind_dn() is not None:
1033 setup_add_ldif(secrets_ldb,
1034 setup_path("secrets_simple_ldap.ldif"), {
1035 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
1036 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password()).decode('utf8')
1039 setup_add_ldif(secrets_ldb,
1040 setup_path("secrets_sasl_ldap.ldif"), {
1041 "LDAPADMINUSER": backend_credentials.get_username(),
1042 "LDAPADMINREALM": backend_credentials.get_realm(),
1043 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password()).decode('utf8')
1046 secrets_ldb.transaction_cancel()
1051 def setup_privileges(path, session_info, lp):
1052 """Setup the privileges database.
1054 :param path: Path to the privileges database.
1055 :param session_info: Session info.
1056 :param credentials: Credentials
1057 :param lp: Loadparm context
1058 :return: LDB handle for the created secrets database
1060 if os.path.exists(path):
1062 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
1063 privilege_ldb.erase()
1064 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
1067 def setup_encrypted_secrets_key(path):
1068 """Setup the encrypted secrets key file.
1070 Any existing key file will be deleted and a new random key generated.
1072 :param path: Path to the secrets key file.
1075 if os.path.exists(path):
1078 flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
1079 mode = stat.S_IRUSR | stat.S_IWUSR
1081 umask_original = os.umask(0)
1083 fd = os.open(path, flags, mode)
1085 os.umask(umask_original)
1087 with os.fdopen(fd, 'wb') as f:
1088 key = samba.generate_random_bytes(16)
1092 def setup_registry(path, session_info, lp):
1093 """Setup the registry.
1095 :param path: Path to the registry database
1096 :param session_info: Session information
1097 :param credentials: Credentials
1098 :param lp: Loadparm context
1100 reg = samba.registry.Registry()
1101 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
1102 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
1103 provision_reg = setup_path("provision.reg")
1104 assert os.path.exists(provision_reg)
1105 reg.diff_apply(provision_reg)
1108 def setup_idmapdb(path, session_info, lp):
1109 """Setup the idmap database.
1111 :param path: path to the idmap database
1112 :param session_info: Session information
1113 :param credentials: Credentials
1114 :param lp: Loadparm context
1116 if os.path.exists(path):
1119 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
1121 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
1125 def setup_samdb_rootdse(samdb, names):
1126 """Setup the SamDB rootdse.
1128 :param samdb: Sam Database handle
1130 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
1131 "SCHEMADN": names.schemadn,
1132 "DOMAINDN": names.domaindn,
1133 "ROOTDN": names.rootdn,
1134 "CONFIGDN": names.configdn,
1135 "SERVERDN": names.serverdn,
1139 def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
1140 dns_backend, dnspass, domainsid, next_rid, invocationid,
1141 policyguid, policyguid_dc,
1142 domainControllerFunctionality, ntdsguid=None, dc_rid=None):
1143 """Join a host to its own domain."""
1144 assert isinstance(invocationid, str)
1145 if ntdsguid is not None:
1146 ntdsguid_line = "objectGUID: %s\n" % ntdsguid
1153 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
1154 "CONFIGDN": names.configdn,
1155 "SCHEMADN": names.schemadn,
1156 "DOMAINDN": names.domaindn,
1157 "SERVERDN": names.serverdn,
1158 "INVOCATIONID": invocationid,
1159 "NETBIOSNAME": names.netbiosname,
1160 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1161 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')).decode('utf8'),
1162 "DOMAINSID": str(domainsid),
1163 "DCRID": str(dc_rid),
1164 "SAMBA_VERSION_STRING": version,
1165 "NTDSGUID": ntdsguid_line,
1166 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1167 domainControllerFunctionality),
1168 "RIDALLOCATIONSTART": str(next_rid + 100),
1169 "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
1171 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1172 "POLICYGUID": policyguid,
1173 "POLICYGUID_DC": policyguid_dc,
1174 "DNSDOMAIN": names.dnsdomain,
1175 "DOMAINDN": names.domaindn})
1177 # If we are setting up a subdomain, then this has been replicated in, so we
1178 # don't need to add it
1179 if fill == FILL_FULL:
1180 setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
1181 "CONFIGDN": names.configdn,
1182 "SCHEMADN": names.schemadn,
1183 "DOMAINDN": names.domaindn,
1184 "SERVERDN": names.serverdn,
1185 "INVOCATIONID": invocationid,
1186 "NETBIOSNAME": names.netbiosname,
1187 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1188 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')).decode('utf8'),
1189 "DOMAINSID": str(domainsid),
1190 "DCRID": str(dc_rid),
1191 "SAMBA_VERSION_STRING": version,
1192 "NTDSGUID": ntdsguid_line,
1193 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1194 domainControllerFunctionality)})
1196 # Setup fSMORoleOwner entries to point at the newly created DC entry
1197 setup_modify_ldif(samdb,
1198 setup_path("provision_self_join_modify_config.ldif"), {
1199 "CONFIGDN": names.configdn,
1200 "SCHEMADN": names.schemadn,
1201 "DEFAULTSITE": names.sitename,
1202 "NETBIOSNAME": names.netbiosname,
1203 "SERVERDN": names.serverdn,
1206 system_session_info = system_session()
1207 samdb.set_session_info(system_session_info)
1208 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1209 # modify a serverReference under cn=config when we are a subdomain, we must
1210 # be system due to ACLs
1211 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1212 "DOMAINDN": names.domaindn,
1213 "SERVERDN": names.serverdn,
1214 "NETBIOSNAME": names.netbiosname,
1217 samdb.set_session_info(admin_session_info)
1219 if dns_backend != "SAMBA_INTERNAL":
1220 # This is Samba4 specific and should be replaced by the correct
1221 # DNS AD-style setup
1222 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1223 "DNSDOMAIN": names.dnsdomain,
1224 "DOMAINDN": names.domaindn,
1225 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')).decode('utf8'),
1226 "HOSTNAME": names.hostname,
1227 "DNSNAME": '%s.%s' % (
1228 names.netbiosname.lower(), names.dnsdomain.lower())
1232 def getpolicypath(sysvolpath, dnsdomain, guid):
1233 """Return the physical path of policy given its guid.
1235 :param sysvolpath: Path to the sysvol folder
1236 :param dnsdomain: DNS name of the AD domain
1237 :param guid: The GUID of the policy
1238 :return: A string with the complete path to the policy folder
1241 guid = "{%s}" % guid
1242 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1246 def create_gpo_struct(policy_path):
1247 if not os.path.exists(policy_path):
1248 os.makedirs(policy_path, 0o775)
1249 f = open(os.path.join(policy_path, "GPT.INI"), 'w')
1251 f.write("[General]\r\nVersion=0")
1254 p = os.path.join(policy_path, "MACHINE")
1255 if not os.path.exists(p):
1256 os.makedirs(p, 0o775)
1257 p = os.path.join(policy_path, "USER")
1258 if not os.path.exists(p):
1259 os.makedirs(p, 0o775)
1262 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1263 """Create the default GPO for a domain
1265 :param sysvolpath: Physical path for the sysvol folder
1266 :param dnsdomain: DNS domain name of the AD domain
1267 :param policyguid: GUID of the default domain policy
1268 :param policyguid_dc: GUID of the default domain controler policy
1270 policy_path = getpolicypath(sysvolpath, dnsdomain, policyguid)
1271 create_gpo_struct(policy_path)
1273 policy_path = getpolicypath(sysvolpath, dnsdomain, policyguid_dc)
1274 create_gpo_struct(policy_path)
1277 def setup_samdb(path, session_info, provision_backend, lp, names,
1278 logger, fill, serverrole, schema, am_rodc=False,
1279 plaintext_secrets=False, backend_store=None):
1280 """Setup a complete SAM Database.
1282 :note: This will wipe the main SAM database file!
1285 # Also wipes the database
1286 setup_samdb_partitions(path, logger=logger, lp=lp,
1287 provision_backend=provision_backend, session_info=session_info,
1288 names=names, serverrole=serverrole, plaintext_secrets=plaintext_secrets,
1289 backend_store=backend_store)
1291 # Load the database, but don's load the global schema and don't connect
1293 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1294 credentials=provision_backend.credentials, lp=lp,
1295 global_schema=False, am_rodc=am_rodc)
1297 logger.info("Pre-loading the Samba 4 and AD schema")
1299 # Load the schema from the one we computed earlier
1300 samdb.set_schema(schema, write_indices_and_attributes=False)
1302 # Set the NTDS settings DN manually - in order to have it already around
1303 # before the provisioned tree exists and we connect
1304 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1306 # And now we can connect to the DB - the schema won't be loaded from the
1310 except ldb.LdbError as e2:
1311 (num, string_error) = e2.args
1312 if (num == ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS):
1313 raise ProvisioningError("Permission denied connecting to %s, are you running as root?" % path)
1317 # But we have to give it one more kick to have it use the schema
1318 # during provision - it needs, now that it is connected, to write
1319 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1320 samdb.set_schema(schema, write_indices_and_attributes=True)
1325 def fill_samdb(samdb, lp, names, logger, policyguid,
1326 policyguid_dc, fill, adminpass, krbtgtpass, machinepass, dns_backend,
1327 dnspass, invocationid, ntdsguid, serverrole, am_rodc=False,
1328 dom_for_fun_level=None, schema=None, next_rid=None, dc_rid=None,
1329 backend_store=None):
1331 if next_rid is None:
1334 # Provision does not make much sense values larger than 1000000000
1335 # as the upper range of the rIDAvailablePool is 1073741823 and
1336 # we don't want to create a domain that cannot allocate rids.
1337 if next_rid < 1000 or next_rid > 1000000000:
1338 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1339 error += "the valid range is %u-%u. The default is %u." % (
1340 1000, 1000000000, 1000)
1341 raise ProvisioningError(error)
1343 # ATTENTION: Do NOT change these default values without discussion with the
1344 # team and/or release manager. They have a big impact on the whole program!
1345 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1347 if dom_for_fun_level is None:
1348 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
1350 if dom_for_fun_level > domainControllerFunctionality:
1351 raise ProvisioningError("You want to run SAMBA 4 on a domain and forest function level which itself is higher than its actual DC function level (2008_R2). This won't work!")
1353 domainFunctionality = dom_for_fun_level
1354 forestFunctionality = dom_for_fun_level
1356 # Set the NTDS settings DN manually - in order to have it already around
1357 # before the provisioned tree exists and we connect
1358 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1360 # Set the domain functionality levels onto the database.
1361 # Various module (the password_hash module in particular) need
1362 # to know what level of AD we are emulating.
1364 # These will be fixed into the database via the database
1365 # modifictions below, but we need them set from the start.
1366 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1367 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1368 samdb.set_opaque_integer("domainControllerFunctionality",
1369 domainControllerFunctionality)
1371 samdb.set_domain_sid(str(names.domainsid))
1372 samdb.set_invocation_id(invocationid)
1374 logger.info("Adding DomainDN: %s" % names.domaindn)
1376 # impersonate domain admin
1377 admin_session_info = admin_session(lp, str(names.domainsid))
1378 samdb.set_session_info(admin_session_info)
1379 if names.domainguid is not None:
1380 domainguid_line = "objectGUID: %s\n-" % names.domainguid
1382 domainguid_line = ""
1384 descr = b64encode(get_domain_descriptor(names.domainsid)).decode('utf8')
1385 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1386 "DOMAINDN": names.domaindn,
1387 "DOMAINSID": str(names.domainsid),
1388 "DESCRIPTOR": descr,
1389 "DOMAINGUID": domainguid_line
1392 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1393 "DOMAINDN": names.domaindn,
1394 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1395 "NEXTRID": str(next_rid),
1396 "DEFAULTSITE": names.sitename,
1397 "CONFIGDN": names.configdn,
1398 "POLICYGUID": policyguid,
1399 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1400 "SAMBA_VERSION_STRING": version,
1401 "MIN_PWD_LENGTH": str(DEFAULT_MIN_PWD_LENGTH)
1404 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1405 if fill == FILL_FULL:
1406 logger.info("Adding configuration container")
1407 descr = b64encode(get_config_descriptor(names.domainsid)).decode('utf8')
1408 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1409 "CONFIGDN": names.configdn,
1410 "DESCRIPTOR": descr,
1413 # The LDIF here was created when the Schema object was constructed
1414 ignore_checks_oid = "local_oid:%s:0" % samba.dsdb.DSDB_CONTROL_SKIP_DUPLICATES_CHECK_OID
1415 logger.info("Setting up sam.ldb schema")
1416 samdb.add_ldif(schema.schema_dn_add,
1417 controls=["relax:0", ignore_checks_oid])
1418 samdb.modify_ldif(schema.schema_dn_modify,
1419 controls=[ignore_checks_oid])
1420 samdb.write_prefixes_from_schema()
1421 samdb.add_ldif(schema.schema_data, controls=["relax:0", ignore_checks_oid])
1422 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1423 {"SCHEMADN": names.schemadn},
1424 controls=["relax:0", ignore_checks_oid])
1426 # Now register this container in the root of the forest
1427 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1428 msg["subRefs"] = ldb.MessageElement(names.configdn, ldb.FLAG_MOD_ADD,
1431 samdb.invocation_id = invocationid
1433 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1434 if fill == FILL_FULL:
1435 logger.info("Setting up sam.ldb configuration data")
1437 partitions_descr = b64encode(get_config_partitions_descriptor(names.domainsid)).decode('utf8')
1438 sites_descr = b64encode(get_config_sites_descriptor(names.domainsid)).decode('utf8')
1439 ntdsquotas_descr = b64encode(get_config_ntds_quotas_descriptor(names.domainsid)).decode('utf8')
1440 protected1_descr = b64encode(get_config_delete_protected1_descriptor(names.domainsid)).decode('utf8')
1441 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid)).decode('utf8')
1442 protected2_descr = b64encode(get_config_delete_protected2_descriptor(names.domainsid)).decode('utf8')
1444 if "2008" in schema.base_schema:
1445 # exclude 2012-specific changes if we're using a 2008 schema
1450 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1451 "CONFIGDN": names.configdn,
1452 "NETBIOSNAME": names.netbiosname,
1453 "DEFAULTSITE": names.sitename,
1454 "DNSDOMAIN": names.dnsdomain,
1455 "DOMAIN": names.domain,
1456 "SCHEMADN": names.schemadn,
1457 "DOMAINDN": names.domaindn,
1458 "SERVERDN": names.serverdn,
1459 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1460 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1461 "NTDSQUOTAS_DESCRIPTOR": ntdsquotas_descr,
1462 "LOSTANDFOUND_DESCRIPTOR": protected1wd_descr,
1463 "SERVICES_DESCRIPTOR": protected1_descr,
1464 "PHYSICALLOCATIONS_DESCRIPTOR": protected1wd_descr,
1465 "FORESTUPDATES_DESCRIPTOR": protected1wd_descr,
1466 "EXTENDEDRIGHTS_DESCRIPTOR": protected2_descr,
1467 "PARTITIONS_DESCRIPTOR": partitions_descr,
1468 "SITES_DESCRIPTOR": sites_descr,
1471 setup_add_ldif(samdb, setup_path("extended-rights.ldif"), {
1472 "CONFIGDN": names.configdn,
1473 "INC2012": incl_2012,
1476 logger.info("Setting up display specifiers")
1477 display_specifiers_ldif = read_ms_ldif(
1478 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1479 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1480 {"CONFIGDN": names.configdn})
1481 check_all_substituted(display_specifiers_ldif)
1482 samdb.add_ldif(display_specifiers_ldif)
1484 logger.info("Modifying display specifiers and extended rights")
1485 setup_modify_ldif(samdb,
1486 setup_path("provision_configuration_modify.ldif"), {
1487 "CONFIGDN": names.configdn,
1488 "DISPLAYSPECIFIERS_DESCRIPTOR": protected2_descr
1491 logger.info("Adding users container")
1492 users_desc = b64encode(get_domain_users_descriptor(names.domainsid)).decode('utf8')
1493 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1494 "DOMAINDN": names.domaindn,
1495 "USERS_DESCRIPTOR": users_desc
1497 logger.info("Modifying users container")
1498 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1499 "DOMAINDN": names.domaindn})
1500 logger.info("Adding computers container")
1501 computers_desc = b64encode(get_domain_computers_descriptor(names.domainsid)).decode('utf8')
1502 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1503 "DOMAINDN": names.domaindn,
1504 "COMPUTERS_DESCRIPTOR": computers_desc
1506 logger.info("Modifying computers container")
1507 setup_modify_ldif(samdb,
1508 setup_path("provision_computers_modify.ldif"), {
1509 "DOMAINDN": names.domaindn})
1510 logger.info("Setting up sam.ldb data")
1511 infrastructure_desc = b64encode(get_domain_infrastructure_descriptor(names.domainsid)).decode('utf8')
1512 lostandfound_desc = b64encode(get_domain_delete_protected2_descriptor(names.domainsid)).decode('utf8')
1513 system_desc = b64encode(get_domain_delete_protected1_descriptor(names.domainsid)).decode('utf8')
1514 builtin_desc = b64encode(get_domain_builtin_descriptor(names.domainsid)).decode('utf8')
1515 controllers_desc = b64encode(get_domain_controllers_descriptor(names.domainsid)).decode('utf8')
1516 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1517 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1518 "DOMAINDN": names.domaindn,
1519 "NETBIOSNAME": names.netbiosname,
1520 "DEFAULTSITE": names.sitename,
1521 "CONFIGDN": names.configdn,
1522 "SERVERDN": names.serverdn,
1523 "RIDAVAILABLESTART": str(next_rid + 600),
1524 "POLICYGUID_DC": policyguid_dc,
1525 "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc,
1526 "LOSTANDFOUND_DESCRIPTOR": lostandfound_desc,
1527 "SYSTEM_DESCRIPTOR": system_desc,
1528 "BUILTIN_DESCRIPTOR": builtin_desc,
1529 "DOMAIN_CONTROLLERS_DESCRIPTOR": controllers_desc,
1532 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1533 if fill == FILL_FULL:
1534 managedservice_descr = b64encode(get_managed_service_accounts_descriptor(names.domainsid)).decode('utf8')
1535 setup_modify_ldif(samdb,
1536 setup_path("provision_configuration_references.ldif"), {
1537 "CONFIGDN": names.configdn,
1538 "SCHEMADN": names.schemadn})
1540 logger.info("Setting up well known security principals")
1541 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid)).decode('utf8')
1542 setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1543 "CONFIGDN": names.configdn,
1544 "WELLKNOWNPRINCIPALS_DESCRIPTOR": protected1wd_descr,
1545 }, controls=["relax:0", "provision:0"])
1547 if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1548 setup_modify_ldif(samdb,
1549 setup_path("provision_basedn_references.ldif"), {
1550 "DOMAINDN": names.domaindn,
1551 "MANAGEDSERVICE_DESCRIPTOR": managedservice_descr
1554 logger.info("Setting up sam.ldb users and groups")
1555 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1556 "DOMAINDN": names.domaindn,
1557 "DOMAINSID": str(names.domainsid),
1558 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')).decode('utf8'),
1559 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le')).decode('utf8')
1560 }, controls=["relax:0", "provision:0"])
1562 logger.info("Setting up self join")
1563 setup_self_join(samdb, admin_session_info, names=names, fill=fill,
1564 invocationid=invocationid,
1565 dns_backend=dns_backend,
1567 machinepass=machinepass,
1568 domainsid=names.domainsid,
1571 policyguid=policyguid,
1572 policyguid_dc=policyguid_dc,
1573 domainControllerFunctionality=domainControllerFunctionality,
1576 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1577 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1578 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE).decode('utf8')
1579 assert isinstance(names.ntdsguid, string_types)
1584 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1585 POLICIES_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)(A;OICI;0x001301bf;;;PA)"
1586 SYSVOL_SERVICE = "sysvol"
1589 def set_dir_acl(path, acl, lp, domsid, use_ntvfs, passdb, service=SYSVOL_SERVICE):
1590 setntacl(lp, path, acl, domsid, use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1591 for root, dirs, files in os.walk(path, topdown=False):
1593 setntacl(lp, os.path.join(root, name), acl, domsid,
1594 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1596 setntacl(lp, os.path.join(root, name), acl, domsid,
1597 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1600 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb):
1601 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1604 :param sysvol: Physical path for the sysvol folder
1605 :param dnsdomain: The DNS name of the domain
1606 :param domainsid: The SID of the domain
1607 :param domaindn: The DN of the domain (ie. DC=...)
1608 :param samdb: An LDB object on the SAM db
1609 :param lp: an LP object
1612 # Set ACL for GPO root folder
1613 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1614 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid),
1615 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=SYSVOL_SERVICE)
1617 res = samdb.search(base="CN=Policies,CN=System,%s" %(domaindn),
1618 attrs=["cn", "nTSecurityDescriptor"],
1619 expression="", scope=ldb.SCOPE_ONELEVEL)
1622 acl = ndr_unpack(security.descriptor,
1623 policy["nTSecurityDescriptor"][0]).as_sddl()
1624 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1625 set_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1626 str(domainsid), use_ntvfs,
1630 def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain,
1631 domaindn, lp, use_ntvfs):
1632 """Set the ACL for the sysvol share and the subfolders
1634 :param samdb: An LDB object on the SAM db
1635 :param netlogon: Physical path for the netlogon folder
1636 :param sysvol: Physical path for the sysvol folder
1637 :param uid: The UID of the "Administrator" user
1638 :param gid: The GID of the "Domain adminstrators" group
1639 :param domainsid: The SID of the domain
1640 :param dnsdomain: The DNS name of the domain
1641 :param domaindn: The DN of the domain (ie. DC=...)
1646 s3conf = s3param.get_context()
1647 s3conf.load(lp.configfile)
1649 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(sysvol))
1652 smbd.set_simple_acl(file.name, 0o755, gid)
1654 if not smbd.have_posix_acls():
1655 # This clue is only strictly correct for RPM and
1656 # Debian-like Linux systems, but hopefully other users
1657 # will get enough clue from it.
1658 raise ProvisioningError("Samba was compiled without the posix ACL support that s3fs requires. "
1659 "Try installing libacl1-dev or libacl-devel, then re-run configure and make.")
1661 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. "
1662 "Try the mounting the filesystem with the 'acl' option.")
1664 smbd.chown(file.name, uid, gid)
1666 raise ProvisioningError("Unable to chown a file on your filesystem. "
1667 "You may not be running provision as root.")
1671 # This will ensure that the smbd code we are running when setting ACLs
1672 # is initialised with the smb.conf
1673 s3conf = s3param.get_context()
1674 s3conf.load(lp.configfile)
1675 # ensure we are using the right samba_dsdb passdb backend, no matter what
1676 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1677 passdb.reload_static_pdb()
1679 # ensure that we init the samba_dsdb backend, so the domain sid is
1680 # marked in secrets.tdb
1681 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1683 # now ensure everything matches correctly, to avoid wierd issues
1684 if passdb.get_global_sam_sid() != domainsid:
1685 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))
1687 domain_info = s4_passdb.domain_info()
1688 if domain_info["dom_sid"] != domainsid:
1689 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))
1691 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1692 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()))
1696 os.chown(sysvol, -1, gid)
1702 # use admin sid dn as user dn, since admin should own most of the files,
1703 # the operation will be much faster
1704 userdn = '<SID={}-{}>'.format(domainsid, security.DOMAIN_RID_ADMINISTRATOR)
1706 flags = (auth.AUTH_SESSION_INFO_DEFAULT_GROUPS |
1707 auth.AUTH_SESSION_INFO_AUTHENTICATED |
1708 auth.AUTH_SESSION_INFO_SIMPLE_PRIVILEGES)
1710 session_info = auth.user_session(samdb, lp_ctx=lp, dn=userdn,
1711 session_info_flags=flags)
1713 def _setntacl(path):
1714 """A helper to reuse args"""
1716 lp, path, SYSVOL_ACL, str(domainsid),
1717 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=s4_passdb,
1718 service=SYSVOL_SERVICE, session_info=session_info)
1720 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1722 for root, dirs, files in os.walk(sysvol, topdown=False):
1724 if use_ntvfs and canchown:
1725 os.chown(os.path.join(root, name), -1, gid)
1726 _setntacl(os.path.join(root, name))
1728 if use_ntvfs and canchown:
1729 os.chown(os.path.join(root, name), -1, gid)
1730 _setntacl(os.path.join(root, name))
1732 # Set acls on Policy folder and policies folders
1733 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb=s4_passdb)
1736 def acl_type(direct_db_access):
1737 if direct_db_access:
1743 def check_dir_acl(path, acl, lp, domainsid, direct_db_access):
1744 fsacl = getntacl(lp, path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1745 fsacl_sddl = fsacl.as_sddl(domainsid)
1746 if fsacl_sddl != acl:
1747 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))
1749 for root, dirs, files in os.walk(path, topdown=False):
1751 fsacl = getntacl(lp, os.path.join(root, name),
1752 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1754 raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1755 fsacl_sddl = fsacl.as_sddl(domainsid)
1756 if fsacl_sddl != acl:
1757 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))
1760 fsacl = getntacl(lp, os.path.join(root, name),
1761 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1763 raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1764 fsacl_sddl = fsacl.as_sddl(domainsid)
1765 if fsacl_sddl != acl:
1766 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))
1769 def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1771 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1774 :param sysvol: Physical path for the sysvol folder
1775 :param dnsdomain: The DNS name of the domain
1776 :param domainsid: The SID of the domain
1777 :param domaindn: The DN of the domain (ie. DC=...)
1778 :param samdb: An LDB object on the SAM db
1779 :param lp: an LP object
1782 # Set ACL for GPO root folder
1783 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1784 fsacl = getntacl(lp, root_policy_path,
1785 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1787 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access), root_policy_path))
1788 fsacl_sddl = fsacl.as_sddl(domainsid)
1789 if fsacl_sddl != POLICIES_ACL:
1790 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))
1791 res = samdb.search(base="CN=Policies,CN=System,%s" %(domaindn),
1792 attrs=["cn", "nTSecurityDescriptor"],
1793 expression="", scope=ldb.SCOPE_ONELEVEL)
1796 acl = ndr_unpack(security.descriptor,
1797 policy["nTSecurityDescriptor"][0]).as_sddl()
1798 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1799 check_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1800 domainsid, direct_db_access)
1803 def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn,
1805 """Set the ACL for the sysvol share and the subfolders
1807 :param samdb: An LDB object on the SAM db
1808 :param netlogon: Physical path for the netlogon folder
1809 :param sysvol: Physical path for the sysvol folder
1810 :param uid: The UID of the "Administrator" user
1811 :param gid: The GID of the "Domain adminstrators" group
1812 :param domainsid: The SID of the domain
1813 :param dnsdomain: The DNS name of the domain
1814 :param domaindn: The DN of the domain (ie. DC=...)
1817 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1818 s3conf = s3param.get_context()
1819 s3conf.load(lp.configfile)
1820 # ensure we are using the right samba_dsdb passdb backend, no matter what
1821 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1822 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1823 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1825 # now ensure everything matches correctly, to avoid wierd issues
1826 if passdb.get_global_sam_sid() != domainsid:
1827 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))
1829 domain_info = s4_passdb.domain_info()
1830 if domain_info["dom_sid"] != domainsid:
1831 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))
1833 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1834 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()))
1836 # Ensure we can read this directly, and via the smbd VFS
1837 for direct_db_access in [True, False]:
1838 # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1839 for dir_path in [os.path.join(sysvol, dnsdomain), netlogon]:
1840 fsacl = getntacl(lp, dir_path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1842 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access), dir_path))
1843 fsacl_sddl = fsacl.as_sddl(domainsid)
1844 if fsacl_sddl != SYSVOL_ACL:
1845 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))
1847 # Check acls on Policy folder and policies folders
1848 check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1852 def interface_ips_v4(lp):
1853 """return only IPv4 IPs"""
1854 ips = samba.interface_ips(lp, False)
1857 if i.find(':') == -1:
1862 def interface_ips_v6(lp):
1863 """return only IPv6 IPs"""
1864 ips = samba.interface_ips(lp, False)
1867 if i.find(':') != -1:
1872 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1874 targetdir=None, samdb_fill=FILL_FULL,
1875 hostip=None, hostip6=None,
1876 next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1877 domainguid=None, policyguid=None, policyguid_dc=None,
1878 invocationid=None, machinepass=None, ntdsguid=None,
1879 dns_backend=None, dnspass=None,
1880 serverrole=None, dom_for_fun_level=None,
1881 am_rodc=False, lp=None, use_ntvfs=False,
1882 skip_sysvolacl=False, backend_store=None):
1883 # create/adapt the group policy GUIDs
1884 # Default GUID for default policy are described at
1885 # "How Core Group Policy Works"
1886 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1887 if policyguid is None:
1888 policyguid = DEFAULT_POLICY_GUID
1889 policyguid = policyguid.upper()
1890 if policyguid_dc is None:
1891 policyguid_dc = DEFAULT_DC_POLICY_GUID
1892 policyguid_dc = policyguid_dc.upper()
1894 if invocationid is None:
1895 invocationid = str(uuid.uuid4())
1897 if krbtgtpass is None:
1898 krbtgtpass = samba.generate_random_machine_password(128, 255)
1899 if machinepass is None:
1900 machinepass = samba.generate_random_machine_password(128, 255)
1902 dnspass = samba.generate_random_password(128, 255)
1904 samdb.transaction_start()
1906 samdb = fill_samdb(samdb, lp, names, logger=logger,
1908 policyguid=policyguid, policyguid_dc=policyguid_dc,
1909 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1910 invocationid=invocationid, machinepass=machinepass,
1911 dns_backend=dns_backend, dnspass=dnspass,
1912 ntdsguid=ntdsguid, serverrole=serverrole,
1913 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1914 next_rid=next_rid, dc_rid=dc_rid,
1915 backend_store=backend_store)
1917 # Set up group policies (domain policy and domain controller
1919 if serverrole == "active directory domain controller":
1920 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1923 samdb.transaction_cancel()
1926 samdb.transaction_commit()
1928 if serverrole == "active directory domain controller":
1929 # Continue setting up sysvol for GPO. This appears to require being
1930 # outside a transaction.
1931 if not skip_sysvolacl:
1932 setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.root_uid,
1933 paths.root_gid, names.domainsid, names.dnsdomain,
1934 names.domaindn, lp, use_ntvfs)
1936 logger.info("Setting acl on sysvol skipped")
1938 secretsdb_self_join(secrets_ldb, domain=names.domain,
1939 realm=names.realm, dnsdomain=names.dnsdomain,
1940 netbiosname=names.netbiosname, domainsid=names.domainsid,
1941 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1943 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1944 # In future, this might be determined from some configuration
1945 kerberos_enctypes = str(ENC_ALL_TYPES)
1948 msg = ldb.Message(ldb.Dn(samdb,
1949 samdb.searchone("distinguishedName",
1950 expression="samAccountName=%s$" % names.netbiosname,
1951 scope=ldb.SCOPE_SUBTREE).decode('utf8')))
1952 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1953 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1954 name="msDS-SupportedEncryptionTypes")
1956 except ldb.LdbError as e:
1957 (enum, estr) = e.args
1958 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1959 # It might be that this attribute does not exist in this schema
1962 setup_ad_dns(samdb, secrets_ldb, names, paths, lp, logger,
1963 hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
1964 dnspass=dnspass, os_level=dom_for_fun_level,
1965 targetdir=targetdir, fill_level=samdb_fill,
1966 backend_store=backend_store)
1968 domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1969 attribute="objectGUID").decode('utf8')
1970 assert isinstance(domainguid, string_types)
1972 lastProvisionUSNs = get_last_provision_usn(samdb)
1973 maxUSN = get_max_usn(samdb, str(names.rootdn))
1974 if lastProvisionUSNs is not None:
1975 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1977 set_provision_usn(samdb, 0, maxUSN, invocationid)
1979 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1980 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1981 {'NTDSGUID': names.ntdsguid})
1983 # fix any dangling GUIDs from the provision
1984 logger.info("Fixing provision GUIDs")
1985 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
1987 samdb.transaction_start()
1989 # a small number of GUIDs are missing because of ordering issues in the
1991 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1992 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1993 scope=ldb.SCOPE_BASE,
1994 attrs=['defaultObjectCategory'])
1995 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1996 scope=ldb.SCOPE_ONELEVEL,
1997 attrs=['ipsecOwnersReference',
1998 'ipsecFilterReference',
1999 'ipsecISAKMPReference',
2000 'ipsecNegotiationPolicyReference',
2001 'ipsecNFAReference'])
2002 if chk.check_database(DN=names.schemadn, scope=ldb.SCOPE_SUBTREE,
2003 attrs=['attributeId', 'governsId']) != 0:
2004 raise ProvisioningError("Duplicate attributeId or governsId in schema. Must be fixed manually!!")
2006 samdb.transaction_cancel()
2009 samdb.transaction_commit()
2013 "ROLE_STANDALONE": "standalone server",
2014 "ROLE_DOMAIN_MEMBER": "member server",
2015 "ROLE_DOMAIN_BDC": "active directory domain controller",
2016 "ROLE_DOMAIN_PDC": "active directory domain controller",
2017 "dc": "active directory domain controller",
2018 "member": "member server",
2019 "domain controller": "active directory domain controller",
2020 "active directory domain controller": "active directory domain controller",
2021 "member server": "member server",
2022 "standalone": "standalone server",
2023 "standalone server": "standalone server",
2027 def sanitize_server_role(role):
2028 """Sanitize a server role name.
2030 :param role: Server role
2031 :raise ValueError: If the role can not be interpreted
2032 :return: Sanitized server role (one of "member server",
2033 "active directory domain controller", "standalone server")
2036 return _ROLES_MAP[role]
2038 raise ValueError(role)
2041 def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain,
2043 """Create AD entries for the fake ypserver.
2045 This is needed for being able to manipulate posix attrs via ADUC.
2047 samdb.transaction_start()
2049 logger.info("Setting up fake yp server settings")
2050 setup_add_ldif(samdb, setup_path("ypServ30.ldif"), {
2051 "DOMAINDN": domaindn,
2052 "NETBIOSNAME": netbiosname,
2053 "NISDOMAIN": nisdomain,
2056 samdb.transaction_cancel()
2059 samdb.transaction_commit()
2062 def directory_create_or_exists(path, mode=0o755):
2063 if not os.path.exists(path):
2065 os.mkdir(path, mode)
2066 except OSError as e:
2067 if e.errno in [errno.EEXIST]:
2070 raise ProvisioningError("Failed to create directory %s: %s" % (path, e.strerror))
2073 def determine_host_ip(logger, lp, hostip=None):
2075 logger.info("Looking up IPv4 addresses")
2076 hostips = interface_ips_v4(lp)
2077 if len(hostips) > 0:
2079 if len(hostips) > 1:
2080 logger.warning("More than one IPv4 address found. Using %s",
2082 if hostip == "127.0.0.1":
2085 logger.warning("No IPv4 address will be assigned")
2090 def determine_host_ip6(logger, lp, hostip6=None):
2092 logger.info("Looking up IPv6 addresses")
2093 hostips = interface_ips_v6(lp)
2095 hostip6 = hostips[0]
2096 if len(hostips) > 1:
2097 logger.warning("More than one IPv6 address found. Using %s", hostip6)
2099 logger.warning("No IPv6 address will be assigned")
2104 def provision(logger, session_info, smbconf=None,
2105 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
2106 domaindn=None, schemadn=None, configdn=None, serverdn=None,
2107 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
2108 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None,
2109 krbtgtpass=None, domainguid=None, policyguid=None, policyguid_dc=None,
2110 dns_backend=None, dns_forwarder=None, dnspass=None,
2111 invocationid=None, machinepass=None, ntdsguid=None,
2112 root=None, nobody=None, users=None, backup=None, aci=None,
2113 serverrole=None, dom_for_fun_level=None, backend_type=None,
2114 sitename=None, ol_mmr_urls=None, ol_olc=None, slapd_path=None,
2115 useeadb=False, am_rodc=False, lp=None, use_ntvfs=False,
2116 use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True,
2117 ldap_backend_forced_uri=None, nosync=False, ldap_dryrun_mode=False,
2118 ldap_backend_extra_port=None, base_schema=None,
2119 plaintext_secrets=False, backend_store=None):
2122 :note: caution, this wipes all existing data!
2126 serverrole = sanitize_server_role(serverrole)
2128 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole)
2130 if ldapadminpass is None:
2131 # Make a new, random password between Samba and it's LDAP server
2132 ldapadminpass = samba.generate_random_password(128, 255)
2134 if backend_type is None:
2135 backend_type = "ldb"
2136 if backend_store is None:
2137 backend_store = get_default_backend_store()
2139 if domainsid is None:
2140 domainsid = security.random_sid()
2142 root_uid = findnss_uid([root or "root"])
2143 nobody_uid = findnss_uid([nobody or "nobody"])
2144 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
2145 root_gid = pwd.getpwuid(root_uid).pw_gid
2148 bind_gid = findnss_gid(["bind", "named"])
2152 if targetdir is not None:
2153 smbconf = os.path.join(targetdir, "etc", "smb.conf")
2154 elif smbconf is None:
2155 smbconf = samba.param.default_path()
2156 if not os.path.exists(os.path.dirname(smbconf)):
2157 os.makedirs(os.path.dirname(smbconf))
2159 server_services = []
2162 global_param["idmap_ldb:use rfc2307"] = ["yes"]
2164 if dns_backend != "SAMBA_INTERNAL":
2165 server_services.append("-dns")
2167 if dns_forwarder is not None:
2168 global_param["dns forwarder"] = [dns_forwarder]
2171 server_services.append("+smb")
2172 server_services.append("-s3fs")
2173 global_param["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
2175 if len(server_services) > 0:
2176 global_param["server services"] = server_services
2178 # only install a new smb.conf if there isn't one there already
2179 if os.path.exists(smbconf):
2180 # if Samba Team members can't figure out the weird errors
2181 # loading an empty smb.conf gives, then we need to be smarter.
2182 # Pretend it just didn't exist --abartlet
2183 f = open(smbconf, 'r')
2185 data = f.read().lstrip()
2188 if data is None or data == "":
2189 make_smbconf(smbconf, hostname, domain, realm,
2190 targetdir, serverrole=serverrole,
2191 eadb=useeadb, use_ntvfs=use_ntvfs,
2192 lp=lp, global_param=global_param)
2194 make_smbconf(smbconf, hostname, domain, realm, targetdir,
2195 serverrole=serverrole,
2196 eadb=useeadb, use_ntvfs=use_ntvfs, lp=lp, global_param=global_param)
2199 lp = samba.param.LoadParm()
2201 names = guess_names(lp=lp, hostname=hostname, domain=domain,
2202 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
2203 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
2204 sitename=sitename, rootdn=rootdn, domain_names_forced=(samdb_fill == FILL_DRS))
2205 paths = provision_paths_from_lp(lp, names.dnsdomain)
2207 paths.bind_gid = bind_gid
2208 paths.root_uid = root_uid
2209 paths.root_gid = root_gid
2211 hostip = determine_host_ip(logger, lp, hostip)
2212 hostip6 = determine_host_ip6(logger, lp, hostip6)
2213 names.hostip = hostip
2214 names.hostip6 = hostip6
2215 names.domainguid = domainguid
2216 names.domainsid = domainsid
2217 names.forestsid = domainsid
2219 if serverrole is None:
2220 serverrole = lp.get("server role")
2222 directory_create_or_exists(paths.private_dir, 0o700)
2223 directory_create_or_exists(paths.binddns_dir, 0o770)
2224 directory_create_or_exists(os.path.join(paths.private_dir, "tls"))
2225 directory_create_or_exists(paths.state_dir)
2226 if not plaintext_secrets:
2227 setup_encrypted_secrets_key(paths.encrypted_secrets_key_path)
2229 if paths.sysvol and not os.path.exists(paths.sysvol):
2230 os.makedirs(paths.sysvol, 0o775)
2232 ldapi_url = "ldapi://%s" % urllib_quote(paths.s4_ldapi_path, safe="")
2234 schema = Schema(domainsid, invocationid=invocationid,
2235 schemadn=names.schemadn, base_schema=base_schema)
2237 if backend_type == "ldb":
2238 provision_backend = LDBBackend(backend_type, paths=paths,
2240 names=names, logger=logger)
2241 elif backend_type == "fedora-ds":
2242 provision_backend = FDSBackend(backend_type, paths=paths,
2244 names=names, logger=logger, domainsid=domainsid,
2245 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2246 slapd_path=slapd_path,
2248 elif backend_type == "openldap":
2249 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
2251 names=names, logger=logger, domainsid=domainsid,
2252 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2253 slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls,
2254 ldap_backend_extra_port=ldap_backend_extra_port,
2255 ldap_dryrun_mode=ldap_dryrun_mode, nosync=nosync,
2256 ldap_backend_forced_uri=ldap_backend_forced_uri)
2258 raise ValueError("Unknown LDAP backend type selected")
2260 provision_backend.init()
2261 provision_backend.start()
2263 # only install a new shares config db if there is none
2264 if not os.path.exists(paths.shareconf):
2265 logger.info("Setting up share.ldb")
2266 share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
2267 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
2269 logger.info("Setting up secrets.ldb")
2270 secrets_ldb = setup_secretsdb(paths,
2271 session_info=session_info,
2272 backend_credentials=provision_backend.credentials, lp=lp)
2275 logger.info("Setting up the registry")
2276 setup_registry(paths.hklm, session_info, lp=lp)
2278 logger.info("Setting up the privileges database")
2279 setup_privileges(paths.privilege, session_info, lp=lp)
2281 logger.info("Setting up idmap db")
2282 idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
2284 setup_name_mappings(idmap, sid=str(domainsid),
2285 root_uid=root_uid, nobody_uid=nobody_uid,
2286 users_gid=users_gid, root_gid=root_gid)
2288 logger.info("Setting up SAM db")
2289 samdb = setup_samdb(paths.samdb, session_info,
2290 provision_backend, lp, names, logger=logger,
2291 serverrole=serverrole,
2292 schema=schema, fill=samdb_fill, am_rodc=am_rodc,
2293 plaintext_secrets=plaintext_secrets,
2294 backend_store=backend_store)
2296 if serverrole == "active directory domain controller":
2297 if paths.netlogon is None:
2298 raise MissingShareError("netlogon", paths.smbconf)
2300 if paths.sysvol is None:
2301 raise MissingShareError("sysvol", paths.smbconf)
2303 if not os.path.isdir(paths.netlogon):
2304 os.makedirs(paths.netlogon, 0o755)
2306 if adminpass is None:
2307 adminpass = samba.generate_random_password(12, 32)
2308 adminpass_generated = True
2310 if isinstance(adminpass, binary_type):
2311 adminpass = adminpass.decode('utf-8')
2312 adminpass_generated = False
2314 if samdb_fill == FILL_FULL:
2315 provision_fill(samdb, secrets_ldb, logger, names, paths,
2316 schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
2317 hostip=hostip, hostip6=hostip6,
2318 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
2319 krbtgtpass=krbtgtpass,
2320 policyguid=policyguid, policyguid_dc=policyguid_dc,
2321 invocationid=invocationid, machinepass=machinepass,
2322 ntdsguid=ntdsguid, dns_backend=dns_backend,
2323 dnspass=dnspass, serverrole=serverrole,
2324 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
2325 lp=lp, use_ntvfs=use_ntvfs,
2326 skip_sysvolacl=skip_sysvolacl,
2327 backend_store=backend_store)
2329 if not is_heimdal_built():
2330 create_kdc_conf(paths.kdcconf, realm, domain, os.path.dirname(lp.get("log file")))
2331 logger.info("The Kerberos KDC configuration for Samba AD is "
2332 "located at %s", paths.kdcconf)
2334 create_krb5_conf(paths.krb5conf,
2335 dnsdomain=names.dnsdomain, hostname=names.hostname,
2337 logger.info("A Kerberos configuration suitable for Samba AD has been "
2338 "generated at %s", paths.krb5conf)
2339 logger.info("Merge the contents of this file with your system "
2340 "krb5.conf or replace it with this one. Do not create a "
2343 if serverrole == "active directory domain controller":
2344 create_dns_update_list(lp, logger, paths)
2346 backend_result = provision_backend.post_setup()
2347 provision_backend.shutdown()
2350 secrets_ldb.transaction_cancel()
2353 # Now commit the secrets.ldb to disk
2354 secrets_ldb.transaction_commit()
2356 # the commit creates the dns.keytab in the private directory
2357 private_dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
2358 bind_dns_keytab_path = os.path.join(paths.binddns_dir, paths.dns_keytab)
2360 if os.path.isfile(private_dns_keytab_path):
2361 if os.path.isfile(bind_dns_keytab_path):
2363 os.unlink(bind_dns_keytab_path)
2364 except OSError as e:
2365 logger.error("Failed to remove %s: %s" %
2366 (bind_dns_keytab_path, e.strerror))
2368 # link the dns.keytab to the bind-dns directory
2370 os.link(private_dns_keytab_path, bind_dns_keytab_path)
2371 except OSError as e:
2372 logger.error("Failed to create link %s -> %s: %s" %
2373 (private_dns_keytab_path, bind_dns_keytab_path, e.strerror))
2375 # chown the dns.keytab in the bind-dns directory
2376 if paths.bind_gid is not None:
2378 os.chmod(paths.binddns_dir, 0o770)
2379 os.chown(paths.binddns_dir, -1, paths.bind_gid)
2381 if 'SAMBA_SELFTEST' not in os.environ:
2382 logger.info("Failed to chown %s to bind gid %u",
2383 paths.binddns_dir, paths.bind_gid)
2386 os.chmod(bind_dns_keytab_path, 0o640)
2387 os.chown(bind_dns_keytab_path, -1, paths.bind_gid)
2389 if 'SAMBA_SELFTEST' not in os.environ:
2390 logger.info("Failed to chown %s to bind gid %u",
2391 bind_dns_keytab_path, paths.bind_gid)
2393 result = ProvisionResult()
2394 result.server_role = serverrole
2395 result.domaindn = domaindn
2396 result.paths = paths
2397 result.names = names
2399 result.samdb = samdb
2400 result.idmap = idmap
2401 result.domainsid = str(domainsid)
2403 if samdb_fill == FILL_FULL:
2404 result.adminpass_generated = adminpass_generated
2405 result.adminpass = adminpass
2407 result.adminpass_generated = False
2408 result.adminpass = None
2410 result.backend_result = backend_result
2413 provision_fake_ypserver(logger=logger, samdb=samdb,
2414 domaindn=names.domaindn, netbiosname=names.netbiosname,
2415 nisdomain=names.domain.lower(), maxuid=maxuid, maxgid=maxgid)
2420 def provision_become_dc(smbconf=None, targetdir=None,
2421 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
2422 serverdn=None, domain=None, hostname=None, domainsid=None,
2423 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
2424 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
2425 dns_backend=None, root=None, nobody=None, users=None,
2426 backup=None, serverrole=None, ldap_backend=None,
2427 ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False):
2429 logger = logging.getLogger("provision")
2430 samba.set_debug_level(debuglevel)
2432 res = provision(logger, system_session(),
2433 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
2434 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
2435 configdn=configdn, serverdn=serverdn, domain=domain,
2436 hostname=hostname, hostip=None, domainsid=domainsid,
2437 machinepass=machinepass,
2438 serverrole="active directory domain controller",
2439 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass,
2440 use_ntvfs=use_ntvfs)
2441 res.lp.set("debuglevel", str(debuglevel))
2445 def create_krb5_conf(path, dnsdomain, hostname, realm):
2446 """Write out a file containing a valid krb5.conf file
2448 :param path: Path of the new krb5.conf file.
2449 :param dnsdomain: DNS Domain name
2450 :param hostname: Local hostname
2451 :param realm: Realm name
2453 setup_file(setup_path("krb5.conf"), path, {
2454 "DNSDOMAIN": dnsdomain,
2455 "HOSTNAME": hostname,
2460 class ProvisioningError(Exception):
2461 """A generic provision error."""
2463 def __init__(self, value):
2467 return "ProvisioningError: " + self.value
2470 class InvalidNetbiosName(Exception):
2471 """A specified name was not a valid NetBIOS name."""
2473 def __init__(self, name):
2474 super(InvalidNetbiosName, self).__init__(
2475 "The name '%r' is not a valid NetBIOS name" % name)
2478 class MissingShareError(ProvisioningError):
2480 def __init__(self, name, smbconf):
2481 super(MissingShareError, self).__init__(
2482 "Existing smb.conf does not have a [%s] share, but you are "
2483 "configuring a DC. Please remove %s or add the share manually." %