1 # Unix SMB/CIFS implementation.
2 # backend code for provisioning a Samba AD server
4 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2012
5 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
6 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
8 # Based on the original in EJS:
9 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 3 of the License, or
14 # (at your option) any later version.
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 """Functions for setting up a Samba configuration."""
27 __docformat__ = "restructuredText"
29 from samba.compat import urllib_quote
30 from samba.compat import string_types
31 from samba.compat import binary_type
32 from base64 import b64encode
48 from samba.auth import system_session, admin_session
50 from samba import auth
51 from samba.samba3 import smbd, passdb
52 from samba.samba3 import param as s3param
53 from samba.dsdb import DS_DOMAIN_FUNCTION_2000
57 check_all_substituted,
58 is_valid_netbios_char,
65 from samba.dcerpc import security, misc
66 from samba.dcerpc.misc import (
70 from samba.dsdb import (
71 DS_DOMAIN_FUNCTION_2003,
72 DS_DOMAIN_FUNCTION_2008_R2,
75 from samba.idmap import IDmapDB
76 from samba.ms_display_specifiers import read_ms_ldif
77 from samba.ntacls import setntacl, getntacl, dsacl2fsacl
78 from samba.ndr import ndr_pack, ndr_unpack
79 from samba.provision.backend import (
84 from samba.descriptor import (
86 get_config_descriptor,
87 get_config_partitions_descriptor,
88 get_config_sites_descriptor,
89 get_config_ntds_quotas_descriptor,
90 get_config_delete_protected1_descriptor,
91 get_config_delete_protected1wd_descriptor,
92 get_config_delete_protected2_descriptor,
93 get_domain_descriptor,
94 get_domain_infrastructure_descriptor,
95 get_domain_builtin_descriptor,
96 get_domain_computers_descriptor,
97 get_domain_users_descriptor,
98 get_domain_controllers_descriptor,
99 get_domain_delete_protected1_descriptor,
100 get_domain_delete_protected2_descriptor,
101 get_dns_partition_descriptor,
102 get_dns_forest_microsoft_dns_descriptor,
103 get_dns_domain_microsoft_dns_descriptor,
104 get_managed_service_accounts_descriptor,
106 from samba.provision.common import (
115 from samba.provision.sambadns import (
118 create_dns_update_list
122 import samba.registry
123 from samba.schema import Schema
124 from samba.samdb import SamDB
125 from samba.dbchecker import dbcheck
126 from samba.provision.kerberos import create_kdc_conf
127 from samba.samdb import get_default_backend_store
129 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
130 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04FB984F9"
131 DEFAULTSITE = "Default-First-Site-Name"
132 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
134 DEFAULT_MIN_PWD_LENGTH = 7
136 SYSVOL_FOLDER_SD = "O:BAG:SYD:PAI(A;;0x001200a9;;;AU)(A;OICIIO;GRGX;;;AU)(A;;0x001200a9;;;SO)(A;OICIIO;GRGX;;;SO)(A;;0x001e01bf;;;BA)(A;OICIIO;WOWDGRGWGX;;;BA)(A;;0x001f01ff;;;SY)(A;OICIIO;GA;;;SY)(A;;0x001e01bf;;;BA)(A;OICIIO;WOWDGRGWGX;;;CO)S:AI(AU;OICISA;SD;;;WD)"
137 SYSVOL_SUBFOLDER_SD = "O:BAG:SYD:PAI(A;;0x001200a9;;;AU)(A;OICIIO;GRGX;;;AU)(A;;0x001200a9;;;SO)(A;OICIIO;GRGX;;;SO)(A;;0x001e01bf;;;BA)(A;OICIIO;GA;;;BA)(A;;0x001f01ff;;;SY)(A;OICIIO;GA;;;SY)(A;;0x001e01bf;;;BA)(A;OICIIO;GA;;;CO)S:AI(AU;SA;SD;;;WD)"
138 SYSVOL_SUBFILE_SD = "O:BAG:SYD:AI(A;ID;0x001200a9;;;AU)(A;ID;0x001200a9;;;SO)(A;ID;0x001f01ff;;;BA)(A;ID;0x001f01ff;;;SY)"
139 POLICIES_FOLDER_SD = "O:BAG:SYD:PAI(A;;0x001200a9;;;AU)(A;OICIIO;GRGX;;;AU)(A;;0x001200a9;;;SO)(A;OICIIO;GRGX;;;SO)(A;;0x001e01bf;;;BA)(A;OICIIO;GA;;;BA)(A;;0x001f01ff;;;SY)(A;OICIIO;GA;;;SY)(A;;0x001e01bf;;;BA)(A;OICIIO;GA;;;CO)(A;;0x001201bf;;;PA)(A;OICIIO;GRGWGX;;;PA)S:AI(AU;SA;SD;;;WD)"
141 class ProvisionPaths(object):
144 self.shareconf = None
155 self.dns_keytab = None
158 self.private_dir = None
159 self.binddns_dir = None
160 self.state_dir = None
163 class ProvisionNames(object):
171 self.dnsforestdn = None
172 self.dnsdomaindn = None
173 self.ldapmanagerdn = None
174 self.dnsdomain = None
176 self.netbiosname = None
181 self.domainsid = None
182 self.forestsid = None
183 self.domainguid = None
187 def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf,
189 """Get key provision parameters (realm, domain, ...) from a given provision
191 :param samdb: An LDB object connected to the sam.ldb file
192 :param secretsdb: An LDB object connected to the secrets.ldb file
193 :param idmapdb: An LDB object connected to the idmap.ldb file
194 :param paths: A list of path to provision object
195 :param smbconf: Path to the smb.conf file
196 :param lp: A LoadParm object
197 :return: A list of key provision parameters
199 names = ProvisionNames()
200 names.adminpass = None
202 # NT domain, kerberos realm, root dn, domain dn, domain dns name
203 names.domain = lp.get("workgroup").upper()
204 names.realm = lp.get("realm")
205 names.dnsdomain = names.realm.lower()
206 basedn = samba.dn_from_dns_name(names.dnsdomain)
207 names.realm = names.realm.upper()
209 # Get the netbiosname first (could be obtained from smb.conf in theory)
210 res = secretsdb.search(expression="(flatname=%s)" %
211 names.domain, base="CN=Primary Domains",
212 scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"])
213 names.netbiosname = str(res[0]["sAMAccountName"]).replace("$", "")
215 names.smbconf = smbconf
217 # That's a bit simplistic but it's ok as long as we have only 3
219 current = samdb.search(expression="(objectClass=*)",
220 base="", scope=ldb.SCOPE_BASE,
221 attrs=["defaultNamingContext", "schemaNamingContext",
222 "configurationNamingContext", "rootDomainNamingContext",
225 names.configdn = str(current[0]["configurationNamingContext"][0])
226 names.schemadn = str(current[0]["schemaNamingContext"][0])
227 if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb,
228 current[0]["defaultNamingContext"][0].decode('utf8')))):
229 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
230 "is not the same ..." % (paths.samdb,
231 str(current[0]["defaultNamingContext"][0].decode('utf8')),
232 paths.smbconf, basedn)))
234 names.domaindn = str(current[0]["defaultNamingContext"][0])
235 names.rootdn = str(current[0]["rootDomainNamingContext"][0])
236 names.ncs = current[0]["namingContexts"]
237 names.dnsforestdn = None
238 names.dnsdomaindn = None
240 for i in range(0, len(names.ncs)):
241 nc = str(names.ncs[i])
243 dnsforestdn = "DC=ForestDnsZones,%s" % (str(names.rootdn))
244 if nc == dnsforestdn:
245 names.dnsforestdn = dnsforestdn
248 dnsdomaindn = "DC=DomainDnsZones,%s" % (str(names.domaindn))
249 if nc == dnsdomaindn:
250 names.dnsdomaindn = dnsdomaindn
254 res3 = samdb.search(expression="(objectClass=site)",
255 base="CN=Sites," + str(names.configdn), scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
256 names.sitename = str(res3[0]["cn"])
258 # dns hostname and server dn
259 res4 = samdb.search(expression="(CN=%s)" % names.netbiosname,
260 base="OU=Domain Controllers,%s" % basedn,
261 scope=ldb.SCOPE_ONELEVEL, attrs=["dNSHostName"])
263 raise ProvisioningError("Unable to find DC called CN=%s under OU=Domain Controllers,%s" % (names.netbiosname, basedn))
265 names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain, "")
267 server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
268 attrs=[], base=names.configdn)
269 names.serverdn = str(server_res[0].dn)
271 # invocation id/objectguid
272 res5 = samdb.search(expression="(objectClass=*)",
273 base="CN=NTDS Settings,%s" % str(names.serverdn),
274 scope=ldb.SCOPE_BASE,
275 attrs=["invocationID", "objectGUID"])
276 names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0]))
277 names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0]))
280 res6 = samdb.search(expression="(objectClass=*)", base=basedn,
281 scope=ldb.SCOPE_BASE, attrs=["objectGUID",
282 "objectSid", "msDS-Behavior-Version"])
283 names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
284 names.domainsid = ndr_unpack(security.dom_sid, res6[0]["objectSid"][0])
285 names.forestsid = ndr_unpack(security.dom_sid, res6[0]["objectSid"][0])
286 if res6[0].get("msDS-Behavior-Version") is None or \
287 int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
288 names.domainlevel = DS_DOMAIN_FUNCTION_2000
290 names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
293 res7 = samdb.search(expression="(name={%s})" % DEFAULT_POLICY_GUID,
294 base="CN=Policies,CN=System," + basedn,
295 scope=ldb.SCOPE_ONELEVEL, attrs=["cn", "displayName"])
296 names.policyid = str(res7[0]["cn"]).replace("{", "").replace("}", "")
298 res8 = samdb.search(expression="(name={%s})" % DEFAULT_DC_POLICY_GUID,
299 base="CN=Policies,CN=System," + basedn,
300 scope=ldb.SCOPE_ONELEVEL,
301 attrs=["cn", "displayName"])
303 names.policyid_dc = str(res8[0]["cn"]).replace("{", "").replace("}", "")
305 names.policyid_dc = None
307 res9 = idmapdb.search(expression="(cn=%s-%s)" %
308 (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR),
309 attrs=["xidNumber", "type"])
311 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid (%s-%s" % (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR))
312 if str(res9[0]["type"][0]) == "ID_TYPE_BOTH":
313 names.root_gid = int(res9[0]["xidNumber"][0])
315 names.root_gid = pwd.getpwuid(int(res9[0]["xidNumber"][0])).pw_gid
317 res10 = samdb.search(expression="(samaccountname=dns)",
318 scope=ldb.SCOPE_SUBTREE, attrs=["dn"],
319 controls=["search_options:1:2"])
321 has_legacy_dns_account = True
323 has_legacy_dns_account = False
325 res11 = samdb.search(expression="(samaccountname=dns-%s)" % names.netbiosname,
326 scope=ldb.SCOPE_SUBTREE, attrs=["dn"],
327 controls=["search_options:1:2"])
329 has_dns_account = True
331 has_dns_account = False
333 if names.dnsdomaindn is not None:
335 names.dns_backend = 'BIND9_DLZ'
337 names.dns_backend = 'SAMBA_INTERNAL'
338 elif has_dns_account or has_legacy_dns_account:
339 names.dns_backend = 'BIND9_FLATFILE'
341 names.dns_backend = 'NONE'
343 dns_admins_sid = get_dnsadmins_sid(samdb, names.domaindn)
344 names.name_map['DnsAdmins'] = str(dns_admins_sid)
349 def update_provision_usn(samdb, low, high, id, replace=False):
350 """Update the field provisionUSN in sam.ldb
352 This field is used to track range of USN modified by provision and
354 This value is used afterward by next provision to figure out if
355 the field have been modified since last provision.
357 :param samdb: An LDB object connect to sam.ldb
358 :param low: The lowest USN modified by this upgrade
359 :param high: The highest USN modified by this upgrade
360 :param id: The invocation id of the samba's dc
361 :param replace: A boolean indicating if the range should replace any
362 existing one or appended (default)
367 entry = samdb.search(base="@PROVISION",
368 scope=ldb.SCOPE_BASE,
369 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
370 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
371 if not re.search(';', str(e)):
372 e = "%s;%s" % (str(e), id)
375 tab.append("%s-%s;%s" % (low, high, id))
376 delta = ldb.Message()
377 delta.dn = ldb.Dn(samdb, "@PROVISION")
378 delta[LAST_PROVISION_USN_ATTRIBUTE] = \
379 ldb.MessageElement(tab,
380 ldb.FLAG_MOD_REPLACE,
381 LAST_PROVISION_USN_ATTRIBUTE)
382 entry = samdb.search(expression='provisionnerID=*',
383 base="@PROVISION", scope=ldb.SCOPE_BASE,
384 attrs=["provisionnerID"])
385 if len(entry) == 0 or len(entry[0]) == 0:
386 delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
390 def set_provision_usn(samdb, low, high, id):
391 """Set the field provisionUSN in sam.ldb
392 This field is used to track range of USN modified by provision and
394 This value is used afterward by next provision to figure out if
395 the field have been modified since last provision.
397 :param samdb: An LDB object connect to sam.ldb
398 :param low: The lowest USN modified by this upgrade
399 :param high: The highest USN modified by this upgrade
400 :param id: The invocationId of the provision"""
403 tab.append("%s-%s;%s" % (low, high, id))
405 delta = ldb.Message()
406 delta.dn = ldb.Dn(samdb, "@PROVISION")
407 delta[LAST_PROVISION_USN_ATTRIBUTE] = \
408 ldb.MessageElement(tab,
410 LAST_PROVISION_USN_ATTRIBUTE)
414 def get_max_usn(samdb, basedn):
415 """ This function return the biggest USN present in the provision
417 :param samdb: A LDB object pointing to the sam.ldb
418 :param basedn: A string containing the base DN of the provision
420 :return: The biggest USN in the provision"""
422 res = samdb.search(expression="objectClass=*", base=basedn,
423 scope=ldb.SCOPE_SUBTREE, attrs=["uSNChanged"],
424 controls=["search_options:1:2",
425 "server_sort:1:1:uSNChanged",
426 "paged_results:1:1"])
427 return res[0]["uSNChanged"]
430 def get_last_provision_usn(sam):
431 """Get USNs ranges modified by a provision or an upgradeprovision
433 :param sam: An LDB object pointing to the sam.ldb
434 :return: a dictionary which keys are invocation id and values are an array
435 of integer representing the different ranges
438 entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE,
439 base="@PROVISION", scope=ldb.SCOPE_BASE,
440 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
441 except ldb.LdbError as e1:
442 (ecode, emsg) = e1.args
443 if ecode == ldb.ERR_NO_SUCH_OBJECT:
450 if entry[0].get("provisionnerID"):
451 for e in entry[0]["provisionnerID"]:
453 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
454 tab1 = str(r).split(';')
459 if (len(myids) > 0 and id not in myids):
461 tab2 = p.split(tab1[0])
462 if range.get(id) is None:
464 range[id].append(tab2[0])
465 range[id].append(tab2[1])
471 class ProvisionResult(object):
472 """Result of a provision.
474 :ivar server_role: The server role
475 :ivar paths: ProvisionPaths instance
476 :ivar domaindn: The domain dn, as string
480 self.server_role = None
487 self.domainsid = None
488 self.adminpass_generated = None
489 self.adminpass = None
490 self.backend_result = None
492 def report_logger(self, logger):
493 """Report this provision result to a logger."""
495 "Once the above files are installed, your Samba AD server will "
497 if self.adminpass_generated:
498 logger.info("Admin password: %s", self.adminpass)
499 logger.info("Server Role: %s", self.server_role)
500 logger.info("Hostname: %s", self.names.hostname)
501 logger.info("NetBIOS Domain: %s", self.names.domain)
502 logger.info("DNS Domain: %s", self.names.dnsdomain)
503 logger.info("DOMAIN SID: %s", self.domainsid)
505 if self.backend_result:
506 self.backend_result.report_logger(logger)
509 def check_install(lp, session_info, credentials):
510 """Check whether the current install seems ok.
512 :param lp: Loadparm context
513 :param session_info: Session information
514 :param credentials: Credentials
516 if lp.get("realm") == "":
517 raise Exception("Realm empty")
518 samdb = Ldb(lp.samdb_url(), session_info=session_info,
519 credentials=credentials, lp=lp)
520 if len(samdb.search("(cn=Administrator)")) != 1:
521 raise ProvisioningError("No administrator account found")
524 def findnss(nssfn, names):
525 """Find a user or group from a list of possibilities.
527 :param nssfn: NSS Function to try (should raise KeyError if not found)
528 :param names: Names to check.
529 :return: Value return by first names list.
536 raise KeyError("Unable to find user/group in %r" % names)
539 def findnss_uid(names):
540 return findnss(pwd.getpwnam, names)[2]
543 def findnss_gid(names):
544 return findnss(grp.getgrnam, names)[2]
547 def provision_paths_from_lp(lp, dnsdomain):
548 """Set the default paths for provisioning.
550 :param lp: Loadparm context.
551 :param dnsdomain: DNS Domain name
553 paths = ProvisionPaths()
554 paths.private_dir = lp.get("private dir")
555 paths.binddns_dir = lp.get("binddns dir")
556 paths.state_dir = lp.get("state directory")
558 # This is stored without path prefix for the "privateKeytab" attribute in
559 # "secrets_dns.ldif".
560 paths.dns_keytab = "dns.keytab"
561 paths.keytab = "secrets.keytab"
563 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
564 paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
565 paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
566 paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
567 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
568 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
569 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
570 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
571 paths.kdcconf = os.path.join(paths.private_dir, "kdc.conf")
572 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
573 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
574 paths.encrypted_secrets_key_path = os.path.join(
576 "encrypted_secrets.key")
578 paths.dns = os.path.join(paths.binddns_dir, "dns", dnsdomain + ".zone")
579 paths.namedconf = os.path.join(paths.binddns_dir, "named.conf")
580 paths.namedconf_update = os.path.join(paths.binddns_dir, "named.conf.update")
581 paths.namedtxt = os.path.join(paths.binddns_dir, "named.txt")
583 paths.hklm = "hklm.ldb"
584 paths.hkcr = "hkcr.ldb"
585 paths.hkcu = "hkcu.ldb"
586 paths.hku = "hku.ldb"
587 paths.hkpd = "hkpd.ldb"
588 paths.hkpt = "hkpt.ldb"
589 paths.sysvol = lp.get("path", "sysvol")
590 paths.netlogon = lp.get("path", "netlogon")
591 paths.smbconf = lp.configfile
595 def determine_netbios_name(hostname):
596 """Determine a netbios name from a hostname."""
597 # remove forbidden chars and force the length to be <16
598 netbiosname = "".join([x for x in hostname if is_valid_netbios_char(x)])
599 return netbiosname[:MAX_NETBIOS_NAME_LEN].upper()
602 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
603 serverrole=None, rootdn=None, domaindn=None, configdn=None,
604 schemadn=None, serverdn=None, sitename=None,
605 domain_names_forced=False):
606 """Guess configuration settings to use."""
609 hostname = socket.gethostname().split(".")[0]
611 netbiosname = lp.get("netbios name")
612 if netbiosname is None:
613 netbiosname = determine_netbios_name(hostname)
614 netbiosname = netbiosname.upper()
615 if not valid_netbios_name(netbiosname):
616 raise InvalidNetbiosName(netbiosname)
618 if dnsdomain is None:
619 dnsdomain = lp.get("realm")
620 if dnsdomain is None or dnsdomain == "":
621 raise ProvisioningError(
622 "guess_names: 'realm' not specified in supplied %s!" %
625 dnsdomain = dnsdomain.lower()
627 if serverrole is None:
628 serverrole = lp.get("server role")
629 if serverrole is None:
630 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
632 serverrole = serverrole.lower()
634 realm = dnsdomain.upper()
636 if lp.get("realm") == "":
637 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
639 if lp.get("realm").upper() != realm:
640 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))
642 if lp.get("server role").lower() != serverrole:
643 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))
645 if serverrole == "active directory domain controller":
647 # This will, for better or worse, default to 'WORKGROUP'
648 domain = lp.get("workgroup")
649 domain = domain.upper()
651 if lp.get("workgroup").upper() != domain:
652 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))
655 domaindn = samba.dn_from_dns_name(dnsdomain)
657 if domain == netbiosname:
658 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
662 domaindn = "DC=" + netbiosname
664 if not valid_netbios_name(domain):
665 raise InvalidNetbiosName(domain)
667 if hostname.upper() == realm:
668 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
669 if netbiosname.upper() == realm:
670 raise ProvisioningError("guess_names: Realm '%s' must not be equal to NetBIOS hostname '%s'!" % (realm, netbiosname))
671 if domain == realm and not domain_names_forced:
672 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
674 if serverrole != "active directory domain controller":
676 # This is the code path for a domain member
677 # where we provision the database as if we where
678 # on a domain controller, so we should not use
679 # the same dnsdomain as the domain controllers
680 # of our primary domain.
682 # This will be important if we start doing
683 # SID/name filtering and reject the local
684 # sid and names if they come from a domain
688 dnsdomain = netbiosname.lower()
694 configdn = "CN=Configuration," + rootdn
696 schemadn = "CN=Schema," + configdn
699 sitename = DEFAULTSITE
701 names = ProvisionNames()
702 names.rootdn = rootdn
703 names.domaindn = domaindn
704 names.configdn = configdn
705 names.schemadn = schemadn
706 names.ldapmanagerdn = "CN=Manager," + rootdn
707 names.dnsdomain = dnsdomain
708 names.domain = domain
710 names.netbiosname = netbiosname
711 names.hostname = hostname
712 names.sitename = sitename
713 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
714 netbiosname, sitename, configdn)
719 def make_smbconf(smbconf, hostname, domain, realm, targetdir,
720 serverrole=None, eadb=False, use_ntvfs=False, lp=None,
722 """Create a new smb.conf file based on a couple of basic settings.
724 assert smbconf is not None
727 hostname = socket.gethostname().split(".")[0]
729 netbiosname = determine_netbios_name(hostname)
731 if serverrole is None:
732 serverrole = "standalone server"
734 assert domain is not None
735 domain = domain.upper()
737 assert realm is not None
738 realm = realm.upper()
741 "netbios name": netbiosname,
744 "server role": serverrole,
748 lp = samba.param.LoadParm()
749 # Load non-existent file
750 if os.path.exists(smbconf):
753 if global_param is not None:
754 for ent in global_param:
755 if global_param[ent] is not None:
756 global_settings[ent] = " ".join(global_param[ent])
758 if targetdir is not None:
759 global_settings["private dir"] = os.path.abspath(os.path.join(targetdir, "private"))
760 global_settings["lock dir"] = os.path.abspath(targetdir)
761 global_settings["state directory"] = os.path.abspath(os.path.join(targetdir, "state"))
762 global_settings["cache directory"] = os.path.abspath(os.path.join(targetdir, "cache"))
763 global_settings["binddns dir"] = os.path.abspath(os.path.join(targetdir, "bind-dns"))
765 lp.set("lock dir", os.path.abspath(targetdir))
766 lp.set("state directory", global_settings["state directory"])
767 lp.set("cache directory", global_settings["cache directory"])
768 lp.set("binddns dir", global_settings["binddns dir"])
771 if use_ntvfs and not lp.get("posix:eadb"):
772 if targetdir is not None:
773 privdir = os.path.join(targetdir, "private")
775 privdir = lp.get("private dir")
776 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
777 elif not use_ntvfs and not lp.get("xattr_tdb:file"):
778 if targetdir is not None:
779 statedir = os.path.join(targetdir, "state")
781 statedir = lp.get("state directory")
782 lp.set("xattr_tdb:file", os.path.abspath(os.path.join(statedir, "xattr.tdb")))
785 if serverrole == "active directory domain controller":
786 shares["sysvol"] = os.path.join(lp.get("state directory"), "sysvol")
787 shares["netlogon"] = os.path.join(shares["sysvol"], realm.lower(),
790 global_settings["passdb backend"] = "samba_dsdb"
792 f = open(smbconf, 'w')
794 f.write("[globals]\n")
795 for key, val in global_settings.items():
796 f.write("\t%s = %s\n" % (key, val))
799 for name, path in shares.items():
800 f.write("[%s]\n" % name)
801 f.write("\tpath = %s\n" % path)
802 f.write("\tread only = no\n")
806 # reload the smb.conf
809 # and dump it without any values that are the default
810 # this ensures that any smb.conf parameters that were set
811 # on the provision/join command line are set in the resulting smb.conf
812 lp.dump(False, smbconf)
815 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
816 users_gid, root_gid):
817 """setup reasonable name mappings for sam names to unix names.
819 :param samdb: SamDB object.
820 :param idmap: IDmap db object.
821 :param sid: The domain sid.
822 :param domaindn: The domain DN.
823 :param root_uid: uid of the UNIX root user.
824 :param nobody_uid: uid of the UNIX nobody user.
825 :param users_gid: gid of the UNIX users group.
826 :param root_gid: gid of the UNIX root group.
828 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
830 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
831 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
834 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
835 provision_backend, names, serverrole,
836 erase=False, plaintext_secrets=False,
838 """Setup the partitions for the SAM database.
840 Alternatively, provision() may call this, and then populate the database.
842 :note: This will wipe the Sam Database!
844 :note: This function always removes the local SAM LDB file. The erase
845 parameter controls whether to erase the existing data, which
846 may not be stored locally but in LDAP.
849 assert session_info is not None
851 # We use options=["modules:"] to stop the modules loading - we
852 # just want to wipe and re-initialise the database, not start it up
855 os.unlink(samdb_path)
859 samdb = Ldb(url=samdb_path, session_info=session_info,
860 lp=lp, options=["modules:"])
862 ldap_backend_line = "# No LDAP backend"
863 if provision_backend.type != "ldb":
864 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
866 required_features = None
867 if not plaintext_secrets:
868 required_features = "requiredFeatures: encryptedSecrets"
870 if backend_store is None:
871 backend_store = get_default_backend_store()
872 backend_store_line = "backendStore: %s" % backend_store
874 if backend_store == "mdb":
875 if required_features is not None:
876 required_features += "\n"
878 required_features = ""
879 required_features += "requiredFeatures: lmdbLevelOne"
881 if required_features is None:
882 required_features = "# No required features"
884 samdb.transaction_start()
886 logger.info("Setting up sam.ldb partitions and settings")
887 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
888 "LDAP_BACKEND_LINE": ldap_backend_line,
889 "BACKEND_STORE": backend_store_line
892 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
893 "BACKEND_TYPE": provision_backend.type,
894 "SERVER_ROLE": serverrole,
895 "REQUIRED_FEATURES": required_features
898 logger.info("Setting up sam.ldb rootDSE")
899 setup_samdb_rootdse(samdb, names)
901 samdb.transaction_cancel()
904 samdb.transaction_commit()
907 def secretsdb_self_join(secretsdb, domain,
908 netbiosname, machinepass, domainsid=None,
909 realm=None, dnsdomain=None,
911 key_version_number=1,
912 secure_channel_type=SEC_CHAN_WKSTA):
913 """Add domain join-specific bits to a secrets database.
915 :param secretsdb: Ldb Handle to the secrets database
916 :param machinepass: Machine password
918 attrs = ["whenChanged",
925 if realm is not None:
926 if dnsdomain is None:
927 dnsdomain = realm.lower()
928 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
931 shortname = netbiosname.lower()
933 # We don't need to set msg["flatname"] here, because rdn_name will handle
934 # it, and it causes problems for modifies anyway
935 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
936 msg["secureChannelType"] = [str(secure_channel_type)]
937 msg["objectClass"] = ["top", "primaryDomain"]
938 if dnsname is not None:
939 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
940 msg["realm"] = [realm]
941 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
942 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
943 msg["privateKeytab"] = ["secrets.keytab"]
945 msg["secret"] = [machinepass.encode('utf-8')]
946 msg["samAccountName"] = ["%s$" % netbiosname]
947 msg["secureChannelType"] = [str(secure_channel_type)]
948 if domainsid is not None:
949 msg["objectSid"] = [ndr_pack(domainsid)]
951 # This complex expression tries to ensure that we don't have more
952 # than one record for this SID, realm or netbios domain at a time,
953 # but we don't delete the old record that we are about to modify,
954 # because that would delete the keytab and previous password.
955 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
956 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
957 scope=ldb.SCOPE_ONELEVEL)
960 secretsdb.delete(del_msg.dn)
962 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
965 msg["priorSecret"] = [res[0]["secret"][0]]
967 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
972 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
977 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
983 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
984 secretsdb.modify(msg)
985 secretsdb.rename(res[0].dn, msg.dn)
987 spn = ['HOST/%s' % shortname]
988 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
989 # we are a domain controller then we add servicePrincipalName
990 # entries for the keytab code to update.
991 spn.extend(['HOST/%s' % dnsname])
992 msg["servicePrincipalName"] = spn
997 def setup_secretsdb(paths, session_info, backend_credentials, lp):
998 """Setup the secrets database.
1000 :note: This function does not handle exceptions and transaction on purpose,
1001 it's up to the caller to do this job.
1003 :param path: Path to the secrets database.
1004 :param session_info: Session info.
1005 :param credentials: Credentials
1006 :param lp: Loadparm context
1007 :return: LDB handle for the created secrets database
1009 if os.path.exists(paths.secrets):
1010 os.unlink(paths.secrets)
1012 keytab_path = os.path.join(paths.private_dir, paths.keytab)
1013 if os.path.exists(keytab_path):
1014 os.unlink(keytab_path)
1016 bind_dns_keytab_path = os.path.join(paths.binddns_dir, paths.dns_keytab)
1017 if os.path.exists(bind_dns_keytab_path):
1018 os.unlink(bind_dns_keytab_path)
1020 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1021 if os.path.exists(dns_keytab_path):
1022 os.unlink(dns_keytab_path)
1024 path = paths.secrets
1026 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
1028 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
1029 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
1030 secrets_ldb.transaction_start()
1032 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
1034 if (backend_credentials is not None and
1035 backend_credentials.authentication_requested()):
1036 if backend_credentials.get_bind_dn() is not None:
1037 setup_add_ldif(secrets_ldb,
1038 setup_path("secrets_simple_ldap.ldif"), {
1039 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
1040 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password()).decode('utf8')
1043 setup_add_ldif(secrets_ldb,
1044 setup_path("secrets_sasl_ldap.ldif"), {
1045 "LDAPADMINUSER": backend_credentials.get_username(),
1046 "LDAPADMINREALM": backend_credentials.get_realm(),
1047 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password()).decode('utf8')
1050 secrets_ldb.transaction_cancel()
1055 def setup_privileges(path, session_info, lp):
1056 """Setup the privileges database.
1058 :param path: Path to the privileges database.
1059 :param session_info: Session info.
1060 :param credentials: Credentials
1061 :param lp: Loadparm context
1062 :return: LDB handle for the created secrets database
1064 if os.path.exists(path):
1066 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
1067 privilege_ldb.erase()
1068 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
1071 def setup_encrypted_secrets_key(path):
1072 """Setup the encrypted secrets key file.
1074 Any existing key file will be deleted and a new random key generated.
1076 :param path: Path to the secrets key file.
1079 if os.path.exists(path):
1082 flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
1083 mode = stat.S_IRUSR | stat.S_IWUSR
1085 umask_original = os.umask(0)
1087 fd = os.open(path, flags, mode)
1089 os.umask(umask_original)
1091 with os.fdopen(fd, 'wb') as f:
1092 key = samba.generate_random_bytes(16)
1096 def setup_registry(path, session_info, lp):
1097 """Setup the registry.
1099 :param path: Path to the registry database
1100 :param session_info: Session information
1101 :param credentials: Credentials
1102 :param lp: Loadparm context
1104 reg = samba.registry.Registry()
1105 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
1106 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
1107 provision_reg = setup_path("provision.reg")
1108 assert os.path.exists(provision_reg)
1109 reg.diff_apply(provision_reg)
1112 def setup_idmapdb(path, session_info, lp):
1113 """Setup the idmap database.
1115 :param path: path to the idmap database
1116 :param session_info: Session information
1117 :param credentials: Credentials
1118 :param lp: Loadparm context
1120 if os.path.exists(path):
1123 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
1125 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
1129 def setup_samdb_rootdse(samdb, names):
1130 """Setup the SamDB rootdse.
1132 :param samdb: Sam Database handle
1134 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
1135 "SCHEMADN": names.schemadn,
1136 "DOMAINDN": names.domaindn,
1137 "ROOTDN": names.rootdn,
1138 "CONFIGDN": names.configdn,
1139 "SERVERDN": names.serverdn,
1143 def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
1144 dns_backend, dnspass, domainsid, next_rid, invocationid,
1145 policyguid, policyguid_dc,
1146 domainControllerFunctionality, ntdsguid=None, dc_rid=None):
1147 """Join a host to its own domain."""
1148 assert isinstance(invocationid, str)
1149 if ntdsguid is not None:
1150 ntdsguid_line = "objectGUID: %s\n" % ntdsguid
1157 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
1158 "CONFIGDN": names.configdn,
1159 "SCHEMADN": names.schemadn,
1160 "DOMAINDN": names.domaindn,
1161 "SERVERDN": names.serverdn,
1162 "INVOCATIONID": invocationid,
1163 "NETBIOSNAME": names.netbiosname,
1164 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1165 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')).decode('utf8'),
1166 "DOMAINSID": str(domainsid),
1167 "DCRID": str(dc_rid),
1168 "SAMBA_VERSION_STRING": version,
1169 "NTDSGUID": ntdsguid_line,
1170 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1171 domainControllerFunctionality),
1172 "RIDALLOCATIONSTART": str(next_rid + 100),
1173 "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
1175 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1176 "POLICYGUID": policyguid,
1177 "POLICYGUID_DC": policyguid_dc,
1178 "DNSDOMAIN": names.dnsdomain,
1179 "DOMAINDN": names.domaindn})
1181 # If we are setting up a subdomain, then this has been replicated in, so we
1182 # don't need to add it
1183 if fill == FILL_FULL:
1184 setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
1185 "CONFIGDN": names.configdn,
1186 "SCHEMADN": names.schemadn,
1187 "DOMAINDN": names.domaindn,
1188 "SERVERDN": names.serverdn,
1189 "INVOCATIONID": invocationid,
1190 "NETBIOSNAME": names.netbiosname,
1191 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1192 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')).decode('utf8'),
1193 "DOMAINSID": str(domainsid),
1194 "DCRID": str(dc_rid),
1195 "SAMBA_VERSION_STRING": version,
1196 "NTDSGUID": ntdsguid_line,
1197 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1198 domainControllerFunctionality)})
1200 # Setup fSMORoleOwner entries to point at the newly created DC entry
1201 setup_modify_ldif(samdb,
1202 setup_path("provision_self_join_modify_config.ldif"), {
1203 "CONFIGDN": names.configdn,
1204 "SCHEMADN": names.schemadn,
1205 "DEFAULTSITE": names.sitename,
1206 "NETBIOSNAME": names.netbiosname,
1207 "SERVERDN": names.serverdn,
1210 system_session_info = system_session()
1211 samdb.set_session_info(system_session_info)
1212 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1213 # modify a serverReference under cn=config when we are a subdomain, we must
1214 # be system due to ACLs
1215 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1216 "DOMAINDN": names.domaindn,
1217 "SERVERDN": names.serverdn,
1218 "NETBIOSNAME": names.netbiosname,
1221 samdb.set_session_info(admin_session_info)
1223 if dns_backend != "SAMBA_INTERNAL":
1224 # This is Samba4 specific and should be replaced by the correct
1225 # DNS AD-style setup
1226 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1227 "DNSDOMAIN": names.dnsdomain,
1228 "DOMAINDN": names.domaindn,
1229 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')).decode('utf8'),
1230 "HOSTNAME": names.hostname,
1231 "DNSNAME": '%s.%s' % (
1232 names.netbiosname.lower(), names.dnsdomain.lower())
1236 def getpolicypath(sysvolpath, dnsdomain, guid):
1237 """Return the physical path of policy given its guid.
1239 :param sysvolpath: Path to the sysvol folder
1240 :param dnsdomain: DNS name of the AD domain
1241 :param guid: The GUID of the policy
1242 :return: A string with the complete path to the policy folder
1245 guid = "{%s}" % guid
1246 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1250 def create_gpo_struct(policy_path):
1251 if not os.path.exists(policy_path):
1252 os.makedirs(policy_path, 0o775)
1253 f = open(os.path.join(policy_path, "GPT.INI"), 'w')
1255 f.write("[General]\r\nVersion=0")
1258 p = os.path.join(policy_path, "MACHINE")
1259 if not os.path.exists(p):
1260 os.makedirs(p, 0o775)
1261 p = os.path.join(policy_path, "USER")
1262 if not os.path.exists(p):
1263 os.makedirs(p, 0o775)
1266 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1267 """Create the default GPO for a domain
1269 :param sysvolpath: Physical path for the sysvol folder
1270 :param dnsdomain: DNS domain name of the AD domain
1271 :param policyguid: GUID of the default domain policy
1272 :param policyguid_dc: GUID of the default domain controler policy
1274 policy_path = getpolicypath(sysvolpath, dnsdomain, policyguid)
1275 create_gpo_struct(policy_path)
1277 policy_path = getpolicypath(sysvolpath, dnsdomain, policyguid_dc)
1278 create_gpo_struct(policy_path)
1281 def setup_samdb(path, session_info, provision_backend, lp, names,
1282 logger, fill, serverrole, schema, am_rodc=False,
1283 plaintext_secrets=False, backend_store=None):
1284 """Setup a complete SAM Database.
1286 :note: This will wipe the main SAM database file!
1289 # Also wipes the database
1290 setup_samdb_partitions(path, logger=logger, lp=lp,
1291 provision_backend=provision_backend, session_info=session_info,
1292 names=names, serverrole=serverrole, plaintext_secrets=plaintext_secrets,
1293 backend_store=backend_store)
1295 # Load the database, but don's load the global schema and don't connect
1297 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1298 credentials=provision_backend.credentials, lp=lp,
1299 global_schema=False, am_rodc=am_rodc)
1301 logger.info("Pre-loading the Samba 4 and AD schema")
1303 # Load the schema from the one we computed earlier
1304 samdb.set_schema(schema, write_indices_and_attributes=False)
1306 # Set the NTDS settings DN manually - in order to have it already around
1307 # before the provisioned tree exists and we connect
1308 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1310 # And now we can connect to the DB - the schema won't be loaded from the
1314 except ldb.LdbError as e2:
1315 (num, string_error) = e2.args
1316 if (num == ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS):
1317 raise ProvisioningError("Permission denied connecting to %s, are you running as root?" % path)
1321 # But we have to give it one more kick to have it use the schema
1322 # during provision - it needs, now that it is connected, to write
1323 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1324 samdb.set_schema(schema, write_indices_and_attributes=True)
1329 def fill_samdb(samdb, lp, names, logger, policyguid,
1330 policyguid_dc, fill, adminpass, krbtgtpass, machinepass, dns_backend,
1331 dnspass, invocationid, ntdsguid, serverrole, am_rodc=False,
1332 dom_for_fun_level=None, schema=None, next_rid=None, dc_rid=None,
1333 backend_store=None):
1335 if next_rid is None:
1338 # Provision does not make much sense values larger than 1000000000
1339 # as the upper range of the rIDAvailablePool is 1073741823 and
1340 # we don't want to create a domain that cannot allocate rids.
1341 if next_rid < 1000 or next_rid > 1000000000:
1342 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1343 error += "the valid range is %u-%u. The default is %u." % (
1344 1000, 1000000000, 1000)
1345 raise ProvisioningError(error)
1347 # ATTENTION: Do NOT change these default values without discussion with the
1348 # team and/or release manager. They have a big impact on the whole program!
1349 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1351 if dom_for_fun_level is None:
1352 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
1354 if dom_for_fun_level > domainControllerFunctionality:
1355 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!")
1357 domainFunctionality = dom_for_fun_level
1358 forestFunctionality = dom_for_fun_level
1360 # Set the NTDS settings DN manually - in order to have it already around
1361 # before the provisioned tree exists and we connect
1362 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1364 # Set the domain functionality levels onto the database.
1365 # Various module (the password_hash module in particular) need
1366 # to know what level of AD we are emulating.
1368 # These will be fixed into the database via the database
1369 # modifictions below, but we need them set from the start.
1370 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1371 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1372 samdb.set_opaque_integer("domainControllerFunctionality",
1373 domainControllerFunctionality)
1375 samdb.set_domain_sid(str(names.domainsid))
1376 samdb.set_invocation_id(invocationid)
1378 logger.info("Adding DomainDN: %s" % names.domaindn)
1380 # impersonate domain admin
1381 admin_session_info = admin_session(lp, str(names.domainsid))
1382 samdb.set_session_info(admin_session_info)
1383 if names.domainguid is not None:
1384 domainguid_line = "objectGUID: %s\n-" % names.domainguid
1386 domainguid_line = ""
1388 descr = b64encode(get_domain_descriptor(names.domainsid)).decode('utf8')
1389 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1390 "DOMAINDN": names.domaindn,
1391 "DOMAINSID": str(names.domainsid),
1392 "DESCRIPTOR": descr,
1393 "DOMAINGUID": domainguid_line
1396 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1397 "DOMAINDN": names.domaindn,
1398 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1399 "NEXTRID": str(next_rid),
1400 "DEFAULTSITE": names.sitename,
1401 "CONFIGDN": names.configdn,
1402 "POLICYGUID": policyguid,
1403 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1404 "SAMBA_VERSION_STRING": version,
1405 "MIN_PWD_LENGTH": str(DEFAULT_MIN_PWD_LENGTH)
1408 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1409 if fill == FILL_FULL:
1410 logger.info("Adding configuration container")
1411 descr = b64encode(get_config_descriptor(names.domainsid)).decode('utf8')
1412 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1413 "CONFIGDN": names.configdn,
1414 "DESCRIPTOR": descr,
1417 # The LDIF here was created when the Schema object was constructed
1418 ignore_checks_oid = "local_oid:%s:0" % samba.dsdb.DSDB_CONTROL_SKIP_DUPLICATES_CHECK_OID
1419 logger.info("Setting up sam.ldb schema")
1420 samdb.add_ldif(schema.schema_dn_add,
1421 controls=["relax:0", ignore_checks_oid])
1422 samdb.modify_ldif(schema.schema_dn_modify,
1423 controls=[ignore_checks_oid])
1424 samdb.write_prefixes_from_schema()
1425 samdb.add_ldif(schema.schema_data, controls=["relax:0", ignore_checks_oid])
1426 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1427 {"SCHEMADN": names.schemadn},
1428 controls=["relax:0", ignore_checks_oid])
1430 # Now register this container in the root of the forest
1431 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1432 msg["subRefs"] = ldb.MessageElement(names.configdn, ldb.FLAG_MOD_ADD,
1435 samdb.invocation_id = invocationid
1437 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1438 if fill == FILL_FULL:
1439 logger.info("Setting up sam.ldb configuration data")
1441 partitions_descr = b64encode(get_config_partitions_descriptor(names.domainsid)).decode('utf8')
1442 sites_descr = b64encode(get_config_sites_descriptor(names.domainsid)).decode('utf8')
1443 ntdsquotas_descr = b64encode(get_config_ntds_quotas_descriptor(names.domainsid)).decode('utf8')
1444 protected1_descr = b64encode(get_config_delete_protected1_descriptor(names.domainsid)).decode('utf8')
1445 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid)).decode('utf8')
1446 protected2_descr = b64encode(get_config_delete_protected2_descriptor(names.domainsid)).decode('utf8')
1448 if "2008" in schema.base_schema:
1449 # exclude 2012-specific changes if we're using a 2008 schema
1454 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1455 "CONFIGDN": names.configdn,
1456 "NETBIOSNAME": names.netbiosname,
1457 "DEFAULTSITE": names.sitename,
1458 "DNSDOMAIN": names.dnsdomain,
1459 "DOMAIN": names.domain,
1460 "SCHEMADN": names.schemadn,
1461 "DOMAINDN": names.domaindn,
1462 "SERVERDN": names.serverdn,
1463 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1464 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1465 "NTDSQUOTAS_DESCRIPTOR": ntdsquotas_descr,
1466 "LOSTANDFOUND_DESCRIPTOR": protected1wd_descr,
1467 "SERVICES_DESCRIPTOR": protected1_descr,
1468 "PHYSICALLOCATIONS_DESCRIPTOR": protected1wd_descr,
1469 "FORESTUPDATES_DESCRIPTOR": protected1wd_descr,
1470 "EXTENDEDRIGHTS_DESCRIPTOR": protected2_descr,
1471 "PARTITIONS_DESCRIPTOR": partitions_descr,
1472 "SITES_DESCRIPTOR": sites_descr,
1475 setup_add_ldif(samdb, setup_path("extended-rights.ldif"), {
1476 "CONFIGDN": names.configdn,
1477 "INC2012": incl_2012,
1480 logger.info("Setting up display specifiers")
1481 display_specifiers_ldif = read_ms_ldif(
1482 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1483 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1484 {"CONFIGDN": names.configdn})
1485 check_all_substituted(display_specifiers_ldif)
1486 samdb.add_ldif(display_specifiers_ldif)
1488 logger.info("Modifying display specifiers and extended rights")
1489 setup_modify_ldif(samdb,
1490 setup_path("provision_configuration_modify.ldif"), {
1491 "CONFIGDN": names.configdn,
1492 "DISPLAYSPECIFIERS_DESCRIPTOR": protected2_descr
1495 logger.info("Adding users container")
1496 users_desc = b64encode(get_domain_users_descriptor(names.domainsid)).decode('utf8')
1497 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1498 "DOMAINDN": names.domaindn,
1499 "USERS_DESCRIPTOR": users_desc
1501 logger.info("Modifying users container")
1502 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1503 "DOMAINDN": names.domaindn})
1504 logger.info("Adding computers container")
1505 computers_desc = b64encode(get_domain_computers_descriptor(names.domainsid)).decode('utf8')
1506 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1507 "DOMAINDN": names.domaindn,
1508 "COMPUTERS_DESCRIPTOR": computers_desc
1510 logger.info("Modifying computers container")
1511 setup_modify_ldif(samdb,
1512 setup_path("provision_computers_modify.ldif"), {
1513 "DOMAINDN": names.domaindn})
1514 logger.info("Setting up sam.ldb data")
1515 infrastructure_desc = b64encode(get_domain_infrastructure_descriptor(names.domainsid)).decode('utf8')
1516 lostandfound_desc = b64encode(get_domain_delete_protected2_descriptor(names.domainsid)).decode('utf8')
1517 system_desc = b64encode(get_domain_delete_protected1_descriptor(names.domainsid)).decode('utf8')
1518 builtin_desc = b64encode(get_domain_builtin_descriptor(names.domainsid)).decode('utf8')
1519 controllers_desc = b64encode(get_domain_controllers_descriptor(names.domainsid)).decode('utf8')
1520 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1521 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1522 "DOMAINDN": names.domaindn,
1523 "NETBIOSNAME": names.netbiosname,
1524 "DEFAULTSITE": names.sitename,
1525 "CONFIGDN": names.configdn,
1526 "SERVERDN": names.serverdn,
1527 "RIDAVAILABLESTART": str(next_rid + 600),
1528 "POLICYGUID_DC": policyguid_dc,
1529 "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc,
1530 "LOSTANDFOUND_DESCRIPTOR": lostandfound_desc,
1531 "SYSTEM_DESCRIPTOR": system_desc,
1532 "BUILTIN_DESCRIPTOR": builtin_desc,
1533 "DOMAIN_CONTROLLERS_DESCRIPTOR": controllers_desc,
1536 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1537 if fill == FILL_FULL:
1538 managedservice_descr = b64encode(get_managed_service_accounts_descriptor(names.domainsid)).decode('utf8')
1539 setup_modify_ldif(samdb,
1540 setup_path("provision_configuration_references.ldif"), {
1541 "CONFIGDN": names.configdn,
1542 "SCHEMADN": names.schemadn})
1544 logger.info("Setting up well known security principals")
1545 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid)).decode('utf8')
1546 setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1547 "CONFIGDN": names.configdn,
1548 "WELLKNOWNPRINCIPALS_DESCRIPTOR": protected1wd_descr,
1549 }, controls=["relax:0", "provision:0"])
1551 if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1552 setup_modify_ldif(samdb,
1553 setup_path("provision_basedn_references.ldif"), {
1554 "DOMAINDN": names.domaindn,
1555 "MANAGEDSERVICE_DESCRIPTOR": managedservice_descr
1558 logger.info("Setting up sam.ldb users and groups")
1559 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1560 "DOMAINDN": names.domaindn,
1561 "DOMAINSID": str(names.domainsid),
1562 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')).decode('utf8'),
1563 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le')).decode('utf8')
1564 }, controls=["relax:0", "provision:0"])
1566 logger.info("Setting up self join")
1567 setup_self_join(samdb, admin_session_info, names=names, fill=fill,
1568 invocationid=invocationid,
1569 dns_backend=dns_backend,
1571 machinepass=machinepass,
1572 domainsid=names.domainsid,
1575 policyguid=policyguid,
1576 policyguid_dc=policyguid_dc,
1577 domainControllerFunctionality=domainControllerFunctionality,
1580 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1581 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1582 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE).decode('utf8')
1583 assert isinstance(names.ntdsguid, string_types)
1588 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1589 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)"
1590 SYSVOL_SERVICE = "sysvol"
1593 def set_dir_acl(path, acl, lp, domsid, use_ntvfs, passdb, service=SYSVOL_SERVICE):
1594 setntacl(lp, path, acl, domsid, use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1595 for root, dirs, files in os.walk(path, topdown=False):
1597 setntacl(lp, os.path.join(root, name), acl, domsid,
1598 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1600 setntacl(lp, os.path.join(root, name), acl, domsid,
1601 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1604 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb):
1605 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1608 :param sysvol: Physical path for the sysvol folder
1609 :param dnsdomain: The DNS name of the domain
1610 :param domainsid: The SID of the domain
1611 :param domaindn: The DN of the domain (ie. DC=...)
1612 :param samdb: An LDB object on the SAM db
1613 :param lp: an LP object
1616 # Set ACL for GPO root folder
1617 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1618 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid),
1619 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=SYSVOL_SERVICE)
1621 res = samdb.search(base="CN=Policies,CN=System,%s" %(domaindn),
1622 attrs=["cn", "nTSecurityDescriptor"],
1623 expression="", scope=ldb.SCOPE_ONELEVEL)
1626 acl = ndr_unpack(security.descriptor,
1627 policy["nTSecurityDescriptor"][0]).as_sddl()
1628 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1629 set_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1630 str(domainsid), use_ntvfs,
1634 def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain,
1635 domaindn, lp, use_ntvfs):
1636 """Set the ACL for the sysvol share and the subfolders
1638 :param samdb: An LDB object on the SAM db
1639 :param netlogon: Physical path for the netlogon folder
1640 :param sysvol: Physical path for the sysvol folder
1641 :param uid: The UID of the "Administrator" user
1642 :param gid: The GID of the "Domain adminstrators" group
1643 :param domainsid: The SID of the domain
1644 :param dnsdomain: The DNS name of the domain
1645 :param domaindn: The DN of the domain (ie. DC=...)
1650 s3conf = s3param.get_context()
1651 s3conf.load(lp.configfile)
1653 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(sysvol))
1656 smbd.set_simple_acl(file.name, 0o755, gid)
1658 if not smbd.have_posix_acls():
1659 # This clue is only strictly correct for RPM and
1660 # Debian-like Linux systems, but hopefully other users
1661 # will get enough clue from it.
1662 raise ProvisioningError("Samba was compiled without the posix ACL support that s3fs requires. "
1663 "Try installing libacl1-dev or libacl-devel, then re-run configure and make.")
1665 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. "
1666 "Try the mounting the filesystem with the 'acl' option.")
1668 smbd.chown(file.name, uid, gid)
1670 raise ProvisioningError("Unable to chown a file on your filesystem. "
1671 "You may not be running provision as root.")
1675 # This will ensure that the smbd code we are running when setting ACLs
1676 # is initialised with the smb.conf
1677 s3conf = s3param.get_context()
1678 s3conf.load(lp.configfile)
1679 # ensure we are using the right samba_dsdb passdb backend, no matter what
1680 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1681 passdb.reload_static_pdb()
1683 # ensure that we init the samba_dsdb backend, so the domain sid is
1684 # marked in secrets.tdb
1685 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1687 # now ensure everything matches correctly, to avoid wierd issues
1688 if passdb.get_global_sam_sid() != domainsid:
1689 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))
1691 domain_info = s4_passdb.domain_info()
1692 if domain_info["dom_sid"] != domainsid:
1693 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))
1695 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1696 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()))
1700 os.chown(sysvol, -1, gid)
1706 # use admin sid dn as user dn, since admin should own most of the files,
1707 # the operation will be much faster
1708 userdn = '<SID={}-{}>'.format(domainsid, security.DOMAIN_RID_ADMINISTRATOR)
1710 flags = (auth.AUTH_SESSION_INFO_DEFAULT_GROUPS |
1711 auth.AUTH_SESSION_INFO_AUTHENTICATED |
1712 auth.AUTH_SESSION_INFO_SIMPLE_PRIVILEGES)
1714 session_info = auth.user_session(samdb, lp_ctx=lp, dn=userdn,
1715 session_info_flags=flags)
1717 def _setntacl(path):
1718 """A helper to reuse args"""
1720 lp, path, SYSVOL_ACL, str(domainsid),
1721 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=s4_passdb,
1722 service=SYSVOL_SERVICE, session_info=session_info)
1724 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1726 for root, dirs, files in os.walk(sysvol, topdown=False):
1728 if use_ntvfs and canchown:
1729 os.chown(os.path.join(root, name), -1, gid)
1730 _setntacl(os.path.join(root, name))
1732 if use_ntvfs and canchown:
1733 os.chown(os.path.join(root, name), -1, gid)
1734 _setntacl(os.path.join(root, name))
1736 # Set acls on Policy folder and policies folders
1737 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb=s4_passdb)
1740 def acl_type(direct_db_access):
1741 if direct_db_access:
1747 def check_dir_acl(path, acl, lp, domainsid, direct_db_access):
1748 fsacl = getntacl(lp, path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1749 fsacl_sddl = fsacl.as_sddl(domainsid)
1750 if fsacl_sddl != acl:
1751 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))
1753 for root, dirs, files in os.walk(path, topdown=False):
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 file %s not found!' %
1759 (acl_type(direct_db_access),
1760 os.path.join(root, name)))
1761 fsacl_sddl = fsacl.as_sddl(domainsid)
1762 if fsacl_sddl != acl:
1763 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))
1766 fsacl = getntacl(lp, os.path.join(root, name),
1767 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1769 raise ProvisioningError('%s ACL on GPO directory %s not found!'
1770 % (acl_type(direct_db_access),
1771 os.path.join(root, name)))
1772 fsacl_sddl = fsacl.as_sddl(domainsid)
1773 if fsacl_sddl != acl:
1774 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))
1777 def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1779 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1782 :param sysvol: Physical path for the sysvol folder
1783 :param dnsdomain: The DNS name of the domain
1784 :param domainsid: The SID of the domain
1785 :param domaindn: The DN of the domain (ie. DC=...)
1786 :param samdb: An LDB object on the SAM db
1787 :param lp: an LP object
1790 # Set ACL for GPO root folder
1791 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1792 fsacl = getntacl(lp, root_policy_path,
1793 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1795 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access), root_policy_path))
1796 fsacl_sddl = fsacl.as_sddl(domainsid)
1797 if fsacl_sddl != POLICIES_ACL:
1798 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))
1799 res = samdb.search(base="CN=Policies,CN=System,%s" %(domaindn),
1800 attrs=["cn", "nTSecurityDescriptor"],
1801 expression="", scope=ldb.SCOPE_ONELEVEL)
1804 acl = ndr_unpack(security.descriptor,
1805 policy["nTSecurityDescriptor"][0]).as_sddl()
1806 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1807 check_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1808 domainsid, direct_db_access)
1811 def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn,
1813 """Set the ACL for the sysvol share and the subfolders
1815 :param samdb: An LDB object on the SAM db
1816 :param netlogon: Physical path for the netlogon folder
1817 :param sysvol: Physical path for the sysvol folder
1818 :param uid: The UID of the "Administrator" user
1819 :param gid: The GID of the "Domain adminstrators" group
1820 :param domainsid: The SID of the domain
1821 :param dnsdomain: The DNS name of the domain
1822 :param domaindn: The DN of the domain (ie. DC=...)
1825 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1826 s3conf = s3param.get_context()
1827 s3conf.load(lp.configfile)
1828 # ensure we are using the right samba_dsdb passdb backend, no matter what
1829 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1830 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1831 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1833 # now ensure everything matches correctly, to avoid wierd issues
1834 if passdb.get_global_sam_sid() != domainsid:
1835 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))
1837 domain_info = s4_passdb.domain_info()
1838 if domain_info["dom_sid"] != domainsid:
1839 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))
1841 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1842 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()))
1844 # Ensure we can read this directly, and via the smbd VFS
1845 for direct_db_access in [True, False]:
1846 # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1847 for dir_path in [os.path.join(sysvol, dnsdomain), netlogon]:
1848 fsacl = getntacl(lp, dir_path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1850 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access), dir_path))
1851 fsacl_sddl = fsacl.as_sddl(domainsid)
1852 if fsacl_sddl != SYSVOL_ACL:
1853 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))
1855 # Check acls on Policy folder and policies folders
1856 check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1860 def interface_ips_v4(lp):
1861 """return only IPv4 IPs"""
1862 ips = samba.interface_ips(lp, False)
1865 if i.find(':') == -1:
1870 def interface_ips_v6(lp):
1871 """return only IPv6 IPs"""
1872 ips = samba.interface_ips(lp, False)
1875 if i.find(':') != -1:
1880 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1882 targetdir=None, samdb_fill=FILL_FULL,
1883 hostip=None, hostip6=None,
1884 next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1885 domainguid=None, policyguid=None, policyguid_dc=None,
1886 invocationid=None, machinepass=None, ntdsguid=None,
1887 dns_backend=None, dnspass=None,
1888 serverrole=None, dom_for_fun_level=None,
1889 am_rodc=False, lp=None, use_ntvfs=False,
1890 skip_sysvolacl=False, backend_store=None):
1891 # create/adapt the group policy GUIDs
1892 # Default GUID for default policy are described at
1893 # "How Core Group Policy Works"
1894 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1895 if policyguid is None:
1896 policyguid = DEFAULT_POLICY_GUID
1897 policyguid = policyguid.upper()
1898 if policyguid_dc is None:
1899 policyguid_dc = DEFAULT_DC_POLICY_GUID
1900 policyguid_dc = policyguid_dc.upper()
1902 if invocationid is None:
1903 invocationid = str(uuid.uuid4())
1905 if krbtgtpass is None:
1906 krbtgtpass = samba.generate_random_machine_password(128, 255)
1907 if machinepass is None:
1908 machinepass = samba.generate_random_machine_password(128, 255)
1910 dnspass = samba.generate_random_password(128, 255)
1912 samdb.transaction_start()
1914 samdb = fill_samdb(samdb, lp, names, logger=logger,
1916 policyguid=policyguid, policyguid_dc=policyguid_dc,
1917 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1918 invocationid=invocationid, machinepass=machinepass,
1919 dns_backend=dns_backend, dnspass=dnspass,
1920 ntdsguid=ntdsguid, serverrole=serverrole,
1921 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1922 next_rid=next_rid, dc_rid=dc_rid,
1923 backend_store=backend_store)
1925 # Set up group policies (domain policy and domain controller
1927 if serverrole == "active directory domain controller":
1928 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1931 samdb.transaction_cancel()
1934 samdb.transaction_commit()
1936 if serverrole == "active directory domain controller":
1937 # Continue setting up sysvol for GPO. This appears to require being
1938 # outside a transaction.
1939 if not skip_sysvolacl:
1940 setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.root_uid,
1941 paths.root_gid, names.domainsid, names.dnsdomain,
1942 names.domaindn, lp, use_ntvfs)
1944 logger.info("Setting acl on sysvol skipped")
1946 secretsdb_self_join(secrets_ldb, domain=names.domain,
1947 realm=names.realm, dnsdomain=names.dnsdomain,
1948 netbiosname=names.netbiosname, domainsid=names.domainsid,
1949 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1951 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1952 # In future, this might be determined from some configuration
1953 kerberos_enctypes = str(ENC_ALL_TYPES)
1956 msg = ldb.Message(ldb.Dn(samdb,
1957 samdb.searchone("distinguishedName",
1958 expression="samAccountName=%s$" % names.netbiosname,
1959 scope=ldb.SCOPE_SUBTREE).decode('utf8')))
1960 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1961 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1962 name="msDS-SupportedEncryptionTypes")
1964 except ldb.LdbError as e:
1965 (enum, estr) = e.args
1966 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1967 # It might be that this attribute does not exist in this schema
1970 setup_ad_dns(samdb, secrets_ldb, names, paths, lp, logger,
1971 hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
1972 dnspass=dnspass, os_level=dom_for_fun_level,
1973 targetdir=targetdir, fill_level=samdb_fill,
1974 backend_store=backend_store)
1976 domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1977 attribute="objectGUID").decode('utf8')
1978 assert isinstance(domainguid, string_types)
1980 lastProvisionUSNs = get_last_provision_usn(samdb)
1981 maxUSN = get_max_usn(samdb, str(names.rootdn))
1982 if lastProvisionUSNs is not None:
1983 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1985 set_provision_usn(samdb, 0, maxUSN, invocationid)
1987 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1988 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1989 {'NTDSGUID': names.ntdsguid})
1991 # fix any dangling GUIDs from the provision
1992 logger.info("Fixing provision GUIDs")
1993 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
1995 samdb.transaction_start()
1997 # a small number of GUIDs are missing because of ordering issues in the
1999 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
2000 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
2001 scope=ldb.SCOPE_BASE,
2002 attrs=['defaultObjectCategory'])
2003 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
2004 scope=ldb.SCOPE_ONELEVEL,
2005 attrs=['ipsecOwnersReference',
2006 'ipsecFilterReference',
2007 'ipsecISAKMPReference',
2008 'ipsecNegotiationPolicyReference',
2009 'ipsecNFAReference'])
2010 if chk.check_database(DN=names.schemadn, scope=ldb.SCOPE_SUBTREE,
2011 attrs=['attributeId', 'governsId']) != 0:
2012 raise ProvisioningError("Duplicate attributeId or governsId in schema. Must be fixed manually!!")
2014 samdb.transaction_cancel()
2017 samdb.transaction_commit()
2021 "ROLE_STANDALONE": "standalone server",
2022 "ROLE_DOMAIN_MEMBER": "member server",
2023 "ROLE_DOMAIN_BDC": "active directory domain controller",
2024 "ROLE_DOMAIN_PDC": "active directory domain controller",
2025 "dc": "active directory domain controller",
2026 "member": "member server",
2027 "domain controller": "active directory domain controller",
2028 "active directory domain controller": "active directory domain controller",
2029 "member server": "member server",
2030 "standalone": "standalone server",
2031 "standalone server": "standalone server",
2035 def sanitize_server_role(role):
2036 """Sanitize a server role name.
2038 :param role: Server role
2039 :raise ValueError: If the role can not be interpreted
2040 :return: Sanitized server role (one of "member server",
2041 "active directory domain controller", "standalone server")
2044 return _ROLES_MAP[role]
2046 raise ValueError(role)
2049 def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain,
2051 """Create AD entries for the fake ypserver.
2053 This is needed for being able to manipulate posix attrs via ADUC.
2055 samdb.transaction_start()
2057 logger.info("Setting up fake yp server settings")
2058 setup_add_ldif(samdb, setup_path("ypServ30.ldif"), {
2059 "DOMAINDN": domaindn,
2060 "NETBIOSNAME": netbiosname,
2061 "NISDOMAIN": nisdomain,
2064 samdb.transaction_cancel()
2067 samdb.transaction_commit()
2070 def directory_create_or_exists(path, mode=0o755):
2071 if not os.path.exists(path):
2073 os.mkdir(path, mode)
2074 except OSError as e:
2075 if e.errno in [errno.EEXIST]:
2078 raise ProvisioningError("Failed to create directory %s: %s" % (path, e.strerror))
2081 def determine_host_ip(logger, lp, hostip=None):
2083 logger.info("Looking up IPv4 addresses")
2084 hostips = interface_ips_v4(lp)
2085 if len(hostips) > 0:
2087 if len(hostips) > 1:
2088 logger.warning("More than one IPv4 address found. Using %s",
2090 if hostip == "127.0.0.1":
2093 logger.warning("No IPv4 address will be assigned")
2098 def determine_host_ip6(logger, lp, hostip6=None):
2100 logger.info("Looking up IPv6 addresses")
2101 hostips = interface_ips_v6(lp)
2103 hostip6 = hostips[0]
2104 if len(hostips) > 1:
2105 logger.warning("More than one IPv6 address found. Using %s", hostip6)
2107 logger.warning("No IPv6 address will be assigned")
2112 def provision(logger, session_info, smbconf=None,
2113 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
2114 domaindn=None, schemadn=None, configdn=None, serverdn=None,
2115 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
2116 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None,
2117 krbtgtpass=None, domainguid=None, policyguid=None, policyguid_dc=None,
2118 dns_backend=None, dns_forwarder=None, dnspass=None,
2119 invocationid=None, machinepass=None, ntdsguid=None,
2120 root=None, nobody=None, users=None, backup=None, aci=None,
2121 serverrole=None, dom_for_fun_level=None, backend_type=None,
2122 sitename=None, ol_mmr_urls=None, ol_olc=None, slapd_path=None,
2123 useeadb=False, am_rodc=False, lp=None, use_ntvfs=False,
2124 use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True,
2125 ldap_backend_forced_uri=None, nosync=False, ldap_dryrun_mode=False,
2126 ldap_backend_extra_port=None, base_schema=None,
2127 plaintext_secrets=False, backend_store=None):
2130 :note: caution, this wipes all existing data!
2134 serverrole = sanitize_server_role(serverrole)
2136 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole)
2138 if ldapadminpass is None:
2139 # Make a new, random password between Samba and it's LDAP server
2140 ldapadminpass = samba.generate_random_password(128, 255)
2142 if backend_type is None:
2143 backend_type = "ldb"
2144 if backend_store is None:
2145 backend_store = get_default_backend_store()
2147 if domainsid is None:
2148 domainsid = security.random_sid()
2150 root_uid = findnss_uid([root or "root"])
2151 nobody_uid = findnss_uid([nobody or "nobody"])
2152 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
2153 root_gid = pwd.getpwuid(root_uid).pw_gid
2156 bind_gid = findnss_gid(["bind", "named"])
2160 if targetdir is not None:
2161 smbconf = os.path.join(targetdir, "etc", "smb.conf")
2162 elif smbconf is None:
2163 smbconf = samba.param.default_path()
2164 if not os.path.exists(os.path.dirname(smbconf)):
2165 os.makedirs(os.path.dirname(smbconf))
2167 server_services = []
2170 global_param["idmap_ldb:use rfc2307"] = ["yes"]
2172 if dns_backend != "SAMBA_INTERNAL":
2173 server_services.append("-dns")
2175 if dns_forwarder is not None:
2176 global_param["dns forwarder"] = [dns_forwarder]
2179 server_services.append("+smb")
2180 server_services.append("-s3fs")
2181 global_param["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
2183 if len(server_services) > 0:
2184 global_param["server services"] = server_services
2186 # only install a new smb.conf if there isn't one there already
2187 if os.path.exists(smbconf):
2188 # if Samba Team members can't figure out the weird errors
2189 # loading an empty smb.conf gives, then we need to be smarter.
2190 # Pretend it just didn't exist --abartlet
2191 f = open(smbconf, 'r')
2193 data = f.read().lstrip()
2196 if data is None or data == "":
2197 make_smbconf(smbconf, hostname, domain, realm,
2198 targetdir, serverrole=serverrole,
2199 eadb=useeadb, use_ntvfs=use_ntvfs,
2200 lp=lp, global_param=global_param)
2202 make_smbconf(smbconf, hostname, domain, realm, targetdir,
2203 serverrole=serverrole,
2204 eadb=useeadb, use_ntvfs=use_ntvfs, lp=lp, global_param=global_param)
2207 lp = samba.param.LoadParm()
2209 names = guess_names(lp=lp, hostname=hostname, domain=domain,
2210 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
2211 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
2212 sitename=sitename, rootdn=rootdn, domain_names_forced=(samdb_fill == FILL_DRS))
2213 paths = provision_paths_from_lp(lp, names.dnsdomain)
2215 paths.bind_gid = bind_gid
2216 paths.root_uid = root_uid
2217 paths.root_gid = root_gid
2219 hostip = determine_host_ip(logger, lp, hostip)
2220 hostip6 = determine_host_ip6(logger, lp, hostip6)
2221 names.hostip = hostip
2222 names.hostip6 = hostip6
2223 names.domainguid = domainguid
2224 names.domainsid = domainsid
2225 names.forestsid = domainsid
2227 if serverrole is None:
2228 serverrole = lp.get("server role")
2230 directory_create_or_exists(paths.private_dir, 0o700)
2231 directory_create_or_exists(paths.binddns_dir, 0o770)
2232 directory_create_or_exists(os.path.join(paths.private_dir, "tls"))
2233 directory_create_or_exists(paths.state_dir)
2234 if not plaintext_secrets:
2235 setup_encrypted_secrets_key(paths.encrypted_secrets_key_path)
2237 if paths.sysvol and not os.path.exists(paths.sysvol):
2238 os.makedirs(paths.sysvol, 0o775)
2240 ldapi_url = "ldapi://%s" % urllib_quote(paths.s4_ldapi_path, safe="")
2242 schema = Schema(domainsid, invocationid=invocationid,
2243 schemadn=names.schemadn, base_schema=base_schema)
2245 if backend_type == "ldb":
2246 provision_backend = LDBBackend(backend_type, paths=paths,
2248 names=names, logger=logger)
2249 elif backend_type == "fedora-ds":
2250 provision_backend = FDSBackend(backend_type, paths=paths,
2252 names=names, logger=logger, domainsid=domainsid,
2253 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2254 slapd_path=slapd_path,
2256 elif backend_type == "openldap":
2257 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
2259 names=names, logger=logger, domainsid=domainsid,
2260 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2261 slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls,
2262 ldap_backend_extra_port=ldap_backend_extra_port,
2263 ldap_dryrun_mode=ldap_dryrun_mode, nosync=nosync,
2264 ldap_backend_forced_uri=ldap_backend_forced_uri)
2266 raise ValueError("Unknown LDAP backend type selected")
2268 provision_backend.init()
2269 provision_backend.start()
2271 # only install a new shares config db if there is none
2272 if not os.path.exists(paths.shareconf):
2273 logger.info("Setting up share.ldb")
2274 share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
2275 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
2277 logger.info("Setting up secrets.ldb")
2278 secrets_ldb = setup_secretsdb(paths,
2279 session_info=session_info,
2280 backend_credentials=provision_backend.credentials, lp=lp)
2283 logger.info("Setting up the registry")
2284 setup_registry(paths.hklm, session_info, lp=lp)
2286 logger.info("Setting up the privileges database")
2287 setup_privileges(paths.privilege, session_info, lp=lp)
2289 logger.info("Setting up idmap db")
2290 idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
2292 setup_name_mappings(idmap, sid=str(domainsid),
2293 root_uid=root_uid, nobody_uid=nobody_uid,
2294 users_gid=users_gid, root_gid=root_gid)
2296 logger.info("Setting up SAM db")
2297 samdb = setup_samdb(paths.samdb, session_info,
2298 provision_backend, lp, names, logger=logger,
2299 serverrole=serverrole,
2300 schema=schema, fill=samdb_fill, am_rodc=am_rodc,
2301 plaintext_secrets=plaintext_secrets,
2302 backend_store=backend_store)
2304 if serverrole == "active directory domain controller":
2305 if paths.netlogon is None:
2306 raise MissingShareError("netlogon", paths.smbconf)
2308 if paths.sysvol is None:
2309 raise MissingShareError("sysvol", paths.smbconf)
2311 if not os.path.isdir(paths.netlogon):
2312 os.makedirs(paths.netlogon, 0o755)
2314 if adminpass is None:
2315 adminpass = samba.generate_random_password(12, 32)
2316 adminpass_generated = True
2318 if isinstance(adminpass, binary_type):
2319 adminpass = adminpass.decode('utf-8')
2320 adminpass_generated = False
2322 if samdb_fill == FILL_FULL:
2323 provision_fill(samdb, secrets_ldb, logger, names, paths,
2324 schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
2325 hostip=hostip, hostip6=hostip6,
2326 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
2327 krbtgtpass=krbtgtpass,
2328 policyguid=policyguid, policyguid_dc=policyguid_dc,
2329 invocationid=invocationid, machinepass=machinepass,
2330 ntdsguid=ntdsguid, dns_backend=dns_backend,
2331 dnspass=dnspass, serverrole=serverrole,
2332 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
2333 lp=lp, use_ntvfs=use_ntvfs,
2334 skip_sysvolacl=skip_sysvolacl,
2335 backend_store=backend_store)
2337 if not is_heimdal_built():
2338 create_kdc_conf(paths.kdcconf, realm, domain, os.path.dirname(lp.get("log file")))
2339 logger.info("The Kerberos KDC configuration for Samba AD is "
2340 "located at %s", paths.kdcconf)
2342 create_krb5_conf(paths.krb5conf,
2343 dnsdomain=names.dnsdomain, hostname=names.hostname,
2345 logger.info("A Kerberos configuration suitable for Samba AD has been "
2346 "generated at %s", paths.krb5conf)
2347 logger.info("Merge the contents of this file with your system "
2348 "krb5.conf or replace it with this one. Do not create a "
2351 if serverrole == "active directory domain controller":
2352 create_dns_update_list(lp, logger, paths)
2354 backend_result = provision_backend.post_setup()
2355 provision_backend.shutdown()
2358 secrets_ldb.transaction_cancel()
2361 # Now commit the secrets.ldb to disk
2362 secrets_ldb.transaction_commit()
2364 # the commit creates the dns.keytab in the private directory
2365 private_dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
2366 bind_dns_keytab_path = os.path.join(paths.binddns_dir, paths.dns_keytab)
2368 if os.path.isfile(private_dns_keytab_path):
2369 if os.path.isfile(bind_dns_keytab_path):
2371 os.unlink(bind_dns_keytab_path)
2372 except OSError as e:
2373 logger.error("Failed to remove %s: %s" %
2374 (bind_dns_keytab_path, e.strerror))
2376 # link the dns.keytab to the bind-dns directory
2378 os.link(private_dns_keytab_path, bind_dns_keytab_path)
2379 except OSError as e:
2380 logger.error("Failed to create link %s -> %s: %s" %
2381 (private_dns_keytab_path, bind_dns_keytab_path, e.strerror))
2383 # chown the dns.keytab in the bind-dns directory
2384 if paths.bind_gid is not None:
2386 os.chmod(paths.binddns_dir, 0o770)
2387 os.chown(paths.binddns_dir, -1, paths.bind_gid)
2389 if 'SAMBA_SELFTEST' not in os.environ:
2390 logger.info("Failed to chown %s to bind gid %u",
2391 paths.binddns_dir, paths.bind_gid)
2394 os.chmod(bind_dns_keytab_path, 0o640)
2395 os.chown(bind_dns_keytab_path, -1, paths.bind_gid)
2397 if 'SAMBA_SELFTEST' not in os.environ:
2398 logger.info("Failed to chown %s to bind gid %u",
2399 bind_dns_keytab_path, paths.bind_gid)
2401 result = ProvisionResult()
2402 result.server_role = serverrole
2403 result.domaindn = domaindn
2404 result.paths = paths
2405 result.names = names
2407 result.samdb = samdb
2408 result.idmap = idmap
2409 result.domainsid = str(domainsid)
2411 if samdb_fill == FILL_FULL:
2412 result.adminpass_generated = adminpass_generated
2413 result.adminpass = adminpass
2415 result.adminpass_generated = False
2416 result.adminpass = None
2418 result.backend_result = backend_result
2421 provision_fake_ypserver(logger=logger, samdb=samdb,
2422 domaindn=names.domaindn, netbiosname=names.netbiosname,
2423 nisdomain=names.domain.lower(), maxuid=maxuid, maxgid=maxgid)
2428 def provision_become_dc(smbconf=None, targetdir=None,
2429 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
2430 serverdn=None, domain=None, hostname=None, domainsid=None,
2431 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
2432 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
2433 dns_backend=None, root=None, nobody=None, users=None,
2434 backup=None, serverrole=None, ldap_backend=None,
2435 ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False):
2437 logger = logging.getLogger("provision")
2438 samba.set_debug_level(debuglevel)
2440 res = provision(logger, system_session(),
2441 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
2442 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
2443 configdn=configdn, serverdn=serverdn, domain=domain,
2444 hostname=hostname, hostip=None, domainsid=domainsid,
2445 machinepass=machinepass,
2446 serverrole="active directory domain controller",
2447 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass,
2448 use_ntvfs=use_ntvfs)
2449 res.lp.set("debuglevel", str(debuglevel))
2453 def create_krb5_conf(path, dnsdomain, hostname, realm):
2454 """Write out a file containing a valid krb5.conf file
2456 :param path: Path of the new krb5.conf file.
2457 :param dnsdomain: DNS Domain name
2458 :param hostname: Local hostname
2459 :param realm: Realm name
2461 setup_file(setup_path("krb5.conf"), path, {
2462 "DNSDOMAIN": dnsdomain,
2463 "HOSTNAME": hostname,
2468 class ProvisioningError(Exception):
2469 """A generic provision error."""
2471 def __init__(self, value):
2475 return "ProvisioningError: " + self.value
2478 class InvalidNetbiosName(Exception):
2479 """A specified name was not a valid NetBIOS name."""
2481 def __init__(self, name):
2482 super(InvalidNetbiosName, self).__init__(
2483 "The name '%r' is not a valid NetBIOS name" % name)
2486 class MissingShareError(ProvisioningError):
2488 def __init__(self, name, smbconf):
2489 super(MissingShareError, self).__init__(
2490 "Existing smb.conf does not have a [%s] share, but you are "
2491 "configuring a DC. Please remove %s or add the share manually." %