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] = \
375 ldb.MessageElement(tab,
376 ldb.FLAG_MOD_REPLACE,
377 LAST_PROVISION_USN_ATTRIBUTE)
378 entry = samdb.search(expression='provisionnerID=*',
379 base="@PROVISION", scope=ldb.SCOPE_BASE,
380 attrs=["provisionnerID"])
381 if len(entry) == 0 or len(entry[0]) == 0:
382 delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
386 def set_provision_usn(samdb, low, high, id):
387 """Set the field provisionUSN in sam.ldb
388 This field is used to track range of USN modified by provision and
390 This value is used afterward by next provision to figure out if
391 the field have been modified since last provision.
393 :param samdb: An LDB object connect to sam.ldb
394 :param low: The lowest USN modified by this upgrade
395 :param high: The highest USN modified by this upgrade
396 :param id: The invocationId of the provision"""
399 tab.append("%s-%s;%s" % (low, high, id))
401 delta = ldb.Message()
402 delta.dn = ldb.Dn(samdb, "@PROVISION")
403 delta[LAST_PROVISION_USN_ATTRIBUTE] = \
404 ldb.MessageElement(tab,
406 LAST_PROVISION_USN_ATTRIBUTE)
410 def get_max_usn(samdb,basedn):
411 """ This function return the biggest USN present in the provision
413 :param samdb: A LDB object pointing to the sam.ldb
414 :param basedn: A string containing the base DN of the provision
416 :return: The biggest USN in the provision"""
418 res = samdb.search(expression="objectClass=*",base=basedn,
419 scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
420 controls=["search_options:1:2",
421 "server_sort:1:1:uSNChanged",
422 "paged_results:1:1"])
423 return res[0]["uSNChanged"]
426 def get_last_provision_usn(sam):
427 """Get USNs ranges modified by a provision or an upgradeprovision
429 :param sam: An LDB object pointing to the sam.ldb
430 :return: a dictionary which keys are invocation id and values are an array
431 of integer representing the different ranges
434 entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE,
435 base="@PROVISION", scope=ldb.SCOPE_BASE,
436 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
437 except ldb.LdbError as e1:
438 (ecode, emsg) = e1.args
439 if ecode == ldb.ERR_NO_SUCH_OBJECT:
446 if entry[0].get("provisionnerID"):
447 for e in entry[0]["provisionnerID"]:
449 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
450 tab1 = str(r).split(';')
455 if (len(myids) > 0 and id not in myids):
457 tab2 = p.split(tab1[0])
458 if range.get(id) is None:
460 range[id].append(tab2[0])
461 range[id].append(tab2[1])
467 class ProvisionResult(object):
468 """Result of a provision.
470 :ivar server_role: The server role
471 :ivar paths: ProvisionPaths instance
472 :ivar domaindn: The domain dn, as string
476 self.server_role = None
483 self.domainsid = None
484 self.adminpass_generated = None
485 self.adminpass = None
486 self.backend_result = None
488 def report_logger(self, logger):
489 """Report this provision result to a logger."""
491 "Once the above files are installed, your Samba AD server will "
493 if self.adminpass_generated:
494 logger.info("Admin password: %s", self.adminpass)
495 logger.info("Server Role: %s", self.server_role)
496 logger.info("Hostname: %s", self.names.hostname)
497 logger.info("NetBIOS Domain: %s", self.names.domain)
498 logger.info("DNS Domain: %s", self.names.dnsdomain)
499 logger.info("DOMAIN SID: %s", self.domainsid)
501 if self.backend_result:
502 self.backend_result.report_logger(logger)
505 def check_install(lp, session_info, credentials):
506 """Check whether the current install seems ok.
508 :param lp: Loadparm context
509 :param session_info: Session information
510 :param credentials: Credentials
512 if lp.get("realm") == "":
513 raise Exception("Realm empty")
514 samdb = Ldb(lp.samdb_url(), session_info=session_info,
515 credentials=credentials, lp=lp)
516 if len(samdb.search("(cn=Administrator)")) != 1:
517 raise ProvisioningError("No administrator account found")
520 def findnss(nssfn, names):
521 """Find a user or group from a list of possibilities.
523 :param nssfn: NSS Function to try (should raise KeyError if not found)
524 :param names: Names to check.
525 :return: Value return by first names list.
532 raise KeyError("Unable to find user/group in %r" % names)
535 def findnss_uid(names):
536 return findnss(pwd.getpwnam, names)[2]
539 def findnss_gid(names):
540 return findnss(grp.getgrnam, names)[2]
543 def provision_paths_from_lp(lp, dnsdomain):
544 """Set the default paths for provisioning.
546 :param lp: Loadparm context.
547 :param dnsdomain: DNS Domain name
549 paths = ProvisionPaths()
550 paths.private_dir = lp.get("private dir")
551 paths.binddns_dir = lp.get("binddns dir")
552 paths.state_dir = lp.get("state directory")
554 # This is stored without path prefix for the "privateKeytab" attribute in
555 # "secrets_dns.ldif".
556 paths.dns_keytab = "dns.keytab"
557 paths.keytab = "secrets.keytab"
559 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
560 paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
561 paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
562 paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
563 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
564 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
565 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
566 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
567 paths.kdcconf = os.path.join(paths.private_dir, "kdc.conf")
568 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
569 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
570 paths.encrypted_secrets_key_path = os.path.join(
572 "encrypted_secrets.key")
574 paths.dns = os.path.join(paths.binddns_dir, "dns", dnsdomain + ".zone")
575 paths.namedconf = os.path.join(paths.binddns_dir, "named.conf")
576 paths.namedconf_update = os.path.join(paths.binddns_dir, "named.conf.update")
577 paths.namedtxt = os.path.join(paths.binddns_dir, "named.txt")
579 paths.hklm = "hklm.ldb"
580 paths.hkcr = "hkcr.ldb"
581 paths.hkcu = "hkcu.ldb"
582 paths.hku = "hku.ldb"
583 paths.hkpd = "hkpd.ldb"
584 paths.hkpt = "hkpt.ldb"
585 paths.sysvol = lp.get("path", "sysvol")
586 paths.netlogon = lp.get("path", "netlogon")
587 paths.smbconf = lp.configfile
591 def determine_netbios_name(hostname):
592 """Determine a netbios name from a hostname."""
593 # remove forbidden chars and force the length to be <16
594 netbiosname = "".join([x for x in hostname if is_valid_netbios_char(x)])
595 return netbiosname[:MAX_NETBIOS_NAME_LEN].upper()
598 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
599 serverrole=None, rootdn=None, domaindn=None, configdn=None,
600 schemadn=None, serverdn=None, sitename=None,
601 domain_names_forced=False):
602 """Guess configuration settings to use."""
605 hostname = socket.gethostname().split(".")[0]
607 netbiosname = lp.get("netbios name")
608 if netbiosname is None:
609 netbiosname = determine_netbios_name(hostname)
610 netbiosname = netbiosname.upper()
611 if not valid_netbios_name(netbiosname):
612 raise InvalidNetbiosName(netbiosname)
614 if dnsdomain is None:
615 dnsdomain = lp.get("realm")
616 if dnsdomain is None or dnsdomain == "":
617 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
619 dnsdomain = dnsdomain.lower()
621 if serverrole is None:
622 serverrole = lp.get("server role")
623 if serverrole is None:
624 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
626 serverrole = serverrole.lower()
628 realm = dnsdomain.upper()
630 if lp.get("realm") == "":
631 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
633 if lp.get("realm").upper() != realm:
634 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))
636 if lp.get("server role").lower() != serverrole:
637 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))
639 if serverrole == "active directory domain controller":
641 # This will, for better or worse, default to 'WORKGROUP'
642 domain = lp.get("workgroup")
643 domain = domain.upper()
645 if lp.get("workgroup").upper() != domain:
646 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))
649 domaindn = samba.dn_from_dns_name(dnsdomain)
651 if domain == netbiosname:
652 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
656 domaindn = "DC=" + netbiosname
658 if not valid_netbios_name(domain):
659 raise InvalidNetbiosName(domain)
661 if hostname.upper() == realm:
662 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
663 if netbiosname.upper() == realm:
664 raise ProvisioningError("guess_names: Realm '%s' must not be equal to NetBIOS hostname '%s'!" % (realm, netbiosname))
665 if domain == realm and not domain_names_forced:
666 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
668 if serverrole != "active directory domain controller":
670 # This is the code path for a domain member
671 # where we provision the database as if we where
672 # on a domain controller, so we should not use
673 # the same dnsdomain as the domain controllers
674 # of our primary domain.
676 # This will be important if we start doing
677 # SID/name filtering and reject the local
678 # sid and names if they come from a domain
682 dnsdomain = netbiosname.lower()
688 configdn = "CN=Configuration," + rootdn
690 schemadn = "CN=Schema," + configdn
693 sitename = DEFAULTSITE
695 names = ProvisionNames()
696 names.rootdn = rootdn
697 names.domaindn = domaindn
698 names.configdn = configdn
699 names.schemadn = schemadn
700 names.ldapmanagerdn = "CN=Manager," + rootdn
701 names.dnsdomain = dnsdomain
702 names.domain = domain
704 names.netbiosname = netbiosname
705 names.hostname = hostname
706 names.sitename = sitename
707 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
708 netbiosname, sitename, configdn)
712 def make_smbconf(smbconf, hostname, domain, realm, targetdir,
713 serverrole=None, eadb=False, use_ntvfs=False, lp=None,
715 """Create a new smb.conf file based on a couple of basic settings.
717 assert smbconf is not None
720 hostname = socket.gethostname().split(".")[0]
722 netbiosname = determine_netbios_name(hostname)
724 if serverrole is None:
725 serverrole = "standalone server"
727 assert domain is not None
728 domain = domain.upper()
730 assert realm is not None
731 realm = realm.upper()
734 "netbios name": netbiosname,
737 "server role": serverrole,
741 lp = samba.param.LoadParm()
742 #Load non-existent file
743 if os.path.exists(smbconf):
746 if global_param is not None:
747 for ent in global_param:
748 if global_param[ent] is not None:
749 global_settings[ent] = " ".join(global_param[ent])
751 if targetdir is not None:
752 global_settings["private dir"] = os.path.abspath(os.path.join(targetdir, "private"))
753 global_settings["lock dir"] = os.path.abspath(targetdir)
754 global_settings["state directory"] = os.path.abspath(os.path.join(targetdir, "state"))
755 global_settings["cache directory"] = os.path.abspath(os.path.join(targetdir, "cache"))
756 global_settings["binddns dir"] = os.path.abspath(os.path.join(targetdir, "bind-dns"))
758 lp.set("lock dir", os.path.abspath(targetdir))
759 lp.set("state directory", global_settings["state directory"])
760 lp.set("cache directory", global_settings["cache directory"])
761 lp.set("binddns dir", global_settings["binddns dir"])
764 if use_ntvfs and not lp.get("posix:eadb"):
765 if targetdir is not None:
766 privdir = os.path.join(targetdir, "private")
768 privdir = lp.get("private dir")
769 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
770 elif not use_ntvfs and not lp.get("xattr_tdb:file"):
771 if targetdir is not None:
772 statedir = os.path.join(targetdir, "state")
774 statedir = lp.get("state directory")
775 lp.set("xattr_tdb:file", os.path.abspath(os.path.join(statedir, "xattr.tdb")))
778 if serverrole == "active directory domain controller":
779 shares["sysvol"] = os.path.join(lp.get("state directory"), "sysvol")
780 shares["netlogon"] = os.path.join(shares["sysvol"], realm.lower(),
783 global_settings["passdb backend"] = "samba_dsdb"
785 f = open(smbconf, 'w')
787 f.write("[globals]\n")
788 for key, val in global_settings.items():
789 f.write("\t%s = %s\n" % (key, val))
792 for name, path in shares.items():
793 f.write("[%s]\n" % name)
794 f.write("\tpath = %s\n" % path)
795 f.write("\tread only = no\n")
799 # reload the smb.conf
802 # and dump it without any values that are the default
803 # this ensures that any smb.conf parameters that were set
804 # on the provision/join command line are set in the resulting smb.conf
805 lp.dump(False, smbconf)
808 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
809 users_gid, root_gid):
810 """setup reasonable name mappings for sam names to unix names.
812 :param samdb: SamDB object.
813 :param idmap: IDmap db object.
814 :param sid: The domain sid.
815 :param domaindn: The domain DN.
816 :param root_uid: uid of the UNIX root user.
817 :param nobody_uid: uid of the UNIX nobody user.
818 :param users_gid: gid of the UNIX users group.
819 :param root_gid: gid of the UNIX root group.
821 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
823 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
824 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
827 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
828 provision_backend, names, serverrole,
829 erase=False, plaintext_secrets=False,
831 """Setup the partitions for the SAM database.
833 Alternatively, provision() may call this, and then populate the database.
835 :note: This will wipe the Sam Database!
837 :note: This function always removes the local SAM LDB file. The erase
838 parameter controls whether to erase the existing data, which
839 may not be stored locally but in LDAP.
842 assert session_info is not None
844 # We use options=["modules:"] to stop the modules loading - we
845 # just want to wipe and re-initialise the database, not start it up
848 os.unlink(samdb_path)
852 samdb = Ldb(url=samdb_path, session_info=session_info,
853 lp=lp, options=["modules:"])
855 ldap_backend_line = "# No LDAP backend"
856 if provision_backend.type != "ldb":
857 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
859 required_features = None
860 if not plaintext_secrets:
861 required_features = "requiredFeatures: encryptedSecrets"
863 if backend_store is None:
864 backend_store = get_default_backend_store()
865 backend_store_line = "backendStore: %s" % backend_store
867 if backend_store == "mdb":
868 if required_features is not None:
869 required_features += "\n"
871 required_features = ""
872 required_features += "requiredFeatures: lmdbLevelOne"
874 if required_features is None:
875 required_features = "# No required features"
877 samdb.transaction_start()
879 logger.info("Setting up sam.ldb partitions and settings")
880 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
881 "LDAP_BACKEND_LINE": ldap_backend_line,
882 "BACKEND_STORE": backend_store_line
886 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
887 "BACKEND_TYPE": provision_backend.type,
888 "SERVER_ROLE": serverrole,
889 "REQUIRED_FEATURES": required_features
892 logger.info("Setting up sam.ldb rootDSE")
893 setup_samdb_rootdse(samdb, names)
895 samdb.transaction_cancel()
898 samdb.transaction_commit()
901 def secretsdb_self_join(secretsdb, domain,
902 netbiosname, machinepass, domainsid=None,
903 realm=None, dnsdomain=None,
905 key_version_number=1,
906 secure_channel_type=SEC_CHAN_WKSTA):
907 """Add domain join-specific bits to a secrets database.
909 :param secretsdb: Ldb Handle to the secrets database
910 :param machinepass: Machine password
912 attrs = ["whenChanged",
919 if realm is not None:
920 if dnsdomain is None:
921 dnsdomain = realm.lower()
922 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
925 shortname = netbiosname.lower()
927 # We don't need to set msg["flatname"] here, because rdn_name will handle
928 # it, and it causes problems for modifies anyway
929 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
930 msg["secureChannelType"] = [str(secure_channel_type)]
931 msg["objectClass"] = ["top", "primaryDomain"]
932 if dnsname is not None:
933 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
934 msg["realm"] = [realm]
935 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
936 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
937 msg["privateKeytab"] = ["secrets.keytab"]
939 msg["secret"] = [machinepass.encode('utf-8')]
940 msg["samAccountName"] = ["%s$" % netbiosname]
941 msg["secureChannelType"] = [str(secure_channel_type)]
942 if domainsid is not None:
943 msg["objectSid"] = [ndr_pack(domainsid)]
945 # This complex expression tries to ensure that we don't have more
946 # than one record for this SID, realm or netbios domain at a time,
947 # but we don't delete the old record that we are about to modify,
948 # because that would delete the keytab and previous password.
949 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
950 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
951 scope=ldb.SCOPE_ONELEVEL)
954 secretsdb.delete(del_msg.dn)
956 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
959 msg["priorSecret"] = [res[0]["secret"][0]]
961 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
966 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
971 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
977 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
978 secretsdb.modify(msg)
979 secretsdb.rename(res[0].dn, msg.dn)
981 spn = [ 'HOST/%s' % shortname ]
982 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
983 # we are a domain controller then we add servicePrincipalName
984 # entries for the keytab code to update.
985 spn.extend([ 'HOST/%s' % dnsname ])
986 msg["servicePrincipalName"] = spn
991 def setup_secretsdb(paths, session_info, backend_credentials, lp):
992 """Setup the secrets database.
994 :note: This function does not handle exceptions and transaction on purpose,
995 it's up to the caller to do this job.
997 :param path: Path to the secrets database.
998 :param session_info: Session info.
999 :param credentials: Credentials
1000 :param lp: Loadparm context
1001 :return: LDB handle for the created secrets database
1003 if os.path.exists(paths.secrets):
1004 os.unlink(paths.secrets)
1006 keytab_path = os.path.join(paths.private_dir, paths.keytab)
1007 if os.path.exists(keytab_path):
1008 os.unlink(keytab_path)
1010 bind_dns_keytab_path = os.path.join(paths.binddns_dir, paths.dns_keytab)
1011 if os.path.exists(bind_dns_keytab_path):
1012 os.unlink(bind_dns_keytab_path)
1014 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1015 if os.path.exists(dns_keytab_path):
1016 os.unlink(dns_keytab_path)
1018 path = paths.secrets
1020 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
1022 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
1023 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
1024 secrets_ldb.transaction_start()
1026 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
1028 if (backend_credentials is not None and
1029 backend_credentials.authentication_requested()):
1030 if backend_credentials.get_bind_dn() is not None:
1031 setup_add_ldif(secrets_ldb,
1032 setup_path("secrets_simple_ldap.ldif"), {
1033 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
1034 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password()).decode('utf8')
1037 setup_add_ldif(secrets_ldb,
1038 setup_path("secrets_sasl_ldap.ldif"), {
1039 "LDAPADMINUSER": backend_credentials.get_username(),
1040 "LDAPADMINREALM": backend_credentials.get_realm(),
1041 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password()).decode('utf8')
1044 secrets_ldb.transaction_cancel()
1049 def setup_privileges(path, session_info, lp):
1050 """Setup the privileges database.
1052 :param path: Path to the privileges database.
1053 :param session_info: Session info.
1054 :param credentials: Credentials
1055 :param lp: Loadparm context
1056 :return: LDB handle for the created secrets database
1058 if os.path.exists(path):
1060 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
1061 privilege_ldb.erase()
1062 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
1064 def setup_encrypted_secrets_key(path):
1065 """Setup the encrypted secrets key file.
1067 Any existing key file will be deleted and a new random key generated.
1069 :param path: Path to the secrets key file.
1072 if os.path.exists(path):
1075 flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
1076 mode = stat.S_IRUSR | stat.S_IWUSR
1078 umask_original = os.umask(0)
1080 fd = os.open(path, flags, mode)
1082 os.umask(umask_original)
1084 with os.fdopen(fd, 'w') as f:
1085 key = samba.generate_random_bytes(16)
1089 def setup_registry(path, session_info, lp):
1090 """Setup the registry.
1092 :param path: Path to the registry database
1093 :param session_info: Session information
1094 :param credentials: Credentials
1095 :param lp: Loadparm context
1097 reg = samba.registry.Registry()
1098 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
1099 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
1100 provision_reg = setup_path("provision.reg")
1101 assert os.path.exists(provision_reg)
1102 reg.diff_apply(provision_reg)
1105 def setup_idmapdb(path, session_info, lp):
1106 """Setup the idmap database.
1108 :param path: path to the idmap database
1109 :param session_info: Session information
1110 :param credentials: Credentials
1111 :param lp: Loadparm context
1113 if os.path.exists(path):
1116 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
1118 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
1122 def setup_samdb_rootdse(samdb, names):
1123 """Setup the SamDB rootdse.
1125 :param samdb: Sam Database handle
1127 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
1128 "SCHEMADN": names.schemadn,
1129 "DOMAINDN": names.domaindn,
1130 "ROOTDN" : names.rootdn,
1131 "CONFIGDN": names.configdn,
1132 "SERVERDN": names.serverdn,
1136 def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
1137 dns_backend, dnspass, domainsid, next_rid, invocationid,
1138 policyguid, policyguid_dc,
1139 domainControllerFunctionality, ntdsguid=None, dc_rid=None):
1140 """Join a host to its own domain."""
1141 assert isinstance(invocationid, str)
1142 if ntdsguid is not None:
1143 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
1150 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
1151 "CONFIGDN": names.configdn,
1152 "SCHEMADN": names.schemadn,
1153 "DOMAINDN": names.domaindn,
1154 "SERVERDN": names.serverdn,
1155 "INVOCATIONID": invocationid,
1156 "NETBIOSNAME": names.netbiosname,
1157 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1158 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')).decode('utf8'),
1159 "DOMAINSID": str(domainsid),
1160 "DCRID": str(dc_rid),
1161 "SAMBA_VERSION_STRING": version,
1162 "NTDSGUID": ntdsguid_line,
1163 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1164 domainControllerFunctionality),
1165 "RIDALLOCATIONSTART": str(next_rid + 100),
1166 "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
1168 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1169 "POLICYGUID": policyguid,
1170 "POLICYGUID_DC": policyguid_dc,
1171 "DNSDOMAIN": names.dnsdomain,
1172 "DOMAINDN": names.domaindn})
1174 # If we are setting up a subdomain, then this has been replicated in, so we
1175 # don't need to add it
1176 if fill == FILL_FULL:
1177 setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
1178 "CONFIGDN": names.configdn,
1179 "SCHEMADN": names.schemadn,
1180 "DOMAINDN": names.domaindn,
1181 "SERVERDN": names.serverdn,
1182 "INVOCATIONID": invocationid,
1183 "NETBIOSNAME": names.netbiosname,
1184 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1185 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')).decode('utf8'),
1186 "DOMAINSID": str(domainsid),
1187 "DCRID": str(dc_rid),
1188 "SAMBA_VERSION_STRING": version,
1189 "NTDSGUID": ntdsguid_line,
1190 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1191 domainControllerFunctionality)})
1193 # Setup fSMORoleOwner entries to point at the newly created DC entry
1194 setup_modify_ldif(samdb,
1195 setup_path("provision_self_join_modify_config.ldif"), {
1196 "CONFIGDN": names.configdn,
1197 "SCHEMADN": names.schemadn,
1198 "DEFAULTSITE": names.sitename,
1199 "NETBIOSNAME": names.netbiosname,
1200 "SERVERDN": names.serverdn,
1203 system_session_info = system_session()
1204 samdb.set_session_info(system_session_info)
1205 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1206 # modify a serverReference under cn=config when we are a subdomain, we must
1207 # be system due to ACLs
1208 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1209 "DOMAINDN": names.domaindn,
1210 "SERVERDN": names.serverdn,
1211 "NETBIOSNAME": names.netbiosname,
1214 samdb.set_session_info(admin_session_info)
1216 if dns_backend != "SAMBA_INTERNAL":
1217 # This is Samba4 specific and should be replaced by the correct
1218 # DNS AD-style setup
1219 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1220 "DNSDOMAIN": names.dnsdomain,
1221 "DOMAINDN": names.domaindn,
1222 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')).decode('utf8'),
1223 "HOSTNAME" : names.hostname,
1224 "DNSNAME" : '%s.%s' % (
1225 names.netbiosname.lower(), names.dnsdomain.lower())
1229 def getpolicypath(sysvolpath, dnsdomain, guid):
1230 """Return the physical path of policy given its guid.
1232 :param sysvolpath: Path to the sysvol folder
1233 :param dnsdomain: DNS name of the AD domain
1234 :param guid: The GUID of the policy
1235 :return: A string with the complete path to the policy folder
1238 guid = "{%s}" % guid
1239 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1243 def create_gpo_struct(policy_path):
1244 if not os.path.exists(policy_path):
1245 os.makedirs(policy_path, 0o775)
1246 f = open(os.path.join(policy_path, "GPT.INI"), 'w')
1248 f.write("[General]\r\nVersion=0")
1251 p = os.path.join(policy_path, "MACHINE")
1252 if not os.path.exists(p):
1253 os.makedirs(p, 0o775)
1254 p = os.path.join(policy_path, "USER")
1255 if not os.path.exists(p):
1256 os.makedirs(p, 0o775)
1259 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1260 """Create the default GPO for a domain
1262 :param sysvolpath: Physical path for the sysvol folder
1263 :param dnsdomain: DNS domain name of the AD domain
1264 :param policyguid: GUID of the default domain policy
1265 :param policyguid_dc: GUID of the default domain controler policy
1267 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1268 create_gpo_struct(policy_path)
1270 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1271 create_gpo_struct(policy_path)
1274 def setup_samdb(path, session_info, provision_backend, lp, names,
1275 logger, fill, serverrole, schema, am_rodc=False,
1276 plaintext_secrets=False, backend_store=None):
1277 """Setup a complete SAM Database.
1279 :note: This will wipe the main SAM database file!
1282 # Also wipes the database
1283 setup_samdb_partitions(path, logger=logger, lp=lp,
1284 provision_backend=provision_backend, session_info=session_info,
1285 names=names, serverrole=serverrole, plaintext_secrets=plaintext_secrets,
1286 backend_store=backend_store)
1288 # Load the database, but don's load the global schema and don't connect
1290 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1291 credentials=provision_backend.credentials, lp=lp,
1292 global_schema=False, am_rodc=am_rodc)
1294 logger.info("Pre-loading the Samba 4 and AD schema")
1296 # Load the schema from the one we computed earlier
1297 samdb.set_schema(schema, write_indices_and_attributes=False)
1299 # Set the NTDS settings DN manually - in order to have it already around
1300 # before the provisioned tree exists and we connect
1301 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1303 # And now we can connect to the DB - the schema won't be loaded from the
1307 except ldb.LdbError as e2:
1308 (num, string_error) = e2.args
1309 if (num == ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS):
1310 raise ProvisioningError("Permission denied connecting to %s, are you running as root?" % path)
1314 # But we have to give it one more kick to have it use the schema
1315 # during provision - it needs, now that it is connected, to write
1316 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1317 samdb.set_schema(schema, write_indices_and_attributes=True)
1322 def fill_samdb(samdb, lp, names, logger, policyguid,
1323 policyguid_dc, fill, adminpass, krbtgtpass, machinepass, dns_backend,
1324 dnspass, invocationid, ntdsguid, serverrole, am_rodc=False,
1325 dom_for_fun_level=None, schema=None, next_rid=None, dc_rid=None,
1326 backend_store=None):
1328 if next_rid is None:
1331 # Provision does not make much sense values larger than 1000000000
1332 # as the upper range of the rIDAvailablePool is 1073741823 and
1333 # we don't want to create a domain that cannot allocate rids.
1334 if next_rid < 1000 or next_rid > 1000000000:
1335 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1336 error += "the valid range is %u-%u. The default is %u." % (
1337 1000, 1000000000, 1000)
1338 raise ProvisioningError(error)
1340 # ATTENTION: Do NOT change these default values without discussion with the
1341 # team and/or release manager. They have a big impact on the whole program!
1342 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1344 if dom_for_fun_level is None:
1345 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
1347 if dom_for_fun_level > domainControllerFunctionality:
1348 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!")
1350 domainFunctionality = dom_for_fun_level
1351 forestFunctionality = dom_for_fun_level
1353 # Set the NTDS settings DN manually - in order to have it already around
1354 # before the provisioned tree exists and we connect
1355 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1357 # Set the domain functionality levels onto the database.
1358 # Various module (the password_hash module in particular) need
1359 # to know what level of AD we are emulating.
1361 # These will be fixed into the database via the database
1362 # modifictions below, but we need them set from the start.
1363 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1364 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1365 samdb.set_opaque_integer("domainControllerFunctionality",
1366 domainControllerFunctionality)
1368 samdb.set_domain_sid(str(names.domainsid))
1369 samdb.set_invocation_id(invocationid)
1371 logger.info("Adding DomainDN: %s" % names.domaindn)
1373 # impersonate domain admin
1374 admin_session_info = admin_session(lp, str(names.domainsid))
1375 samdb.set_session_info(admin_session_info)
1376 if names.domainguid is not None:
1377 domainguid_line = "objectGUID: %s\n-" % names.domainguid
1379 domainguid_line = ""
1381 descr = b64encode(get_domain_descriptor(names.domainsid)).decode('utf8')
1382 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1383 "DOMAINDN": names.domaindn,
1384 "DOMAINSID": str(names.domainsid),
1385 "DESCRIPTOR": descr,
1386 "DOMAINGUID": domainguid_line
1389 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1390 "DOMAINDN": names.domaindn,
1391 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1392 "NEXTRID": str(next_rid),
1393 "DEFAULTSITE": names.sitename,
1394 "CONFIGDN": names.configdn,
1395 "POLICYGUID": policyguid,
1396 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1397 "SAMBA_VERSION_STRING": version,
1398 "MIN_PWD_LENGTH": str(DEFAULT_MIN_PWD_LENGTH)
1401 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1402 if fill == FILL_FULL:
1403 logger.info("Adding configuration container")
1404 descr = b64encode(get_config_descriptor(names.domainsid)).decode('utf8')
1405 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1406 "CONFIGDN": names.configdn,
1407 "DESCRIPTOR": descr,
1410 # The LDIF here was created when the Schema object was constructed
1411 ignore_checks_oid = "local_oid:%s:0" % samba.dsdb.DSDB_CONTROL_SKIP_DUPLICATES_CHECK_OID
1412 logger.info("Setting up sam.ldb schema")
1413 samdb.add_ldif(schema.schema_dn_add,
1414 controls=["relax:0", ignore_checks_oid])
1415 samdb.modify_ldif(schema.schema_dn_modify,
1416 controls=[ignore_checks_oid])
1417 samdb.write_prefixes_from_schema()
1418 samdb.add_ldif(schema.schema_data, controls=["relax:0", ignore_checks_oid])
1419 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1420 {"SCHEMADN": names.schemadn},
1421 controls=["relax:0", ignore_checks_oid])
1423 # Now register this container in the root of the forest
1424 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1425 msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1428 samdb.invocation_id = invocationid
1430 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1431 if fill == FILL_FULL:
1432 logger.info("Setting up sam.ldb configuration data")
1434 partitions_descr = b64encode(get_config_partitions_descriptor(names.domainsid)).decode('utf8')
1435 sites_descr = b64encode(get_config_sites_descriptor(names.domainsid)).decode('utf8')
1436 ntdsquotas_descr = b64encode(get_config_ntds_quotas_descriptor(names.domainsid)).decode('utf8')
1437 protected1_descr = b64encode(get_config_delete_protected1_descriptor(names.domainsid)).decode('utf8')
1438 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid)).decode('utf8')
1439 protected2_descr = b64encode(get_config_delete_protected2_descriptor(names.domainsid)).decode('utf8')
1441 if "2008" in schema.base_schema:
1442 # exclude 2012-specific changes if we're using a 2008 schema
1447 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1448 "CONFIGDN": names.configdn,
1449 "NETBIOSNAME": names.netbiosname,
1450 "DEFAULTSITE": names.sitename,
1451 "DNSDOMAIN": names.dnsdomain,
1452 "DOMAIN": names.domain,
1453 "SCHEMADN": names.schemadn,
1454 "DOMAINDN": names.domaindn,
1455 "SERVERDN": names.serverdn,
1456 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1457 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1458 "NTDSQUOTAS_DESCRIPTOR": ntdsquotas_descr,
1459 "LOSTANDFOUND_DESCRIPTOR": protected1wd_descr,
1460 "SERVICES_DESCRIPTOR": protected1_descr,
1461 "PHYSICALLOCATIONS_DESCRIPTOR": protected1wd_descr,
1462 "FORESTUPDATES_DESCRIPTOR": protected1wd_descr,
1463 "EXTENDEDRIGHTS_DESCRIPTOR": protected2_descr,
1464 "PARTITIONS_DESCRIPTOR": partitions_descr,
1465 "SITES_DESCRIPTOR": sites_descr,
1468 setup_add_ldif(samdb, setup_path("extended-rights.ldif"), {
1469 "CONFIGDN": names.configdn,
1470 "INC2012" : incl_2012,
1473 logger.info("Setting up display specifiers")
1474 display_specifiers_ldif = read_ms_ldif(
1475 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1476 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1477 {"CONFIGDN": names.configdn})
1478 check_all_substituted(display_specifiers_ldif)
1479 samdb.add_ldif(display_specifiers_ldif)
1481 logger.info("Modifying display specifiers and extended rights")
1482 setup_modify_ldif(samdb,
1483 setup_path("provision_configuration_modify.ldif"), {
1484 "CONFIGDN": names.configdn,
1485 "DISPLAYSPECIFIERS_DESCRIPTOR": protected2_descr
1488 logger.info("Adding users container")
1489 users_desc = b64encode(get_domain_users_descriptor(names.domainsid)).decode('utf8')
1490 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1491 "DOMAINDN": names.domaindn,
1492 "USERS_DESCRIPTOR": users_desc
1494 logger.info("Modifying users container")
1495 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1496 "DOMAINDN": names.domaindn})
1497 logger.info("Adding computers container")
1498 computers_desc = b64encode(get_domain_computers_descriptor(names.domainsid)).decode('utf8')
1499 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1500 "DOMAINDN": names.domaindn,
1501 "COMPUTERS_DESCRIPTOR": computers_desc
1503 logger.info("Modifying computers container")
1504 setup_modify_ldif(samdb,
1505 setup_path("provision_computers_modify.ldif"), {
1506 "DOMAINDN": names.domaindn})
1507 logger.info("Setting up sam.ldb data")
1508 infrastructure_desc = b64encode(get_domain_infrastructure_descriptor(names.domainsid)).decode('utf8')
1509 lostandfound_desc = b64encode(get_domain_delete_protected2_descriptor(names.domainsid)).decode('utf8')
1510 system_desc = b64encode(get_domain_delete_protected1_descriptor(names.domainsid)).decode('utf8')
1511 builtin_desc = b64encode(get_domain_builtin_descriptor(names.domainsid)).decode('utf8')
1512 controllers_desc = b64encode(get_domain_controllers_descriptor(names.domainsid)).decode('utf8')
1513 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1514 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1515 "DOMAINDN": names.domaindn,
1516 "NETBIOSNAME": names.netbiosname,
1517 "DEFAULTSITE": names.sitename,
1518 "CONFIGDN": names.configdn,
1519 "SERVERDN": names.serverdn,
1520 "RIDAVAILABLESTART": str(next_rid + 600),
1521 "POLICYGUID_DC": policyguid_dc,
1522 "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc,
1523 "LOSTANDFOUND_DESCRIPTOR": lostandfound_desc,
1524 "SYSTEM_DESCRIPTOR": system_desc,
1525 "BUILTIN_DESCRIPTOR": builtin_desc,
1526 "DOMAIN_CONTROLLERS_DESCRIPTOR": controllers_desc,
1529 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1530 if fill == FILL_FULL:
1531 managedservice_descr = b64encode(get_managed_service_accounts_descriptor(names.domainsid)).decode('utf8')
1532 setup_modify_ldif(samdb,
1533 setup_path("provision_configuration_references.ldif"), {
1534 "CONFIGDN": names.configdn,
1535 "SCHEMADN": names.schemadn})
1537 logger.info("Setting up well known security principals")
1538 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid)).decode('utf8')
1539 setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1540 "CONFIGDN": names.configdn,
1541 "WELLKNOWNPRINCIPALS_DESCRIPTOR": protected1wd_descr,
1542 }, controls=["relax:0", "provision:0"])
1544 if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1545 setup_modify_ldif(samdb,
1546 setup_path("provision_basedn_references.ldif"), {
1547 "DOMAINDN": names.domaindn,
1548 "MANAGEDSERVICE_DESCRIPTOR": managedservice_descr
1551 logger.info("Setting up sam.ldb users and groups")
1552 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1553 "DOMAINDN": names.domaindn,
1554 "DOMAINSID": str(names.domainsid),
1555 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')).decode('utf8'),
1556 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le')).decode('utf8')
1557 }, controls=["relax:0", "provision:0"])
1559 logger.info("Setting up self join")
1560 setup_self_join(samdb, admin_session_info, names=names, fill=fill,
1561 invocationid=invocationid,
1562 dns_backend=dns_backend,
1564 machinepass=machinepass,
1565 domainsid=names.domainsid,
1568 policyguid=policyguid,
1569 policyguid_dc=policyguid_dc,
1570 domainControllerFunctionality=domainControllerFunctionality,
1573 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1574 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1575 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1576 assert isinstance(names.ntdsguid, str)
1581 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1582 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)"
1583 SYSVOL_SERVICE="sysvol"
1585 def set_dir_acl(path, acl, lp, domsid, use_ntvfs, passdb, service=SYSVOL_SERVICE):
1586 setntacl(lp, path, acl, domsid, use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1587 for root, dirs, files in os.walk(path, topdown=False):
1589 setntacl(lp, os.path.join(root, name), acl, domsid,
1590 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1592 setntacl(lp, os.path.join(root, name), acl, domsid,
1593 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1596 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb):
1597 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1600 :param sysvol: Physical path for the sysvol folder
1601 :param dnsdomain: The DNS name of the domain
1602 :param domainsid: The SID of the domain
1603 :param domaindn: The DN of the domain (ie. DC=...)
1604 :param samdb: An LDB object on the SAM db
1605 :param lp: an LP object
1608 # Set ACL for GPO root folder
1609 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1610 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid),
1611 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=SYSVOL_SERVICE)
1613 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1614 attrs=["cn", "nTSecurityDescriptor"],
1615 expression="", scope=ldb.SCOPE_ONELEVEL)
1618 acl = ndr_unpack(security.descriptor,
1619 str(policy["nTSecurityDescriptor"])).as_sddl()
1620 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1621 set_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1622 str(domainsid), use_ntvfs,
1626 def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain,
1627 domaindn, lp, use_ntvfs):
1628 """Set the ACL for the sysvol share and the subfolders
1630 :param samdb: An LDB object on the SAM db
1631 :param netlogon: Physical path for the netlogon folder
1632 :param sysvol: Physical path for the sysvol folder
1633 :param uid: The UID of the "Administrator" user
1634 :param gid: The GID of the "Domain adminstrators" group
1635 :param domainsid: The SID of the domain
1636 :param dnsdomain: The DNS name of the domain
1637 :param domaindn: The DN of the domain (ie. DC=...)
1642 s3conf = s3param.get_context()
1643 s3conf.load(lp.configfile)
1645 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(sysvol))
1648 smbd.set_simple_acl(file.name, 0o755, gid)
1650 if not smbd.have_posix_acls():
1651 # This clue is only strictly correct for RPM and
1652 # Debian-like Linux systems, but hopefully other users
1653 # will get enough clue from it.
1654 raise ProvisioningError("Samba was compiled without the posix ACL support that s3fs requires. "
1655 "Try installing libacl1-dev or libacl-devel, then re-run configure and make.")
1657 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. "
1658 "Try the mounting the filesystem with the 'acl' option.")
1660 smbd.chown(file.name, uid, gid)
1662 raise ProvisioningError("Unable to chown a file on your filesystem. "
1663 "You may not be running provision as root.")
1667 # This will ensure that the smbd code we are running when setting ACLs
1668 # is initialised with the smb.conf
1669 s3conf = s3param.get_context()
1670 s3conf.load(lp.configfile)
1671 # ensure we are using the right samba_dsdb passdb backend, no matter what
1672 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1673 passdb.reload_static_pdb()
1675 # ensure that we init the samba_dsdb backend, so the domain sid is
1676 # marked in secrets.tdb
1677 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1679 # now ensure everything matches correctly, to avoid wierd issues
1680 if passdb.get_global_sam_sid() != domainsid:
1681 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))
1683 domain_info = s4_passdb.domain_info()
1684 if domain_info["dom_sid"] != domainsid:
1685 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))
1687 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1688 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()))
1693 os.chown(sysvol, -1, gid)
1699 # use admin sid dn as user dn, since admin should own most of the files,
1700 # the operation will be much faster
1701 userdn = '<SID={}-{}>'.format(domainsid, security.DOMAIN_RID_ADMINISTRATOR)
1703 flags = (auth.AUTH_SESSION_INFO_DEFAULT_GROUPS |
1704 auth.AUTH_SESSION_INFO_AUTHENTICATED |
1705 auth.AUTH_SESSION_INFO_SIMPLE_PRIVILEGES)
1707 session_info = auth.user_session(samdb, lp_ctx=lp, dn=userdn,
1708 session_info_flags=flags)
1710 def _setntacl(path):
1711 """A helper to reuse args"""
1713 lp, path, SYSVOL_ACL, str(domainsid),
1714 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=s4_passdb,
1715 service=SYSVOL_SERVICE, session_info=session_info)
1717 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1719 for root, dirs, files in os.walk(sysvol, topdown=False):
1721 if use_ntvfs and canchown:
1722 os.chown(os.path.join(root, name), -1, gid)
1723 _setntacl(os.path.join(root, name))
1725 if use_ntvfs and canchown:
1726 os.chown(os.path.join(root, name), -1, gid)
1727 _setntacl(os.path.join(root, name))
1729 # Set acls on Policy folder and policies folders
1730 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb=s4_passdb)
1732 def acl_type(direct_db_access):
1733 if direct_db_access:
1738 def check_dir_acl(path, acl, lp, domainsid, direct_db_access):
1739 fsacl = getntacl(lp, path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1740 fsacl_sddl = fsacl.as_sddl(domainsid)
1741 if fsacl_sddl != acl:
1742 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))
1744 for root, dirs, files in os.walk(path, topdown=False):
1746 fsacl = getntacl(lp, os.path.join(root, name),
1747 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1749 raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1750 fsacl_sddl = fsacl.as_sddl(domainsid)
1751 if fsacl_sddl != acl:
1752 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))
1755 fsacl = getntacl(lp, os.path.join(root, name),
1756 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1758 raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1759 fsacl_sddl = fsacl.as_sddl(domainsid)
1760 if fsacl_sddl != acl:
1761 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))
1764 def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1766 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1769 :param sysvol: Physical path for the sysvol folder
1770 :param dnsdomain: The DNS name of the domain
1771 :param domainsid: The SID of the domain
1772 :param domaindn: The DN of the domain (ie. DC=...)
1773 :param samdb: An LDB object on the SAM db
1774 :param lp: an LP object
1777 # Set ACL for GPO root folder
1778 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1779 fsacl = getntacl(lp, root_policy_path,
1780 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1782 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access), root_policy_path))
1783 fsacl_sddl = fsacl.as_sddl(domainsid)
1784 if fsacl_sddl != POLICIES_ACL:
1785 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))
1786 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1787 attrs=["cn", "nTSecurityDescriptor"],
1788 expression="", scope=ldb.SCOPE_ONELEVEL)
1791 acl = ndr_unpack(security.descriptor,
1792 str(policy["nTSecurityDescriptor"])).as_sddl()
1793 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1794 check_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1795 domainsid, direct_db_access)
1798 def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn,
1800 """Set the ACL for the sysvol share and the subfolders
1802 :param samdb: An LDB object on the SAM db
1803 :param netlogon: Physical path for the netlogon folder
1804 :param sysvol: Physical path for the sysvol folder
1805 :param uid: The UID of the "Administrator" user
1806 :param gid: The GID of the "Domain adminstrators" group
1807 :param domainsid: The SID of the domain
1808 :param dnsdomain: The DNS name of the domain
1809 :param domaindn: The DN of the domain (ie. DC=...)
1812 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1813 s3conf = s3param.get_context()
1814 s3conf.load(lp.configfile)
1815 # ensure we are using the right samba_dsdb passdb backend, no matter what
1816 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1817 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1818 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1820 # now ensure everything matches correctly, to avoid wierd issues
1821 if passdb.get_global_sam_sid() != domainsid:
1822 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))
1824 domain_info = s4_passdb.domain_info()
1825 if domain_info["dom_sid"] != domainsid:
1826 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))
1828 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1829 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()))
1831 # Ensure we can read this directly, and via the smbd VFS
1832 for direct_db_access in [True, False]:
1833 # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1834 for dir_path in [os.path.join(sysvol, dnsdomain), netlogon]:
1835 fsacl = getntacl(lp, dir_path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1837 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access), dir_path))
1838 fsacl_sddl = fsacl.as_sddl(domainsid)
1839 if fsacl_sddl != SYSVOL_ACL:
1840 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))
1842 # Check acls on Policy folder and policies folders
1843 check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1847 def interface_ips_v4(lp):
1848 """return only IPv4 IPs"""
1849 ips = samba.interface_ips(lp, False)
1852 if i.find(':') == -1:
1857 def interface_ips_v6(lp):
1858 """return only IPv6 IPs"""
1859 ips = samba.interface_ips(lp, False)
1862 if i.find(':') != -1:
1867 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1869 targetdir=None, samdb_fill=FILL_FULL,
1870 hostip=None, hostip6=None,
1871 next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1872 domainguid=None, policyguid=None, policyguid_dc=None,
1873 invocationid=None, machinepass=None, ntdsguid=None,
1874 dns_backend=None, dnspass=None,
1875 serverrole=None, dom_for_fun_level=None,
1876 am_rodc=False, lp=None, use_ntvfs=False,
1877 skip_sysvolacl=False, backend_store=None):
1878 # create/adapt the group policy GUIDs
1879 # Default GUID for default policy are described at
1880 # "How Core Group Policy Works"
1881 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1882 if policyguid is None:
1883 policyguid = DEFAULT_POLICY_GUID
1884 policyguid = policyguid.upper()
1885 if policyguid_dc is None:
1886 policyguid_dc = DEFAULT_DC_POLICY_GUID
1887 policyguid_dc = policyguid_dc.upper()
1889 if invocationid is None:
1890 invocationid = str(uuid.uuid4())
1892 if krbtgtpass is None:
1893 krbtgtpass = samba.generate_random_machine_password(128, 255)
1894 if machinepass is None:
1895 machinepass = samba.generate_random_machine_password(128, 255)
1897 dnspass = samba.generate_random_password(128, 255)
1899 samdb.transaction_start()
1901 samdb = fill_samdb(samdb, lp, names, logger=logger,
1903 policyguid=policyguid, policyguid_dc=policyguid_dc,
1904 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1905 invocationid=invocationid, machinepass=machinepass,
1906 dns_backend=dns_backend, dnspass=dnspass,
1907 ntdsguid=ntdsguid, serverrole=serverrole,
1908 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1909 next_rid=next_rid, dc_rid=dc_rid,
1910 backend_store=backend_store)
1912 # Set up group policies (domain policy and domain controller
1914 if serverrole == "active directory domain controller":
1915 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1918 samdb.transaction_cancel()
1921 samdb.transaction_commit()
1923 if serverrole == "active directory domain controller":
1924 # Continue setting up sysvol for GPO. This appears to require being
1925 # outside a transaction.
1926 if not skip_sysvolacl:
1927 setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.root_uid,
1928 paths.root_gid, names.domainsid, names.dnsdomain,
1929 names.domaindn, lp, use_ntvfs)
1931 logger.info("Setting acl on sysvol skipped")
1933 secretsdb_self_join(secrets_ldb, domain=names.domain,
1934 realm=names.realm, dnsdomain=names.dnsdomain,
1935 netbiosname=names.netbiosname, domainsid=names.domainsid,
1936 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1938 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1939 # In future, this might be determined from some configuration
1940 kerberos_enctypes = str(ENC_ALL_TYPES)
1943 msg = ldb.Message(ldb.Dn(samdb,
1944 samdb.searchone("distinguishedName",
1945 expression="samAccountName=%s$" % names.netbiosname,
1946 scope=ldb.SCOPE_SUBTREE).decode('utf8')))
1947 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1948 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1949 name="msDS-SupportedEncryptionTypes")
1951 except ldb.LdbError as e:
1952 (enum, estr) = e.args
1953 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1954 # It might be that this attribute does not exist in this schema
1957 setup_ad_dns(samdb, secrets_ldb, names, paths, lp, logger,
1958 hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
1959 dnspass=dnspass, os_level=dom_for_fun_level,
1960 targetdir=targetdir, fill_level=samdb_fill,
1961 backend_store=backend_store)
1963 domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1964 attribute="objectGUID")
1965 assert isinstance(domainguid, str)
1967 lastProvisionUSNs = get_last_provision_usn(samdb)
1968 maxUSN = get_max_usn(samdb, str(names.rootdn))
1969 if lastProvisionUSNs is not None:
1970 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1972 set_provision_usn(samdb, 0, maxUSN, invocationid)
1974 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1975 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1976 { 'NTDSGUID' : names.ntdsguid })
1978 # fix any dangling GUIDs from the provision
1979 logger.info("Fixing provision GUIDs")
1980 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
1982 samdb.transaction_start()
1984 # a small number of GUIDs are missing because of ordering issues in the
1986 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1987 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1988 scope=ldb.SCOPE_BASE,
1989 attrs=['defaultObjectCategory'])
1990 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1991 scope=ldb.SCOPE_ONELEVEL,
1992 attrs=['ipsecOwnersReference',
1993 'ipsecFilterReference',
1994 'ipsecISAKMPReference',
1995 'ipsecNegotiationPolicyReference',
1996 'ipsecNFAReference'])
1997 if chk.check_database(DN=names.schemadn, scope=ldb.SCOPE_SUBTREE,
1998 attrs=['attributeId', 'governsId']) != 0:
1999 raise ProvisioningError("Duplicate attributeId or governsId in schema. Must be fixed manually!!")
2001 samdb.transaction_cancel()
2004 samdb.transaction_commit()
2008 "ROLE_STANDALONE": "standalone server",
2009 "ROLE_DOMAIN_MEMBER": "member server",
2010 "ROLE_DOMAIN_BDC": "active directory domain controller",
2011 "ROLE_DOMAIN_PDC": "active directory domain controller",
2012 "dc": "active directory domain controller",
2013 "member": "member server",
2014 "domain controller": "active directory domain controller",
2015 "active directory domain controller": "active directory domain controller",
2016 "member server": "member server",
2017 "standalone": "standalone server",
2018 "standalone server": "standalone server",
2022 def sanitize_server_role(role):
2023 """Sanitize a server role name.
2025 :param role: Server role
2026 :raise ValueError: If the role can not be interpreted
2027 :return: Sanitized server role (one of "member server",
2028 "active directory domain controller", "standalone server")
2031 return _ROLES_MAP[role]
2033 raise ValueError(role)
2036 def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain,
2038 """Create AD entries for the fake ypserver.
2040 This is needed for being able to manipulate posix attrs via ADUC.
2042 samdb.transaction_start()
2044 logger.info("Setting up fake yp server settings")
2045 setup_add_ldif(samdb, setup_path("ypServ30.ldif"), {
2046 "DOMAINDN": domaindn,
2047 "NETBIOSNAME": netbiosname,
2048 "NISDOMAIN": nisdomain,
2051 samdb.transaction_cancel()
2054 samdb.transaction_commit()
2056 def directory_create_or_exists(path, mode=0o755):
2057 if not os.path.exists(path):
2059 os.mkdir(path, mode)
2060 except OSError as e:
2061 if e.errno in [errno.EEXIST]:
2064 raise ProvisioningError("Failed to create directory %s: %s" % (path, e.strerror))
2066 def determine_host_ip(logger, lp, hostip=None):
2068 logger.info("Looking up IPv4 addresses")
2069 hostips = interface_ips_v4(lp)
2070 if len(hostips) > 0:
2072 if len(hostips) > 1:
2073 logger.warning("More than one IPv4 address found. Using %s",
2075 if hostip == "127.0.0.1":
2078 logger.warning("No IPv4 address will be assigned")
2082 def determine_host_ip6(logger, lp, hostip6=None):
2084 logger.info("Looking up IPv6 addresses")
2085 hostips = interface_ips_v6(lp)
2087 hostip6 = hostips[0]
2088 if len(hostips) > 1:
2089 logger.warning("More than one IPv6 address found. Using %s", hostip6)
2091 logger.warning("No IPv6 address will be assigned")
2095 def provision(logger, session_info, smbconf=None,
2096 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
2097 domaindn=None, schemadn=None, configdn=None, serverdn=None,
2098 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
2099 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None,
2100 krbtgtpass=None, domainguid=None, policyguid=None, policyguid_dc=None,
2101 dns_backend=None, dns_forwarder=None, dnspass=None,
2102 invocationid=None, machinepass=None, ntdsguid=None,
2103 root=None, nobody=None, users=None, backup=None, aci=None,
2104 serverrole=None, dom_for_fun_level=None, backend_type=None,
2105 sitename=None, ol_mmr_urls=None, ol_olc=None, slapd_path=None,
2106 useeadb=False, am_rodc=False, lp=None, use_ntvfs=False,
2107 use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True,
2108 ldap_backend_forced_uri=None, nosync=False, ldap_dryrun_mode=False,
2109 ldap_backend_extra_port=None, base_schema=None,
2110 plaintext_secrets=False, backend_store=None):
2113 :note: caution, this wipes all existing data!
2117 serverrole = sanitize_server_role(serverrole)
2119 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole)
2121 if ldapadminpass is None:
2122 # Make a new, random password between Samba and it's LDAP server
2123 ldapadminpass = samba.generate_random_password(128, 255)
2125 if backend_type is None:
2126 backend_type = "ldb"
2127 if backend_store is None:
2128 backend_store = get_default_backend_store()
2130 if domainsid is None:
2131 domainsid = security.random_sid()
2133 root_uid = findnss_uid([root or "root"])
2134 nobody_uid = findnss_uid([nobody or "nobody"])
2135 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
2136 root_gid = pwd.getpwuid(root_uid).pw_gid
2139 bind_gid = findnss_gid(["bind", "named"])
2143 if targetdir is not None:
2144 smbconf = os.path.join(targetdir, "etc", "smb.conf")
2145 elif smbconf is None:
2146 smbconf = samba.param.default_path()
2147 if not os.path.exists(os.path.dirname(smbconf)):
2148 os.makedirs(os.path.dirname(smbconf))
2150 server_services = []
2153 global_param["idmap_ldb:use rfc2307"] = ["yes"]
2155 if dns_backend != "SAMBA_INTERNAL":
2156 server_services.append("-dns")
2158 if dns_forwarder is not None:
2159 global_param["dns forwarder"] = [dns_forwarder]
2162 server_services.append("+smb")
2163 server_services.append("-s3fs")
2164 global_param["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
2166 if len(server_services) > 0:
2167 global_param["server services"] = server_services
2169 # only install a new smb.conf if there isn't one there already
2170 if os.path.exists(smbconf):
2171 # if Samba Team members can't figure out the weird errors
2172 # loading an empty smb.conf gives, then we need to be smarter.
2173 # Pretend it just didn't exist --abartlet
2174 f = open(smbconf, 'r')
2176 data = f.read().lstrip()
2179 if data is None or data == "":
2180 make_smbconf(smbconf, hostname, domain, realm,
2181 targetdir, serverrole=serverrole,
2182 eadb=useeadb, use_ntvfs=use_ntvfs,
2183 lp=lp, global_param=global_param)
2185 make_smbconf(smbconf, hostname, domain, realm, targetdir,
2186 serverrole=serverrole,
2187 eadb=useeadb, use_ntvfs=use_ntvfs, lp=lp, global_param=global_param)
2190 lp = samba.param.LoadParm()
2192 names = guess_names(lp=lp, hostname=hostname, domain=domain,
2193 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
2194 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
2195 sitename=sitename, rootdn=rootdn, domain_names_forced=(samdb_fill == FILL_DRS))
2196 paths = provision_paths_from_lp(lp, names.dnsdomain)
2198 paths.bind_gid = bind_gid
2199 paths.root_uid = root_uid;
2200 paths.root_gid = root_gid
2202 hostip = determine_host_ip(logger, lp, hostip)
2203 hostip6 = determine_host_ip6(logger, lp, hostip6)
2204 names.hostip = hostip
2205 names.hostip6 = hostip6
2206 names.domainguid = domainguid
2207 names.domainsid = domainsid
2208 names.forestsid = domainsid
2210 if serverrole is None:
2211 serverrole = lp.get("server role")
2213 directory_create_or_exists(paths.private_dir, 0o700)
2214 directory_create_or_exists(paths.binddns_dir, 0o770)
2215 directory_create_or_exists(os.path.join(paths.private_dir, "tls"))
2216 directory_create_or_exists(paths.state_dir)
2217 if not plaintext_secrets:
2218 setup_encrypted_secrets_key(paths.encrypted_secrets_key_path)
2220 if paths.sysvol and not os.path.exists(paths.sysvol):
2221 os.makedirs(paths.sysvol, 0o775)
2223 ldapi_url = "ldapi://%s" % urllib_quote(paths.s4_ldapi_path, safe="")
2225 schema = Schema(domainsid, invocationid=invocationid,
2226 schemadn=names.schemadn, base_schema=base_schema)
2228 if backend_type == "ldb":
2229 provision_backend = LDBBackend(backend_type, paths=paths,
2231 names=names, logger=logger)
2232 elif backend_type == "existing":
2233 # If support for this is ever added back, then the URI will need to be
2235 provision_backend = ExistingBackend(backend_type, paths=paths,
2237 names=names, logger=logger,
2238 ldap_backend_forced_uri=ldap_backend_forced_uri)
2239 elif backend_type == "fedora-ds":
2240 provision_backend = FDSBackend(backend_type, paths=paths,
2242 names=names, logger=logger, domainsid=domainsid,
2243 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2244 slapd_path=slapd_path,
2246 elif backend_type == "openldap":
2247 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
2249 names=names, logger=logger, domainsid=domainsid,
2250 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2251 slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls,
2252 ldap_backend_extra_port=ldap_backend_extra_port,
2253 ldap_dryrun_mode=ldap_dryrun_mode, nosync=nosync,
2254 ldap_backend_forced_uri=ldap_backend_forced_uri)
2256 raise ValueError("Unknown LDAP backend type selected")
2258 provision_backend.init()
2259 provision_backend.start()
2261 # only install a new shares config db if there is none
2262 if not os.path.exists(paths.shareconf):
2263 logger.info("Setting up share.ldb")
2264 share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
2265 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
2267 logger.info("Setting up secrets.ldb")
2268 secrets_ldb = setup_secretsdb(paths,
2269 session_info=session_info,
2270 backend_credentials=provision_backend.credentials, lp=lp)
2273 logger.info("Setting up the registry")
2274 setup_registry(paths.hklm, session_info, lp=lp)
2276 logger.info("Setting up the privileges database")
2277 setup_privileges(paths.privilege, session_info, lp=lp)
2279 logger.info("Setting up idmap db")
2280 idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
2282 setup_name_mappings(idmap, sid=str(domainsid),
2283 root_uid=root_uid, nobody_uid=nobody_uid,
2284 users_gid=users_gid, root_gid=root_gid)
2286 logger.info("Setting up SAM db")
2287 samdb = setup_samdb(paths.samdb, session_info,
2288 provision_backend, lp, names, logger=logger,
2289 serverrole=serverrole,
2290 schema=schema, fill=samdb_fill, am_rodc=am_rodc,
2291 plaintext_secrets=plaintext_secrets,
2292 backend_store=backend_store)
2294 if serverrole == "active directory domain controller":
2295 if paths.netlogon is None:
2296 raise MissingShareError("netlogon", paths.smbconf)
2298 if paths.sysvol is None:
2299 raise MissingShareError("sysvol", paths.smbconf)
2301 if not os.path.isdir(paths.netlogon):
2302 os.makedirs(paths.netlogon, 0o755)
2304 if adminpass is None:
2305 adminpass = samba.generate_random_password(12, 32)
2306 adminpass_generated = True
2308 adminpass = unicode(adminpass, 'utf-8')
2309 adminpass_generated = False
2311 if samdb_fill == FILL_FULL:
2312 provision_fill(samdb, secrets_ldb, logger, names, paths,
2313 schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
2314 hostip=hostip, hostip6=hostip6,
2315 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
2316 krbtgtpass=krbtgtpass,
2317 policyguid=policyguid, policyguid_dc=policyguid_dc,
2318 invocationid=invocationid, machinepass=machinepass,
2319 ntdsguid=ntdsguid, dns_backend=dns_backend,
2320 dnspass=dnspass, serverrole=serverrole,
2321 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
2322 lp=lp, use_ntvfs=use_ntvfs,
2323 skip_sysvolacl=skip_sysvolacl,
2324 backend_store=backend_store)
2326 if not is_heimdal_built():
2327 create_kdc_conf(paths.kdcconf, realm, domain, os.path.dirname(lp.get("log file")))
2328 logger.info("The Kerberos KDC configuration for Samba AD is "
2329 "located at %s", paths.kdcconf)
2331 create_krb5_conf(paths.krb5conf,
2332 dnsdomain=names.dnsdomain, hostname=names.hostname,
2334 logger.info("A Kerberos configuration suitable for Samba AD has been "
2335 "generated at %s", paths.krb5conf)
2336 logger.info("Merge the contents of this file with your system "
2337 "krb5.conf or replace it with this one. Do not create a "
2340 if serverrole == "active directory domain controller":
2341 create_dns_update_list(lp, logger, paths)
2343 backend_result = provision_backend.post_setup()
2344 provision_backend.shutdown()
2347 secrets_ldb.transaction_cancel()
2350 # Now commit the secrets.ldb to disk
2351 secrets_ldb.transaction_commit()
2353 # the commit creates the dns.keytab in the private directory
2354 private_dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
2355 bind_dns_keytab_path = os.path.join(paths.binddns_dir, paths.dns_keytab)
2357 if os.path.isfile(private_dns_keytab_path):
2358 if os.path.isfile(bind_dns_keytab_path):
2360 os.unlink(bind_dns_keytab_path)
2361 except OSError as e:
2362 logger.error("Failed to remove %s: %s" %
2363 (bind_dns_keytab_path, e.strerror))
2365 # link the dns.keytab to the bind-dns directory
2367 os.link(private_dns_keytab_path, bind_dns_keytab_path)
2368 except OSError as e:
2369 logger.error("Failed to create link %s -> %s: %s" %
2370 (private_dns_keytab_path, bind_dns_keytab_path, e.strerror))
2372 # chown the dns.keytab in the bind-dns directory
2373 if paths.bind_gid is not None:
2375 os.chmod(paths.binddns_dir, 0o770)
2376 os.chown(paths.binddns_dir, -1, paths.bind_gid)
2378 if 'SAMBA_SELFTEST' not in os.environ:
2379 logger.info("Failed to chown %s to bind gid %u",
2380 paths.binddns_dir, paths.bind_gid)
2383 os.chmod(bind_dns_keytab_path, 0o640)
2384 os.chown(bind_dns_keytab_path, -1, paths.bind_gid)
2386 if 'SAMBA_SELFTEST' not in os.environ:
2387 logger.info("Failed to chown %s to bind gid %u",
2388 bind_dns_keytab_path, paths.bind_gid)
2390 result = ProvisionResult()
2391 result.server_role = serverrole
2392 result.domaindn = domaindn
2393 result.paths = paths
2394 result.names = names
2396 result.samdb = samdb
2397 result.idmap = idmap
2398 result.domainsid = str(domainsid)
2400 if samdb_fill == FILL_FULL:
2401 result.adminpass_generated = adminpass_generated
2402 result.adminpass = adminpass
2404 result.adminpass_generated = False
2405 result.adminpass = None
2407 result.backend_result = backend_result
2410 provision_fake_ypserver(logger=logger, samdb=samdb,
2411 domaindn=names.domaindn, netbiosname=names.netbiosname,
2412 nisdomain=names.domain.lower(), maxuid=maxuid, maxgid=maxgid)
2417 def provision_become_dc(smbconf=None, targetdir=None,
2418 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
2419 serverdn=None, domain=None, hostname=None, domainsid=None,
2420 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
2421 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
2422 dns_backend=None, root=None, nobody=None, users=None,
2423 backup=None, serverrole=None, ldap_backend=None,
2424 ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False):
2426 logger = logging.getLogger("provision")
2427 samba.set_debug_level(debuglevel)
2429 res = provision(logger, system_session(),
2430 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
2431 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
2432 configdn=configdn, serverdn=serverdn, domain=domain,
2433 hostname=hostname, hostip=None, domainsid=domainsid,
2434 machinepass=machinepass,
2435 serverrole="active directory domain controller",
2436 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass,
2437 use_ntvfs=use_ntvfs)
2438 res.lp.set("debuglevel", str(debuglevel))
2442 def create_krb5_conf(path, dnsdomain, hostname, realm):
2443 """Write out a file containing a valid krb5.conf file
2445 :param path: Path of the new krb5.conf file.
2446 :param dnsdomain: DNS Domain name
2447 :param hostname: Local hostname
2448 :param realm: Realm name
2450 setup_file(setup_path("krb5.conf"), path, {
2451 "DNSDOMAIN": dnsdomain,
2452 "HOSTNAME": hostname,
2457 class ProvisioningError(Exception):
2458 """A generic provision error."""
2460 def __init__(self, value):
2464 return "ProvisioningError: " + self.value
2467 class InvalidNetbiosName(Exception):
2468 """A specified name was not a valid NetBIOS name."""
2470 def __init__(self, name):
2471 super(InvalidNetbiosName, self).__init__(
2472 "The name '%r' is not a valid NetBIOS name" % name)
2475 class MissingShareError(ProvisioningError):
2477 def __init__(self, name, smbconf):
2478 super(MissingShareError, self).__init__(
2479 "Existing smb.conf does not have a [%s] share, but you are "
2480 "configuring a DC. Please remove %s or add the share manually." %