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 base64 import b64encode
45 from samba.auth import system_session, admin_session
47 from samba.samba3 import smbd, passdb
48 from samba.samba3 import param as s3param
49 from samba.dsdb import DS_DOMAIN_FUNCTION_2000
53 check_all_substituted,
54 is_valid_netbios_char,
61 from samba.dcerpc import security, misc
62 from samba.dcerpc.misc import (
66 from samba.dsdb import (
67 DS_DOMAIN_FUNCTION_2003,
68 DS_DOMAIN_FUNCTION_2008_R2,
71 from samba.idmap import IDmapDB
72 from samba.ms_display_specifiers import read_ms_ldif
73 from samba.ntacls import setntacl, getntacl, dsacl2fsacl
74 from samba.ndr import ndr_pack, ndr_unpack
75 from samba.provision.backend import (
81 from samba.descriptor import (
83 get_config_descriptor,
84 get_config_partitions_descriptor,
85 get_config_sites_descriptor,
86 get_config_ntds_quotas_descriptor,
87 get_config_delete_protected1_descriptor,
88 get_config_delete_protected1wd_descriptor,
89 get_config_delete_protected2_descriptor,
90 get_domain_descriptor,
91 get_domain_infrastructure_descriptor,
92 get_domain_builtin_descriptor,
93 get_domain_computers_descriptor,
94 get_domain_users_descriptor,
95 get_domain_controllers_descriptor,
96 get_domain_delete_protected1_descriptor,
97 get_domain_delete_protected2_descriptor,
98 get_dns_partition_descriptor,
99 get_dns_forest_microsoft_dns_descriptor,
100 get_dns_domain_microsoft_dns_descriptor,
102 from samba.provision.common import (
111 from samba.provision.sambadns import (
114 create_dns_update_list
118 import samba.registry
119 from samba.schema import Schema
120 from samba.samdb import SamDB
121 from samba.dbchecker import dbcheck
122 from samba.provision.kerberos import create_kdc_conf
124 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
125 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04FB984F9"
126 DEFAULTSITE = "Default-First-Site-Name"
127 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
130 class ProvisionPaths(object):
133 self.shareconf = None
144 self.dns_keytab = None
147 self.private_dir = None
148 self.state_dir = None
151 class ProvisionNames(object):
159 self.dnsforestdn = None
160 self.dnsdomaindn = None
161 self.ldapmanagerdn = None
162 self.dnsdomain = None
164 self.netbiosname = None
169 self.domainsid = None
170 self.forestsid = None
171 self.domainguid = None
175 def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf,
177 """Get key provision parameters (realm, domain, ...) from a given provision
179 :param samdb: An LDB object connected to the sam.ldb file
180 :param secretsdb: An LDB object connected to the secrets.ldb file
181 :param idmapdb: An LDB object connected to the idmap.ldb file
182 :param paths: A list of path to provision object
183 :param smbconf: Path to the smb.conf file
184 :param lp: A LoadParm object
185 :return: A list of key provision parameters
187 names = ProvisionNames()
188 names.adminpass = None
190 # NT domain, kerberos realm, root dn, domain dn, domain dns name
191 names.domain = string.upper(lp.get("workgroup"))
192 names.realm = lp.get("realm")
193 names.dnsdomain = names.realm.lower()
194 basedn = samba.dn_from_dns_name(names.dnsdomain)
195 names.realm = string.upper(names.realm)
197 # Get the netbiosname first (could be obtained from smb.conf in theory)
198 res = secretsdb.search(expression="(flatname=%s)" %
199 names.domain,base="CN=Primary Domains",
200 scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"])
201 names.netbiosname = str(res[0]["sAMAccountName"]).replace("$","")
203 names.smbconf = smbconf
205 # That's a bit simplistic but it's ok as long as we have only 3
207 current = samdb.search(expression="(objectClass=*)",
208 base="", scope=ldb.SCOPE_BASE,
209 attrs=["defaultNamingContext", "schemaNamingContext",
210 "configurationNamingContext","rootDomainNamingContext",
213 names.configdn = current[0]["configurationNamingContext"][0]
214 names.schemadn = current[0]["schemaNamingContext"][0]
215 if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb,
216 current[0]["defaultNamingContext"][0]))):
217 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
218 "is not the same ..." % (paths.samdb,
219 str(current[0]["defaultNamingContext"][0]),
220 paths.smbconf, basedn)))
222 names.domaindn=current[0]["defaultNamingContext"][0]
223 names.rootdn=current[0]["rootDomainNamingContext"][0]
224 names.ncs=current[0]["namingContexts"]
225 names.dnsforestdn = None
226 names.dnsdomaindn = None
228 for i in range(0, len(names.ncs)):
231 dnsforestdn = "DC=ForestDnsZones,%s" % (str(names.rootdn))
232 if nc == dnsforestdn:
233 names.dnsforestdn = dnsforestdn
236 dnsdomaindn = "DC=DomainDnsZones,%s" % (str(names.domaindn))
237 if nc == dnsdomaindn:
238 names.dnsdomaindn = dnsdomaindn
242 res3 = samdb.search(expression="(objectClass=site)",
243 base="CN=Sites," + names.configdn, scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
244 names.sitename = str(res3[0]["cn"])
246 # dns hostname and server dn
247 res4 = samdb.search(expression="(CN=%s)" % names.netbiosname,
248 base="OU=Domain Controllers,%s" % basedn,
249 scope=ldb.SCOPE_ONELEVEL, attrs=["dNSHostName"])
251 raise ProvisioningError("Unable to find DC called CN=%s under OU=Domain Controllers,%s" % (names.netbiosname, basedn))
253 names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain, "")
255 server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
256 attrs=[], base=names.configdn)
257 names.serverdn = str(server_res[0].dn)
259 # invocation id/objectguid
260 res5 = samdb.search(expression="(objectClass=*)",
261 base="CN=NTDS Settings,%s" % str(names.serverdn),
262 scope=ldb.SCOPE_BASE,
263 attrs=["invocationID", "objectGUID"])
264 names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0]))
265 names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0]))
268 res6 = samdb.search(expression="(objectClass=*)", base=basedn,
269 scope=ldb.SCOPE_BASE, attrs=["objectGUID",
270 "objectSid","msDS-Behavior-Version" ])
271 names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
272 names.domainsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
273 names.forestsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
274 if res6[0].get("msDS-Behavior-Version") is None or \
275 int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
276 names.domainlevel = DS_DOMAIN_FUNCTION_2000
278 names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
281 res7 = samdb.search(expression="(name={%s})" % DEFAULT_POLICY_GUID,
282 base="CN=Policies,CN=System," + basedn,
283 scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
284 names.policyid = str(res7[0]["cn"]).replace("{","").replace("}","")
286 res8 = samdb.search(expression="(name={%s})" % DEFAULT_DC_POLICY_GUID,
287 base="CN=Policies,CN=System," + basedn,
288 scope=ldb.SCOPE_ONELEVEL,
289 attrs=["cn","displayName"])
291 names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","")
293 names.policyid_dc = None
295 res9 = idmapdb.search(expression="(cn=%s-%s)" %
296 (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR),
297 attrs=["xidNumber", "type"])
299 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid (%s-%s" % (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR))
300 if res9[0]["type"][0] == "ID_TYPE_BOTH":
301 names.root_gid = res9[0]["xidNumber"][0]
303 names.root_gid = pwd.getpwuid(int(res9[0]["xidNumber"][0])).pw_gid
305 res10 = samdb.search(expression="(samaccountname=dns)",
306 scope=ldb.SCOPE_SUBTREE, attrs=["dn"],
307 controls=["search_options:1:2"])
309 has_legacy_dns_account = True
311 has_legacy_dns_account = False
313 res11 = samdb.search(expression="(samaccountname=dns-%s)" % names.netbiosname,
314 scope=ldb.SCOPE_SUBTREE, attrs=["dn"],
315 controls=["search_options:1:2"])
317 has_dns_account = True
319 has_dns_account = False
321 if names.dnsdomaindn is not None:
323 names.dns_backend = 'BIND9_DLZ'
325 names.dns_backend = 'SAMBA_INTERNAL'
326 elif has_dns_account or has_legacy_dns_account:
327 names.dns_backend = 'BIND9_FLATFILE'
329 names.dns_backend = 'NONE'
331 dns_admins_sid = get_dnsadmins_sid(samdb, names.domaindn)
332 names.name_map['DnsAdmins'] = str(dns_admins_sid)
337 def update_provision_usn(samdb, low, high, id, replace=False):
338 """Update the field provisionUSN in sam.ldb
340 This field is used to track range of USN modified by provision and
342 This value is used afterward by next provision to figure out if
343 the field have been modified since last provision.
345 :param samdb: An LDB object connect to sam.ldb
346 :param low: The lowest USN modified by this upgrade
347 :param high: The highest USN modified by this upgrade
348 :param id: The invocation id of the samba's dc
349 :param replace: A boolean indicating if the range should replace any
350 existing one or appended (default)
355 entry = samdb.search(base="@PROVISION",
356 scope=ldb.SCOPE_BASE,
357 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
358 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
359 if not re.search(';', e):
360 e = "%s;%s" % (e, id)
363 tab.append("%s-%s;%s" % (low, high, id))
364 delta = ldb.Message()
365 delta.dn = ldb.Dn(samdb, "@PROVISION")
366 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
367 ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE)
368 entry = samdb.search(expression='provisionnerID=*',
369 base="@PROVISION", scope=ldb.SCOPE_BASE,
370 attrs=["provisionnerID"])
371 if len(entry) == 0 or len(entry[0]) == 0:
372 delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
376 def set_provision_usn(samdb, low, high, id):
377 """Set the field provisionUSN in sam.ldb
378 This field is used to track range of USN modified by provision and
380 This value is used afterward by next provision to figure out if
381 the field have been modified since last provision.
383 :param samdb: An LDB object connect to sam.ldb
384 :param low: The lowest USN modified by this upgrade
385 :param high: The highest USN modified by this upgrade
386 :param id: The invocationId of the provision"""
389 tab.append("%s-%s;%s" % (low, high, id))
391 delta = ldb.Message()
392 delta.dn = ldb.Dn(samdb, "@PROVISION")
393 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
394 ldb.FLAG_MOD_ADD, LAST_PROVISION_USN_ATTRIBUTE)
398 def get_max_usn(samdb,basedn):
399 """ This function return the biggest USN present in the provision
401 :param samdb: A LDB object pointing to the sam.ldb
402 :param basedn: A string containing the base DN of the provision
404 :return: The biggest USN in the provision"""
406 res = samdb.search(expression="objectClass=*",base=basedn,
407 scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
408 controls=["search_options:1:2",
409 "server_sort:1:1:uSNChanged",
410 "paged_results:1:1"])
411 return res[0]["uSNChanged"]
414 def get_last_provision_usn(sam):
415 """Get USNs ranges modified by a provision or an upgradeprovision
417 :param sam: An LDB object pointing to the sam.ldb
418 :return: a dictionary which keys are invocation id and values are an array
419 of integer representing the different ranges
422 entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE,
423 base="@PROVISION", scope=ldb.SCOPE_BASE,
424 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
425 except ldb.LdbError, (ecode, emsg):
426 if ecode == ldb.ERR_NO_SUCH_OBJECT:
433 if entry[0].get("provisionnerID"):
434 for e in entry[0]["provisionnerID"]:
436 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
437 tab1 = str(r).split(';')
442 if (len(myids) > 0 and id not in myids):
444 tab2 = p.split(tab1[0])
445 if range.get(id) is None:
447 range[id].append(tab2[0])
448 range[id].append(tab2[1])
454 class ProvisionResult(object):
455 """Result of a provision.
457 :ivar server_role: The server role
458 :ivar paths: ProvisionPaths instance
459 :ivar domaindn: The domain dn, as string
463 self.server_role = None
470 self.domainsid = None
471 self.adminpass_generated = None
472 self.adminpass = None
473 self.backend_result = None
475 def report_logger(self, logger):
476 """Report this provision result to a logger."""
478 "Once the above files are installed, your Samba AD server will "
480 if self.adminpass_generated:
481 logger.info("Admin password: %s", self.adminpass)
482 logger.info("Server Role: %s", self.server_role)
483 logger.info("Hostname: %s", self.names.hostname)
484 logger.info("NetBIOS Domain: %s", self.names.domain)
485 logger.info("DNS Domain: %s", self.names.dnsdomain)
486 logger.info("DOMAIN SID: %s", self.domainsid)
488 if self.backend_result:
489 self.backend_result.report_logger(logger)
492 def check_install(lp, session_info, credentials):
493 """Check whether the current install seems ok.
495 :param lp: Loadparm context
496 :param session_info: Session information
497 :param credentials: Credentials
499 if lp.get("realm") == "":
500 raise Exception("Realm empty")
501 samdb = Ldb(lp.samdb_url(), session_info=session_info,
502 credentials=credentials, lp=lp)
503 if len(samdb.search("(cn=Administrator)")) != 1:
504 raise ProvisioningError("No administrator account found")
507 def findnss(nssfn, names):
508 """Find a user or group from a list of possibilities.
510 :param nssfn: NSS Function to try (should raise KeyError if not found)
511 :param names: Names to check.
512 :return: Value return by first names list.
519 raise KeyError("Unable to find user/group in %r" % names)
522 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
523 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
526 def provision_paths_from_lp(lp, dnsdomain):
527 """Set the default paths for provisioning.
529 :param lp: Loadparm context.
530 :param dnsdomain: DNS Domain name
532 paths = ProvisionPaths()
533 paths.private_dir = lp.get("private dir")
534 paths.state_dir = lp.get("state directory")
536 # This is stored without path prefix for the "privateKeytab" attribute in
537 # "secrets_dns.ldif".
538 paths.dns_keytab = "dns.keytab"
539 paths.keytab = "secrets.keytab"
541 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
542 paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
543 paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
544 paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
545 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
546 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
547 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
548 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
549 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
550 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
551 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
552 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
553 paths.kdcconf = os.path.join(paths.private_dir, "kdc.conf")
554 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
555 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
556 paths.hklm = "hklm.ldb"
557 paths.hkcr = "hkcr.ldb"
558 paths.hkcu = "hkcu.ldb"
559 paths.hku = "hku.ldb"
560 paths.hkpd = "hkpd.ldb"
561 paths.hkpt = "hkpt.ldb"
562 paths.sysvol = lp.get("path", "sysvol")
563 paths.netlogon = lp.get("path", "netlogon")
564 paths.smbconf = lp.configfile
568 def determine_netbios_name(hostname):
569 """Determine a netbios name from a hostname."""
570 # remove forbidden chars and force the length to be <16
571 netbiosname = "".join([x for x in hostname if is_valid_netbios_char(x)])
572 return netbiosname[:MAX_NETBIOS_NAME_LEN].upper()
575 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
576 serverrole=None, rootdn=None, domaindn=None, configdn=None,
577 schemadn=None, serverdn=None, sitename=None,
578 domain_names_forced=False):
579 """Guess configuration settings to use."""
582 hostname = socket.gethostname().split(".")[0]
584 netbiosname = lp.get("netbios name")
585 if netbiosname is None:
586 netbiosname = determine_netbios_name(hostname)
587 netbiosname = netbiosname.upper()
588 if not valid_netbios_name(netbiosname):
589 raise InvalidNetbiosName(netbiosname)
591 if dnsdomain is None:
592 dnsdomain = lp.get("realm")
593 if dnsdomain is None or dnsdomain == "":
594 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
596 dnsdomain = dnsdomain.lower()
598 if serverrole is None:
599 serverrole = lp.get("server role")
600 if serverrole is None:
601 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
603 serverrole = serverrole.lower()
605 realm = dnsdomain.upper()
607 if lp.get("realm") == "":
608 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
610 if lp.get("realm").upper() != realm:
611 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))
613 if lp.get("server role").lower() != serverrole:
614 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))
616 if serverrole == "active directory domain controller":
618 # This will, for better or worse, default to 'WORKGROUP'
619 domain = lp.get("workgroup")
620 domain = domain.upper()
622 if lp.get("workgroup").upper() != domain:
623 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))
626 domaindn = samba.dn_from_dns_name(dnsdomain)
628 if domain == netbiosname:
629 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
633 domaindn = "DC=" + netbiosname
635 if not valid_netbios_name(domain):
636 raise InvalidNetbiosName(domain)
638 if hostname.upper() == realm:
639 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
640 if netbiosname.upper() == realm:
641 raise ProvisioningError("guess_names: Realm '%s' must not be equal to NetBIOS hostname '%s'!" % (realm, netbiosname))
642 if domain == realm and not domain_names_forced:
643 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
649 configdn = "CN=Configuration," + rootdn
651 schemadn = "CN=Schema," + configdn
654 sitename = DEFAULTSITE
656 names = ProvisionNames()
657 names.rootdn = rootdn
658 names.domaindn = domaindn
659 names.configdn = configdn
660 names.schemadn = schemadn
661 names.ldapmanagerdn = "CN=Manager," + rootdn
662 names.dnsdomain = dnsdomain
663 names.domain = domain
665 names.netbiosname = netbiosname
666 names.hostname = hostname
667 names.sitename = sitename
668 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
669 netbiosname, sitename, configdn)
673 def make_smbconf(smbconf, hostname, domain, realm, targetdir,
674 serverrole=None, eadb=False, use_ntvfs=False, lp=None,
676 """Create a new smb.conf file based on a couple of basic settings.
678 assert smbconf is not None
681 hostname = socket.gethostname().split(".")[0]
683 netbiosname = determine_netbios_name(hostname)
685 if serverrole is None:
686 serverrole = "standalone server"
688 assert domain is not None
689 domain = domain.upper()
691 assert realm is not None
692 realm = realm.upper()
695 "netbios name": netbiosname,
698 "server role": serverrole,
702 lp = samba.param.LoadParm()
703 #Load non-existent file
704 if os.path.exists(smbconf):
707 if global_param is not None:
708 for ent in global_param:
709 if global_param[ent] is not None:
710 global_settings[ent] = " ".join(global_param[ent])
712 if targetdir is not None:
713 global_settings["private dir"] = os.path.abspath(os.path.join(targetdir, "private"))
714 global_settings["lock dir"] = os.path.abspath(targetdir)
715 global_settings["state directory"] = os.path.abspath(os.path.join(targetdir, "state"))
716 global_settings["cache directory"] = os.path.abspath(os.path.join(targetdir, "cache"))
718 lp.set("lock dir", os.path.abspath(targetdir))
719 lp.set("state directory", global_settings["state directory"])
720 lp.set("cache directory", global_settings["cache directory"])
723 if use_ntvfs and not lp.get("posix:eadb"):
724 if targetdir is not None:
725 privdir = os.path.join(targetdir, "private")
727 privdir = lp.get("private dir")
728 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
729 elif not use_ntvfs and not lp.get("xattr_tdb:file"):
730 if targetdir is not None:
731 statedir = os.path.join(targetdir, "state")
733 statedir = lp.get("state directory")
734 lp.set("xattr_tdb:file", os.path.abspath(os.path.join(statedir, "xattr.tdb")))
737 if serverrole == "active directory domain controller":
738 shares["sysvol"] = os.path.join(lp.get("state directory"), "sysvol")
739 shares["netlogon"] = os.path.join(shares["sysvol"], realm.lower(),
742 global_settings["passdb backend"] = "samba_dsdb"
744 f = open(smbconf, 'w')
746 f.write("[globals]\n")
747 for key, val in global_settings.iteritems():
748 f.write("\t%s = %s\n" % (key, val))
751 for name, path in shares.iteritems():
752 f.write("[%s]\n" % name)
753 f.write("\tpath = %s\n" % path)
754 f.write("\tread only = no\n")
758 # reload the smb.conf
761 # and dump it without any values that are the default
762 # this ensures that any smb.conf parameters that were set
763 # on the provision/join command line are set in the resulting smb.conf
764 lp.dump(False, smbconf)
767 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
768 users_gid, root_gid):
769 """setup reasonable name mappings for sam names to unix names.
771 :param samdb: SamDB object.
772 :param idmap: IDmap db object.
773 :param sid: The domain sid.
774 :param domaindn: The domain DN.
775 :param root_uid: uid of the UNIX root user.
776 :param nobody_uid: uid of the UNIX nobody user.
777 :param users_gid: gid of the UNIX users group.
778 :param root_gid: gid of the UNIX root group.
780 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
782 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
783 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
786 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
787 provision_backend, names, serverrole,
789 """Setup the partitions for the SAM database.
791 Alternatively, provision() may call this, and then populate the database.
793 :note: This will wipe the Sam Database!
795 :note: This function always removes the local SAM LDB file. The erase
796 parameter controls whether to erase the existing data, which
797 may not be stored locally but in LDAP.
800 assert session_info is not None
802 # We use options=["modules:"] to stop the modules loading - we
803 # just want to wipe and re-initialise the database, not start it up
806 os.unlink(samdb_path)
810 samdb = Ldb(url=samdb_path, session_info=session_info,
811 lp=lp, options=["modules:"])
813 ldap_backend_line = "# No LDAP backend"
814 if provision_backend.type != "ldb":
815 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
817 samdb.transaction_start()
819 logger.info("Setting up sam.ldb partitions and settings")
820 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
821 "LDAP_BACKEND_LINE": ldap_backend_line
825 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
826 "BACKEND_TYPE": provision_backend.type,
827 "SERVER_ROLE": serverrole
830 logger.info("Setting up sam.ldb rootDSE")
831 setup_samdb_rootdse(samdb, names)
833 samdb.transaction_cancel()
836 samdb.transaction_commit()
839 def secretsdb_self_join(secretsdb, domain,
840 netbiosname, machinepass, domainsid=None,
841 realm=None, dnsdomain=None,
843 key_version_number=1,
844 secure_channel_type=SEC_CHAN_WKSTA):
845 """Add domain join-specific bits to a secrets database.
847 :param secretsdb: Ldb Handle to the secrets database
848 :param machinepass: Machine password
850 attrs = ["whenChanged",
857 if realm is not None:
858 if dnsdomain is None:
859 dnsdomain = realm.lower()
860 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
863 shortname = netbiosname.lower()
865 # We don't need to set msg["flatname"] here, because rdn_name will handle
866 # it, and it causes problems for modifies anyway
867 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
868 msg["secureChannelType"] = [str(secure_channel_type)]
869 msg["objectClass"] = ["top", "primaryDomain"]
870 if dnsname is not None:
871 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
872 msg["realm"] = [realm]
873 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
874 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
875 msg["privateKeytab"] = ["secrets.keytab"]
877 msg["secret"] = [machinepass.encode('utf-8')]
878 msg["samAccountName"] = ["%s$" % netbiosname]
879 msg["secureChannelType"] = [str(secure_channel_type)]
880 if domainsid is not None:
881 msg["objectSid"] = [ndr_pack(domainsid)]
883 # This complex expression tries to ensure that we don't have more
884 # than one record for this SID, realm or netbios domain at a time,
885 # but we don't delete the old record that we are about to modify,
886 # because that would delete the keytab and previous password.
887 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
888 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
889 scope=ldb.SCOPE_ONELEVEL)
892 secretsdb.delete(del_msg.dn)
894 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
897 msg["priorSecret"] = [res[0]["secret"][0]]
899 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
904 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
909 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
915 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
916 secretsdb.modify(msg)
917 secretsdb.rename(res[0].dn, msg.dn)
919 spn = [ 'HOST/%s' % shortname ]
920 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
921 # we are a domain controller then we add servicePrincipalName
922 # entries for the keytab code to update.
923 spn.extend([ 'HOST/%s' % dnsname ])
924 msg["servicePrincipalName"] = spn
929 def setup_secretsdb(paths, session_info, backend_credentials, lp):
930 """Setup the secrets database.
932 :note: This function does not handle exceptions and transaction on purpose,
933 it's up to the caller to do this job.
935 :param path: Path to the secrets database.
936 :param session_info: Session info.
937 :param credentials: Credentials
938 :param lp: Loadparm context
939 :return: LDB handle for the created secrets database
941 if os.path.exists(paths.secrets):
942 os.unlink(paths.secrets)
944 keytab_path = os.path.join(paths.private_dir, paths.keytab)
945 if os.path.exists(keytab_path):
946 os.unlink(keytab_path)
948 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
949 if os.path.exists(dns_keytab_path):
950 os.unlink(dns_keytab_path)
954 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
956 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
957 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
958 secrets_ldb.transaction_start()
960 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
962 if (backend_credentials is not None and
963 backend_credentials.authentication_requested()):
964 if backend_credentials.get_bind_dn() is not None:
965 setup_add_ldif(secrets_ldb,
966 setup_path("secrets_simple_ldap.ldif"), {
967 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
968 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
971 setup_add_ldif(secrets_ldb,
972 setup_path("secrets_sasl_ldap.ldif"), {
973 "LDAPADMINUSER": backend_credentials.get_username(),
974 "LDAPADMINREALM": backend_credentials.get_realm(),
975 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
978 secrets_ldb.transaction_cancel()
983 def setup_privileges(path, session_info, lp):
984 """Setup the privileges database.
986 :param path: Path to the privileges database.
987 :param session_info: Session info.
988 :param credentials: Credentials
989 :param lp: Loadparm context
990 :return: LDB handle for the created secrets database
992 if os.path.exists(path):
994 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
995 privilege_ldb.erase()
996 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
999 def setup_registry(path, session_info, lp):
1000 """Setup the registry.
1002 :param path: Path to the registry database
1003 :param session_info: Session information
1004 :param credentials: Credentials
1005 :param lp: Loadparm context
1007 reg = samba.registry.Registry()
1008 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
1009 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
1010 provision_reg = setup_path("provision.reg")
1011 assert os.path.exists(provision_reg)
1012 reg.diff_apply(provision_reg)
1015 def setup_idmapdb(path, session_info, lp):
1016 """Setup the idmap database.
1018 :param path: path to the idmap database
1019 :param session_info: Session information
1020 :param credentials: Credentials
1021 :param lp: Loadparm context
1023 if os.path.exists(path):
1026 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
1028 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
1032 def setup_samdb_rootdse(samdb, names):
1033 """Setup the SamDB rootdse.
1035 :param samdb: Sam Database handle
1037 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
1038 "SCHEMADN": names.schemadn,
1039 "DOMAINDN": names.domaindn,
1040 "ROOTDN" : names.rootdn,
1041 "CONFIGDN": names.configdn,
1042 "SERVERDN": names.serverdn,
1046 def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
1047 dns_backend, dnspass, domainsid, next_rid, invocationid,
1048 policyguid, policyguid_dc,
1049 domainControllerFunctionality, ntdsguid=None, dc_rid=None):
1050 """Join a host to its own domain."""
1051 assert isinstance(invocationid, str)
1052 if ntdsguid is not None:
1053 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
1060 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
1061 "CONFIGDN": names.configdn,
1062 "SCHEMADN": names.schemadn,
1063 "DOMAINDN": names.domaindn,
1064 "SERVERDN": names.serverdn,
1065 "INVOCATIONID": invocationid,
1066 "NETBIOSNAME": names.netbiosname,
1067 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1068 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1069 "DOMAINSID": str(domainsid),
1070 "DCRID": str(dc_rid),
1071 "SAMBA_VERSION_STRING": version,
1072 "NTDSGUID": ntdsguid_line,
1073 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1074 domainControllerFunctionality),
1075 "RIDALLOCATIONSTART": str(next_rid + 100),
1076 "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
1078 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1079 "POLICYGUID": policyguid,
1080 "POLICYGUID_DC": policyguid_dc,
1081 "DNSDOMAIN": names.dnsdomain,
1082 "DOMAINDN": names.domaindn})
1084 # If we are setting up a subdomain, then this has been replicated in, so we
1085 # don't need to add it
1086 if fill == FILL_FULL:
1087 setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
1088 "CONFIGDN": names.configdn,
1089 "SCHEMADN": names.schemadn,
1090 "DOMAINDN": names.domaindn,
1091 "SERVERDN": names.serverdn,
1092 "INVOCATIONID": invocationid,
1093 "NETBIOSNAME": names.netbiosname,
1094 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1095 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1096 "DOMAINSID": str(domainsid),
1097 "DCRID": str(dc_rid),
1098 "SAMBA_VERSION_STRING": version,
1099 "NTDSGUID": ntdsguid_line,
1100 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1101 domainControllerFunctionality)})
1103 # Setup fSMORoleOwner entries to point at the newly created DC entry
1104 setup_modify_ldif(samdb,
1105 setup_path("provision_self_join_modify_config.ldif"), {
1106 "CONFIGDN": names.configdn,
1107 "SCHEMADN": names.schemadn,
1108 "DEFAULTSITE": names.sitename,
1109 "NETBIOSNAME": names.netbiosname,
1110 "SERVERDN": names.serverdn,
1113 system_session_info = system_session()
1114 samdb.set_session_info(system_session_info)
1115 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1116 # modify a serverReference under cn=config when we are a subdomain, we must
1117 # be system due to ACLs
1118 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1119 "DOMAINDN": names.domaindn,
1120 "SERVERDN": names.serverdn,
1121 "NETBIOSNAME": names.netbiosname,
1124 samdb.set_session_info(admin_session_info)
1126 if dns_backend != "SAMBA_INTERNAL":
1127 # This is Samba4 specific and should be replaced by the correct
1128 # DNS AD-style setup
1129 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1130 "DNSDOMAIN": names.dnsdomain,
1131 "DOMAINDN": names.domaindn,
1132 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1133 "HOSTNAME" : names.hostname,
1134 "DNSNAME" : '%s.%s' % (
1135 names.netbiosname.lower(), names.dnsdomain.lower())
1139 def getpolicypath(sysvolpath, dnsdomain, guid):
1140 """Return the physical path of policy given its guid.
1142 :param sysvolpath: Path to the sysvol folder
1143 :param dnsdomain: DNS name of the AD domain
1144 :param guid: The GUID of the policy
1145 :return: A string with the complete path to the policy folder
1148 guid = "{%s}" % guid
1149 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1153 def create_gpo_struct(policy_path):
1154 if not os.path.exists(policy_path):
1155 os.makedirs(policy_path, 0775)
1156 f = open(os.path.join(policy_path, "GPT.INI"), 'w')
1158 f.write("[General]\r\nVersion=0")
1161 p = os.path.join(policy_path, "MACHINE")
1162 if not os.path.exists(p):
1163 os.makedirs(p, 0775)
1164 p = os.path.join(policy_path, "USER")
1165 if not os.path.exists(p):
1166 os.makedirs(p, 0775)
1169 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1170 """Create the default GPO for a domain
1172 :param sysvolpath: Physical path for the sysvol folder
1173 :param dnsdomain: DNS domain name of the AD domain
1174 :param policyguid: GUID of the default domain policy
1175 :param policyguid_dc: GUID of the default domain controler policy
1177 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1178 create_gpo_struct(policy_path)
1180 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1181 create_gpo_struct(policy_path)
1184 def setup_samdb(path, session_info, provision_backend, lp, names,
1185 logger, fill, serverrole, schema, am_rodc=False):
1186 """Setup a complete SAM Database.
1188 :note: This will wipe the main SAM database file!
1191 # Also wipes the database
1192 setup_samdb_partitions(path, logger=logger, lp=lp,
1193 provision_backend=provision_backend, session_info=session_info,
1194 names=names, serverrole=serverrole)
1196 # Load the database, but don's load the global schema and don't connect
1198 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1199 credentials=provision_backend.credentials, lp=lp,
1200 global_schema=False, am_rodc=am_rodc)
1202 logger.info("Pre-loading the Samba 4 and AD schema")
1204 # Load the schema from the one we computed earlier
1205 samdb.set_schema(schema, write_indices_and_attributes=False)
1207 # Set the NTDS settings DN manually - in order to have it already around
1208 # before the provisioned tree exists and we connect
1209 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1211 # And now we can connect to the DB - the schema won't be loaded from the
1215 except ldb.LdbError, (num, string_error):
1216 if (num == ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS):
1217 raise ProvisioningError("Permission denied connecting to %s, are you running as root?" % path)
1221 # But we have to give it one more kick to have it use the schema
1222 # during provision - it needs, now that it is connected, to write
1223 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1224 samdb.set_schema(schema, write_indices_and_attributes=True)
1229 def fill_samdb(samdb, lp, names, logger, policyguid,
1230 policyguid_dc, fill, adminpass, krbtgtpass, machinepass, dns_backend,
1231 dnspass, invocationid, ntdsguid, serverrole, am_rodc=False,
1232 dom_for_fun_level=None, schema=None, next_rid=None, dc_rid=None):
1234 if next_rid is None:
1237 # Provision does not make much sense values larger than 1000000000
1238 # as the upper range of the rIDAvailablePool is 1073741823 and
1239 # we don't want to create a domain that cannot allocate rids.
1240 if next_rid < 1000 or next_rid > 1000000000:
1241 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1242 error += "the valid range is %u-%u. The default is %u." % (
1243 1000, 1000000000, 1000)
1244 raise ProvisioningError(error)
1246 # ATTENTION: Do NOT change these default values without discussion with the
1247 # team and/or release manager. They have a big impact on the whole program!
1248 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1250 if dom_for_fun_level is None:
1251 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
1253 if dom_for_fun_level > domainControllerFunctionality:
1254 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!")
1256 domainFunctionality = dom_for_fun_level
1257 forestFunctionality = dom_for_fun_level
1259 # Set the NTDS settings DN manually - in order to have it already around
1260 # before the provisioned tree exists and we connect
1261 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1263 # Set the domain functionality levels onto the database.
1264 # Various module (the password_hash module in particular) need
1265 # to know what level of AD we are emulating.
1267 # These will be fixed into the database via the database
1268 # modifictions below, but we need them set from the start.
1269 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1270 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1271 samdb.set_opaque_integer("domainControllerFunctionality",
1272 domainControllerFunctionality)
1274 samdb.set_domain_sid(str(names.domainsid))
1275 samdb.set_invocation_id(invocationid)
1277 logger.info("Adding DomainDN: %s" % names.domaindn)
1279 # impersonate domain admin
1280 admin_session_info = admin_session(lp, str(names.domainsid))
1281 samdb.set_session_info(admin_session_info)
1282 if names.domainguid is not None:
1283 domainguid_line = "objectGUID: %s\n-" % names.domainguid
1285 domainguid_line = ""
1287 descr = b64encode(get_domain_descriptor(names.domainsid))
1288 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1289 "DOMAINDN": names.domaindn,
1290 "DOMAINSID": str(names.domainsid),
1291 "DESCRIPTOR": descr,
1292 "DOMAINGUID": domainguid_line
1295 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1296 "DOMAINDN": names.domaindn,
1297 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1298 "NEXTRID": str(next_rid),
1299 "DEFAULTSITE": names.sitename,
1300 "CONFIGDN": names.configdn,
1301 "POLICYGUID": policyguid,
1302 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1303 "SAMBA_VERSION_STRING": version
1306 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1307 if fill == FILL_FULL:
1308 logger.info("Adding configuration container")
1309 descr = b64encode(get_config_descriptor(names.domainsid))
1310 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1311 "CONFIGDN": names.configdn,
1312 "DESCRIPTOR": descr,
1315 # The LDIF here was created when the Schema object was constructed
1316 ignore_checks_oid = "local_oid:%s:0" % samba.dsdb.DSDB_CONTROL_SKIP_DUPLICATES_CHECK_OID
1317 logger.info("Setting up sam.ldb schema")
1318 samdb.add_ldif(schema.schema_dn_add,
1319 controls=["relax:0", ignore_checks_oid])
1320 samdb.modify_ldif(schema.schema_dn_modify,
1321 controls=[ignore_checks_oid])
1322 samdb.write_prefixes_from_schema()
1323 samdb.add_ldif(schema.schema_data, controls=["relax:0", ignore_checks_oid])
1324 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1325 {"SCHEMADN": names.schemadn},
1326 controls=["relax:0", ignore_checks_oid])
1328 # Now register this container in the root of the forest
1329 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1330 msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1333 samdb.invocation_id = invocationid
1335 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1336 if fill == FILL_FULL:
1337 logger.info("Setting up sam.ldb configuration data")
1339 partitions_descr = b64encode(get_config_partitions_descriptor(names.domainsid))
1340 sites_descr = b64encode(get_config_sites_descriptor(names.domainsid))
1341 ntdsquotas_descr = b64encode(get_config_ntds_quotas_descriptor(names.domainsid))
1342 protected1_descr = b64encode(get_config_delete_protected1_descriptor(names.domainsid))
1343 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid))
1344 protected2_descr = b64encode(get_config_delete_protected2_descriptor(names.domainsid))
1346 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1347 "CONFIGDN": names.configdn,
1348 "NETBIOSNAME": names.netbiosname,
1349 "DEFAULTSITE": names.sitename,
1350 "DNSDOMAIN": names.dnsdomain,
1351 "DOMAIN": names.domain,
1352 "SCHEMADN": names.schemadn,
1353 "DOMAINDN": names.domaindn,
1354 "SERVERDN": names.serverdn,
1355 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1356 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1357 "NTDSQUOTAS_DESCRIPTOR": ntdsquotas_descr,
1358 "LOSTANDFOUND_DESCRIPTOR": protected1wd_descr,
1359 "SERVICES_DESCRIPTOR": protected1_descr,
1360 "PHYSICALLOCATIONS_DESCRIPTOR": protected1wd_descr,
1361 "FORESTUPDATES_DESCRIPTOR": protected1wd_descr,
1362 "EXTENDEDRIGHTS_DESCRIPTOR": protected2_descr,
1363 "PARTITIONS_DESCRIPTOR": partitions_descr,
1364 "SITES_DESCRIPTOR": sites_descr,
1367 logger.info("Setting up display specifiers")
1368 display_specifiers_ldif = read_ms_ldif(
1369 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1370 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1371 {"CONFIGDN": names.configdn})
1372 check_all_substituted(display_specifiers_ldif)
1373 samdb.add_ldif(display_specifiers_ldif)
1375 logger.info("Modifying display specifiers")
1376 setup_modify_ldif(samdb,
1377 setup_path("provision_configuration_modify.ldif"), {
1378 "CONFIGDN": names.configdn,
1379 "DISPLAYSPECIFIERS_DESCRIPTOR": protected2_descr
1382 logger.info("Adding users container")
1383 users_desc = b64encode(get_domain_users_descriptor(names.domainsid))
1384 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1385 "DOMAINDN": names.domaindn,
1386 "USERS_DESCRIPTOR": users_desc
1388 logger.info("Modifying users container")
1389 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1390 "DOMAINDN": names.domaindn})
1391 logger.info("Adding computers container")
1392 computers_desc = b64encode(get_domain_computers_descriptor(names.domainsid))
1393 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1394 "DOMAINDN": names.domaindn,
1395 "COMPUTERS_DESCRIPTOR": computers_desc
1397 logger.info("Modifying computers container")
1398 setup_modify_ldif(samdb,
1399 setup_path("provision_computers_modify.ldif"), {
1400 "DOMAINDN": names.domaindn})
1401 logger.info("Setting up sam.ldb data")
1402 infrastructure_desc = b64encode(get_domain_infrastructure_descriptor(names.domainsid))
1403 lostandfound_desc = b64encode(get_domain_delete_protected2_descriptor(names.domainsid))
1404 system_desc = b64encode(get_domain_delete_protected1_descriptor(names.domainsid))
1405 builtin_desc = b64encode(get_domain_builtin_descriptor(names.domainsid))
1406 controllers_desc = b64encode(get_domain_controllers_descriptor(names.domainsid))
1407 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1408 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1409 "DOMAINDN": names.domaindn,
1410 "NETBIOSNAME": names.netbiosname,
1411 "DEFAULTSITE": names.sitename,
1412 "CONFIGDN": names.configdn,
1413 "SERVERDN": names.serverdn,
1414 "RIDAVAILABLESTART": str(next_rid + 600),
1415 "POLICYGUID_DC": policyguid_dc,
1416 "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc,
1417 "LOSTANDFOUND_DESCRIPTOR": lostandfound_desc,
1418 "SYSTEM_DESCRIPTOR": system_desc,
1419 "BUILTIN_DESCRIPTOR": builtin_desc,
1420 "DOMAIN_CONTROLLERS_DESCRIPTOR": controllers_desc,
1423 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1424 if fill == FILL_FULL:
1425 setup_modify_ldif(samdb,
1426 setup_path("provision_configuration_references.ldif"), {
1427 "CONFIGDN": names.configdn,
1428 "SCHEMADN": names.schemadn})
1430 logger.info("Setting up well known security principals")
1431 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid))
1432 setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1433 "CONFIGDN": names.configdn,
1434 "WELLKNOWNPRINCIPALS_DESCRIPTOR": protected1wd_descr,
1437 if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1438 setup_modify_ldif(samdb,
1439 setup_path("provision_basedn_references.ldif"),
1440 {"DOMAINDN": names.domaindn})
1442 logger.info("Setting up sam.ldb users and groups")
1443 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1444 "DOMAINDN": names.domaindn,
1445 "DOMAINSID": str(names.domainsid),
1446 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1447 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1450 logger.info("Setting up self join")
1451 setup_self_join(samdb, admin_session_info, names=names, fill=fill,
1452 invocationid=invocationid,
1453 dns_backend=dns_backend,
1455 machinepass=machinepass,
1456 domainsid=names.domainsid,
1459 policyguid=policyguid,
1460 policyguid_dc=policyguid_dc,
1461 domainControllerFunctionality=domainControllerFunctionality,
1464 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1465 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1466 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1467 assert isinstance(names.ntdsguid, str)
1472 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1473 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)"
1474 SYSVOL_SERVICE="sysvol"
1476 def set_dir_acl(path, acl, lp, domsid, use_ntvfs, passdb, service=SYSVOL_SERVICE):
1477 setntacl(lp, path, acl, domsid, use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1478 for root, dirs, files in os.walk(path, topdown=False):
1480 setntacl(lp, os.path.join(root, name), acl, domsid,
1481 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1483 setntacl(lp, os.path.join(root, name), acl, domsid,
1484 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1487 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb):
1488 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1491 :param sysvol: Physical path for the sysvol folder
1492 :param dnsdomain: The DNS name of the domain
1493 :param domainsid: The SID of the domain
1494 :param domaindn: The DN of the domain (ie. DC=...)
1495 :param samdb: An LDB object on the SAM db
1496 :param lp: an LP object
1499 # Set ACL for GPO root folder
1500 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1501 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid),
1502 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=SYSVOL_SERVICE)
1504 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1505 attrs=["cn", "nTSecurityDescriptor"],
1506 expression="", scope=ldb.SCOPE_ONELEVEL)
1509 acl = ndr_unpack(security.descriptor,
1510 str(policy["nTSecurityDescriptor"])).as_sddl()
1511 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1512 set_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1513 str(domainsid), use_ntvfs,
1517 def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain,
1518 domaindn, lp, use_ntvfs):
1519 """Set the ACL for the sysvol share and the subfolders
1521 :param samdb: An LDB object on the SAM db
1522 :param netlogon: Physical path for the netlogon folder
1523 :param sysvol: Physical path for the sysvol folder
1524 :param uid: The UID of the "Administrator" user
1525 :param gid: The GID of the "Domain adminstrators" group
1526 :param domainsid: The SID of the domain
1527 :param dnsdomain: The DNS name of the domain
1528 :param domaindn: The DN of the domain (ie. DC=...)
1533 s3conf = s3param.get_context()
1534 s3conf.load(lp.configfile)
1536 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(sysvol))
1539 smbd.set_simple_acl(file.name, 0755, gid)
1541 if not smbd.have_posix_acls():
1542 # This clue is only strictly correct for RPM and
1543 # Debian-like Linux systems, but hopefully other users
1544 # will get enough clue from it.
1545 raise ProvisioningError("Samba was compiled without the posix ACL support that s3fs requires. "
1546 "Try installing libacl1-dev or libacl-devel, then re-run configure and make.")
1548 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. "
1549 "Try the mounting the filesystem with the 'acl' option.")
1551 smbd.chown(file.name, uid, gid)
1553 raise ProvisioningError("Unable to chown a file on your filesystem. "
1554 "You may not be running provision as root.")
1558 # This will ensure that the smbd code we are running when setting ACLs
1559 # is initialised with the smb.conf
1560 s3conf = s3param.get_context()
1561 s3conf.load(lp.configfile)
1562 # ensure we are using the right samba_dsdb passdb backend, no matter what
1563 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1564 passdb.reload_static_pdb()
1566 # ensure that we init the samba_dsdb backend, so the domain sid is
1567 # marked in secrets.tdb
1568 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1570 # now ensure everything matches correctly, to avoid wierd issues
1571 if passdb.get_global_sam_sid() != domainsid:
1572 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))
1574 domain_info = s4_passdb.domain_info()
1575 if domain_info["dom_sid"] != domainsid:
1576 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))
1578 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1579 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()))
1584 os.chown(sysvol, -1, gid)
1590 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1591 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs,
1592 skip_invalid_chown=True, passdb=s4_passdb,
1593 service=SYSVOL_SERVICE)
1594 for root, dirs, files in os.walk(sysvol, topdown=False):
1596 if use_ntvfs and canchown:
1597 os.chown(os.path.join(root, name), -1, gid)
1598 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid),
1599 use_ntvfs=use_ntvfs, skip_invalid_chown=True,
1600 passdb=s4_passdb, service=SYSVOL_SERVICE)
1602 if use_ntvfs and canchown:
1603 os.chown(os.path.join(root, name), -1, gid)
1604 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid),
1605 use_ntvfs=use_ntvfs, skip_invalid_chown=True,
1606 passdb=s4_passdb, service=SYSVOL_SERVICE)
1608 # Set acls on Policy folder and policies folders
1609 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb=s4_passdb)
1611 def acl_type(direct_db_access):
1612 if direct_db_access:
1617 def check_dir_acl(path, acl, lp, domainsid, direct_db_access):
1618 fsacl = getntacl(lp, path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1619 fsacl_sddl = fsacl.as_sddl(domainsid)
1620 if fsacl_sddl != acl:
1621 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))
1623 for root, dirs, files in os.walk(path, topdown=False):
1625 fsacl = getntacl(lp, os.path.join(root, name),
1626 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1628 raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1629 fsacl_sddl = fsacl.as_sddl(domainsid)
1630 if fsacl_sddl != acl:
1631 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))
1634 fsacl = getntacl(lp, os.path.join(root, name),
1635 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1637 raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1638 fsacl_sddl = fsacl.as_sddl(domainsid)
1639 if fsacl_sddl != acl:
1640 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))
1643 def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1645 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1648 :param sysvol: Physical path for the sysvol folder
1649 :param dnsdomain: The DNS name of the domain
1650 :param domainsid: The SID of the domain
1651 :param domaindn: The DN of the domain (ie. DC=...)
1652 :param samdb: An LDB object on the SAM db
1653 :param lp: an LP object
1656 # Set ACL for GPO root folder
1657 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1658 fsacl = getntacl(lp, root_policy_path,
1659 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1661 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access), root_policy_path))
1662 fsacl_sddl = fsacl.as_sddl(domainsid)
1663 if fsacl_sddl != POLICIES_ACL:
1664 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))
1665 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1666 attrs=["cn", "nTSecurityDescriptor"],
1667 expression="", scope=ldb.SCOPE_ONELEVEL)
1670 acl = ndr_unpack(security.descriptor,
1671 str(policy["nTSecurityDescriptor"])).as_sddl()
1672 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1673 check_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1674 domainsid, direct_db_access)
1677 def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn,
1679 """Set the ACL for the sysvol share and the subfolders
1681 :param samdb: An LDB object on the SAM db
1682 :param netlogon: Physical path for the netlogon folder
1683 :param sysvol: Physical path for the sysvol folder
1684 :param uid: The UID of the "Administrator" user
1685 :param gid: The GID of the "Domain adminstrators" group
1686 :param domainsid: The SID of the domain
1687 :param dnsdomain: The DNS name of the domain
1688 :param domaindn: The DN of the domain (ie. DC=...)
1691 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1692 s3conf = s3param.get_context()
1693 s3conf.load(lp.configfile)
1694 # ensure we are using the right samba_dsdb passdb backend, no matter what
1695 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1696 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1697 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1699 # now ensure everything matches correctly, to avoid wierd issues
1700 if passdb.get_global_sam_sid() != domainsid:
1701 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))
1703 domain_info = s4_passdb.domain_info()
1704 if domain_info["dom_sid"] != domainsid:
1705 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))
1707 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1708 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()))
1710 # Ensure we can read this directly, and via the smbd VFS
1711 for direct_db_access in [True, False]:
1712 # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1713 for dir_path in [os.path.join(sysvol, dnsdomain), netlogon]:
1714 fsacl = getntacl(lp, dir_path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1716 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access), dir_path))
1717 fsacl_sddl = fsacl.as_sddl(domainsid)
1718 if fsacl_sddl != SYSVOL_ACL:
1719 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))
1721 # Check acls on Policy folder and policies folders
1722 check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1726 def interface_ips_v4(lp):
1727 """return only IPv4 IPs"""
1728 ips = samba.interface_ips(lp, False)
1731 if i.find(':') == -1:
1736 def interface_ips_v6(lp):
1737 """return only IPv6 IPs"""
1738 ips = samba.interface_ips(lp, False)
1741 if i.find(':') != -1:
1746 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1748 targetdir=None, samdb_fill=FILL_FULL,
1749 hostip=None, hostip6=None,
1750 next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1751 domainguid=None, policyguid=None, policyguid_dc=None,
1752 invocationid=None, machinepass=None, ntdsguid=None,
1753 dns_backend=None, dnspass=None,
1754 serverrole=None, dom_for_fun_level=None,
1755 am_rodc=False, lp=None, use_ntvfs=False, skip_sysvolacl=False):
1756 # create/adapt the group policy GUIDs
1757 # Default GUID for default policy are described at
1758 # "How Core Group Policy Works"
1759 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1760 if policyguid is None:
1761 policyguid = DEFAULT_POLICY_GUID
1762 policyguid = policyguid.upper()
1763 if policyguid_dc is None:
1764 policyguid_dc = DEFAULT_DC_POLICY_GUID
1765 policyguid_dc = policyguid_dc.upper()
1767 if invocationid is None:
1768 invocationid = str(uuid.uuid4())
1770 if krbtgtpass is None:
1771 krbtgtpass = samba.generate_random_machine_password(128, 255)
1772 if machinepass is None:
1773 machinepass = samba.generate_random_machine_password(128, 255)
1775 dnspass = samba.generate_random_password(128, 255)
1777 samdb.transaction_start()
1779 samdb = fill_samdb(samdb, lp, names, logger=logger,
1781 policyguid=policyguid, policyguid_dc=policyguid_dc,
1782 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1783 invocationid=invocationid, machinepass=machinepass,
1784 dns_backend=dns_backend, dnspass=dnspass,
1785 ntdsguid=ntdsguid, serverrole=serverrole,
1786 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1787 next_rid=next_rid, dc_rid=dc_rid)
1789 # Set up group policies (domain policy and domain controller
1791 if serverrole == "active directory domain controller":
1792 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1795 samdb.transaction_cancel()
1798 samdb.transaction_commit()
1800 if serverrole == "active directory domain controller":
1801 # Continue setting up sysvol for GPO. This appears to require being
1802 # outside a transaction.
1803 if not skip_sysvolacl:
1804 setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.root_uid,
1805 paths.root_gid, names.domainsid, names.dnsdomain,
1806 names.domaindn, lp, use_ntvfs)
1808 logger.info("Setting acl on sysvol skipped")
1810 secretsdb_self_join(secrets_ldb, domain=names.domain,
1811 realm=names.realm, dnsdomain=names.dnsdomain,
1812 netbiosname=names.netbiosname, domainsid=names.domainsid,
1813 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1815 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1816 # In future, this might be determined from some configuration
1817 kerberos_enctypes = str(ENC_ALL_TYPES)
1820 msg = ldb.Message(ldb.Dn(samdb,
1821 samdb.searchone("distinguishedName",
1822 expression="samAccountName=%s$" % names.netbiosname,
1823 scope=ldb.SCOPE_SUBTREE)))
1824 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1825 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1826 name="msDS-SupportedEncryptionTypes")
1828 except ldb.LdbError, (enum, estr):
1829 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1830 # It might be that this attribute does not exist in this schema
1833 setup_ad_dns(samdb, secrets_ldb, names, paths, lp, logger,
1834 hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
1835 dnspass=dnspass, os_level=dom_for_fun_level,
1836 targetdir=targetdir, fill_level=samdb_fill)
1838 domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1839 attribute="objectGUID")
1840 assert isinstance(domainguid, str)
1842 lastProvisionUSNs = get_last_provision_usn(samdb)
1843 maxUSN = get_max_usn(samdb, str(names.rootdn))
1844 if lastProvisionUSNs is not None:
1845 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1847 set_provision_usn(samdb, 0, maxUSN, invocationid)
1849 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1850 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1851 { 'NTDSGUID' : names.ntdsguid })
1853 # fix any dangling GUIDs from the provision
1854 logger.info("Fixing provision GUIDs")
1855 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
1857 samdb.transaction_start()
1859 # a small number of GUIDs are missing because of ordering issues in the
1861 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1862 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1863 scope=ldb.SCOPE_BASE,
1864 attrs=['defaultObjectCategory'])
1865 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1866 scope=ldb.SCOPE_ONELEVEL,
1867 attrs=['ipsecOwnersReference',
1868 'ipsecFilterReference',
1869 'ipsecISAKMPReference',
1870 'ipsecNegotiationPolicyReference',
1871 'ipsecNFAReference'])
1872 if chk.check_database(DN=names.schemadn, scope=ldb.SCOPE_SUBTREE,
1873 attrs=['attributeId', 'governsId']) != 0:
1874 raise ProvisioningError("Duplicate attributeId or governsId in schema. Must be fixed manually!!")
1876 samdb.transaction_cancel()
1879 samdb.transaction_commit()
1883 "ROLE_STANDALONE": "standalone server",
1884 "ROLE_DOMAIN_MEMBER": "member server",
1885 "ROLE_DOMAIN_BDC": "active directory domain controller",
1886 "ROLE_DOMAIN_PDC": "active directory domain controller",
1887 "dc": "active directory domain controller",
1888 "member": "member server",
1889 "domain controller": "active directory domain controller",
1890 "active directory domain controller": "active directory domain controller",
1891 "member server": "member server",
1892 "standalone": "standalone server",
1893 "standalone server": "standalone server",
1897 def sanitize_server_role(role):
1898 """Sanitize a server role name.
1900 :param role: Server role
1901 :raise ValueError: If the role can not be interpreted
1902 :return: Sanitized server role (one of "member server",
1903 "active directory domain controller", "standalone server")
1906 return _ROLES_MAP[role]
1908 raise ValueError(role)
1911 def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain,
1913 """Create AD entries for the fake ypserver.
1915 This is needed for being able to manipulate posix attrs via ADUC.
1917 samdb.transaction_start()
1919 logger.info("Setting up fake yp server settings")
1920 setup_add_ldif(samdb, setup_path("ypServ30.ldif"), {
1921 "DOMAINDN": domaindn,
1922 "NETBIOSNAME": netbiosname,
1923 "NISDOMAIN": nisdomain,
1926 samdb.transaction_cancel()
1929 samdb.transaction_commit()
1932 def provision(logger, session_info, smbconf=None,
1933 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1934 domaindn=None, schemadn=None, configdn=None, serverdn=None,
1935 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1936 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None,
1937 krbtgtpass=None, domainguid=None, policyguid=None, policyguid_dc=None,
1938 dns_backend=None, dns_forwarder=None, dnspass=None,
1939 invocationid=None, machinepass=None, ntdsguid=None,
1940 root=None, nobody=None, users=None, backup=None, aci=None,
1941 serverrole=None, dom_for_fun_level=None, backend_type=None,
1942 sitename=None, ol_mmr_urls=None, ol_olc=None, slapd_path=None,
1943 useeadb=False, am_rodc=False, lp=None, use_ntvfs=False,
1944 use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True,
1945 ldap_backend_forced_uri=None, nosync=False, ldap_dryrun_mode=False, ldap_backend_extra_port=None):
1948 :note: caution, this wipes all existing data!
1952 serverrole = sanitize_server_role(serverrole)
1954 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole)
1956 if ldapadminpass is None:
1957 # Make a new, random password between Samba and it's LDAP server
1958 ldapadminpass = samba.generate_random_password(128, 255)
1960 if backend_type is None:
1961 backend_type = "ldb"
1963 if domainsid is None:
1964 domainsid = security.random_sid()
1966 root_uid = findnss_uid([root or "root"])
1967 nobody_uid = findnss_uid([nobody or "nobody"])
1968 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1969 root_gid = pwd.getpwuid(root_uid).pw_gid
1972 bind_gid = findnss_gid(["bind", "named"])
1976 if targetdir is not None:
1977 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1978 elif smbconf is None:
1979 smbconf = samba.param.default_path()
1980 if not os.path.exists(os.path.dirname(smbconf)):
1981 os.makedirs(os.path.dirname(smbconf))
1983 server_services = []
1986 global_param["idmap_ldb:use rfc2307"] = ["yes"]
1988 if dns_backend != "SAMBA_INTERNAL":
1989 server_services.append("-dns")
1991 if dns_forwarder is not None:
1992 global_param["dns forwarder"] = [dns_forwarder]
1995 server_services.append("+smb")
1996 server_services.append("-s3fs")
1997 global_param["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
1999 if len(server_services) > 0:
2000 global_param["server services"] = server_services
2002 # only install a new smb.conf if there isn't one there already
2003 if os.path.exists(smbconf):
2004 # if Samba Team members can't figure out the weird errors
2005 # loading an empty smb.conf gives, then we need to be smarter.
2006 # Pretend it just didn't exist --abartlet
2007 f = open(smbconf, 'r')
2009 data = f.read().lstrip()
2012 if data is None or data == "":
2013 make_smbconf(smbconf, hostname, domain, realm,
2014 targetdir, serverrole=serverrole,
2015 eadb=useeadb, use_ntvfs=use_ntvfs,
2016 lp=lp, global_param=global_param)
2018 make_smbconf(smbconf, hostname, domain, realm, targetdir,
2019 serverrole=serverrole,
2020 eadb=useeadb, use_ntvfs=use_ntvfs, lp=lp, global_param=global_param)
2023 lp = samba.param.LoadParm()
2025 names = guess_names(lp=lp, hostname=hostname, domain=domain,
2026 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
2027 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
2028 sitename=sitename, rootdn=rootdn, domain_names_forced=(samdb_fill == FILL_DRS))
2029 paths = provision_paths_from_lp(lp, names.dnsdomain)
2031 paths.bind_gid = bind_gid
2032 paths.root_uid = root_uid;
2033 paths.root_gid = root_gid
2036 logger.info("Looking up IPv4 addresses")
2037 hostips = interface_ips_v4(lp)
2038 if len(hostips) > 0:
2040 if len(hostips) > 1:
2041 logger.warning("More than one IPv4 address found. Using %s",
2043 if hostip == "127.0.0.1":
2046 logger.warning("No IPv4 address will be assigned")
2049 logger.info("Looking up IPv6 addresses")
2050 hostips = interface_ips_v6(lp)
2052 hostip6 = hostips[0]
2053 if len(hostips) > 1:
2054 logger.warning("More than one IPv6 address found. Using %s", hostip6)
2056 logger.warning("No IPv6 address will be assigned")
2058 names.hostip = hostip
2059 names.hostip6 = hostip6
2060 names.domainguid = domainguid
2061 names.domainsid = domainsid
2062 names.forestsid = domainsid
2064 if serverrole is None:
2065 serverrole = lp.get("server role")
2067 if not os.path.exists(paths.private_dir):
2068 os.mkdir(paths.private_dir, 0o700)
2069 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
2070 os.makedirs(os.path.join(paths.private_dir, "tls"), 0700)
2071 if not os.path.exists(paths.state_dir):
2072 os.mkdir(paths.state_dir)
2074 if paths.sysvol and not os.path.exists(paths.sysvol):
2075 os.makedirs(paths.sysvol, 0775)
2077 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
2079 schema = Schema(domainsid, invocationid=invocationid,
2080 schemadn=names.schemadn)
2082 if backend_type == "ldb":
2083 provision_backend = LDBBackend(backend_type, paths=paths,
2085 names=names, logger=logger)
2086 elif backend_type == "existing":
2087 # If support for this is ever added back, then the URI will need to be
2089 provision_backend = ExistingBackend(backend_type, paths=paths,
2091 names=names, logger=logger,
2092 ldap_backend_forced_uri=ldap_backend_forced_uri)
2093 elif backend_type == "fedora-ds":
2094 provision_backend = FDSBackend(backend_type, paths=paths,
2096 names=names, logger=logger, domainsid=domainsid,
2097 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2098 slapd_path=slapd_path,
2100 elif backend_type == "openldap":
2101 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
2103 names=names, logger=logger, domainsid=domainsid,
2104 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2105 slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls,
2106 ldap_backend_extra_port=ldap_backend_extra_port,
2107 ldap_dryrun_mode=ldap_dryrun_mode, nosync=nosync,
2108 ldap_backend_forced_uri=ldap_backend_forced_uri)
2110 raise ValueError("Unknown LDAP backend type selected")
2112 provision_backend.init()
2113 provision_backend.start()
2115 # only install a new shares config db if there is none
2116 if not os.path.exists(paths.shareconf):
2117 logger.info("Setting up share.ldb")
2118 share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
2119 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
2121 logger.info("Setting up secrets.ldb")
2122 secrets_ldb = setup_secretsdb(paths,
2123 session_info=session_info,
2124 backend_credentials=provision_backend.credentials, lp=lp)
2127 logger.info("Setting up the registry")
2128 setup_registry(paths.hklm, session_info, lp=lp)
2130 logger.info("Setting up the privileges database")
2131 setup_privileges(paths.privilege, session_info, lp=lp)
2133 logger.info("Setting up idmap db")
2134 idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
2136 setup_name_mappings(idmap, sid=str(domainsid),
2137 root_uid=root_uid, nobody_uid=nobody_uid,
2138 users_gid=users_gid, root_gid=root_gid)
2140 logger.info("Setting up SAM db")
2141 samdb = setup_samdb(paths.samdb, session_info,
2142 provision_backend, lp, names, logger=logger,
2143 serverrole=serverrole,
2144 schema=schema, fill=samdb_fill, am_rodc=am_rodc)
2146 if serverrole == "active directory domain controller":
2147 if paths.netlogon is None:
2148 raise MissingShareError("netlogon", paths.smbconf)
2150 if paths.sysvol is None:
2151 raise MissingShareError("sysvol", paths.smbconf)
2153 if not os.path.isdir(paths.netlogon):
2154 os.makedirs(paths.netlogon, 0755)
2156 if adminpass is None:
2157 adminpass = samba.generate_random_password(12, 32)
2158 adminpass_generated = True
2160 adminpass = unicode(adminpass, 'utf-8')
2161 adminpass_generated = False
2163 if samdb_fill == FILL_FULL:
2164 provision_fill(samdb, secrets_ldb, logger, names, paths,
2165 schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
2166 hostip=hostip, hostip6=hostip6,
2167 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
2168 krbtgtpass=krbtgtpass,
2169 policyguid=policyguid, policyguid_dc=policyguid_dc,
2170 invocationid=invocationid, machinepass=machinepass,
2171 ntdsguid=ntdsguid, dns_backend=dns_backend,
2172 dnspass=dnspass, serverrole=serverrole,
2173 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
2174 lp=lp, use_ntvfs=use_ntvfs,
2175 skip_sysvolacl=skip_sysvolacl)
2177 if not is_heimdal_built():
2178 create_kdc_conf(paths.kdcconf, realm, domain, os.path.dirname(lp.get("log file")))
2179 logger.info("The Kerberos KDC configuration for Samba AD is "
2180 "located at %s", paths.kdcconf)
2182 create_krb5_conf(paths.krb5conf,
2183 dnsdomain=names.dnsdomain, hostname=names.hostname,
2185 logger.info("A Kerberos configuration suitable for Samba AD has been "
2186 "generated at %s", paths.krb5conf)
2188 if serverrole == "active directory domain controller":
2189 create_dns_update_list(lp, logger, paths)
2191 backend_result = provision_backend.post_setup()
2192 provision_backend.shutdown()
2195 secrets_ldb.transaction_cancel()
2198 # Now commit the secrets.ldb to disk
2199 secrets_ldb.transaction_commit()
2201 # the commit creates the dns.keytab, now chown it
2202 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
2203 if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
2205 os.chmod(dns_keytab_path, 0640)
2206 os.chown(dns_keytab_path, -1, paths.bind_gid)
2208 if not os.environ.has_key('SAMBA_SELFTEST'):
2209 logger.info("Failed to chown %s to bind gid %u",
2210 dns_keytab_path, paths.bind_gid)
2212 result = ProvisionResult()
2213 result.server_role = serverrole
2214 result.domaindn = domaindn
2215 result.paths = paths
2216 result.names = names
2218 result.samdb = samdb
2219 result.idmap = idmap
2220 result.domainsid = str(domainsid)
2222 if samdb_fill == FILL_FULL:
2223 result.adminpass_generated = adminpass_generated
2224 result.adminpass = adminpass
2226 result.adminpass_generated = False
2227 result.adminpass = None
2229 result.backend_result = backend_result
2232 provision_fake_ypserver(logger=logger, samdb=samdb,
2233 domaindn=names.domaindn, netbiosname=names.netbiosname,
2234 nisdomain=names.domain.lower(), maxuid=maxuid, maxgid=maxgid)
2239 def provision_become_dc(smbconf=None, targetdir=None,
2240 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
2241 serverdn=None, domain=None, hostname=None, domainsid=None,
2242 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
2243 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
2244 dns_backend=None, root=None, nobody=None, users=None,
2245 backup=None, serverrole=None, ldap_backend=None,
2246 ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False):
2248 logger = logging.getLogger("provision")
2249 samba.set_debug_level(debuglevel)
2251 res = provision(logger, system_session(),
2252 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
2253 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
2254 configdn=configdn, serverdn=serverdn, domain=domain,
2255 hostname=hostname, hostip=None, domainsid=domainsid,
2256 machinepass=machinepass,
2257 serverrole="active directory domain controller",
2258 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass,
2259 use_ntvfs=use_ntvfs)
2260 res.lp.set("debuglevel", str(debuglevel))
2264 def create_krb5_conf(path, dnsdomain, hostname, realm):
2265 """Write out a file containing a valid krb5.conf file
2267 :param path: Path of the new krb5.conf file.
2268 :param dnsdomain: DNS Domain name
2269 :param hostname: Local hostname
2270 :param realm: Realm name
2272 setup_file(setup_path("krb5.conf"), path, {
2273 "DNSDOMAIN": dnsdomain,
2274 "HOSTNAME": hostname,
2279 class ProvisioningError(Exception):
2280 """A generic provision error."""
2282 def __init__(self, value):
2286 return "ProvisioningError: " + self.value
2289 class InvalidNetbiosName(Exception):
2290 """A specified name was not a valid NetBIOS name."""
2292 def __init__(self, name):
2293 super(InvalidNetbiosName, self).__init__(
2294 "The name '%r' is not a valid NetBIOS name" % name)
2297 class MissingShareError(ProvisioningError):
2299 def __init__(self, name, smbconf):
2300 super(MissingShareError, self).__init__(
2301 "Existing smb.conf does not have a [%s] share, but you are "
2302 "configuring a DC. Please remove %s or add the share manually." %