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 base64 import b64encode
47 from samba.auth import system_session, admin_session
49 from samba import auth
50 from samba.samba3 import smbd, passdb
51 from samba.samba3 import param as s3param
52 from samba.dsdb import DS_DOMAIN_FUNCTION_2000
56 check_all_substituted,
57 is_valid_netbios_char,
64 from samba.dcerpc import security, misc
65 from samba.dcerpc.misc import (
69 from samba.dsdb import (
70 DS_DOMAIN_FUNCTION_2003,
71 DS_DOMAIN_FUNCTION_2008_R2,
74 from samba.idmap import IDmapDB
75 from samba.ms_display_specifiers import read_ms_ldif
76 from samba.ntacls import setntacl, getntacl, dsacl2fsacl
77 from samba.ndr import ndr_pack, ndr_unpack
78 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 = current[0]["configurationNamingContext"][0]
222 names.schemadn = 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=current[0]["defaultNamingContext"][0]
231 names.rootdn=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)):
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," + 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 res9[0]["type"][0] == "ID_TYPE_BOTH":
309 names.root_gid = 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(';', e):
368 e = "%s;%s" % (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] = ldb.MessageElement(tab,
375 ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE)
376 entry = samdb.search(expression='provisionnerID=*',
377 base="@PROVISION", scope=ldb.SCOPE_BASE,
378 attrs=["provisionnerID"])
379 if len(entry) == 0 or len(entry[0]) == 0:
380 delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
384 def set_provision_usn(samdb, low, high, id):
385 """Set the field provisionUSN in sam.ldb
386 This field is used to track range of USN modified by provision and
388 This value is used afterward by next provision to figure out if
389 the field have been modified since last provision.
391 :param samdb: An LDB object connect to sam.ldb
392 :param low: The lowest USN modified by this upgrade
393 :param high: The highest USN modified by this upgrade
394 :param id: The invocationId of the provision"""
397 tab.append("%s-%s;%s" % (low, high, id))
399 delta = ldb.Message()
400 delta.dn = ldb.Dn(samdb, "@PROVISION")
401 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
402 ldb.FLAG_MOD_ADD, LAST_PROVISION_USN_ATTRIBUTE)
406 def get_max_usn(samdb,basedn):
407 """ This function return the biggest USN present in the provision
409 :param samdb: A LDB object pointing to the sam.ldb
410 :param basedn: A string containing the base DN of the provision
412 :return: The biggest USN in the provision"""
414 res = samdb.search(expression="objectClass=*",base=basedn,
415 scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
416 controls=["search_options:1:2",
417 "server_sort:1:1:uSNChanged",
418 "paged_results:1:1"])
419 return res[0]["uSNChanged"]
422 def get_last_provision_usn(sam):
423 """Get USNs ranges modified by a provision or an upgradeprovision
425 :param sam: An LDB object pointing to the sam.ldb
426 :return: a dictionary which keys are invocation id and values are an array
427 of integer representing the different ranges
430 entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE,
431 base="@PROVISION", scope=ldb.SCOPE_BASE,
432 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
433 except ldb.LdbError as e1:
434 (ecode, emsg) = e1.args
435 if ecode == ldb.ERR_NO_SUCH_OBJECT:
442 if entry[0].get("provisionnerID"):
443 for e in entry[0]["provisionnerID"]:
445 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
446 tab1 = str(r).split(';')
451 if (len(myids) > 0 and id not in myids):
453 tab2 = p.split(tab1[0])
454 if range.get(id) is None:
456 range[id].append(tab2[0])
457 range[id].append(tab2[1])
463 class ProvisionResult(object):
464 """Result of a provision.
466 :ivar server_role: The server role
467 :ivar paths: ProvisionPaths instance
468 :ivar domaindn: The domain dn, as string
472 self.server_role = None
479 self.domainsid = None
480 self.adminpass_generated = None
481 self.adminpass = None
482 self.backend_result = None
484 def report_logger(self, logger):
485 """Report this provision result to a logger."""
487 "Once the above files are installed, your Samba AD server will "
489 if self.adminpass_generated:
490 logger.info("Admin password: %s", self.adminpass)
491 logger.info("Server Role: %s", self.server_role)
492 logger.info("Hostname: %s", self.names.hostname)
493 logger.info("NetBIOS Domain: %s", self.names.domain)
494 logger.info("DNS Domain: %s", self.names.dnsdomain)
495 logger.info("DOMAIN SID: %s", self.domainsid)
497 if self.backend_result:
498 self.backend_result.report_logger(logger)
501 def check_install(lp, session_info, credentials):
502 """Check whether the current install seems ok.
504 :param lp: Loadparm context
505 :param session_info: Session information
506 :param credentials: Credentials
508 if lp.get("realm") == "":
509 raise Exception("Realm empty")
510 samdb = Ldb(lp.samdb_url(), session_info=session_info,
511 credentials=credentials, lp=lp)
512 if len(samdb.search("(cn=Administrator)")) != 1:
513 raise ProvisioningError("No administrator account found")
516 def findnss(nssfn, names):
517 """Find a user or group from a list of possibilities.
519 :param nssfn: NSS Function to try (should raise KeyError if not found)
520 :param names: Names to check.
521 :return: Value return by first names list.
528 raise KeyError("Unable to find user/group in %r" % names)
531 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
532 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
535 def provision_paths_from_lp(lp, dnsdomain):
536 """Set the default paths for provisioning.
538 :param lp: Loadparm context.
539 :param dnsdomain: DNS Domain name
541 paths = ProvisionPaths()
542 paths.private_dir = lp.get("private dir")
543 paths.binddns_dir = lp.get("binddns dir")
544 paths.state_dir = lp.get("state directory")
546 # This is stored without path prefix for the "privateKeytab" attribute in
547 # "secrets_dns.ldif".
548 paths.dns_keytab = "dns.keytab"
549 paths.keytab = "secrets.keytab"
551 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
552 paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
553 paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
554 paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
555 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
556 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
557 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
558 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
559 paths.kdcconf = os.path.join(paths.private_dir, "kdc.conf")
560 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
561 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
562 paths.encrypted_secrets_key_path = os.path.join(
564 "encrypted_secrets.key")
566 paths.dns = os.path.join(paths.binddns_dir, "dns", dnsdomain + ".zone")
567 paths.namedconf = os.path.join(paths.binddns_dir, "named.conf")
568 paths.namedconf_update = os.path.join(paths.binddns_dir, "named.conf.update")
569 paths.namedtxt = os.path.join(paths.binddns_dir, "named.txt")
571 paths.hklm = "hklm.ldb"
572 paths.hkcr = "hkcr.ldb"
573 paths.hkcu = "hkcu.ldb"
574 paths.hku = "hku.ldb"
575 paths.hkpd = "hkpd.ldb"
576 paths.hkpt = "hkpt.ldb"
577 paths.sysvol = lp.get("path", "sysvol")
578 paths.netlogon = lp.get("path", "netlogon")
579 paths.smbconf = lp.configfile
583 def determine_netbios_name(hostname):
584 """Determine a netbios name from a hostname."""
585 # remove forbidden chars and force the length to be <16
586 netbiosname = "".join([x for x in hostname if is_valid_netbios_char(x)])
587 return netbiosname[:MAX_NETBIOS_NAME_LEN].upper()
590 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
591 serverrole=None, rootdn=None, domaindn=None, configdn=None,
592 schemadn=None, serverdn=None, sitename=None,
593 domain_names_forced=False):
594 """Guess configuration settings to use."""
597 hostname = socket.gethostname().split(".")[0]
599 netbiosname = lp.get("netbios name")
600 if netbiosname is None:
601 netbiosname = determine_netbios_name(hostname)
602 netbiosname = netbiosname.upper()
603 if not valid_netbios_name(netbiosname):
604 raise InvalidNetbiosName(netbiosname)
606 if dnsdomain is None:
607 dnsdomain = lp.get("realm")
608 if dnsdomain is None or dnsdomain == "":
609 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
611 dnsdomain = dnsdomain.lower()
613 if serverrole is None:
614 serverrole = lp.get("server role")
615 if serverrole is None:
616 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
618 serverrole = serverrole.lower()
620 realm = dnsdomain.upper()
622 if lp.get("realm") == "":
623 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
625 if lp.get("realm").upper() != realm:
626 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))
628 if lp.get("server role").lower() != serverrole:
629 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))
631 if serverrole == "active directory domain controller":
633 # This will, for better or worse, default to 'WORKGROUP'
634 domain = lp.get("workgroup")
635 domain = domain.upper()
637 if lp.get("workgroup").upper() != domain:
638 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))
641 domaindn = samba.dn_from_dns_name(dnsdomain)
643 if domain == netbiosname:
644 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
648 domaindn = "DC=" + netbiosname
650 if not valid_netbios_name(domain):
651 raise InvalidNetbiosName(domain)
653 if hostname.upper() == realm:
654 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
655 if netbiosname.upper() == realm:
656 raise ProvisioningError("guess_names: Realm '%s' must not be equal to NetBIOS hostname '%s'!" % (realm, netbiosname))
657 if domain == realm and not domain_names_forced:
658 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
660 if serverrole != "active directory domain controller":
662 # This is the code path for a domain member
663 # where we provision the database as if we where
664 # on a domain controller, so we should not use
665 # the same dnsdomain as the domain controllers
666 # of our primary domain.
668 # This will be important if we start doing
669 # SID/name filtering and reject the local
670 # sid and names if they come from a domain
674 dnsdomain = netbiosname.lower()
680 configdn = "CN=Configuration," + rootdn
682 schemadn = "CN=Schema," + configdn
685 sitename = DEFAULTSITE
687 names = ProvisionNames()
688 names.rootdn = rootdn
689 names.domaindn = domaindn
690 names.configdn = configdn
691 names.schemadn = schemadn
692 names.ldapmanagerdn = "CN=Manager," + rootdn
693 names.dnsdomain = dnsdomain
694 names.domain = domain
696 names.netbiosname = netbiosname
697 names.hostname = hostname
698 names.sitename = sitename
699 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
700 netbiosname, sitename, configdn)
704 def make_smbconf(smbconf, hostname, domain, realm, targetdir,
705 serverrole=None, eadb=False, use_ntvfs=False, lp=None,
707 """Create a new smb.conf file based on a couple of basic settings.
709 assert smbconf is not None
712 hostname = socket.gethostname().split(".")[0]
714 netbiosname = determine_netbios_name(hostname)
716 if serverrole is None:
717 serverrole = "standalone server"
719 assert domain is not None
720 domain = domain.upper()
722 assert realm is not None
723 realm = realm.upper()
726 "netbios name": netbiosname,
729 "server role": serverrole,
733 lp = samba.param.LoadParm()
734 #Load non-existent file
735 if os.path.exists(smbconf):
738 if global_param is not None:
739 for ent in global_param:
740 if global_param[ent] is not None:
741 global_settings[ent] = " ".join(global_param[ent])
743 if targetdir is not None:
744 global_settings["private dir"] = os.path.abspath(os.path.join(targetdir, "private"))
745 global_settings["lock dir"] = os.path.abspath(targetdir)
746 global_settings["state directory"] = os.path.abspath(os.path.join(targetdir, "state"))
747 global_settings["cache directory"] = os.path.abspath(os.path.join(targetdir, "cache"))
748 global_settings["binddns dir"] = os.path.abspath(os.path.join(targetdir, "bind-dns"))
750 lp.set("lock dir", os.path.abspath(targetdir))
751 lp.set("state directory", global_settings["state directory"])
752 lp.set("cache directory", global_settings["cache directory"])
753 lp.set("binddns dir", global_settings["binddns dir"])
756 if use_ntvfs and not lp.get("posix:eadb"):
757 if targetdir is not None:
758 privdir = os.path.join(targetdir, "private")
760 privdir = lp.get("private dir")
761 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
762 elif not use_ntvfs and not lp.get("xattr_tdb:file"):
763 if targetdir is not None:
764 statedir = os.path.join(targetdir, "state")
766 statedir = lp.get("state directory")
767 lp.set("xattr_tdb:file", os.path.abspath(os.path.join(statedir, "xattr.tdb")))
770 if serverrole == "active directory domain controller":
771 shares["sysvol"] = os.path.join(lp.get("state directory"), "sysvol")
772 shares["netlogon"] = os.path.join(shares["sysvol"], realm.lower(),
775 global_settings["passdb backend"] = "samba_dsdb"
777 f = open(smbconf, 'w')
779 f.write("[globals]\n")
780 for key, val in global_settings.items():
781 f.write("\t%s = %s\n" % (key, val))
784 for name, path in shares.items():
785 f.write("[%s]\n" % name)
786 f.write("\tpath = %s\n" % path)
787 f.write("\tread only = no\n")
791 # reload the smb.conf
794 # and dump it without any values that are the default
795 # this ensures that any smb.conf parameters that were set
796 # on the provision/join command line are set in the resulting smb.conf
797 lp.dump(False, smbconf)
800 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
801 users_gid, root_gid):
802 """setup reasonable name mappings for sam names to unix names.
804 :param samdb: SamDB object.
805 :param idmap: IDmap db object.
806 :param sid: The domain sid.
807 :param domaindn: The domain DN.
808 :param root_uid: uid of the UNIX root user.
809 :param nobody_uid: uid of the UNIX nobody user.
810 :param users_gid: gid of the UNIX users group.
811 :param root_gid: gid of the UNIX root group.
813 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
815 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
816 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
819 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
820 provision_backend, names, serverrole,
821 erase=False, plaintext_secrets=False,
823 """Setup the partitions for the SAM database.
825 Alternatively, provision() may call this, and then populate the database.
827 :note: This will wipe the Sam Database!
829 :note: This function always removes the local SAM LDB file. The erase
830 parameter controls whether to erase the existing data, which
831 may not be stored locally but in LDAP.
834 assert session_info is not None
836 # We use options=["modules:"] to stop the modules loading - we
837 # just want to wipe and re-initialise the database, not start it up
840 os.unlink(samdb_path)
844 samdb = Ldb(url=samdb_path, session_info=session_info,
845 lp=lp, options=["modules:"])
847 ldap_backend_line = "# No LDAP backend"
848 if provision_backend.type != "ldb":
849 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
851 required_features = None
852 if not plaintext_secrets:
853 required_features = "requiredFeatures: encryptedSecrets"
855 if backend_store is None:
856 backend_store = get_default_backend_store()
857 backend_store_line = "backendStore: %s" % backend_store
859 if backend_store == "mdb":
860 if required_features is not None:
861 required_features += "\n"
863 required_features = ""
864 required_features += "requiredFeatures: lmdbLevelOne"
866 if required_features is None:
867 required_features = "# No required features"
869 samdb.transaction_start()
871 logger.info("Setting up sam.ldb partitions and settings")
872 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
873 "LDAP_BACKEND_LINE": ldap_backend_line,
874 "BACKEND_STORE": backend_store_line
878 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
879 "BACKEND_TYPE": provision_backend.type,
880 "SERVER_ROLE": serverrole,
881 "REQUIRED_FEATURES": required_features
884 logger.info("Setting up sam.ldb rootDSE")
885 setup_samdb_rootdse(samdb, names)
887 samdb.transaction_cancel()
890 samdb.transaction_commit()
893 def secretsdb_self_join(secretsdb, domain,
894 netbiosname, machinepass, domainsid=None,
895 realm=None, dnsdomain=None,
897 key_version_number=1,
898 secure_channel_type=SEC_CHAN_WKSTA):
899 """Add domain join-specific bits to a secrets database.
901 :param secretsdb: Ldb Handle to the secrets database
902 :param machinepass: Machine password
904 attrs = ["whenChanged",
911 if realm is not None:
912 if dnsdomain is None:
913 dnsdomain = realm.lower()
914 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
917 shortname = netbiosname.lower()
919 # We don't need to set msg["flatname"] here, because rdn_name will handle
920 # it, and it causes problems for modifies anyway
921 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
922 msg["secureChannelType"] = [str(secure_channel_type)]
923 msg["objectClass"] = ["top", "primaryDomain"]
924 if dnsname is not None:
925 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
926 msg["realm"] = [realm]
927 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
928 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
929 msg["privateKeytab"] = ["secrets.keytab"]
931 msg["secret"] = [machinepass.encode('utf-8')]
932 msg["samAccountName"] = ["%s$" % netbiosname]
933 msg["secureChannelType"] = [str(secure_channel_type)]
934 if domainsid is not None:
935 msg["objectSid"] = [ndr_pack(domainsid)]
937 # This complex expression tries to ensure that we don't have more
938 # than one record for this SID, realm or netbios domain at a time,
939 # but we don't delete the old record that we are about to modify,
940 # because that would delete the keytab and previous password.
941 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
942 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
943 scope=ldb.SCOPE_ONELEVEL)
946 secretsdb.delete(del_msg.dn)
948 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
951 msg["priorSecret"] = [res[0]["secret"][0]]
953 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
958 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
963 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
969 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
970 secretsdb.modify(msg)
971 secretsdb.rename(res[0].dn, msg.dn)
973 spn = [ 'HOST/%s' % shortname ]
974 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
975 # we are a domain controller then we add servicePrincipalName
976 # entries for the keytab code to update.
977 spn.extend([ 'HOST/%s' % dnsname ])
978 msg["servicePrincipalName"] = spn
983 def setup_secretsdb(paths, session_info, backend_credentials, lp):
984 """Setup the secrets database.
986 :note: This function does not handle exceptions and transaction on purpose,
987 it's up to the caller to do this job.
989 :param path: Path to the secrets database.
990 :param session_info: Session info.
991 :param credentials: Credentials
992 :param lp: Loadparm context
993 :return: LDB handle for the created secrets database
995 if os.path.exists(paths.secrets):
996 os.unlink(paths.secrets)
998 keytab_path = os.path.join(paths.private_dir, paths.keytab)
999 if os.path.exists(keytab_path):
1000 os.unlink(keytab_path)
1002 bind_dns_keytab_path = os.path.join(paths.binddns_dir, paths.dns_keytab)
1003 if os.path.exists(bind_dns_keytab_path):
1004 os.unlink(bind_dns_keytab_path)
1006 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1007 if os.path.exists(dns_keytab_path):
1008 os.unlink(dns_keytab_path)
1010 path = paths.secrets
1012 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
1014 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
1015 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
1016 secrets_ldb.transaction_start()
1018 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
1020 if (backend_credentials is not None and
1021 backend_credentials.authentication_requested()):
1022 if backend_credentials.get_bind_dn() is not None:
1023 setup_add_ldif(secrets_ldb,
1024 setup_path("secrets_simple_ldap.ldif"), {
1025 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
1026 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password()).decode('utf8')
1029 setup_add_ldif(secrets_ldb,
1030 setup_path("secrets_sasl_ldap.ldif"), {
1031 "LDAPADMINUSER": backend_credentials.get_username(),
1032 "LDAPADMINREALM": backend_credentials.get_realm(),
1033 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password()).decode('utf8')
1036 secrets_ldb.transaction_cancel()
1041 def setup_privileges(path, session_info, lp):
1042 """Setup the privileges database.
1044 :param path: Path to the privileges database.
1045 :param session_info: Session info.
1046 :param credentials: Credentials
1047 :param lp: Loadparm context
1048 :return: LDB handle for the created secrets database
1050 if os.path.exists(path):
1052 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
1053 privilege_ldb.erase()
1054 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
1056 def setup_encrypted_secrets_key(path):
1057 """Setup the encrypted secrets key file.
1059 Any existing key file will be deleted and a new random key generated.
1061 :param path: Path to the secrets key file.
1064 if os.path.exists(path):
1067 flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
1068 mode = stat.S_IRUSR | stat.S_IWUSR
1070 umask_original = os.umask(0)
1072 fd = os.open(path, flags, mode)
1074 os.umask(umask_original)
1076 with os.fdopen(fd, 'w') as f:
1077 key = samba.generate_random_bytes(16)
1081 def setup_registry(path, session_info, lp):
1082 """Setup the registry.
1084 :param path: Path to the registry database
1085 :param session_info: Session information
1086 :param credentials: Credentials
1087 :param lp: Loadparm context
1089 reg = samba.registry.Registry()
1090 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
1091 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
1092 provision_reg = setup_path("provision.reg")
1093 assert os.path.exists(provision_reg)
1094 reg.diff_apply(provision_reg)
1097 def setup_idmapdb(path, session_info, lp):
1098 """Setup the idmap database.
1100 :param path: path to the idmap database
1101 :param session_info: Session information
1102 :param credentials: Credentials
1103 :param lp: Loadparm context
1105 if os.path.exists(path):
1108 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
1110 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
1114 def setup_samdb_rootdse(samdb, names):
1115 """Setup the SamDB rootdse.
1117 :param samdb: Sam Database handle
1119 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
1120 "SCHEMADN": names.schemadn,
1121 "DOMAINDN": names.domaindn,
1122 "ROOTDN" : names.rootdn,
1123 "CONFIGDN": names.configdn,
1124 "SERVERDN": names.serverdn,
1128 def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
1129 dns_backend, dnspass, domainsid, next_rid, invocationid,
1130 policyguid, policyguid_dc,
1131 domainControllerFunctionality, ntdsguid=None, dc_rid=None):
1132 """Join a host to its own domain."""
1133 assert isinstance(invocationid, str)
1134 if ntdsguid is not None:
1135 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
1142 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
1143 "CONFIGDN": names.configdn,
1144 "SCHEMADN": names.schemadn,
1145 "DOMAINDN": names.domaindn,
1146 "SERVERDN": names.serverdn,
1147 "INVOCATIONID": invocationid,
1148 "NETBIOSNAME": names.netbiosname,
1149 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1150 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')).decode('utf8'),
1151 "DOMAINSID": str(domainsid),
1152 "DCRID": str(dc_rid),
1153 "SAMBA_VERSION_STRING": version,
1154 "NTDSGUID": ntdsguid_line,
1155 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1156 domainControllerFunctionality),
1157 "RIDALLOCATIONSTART": str(next_rid + 100),
1158 "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
1160 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1161 "POLICYGUID": policyguid,
1162 "POLICYGUID_DC": policyguid_dc,
1163 "DNSDOMAIN": names.dnsdomain,
1164 "DOMAINDN": names.domaindn})
1166 # If we are setting up a subdomain, then this has been replicated in, so we
1167 # don't need to add it
1168 if fill == FILL_FULL:
1169 setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
1170 "CONFIGDN": names.configdn,
1171 "SCHEMADN": names.schemadn,
1172 "DOMAINDN": names.domaindn,
1173 "SERVERDN": names.serverdn,
1174 "INVOCATIONID": invocationid,
1175 "NETBIOSNAME": names.netbiosname,
1176 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1177 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')).decode('utf8'),
1178 "DOMAINSID": str(domainsid),
1179 "DCRID": str(dc_rid),
1180 "SAMBA_VERSION_STRING": version,
1181 "NTDSGUID": ntdsguid_line,
1182 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1183 domainControllerFunctionality)})
1185 # Setup fSMORoleOwner entries to point at the newly created DC entry
1186 setup_modify_ldif(samdb,
1187 setup_path("provision_self_join_modify_config.ldif"), {
1188 "CONFIGDN": names.configdn,
1189 "SCHEMADN": names.schemadn,
1190 "DEFAULTSITE": names.sitename,
1191 "NETBIOSNAME": names.netbiosname,
1192 "SERVERDN": names.serverdn,
1195 system_session_info = system_session()
1196 samdb.set_session_info(system_session_info)
1197 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1198 # modify a serverReference under cn=config when we are a subdomain, we must
1199 # be system due to ACLs
1200 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1201 "DOMAINDN": names.domaindn,
1202 "SERVERDN": names.serverdn,
1203 "NETBIOSNAME": names.netbiosname,
1206 samdb.set_session_info(admin_session_info)
1208 if dns_backend != "SAMBA_INTERNAL":
1209 # This is Samba4 specific and should be replaced by the correct
1210 # DNS AD-style setup
1211 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1212 "DNSDOMAIN": names.dnsdomain,
1213 "DOMAINDN": names.domaindn,
1214 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')).decode('utf8'),
1215 "HOSTNAME" : names.hostname,
1216 "DNSNAME" : '%s.%s' % (
1217 names.netbiosname.lower(), names.dnsdomain.lower())
1221 def getpolicypath(sysvolpath, dnsdomain, guid):
1222 """Return the physical path of policy given its guid.
1224 :param sysvolpath: Path to the sysvol folder
1225 :param dnsdomain: DNS name of the AD domain
1226 :param guid: The GUID of the policy
1227 :return: A string with the complete path to the policy folder
1230 guid = "{%s}" % guid
1231 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1235 def create_gpo_struct(policy_path):
1236 if not os.path.exists(policy_path):
1237 os.makedirs(policy_path, 0o775)
1238 f = open(os.path.join(policy_path, "GPT.INI"), 'w')
1240 f.write("[General]\r\nVersion=0")
1243 p = os.path.join(policy_path, "MACHINE")
1244 if not os.path.exists(p):
1245 os.makedirs(p, 0o775)
1246 p = os.path.join(policy_path, "USER")
1247 if not os.path.exists(p):
1248 os.makedirs(p, 0o775)
1251 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1252 """Create the default GPO for a domain
1254 :param sysvolpath: Physical path for the sysvol folder
1255 :param dnsdomain: DNS domain name of the AD domain
1256 :param policyguid: GUID of the default domain policy
1257 :param policyguid_dc: GUID of the default domain controler policy
1259 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1260 create_gpo_struct(policy_path)
1262 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1263 create_gpo_struct(policy_path)
1266 def setup_samdb(path, session_info, provision_backend, lp, names,
1267 logger, fill, serverrole, schema, am_rodc=False,
1268 plaintext_secrets=False, backend_store=None):
1269 """Setup a complete SAM Database.
1271 :note: This will wipe the main SAM database file!
1274 # Also wipes the database
1275 setup_samdb_partitions(path, logger=logger, lp=lp,
1276 provision_backend=provision_backend, session_info=session_info,
1277 names=names, serverrole=serverrole, plaintext_secrets=plaintext_secrets,
1278 backend_store=backend_store)
1280 # Load the database, but don's load the global schema and don't connect
1282 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1283 credentials=provision_backend.credentials, lp=lp,
1284 global_schema=False, am_rodc=am_rodc)
1286 logger.info("Pre-loading the Samba 4 and AD schema")
1288 # Load the schema from the one we computed earlier
1289 samdb.set_schema(schema, write_indices_and_attributes=False)
1291 # Set the NTDS settings DN manually - in order to have it already around
1292 # before the provisioned tree exists and we connect
1293 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1295 # And now we can connect to the DB - the schema won't be loaded from the
1299 except ldb.LdbError as e2:
1300 (num, string_error) = e2.args
1301 if (num == ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS):
1302 raise ProvisioningError("Permission denied connecting to %s, are you running as root?" % path)
1306 # But we have to give it one more kick to have it use the schema
1307 # during provision - it needs, now that it is connected, to write
1308 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1309 samdb.set_schema(schema, write_indices_and_attributes=True)
1314 def fill_samdb(samdb, lp, names, logger, policyguid,
1315 policyguid_dc, fill, adminpass, krbtgtpass, machinepass, dns_backend,
1316 dnspass, invocationid, ntdsguid, serverrole, am_rodc=False,
1317 dom_for_fun_level=None, schema=None, next_rid=None, dc_rid=None,
1318 backend_store=None):
1320 if next_rid is None:
1323 # Provision does not make much sense values larger than 1000000000
1324 # as the upper range of the rIDAvailablePool is 1073741823 and
1325 # we don't want to create a domain that cannot allocate rids.
1326 if next_rid < 1000 or next_rid > 1000000000:
1327 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1328 error += "the valid range is %u-%u. The default is %u." % (
1329 1000, 1000000000, 1000)
1330 raise ProvisioningError(error)
1332 # ATTENTION: Do NOT change these default values without discussion with the
1333 # team and/or release manager. They have a big impact on the whole program!
1334 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1336 if dom_for_fun_level is None:
1337 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
1339 if dom_for_fun_level > domainControllerFunctionality:
1340 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!")
1342 domainFunctionality = dom_for_fun_level
1343 forestFunctionality = dom_for_fun_level
1345 # Set the NTDS settings DN manually - in order to have it already around
1346 # before the provisioned tree exists and we connect
1347 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1349 # Set the domain functionality levels onto the database.
1350 # Various module (the password_hash module in particular) need
1351 # to know what level of AD we are emulating.
1353 # These will be fixed into the database via the database
1354 # modifictions below, but we need them set from the start.
1355 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1356 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1357 samdb.set_opaque_integer("domainControllerFunctionality",
1358 domainControllerFunctionality)
1360 samdb.set_domain_sid(str(names.domainsid))
1361 samdb.set_invocation_id(invocationid)
1363 logger.info("Adding DomainDN: %s" % names.domaindn)
1365 # impersonate domain admin
1366 admin_session_info = admin_session(lp, str(names.domainsid))
1367 samdb.set_session_info(admin_session_info)
1368 if names.domainguid is not None:
1369 domainguid_line = "objectGUID: %s\n-" % names.domainguid
1371 domainguid_line = ""
1373 descr = b64encode(get_domain_descriptor(names.domainsid)).decode('utf8')
1374 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1375 "DOMAINDN": names.domaindn,
1376 "DOMAINSID": str(names.domainsid),
1377 "DESCRIPTOR": descr,
1378 "DOMAINGUID": domainguid_line
1381 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1382 "DOMAINDN": names.domaindn,
1383 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1384 "NEXTRID": str(next_rid),
1385 "DEFAULTSITE": names.sitename,
1386 "CONFIGDN": names.configdn,
1387 "POLICYGUID": policyguid,
1388 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1389 "SAMBA_VERSION_STRING": version,
1390 "MIN_PWD_LENGTH": str(DEFAULT_MIN_PWD_LENGTH)
1393 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1394 if fill == FILL_FULL:
1395 logger.info("Adding configuration container")
1396 descr = b64encode(get_config_descriptor(names.domainsid)).decode('utf8')
1397 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1398 "CONFIGDN": names.configdn,
1399 "DESCRIPTOR": descr,
1402 # The LDIF here was created when the Schema object was constructed
1403 ignore_checks_oid = "local_oid:%s:0" % samba.dsdb.DSDB_CONTROL_SKIP_DUPLICATES_CHECK_OID
1404 logger.info("Setting up sam.ldb schema")
1405 samdb.add_ldif(schema.schema_dn_add,
1406 controls=["relax:0", ignore_checks_oid])
1407 samdb.modify_ldif(schema.schema_dn_modify,
1408 controls=[ignore_checks_oid])
1409 samdb.write_prefixes_from_schema()
1410 samdb.add_ldif(schema.schema_data, controls=["relax:0", ignore_checks_oid])
1411 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1412 {"SCHEMADN": names.schemadn},
1413 controls=["relax:0", ignore_checks_oid])
1415 # Now register this container in the root of the forest
1416 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1417 msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1420 samdb.invocation_id = invocationid
1422 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1423 if fill == FILL_FULL:
1424 logger.info("Setting up sam.ldb configuration data")
1426 partitions_descr = b64encode(get_config_partitions_descriptor(names.domainsid)).decode('utf8')
1427 sites_descr = b64encode(get_config_sites_descriptor(names.domainsid)).decode('utf8')
1428 ntdsquotas_descr = b64encode(get_config_ntds_quotas_descriptor(names.domainsid)).decode('utf8')
1429 protected1_descr = b64encode(get_config_delete_protected1_descriptor(names.domainsid)).decode('utf8')
1430 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid)).decode('utf8')
1431 protected2_descr = b64encode(get_config_delete_protected2_descriptor(names.domainsid)).decode('utf8')
1433 if "2008" in schema.base_schema:
1434 # exclude 2012-specific changes if we're using a 2008 schema
1439 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1440 "CONFIGDN": names.configdn,
1441 "NETBIOSNAME": names.netbiosname,
1442 "DEFAULTSITE": names.sitename,
1443 "DNSDOMAIN": names.dnsdomain,
1444 "DOMAIN": names.domain,
1445 "SCHEMADN": names.schemadn,
1446 "DOMAINDN": names.domaindn,
1447 "SERVERDN": names.serverdn,
1448 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1449 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1450 "NTDSQUOTAS_DESCRIPTOR": ntdsquotas_descr,
1451 "LOSTANDFOUND_DESCRIPTOR": protected1wd_descr,
1452 "SERVICES_DESCRIPTOR": protected1_descr,
1453 "PHYSICALLOCATIONS_DESCRIPTOR": protected1wd_descr,
1454 "FORESTUPDATES_DESCRIPTOR": protected1wd_descr,
1455 "EXTENDEDRIGHTS_DESCRIPTOR": protected2_descr,
1456 "PARTITIONS_DESCRIPTOR": partitions_descr,
1457 "SITES_DESCRIPTOR": sites_descr,
1460 setup_add_ldif(samdb, setup_path("extended-rights.ldif"), {
1461 "CONFIGDN": names.configdn,
1462 "INC2012" : incl_2012,
1465 logger.info("Setting up display specifiers")
1466 display_specifiers_ldif = read_ms_ldif(
1467 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1468 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1469 {"CONFIGDN": names.configdn})
1470 check_all_substituted(display_specifiers_ldif)
1471 samdb.add_ldif(display_specifiers_ldif)
1473 logger.info("Modifying display specifiers and extended rights")
1474 setup_modify_ldif(samdb,
1475 setup_path("provision_configuration_modify.ldif"), {
1476 "CONFIGDN": names.configdn,
1477 "DISPLAYSPECIFIERS_DESCRIPTOR": protected2_descr
1480 logger.info("Adding users container")
1481 users_desc = b64encode(get_domain_users_descriptor(names.domainsid)).decode('utf8')
1482 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1483 "DOMAINDN": names.domaindn,
1484 "USERS_DESCRIPTOR": users_desc
1486 logger.info("Modifying users container")
1487 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1488 "DOMAINDN": names.domaindn})
1489 logger.info("Adding computers container")
1490 computers_desc = b64encode(get_domain_computers_descriptor(names.domainsid)).decode('utf8')
1491 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1492 "DOMAINDN": names.domaindn,
1493 "COMPUTERS_DESCRIPTOR": computers_desc
1495 logger.info("Modifying computers container")
1496 setup_modify_ldif(samdb,
1497 setup_path("provision_computers_modify.ldif"), {
1498 "DOMAINDN": names.domaindn})
1499 logger.info("Setting up sam.ldb data")
1500 infrastructure_desc = b64encode(get_domain_infrastructure_descriptor(names.domainsid)).decode('utf8')
1501 lostandfound_desc = b64encode(get_domain_delete_protected2_descriptor(names.domainsid)).decode('utf8')
1502 system_desc = b64encode(get_domain_delete_protected1_descriptor(names.domainsid)).decode('utf8')
1503 builtin_desc = b64encode(get_domain_builtin_descriptor(names.domainsid)).decode('utf8')
1504 controllers_desc = b64encode(get_domain_controllers_descriptor(names.domainsid)).decode('utf8')
1505 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1506 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1507 "DOMAINDN": names.domaindn,
1508 "NETBIOSNAME": names.netbiosname,
1509 "DEFAULTSITE": names.sitename,
1510 "CONFIGDN": names.configdn,
1511 "SERVERDN": names.serverdn,
1512 "RIDAVAILABLESTART": str(next_rid + 600),
1513 "POLICYGUID_DC": policyguid_dc,
1514 "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc,
1515 "LOSTANDFOUND_DESCRIPTOR": lostandfound_desc,
1516 "SYSTEM_DESCRIPTOR": system_desc,
1517 "BUILTIN_DESCRIPTOR": builtin_desc,
1518 "DOMAIN_CONTROLLERS_DESCRIPTOR": controllers_desc,
1521 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1522 if fill == FILL_FULL:
1523 managedservice_descr = b64encode(get_managed_service_accounts_descriptor(names.domainsid)).decode('utf8')
1524 setup_modify_ldif(samdb,
1525 setup_path("provision_configuration_references.ldif"), {
1526 "CONFIGDN": names.configdn,
1527 "SCHEMADN": names.schemadn})
1529 logger.info("Setting up well known security principals")
1530 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid)).decode('utf8')
1531 setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1532 "CONFIGDN": names.configdn,
1533 "WELLKNOWNPRINCIPALS_DESCRIPTOR": protected1wd_descr,
1534 }, controls=["relax:0", "provision:0"])
1536 if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1537 setup_modify_ldif(samdb,
1538 setup_path("provision_basedn_references.ldif"), {
1539 "DOMAINDN": names.domaindn,
1540 "MANAGEDSERVICE_DESCRIPTOR": managedservice_descr
1543 logger.info("Setting up sam.ldb users and groups")
1544 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1545 "DOMAINDN": names.domaindn,
1546 "DOMAINSID": str(names.domainsid),
1547 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')).decode('utf8'),
1548 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le')).decode('utf8')
1549 }, controls=["relax:0", "provision:0"])
1551 logger.info("Setting up self join")
1552 setup_self_join(samdb, admin_session_info, names=names, fill=fill,
1553 invocationid=invocationid,
1554 dns_backend=dns_backend,
1556 machinepass=machinepass,
1557 domainsid=names.domainsid,
1560 policyguid=policyguid,
1561 policyguid_dc=policyguid_dc,
1562 domainControllerFunctionality=domainControllerFunctionality,
1565 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1566 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1567 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1568 assert isinstance(names.ntdsguid, str)
1573 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1574 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)"
1575 SYSVOL_SERVICE="sysvol"
1577 def set_dir_acl(path, acl, lp, domsid, use_ntvfs, passdb, service=SYSVOL_SERVICE):
1578 setntacl(lp, path, acl, domsid, use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1579 for root, dirs, files in os.walk(path, topdown=False):
1581 setntacl(lp, os.path.join(root, name), acl, domsid,
1582 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1584 setntacl(lp, os.path.join(root, name), acl, domsid,
1585 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1588 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb):
1589 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1592 :param sysvol: Physical path for the sysvol folder
1593 :param dnsdomain: The DNS name of the domain
1594 :param domainsid: The SID of the domain
1595 :param domaindn: The DN of the domain (ie. DC=...)
1596 :param samdb: An LDB object on the SAM db
1597 :param lp: an LP object
1600 # Set ACL for GPO root folder
1601 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1602 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid),
1603 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=SYSVOL_SERVICE)
1605 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1606 attrs=["cn", "nTSecurityDescriptor"],
1607 expression="", scope=ldb.SCOPE_ONELEVEL)
1610 acl = ndr_unpack(security.descriptor,
1611 str(policy["nTSecurityDescriptor"])).as_sddl()
1612 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1613 set_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1614 str(domainsid), use_ntvfs,
1618 def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain,
1619 domaindn, lp, use_ntvfs):
1620 """Set the ACL for the sysvol share and the subfolders
1622 :param samdb: An LDB object on the SAM db
1623 :param netlogon: Physical path for the netlogon folder
1624 :param sysvol: Physical path for the sysvol folder
1625 :param uid: The UID of the "Administrator" user
1626 :param gid: The GID of the "Domain adminstrators" group
1627 :param domainsid: The SID of the domain
1628 :param dnsdomain: The DNS name of the domain
1629 :param domaindn: The DN of the domain (ie. DC=...)
1634 s3conf = s3param.get_context()
1635 s3conf.load(lp.configfile)
1637 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(sysvol))
1640 smbd.set_simple_acl(file.name, 0o755, gid)
1642 if not smbd.have_posix_acls():
1643 # This clue is only strictly correct for RPM and
1644 # Debian-like Linux systems, but hopefully other users
1645 # will get enough clue from it.
1646 raise ProvisioningError("Samba was compiled without the posix ACL support that s3fs requires. "
1647 "Try installing libacl1-dev or libacl-devel, then re-run configure and make.")
1649 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. "
1650 "Try the mounting the filesystem with the 'acl' option.")
1652 smbd.chown(file.name, uid, gid)
1654 raise ProvisioningError("Unable to chown a file on your filesystem. "
1655 "You may not be running provision as root.")
1659 # This will ensure that the smbd code we are running when setting ACLs
1660 # is initialised with the smb.conf
1661 s3conf = s3param.get_context()
1662 s3conf.load(lp.configfile)
1663 # ensure we are using the right samba_dsdb passdb backend, no matter what
1664 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1665 passdb.reload_static_pdb()
1667 # ensure that we init the samba_dsdb backend, so the domain sid is
1668 # marked in secrets.tdb
1669 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1671 # now ensure everything matches correctly, to avoid wierd issues
1672 if passdb.get_global_sam_sid() != domainsid:
1673 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))
1675 domain_info = s4_passdb.domain_info()
1676 if domain_info["dom_sid"] != domainsid:
1677 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))
1679 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1680 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()))
1685 os.chown(sysvol, -1, gid)
1691 # use admin sid dn as user dn, since admin should own most of the files,
1692 # the operation will be much faster
1693 userdn = '<SID={}-{}>'.format(domainsid, security.DOMAIN_RID_ADMINISTRATOR)
1695 flags = (auth.AUTH_SESSION_INFO_DEFAULT_GROUPS |
1696 auth.AUTH_SESSION_INFO_AUTHENTICATED |
1697 auth.AUTH_SESSION_INFO_SIMPLE_PRIVILEGES)
1699 session_info = auth.user_session(samdb, lp_ctx=lp, dn=userdn,
1700 session_info_flags=flags)
1702 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1703 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs,
1704 skip_invalid_chown=True, passdb=s4_passdb,
1705 service=SYSVOL_SERVICE, session_info=session_info)
1706 for root, dirs, files in os.walk(sysvol, topdown=False):
1708 if use_ntvfs and canchown:
1709 os.chown(os.path.join(root, name), -1, gid)
1710 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid),
1711 use_ntvfs=use_ntvfs, skip_invalid_chown=True,
1712 passdb=s4_passdb, service=SYSVOL_SERVICE,
1713 session_info=session_info)
1715 if use_ntvfs and canchown:
1716 os.chown(os.path.join(root, name), -1, gid)
1717 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid),
1718 use_ntvfs=use_ntvfs, skip_invalid_chown=True,
1719 passdb=s4_passdb, service=SYSVOL_SERVICE,
1720 session_info=session_info)
1722 # Set acls on Policy folder and policies folders
1723 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb=s4_passdb)
1725 def acl_type(direct_db_access):
1726 if direct_db_access:
1731 def check_dir_acl(path, acl, lp, domainsid, direct_db_access):
1732 fsacl = getntacl(lp, path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1733 fsacl_sddl = fsacl.as_sddl(domainsid)
1734 if fsacl_sddl != acl:
1735 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))
1737 for root, dirs, files in os.walk(path, topdown=False):
1739 fsacl = getntacl(lp, os.path.join(root, name),
1740 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1742 raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1743 fsacl_sddl = fsacl.as_sddl(domainsid)
1744 if fsacl_sddl != acl:
1745 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))
1748 fsacl = getntacl(lp, os.path.join(root, name),
1749 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1751 raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1752 fsacl_sddl = fsacl.as_sddl(domainsid)
1753 if fsacl_sddl != acl:
1754 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))
1757 def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1759 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1762 :param sysvol: Physical path for the sysvol folder
1763 :param dnsdomain: The DNS name of the domain
1764 :param domainsid: The SID of the domain
1765 :param domaindn: The DN of the domain (ie. DC=...)
1766 :param samdb: An LDB object on the SAM db
1767 :param lp: an LP object
1770 # Set ACL for GPO root folder
1771 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1772 fsacl = getntacl(lp, root_policy_path,
1773 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1775 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access), root_policy_path))
1776 fsacl_sddl = fsacl.as_sddl(domainsid)
1777 if fsacl_sddl != POLICIES_ACL:
1778 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))
1779 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1780 attrs=["cn", "nTSecurityDescriptor"],
1781 expression="", scope=ldb.SCOPE_ONELEVEL)
1784 acl = ndr_unpack(security.descriptor,
1785 str(policy["nTSecurityDescriptor"])).as_sddl()
1786 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1787 check_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1788 domainsid, direct_db_access)
1791 def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn,
1793 """Set the ACL for the sysvol share and the subfolders
1795 :param samdb: An LDB object on the SAM db
1796 :param netlogon: Physical path for the netlogon folder
1797 :param sysvol: Physical path for the sysvol folder
1798 :param uid: The UID of the "Administrator" user
1799 :param gid: The GID of the "Domain adminstrators" group
1800 :param domainsid: The SID of the domain
1801 :param dnsdomain: The DNS name of the domain
1802 :param domaindn: The DN of the domain (ie. DC=...)
1805 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1806 s3conf = s3param.get_context()
1807 s3conf.load(lp.configfile)
1808 # ensure we are using the right samba_dsdb passdb backend, no matter what
1809 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1810 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1811 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1813 # now ensure everything matches correctly, to avoid wierd issues
1814 if passdb.get_global_sam_sid() != domainsid:
1815 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))
1817 domain_info = s4_passdb.domain_info()
1818 if domain_info["dom_sid"] != domainsid:
1819 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))
1821 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1822 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()))
1824 # Ensure we can read this directly, and via the smbd VFS
1825 for direct_db_access in [True, False]:
1826 # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1827 for dir_path in [os.path.join(sysvol, dnsdomain), netlogon]:
1828 fsacl = getntacl(lp, dir_path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1830 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access), dir_path))
1831 fsacl_sddl = fsacl.as_sddl(domainsid)
1832 if fsacl_sddl != SYSVOL_ACL:
1833 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))
1835 # Check acls on Policy folder and policies folders
1836 check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1840 def interface_ips_v4(lp):
1841 """return only IPv4 IPs"""
1842 ips = samba.interface_ips(lp, False)
1845 if i.find(':') == -1:
1850 def interface_ips_v6(lp):
1851 """return only IPv6 IPs"""
1852 ips = samba.interface_ips(lp, False)
1855 if i.find(':') != -1:
1860 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1862 targetdir=None, samdb_fill=FILL_FULL,
1863 hostip=None, hostip6=None,
1864 next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1865 domainguid=None, policyguid=None, policyguid_dc=None,
1866 invocationid=None, machinepass=None, ntdsguid=None,
1867 dns_backend=None, dnspass=None,
1868 serverrole=None, dom_for_fun_level=None,
1869 am_rodc=False, lp=None, use_ntvfs=False,
1870 skip_sysvolacl=False, backend_store=None):
1871 # create/adapt the group policy GUIDs
1872 # Default GUID for default policy are described at
1873 # "How Core Group Policy Works"
1874 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1875 if policyguid is None:
1876 policyguid = DEFAULT_POLICY_GUID
1877 policyguid = policyguid.upper()
1878 if policyguid_dc is None:
1879 policyguid_dc = DEFAULT_DC_POLICY_GUID
1880 policyguid_dc = policyguid_dc.upper()
1882 if invocationid is None:
1883 invocationid = str(uuid.uuid4())
1885 if krbtgtpass is None:
1886 krbtgtpass = samba.generate_random_machine_password(128, 255)
1887 if machinepass is None:
1888 machinepass = samba.generate_random_machine_password(128, 255)
1890 dnspass = samba.generate_random_password(128, 255)
1892 samdb.transaction_start()
1894 samdb = fill_samdb(samdb, lp, names, logger=logger,
1896 policyguid=policyguid, policyguid_dc=policyguid_dc,
1897 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1898 invocationid=invocationid, machinepass=machinepass,
1899 dns_backend=dns_backend, dnspass=dnspass,
1900 ntdsguid=ntdsguid, serverrole=serverrole,
1901 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1902 next_rid=next_rid, dc_rid=dc_rid,
1903 backend_store=backend_store)
1905 # Set up group policies (domain policy and domain controller
1907 if serverrole == "active directory domain controller":
1908 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1911 samdb.transaction_cancel()
1914 samdb.transaction_commit()
1916 if serverrole == "active directory domain controller":
1917 # Continue setting up sysvol for GPO. This appears to require being
1918 # outside a transaction.
1919 if not skip_sysvolacl:
1920 setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.root_uid,
1921 paths.root_gid, names.domainsid, names.dnsdomain,
1922 names.domaindn, lp, use_ntvfs)
1924 logger.info("Setting acl on sysvol skipped")
1926 secretsdb_self_join(secrets_ldb, domain=names.domain,
1927 realm=names.realm, dnsdomain=names.dnsdomain,
1928 netbiosname=names.netbiosname, domainsid=names.domainsid,
1929 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1931 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1932 # In future, this might be determined from some configuration
1933 kerberos_enctypes = str(ENC_ALL_TYPES)
1936 msg = ldb.Message(ldb.Dn(samdb,
1937 samdb.searchone("distinguishedName",
1938 expression="samAccountName=%s$" % names.netbiosname,
1939 scope=ldb.SCOPE_SUBTREE).decode('utf8')))
1940 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1941 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1942 name="msDS-SupportedEncryptionTypes")
1944 except ldb.LdbError as e:
1945 (enum, estr) = e.args
1946 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1947 # It might be that this attribute does not exist in this schema
1950 setup_ad_dns(samdb, secrets_ldb, names, paths, lp, logger,
1951 hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
1952 dnspass=dnspass, os_level=dom_for_fun_level,
1953 targetdir=targetdir, fill_level=samdb_fill,
1954 backend_store=backend_store)
1956 domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1957 attribute="objectGUID")
1958 assert isinstance(domainguid, str)
1960 lastProvisionUSNs = get_last_provision_usn(samdb)
1961 maxUSN = get_max_usn(samdb, str(names.rootdn))
1962 if lastProvisionUSNs is not None:
1963 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1965 set_provision_usn(samdb, 0, maxUSN, invocationid)
1967 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1968 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1969 { 'NTDSGUID' : names.ntdsguid })
1971 # fix any dangling GUIDs from the provision
1972 logger.info("Fixing provision GUIDs")
1973 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
1975 samdb.transaction_start()
1977 # a small number of GUIDs are missing because of ordering issues in the
1979 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1980 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1981 scope=ldb.SCOPE_BASE,
1982 attrs=['defaultObjectCategory'])
1983 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1984 scope=ldb.SCOPE_ONELEVEL,
1985 attrs=['ipsecOwnersReference',
1986 'ipsecFilterReference',
1987 'ipsecISAKMPReference',
1988 'ipsecNegotiationPolicyReference',
1989 'ipsecNFAReference'])
1990 if chk.check_database(DN=names.schemadn, scope=ldb.SCOPE_SUBTREE,
1991 attrs=['attributeId', 'governsId']) != 0:
1992 raise ProvisioningError("Duplicate attributeId or governsId in schema. Must be fixed manually!!")
1994 samdb.transaction_cancel()
1997 samdb.transaction_commit()
2001 "ROLE_STANDALONE": "standalone server",
2002 "ROLE_DOMAIN_MEMBER": "member server",
2003 "ROLE_DOMAIN_BDC": "active directory domain controller",
2004 "ROLE_DOMAIN_PDC": "active directory domain controller",
2005 "dc": "active directory domain controller",
2006 "member": "member server",
2007 "domain controller": "active directory domain controller",
2008 "active directory domain controller": "active directory domain controller",
2009 "member server": "member server",
2010 "standalone": "standalone server",
2011 "standalone server": "standalone server",
2015 def sanitize_server_role(role):
2016 """Sanitize a server role name.
2018 :param role: Server role
2019 :raise ValueError: If the role can not be interpreted
2020 :return: Sanitized server role (one of "member server",
2021 "active directory domain controller", "standalone server")
2024 return _ROLES_MAP[role]
2026 raise ValueError(role)
2029 def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain,
2031 """Create AD entries for the fake ypserver.
2033 This is needed for being able to manipulate posix attrs via ADUC.
2035 samdb.transaction_start()
2037 logger.info("Setting up fake yp server settings")
2038 setup_add_ldif(samdb, setup_path("ypServ30.ldif"), {
2039 "DOMAINDN": domaindn,
2040 "NETBIOSNAME": netbiosname,
2041 "NISDOMAIN": nisdomain,
2044 samdb.transaction_cancel()
2047 samdb.transaction_commit()
2049 def directory_create_or_exists(path, mode=0o755):
2050 if not os.path.exists(path):
2052 os.mkdir(path, mode)
2053 except OSError as e:
2054 if e.errno in [errno.EEXIST]:
2057 raise ProvisioningError("Failed to create directory %s: %s" % (path, e.strerror))
2059 def determine_host_ip(logger, lp, hostip=None):
2061 logger.info("Looking up IPv4 addresses")
2062 hostips = interface_ips_v4(lp)
2063 if len(hostips) > 0:
2065 if len(hostips) > 1:
2066 logger.warning("More than one IPv4 address found. Using %s",
2068 if hostip == "127.0.0.1":
2071 logger.warning("No IPv4 address will be assigned")
2075 def determine_host_ip6(logger, lp, hostip6=None):
2077 logger.info("Looking up IPv6 addresses")
2078 hostips = interface_ips_v6(lp)
2080 hostip6 = hostips[0]
2081 if len(hostips) > 1:
2082 logger.warning("More than one IPv6 address found. Using %s", hostip6)
2084 logger.warning("No IPv6 address will be assigned")
2088 def provision(logger, session_info, smbconf=None,
2089 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
2090 domaindn=None, schemadn=None, configdn=None, serverdn=None,
2091 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
2092 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None,
2093 krbtgtpass=None, domainguid=None, policyguid=None, policyguid_dc=None,
2094 dns_backend=None, dns_forwarder=None, dnspass=None,
2095 invocationid=None, machinepass=None, ntdsguid=None,
2096 root=None, nobody=None, users=None, backup=None, aci=None,
2097 serverrole=None, dom_for_fun_level=None, backend_type=None,
2098 sitename=None, ol_mmr_urls=None, ol_olc=None, slapd_path=None,
2099 useeadb=False, am_rodc=False, lp=None, use_ntvfs=False,
2100 use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True,
2101 ldap_backend_forced_uri=None, nosync=False, ldap_dryrun_mode=False,
2102 ldap_backend_extra_port=None, base_schema=None,
2103 plaintext_secrets=False, backend_store=None):
2106 :note: caution, this wipes all existing data!
2110 serverrole = sanitize_server_role(serverrole)
2112 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole)
2114 if ldapadminpass is None:
2115 # Make a new, random password between Samba and it's LDAP server
2116 ldapadminpass = samba.generate_random_password(128, 255)
2118 if backend_type is None:
2119 backend_type = "ldb"
2120 if backend_store is None:
2121 backend_store = get_default_backend_store()
2123 if domainsid is None:
2124 domainsid = security.random_sid()
2126 root_uid = findnss_uid([root or "root"])
2127 nobody_uid = findnss_uid([nobody or "nobody"])
2128 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
2129 root_gid = pwd.getpwuid(root_uid).pw_gid
2132 bind_gid = findnss_gid(["bind", "named"])
2136 if targetdir is not None:
2137 smbconf = os.path.join(targetdir, "etc", "smb.conf")
2138 elif smbconf is None:
2139 smbconf = samba.param.default_path()
2140 if not os.path.exists(os.path.dirname(smbconf)):
2141 os.makedirs(os.path.dirname(smbconf))
2143 server_services = []
2146 global_param["idmap_ldb:use rfc2307"] = ["yes"]
2148 if dns_backend != "SAMBA_INTERNAL":
2149 server_services.append("-dns")
2151 if dns_forwarder is not None:
2152 global_param["dns forwarder"] = [dns_forwarder]
2155 server_services.append("+smb")
2156 server_services.append("-s3fs")
2157 global_param["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
2159 if len(server_services) > 0:
2160 global_param["server services"] = server_services
2162 # only install a new smb.conf if there isn't one there already
2163 if os.path.exists(smbconf):
2164 # if Samba Team members can't figure out the weird errors
2165 # loading an empty smb.conf gives, then we need to be smarter.
2166 # Pretend it just didn't exist --abartlet
2167 f = open(smbconf, 'r')
2169 data = f.read().lstrip()
2172 if data is None or data == "":
2173 make_smbconf(smbconf, hostname, domain, realm,
2174 targetdir, serverrole=serverrole,
2175 eadb=useeadb, use_ntvfs=use_ntvfs,
2176 lp=lp, global_param=global_param)
2178 make_smbconf(smbconf, hostname, domain, realm, targetdir,
2179 serverrole=serverrole,
2180 eadb=useeadb, use_ntvfs=use_ntvfs, lp=lp, global_param=global_param)
2183 lp = samba.param.LoadParm()
2185 names = guess_names(lp=lp, hostname=hostname, domain=domain,
2186 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
2187 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
2188 sitename=sitename, rootdn=rootdn, domain_names_forced=(samdb_fill == FILL_DRS))
2189 paths = provision_paths_from_lp(lp, names.dnsdomain)
2191 paths.bind_gid = bind_gid
2192 paths.root_uid = root_uid;
2193 paths.root_gid = root_gid
2195 hostip = determine_host_ip(logger, lp, hostip)
2196 hostip6 = determine_host_ip6(logger, lp, hostip6)
2197 names.hostip = hostip
2198 names.hostip6 = hostip6
2199 names.domainguid = domainguid
2200 names.domainsid = domainsid
2201 names.forestsid = domainsid
2203 if serverrole is None:
2204 serverrole = lp.get("server role")
2206 directory_create_or_exists(paths.private_dir, 0o700)
2207 directory_create_or_exists(paths.binddns_dir, 0o770)
2208 directory_create_or_exists(os.path.join(paths.private_dir, "tls"))
2209 directory_create_or_exists(paths.state_dir)
2210 if not plaintext_secrets:
2211 setup_encrypted_secrets_key(paths.encrypted_secrets_key_path)
2213 if paths.sysvol and not os.path.exists(paths.sysvol):
2214 os.makedirs(paths.sysvol, 0o775)
2216 ldapi_url = "ldapi://%s" % urllib_quote(paths.s4_ldapi_path, safe="")
2218 schema = Schema(domainsid, invocationid=invocationid,
2219 schemadn=names.schemadn, base_schema=base_schema)
2221 if backend_type == "ldb":
2222 provision_backend = LDBBackend(backend_type, paths=paths,
2224 names=names, logger=logger)
2225 elif backend_type == "existing":
2226 # If support for this is ever added back, then the URI will need to be
2228 provision_backend = ExistingBackend(backend_type, paths=paths,
2230 names=names, logger=logger,
2231 ldap_backend_forced_uri=ldap_backend_forced_uri)
2232 elif backend_type == "fedora-ds":
2233 provision_backend = FDSBackend(backend_type, paths=paths,
2235 names=names, logger=logger, domainsid=domainsid,
2236 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2237 slapd_path=slapd_path,
2239 elif backend_type == "openldap":
2240 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
2242 names=names, logger=logger, domainsid=domainsid,
2243 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2244 slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls,
2245 ldap_backend_extra_port=ldap_backend_extra_port,
2246 ldap_dryrun_mode=ldap_dryrun_mode, nosync=nosync,
2247 ldap_backend_forced_uri=ldap_backend_forced_uri)
2249 raise ValueError("Unknown LDAP backend type selected")
2251 provision_backend.init()
2252 provision_backend.start()
2254 # only install a new shares config db if there is none
2255 if not os.path.exists(paths.shareconf):
2256 logger.info("Setting up share.ldb")
2257 share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
2258 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
2260 logger.info("Setting up secrets.ldb")
2261 secrets_ldb = setup_secretsdb(paths,
2262 session_info=session_info,
2263 backend_credentials=provision_backend.credentials, lp=lp)
2266 logger.info("Setting up the registry")
2267 setup_registry(paths.hklm, session_info, lp=lp)
2269 logger.info("Setting up the privileges database")
2270 setup_privileges(paths.privilege, session_info, lp=lp)
2272 logger.info("Setting up idmap db")
2273 idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
2275 setup_name_mappings(idmap, sid=str(domainsid),
2276 root_uid=root_uid, nobody_uid=nobody_uid,
2277 users_gid=users_gid, root_gid=root_gid)
2279 logger.info("Setting up SAM db")
2280 samdb = setup_samdb(paths.samdb, session_info,
2281 provision_backend, lp, names, logger=logger,
2282 serverrole=serverrole,
2283 schema=schema, fill=samdb_fill, am_rodc=am_rodc,
2284 plaintext_secrets=plaintext_secrets,
2285 backend_store=backend_store)
2287 if serverrole == "active directory domain controller":
2288 if paths.netlogon is None:
2289 raise MissingShareError("netlogon", paths.smbconf)
2291 if paths.sysvol is None:
2292 raise MissingShareError("sysvol", paths.smbconf)
2294 if not os.path.isdir(paths.netlogon):
2295 os.makedirs(paths.netlogon, 0o755)
2297 if adminpass is None:
2298 adminpass = samba.generate_random_password(12, 32)
2299 adminpass_generated = True
2301 adminpass = unicode(adminpass, 'utf-8')
2302 adminpass_generated = False
2304 if samdb_fill == FILL_FULL:
2305 provision_fill(samdb, secrets_ldb, logger, names, paths,
2306 schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
2307 hostip=hostip, hostip6=hostip6,
2308 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
2309 krbtgtpass=krbtgtpass,
2310 policyguid=policyguid, policyguid_dc=policyguid_dc,
2311 invocationid=invocationid, machinepass=machinepass,
2312 ntdsguid=ntdsguid, dns_backend=dns_backend,
2313 dnspass=dnspass, serverrole=serverrole,
2314 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
2315 lp=lp, use_ntvfs=use_ntvfs,
2316 skip_sysvolacl=skip_sysvolacl,
2317 backend_store=backend_store)
2319 if not is_heimdal_built():
2320 create_kdc_conf(paths.kdcconf, realm, domain, os.path.dirname(lp.get("log file")))
2321 logger.info("The Kerberos KDC configuration for Samba AD is "
2322 "located at %s", paths.kdcconf)
2324 create_krb5_conf(paths.krb5conf,
2325 dnsdomain=names.dnsdomain, hostname=names.hostname,
2327 logger.info("A Kerberos configuration suitable for Samba AD has been "
2328 "generated at %s", paths.krb5conf)
2329 logger.info("Merge the contents of this file with your system "
2330 "krb5.conf or replace it with this one. Do not create a "
2333 if serverrole == "active directory domain controller":
2334 create_dns_update_list(lp, logger, paths)
2336 backend_result = provision_backend.post_setup()
2337 provision_backend.shutdown()
2340 secrets_ldb.transaction_cancel()
2343 # Now commit the secrets.ldb to disk
2344 secrets_ldb.transaction_commit()
2346 # the commit creates the dns.keytab in the private directory
2347 private_dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
2348 bind_dns_keytab_path = os.path.join(paths.binddns_dir, paths.dns_keytab)
2350 if os.path.isfile(private_dns_keytab_path):
2351 if os.path.isfile(bind_dns_keytab_path):
2353 os.unlink(bind_dns_keytab_path)
2354 except OSError as e:
2355 logger.error("Failed to remove %s: %s" %
2356 (bind_dns_keytab_path, e.strerror))
2358 # link the dns.keytab to the bind-dns directory
2360 os.link(private_dns_keytab_path, bind_dns_keytab_path)
2361 except OSError as e:
2362 logger.error("Failed to create link %s -> %s: %s" %
2363 (private_dns_keytab_path, bind_dns_keytab_path, e.strerror))
2365 # chown the dns.keytab in the bind-dns directory
2366 if paths.bind_gid is not None:
2368 os.chmod(paths.binddns_dir, 0o770)
2369 os.chown(paths.binddns_dir, -1, paths.bind_gid)
2371 if not os.environ.has_key('SAMBA_SELFTEST'):
2372 logger.info("Failed to chown %s to bind gid %u",
2373 paths.binddns_dir, paths.bind_gid)
2376 os.chmod(bind_dns_keytab_path, 0o640)
2377 os.chown(bind_dns_keytab_path, -1, paths.bind_gid)
2379 if not os.environ.has_key('SAMBA_SELFTEST'):
2380 logger.info("Failed to chown %s to bind gid %u",
2381 bind_dns_keytab_path, paths.bind_gid)
2383 result = ProvisionResult()
2384 result.server_role = serverrole
2385 result.domaindn = domaindn
2386 result.paths = paths
2387 result.names = names
2389 result.samdb = samdb
2390 result.idmap = idmap
2391 result.domainsid = str(domainsid)
2393 if samdb_fill == FILL_FULL:
2394 result.adminpass_generated = adminpass_generated
2395 result.adminpass = adminpass
2397 result.adminpass_generated = False
2398 result.adminpass = None
2400 result.backend_result = backend_result
2403 provision_fake_ypserver(logger=logger, samdb=samdb,
2404 domaindn=names.domaindn, netbiosname=names.netbiosname,
2405 nisdomain=names.domain.lower(), maxuid=maxuid, maxgid=maxgid)
2410 def provision_become_dc(smbconf=None, targetdir=None,
2411 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
2412 serverdn=None, domain=None, hostname=None, domainsid=None,
2413 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
2414 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
2415 dns_backend=None, root=None, nobody=None, users=None,
2416 backup=None, serverrole=None, ldap_backend=None,
2417 ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False):
2419 logger = logging.getLogger("provision")
2420 samba.set_debug_level(debuglevel)
2422 res = provision(logger, system_session(),
2423 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
2424 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
2425 configdn=configdn, serverdn=serverdn, domain=domain,
2426 hostname=hostname, hostip=None, domainsid=domainsid,
2427 machinepass=machinepass,
2428 serverrole="active directory domain controller",
2429 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass,
2430 use_ntvfs=use_ntvfs)
2431 res.lp.set("debuglevel", str(debuglevel))
2435 def create_krb5_conf(path, dnsdomain, hostname, realm):
2436 """Write out a file containing a valid krb5.conf file
2438 :param path: Path of the new krb5.conf file.
2439 :param dnsdomain: DNS Domain name
2440 :param hostname: Local hostname
2441 :param realm: Realm name
2443 setup_file(setup_path("krb5.conf"), path, {
2444 "DNSDOMAIN": dnsdomain,
2445 "HOSTNAME": hostname,
2450 class ProvisioningError(Exception):
2451 """A generic provision error."""
2453 def __init__(self, value):
2457 return "ProvisioningError: " + self.value
2460 class InvalidNetbiosName(Exception):
2461 """A specified name was not a valid NetBIOS name."""
2463 def __init__(self, name):
2464 super(InvalidNetbiosName, self).__init__(
2465 "The name '%r' is not a valid NetBIOS name" % name)
2468 class MissingShareError(ProvisioningError):
2470 def __init__(self, name, smbconf):
2471 super(MissingShareError, self).__init__(
2472 "Existing smb.conf does not have a [%s] share, but you are "
2473 "configuring a DC. Please remove %s or add the share manually." %