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 (
85 from samba.descriptor import (
87 get_config_descriptor,
88 get_config_partitions_descriptor,
89 get_config_sites_descriptor,
90 get_config_ntds_quotas_descriptor,
91 get_config_delete_protected1_descriptor,
92 get_config_delete_protected1wd_descriptor,
93 get_config_delete_protected2_descriptor,
94 get_domain_descriptor,
95 get_domain_infrastructure_descriptor,
96 get_domain_builtin_descriptor,
97 get_domain_computers_descriptor,
98 get_domain_users_descriptor,
99 get_domain_controllers_descriptor,
100 get_domain_delete_protected1_descriptor,
101 get_domain_delete_protected2_descriptor,
102 get_dns_partition_descriptor,
103 get_dns_forest_microsoft_dns_descriptor,
104 get_dns_domain_microsoft_dns_descriptor,
105 get_managed_service_accounts_descriptor,
107 from samba.provision.common import (
116 from samba.provision.sambadns import (
119 create_dns_update_list
123 import samba.registry
124 from samba.schema import Schema
125 from samba.samdb import SamDB
126 from samba.dbchecker import dbcheck
127 from samba.provision.kerberos import create_kdc_conf
128 from samba.samdb import get_default_backend_store
130 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
131 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04FB984F9"
132 DEFAULTSITE = "Default-First-Site-Name"
133 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
135 DEFAULT_MIN_PWD_LENGTH = 7
138 class ProvisionPaths(object):
141 self.shareconf = None
152 self.dns_keytab = None
155 self.private_dir = None
156 self.binddns_dir = None
157 self.state_dir = None
160 class ProvisionNames(object):
168 self.dnsforestdn = None
169 self.dnsdomaindn = None
170 self.ldapmanagerdn = None
171 self.dnsdomain = None
173 self.netbiosname = None
178 self.domainsid = None
179 self.forestsid = None
180 self.domainguid = None
184 def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf,
186 """Get key provision parameters (realm, domain, ...) from a given provision
188 :param samdb: An LDB object connected to the sam.ldb file
189 :param secretsdb: An LDB object connected to the secrets.ldb file
190 :param idmapdb: An LDB object connected to the idmap.ldb file
191 :param paths: A list of path to provision object
192 :param smbconf: Path to the smb.conf file
193 :param lp: A LoadParm object
194 :return: A list of key provision parameters
196 names = ProvisionNames()
197 names.adminpass = None
199 # NT domain, kerberos realm, root dn, domain dn, domain dns name
200 names.domain = lp.get("workgroup").upper()
201 names.realm = lp.get("realm")
202 names.dnsdomain = names.realm.lower()
203 basedn = samba.dn_from_dns_name(names.dnsdomain)
204 names.realm = names.realm.upper()
206 # Get the netbiosname first (could be obtained from smb.conf in theory)
207 res = secretsdb.search(expression="(flatname=%s)" %
208 names.domain, base="CN=Primary Domains",
209 scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"])
210 names.netbiosname = str(res[0]["sAMAccountName"]).replace("$", "")
212 names.smbconf = smbconf
214 # That's a bit simplistic but it's ok as long as we have only 3
216 current = samdb.search(expression="(objectClass=*)",
217 base="", scope=ldb.SCOPE_BASE,
218 attrs=["defaultNamingContext", "schemaNamingContext",
219 "configurationNamingContext", "rootDomainNamingContext",
222 names.configdn = str(current[0]["configurationNamingContext"][0])
223 names.schemadn = str(current[0]["schemaNamingContext"][0])
224 if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb,
225 current[0]["defaultNamingContext"][0].decode('utf8')))):
226 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
227 "is not the same ..." % (paths.samdb,
228 str(current[0]["defaultNamingContext"][0].decode('utf8')),
229 paths.smbconf, basedn)))
231 names.domaindn = str(current[0]["defaultNamingContext"][0])
232 names.rootdn = str(current[0]["rootDomainNamingContext"][0])
233 names.ncs = current[0]["namingContexts"]
234 names.dnsforestdn = None
235 names.dnsdomaindn = None
237 for i in range(0, len(names.ncs)):
238 nc = str(names.ncs[i])
240 dnsforestdn = "DC=ForestDnsZones,%s" % (str(names.rootdn))
241 if nc == dnsforestdn:
242 names.dnsforestdn = dnsforestdn
245 dnsdomaindn = "DC=DomainDnsZones,%s" % (str(names.domaindn))
246 if nc == dnsdomaindn:
247 names.dnsdomaindn = dnsdomaindn
251 res3 = samdb.search(expression="(objectClass=site)",
252 base="CN=Sites," + str(names.configdn), scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
253 names.sitename = str(res3[0]["cn"])
255 # dns hostname and server dn
256 res4 = samdb.search(expression="(CN=%s)" % names.netbiosname,
257 base="OU=Domain Controllers,%s" % basedn,
258 scope=ldb.SCOPE_ONELEVEL, attrs=["dNSHostName"])
260 raise ProvisioningError("Unable to find DC called CN=%s under OU=Domain Controllers,%s" % (names.netbiosname, basedn))
262 names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain, "")
264 server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
265 attrs=[], base=names.configdn)
266 names.serverdn = str(server_res[0].dn)
268 # invocation id/objectguid
269 res5 = samdb.search(expression="(objectClass=*)",
270 base="CN=NTDS Settings,%s" % str(names.serverdn),
271 scope=ldb.SCOPE_BASE,
272 attrs=["invocationID", "objectGUID"])
273 names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0]))
274 names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0]))
277 res6 = samdb.search(expression="(objectClass=*)", base=basedn,
278 scope=ldb.SCOPE_BASE, attrs=["objectGUID",
279 "objectSid", "msDS-Behavior-Version"])
280 names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
281 names.domainsid = ndr_unpack(security.dom_sid, res6[0]["objectSid"][0])
282 names.forestsid = ndr_unpack(security.dom_sid, res6[0]["objectSid"][0])
283 if res6[0].get("msDS-Behavior-Version") is None or \
284 int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
285 names.domainlevel = DS_DOMAIN_FUNCTION_2000
287 names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
290 res7 = samdb.search(expression="(name={%s})" % DEFAULT_POLICY_GUID,
291 base="CN=Policies,CN=System," + basedn,
292 scope=ldb.SCOPE_ONELEVEL, attrs=["cn", "displayName"])
293 names.policyid = str(res7[0]["cn"]).replace("{", "").replace("}", "")
295 res8 = samdb.search(expression="(name={%s})" % DEFAULT_DC_POLICY_GUID,
296 base="CN=Policies,CN=System," + basedn,
297 scope=ldb.SCOPE_ONELEVEL,
298 attrs=["cn", "displayName"])
300 names.policyid_dc = str(res8[0]["cn"]).replace("{", "").replace("}", "")
302 names.policyid_dc = None
304 res9 = idmapdb.search(expression="(cn=%s-%s)" %
305 (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR),
306 attrs=["xidNumber", "type"])
308 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid (%s-%s" % (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR))
309 if str(res9[0]["type"][0]) == "ID_TYPE_BOTH":
310 names.root_gid = int(res9[0]["xidNumber"][0])
312 names.root_gid = pwd.getpwuid(int(res9[0]["xidNumber"][0])).pw_gid
314 res10 = samdb.search(expression="(samaccountname=dns)",
315 scope=ldb.SCOPE_SUBTREE, attrs=["dn"],
316 controls=["search_options:1:2"])
318 has_legacy_dns_account = True
320 has_legacy_dns_account = False
322 res11 = samdb.search(expression="(samaccountname=dns-%s)" % names.netbiosname,
323 scope=ldb.SCOPE_SUBTREE, attrs=["dn"],
324 controls=["search_options:1:2"])
326 has_dns_account = True
328 has_dns_account = False
330 if names.dnsdomaindn is not None:
332 names.dns_backend = 'BIND9_DLZ'
334 names.dns_backend = 'SAMBA_INTERNAL'
335 elif has_dns_account or has_legacy_dns_account:
336 names.dns_backend = 'BIND9_FLATFILE'
338 names.dns_backend = 'NONE'
340 dns_admins_sid = get_dnsadmins_sid(samdb, names.domaindn)
341 names.name_map['DnsAdmins'] = str(dns_admins_sid)
346 def update_provision_usn(samdb, low, high, id, replace=False):
347 """Update the field provisionUSN in sam.ldb
349 This field is used to track range of USN modified by provision and
351 This value is used afterward by next provision to figure out if
352 the field have been modified since last provision.
354 :param samdb: An LDB object connect to sam.ldb
355 :param low: The lowest USN modified by this upgrade
356 :param high: The highest USN modified by this upgrade
357 :param id: The invocation id of the samba's dc
358 :param replace: A boolean indicating if the range should replace any
359 existing one or appended (default)
364 entry = samdb.search(base="@PROVISION",
365 scope=ldb.SCOPE_BASE,
366 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
367 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
368 if not re.search(';', str(e)):
369 e = "%s;%s" % (str(e), id)
372 tab.append("%s-%s;%s" % (low, high, id))
373 delta = ldb.Message()
374 delta.dn = ldb.Dn(samdb, "@PROVISION")
375 delta[LAST_PROVISION_USN_ATTRIBUTE] = \
376 ldb.MessageElement(tab,
377 ldb.FLAG_MOD_REPLACE,
378 LAST_PROVISION_USN_ATTRIBUTE)
379 entry = samdb.search(expression='provisionnerID=*',
380 base="@PROVISION", scope=ldb.SCOPE_BASE,
381 attrs=["provisionnerID"])
382 if len(entry) == 0 or len(entry[0]) == 0:
383 delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
387 def set_provision_usn(samdb, low, high, id):
388 """Set the field provisionUSN in sam.ldb
389 This field is used to track range of USN modified by provision and
391 This value is used afterward by next provision to figure out if
392 the field have been modified since last provision.
394 :param samdb: An LDB object connect to sam.ldb
395 :param low: The lowest USN modified by this upgrade
396 :param high: The highest USN modified by this upgrade
397 :param id: The invocationId of the provision"""
400 tab.append("%s-%s;%s" % (low, high, id))
402 delta = ldb.Message()
403 delta.dn = ldb.Dn(samdb, "@PROVISION")
404 delta[LAST_PROVISION_USN_ATTRIBUTE] = \
405 ldb.MessageElement(tab,
407 LAST_PROVISION_USN_ATTRIBUTE)
411 def get_max_usn(samdb, basedn):
412 """ This function return the biggest USN present in the provision
414 :param samdb: A LDB object pointing to the sam.ldb
415 :param basedn: A string containing the base DN of the provision
417 :return: The biggest USN in the provision"""
419 res = samdb.search(expression="objectClass=*", base=basedn,
420 scope=ldb.SCOPE_SUBTREE, attrs=["uSNChanged"],
421 controls=["search_options:1:2",
422 "server_sort:1:1:uSNChanged",
423 "paged_results:1:1"])
424 return res[0]["uSNChanged"]
427 def get_last_provision_usn(sam):
428 """Get USNs ranges modified by a provision or an upgradeprovision
430 :param sam: An LDB object pointing to the sam.ldb
431 :return: a dictionary which keys are invocation id and values are an array
432 of integer representing the different ranges
435 entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE,
436 base="@PROVISION", scope=ldb.SCOPE_BASE,
437 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
438 except ldb.LdbError as e1:
439 (ecode, emsg) = e1.args
440 if ecode == ldb.ERR_NO_SUCH_OBJECT:
447 if entry[0].get("provisionnerID"):
448 for e in entry[0]["provisionnerID"]:
450 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
451 tab1 = str(r).split(';')
456 if (len(myids) > 0 and id not in myids):
458 tab2 = p.split(tab1[0])
459 if range.get(id) is None:
461 range[id].append(tab2[0])
462 range[id].append(tab2[1])
468 class ProvisionResult(object):
469 """Result of a provision.
471 :ivar server_role: The server role
472 :ivar paths: ProvisionPaths instance
473 :ivar domaindn: The domain dn, as string
477 self.server_role = None
484 self.domainsid = None
485 self.adminpass_generated = None
486 self.adminpass = None
487 self.backend_result = None
489 def report_logger(self, logger):
490 """Report this provision result to a logger."""
492 "Once the above files are installed, your Samba AD server will "
494 if self.adminpass_generated:
495 logger.info("Admin password: %s", self.adminpass)
496 logger.info("Server Role: %s", self.server_role)
497 logger.info("Hostname: %s", self.names.hostname)
498 logger.info("NetBIOS Domain: %s", self.names.domain)
499 logger.info("DNS Domain: %s", self.names.dnsdomain)
500 logger.info("DOMAIN SID: %s", self.domainsid)
502 if self.backend_result:
503 self.backend_result.report_logger(logger)
506 def check_install(lp, session_info, credentials):
507 """Check whether the current install seems ok.
509 :param lp: Loadparm context
510 :param session_info: Session information
511 :param credentials: Credentials
513 if lp.get("realm") == "":
514 raise Exception("Realm empty")
515 samdb = Ldb(lp.samdb_url(), session_info=session_info,
516 credentials=credentials, lp=lp)
517 if len(samdb.search("(cn=Administrator)")) != 1:
518 raise ProvisioningError("No administrator account found")
521 def findnss(nssfn, names):
522 """Find a user or group from a list of possibilities.
524 :param nssfn: NSS Function to try (should raise KeyError if not found)
525 :param names: Names to check.
526 :return: Value return by first names list.
533 raise KeyError("Unable to find user/group in %r" % names)
536 def findnss_uid(names):
537 return findnss(pwd.getpwnam, names)[2]
540 def findnss_gid(names):
541 return findnss(grp.getgrnam, names)[2]
544 def provision_paths_from_lp(lp, dnsdomain):
545 """Set the default paths for provisioning.
547 :param lp: Loadparm context.
548 :param dnsdomain: DNS Domain name
550 paths = ProvisionPaths()
551 paths.private_dir = lp.get("private dir")
552 paths.binddns_dir = lp.get("binddns dir")
553 paths.state_dir = lp.get("state directory")
555 # This is stored without path prefix for the "privateKeytab" attribute in
556 # "secrets_dns.ldif".
557 paths.dns_keytab = "dns.keytab"
558 paths.keytab = "secrets.keytab"
560 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
561 paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
562 paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
563 paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
564 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
565 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
566 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
567 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
568 paths.kdcconf = os.path.join(paths.private_dir, "kdc.conf")
569 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
570 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
571 paths.encrypted_secrets_key_path = os.path.join(
573 "encrypted_secrets.key")
575 paths.dns = os.path.join(paths.binddns_dir, "dns", dnsdomain + ".zone")
576 paths.namedconf = os.path.join(paths.binddns_dir, "named.conf")
577 paths.namedconf_update = os.path.join(paths.binddns_dir, "named.conf.update")
578 paths.namedtxt = os.path.join(paths.binddns_dir, "named.txt")
580 paths.hklm = "hklm.ldb"
581 paths.hkcr = "hkcr.ldb"
582 paths.hkcu = "hkcu.ldb"
583 paths.hku = "hku.ldb"
584 paths.hkpd = "hkpd.ldb"
585 paths.hkpt = "hkpt.ldb"
586 paths.sysvol = lp.get("path", "sysvol")
587 paths.netlogon = lp.get("path", "netlogon")
588 paths.smbconf = lp.configfile
592 def determine_netbios_name(hostname):
593 """Determine a netbios name from a hostname."""
594 # remove forbidden chars and force the length to be <16
595 netbiosname = "".join([x for x in hostname if is_valid_netbios_char(x)])
596 return netbiosname[:MAX_NETBIOS_NAME_LEN].upper()
599 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
600 serverrole=None, rootdn=None, domaindn=None, configdn=None,
601 schemadn=None, serverdn=None, sitename=None,
602 domain_names_forced=False):
603 """Guess configuration settings to use."""
606 hostname = socket.gethostname().split(".")[0]
608 netbiosname = lp.get("netbios name")
609 if netbiosname is None:
610 netbiosname = determine_netbios_name(hostname)
611 netbiosname = netbiosname.upper()
612 if not valid_netbios_name(netbiosname):
613 raise InvalidNetbiosName(netbiosname)
615 if dnsdomain is None:
616 dnsdomain = lp.get("realm")
617 if dnsdomain is None or dnsdomain == "":
618 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
620 dnsdomain = dnsdomain.lower()
622 if serverrole is None:
623 serverrole = lp.get("server role")
624 if serverrole is None:
625 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
627 serverrole = serverrole.lower()
629 realm = dnsdomain.upper()
631 if lp.get("realm") == "":
632 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
634 if lp.get("realm").upper() != realm:
635 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))
637 if lp.get("server role").lower() != serverrole:
638 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))
640 if serverrole == "active directory domain controller":
642 # This will, for better or worse, default to 'WORKGROUP'
643 domain = lp.get("workgroup")
644 domain = domain.upper()
646 if lp.get("workgroup").upper() != domain:
647 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))
650 domaindn = samba.dn_from_dns_name(dnsdomain)
652 if domain == netbiosname:
653 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
657 domaindn = "DC=" + netbiosname
659 if not valid_netbios_name(domain):
660 raise InvalidNetbiosName(domain)
662 if hostname.upper() == realm:
663 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
664 if netbiosname.upper() == realm:
665 raise ProvisioningError("guess_names: Realm '%s' must not be equal to NetBIOS hostname '%s'!" % (realm, netbiosname))
666 if domain == realm and not domain_names_forced:
667 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
669 if serverrole != "active directory domain controller":
671 # This is the code path for a domain member
672 # where we provision the database as if we where
673 # on a domain controller, so we should not use
674 # the same dnsdomain as the domain controllers
675 # of our primary domain.
677 # This will be important if we start doing
678 # SID/name filtering and reject the local
679 # sid and names if they come from a domain
683 dnsdomain = netbiosname.lower()
689 configdn = "CN=Configuration," + rootdn
691 schemadn = "CN=Schema," + configdn
694 sitename = DEFAULTSITE
696 names = ProvisionNames()
697 names.rootdn = rootdn
698 names.domaindn = domaindn
699 names.configdn = configdn
700 names.schemadn = schemadn
701 names.ldapmanagerdn = "CN=Manager," + rootdn
702 names.dnsdomain = dnsdomain
703 names.domain = domain
705 names.netbiosname = netbiosname
706 names.hostname = hostname
707 names.sitename = sitename
708 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
709 netbiosname, sitename, configdn)
714 def make_smbconf(smbconf, hostname, domain, realm, targetdir,
715 serverrole=None, eadb=False, use_ntvfs=False, lp=None,
717 """Create a new smb.conf file based on a couple of basic settings.
719 assert smbconf is not None
722 hostname = socket.gethostname().split(".")[0]
724 netbiosname = determine_netbios_name(hostname)
726 if serverrole is None:
727 serverrole = "standalone server"
729 assert domain is not None
730 domain = domain.upper()
732 assert realm is not None
733 realm = realm.upper()
736 "netbios name": netbiosname,
739 "server role": serverrole,
743 lp = samba.param.LoadParm()
744 # Load non-existent file
745 if os.path.exists(smbconf):
748 if global_param is not None:
749 for ent in global_param:
750 if global_param[ent] is not None:
751 global_settings[ent] = " ".join(global_param[ent])
753 if targetdir is not None:
754 global_settings["private dir"] = os.path.abspath(os.path.join(targetdir, "private"))
755 global_settings["lock dir"] = os.path.abspath(targetdir)
756 global_settings["state directory"] = os.path.abspath(os.path.join(targetdir, "state"))
757 global_settings["cache directory"] = os.path.abspath(os.path.join(targetdir, "cache"))
758 global_settings["binddns dir"] = os.path.abspath(os.path.join(targetdir, "bind-dns"))
760 lp.set("lock dir", os.path.abspath(targetdir))
761 lp.set("state directory", global_settings["state directory"])
762 lp.set("cache directory", global_settings["cache directory"])
763 lp.set("binddns dir", global_settings["binddns dir"])
766 if use_ntvfs and not lp.get("posix:eadb"):
767 if targetdir is not None:
768 privdir = os.path.join(targetdir, "private")
770 privdir = lp.get("private dir")
771 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
772 elif not use_ntvfs and not lp.get("xattr_tdb:file"):
773 if targetdir is not None:
774 statedir = os.path.join(targetdir, "state")
776 statedir = lp.get("state directory")
777 lp.set("xattr_tdb:file", os.path.abspath(os.path.join(statedir, "xattr.tdb")))
780 if serverrole == "active directory domain controller":
781 shares["sysvol"] = os.path.join(lp.get("state directory"), "sysvol")
782 shares["netlogon"] = os.path.join(shares["sysvol"], realm.lower(),
785 global_settings["passdb backend"] = "samba_dsdb"
787 f = open(smbconf, 'w')
789 f.write("[globals]\n")
790 for key, val in global_settings.items():
791 f.write("\t%s = %s\n" % (key, val))
794 for name, path in shares.items():
795 f.write("[%s]\n" % name)
796 f.write("\tpath = %s\n" % path)
797 f.write("\tread only = no\n")
801 # reload the smb.conf
804 # and dump it without any values that are the default
805 # this ensures that any smb.conf parameters that were set
806 # on the provision/join command line are set in the resulting smb.conf
807 lp.dump(False, smbconf)
810 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
811 users_gid, root_gid):
812 """setup reasonable name mappings for sam names to unix names.
814 :param samdb: SamDB object.
815 :param idmap: IDmap db object.
816 :param sid: The domain sid.
817 :param domaindn: The domain DN.
818 :param root_uid: uid of the UNIX root user.
819 :param nobody_uid: uid of the UNIX nobody user.
820 :param users_gid: gid of the UNIX users group.
821 :param root_gid: gid of the UNIX root group.
823 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
825 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
826 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
829 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
830 provision_backend, names, serverrole,
831 erase=False, plaintext_secrets=False,
833 """Setup the partitions for the SAM database.
835 Alternatively, provision() may call this, and then populate the database.
837 :note: This will wipe the Sam Database!
839 :note: This function always removes the local SAM LDB file. The erase
840 parameter controls whether to erase the existing data, which
841 may not be stored locally but in LDAP.
844 assert session_info is not None
846 # We use options=["modules:"] to stop the modules loading - we
847 # just want to wipe and re-initialise the database, not start it up
850 os.unlink(samdb_path)
854 samdb = Ldb(url=samdb_path, session_info=session_info,
855 lp=lp, options=["modules:"])
857 ldap_backend_line = "# No LDAP backend"
858 if provision_backend.type != "ldb":
859 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
861 required_features = None
862 if not plaintext_secrets:
863 required_features = "requiredFeatures: encryptedSecrets"
865 if backend_store is None:
866 backend_store = get_default_backend_store()
867 backend_store_line = "backendStore: %s" % backend_store
869 if backend_store == "mdb":
870 if required_features is not None:
871 required_features += "\n"
873 required_features = ""
874 required_features += "requiredFeatures: lmdbLevelOne"
876 if required_features is None:
877 required_features = "# No required features"
879 samdb.transaction_start()
881 logger.info("Setting up sam.ldb partitions and settings")
882 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
883 "LDAP_BACKEND_LINE": ldap_backend_line,
884 "BACKEND_STORE": backend_store_line
887 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
888 "BACKEND_TYPE": provision_backend.type,
889 "SERVER_ROLE": serverrole,
890 "REQUIRED_FEATURES": required_features
893 logger.info("Setting up sam.ldb rootDSE")
894 setup_samdb_rootdse(samdb, names)
896 samdb.transaction_cancel()
899 samdb.transaction_commit()
902 def secretsdb_self_join(secretsdb, domain,
903 netbiosname, machinepass, domainsid=None,
904 realm=None, dnsdomain=None,
906 key_version_number=1,
907 secure_channel_type=SEC_CHAN_WKSTA):
908 """Add domain join-specific bits to a secrets database.
910 :param secretsdb: Ldb Handle to the secrets database
911 :param machinepass: Machine password
913 attrs = ["whenChanged",
920 if realm is not None:
921 if dnsdomain is None:
922 dnsdomain = realm.lower()
923 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
926 shortname = netbiosname.lower()
928 # We don't need to set msg["flatname"] here, because rdn_name will handle
929 # it, and it causes problems for modifies anyway
930 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
931 msg["secureChannelType"] = [str(secure_channel_type)]
932 msg["objectClass"] = ["top", "primaryDomain"]
933 if dnsname is not None:
934 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
935 msg["realm"] = [realm]
936 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
937 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
938 msg["privateKeytab"] = ["secrets.keytab"]
940 msg["secret"] = [machinepass.encode('utf-8')]
941 msg["samAccountName"] = ["%s$" % netbiosname]
942 msg["secureChannelType"] = [str(secure_channel_type)]
943 if domainsid is not None:
944 msg["objectSid"] = [ndr_pack(domainsid)]
946 # This complex expression tries to ensure that we don't have more
947 # than one record for this SID, realm or netbios domain at a time,
948 # but we don't delete the old record that we are about to modify,
949 # because that would delete the keytab and previous password.
950 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
951 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
952 scope=ldb.SCOPE_ONELEVEL)
955 secretsdb.delete(del_msg.dn)
957 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
960 msg["priorSecret"] = [res[0]["secret"][0]]
962 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
967 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
972 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
978 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
979 secretsdb.modify(msg)
980 secretsdb.rename(res[0].dn, msg.dn)
982 spn = ['HOST/%s' % shortname]
983 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
984 # we are a domain controller then we add servicePrincipalName
985 # entries for the keytab code to update.
986 spn.extend(['HOST/%s' % dnsname])
987 msg["servicePrincipalName"] = spn
992 def setup_secretsdb(paths, session_info, backend_credentials, lp):
993 """Setup the secrets database.
995 :note: This function does not handle exceptions and transaction on purpose,
996 it's up to the caller to do this job.
998 :param path: Path to the secrets database.
999 :param session_info: Session info.
1000 :param credentials: Credentials
1001 :param lp: Loadparm context
1002 :return: LDB handle for the created secrets database
1004 if os.path.exists(paths.secrets):
1005 os.unlink(paths.secrets)
1007 keytab_path = os.path.join(paths.private_dir, paths.keytab)
1008 if os.path.exists(keytab_path):
1009 os.unlink(keytab_path)
1011 bind_dns_keytab_path = os.path.join(paths.binddns_dir, paths.dns_keytab)
1012 if os.path.exists(bind_dns_keytab_path):
1013 os.unlink(bind_dns_keytab_path)
1015 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1016 if os.path.exists(dns_keytab_path):
1017 os.unlink(dns_keytab_path)
1019 path = paths.secrets
1021 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
1023 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
1024 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
1025 secrets_ldb.transaction_start()
1027 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
1029 if (backend_credentials is not None and
1030 backend_credentials.authentication_requested()):
1031 if backend_credentials.get_bind_dn() is not None:
1032 setup_add_ldif(secrets_ldb,
1033 setup_path("secrets_simple_ldap.ldif"), {
1034 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
1035 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password()).decode('utf8')
1038 setup_add_ldif(secrets_ldb,
1039 setup_path("secrets_sasl_ldap.ldif"), {
1040 "LDAPADMINUSER": backend_credentials.get_username(),
1041 "LDAPADMINREALM": backend_credentials.get_realm(),
1042 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password()).decode('utf8')
1045 secrets_ldb.transaction_cancel()
1050 def setup_privileges(path, session_info, lp):
1051 """Setup the privileges database.
1053 :param path: Path to the privileges database.
1054 :param session_info: Session info.
1055 :param credentials: Credentials
1056 :param lp: Loadparm context
1057 :return: LDB handle for the created secrets database
1059 if os.path.exists(path):
1061 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
1062 privilege_ldb.erase()
1063 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
1066 def setup_encrypted_secrets_key(path):
1067 """Setup the encrypted secrets key file.
1069 Any existing key file will be deleted and a new random key generated.
1071 :param path: Path to the secrets key file.
1074 if os.path.exists(path):
1077 flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
1078 mode = stat.S_IRUSR | stat.S_IWUSR
1080 umask_original = os.umask(0)
1082 fd = os.open(path, flags, mode)
1084 os.umask(umask_original)
1086 with os.fdopen(fd, 'wb') as f:
1087 key = samba.generate_random_bytes(16)
1091 def setup_registry(path, session_info, lp):
1092 """Setup the registry.
1094 :param path: Path to the registry database
1095 :param session_info: Session information
1096 :param credentials: Credentials
1097 :param lp: Loadparm context
1099 reg = samba.registry.Registry()
1100 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
1101 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
1102 provision_reg = setup_path("provision.reg")
1103 assert os.path.exists(provision_reg)
1104 reg.diff_apply(provision_reg)
1107 def setup_idmapdb(path, session_info, lp):
1108 """Setup the idmap database.
1110 :param path: path to the idmap database
1111 :param session_info: Session information
1112 :param credentials: Credentials
1113 :param lp: Loadparm context
1115 if os.path.exists(path):
1118 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
1120 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
1124 def setup_samdb_rootdse(samdb, names):
1125 """Setup the SamDB rootdse.
1127 :param samdb: Sam Database handle
1129 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
1130 "SCHEMADN": names.schemadn,
1131 "DOMAINDN": names.domaindn,
1132 "ROOTDN": names.rootdn,
1133 "CONFIGDN": names.configdn,
1134 "SERVERDN": names.serverdn,
1138 def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
1139 dns_backend, dnspass, domainsid, next_rid, invocationid,
1140 policyguid, policyguid_dc,
1141 domainControllerFunctionality, ntdsguid=None, dc_rid=None):
1142 """Join a host to its own domain."""
1143 assert isinstance(invocationid, str)
1144 if ntdsguid is not None:
1145 ntdsguid_line = "objectGUID: %s\n" % ntdsguid
1152 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
1153 "CONFIGDN": names.configdn,
1154 "SCHEMADN": names.schemadn,
1155 "DOMAINDN": names.domaindn,
1156 "SERVERDN": names.serverdn,
1157 "INVOCATIONID": invocationid,
1158 "NETBIOSNAME": names.netbiosname,
1159 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1160 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')).decode('utf8'),
1161 "DOMAINSID": str(domainsid),
1162 "DCRID": str(dc_rid),
1163 "SAMBA_VERSION_STRING": version,
1164 "NTDSGUID": ntdsguid_line,
1165 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1166 domainControllerFunctionality),
1167 "RIDALLOCATIONSTART": str(next_rid + 100),
1168 "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
1170 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1171 "POLICYGUID": policyguid,
1172 "POLICYGUID_DC": policyguid_dc,
1173 "DNSDOMAIN": names.dnsdomain,
1174 "DOMAINDN": names.domaindn})
1176 # If we are setting up a subdomain, then this has been replicated in, so we
1177 # don't need to add it
1178 if fill == FILL_FULL:
1179 setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
1180 "CONFIGDN": names.configdn,
1181 "SCHEMADN": names.schemadn,
1182 "DOMAINDN": names.domaindn,
1183 "SERVERDN": names.serverdn,
1184 "INVOCATIONID": invocationid,
1185 "NETBIOSNAME": names.netbiosname,
1186 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1187 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')).decode('utf8'),
1188 "DOMAINSID": str(domainsid),
1189 "DCRID": str(dc_rid),
1190 "SAMBA_VERSION_STRING": version,
1191 "NTDSGUID": ntdsguid_line,
1192 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1193 domainControllerFunctionality)})
1195 # Setup fSMORoleOwner entries to point at the newly created DC entry
1196 setup_modify_ldif(samdb,
1197 setup_path("provision_self_join_modify_config.ldif"), {
1198 "CONFIGDN": names.configdn,
1199 "SCHEMADN": names.schemadn,
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 controler 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 def setup_samdb(path, session_info, provision_backend, lp, names,
1277 logger, fill, serverrole, schema, am_rodc=False,
1278 plaintext_secrets=False, backend_store=None):
1279 """Setup a complete SAM Database.
1281 :note: This will wipe the main SAM database file!
1284 # Also wipes the database
1285 setup_samdb_partitions(path, logger=logger, lp=lp,
1286 provision_backend=provision_backend, session_info=session_info,
1287 names=names, serverrole=serverrole, plaintext_secrets=plaintext_secrets,
1288 backend_store=backend_store)
1290 # Load the database, but don's load the global schema and don't connect
1292 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1293 credentials=provision_backend.credentials, lp=lp,
1294 global_schema=False, am_rodc=am_rodc)
1296 logger.info("Pre-loading the Samba 4 and AD schema")
1298 # Load the schema from the one we computed earlier
1299 samdb.set_schema(schema, write_indices_and_attributes=False)
1301 # Set the NTDS settings DN manually - in order to have it already around
1302 # before the provisioned tree exists and we connect
1303 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1305 # And now we can connect to the DB - the schema won't be loaded from the
1309 except ldb.LdbError as e2:
1310 (num, string_error) = e2.args
1311 if (num == ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS):
1312 raise ProvisioningError("Permission denied connecting to %s, are you running as root?" % path)
1316 # But we have to give it one more kick to have it use the schema
1317 # during provision - it needs, now that it is connected, to write
1318 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1319 samdb.set_schema(schema, write_indices_and_attributes=True)
1324 def fill_samdb(samdb, lp, names, logger, policyguid,
1325 policyguid_dc, fill, adminpass, krbtgtpass, machinepass, dns_backend,
1326 dnspass, invocationid, ntdsguid, serverrole, am_rodc=False,
1327 dom_for_fun_level=None, schema=None, next_rid=None, dc_rid=None,
1328 backend_store=None):
1330 if next_rid is None:
1333 # Provision does not make much sense values larger than 1000000000
1334 # as the upper range of the rIDAvailablePool is 1073741823 and
1335 # we don't want to create a domain that cannot allocate rids.
1336 if next_rid < 1000 or next_rid > 1000000000:
1337 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1338 error += "the valid range is %u-%u. The default is %u." % (
1339 1000, 1000000000, 1000)
1340 raise ProvisioningError(error)
1342 # ATTENTION: Do NOT change these default values without discussion with the
1343 # team and/or release manager. They have a big impact on the whole program!
1344 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1346 if dom_for_fun_level is None:
1347 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
1349 if dom_for_fun_level > domainControllerFunctionality:
1350 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!")
1352 domainFunctionality = dom_for_fun_level
1353 forestFunctionality = dom_for_fun_level
1355 # Set the NTDS settings DN manually - in order to have it already around
1356 # before the provisioned tree exists and we connect
1357 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1359 # Set the domain functionality levels onto the database.
1360 # Various module (the password_hash module in particular) need
1361 # to know what level of AD we are emulating.
1363 # These will be fixed into the database via the database
1364 # modifictions below, but we need them set from the start.
1365 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1366 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1367 samdb.set_opaque_integer("domainControllerFunctionality",
1368 domainControllerFunctionality)
1370 samdb.set_domain_sid(str(names.domainsid))
1371 samdb.set_invocation_id(invocationid)
1373 logger.info("Adding DomainDN: %s" % names.domaindn)
1375 # impersonate domain admin
1376 admin_session_info = admin_session(lp, str(names.domainsid))
1377 samdb.set_session_info(admin_session_info)
1378 if names.domainguid is not None:
1379 domainguid_line = "objectGUID: %s\n-" % names.domainguid
1381 domainguid_line = ""
1383 descr = b64encode(get_domain_descriptor(names.domainsid)).decode('utf8')
1384 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1385 "DOMAINDN": names.domaindn,
1386 "DOMAINSID": str(names.domainsid),
1387 "DESCRIPTOR": descr,
1388 "DOMAINGUID": domainguid_line
1391 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1392 "DOMAINDN": names.domaindn,
1393 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1394 "NEXTRID": str(next_rid),
1395 "DEFAULTSITE": names.sitename,
1396 "CONFIGDN": names.configdn,
1397 "POLICYGUID": policyguid,
1398 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1399 "SAMBA_VERSION_STRING": version,
1400 "MIN_PWD_LENGTH": str(DEFAULT_MIN_PWD_LENGTH)
1403 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1404 if fill == FILL_FULL:
1405 logger.info("Adding configuration container")
1406 descr = b64encode(get_config_descriptor(names.domainsid)).decode('utf8')
1407 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1408 "CONFIGDN": names.configdn,
1409 "DESCRIPTOR": descr,
1412 # The LDIF here was created when the Schema object was constructed
1413 ignore_checks_oid = "local_oid:%s:0" % samba.dsdb.DSDB_CONTROL_SKIP_DUPLICATES_CHECK_OID
1414 logger.info("Setting up sam.ldb schema")
1415 samdb.add_ldif(schema.schema_dn_add,
1416 controls=["relax:0", ignore_checks_oid])
1417 samdb.modify_ldif(schema.schema_dn_modify,
1418 controls=[ignore_checks_oid])
1419 samdb.write_prefixes_from_schema()
1420 samdb.add_ldif(schema.schema_data, controls=["relax:0", ignore_checks_oid])
1421 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1422 {"SCHEMADN": names.schemadn},
1423 controls=["relax:0", ignore_checks_oid])
1425 # Now register this container in the root of the forest
1426 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1427 msg["subRefs"] = ldb.MessageElement(names.configdn, ldb.FLAG_MOD_ADD,
1430 samdb.invocation_id = invocationid
1432 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1433 if fill == FILL_FULL:
1434 logger.info("Setting up sam.ldb configuration data")
1436 partitions_descr = b64encode(get_config_partitions_descriptor(names.domainsid)).decode('utf8')
1437 sites_descr = b64encode(get_config_sites_descriptor(names.domainsid)).decode('utf8')
1438 ntdsquotas_descr = b64encode(get_config_ntds_quotas_descriptor(names.domainsid)).decode('utf8')
1439 protected1_descr = b64encode(get_config_delete_protected1_descriptor(names.domainsid)).decode('utf8')
1440 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid)).decode('utf8')
1441 protected2_descr = b64encode(get_config_delete_protected2_descriptor(names.domainsid)).decode('utf8')
1443 if "2008" in schema.base_schema:
1444 # exclude 2012-specific changes if we're using a 2008 schema
1449 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1450 "CONFIGDN": names.configdn,
1451 "NETBIOSNAME": names.netbiosname,
1452 "DEFAULTSITE": names.sitename,
1453 "DNSDOMAIN": names.dnsdomain,
1454 "DOMAIN": names.domain,
1455 "SCHEMADN": names.schemadn,
1456 "DOMAINDN": names.domaindn,
1457 "SERVERDN": names.serverdn,
1458 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1459 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1460 "NTDSQUOTAS_DESCRIPTOR": ntdsquotas_descr,
1461 "LOSTANDFOUND_DESCRIPTOR": protected1wd_descr,
1462 "SERVICES_DESCRIPTOR": protected1_descr,
1463 "PHYSICALLOCATIONS_DESCRIPTOR": protected1wd_descr,
1464 "FORESTUPDATES_DESCRIPTOR": protected1wd_descr,
1465 "EXTENDEDRIGHTS_DESCRIPTOR": protected2_descr,
1466 "PARTITIONS_DESCRIPTOR": partitions_descr,
1467 "SITES_DESCRIPTOR": sites_descr,
1470 setup_add_ldif(samdb, setup_path("extended-rights.ldif"), {
1471 "CONFIGDN": names.configdn,
1472 "INC2012": incl_2012,
1475 logger.info("Setting up display specifiers")
1476 display_specifiers_ldif = read_ms_ldif(
1477 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1478 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1479 {"CONFIGDN": names.configdn})
1480 check_all_substituted(display_specifiers_ldif)
1481 samdb.add_ldif(display_specifiers_ldif)
1483 logger.info("Modifying display specifiers and extended rights")
1484 setup_modify_ldif(samdb,
1485 setup_path("provision_configuration_modify.ldif"), {
1486 "CONFIGDN": names.configdn,
1487 "DISPLAYSPECIFIERS_DESCRIPTOR": protected2_descr
1490 logger.info("Adding users container")
1491 users_desc = b64encode(get_domain_users_descriptor(names.domainsid)).decode('utf8')
1492 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1493 "DOMAINDN": names.domaindn,
1494 "USERS_DESCRIPTOR": users_desc
1496 logger.info("Modifying users container")
1497 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1498 "DOMAINDN": names.domaindn})
1499 logger.info("Adding computers container")
1500 computers_desc = b64encode(get_domain_computers_descriptor(names.domainsid)).decode('utf8')
1501 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1502 "DOMAINDN": names.domaindn,
1503 "COMPUTERS_DESCRIPTOR": computers_desc
1505 logger.info("Modifying computers container")
1506 setup_modify_ldif(samdb,
1507 setup_path("provision_computers_modify.ldif"), {
1508 "DOMAINDN": names.domaindn})
1509 logger.info("Setting up sam.ldb data")
1510 infrastructure_desc = b64encode(get_domain_infrastructure_descriptor(names.domainsid)).decode('utf8')
1511 lostandfound_desc = b64encode(get_domain_delete_protected2_descriptor(names.domainsid)).decode('utf8')
1512 system_desc = b64encode(get_domain_delete_protected1_descriptor(names.domainsid)).decode('utf8')
1513 builtin_desc = b64encode(get_domain_builtin_descriptor(names.domainsid)).decode('utf8')
1514 controllers_desc = b64encode(get_domain_controllers_descriptor(names.domainsid)).decode('utf8')
1515 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1516 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1517 "DOMAINDN": names.domaindn,
1518 "NETBIOSNAME": names.netbiosname,
1519 "DEFAULTSITE": names.sitename,
1520 "CONFIGDN": names.configdn,
1521 "SERVERDN": names.serverdn,
1522 "RIDAVAILABLESTART": str(next_rid + 600),
1523 "POLICYGUID_DC": policyguid_dc,
1524 "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc,
1525 "LOSTANDFOUND_DESCRIPTOR": lostandfound_desc,
1526 "SYSTEM_DESCRIPTOR": system_desc,
1527 "BUILTIN_DESCRIPTOR": builtin_desc,
1528 "DOMAIN_CONTROLLERS_DESCRIPTOR": controllers_desc,
1531 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1532 if fill == FILL_FULL:
1533 managedservice_descr = b64encode(get_managed_service_accounts_descriptor(names.domainsid)).decode('utf8')
1534 setup_modify_ldif(samdb,
1535 setup_path("provision_configuration_references.ldif"), {
1536 "CONFIGDN": names.configdn,
1537 "SCHEMADN": names.schemadn})
1539 logger.info("Setting up well known security principals")
1540 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid)).decode('utf8')
1541 setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1542 "CONFIGDN": names.configdn,
1543 "WELLKNOWNPRINCIPALS_DESCRIPTOR": protected1wd_descr,
1544 }, controls=["relax:0", "provision:0"])
1546 if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1547 setup_modify_ldif(samdb,
1548 setup_path("provision_basedn_references.ldif"), {
1549 "DOMAINDN": names.domaindn,
1550 "MANAGEDSERVICE_DESCRIPTOR": managedservice_descr
1553 logger.info("Setting up sam.ldb users and groups")
1554 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1555 "DOMAINDN": names.domaindn,
1556 "DOMAINSID": str(names.domainsid),
1557 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')).decode('utf8'),
1558 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le')).decode('utf8')
1559 }, controls=["relax:0", "provision:0"])
1561 logger.info("Setting up self join")
1562 setup_self_join(samdb, admin_session_info, names=names, fill=fill,
1563 invocationid=invocationid,
1564 dns_backend=dns_backend,
1566 machinepass=machinepass,
1567 domainsid=names.domainsid,
1570 policyguid=policyguid,
1571 policyguid_dc=policyguid_dc,
1572 domainControllerFunctionality=domainControllerFunctionality,
1575 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1576 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1577 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE).decode('utf8')
1578 assert isinstance(names.ntdsguid, string_types)
1583 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1584 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)"
1585 SYSVOL_SERVICE = "sysvol"
1588 def set_dir_acl(path, acl, lp, domsid, use_ntvfs, passdb, service=SYSVOL_SERVICE):
1589 setntacl(lp, path, acl, domsid, use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1590 for root, dirs, files in os.walk(path, topdown=False):
1592 setntacl(lp, os.path.join(root, name), acl, domsid,
1593 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1595 setntacl(lp, os.path.join(root, name), acl, domsid,
1596 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1599 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb):
1600 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1603 :param sysvol: Physical path for the sysvol folder
1604 :param dnsdomain: The DNS name of the domain
1605 :param domainsid: The SID of the domain
1606 :param domaindn: The DN of the domain (ie. DC=...)
1607 :param samdb: An LDB object on the SAM db
1608 :param lp: an LP object
1611 # Set ACL for GPO root folder
1612 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1613 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid),
1614 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=SYSVOL_SERVICE)
1616 res = samdb.search(base="CN=Policies,CN=System,%s" %(domaindn),
1617 attrs=["cn", "nTSecurityDescriptor"],
1618 expression="", scope=ldb.SCOPE_ONELEVEL)
1621 acl = ndr_unpack(security.descriptor,
1622 policy["nTSecurityDescriptor"][0]).as_sddl()
1623 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1624 set_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1625 str(domainsid), use_ntvfs,
1629 def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain,
1630 domaindn, lp, use_ntvfs):
1631 """Set the ACL for the sysvol share and the subfolders
1633 :param samdb: An LDB object on the SAM db
1634 :param netlogon: Physical path for the netlogon folder
1635 :param sysvol: Physical path for the sysvol folder
1636 :param uid: The UID of the "Administrator" user
1637 :param gid: The GID of the "Domain adminstrators" group
1638 :param domainsid: The SID of the domain
1639 :param dnsdomain: The DNS name of the domain
1640 :param domaindn: The DN of the domain (ie. DC=...)
1645 s3conf = s3param.get_context()
1646 s3conf.load(lp.configfile)
1648 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(sysvol))
1651 smbd.set_simple_acl(file.name, 0o755, gid)
1653 if not smbd.have_posix_acls():
1654 # This clue is only strictly correct for RPM and
1655 # Debian-like Linux systems, but hopefully other users
1656 # will get enough clue from it.
1657 raise ProvisioningError("Samba was compiled without the posix ACL support that s3fs requires. "
1658 "Try installing libacl1-dev or libacl-devel, then re-run configure and make.")
1660 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. "
1661 "Try the mounting the filesystem with the 'acl' option.")
1663 smbd.chown(file.name, uid, gid)
1665 raise ProvisioningError("Unable to chown a file on your filesystem. "
1666 "You may not be running provision as root.")
1670 # This will ensure that the smbd code we are running when setting ACLs
1671 # is initialised with the smb.conf
1672 s3conf = s3param.get_context()
1673 s3conf.load(lp.configfile)
1674 # ensure we are using the right samba_dsdb passdb backend, no matter what
1675 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1676 passdb.reload_static_pdb()
1678 # ensure that we init the samba_dsdb backend, so the domain sid is
1679 # marked in secrets.tdb
1680 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1682 # now ensure everything matches correctly, to avoid wierd issues
1683 if passdb.get_global_sam_sid() != domainsid:
1684 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))
1686 domain_info = s4_passdb.domain_info()
1687 if domain_info["dom_sid"] != domainsid:
1688 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))
1690 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1691 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()))
1695 os.chown(sysvol, -1, gid)
1701 # use admin sid dn as user dn, since admin should own most of the files,
1702 # the operation will be much faster
1703 userdn = '<SID={}-{}>'.format(domainsid, security.DOMAIN_RID_ADMINISTRATOR)
1705 flags = (auth.AUTH_SESSION_INFO_DEFAULT_GROUPS |
1706 auth.AUTH_SESSION_INFO_AUTHENTICATED |
1707 auth.AUTH_SESSION_INFO_SIMPLE_PRIVILEGES)
1709 session_info = auth.user_session(samdb, lp_ctx=lp, dn=userdn,
1710 session_info_flags=flags)
1712 def _setntacl(path):
1713 """A helper to reuse args"""
1715 lp, path, SYSVOL_ACL, str(domainsid),
1716 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=s4_passdb,
1717 service=SYSVOL_SERVICE, session_info=session_info)
1719 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1721 for root, dirs, files in os.walk(sysvol, topdown=False):
1723 if use_ntvfs and canchown:
1724 os.chown(os.path.join(root, name), -1, gid)
1725 _setntacl(os.path.join(root, name))
1727 if use_ntvfs and canchown:
1728 os.chown(os.path.join(root, name), -1, gid)
1729 _setntacl(os.path.join(root, name))
1731 # Set acls on Policy folder and policies folders
1732 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb=s4_passdb)
1735 def acl_type(direct_db_access):
1736 if direct_db_access:
1742 def check_dir_acl(path, acl, lp, domainsid, direct_db_access):
1743 fsacl = getntacl(lp, path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1744 fsacl_sddl = fsacl.as_sddl(domainsid)
1745 if fsacl_sddl != acl:
1746 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))
1748 for root, dirs, files in os.walk(path, topdown=False):
1750 fsacl = getntacl(lp, os.path.join(root, name),
1751 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1753 raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1754 fsacl_sddl = fsacl.as_sddl(domainsid)
1755 if fsacl_sddl != acl:
1756 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))
1759 fsacl = getntacl(lp, os.path.join(root, name),
1760 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1762 raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1763 fsacl_sddl = fsacl.as_sddl(domainsid)
1764 if fsacl_sddl != acl:
1765 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))
1768 def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1770 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1773 :param sysvol: Physical path for the sysvol folder
1774 :param dnsdomain: The DNS name of the domain
1775 :param domainsid: The SID of the domain
1776 :param domaindn: The DN of the domain (ie. DC=...)
1777 :param samdb: An LDB object on the SAM db
1778 :param lp: an LP object
1781 # Set ACL for GPO root folder
1782 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1783 fsacl = getntacl(lp, root_policy_path,
1784 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1786 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access), root_policy_path))
1787 fsacl_sddl = fsacl.as_sddl(domainsid)
1788 if fsacl_sddl != POLICIES_ACL:
1789 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))
1790 res = samdb.search(base="CN=Policies,CN=System,%s" %(domaindn),
1791 attrs=["cn", "nTSecurityDescriptor"],
1792 expression="", scope=ldb.SCOPE_ONELEVEL)
1795 acl = ndr_unpack(security.descriptor,
1796 policy["nTSecurityDescriptor"][0]).as_sddl()
1797 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1798 check_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1799 domainsid, direct_db_access)
1802 def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn,
1804 """Set the ACL for the sysvol share and the subfolders
1806 :param samdb: An LDB object on the SAM db
1807 :param netlogon: Physical path for the netlogon folder
1808 :param sysvol: Physical path for the sysvol folder
1809 :param uid: The UID of the "Administrator" user
1810 :param gid: The GID of the "Domain adminstrators" group
1811 :param domainsid: The SID of the domain
1812 :param dnsdomain: The DNS name of the domain
1813 :param domaindn: The DN of the domain (ie. DC=...)
1816 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1817 s3conf = s3param.get_context()
1818 s3conf.load(lp.configfile)
1819 # ensure we are using the right samba_dsdb passdb backend, no matter what
1820 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1821 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1822 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1824 # now ensure everything matches correctly, to avoid wierd issues
1825 if passdb.get_global_sam_sid() != domainsid:
1826 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))
1828 domain_info = s4_passdb.domain_info()
1829 if domain_info["dom_sid"] != domainsid:
1830 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))
1832 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1833 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()))
1835 # Ensure we can read this directly, and via the smbd VFS
1836 for direct_db_access in [True, False]:
1837 # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1838 for dir_path in [os.path.join(sysvol, dnsdomain), netlogon]:
1839 fsacl = getntacl(lp, dir_path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1841 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access), dir_path))
1842 fsacl_sddl = fsacl.as_sddl(domainsid)
1843 if fsacl_sddl != SYSVOL_ACL:
1844 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))
1846 # Check acls on Policy folder and policies folders
1847 check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1851 def interface_ips_v4(lp):
1852 """return only IPv4 IPs"""
1853 ips = samba.interface_ips(lp, False)
1856 if i.find(':') == -1:
1861 def interface_ips_v6(lp):
1862 """return only IPv6 IPs"""
1863 ips = samba.interface_ips(lp, False)
1866 if i.find(':') != -1:
1871 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1873 targetdir=None, samdb_fill=FILL_FULL,
1874 hostip=None, hostip6=None,
1875 next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1876 domainguid=None, policyguid=None, policyguid_dc=None,
1877 invocationid=None, machinepass=None, ntdsguid=None,
1878 dns_backend=None, dnspass=None,
1879 serverrole=None, dom_for_fun_level=None,
1880 am_rodc=False, lp=None, use_ntvfs=False,
1881 skip_sysvolacl=False, backend_store=None):
1882 # create/adapt the group policy GUIDs
1883 # Default GUID for default policy are described at
1884 # "How Core Group Policy Works"
1885 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1886 if policyguid is None:
1887 policyguid = DEFAULT_POLICY_GUID
1888 policyguid = policyguid.upper()
1889 if policyguid_dc is None:
1890 policyguid_dc = DEFAULT_DC_POLICY_GUID
1891 policyguid_dc = policyguid_dc.upper()
1893 if invocationid is None:
1894 invocationid = str(uuid.uuid4())
1896 if krbtgtpass is None:
1897 krbtgtpass = samba.generate_random_machine_password(128, 255)
1898 if machinepass is None:
1899 machinepass = samba.generate_random_machine_password(128, 255)
1901 dnspass = samba.generate_random_password(128, 255)
1903 samdb.transaction_start()
1905 samdb = fill_samdb(samdb, lp, names, logger=logger,
1907 policyguid=policyguid, policyguid_dc=policyguid_dc,
1908 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1909 invocationid=invocationid, machinepass=machinepass,
1910 dns_backend=dns_backend, dnspass=dnspass,
1911 ntdsguid=ntdsguid, serverrole=serverrole,
1912 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1913 next_rid=next_rid, dc_rid=dc_rid,
1914 backend_store=backend_store)
1916 # Set up group policies (domain policy and domain controller
1918 if serverrole == "active directory domain controller":
1919 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1922 samdb.transaction_cancel()
1925 samdb.transaction_commit()
1927 if serverrole == "active directory domain controller":
1928 # Continue setting up sysvol for GPO. This appears to require being
1929 # outside a transaction.
1930 if not skip_sysvolacl:
1931 setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.root_uid,
1932 paths.root_gid, names.domainsid, names.dnsdomain,
1933 names.domaindn, lp, use_ntvfs)
1935 logger.info("Setting acl on sysvol skipped")
1937 secretsdb_self_join(secrets_ldb, domain=names.domain,
1938 realm=names.realm, dnsdomain=names.dnsdomain,
1939 netbiosname=names.netbiosname, domainsid=names.domainsid,
1940 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1942 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1943 # In future, this might be determined from some configuration
1944 kerberos_enctypes = str(ENC_ALL_TYPES)
1947 msg = ldb.Message(ldb.Dn(samdb,
1948 samdb.searchone("distinguishedName",
1949 expression="samAccountName=%s$" % names.netbiosname,
1950 scope=ldb.SCOPE_SUBTREE).decode('utf8')))
1951 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1952 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1953 name="msDS-SupportedEncryptionTypes")
1955 except ldb.LdbError as e:
1956 (enum, estr) = e.args
1957 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1958 # It might be that this attribute does not exist in this schema
1961 setup_ad_dns(samdb, secrets_ldb, names, paths, lp, logger,
1962 hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
1963 dnspass=dnspass, os_level=dom_for_fun_level,
1964 targetdir=targetdir, fill_level=samdb_fill,
1965 backend_store=backend_store)
1967 domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1968 attribute="objectGUID").decode('utf8')
1969 assert isinstance(domainguid, string_types)
1971 lastProvisionUSNs = get_last_provision_usn(samdb)
1972 maxUSN = get_max_usn(samdb, str(names.rootdn))
1973 if lastProvisionUSNs is not None:
1974 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1976 set_provision_usn(samdb, 0, maxUSN, invocationid)
1978 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1979 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1980 {'NTDSGUID': names.ntdsguid})
1982 # fix any dangling GUIDs from the provision
1983 logger.info("Fixing provision GUIDs")
1984 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
1986 samdb.transaction_start()
1988 # a small number of GUIDs are missing because of ordering issues in the
1990 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1991 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1992 scope=ldb.SCOPE_BASE,
1993 attrs=['defaultObjectCategory'])
1994 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1995 scope=ldb.SCOPE_ONELEVEL,
1996 attrs=['ipsecOwnersReference',
1997 'ipsecFilterReference',
1998 'ipsecISAKMPReference',
1999 'ipsecNegotiationPolicyReference',
2000 'ipsecNFAReference'])
2001 if chk.check_database(DN=names.schemadn, scope=ldb.SCOPE_SUBTREE,
2002 attrs=['attributeId', 'governsId']) != 0:
2003 raise ProvisioningError("Duplicate attributeId or governsId in schema. Must be fixed manually!!")
2005 samdb.transaction_cancel()
2008 samdb.transaction_commit()
2012 "ROLE_STANDALONE": "standalone server",
2013 "ROLE_DOMAIN_MEMBER": "member server",
2014 "ROLE_DOMAIN_BDC": "active directory domain controller",
2015 "ROLE_DOMAIN_PDC": "active directory domain controller",
2016 "dc": "active directory domain controller",
2017 "member": "member server",
2018 "domain controller": "active directory domain controller",
2019 "active directory domain controller": "active directory domain controller",
2020 "member server": "member server",
2021 "standalone": "standalone server",
2022 "standalone server": "standalone server",
2026 def sanitize_server_role(role):
2027 """Sanitize a server role name.
2029 :param role: Server role
2030 :raise ValueError: If the role can not be interpreted
2031 :return: Sanitized server role (one of "member server",
2032 "active directory domain controller", "standalone server")
2035 return _ROLES_MAP[role]
2037 raise ValueError(role)
2040 def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain,
2042 """Create AD entries for the fake ypserver.
2044 This is needed for being able to manipulate posix attrs via ADUC.
2046 samdb.transaction_start()
2048 logger.info("Setting up fake yp server settings")
2049 setup_add_ldif(samdb, setup_path("ypServ30.ldif"), {
2050 "DOMAINDN": domaindn,
2051 "NETBIOSNAME": netbiosname,
2052 "NISDOMAIN": nisdomain,
2055 samdb.transaction_cancel()
2058 samdb.transaction_commit()
2061 def directory_create_or_exists(path, mode=0o755):
2062 if not os.path.exists(path):
2064 os.mkdir(path, mode)
2065 except OSError as e:
2066 if e.errno in [errno.EEXIST]:
2069 raise ProvisioningError("Failed to create directory %s: %s" % (path, e.strerror))
2072 def determine_host_ip(logger, lp, hostip=None):
2074 logger.info("Looking up IPv4 addresses")
2075 hostips = interface_ips_v4(lp)
2076 if len(hostips) > 0:
2078 if len(hostips) > 1:
2079 logger.warning("More than one IPv4 address found. Using %s",
2081 if hostip == "127.0.0.1":
2084 logger.warning("No IPv4 address will be assigned")
2089 def determine_host_ip6(logger, lp, hostip6=None):
2091 logger.info("Looking up IPv6 addresses")
2092 hostips = interface_ips_v6(lp)
2094 hostip6 = hostips[0]
2095 if len(hostips) > 1:
2096 logger.warning("More than one IPv6 address found. Using %s", hostip6)
2098 logger.warning("No IPv6 address will be assigned")
2103 def provision(logger, session_info, smbconf=None,
2104 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
2105 domaindn=None, schemadn=None, configdn=None, serverdn=None,
2106 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
2107 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None,
2108 krbtgtpass=None, domainguid=None, policyguid=None, policyguid_dc=None,
2109 dns_backend=None, dns_forwarder=None, dnspass=None,
2110 invocationid=None, machinepass=None, ntdsguid=None,
2111 root=None, nobody=None, users=None, backup=None, aci=None,
2112 serverrole=None, dom_for_fun_level=None, backend_type=None,
2113 sitename=None, ol_mmr_urls=None, ol_olc=None, slapd_path=None,
2114 useeadb=False, am_rodc=False, lp=None, use_ntvfs=False,
2115 use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True,
2116 ldap_backend_forced_uri=None, nosync=False, ldap_dryrun_mode=False,
2117 ldap_backend_extra_port=None, base_schema=None,
2118 plaintext_secrets=False, backend_store=None):
2121 :note: caution, this wipes all existing data!
2125 serverrole = sanitize_server_role(serverrole)
2127 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole)
2129 if ldapadminpass is None:
2130 # Make a new, random password between Samba and it's LDAP server
2131 ldapadminpass = samba.generate_random_password(128, 255)
2133 if backend_type is None:
2134 backend_type = "ldb"
2135 if backend_store is None:
2136 backend_store = get_default_backend_store()
2138 if domainsid is None:
2139 domainsid = security.random_sid()
2141 root_uid = findnss_uid([root or "root"])
2142 nobody_uid = findnss_uid([nobody or "nobody"])
2143 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
2144 root_gid = pwd.getpwuid(root_uid).pw_gid
2147 bind_gid = findnss_gid(["bind", "named"])
2151 if targetdir is not None:
2152 smbconf = os.path.join(targetdir, "etc", "smb.conf")
2153 elif smbconf is None:
2154 smbconf = samba.param.default_path()
2155 if not os.path.exists(os.path.dirname(smbconf)):
2156 os.makedirs(os.path.dirname(smbconf))
2158 server_services = []
2161 global_param["idmap_ldb:use rfc2307"] = ["yes"]
2163 if dns_backend != "SAMBA_INTERNAL":
2164 server_services.append("-dns")
2166 if dns_forwarder is not None:
2167 global_param["dns forwarder"] = [dns_forwarder]
2170 server_services.append("+smb")
2171 server_services.append("-s3fs")
2172 global_param["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
2174 if len(server_services) > 0:
2175 global_param["server services"] = server_services
2177 # only install a new smb.conf if there isn't one there already
2178 if os.path.exists(smbconf):
2179 # if Samba Team members can't figure out the weird errors
2180 # loading an empty smb.conf gives, then we need to be smarter.
2181 # Pretend it just didn't exist --abartlet
2182 f = open(smbconf, 'r')
2184 data = f.read().lstrip()
2187 if data is None or data == "":
2188 make_smbconf(smbconf, hostname, domain, realm,
2189 targetdir, serverrole=serverrole,
2190 eadb=useeadb, use_ntvfs=use_ntvfs,
2191 lp=lp, global_param=global_param)
2193 make_smbconf(smbconf, hostname, domain, realm, targetdir,
2194 serverrole=serverrole,
2195 eadb=useeadb, use_ntvfs=use_ntvfs, lp=lp, global_param=global_param)
2198 lp = samba.param.LoadParm()
2200 names = guess_names(lp=lp, hostname=hostname, domain=domain,
2201 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
2202 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
2203 sitename=sitename, rootdn=rootdn, domain_names_forced=(samdb_fill == FILL_DRS))
2204 paths = provision_paths_from_lp(lp, names.dnsdomain)
2206 paths.bind_gid = bind_gid
2207 paths.root_uid = root_uid
2208 paths.root_gid = root_gid
2210 hostip = determine_host_ip(logger, lp, hostip)
2211 hostip6 = determine_host_ip6(logger, lp, hostip6)
2212 names.hostip = hostip
2213 names.hostip6 = hostip6
2214 names.domainguid = domainguid
2215 names.domainsid = domainsid
2216 names.forestsid = domainsid
2218 if serverrole is None:
2219 serverrole = lp.get("server role")
2221 directory_create_or_exists(paths.private_dir, 0o700)
2222 directory_create_or_exists(paths.binddns_dir, 0o770)
2223 directory_create_or_exists(os.path.join(paths.private_dir, "tls"))
2224 directory_create_or_exists(paths.state_dir)
2225 if not plaintext_secrets:
2226 setup_encrypted_secrets_key(paths.encrypted_secrets_key_path)
2228 if paths.sysvol and not os.path.exists(paths.sysvol):
2229 os.makedirs(paths.sysvol, 0o775)
2231 ldapi_url = "ldapi://%s" % urllib_quote(paths.s4_ldapi_path, safe="")
2233 schema = Schema(domainsid, invocationid=invocationid,
2234 schemadn=names.schemadn, base_schema=base_schema)
2236 if backend_type == "ldb":
2237 provision_backend = LDBBackend(backend_type, paths=paths,
2239 names=names, logger=logger)
2240 elif backend_type == "existing":
2241 # If support for this is ever added back, then the URI will need to be
2243 provision_backend = ExistingBackend(backend_type, paths=paths,
2245 names=names, logger=logger,
2246 ldap_backend_forced_uri=ldap_backend_forced_uri)
2247 elif backend_type == "fedora-ds":
2248 provision_backend = FDSBackend(backend_type, paths=paths,
2250 names=names, logger=logger, domainsid=domainsid,
2251 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2252 slapd_path=slapd_path,
2254 elif backend_type == "openldap":
2255 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
2257 names=names, logger=logger, domainsid=domainsid,
2258 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2259 slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls,
2260 ldap_backend_extra_port=ldap_backend_extra_port,
2261 ldap_dryrun_mode=ldap_dryrun_mode, nosync=nosync,
2262 ldap_backend_forced_uri=ldap_backend_forced_uri)
2264 raise ValueError("Unknown LDAP backend type selected")
2266 provision_backend.init()
2267 provision_backend.start()
2269 # only install a new shares config db if there is none
2270 if not os.path.exists(paths.shareconf):
2271 logger.info("Setting up share.ldb")
2272 share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
2273 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
2275 logger.info("Setting up secrets.ldb")
2276 secrets_ldb = setup_secretsdb(paths,
2277 session_info=session_info,
2278 backend_credentials=provision_backend.credentials, lp=lp)
2281 logger.info("Setting up the registry")
2282 setup_registry(paths.hklm, session_info, lp=lp)
2284 logger.info("Setting up the privileges database")
2285 setup_privileges(paths.privilege, session_info, lp=lp)
2287 logger.info("Setting up idmap db")
2288 idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
2290 setup_name_mappings(idmap, sid=str(domainsid),
2291 root_uid=root_uid, nobody_uid=nobody_uid,
2292 users_gid=users_gid, root_gid=root_gid)
2294 logger.info("Setting up SAM db")
2295 samdb = setup_samdb(paths.samdb, session_info,
2296 provision_backend, lp, names, logger=logger,
2297 serverrole=serverrole,
2298 schema=schema, fill=samdb_fill, am_rodc=am_rodc,
2299 plaintext_secrets=plaintext_secrets,
2300 backend_store=backend_store)
2302 if serverrole == "active directory domain controller":
2303 if paths.netlogon is None:
2304 raise MissingShareError("netlogon", paths.smbconf)
2306 if paths.sysvol is None:
2307 raise MissingShareError("sysvol", paths.smbconf)
2309 if not os.path.isdir(paths.netlogon):
2310 os.makedirs(paths.netlogon, 0o755)
2312 if adminpass is None:
2313 adminpass = samba.generate_random_password(12, 32)
2314 adminpass_generated = True
2316 if isinstance(adminpass, binary_type):
2317 adminpass = adminpass.decode('utf-8')
2318 adminpass_generated = False
2320 if samdb_fill == FILL_FULL:
2321 provision_fill(samdb, secrets_ldb, logger, names, paths,
2322 schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
2323 hostip=hostip, hostip6=hostip6,
2324 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
2325 krbtgtpass=krbtgtpass,
2326 policyguid=policyguid, policyguid_dc=policyguid_dc,
2327 invocationid=invocationid, machinepass=machinepass,
2328 ntdsguid=ntdsguid, dns_backend=dns_backend,
2329 dnspass=dnspass, serverrole=serverrole,
2330 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
2331 lp=lp, use_ntvfs=use_ntvfs,
2332 skip_sysvolacl=skip_sysvolacl,
2333 backend_store=backend_store)
2335 if not is_heimdal_built():
2336 create_kdc_conf(paths.kdcconf, realm, domain, os.path.dirname(lp.get("log file")))
2337 logger.info("The Kerberos KDC configuration for Samba AD is "
2338 "located at %s", paths.kdcconf)
2340 create_krb5_conf(paths.krb5conf,
2341 dnsdomain=names.dnsdomain, hostname=names.hostname,
2343 logger.info("A Kerberos configuration suitable for Samba AD has been "
2344 "generated at %s", paths.krb5conf)
2345 logger.info("Merge the contents of this file with your system "
2346 "krb5.conf or replace it with this one. Do not create a "
2349 if serverrole == "active directory domain controller":
2350 create_dns_update_list(lp, logger, paths)
2352 backend_result = provision_backend.post_setup()
2353 provision_backend.shutdown()
2356 secrets_ldb.transaction_cancel()
2359 # Now commit the secrets.ldb to disk
2360 secrets_ldb.transaction_commit()
2362 # the commit creates the dns.keytab in the private directory
2363 private_dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
2364 bind_dns_keytab_path = os.path.join(paths.binddns_dir, paths.dns_keytab)
2366 if os.path.isfile(private_dns_keytab_path):
2367 if os.path.isfile(bind_dns_keytab_path):
2369 os.unlink(bind_dns_keytab_path)
2370 except OSError as e:
2371 logger.error("Failed to remove %s: %s" %
2372 (bind_dns_keytab_path, e.strerror))
2374 # link the dns.keytab to the bind-dns directory
2376 os.link(private_dns_keytab_path, bind_dns_keytab_path)
2377 except OSError as e:
2378 logger.error("Failed to create link %s -> %s: %s" %
2379 (private_dns_keytab_path, bind_dns_keytab_path, e.strerror))
2381 # chown the dns.keytab in the bind-dns directory
2382 if paths.bind_gid is not None:
2384 os.chmod(paths.binddns_dir, 0o770)
2385 os.chown(paths.binddns_dir, -1, paths.bind_gid)
2387 if 'SAMBA_SELFTEST' not in os.environ:
2388 logger.info("Failed to chown %s to bind gid %u",
2389 paths.binddns_dir, paths.bind_gid)
2392 os.chmod(bind_dns_keytab_path, 0o640)
2393 os.chown(bind_dns_keytab_path, -1, paths.bind_gid)
2395 if 'SAMBA_SELFTEST' not in os.environ:
2396 logger.info("Failed to chown %s to bind gid %u",
2397 bind_dns_keytab_path, paths.bind_gid)
2399 result = ProvisionResult()
2400 result.server_role = serverrole
2401 result.domaindn = domaindn
2402 result.paths = paths
2403 result.names = names
2405 result.samdb = samdb
2406 result.idmap = idmap
2407 result.domainsid = str(domainsid)
2409 if samdb_fill == FILL_FULL:
2410 result.adminpass_generated = adminpass_generated
2411 result.adminpass = adminpass
2413 result.adminpass_generated = False
2414 result.adminpass = None
2416 result.backend_result = backend_result
2419 provision_fake_ypserver(logger=logger, samdb=samdb,
2420 domaindn=names.domaindn, netbiosname=names.netbiosname,
2421 nisdomain=names.domain.lower(), maxuid=maxuid, maxgid=maxgid)
2426 def provision_become_dc(smbconf=None, targetdir=None,
2427 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
2428 serverdn=None, domain=None, hostname=None, domainsid=None,
2429 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
2430 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
2431 dns_backend=None, root=None, nobody=None, users=None,
2432 backup=None, serverrole=None, ldap_backend=None,
2433 ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False):
2435 logger = logging.getLogger("provision")
2436 samba.set_debug_level(debuglevel)
2438 res = provision(logger, system_session(),
2439 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
2440 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
2441 configdn=configdn, serverdn=serverdn, domain=domain,
2442 hostname=hostname, hostip=None, domainsid=domainsid,
2443 machinepass=machinepass,
2444 serverrole="active directory domain controller",
2445 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass,
2446 use_ntvfs=use_ntvfs)
2447 res.lp.set("debuglevel", str(debuglevel))
2451 def create_krb5_conf(path, dnsdomain, hostname, realm):
2452 """Write out a file containing a valid krb5.conf file
2454 :param path: Path of the new krb5.conf file.
2455 :param dnsdomain: DNS Domain name
2456 :param hostname: Local hostname
2457 :param realm: Realm name
2459 setup_file(setup_path("krb5.conf"), path, {
2460 "DNSDOMAIN": dnsdomain,
2461 "HOSTNAME": hostname,
2466 class ProvisioningError(Exception):
2467 """A generic provision error."""
2469 def __init__(self, value):
2473 return "ProvisioningError: " + self.value
2476 class InvalidNetbiosName(Exception):
2477 """A specified name was not a valid NetBIOS name."""
2479 def __init__(self, name):
2480 super(InvalidNetbiosName, self).__init__(
2481 "The name '%r' is not a valid NetBIOS name" % name)
2484 class MissingShareError(ProvisioningError):
2486 def __init__(self, name, smbconf):
2487 super(MissingShareError, self).__init__(
2488 "Existing smb.conf does not have a [%s] share, but you are "
2489 "configuring a DC. Please remove %s or add the share manually." %