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
47 from samba.auth import system_session, admin_session
49 from samba.samba3 import smbd, passdb
50 from samba.samba3 import param as s3param
51 from samba.dsdb import DS_DOMAIN_FUNCTION_2000
55 check_all_substituted,
56 is_valid_netbios_char,
63 from samba.dcerpc import security, misc
64 from samba.dcerpc.misc import (
68 from samba.dsdb import (
69 DS_DOMAIN_FUNCTION_2003,
70 DS_DOMAIN_FUNCTION_2008_R2,
73 from samba.idmap import IDmapDB
74 from samba.ms_display_specifiers import read_ms_ldif
75 from samba.ntacls import setntacl, getntacl, dsacl2fsacl
76 from samba.ndr import ndr_pack, ndr_unpack
77 from samba.provision.backend import (
83 from samba.descriptor import (
85 get_config_descriptor,
86 get_config_partitions_descriptor,
87 get_config_sites_descriptor,
88 get_config_ntds_quotas_descriptor,
89 get_config_delete_protected1_descriptor,
90 get_config_delete_protected1wd_descriptor,
91 get_config_delete_protected2_descriptor,
92 get_domain_descriptor,
93 get_domain_infrastructure_descriptor,
94 get_domain_builtin_descriptor,
95 get_domain_computers_descriptor,
96 get_domain_users_descriptor,
97 get_domain_controllers_descriptor,
98 get_domain_delete_protected1_descriptor,
99 get_domain_delete_protected2_descriptor,
100 get_dns_partition_descriptor,
101 get_dns_forest_microsoft_dns_descriptor,
102 get_dns_domain_microsoft_dns_descriptor,
103 get_managed_service_accounts_descriptor,
105 from samba.provision.common import (
114 from samba.provision.sambadns import (
117 create_dns_update_list
121 import samba.registry
122 from samba.schema import Schema
123 from samba.samdb import SamDB
124 from samba.dbchecker import dbcheck
125 from samba.provision.kerberos import create_kdc_conf
126 from samba.samdb import get_default_backend_store
128 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
129 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04FB984F9"
130 DEFAULTSITE = "Default-First-Site-Name"
131 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
133 DEFAULT_MIN_PWD_LENGTH = 7
136 class ProvisionPaths(object):
139 self.shareconf = None
150 self.dns_keytab = None
153 self.private_dir = None
154 self.binddns_dir = None
155 self.state_dir = None
158 class ProvisionNames(object):
166 self.dnsforestdn = None
167 self.dnsdomaindn = None
168 self.ldapmanagerdn = None
169 self.dnsdomain = None
171 self.netbiosname = None
176 self.domainsid = None
177 self.forestsid = None
178 self.domainguid = None
182 def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf,
184 """Get key provision parameters (realm, domain, ...) from a given provision
186 :param samdb: An LDB object connected to the sam.ldb file
187 :param secretsdb: An LDB object connected to the secrets.ldb file
188 :param idmapdb: An LDB object connected to the idmap.ldb file
189 :param paths: A list of path to provision object
190 :param smbconf: Path to the smb.conf file
191 :param lp: A LoadParm object
192 :return: A list of key provision parameters
194 names = ProvisionNames()
195 names.adminpass = None
197 # NT domain, kerberos realm, root dn, domain dn, domain dns name
198 names.domain = string.upper(lp.get("workgroup"))
199 names.realm = lp.get("realm")
200 names.dnsdomain = names.realm.lower()
201 basedn = samba.dn_from_dns_name(names.dnsdomain)
202 names.realm = string.upper(names.realm)
204 # Get the netbiosname first (could be obtained from smb.conf in theory)
205 res = secretsdb.search(expression="(flatname=%s)" %
206 names.domain,base="CN=Primary Domains",
207 scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"])
208 names.netbiosname = str(res[0]["sAMAccountName"]).replace("$","")
210 names.smbconf = smbconf
212 # That's a bit simplistic but it's ok as long as we have only 3
214 current = samdb.search(expression="(objectClass=*)",
215 base="", scope=ldb.SCOPE_BASE,
216 attrs=["defaultNamingContext", "schemaNamingContext",
217 "configurationNamingContext","rootDomainNamingContext",
220 names.configdn = current[0]["configurationNamingContext"][0]
221 names.schemadn = current[0]["schemaNamingContext"][0]
222 if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb,
223 current[0]["defaultNamingContext"][0]))):
224 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
225 "is not the same ..." % (paths.samdb,
226 str(current[0]["defaultNamingContext"][0]),
227 paths.smbconf, basedn)))
229 names.domaindn=current[0]["defaultNamingContext"][0]
230 names.rootdn=current[0]["rootDomainNamingContext"][0]
231 names.ncs=current[0]["namingContexts"]
232 names.dnsforestdn = None
233 names.dnsdomaindn = None
235 for i in range(0, len(names.ncs)):
238 dnsforestdn = "DC=ForestDnsZones,%s" % (str(names.rootdn))
239 if nc == dnsforestdn:
240 names.dnsforestdn = dnsforestdn
243 dnsdomaindn = "DC=DomainDnsZones,%s" % (str(names.domaindn))
244 if nc == dnsdomaindn:
245 names.dnsdomaindn = dnsdomaindn
249 res3 = samdb.search(expression="(objectClass=site)",
250 base="CN=Sites," + names.configdn, scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
251 names.sitename = str(res3[0]["cn"])
253 # dns hostname and server dn
254 res4 = samdb.search(expression="(CN=%s)" % names.netbiosname,
255 base="OU=Domain Controllers,%s" % basedn,
256 scope=ldb.SCOPE_ONELEVEL, attrs=["dNSHostName"])
258 raise ProvisioningError("Unable to find DC called CN=%s under OU=Domain Controllers,%s" % (names.netbiosname, basedn))
260 names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain, "")
262 server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
263 attrs=[], base=names.configdn)
264 names.serverdn = str(server_res[0].dn)
266 # invocation id/objectguid
267 res5 = samdb.search(expression="(objectClass=*)",
268 base="CN=NTDS Settings,%s" % str(names.serverdn),
269 scope=ldb.SCOPE_BASE,
270 attrs=["invocationID", "objectGUID"])
271 names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0]))
272 names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0]))
275 res6 = samdb.search(expression="(objectClass=*)", base=basedn,
276 scope=ldb.SCOPE_BASE, attrs=["objectGUID",
277 "objectSid","msDS-Behavior-Version" ])
278 names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
279 names.domainsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
280 names.forestsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
281 if res6[0].get("msDS-Behavior-Version") is None or \
282 int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
283 names.domainlevel = DS_DOMAIN_FUNCTION_2000
285 names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
288 res7 = samdb.search(expression="(name={%s})" % DEFAULT_POLICY_GUID,
289 base="CN=Policies,CN=System," + basedn,
290 scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
291 names.policyid = str(res7[0]["cn"]).replace("{","").replace("}","")
293 res8 = samdb.search(expression="(name={%s})" % DEFAULT_DC_POLICY_GUID,
294 base="CN=Policies,CN=System," + basedn,
295 scope=ldb.SCOPE_ONELEVEL,
296 attrs=["cn","displayName"])
298 names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","")
300 names.policyid_dc = None
302 res9 = idmapdb.search(expression="(cn=%s-%s)" %
303 (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR),
304 attrs=["xidNumber", "type"])
306 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid (%s-%s" % (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR))
307 if res9[0]["type"][0] == "ID_TYPE_BOTH":
308 names.root_gid = res9[0]["xidNumber"][0]
310 names.root_gid = pwd.getpwuid(int(res9[0]["xidNumber"][0])).pw_gid
312 res10 = samdb.search(expression="(samaccountname=dns)",
313 scope=ldb.SCOPE_SUBTREE, attrs=["dn"],
314 controls=["search_options:1:2"])
316 has_legacy_dns_account = True
318 has_legacy_dns_account = False
320 res11 = samdb.search(expression="(samaccountname=dns-%s)" % names.netbiosname,
321 scope=ldb.SCOPE_SUBTREE, attrs=["dn"],
322 controls=["search_options:1:2"])
324 has_dns_account = True
326 has_dns_account = False
328 if names.dnsdomaindn is not None:
330 names.dns_backend = 'BIND9_DLZ'
332 names.dns_backend = 'SAMBA_INTERNAL'
333 elif has_dns_account or has_legacy_dns_account:
334 names.dns_backend = 'BIND9_FLATFILE'
336 names.dns_backend = 'NONE'
338 dns_admins_sid = get_dnsadmins_sid(samdb, names.domaindn)
339 names.name_map['DnsAdmins'] = str(dns_admins_sid)
344 def update_provision_usn(samdb, low, high, id, replace=False):
345 """Update the field provisionUSN in sam.ldb
347 This field is used to track range of USN modified by provision and
349 This value is used afterward by next provision to figure out if
350 the field have been modified since last provision.
352 :param samdb: An LDB object connect to sam.ldb
353 :param low: The lowest USN modified by this upgrade
354 :param high: The highest USN modified by this upgrade
355 :param id: The invocation id of the samba's dc
356 :param replace: A boolean indicating if the range should replace any
357 existing one or appended (default)
362 entry = samdb.search(base="@PROVISION",
363 scope=ldb.SCOPE_BASE,
364 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
365 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
366 if not re.search(';', e):
367 e = "%s;%s" % (e, id)
370 tab.append("%s-%s;%s" % (low, high, id))
371 delta = ldb.Message()
372 delta.dn = ldb.Dn(samdb, "@PROVISION")
373 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
374 ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE)
375 entry = samdb.search(expression='provisionnerID=*',
376 base="@PROVISION", scope=ldb.SCOPE_BASE,
377 attrs=["provisionnerID"])
378 if len(entry) == 0 or len(entry[0]) == 0:
379 delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
383 def set_provision_usn(samdb, low, high, id):
384 """Set the field provisionUSN in sam.ldb
385 This field is used to track range of USN modified by provision and
387 This value is used afterward by next provision to figure out if
388 the field have been modified since last provision.
390 :param samdb: An LDB object connect to sam.ldb
391 :param low: The lowest USN modified by this upgrade
392 :param high: The highest USN modified by this upgrade
393 :param id: The invocationId of the provision"""
396 tab.append("%s-%s;%s" % (low, high, id))
398 delta = ldb.Message()
399 delta.dn = ldb.Dn(samdb, "@PROVISION")
400 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
401 ldb.FLAG_MOD_ADD, LAST_PROVISION_USN_ATTRIBUTE)
405 def get_max_usn(samdb,basedn):
406 """ This function return the biggest USN present in the provision
408 :param samdb: A LDB object pointing to the sam.ldb
409 :param basedn: A string containing the base DN of the provision
411 :return: The biggest USN in the provision"""
413 res = samdb.search(expression="objectClass=*",base=basedn,
414 scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
415 controls=["search_options:1:2",
416 "server_sort:1:1:uSNChanged",
417 "paged_results:1:1"])
418 return res[0]["uSNChanged"]
421 def get_last_provision_usn(sam):
422 """Get USNs ranges modified by a provision or an upgradeprovision
424 :param sam: An LDB object pointing to the sam.ldb
425 :return: a dictionary which keys are invocation id and values are an array
426 of integer representing the different ranges
429 entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE,
430 base="@PROVISION", scope=ldb.SCOPE_BASE,
431 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
432 except ldb.LdbError as e1:
433 (ecode, emsg) = e1.args
434 if ecode == ldb.ERR_NO_SUCH_OBJECT:
441 if entry[0].get("provisionnerID"):
442 for e in entry[0]["provisionnerID"]:
444 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
445 tab1 = str(r).split(';')
450 if (len(myids) > 0 and id not in myids):
452 tab2 = p.split(tab1[0])
453 if range.get(id) is None:
455 range[id].append(tab2[0])
456 range[id].append(tab2[1])
462 class ProvisionResult(object):
463 """Result of a provision.
465 :ivar server_role: The server role
466 :ivar paths: ProvisionPaths instance
467 :ivar domaindn: The domain dn, as string
471 self.server_role = None
478 self.domainsid = None
479 self.adminpass_generated = None
480 self.adminpass = None
481 self.backend_result = None
483 def report_logger(self, logger):
484 """Report this provision result to a logger."""
486 "Once the above files are installed, your Samba AD server will "
488 if self.adminpass_generated:
489 logger.info("Admin password: %s", self.adminpass)
490 logger.info("Server Role: %s", self.server_role)
491 logger.info("Hostname: %s", self.names.hostname)
492 logger.info("NetBIOS Domain: %s", self.names.domain)
493 logger.info("DNS Domain: %s", self.names.dnsdomain)
494 logger.info("DOMAIN SID: %s", self.domainsid)
496 if self.backend_result:
497 self.backend_result.report_logger(logger)
500 def check_install(lp, session_info, credentials):
501 """Check whether the current install seems ok.
503 :param lp: Loadparm context
504 :param session_info: Session information
505 :param credentials: Credentials
507 if lp.get("realm") == "":
508 raise Exception("Realm empty")
509 samdb = Ldb(lp.samdb_url(), session_info=session_info,
510 credentials=credentials, lp=lp)
511 if len(samdb.search("(cn=Administrator)")) != 1:
512 raise ProvisioningError("No administrator account found")
515 def findnss(nssfn, names):
516 """Find a user or group from a list of possibilities.
518 :param nssfn: NSS Function to try (should raise KeyError if not found)
519 :param names: Names to check.
520 :return: Value return by first names list.
527 raise KeyError("Unable to find user/group in %r" % names)
530 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
531 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
534 def provision_paths_from_lp(lp, dnsdomain):
535 """Set the default paths for provisioning.
537 :param lp: Loadparm context.
538 :param dnsdomain: DNS Domain name
540 paths = ProvisionPaths()
541 paths.private_dir = lp.get("private dir")
542 paths.binddns_dir = lp.get("binddns dir")
543 paths.state_dir = lp.get("state directory")
545 # This is stored without path prefix for the "privateKeytab" attribute in
546 # "secrets_dns.ldif".
547 paths.dns_keytab = "dns.keytab"
548 paths.keytab = "secrets.keytab"
550 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
551 paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
552 paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
553 paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
554 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
555 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
556 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
557 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
558 paths.kdcconf = os.path.join(paths.private_dir, "kdc.conf")
559 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
560 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
561 paths.encrypted_secrets_key_path = os.path.join(
563 "encrypted_secrets.key")
565 paths.dns = os.path.join(paths.binddns_dir, "dns", dnsdomain + ".zone")
566 paths.namedconf = os.path.join(paths.binddns_dir, "named.conf")
567 paths.namedconf_update = os.path.join(paths.binddns_dir, "named.conf.update")
568 paths.namedtxt = os.path.join(paths.binddns_dir, "named.txt")
570 paths.hklm = "hklm.ldb"
571 paths.hkcr = "hkcr.ldb"
572 paths.hkcu = "hkcu.ldb"
573 paths.hku = "hku.ldb"
574 paths.hkpd = "hkpd.ldb"
575 paths.hkpt = "hkpt.ldb"
576 paths.sysvol = lp.get("path", "sysvol")
577 paths.netlogon = lp.get("path", "netlogon")
578 paths.smbconf = lp.configfile
582 def determine_netbios_name(hostname):
583 """Determine a netbios name from a hostname."""
584 # remove forbidden chars and force the length to be <16
585 netbiosname = "".join([x for x in hostname if is_valid_netbios_char(x)])
586 return netbiosname[:MAX_NETBIOS_NAME_LEN].upper()
589 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
590 serverrole=None, rootdn=None, domaindn=None, configdn=None,
591 schemadn=None, serverdn=None, sitename=None,
592 domain_names_forced=False):
593 """Guess configuration settings to use."""
596 hostname = socket.gethostname().split(".")[0]
598 netbiosname = lp.get("netbios name")
599 if netbiosname is None:
600 netbiosname = determine_netbios_name(hostname)
601 netbiosname = netbiosname.upper()
602 if not valid_netbios_name(netbiosname):
603 raise InvalidNetbiosName(netbiosname)
605 if dnsdomain is None:
606 dnsdomain = lp.get("realm")
607 if dnsdomain is None or dnsdomain == "":
608 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
610 dnsdomain = dnsdomain.lower()
612 if serverrole is None:
613 serverrole = lp.get("server role")
614 if serverrole is None:
615 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
617 serverrole = serverrole.lower()
619 realm = dnsdomain.upper()
621 if lp.get("realm") == "":
622 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
624 if lp.get("realm").upper() != realm:
625 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))
627 if lp.get("server role").lower() != serverrole:
628 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))
630 if serverrole == "active directory domain controller":
632 # This will, for better or worse, default to 'WORKGROUP'
633 domain = lp.get("workgroup")
634 domain = domain.upper()
636 if lp.get("workgroup").upper() != domain:
637 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))
640 domaindn = samba.dn_from_dns_name(dnsdomain)
642 if domain == netbiosname:
643 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
647 domaindn = "DC=" + netbiosname
649 if not valid_netbios_name(domain):
650 raise InvalidNetbiosName(domain)
652 if hostname.upper() == realm:
653 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
654 if netbiosname.upper() == realm:
655 raise ProvisioningError("guess_names: Realm '%s' must not be equal to NetBIOS hostname '%s'!" % (realm, netbiosname))
656 if domain == realm and not domain_names_forced:
657 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
659 if serverrole != "active directory domain controller":
661 # This is the code path for a domain member
662 # where we provision the database as if we where
663 # on a domain controller, so we should not use
664 # the same dnsdomain as the domain controllers
665 # of our primary domain.
667 # This will be important if we start doing
668 # SID/name filtering and reject the local
669 # sid and names if they come from a domain
673 dnsdomain = netbiosname.lower()
679 configdn = "CN=Configuration," + rootdn
681 schemadn = "CN=Schema," + configdn
684 sitename = DEFAULTSITE
686 names = ProvisionNames()
687 names.rootdn = rootdn
688 names.domaindn = domaindn
689 names.configdn = configdn
690 names.schemadn = schemadn
691 names.ldapmanagerdn = "CN=Manager," + rootdn
692 names.dnsdomain = dnsdomain
693 names.domain = domain
695 names.netbiosname = netbiosname
696 names.hostname = hostname
697 names.sitename = sitename
698 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
699 netbiosname, sitename, configdn)
703 def make_smbconf(smbconf, hostname, domain, realm, targetdir,
704 serverrole=None, eadb=False, use_ntvfs=False, lp=None,
706 """Create a new smb.conf file based on a couple of basic settings.
708 assert smbconf is not None
711 hostname = socket.gethostname().split(".")[0]
713 netbiosname = determine_netbios_name(hostname)
715 if serverrole is None:
716 serverrole = "standalone server"
718 assert domain is not None
719 domain = domain.upper()
721 assert realm is not None
722 realm = realm.upper()
725 "netbios name": netbiosname,
728 "server role": serverrole,
732 lp = samba.param.LoadParm()
733 #Load non-existent file
734 if os.path.exists(smbconf):
737 if global_param is not None:
738 for ent in global_param:
739 if global_param[ent] is not None:
740 global_settings[ent] = " ".join(global_param[ent])
742 if targetdir is not None:
743 global_settings["private dir"] = os.path.abspath(os.path.join(targetdir, "private"))
744 global_settings["lock dir"] = os.path.abspath(targetdir)
745 global_settings["state directory"] = os.path.abspath(os.path.join(targetdir, "state"))
746 global_settings["cache directory"] = os.path.abspath(os.path.join(targetdir, "cache"))
748 lp.set("lock dir", os.path.abspath(targetdir))
749 lp.set("state directory", global_settings["state directory"])
750 lp.set("cache directory", global_settings["cache directory"])
753 if use_ntvfs and not lp.get("posix:eadb"):
754 if targetdir is not None:
755 privdir = os.path.join(targetdir, "private")
757 privdir = lp.get("private dir")
758 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
759 elif not use_ntvfs and not lp.get("xattr_tdb:file"):
760 if targetdir is not None:
761 statedir = os.path.join(targetdir, "state")
763 statedir = lp.get("state directory")
764 lp.set("xattr_tdb:file", os.path.abspath(os.path.join(statedir, "xattr.tdb")))
767 if serverrole == "active directory domain controller":
768 shares["sysvol"] = os.path.join(lp.get("state directory"), "sysvol")
769 shares["netlogon"] = os.path.join(shares["sysvol"], realm.lower(),
772 global_settings["passdb backend"] = "samba_dsdb"
774 f = open(smbconf, 'w')
776 f.write("[globals]\n")
777 for key, val in global_settings.iteritems():
778 f.write("\t%s = %s\n" % (key, val))
781 for name, path in shares.iteritems():
782 f.write("[%s]\n" % name)
783 f.write("\tpath = %s\n" % path)
784 f.write("\tread only = no\n")
788 # reload the smb.conf
791 # and dump it without any values that are the default
792 # this ensures that any smb.conf parameters that were set
793 # on the provision/join command line are set in the resulting smb.conf
794 lp.dump(False, smbconf)
797 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
798 users_gid, root_gid):
799 """setup reasonable name mappings for sam names to unix names.
801 :param samdb: SamDB object.
802 :param idmap: IDmap db object.
803 :param sid: The domain sid.
804 :param domaindn: The domain DN.
805 :param root_uid: uid of the UNIX root user.
806 :param nobody_uid: uid of the UNIX nobody user.
807 :param users_gid: gid of the UNIX users group.
808 :param root_gid: gid of the UNIX root group.
810 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
812 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
813 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
816 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
817 provision_backend, names, serverrole,
818 erase=False, plaintext_secrets=False,
820 """Setup the partitions for the SAM database.
822 Alternatively, provision() may call this, and then populate the database.
824 :note: This will wipe the Sam Database!
826 :note: This function always removes the local SAM LDB file. The erase
827 parameter controls whether to erase the existing data, which
828 may not be stored locally but in LDAP.
831 assert session_info is not None
833 # We use options=["modules:"] to stop the modules loading - we
834 # just want to wipe and re-initialise the database, not start it up
837 os.unlink(samdb_path)
841 samdb = Ldb(url=samdb_path, session_info=session_info,
842 lp=lp, options=["modules:"])
844 ldap_backend_line = "# No LDAP backend"
845 if provision_backend.type != "ldb":
846 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
848 required_features = "# No required features"
849 if not plaintext_secrets:
850 required_features = "requiredFeatures: encryptedSecrets"
852 if backend_store is None:
853 backend_store = get_default_backend_store()
854 backend_store_line = "backendStore: %s" % backend_store
856 samdb.transaction_start()
858 logger.info("Setting up sam.ldb partitions and settings")
859 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
860 "LDAP_BACKEND_LINE": ldap_backend_line,
861 "BACKEND_STORE": backend_store_line
865 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
866 "BACKEND_TYPE": provision_backend.type,
867 "SERVER_ROLE": serverrole,
868 "REQUIRED_FEATURES": required_features
871 logger.info("Setting up sam.ldb rootDSE")
872 setup_samdb_rootdse(samdb, names)
874 samdb.transaction_cancel()
877 samdb.transaction_commit()
880 def secretsdb_self_join(secretsdb, domain,
881 netbiosname, machinepass, domainsid=None,
882 realm=None, dnsdomain=None,
884 key_version_number=1,
885 secure_channel_type=SEC_CHAN_WKSTA):
886 """Add domain join-specific bits to a secrets database.
888 :param secretsdb: Ldb Handle to the secrets database
889 :param machinepass: Machine password
891 attrs = ["whenChanged",
898 if realm is not None:
899 if dnsdomain is None:
900 dnsdomain = realm.lower()
901 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
904 shortname = netbiosname.lower()
906 # We don't need to set msg["flatname"] here, because rdn_name will handle
907 # it, and it causes problems for modifies anyway
908 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
909 msg["secureChannelType"] = [str(secure_channel_type)]
910 msg["objectClass"] = ["top", "primaryDomain"]
911 if dnsname is not None:
912 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
913 msg["realm"] = [realm]
914 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
915 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
916 msg["privateKeytab"] = ["secrets.keytab"]
918 msg["secret"] = [machinepass.encode('utf-8')]
919 msg["samAccountName"] = ["%s$" % netbiosname]
920 msg["secureChannelType"] = [str(secure_channel_type)]
921 if domainsid is not None:
922 msg["objectSid"] = [ndr_pack(domainsid)]
924 # This complex expression tries to ensure that we don't have more
925 # than one record for this SID, realm or netbios domain at a time,
926 # but we don't delete the old record that we are about to modify,
927 # because that would delete the keytab and previous password.
928 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
929 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
930 scope=ldb.SCOPE_ONELEVEL)
933 secretsdb.delete(del_msg.dn)
935 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
938 msg["priorSecret"] = [res[0]["secret"][0]]
940 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
945 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
950 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
956 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
957 secretsdb.modify(msg)
958 secretsdb.rename(res[0].dn, msg.dn)
960 spn = [ 'HOST/%s' % shortname ]
961 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
962 # we are a domain controller then we add servicePrincipalName
963 # entries for the keytab code to update.
964 spn.extend([ 'HOST/%s' % dnsname ])
965 msg["servicePrincipalName"] = spn
970 def setup_secretsdb(paths, session_info, backend_credentials, lp):
971 """Setup the secrets database.
973 :note: This function does not handle exceptions and transaction on purpose,
974 it's up to the caller to do this job.
976 :param path: Path to the secrets database.
977 :param session_info: Session info.
978 :param credentials: Credentials
979 :param lp: Loadparm context
980 :return: LDB handle for the created secrets database
982 if os.path.exists(paths.secrets):
983 os.unlink(paths.secrets)
985 keytab_path = os.path.join(paths.private_dir, paths.keytab)
986 if os.path.exists(keytab_path):
987 os.unlink(keytab_path)
989 bind_dns_keytab_path = os.path.join(paths.binddns_dir, paths.dns_keytab)
990 if os.path.exists(bind_dns_keytab_path):
991 os.unlink(bind_dns_keytab_path)
993 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
994 if os.path.exists(dns_keytab_path):
995 os.unlink(dns_keytab_path)
999 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
1001 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
1002 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
1003 secrets_ldb.transaction_start()
1005 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
1007 if (backend_credentials is not None and
1008 backend_credentials.authentication_requested()):
1009 if backend_credentials.get_bind_dn() is not None:
1010 setup_add_ldif(secrets_ldb,
1011 setup_path("secrets_simple_ldap.ldif"), {
1012 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
1013 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
1016 setup_add_ldif(secrets_ldb,
1017 setup_path("secrets_sasl_ldap.ldif"), {
1018 "LDAPADMINUSER": backend_credentials.get_username(),
1019 "LDAPADMINREALM": backend_credentials.get_realm(),
1020 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
1023 secrets_ldb.transaction_cancel()
1028 def setup_privileges(path, session_info, lp):
1029 """Setup the privileges database.
1031 :param path: Path to the privileges database.
1032 :param session_info: Session info.
1033 :param credentials: Credentials
1034 :param lp: Loadparm context
1035 :return: LDB handle for the created secrets database
1037 if os.path.exists(path):
1039 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
1040 privilege_ldb.erase()
1041 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
1043 def setup_encrypted_secrets_key(path):
1044 """Setup the encrypted secrets key file.
1046 Any existing key file will be deleted and a new random key generated.
1048 :param path: Path to the secrets key file.
1051 if os.path.exists(path):
1054 flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
1055 mode = stat.S_IRUSR | stat.S_IWUSR
1057 umask_original = os.umask(0)
1059 fd = os.open(path, flags, mode)
1061 os.umask(umask_original)
1063 with os.fdopen(fd, 'w') as f:
1064 key = samba.generate_random_bytes(16)
1068 def setup_registry(path, session_info, lp):
1069 """Setup the registry.
1071 :param path: Path to the registry database
1072 :param session_info: Session information
1073 :param credentials: Credentials
1074 :param lp: Loadparm context
1076 reg = samba.registry.Registry()
1077 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
1078 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
1079 provision_reg = setup_path("provision.reg")
1080 assert os.path.exists(provision_reg)
1081 reg.diff_apply(provision_reg)
1084 def setup_idmapdb(path, session_info, lp):
1085 """Setup the idmap database.
1087 :param path: path to the idmap database
1088 :param session_info: Session information
1089 :param credentials: Credentials
1090 :param lp: Loadparm context
1092 if os.path.exists(path):
1095 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
1097 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
1101 def setup_samdb_rootdse(samdb, names):
1102 """Setup the SamDB rootdse.
1104 :param samdb: Sam Database handle
1106 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
1107 "SCHEMADN": names.schemadn,
1108 "DOMAINDN": names.domaindn,
1109 "ROOTDN" : names.rootdn,
1110 "CONFIGDN": names.configdn,
1111 "SERVERDN": names.serverdn,
1115 def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
1116 dns_backend, dnspass, domainsid, next_rid, invocationid,
1117 policyguid, policyguid_dc,
1118 domainControllerFunctionality, ntdsguid=None, dc_rid=None):
1119 """Join a host to its own domain."""
1120 assert isinstance(invocationid, str)
1121 if ntdsguid is not None:
1122 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
1129 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
1130 "CONFIGDN": names.configdn,
1131 "SCHEMADN": names.schemadn,
1132 "DOMAINDN": names.domaindn,
1133 "SERVERDN": names.serverdn,
1134 "INVOCATIONID": invocationid,
1135 "NETBIOSNAME": names.netbiosname,
1136 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1137 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1138 "DOMAINSID": str(domainsid),
1139 "DCRID": str(dc_rid),
1140 "SAMBA_VERSION_STRING": version,
1141 "NTDSGUID": ntdsguid_line,
1142 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1143 domainControllerFunctionality),
1144 "RIDALLOCATIONSTART": str(next_rid + 100),
1145 "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
1147 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1148 "POLICYGUID": policyguid,
1149 "POLICYGUID_DC": policyguid_dc,
1150 "DNSDOMAIN": names.dnsdomain,
1151 "DOMAINDN": names.domaindn})
1153 # If we are setting up a subdomain, then this has been replicated in, so we
1154 # don't need to add it
1155 if fill == FILL_FULL:
1156 setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
1157 "CONFIGDN": names.configdn,
1158 "SCHEMADN": names.schemadn,
1159 "DOMAINDN": names.domaindn,
1160 "SERVERDN": names.serverdn,
1161 "INVOCATIONID": invocationid,
1162 "NETBIOSNAME": names.netbiosname,
1163 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1164 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1165 "DOMAINSID": str(domainsid),
1166 "DCRID": str(dc_rid),
1167 "SAMBA_VERSION_STRING": version,
1168 "NTDSGUID": ntdsguid_line,
1169 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1170 domainControllerFunctionality)})
1172 # Setup fSMORoleOwner entries to point at the newly created DC entry
1173 setup_modify_ldif(samdb,
1174 setup_path("provision_self_join_modify_config.ldif"), {
1175 "CONFIGDN": names.configdn,
1176 "SCHEMADN": names.schemadn,
1177 "DEFAULTSITE": names.sitename,
1178 "NETBIOSNAME": names.netbiosname,
1179 "SERVERDN": names.serverdn,
1182 system_session_info = system_session()
1183 samdb.set_session_info(system_session_info)
1184 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1185 # modify a serverReference under cn=config when we are a subdomain, we must
1186 # be system due to ACLs
1187 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1188 "DOMAINDN": names.domaindn,
1189 "SERVERDN": names.serverdn,
1190 "NETBIOSNAME": names.netbiosname,
1193 samdb.set_session_info(admin_session_info)
1195 if dns_backend != "SAMBA_INTERNAL":
1196 # This is Samba4 specific and should be replaced by the correct
1197 # DNS AD-style setup
1198 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1199 "DNSDOMAIN": names.dnsdomain,
1200 "DOMAINDN": names.domaindn,
1201 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1202 "HOSTNAME" : names.hostname,
1203 "DNSNAME" : '%s.%s' % (
1204 names.netbiosname.lower(), names.dnsdomain.lower())
1208 def getpolicypath(sysvolpath, dnsdomain, guid):
1209 """Return the physical path of policy given its guid.
1211 :param sysvolpath: Path to the sysvol folder
1212 :param dnsdomain: DNS name of the AD domain
1213 :param guid: The GUID of the policy
1214 :return: A string with the complete path to the policy folder
1217 guid = "{%s}" % guid
1218 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1222 def create_gpo_struct(policy_path):
1223 if not os.path.exists(policy_path):
1224 os.makedirs(policy_path, 0o775)
1225 f = open(os.path.join(policy_path, "GPT.INI"), 'w')
1227 f.write("[General]\r\nVersion=0")
1230 p = os.path.join(policy_path, "MACHINE")
1231 if not os.path.exists(p):
1232 os.makedirs(p, 0o775)
1233 p = os.path.join(policy_path, "USER")
1234 if not os.path.exists(p):
1235 os.makedirs(p, 0o775)
1238 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1239 """Create the default GPO for a domain
1241 :param sysvolpath: Physical path for the sysvol folder
1242 :param dnsdomain: DNS domain name of the AD domain
1243 :param policyguid: GUID of the default domain policy
1244 :param policyguid_dc: GUID of the default domain controler policy
1246 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1247 create_gpo_struct(policy_path)
1249 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1250 create_gpo_struct(policy_path)
1253 def setup_samdb(path, session_info, provision_backend, lp, names,
1254 logger, fill, serverrole, schema, am_rodc=False,
1255 plaintext_secrets=False, backend_store=None):
1256 """Setup a complete SAM Database.
1258 :note: This will wipe the main SAM database file!
1261 # Also wipes the database
1262 setup_samdb_partitions(path, logger=logger, lp=lp,
1263 provision_backend=provision_backend, session_info=session_info,
1264 names=names, serverrole=serverrole, plaintext_secrets=plaintext_secrets,
1265 backend_store=backend_store)
1267 # Load the database, but don's load the global schema and don't connect
1269 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1270 credentials=provision_backend.credentials, lp=lp,
1271 global_schema=False, am_rodc=am_rodc)
1273 logger.info("Pre-loading the Samba 4 and AD schema")
1275 # Load the schema from the one we computed earlier
1276 samdb.set_schema(schema, write_indices_and_attributes=False)
1278 # Set the NTDS settings DN manually - in order to have it already around
1279 # before the provisioned tree exists and we connect
1280 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1282 # And now we can connect to the DB - the schema won't be loaded from the
1286 except ldb.LdbError as e2:
1287 (num, string_error) = e2.args
1288 if (num == ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS):
1289 raise ProvisioningError("Permission denied connecting to %s, are you running as root?" % path)
1293 # But we have to give it one more kick to have it use the schema
1294 # during provision - it needs, now that it is connected, to write
1295 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1296 samdb.set_schema(schema, write_indices_and_attributes=True)
1301 def fill_samdb(samdb, lp, names, logger, policyguid,
1302 policyguid_dc, fill, adminpass, krbtgtpass, machinepass, dns_backend,
1303 dnspass, invocationid, ntdsguid, serverrole, am_rodc=False,
1304 dom_for_fun_level=None, schema=None, next_rid=None, dc_rid=None,
1305 backend_store=None):
1307 if next_rid is None:
1310 # Provision does not make much sense values larger than 1000000000
1311 # as the upper range of the rIDAvailablePool is 1073741823 and
1312 # we don't want to create a domain that cannot allocate rids.
1313 if next_rid < 1000 or next_rid > 1000000000:
1314 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1315 error += "the valid range is %u-%u. The default is %u." % (
1316 1000, 1000000000, 1000)
1317 raise ProvisioningError(error)
1319 # ATTENTION: Do NOT change these default values without discussion with the
1320 # team and/or release manager. They have a big impact on the whole program!
1321 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1323 if dom_for_fun_level is None:
1324 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
1326 if dom_for_fun_level > domainControllerFunctionality:
1327 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!")
1329 domainFunctionality = dom_for_fun_level
1330 forestFunctionality = dom_for_fun_level
1332 # Set the NTDS settings DN manually - in order to have it already around
1333 # before the provisioned tree exists and we connect
1334 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1336 # Set the domain functionality levels onto the database.
1337 # Various module (the password_hash module in particular) need
1338 # to know what level of AD we are emulating.
1340 # These will be fixed into the database via the database
1341 # modifictions below, but we need them set from the start.
1342 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1343 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1344 samdb.set_opaque_integer("domainControllerFunctionality",
1345 domainControllerFunctionality)
1347 samdb.set_domain_sid(str(names.domainsid))
1348 samdb.set_invocation_id(invocationid)
1350 logger.info("Adding DomainDN: %s" % names.domaindn)
1352 # impersonate domain admin
1353 admin_session_info = admin_session(lp, str(names.domainsid))
1354 samdb.set_session_info(admin_session_info)
1355 if names.domainguid is not None:
1356 domainguid_line = "objectGUID: %s\n-" % names.domainguid
1358 domainguid_line = ""
1360 descr = b64encode(get_domain_descriptor(names.domainsid))
1361 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1362 "DOMAINDN": names.domaindn,
1363 "DOMAINSID": str(names.domainsid),
1364 "DESCRIPTOR": descr,
1365 "DOMAINGUID": domainguid_line
1368 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1369 "DOMAINDN": names.domaindn,
1370 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1371 "NEXTRID": str(next_rid),
1372 "DEFAULTSITE": names.sitename,
1373 "CONFIGDN": names.configdn,
1374 "POLICYGUID": policyguid,
1375 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1376 "SAMBA_VERSION_STRING": version,
1377 "MIN_PWD_LENGTH": str(DEFAULT_MIN_PWD_LENGTH)
1380 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1381 if fill == FILL_FULL:
1382 logger.info("Adding configuration container")
1383 descr = b64encode(get_config_descriptor(names.domainsid))
1384 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1385 "CONFIGDN": names.configdn,
1386 "DESCRIPTOR": descr,
1389 # The LDIF here was created when the Schema object was constructed
1390 ignore_checks_oid = "local_oid:%s:0" % samba.dsdb.DSDB_CONTROL_SKIP_DUPLICATES_CHECK_OID
1391 logger.info("Setting up sam.ldb schema")
1392 samdb.add_ldif(schema.schema_dn_add,
1393 controls=["relax:0", ignore_checks_oid])
1394 samdb.modify_ldif(schema.schema_dn_modify,
1395 controls=[ignore_checks_oid])
1396 samdb.write_prefixes_from_schema()
1397 samdb.add_ldif(schema.schema_data, controls=["relax:0", ignore_checks_oid])
1398 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1399 {"SCHEMADN": names.schemadn},
1400 controls=["relax:0", ignore_checks_oid])
1402 # Now register this container in the root of the forest
1403 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1404 msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1407 samdb.invocation_id = invocationid
1409 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1410 if fill == FILL_FULL:
1411 logger.info("Setting up sam.ldb configuration data")
1413 partitions_descr = b64encode(get_config_partitions_descriptor(names.domainsid))
1414 sites_descr = b64encode(get_config_sites_descriptor(names.domainsid))
1415 ntdsquotas_descr = b64encode(get_config_ntds_quotas_descriptor(names.domainsid))
1416 protected1_descr = b64encode(get_config_delete_protected1_descriptor(names.domainsid))
1417 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid))
1418 protected2_descr = b64encode(get_config_delete_protected2_descriptor(names.domainsid))
1420 if "2008" in schema.base_schema:
1421 # exclude 2012-specific changes if we're using a 2008 schema
1426 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1427 "CONFIGDN": names.configdn,
1428 "NETBIOSNAME": names.netbiosname,
1429 "DEFAULTSITE": names.sitename,
1430 "DNSDOMAIN": names.dnsdomain,
1431 "DOMAIN": names.domain,
1432 "SCHEMADN": names.schemadn,
1433 "DOMAINDN": names.domaindn,
1434 "SERVERDN": names.serverdn,
1435 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1436 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1437 "NTDSQUOTAS_DESCRIPTOR": ntdsquotas_descr,
1438 "LOSTANDFOUND_DESCRIPTOR": protected1wd_descr,
1439 "SERVICES_DESCRIPTOR": protected1_descr,
1440 "PHYSICALLOCATIONS_DESCRIPTOR": protected1wd_descr,
1441 "FORESTUPDATES_DESCRIPTOR": protected1wd_descr,
1442 "EXTENDEDRIGHTS_DESCRIPTOR": protected2_descr,
1443 "PARTITIONS_DESCRIPTOR": partitions_descr,
1444 "SITES_DESCRIPTOR": sites_descr,
1447 setup_add_ldif(samdb, setup_path("extended-rights.ldif"), {
1448 "CONFIGDN": names.configdn,
1449 "INC2012" : incl_2012,
1452 logger.info("Setting up display specifiers")
1453 display_specifiers_ldif = read_ms_ldif(
1454 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1455 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1456 {"CONFIGDN": names.configdn})
1457 check_all_substituted(display_specifiers_ldif)
1458 samdb.add_ldif(display_specifiers_ldif)
1460 logger.info("Modifying display specifiers and extended rights")
1461 setup_modify_ldif(samdb,
1462 setup_path("provision_configuration_modify.ldif"), {
1463 "CONFIGDN": names.configdn,
1464 "DISPLAYSPECIFIERS_DESCRIPTOR": protected2_descr
1467 logger.info("Adding users container")
1468 users_desc = b64encode(get_domain_users_descriptor(names.domainsid))
1469 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1470 "DOMAINDN": names.domaindn,
1471 "USERS_DESCRIPTOR": users_desc
1473 logger.info("Modifying users container")
1474 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1475 "DOMAINDN": names.domaindn})
1476 logger.info("Adding computers container")
1477 computers_desc = b64encode(get_domain_computers_descriptor(names.domainsid))
1478 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1479 "DOMAINDN": names.domaindn,
1480 "COMPUTERS_DESCRIPTOR": computers_desc
1482 logger.info("Modifying computers container")
1483 setup_modify_ldif(samdb,
1484 setup_path("provision_computers_modify.ldif"), {
1485 "DOMAINDN": names.domaindn})
1486 logger.info("Setting up sam.ldb data")
1487 infrastructure_desc = b64encode(get_domain_infrastructure_descriptor(names.domainsid))
1488 lostandfound_desc = b64encode(get_domain_delete_protected2_descriptor(names.domainsid))
1489 system_desc = b64encode(get_domain_delete_protected1_descriptor(names.domainsid))
1490 builtin_desc = b64encode(get_domain_builtin_descriptor(names.domainsid))
1491 controllers_desc = b64encode(get_domain_controllers_descriptor(names.domainsid))
1492 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1493 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1494 "DOMAINDN": names.domaindn,
1495 "NETBIOSNAME": names.netbiosname,
1496 "DEFAULTSITE": names.sitename,
1497 "CONFIGDN": names.configdn,
1498 "SERVERDN": names.serverdn,
1499 "RIDAVAILABLESTART": str(next_rid + 600),
1500 "POLICYGUID_DC": policyguid_dc,
1501 "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc,
1502 "LOSTANDFOUND_DESCRIPTOR": lostandfound_desc,
1503 "SYSTEM_DESCRIPTOR": system_desc,
1504 "BUILTIN_DESCRIPTOR": builtin_desc,
1505 "DOMAIN_CONTROLLERS_DESCRIPTOR": controllers_desc,
1508 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1509 if fill == FILL_FULL:
1510 managedservice_descr = b64encode(get_managed_service_accounts_descriptor(names.domainsid))
1511 setup_modify_ldif(samdb,
1512 setup_path("provision_configuration_references.ldif"), {
1513 "CONFIGDN": names.configdn,
1514 "SCHEMADN": names.schemadn})
1516 logger.info("Setting up well known security principals")
1517 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid))
1518 setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1519 "CONFIGDN": names.configdn,
1520 "WELLKNOWNPRINCIPALS_DESCRIPTOR": protected1wd_descr,
1521 }, controls=["relax:0", "provision:0"])
1523 if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1524 setup_modify_ldif(samdb,
1525 setup_path("provision_basedn_references.ldif"), {
1526 "DOMAINDN": names.domaindn,
1527 "MANAGEDSERVICE_DESCRIPTOR": managedservice_descr
1530 logger.info("Setting up sam.ldb users and groups")
1531 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1532 "DOMAINDN": names.domaindn,
1533 "DOMAINSID": str(names.domainsid),
1534 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1535 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1536 }, controls=["relax:0", "provision:0"])
1538 logger.info("Setting up self join")
1539 setup_self_join(samdb, admin_session_info, names=names, fill=fill,
1540 invocationid=invocationid,
1541 dns_backend=dns_backend,
1543 machinepass=machinepass,
1544 domainsid=names.domainsid,
1547 policyguid=policyguid,
1548 policyguid_dc=policyguid_dc,
1549 domainControllerFunctionality=domainControllerFunctionality,
1552 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1553 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1554 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1555 assert isinstance(names.ntdsguid, str)
1560 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1561 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)"
1562 SYSVOL_SERVICE="sysvol"
1564 def set_dir_acl(path, acl, lp, domsid, use_ntvfs, passdb, service=SYSVOL_SERVICE):
1565 setntacl(lp, path, acl, domsid, use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1566 for root, dirs, files in os.walk(path, topdown=False):
1568 setntacl(lp, os.path.join(root, name), acl, domsid,
1569 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1571 setntacl(lp, os.path.join(root, name), acl, domsid,
1572 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1575 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb):
1576 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1579 :param sysvol: Physical path for the sysvol folder
1580 :param dnsdomain: The DNS name of the domain
1581 :param domainsid: The SID of the domain
1582 :param domaindn: The DN of the domain (ie. DC=...)
1583 :param samdb: An LDB object on the SAM db
1584 :param lp: an LP object
1587 # Set ACL for GPO root folder
1588 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1589 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid),
1590 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=SYSVOL_SERVICE)
1592 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1593 attrs=["cn", "nTSecurityDescriptor"],
1594 expression="", scope=ldb.SCOPE_ONELEVEL)
1597 acl = ndr_unpack(security.descriptor,
1598 str(policy["nTSecurityDescriptor"])).as_sddl()
1599 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1600 set_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1601 str(domainsid), use_ntvfs,
1605 def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain,
1606 domaindn, lp, use_ntvfs):
1607 """Set the ACL for the sysvol share and the subfolders
1609 :param samdb: An LDB object on the SAM db
1610 :param netlogon: Physical path for the netlogon folder
1611 :param sysvol: Physical path for the sysvol folder
1612 :param uid: The UID of the "Administrator" user
1613 :param gid: The GID of the "Domain adminstrators" group
1614 :param domainsid: The SID of the domain
1615 :param dnsdomain: The DNS name of the domain
1616 :param domaindn: The DN of the domain (ie. DC=...)
1621 s3conf = s3param.get_context()
1622 s3conf.load(lp.configfile)
1624 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(sysvol))
1627 smbd.set_simple_acl(file.name, 0o755, gid)
1629 if not smbd.have_posix_acls():
1630 # This clue is only strictly correct for RPM and
1631 # Debian-like Linux systems, but hopefully other users
1632 # will get enough clue from it.
1633 raise ProvisioningError("Samba was compiled without the posix ACL support that s3fs requires. "
1634 "Try installing libacl1-dev or libacl-devel, then re-run configure and make.")
1636 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. "
1637 "Try the mounting the filesystem with the 'acl' option.")
1639 smbd.chown(file.name, uid, gid)
1641 raise ProvisioningError("Unable to chown a file on your filesystem. "
1642 "You may not be running provision as root.")
1646 # This will ensure that the smbd code we are running when setting ACLs
1647 # is initialised with the smb.conf
1648 s3conf = s3param.get_context()
1649 s3conf.load(lp.configfile)
1650 # ensure we are using the right samba_dsdb passdb backend, no matter what
1651 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1652 passdb.reload_static_pdb()
1654 # ensure that we init the samba_dsdb backend, so the domain sid is
1655 # marked in secrets.tdb
1656 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1658 # now ensure everything matches correctly, to avoid wierd issues
1659 if passdb.get_global_sam_sid() != domainsid:
1660 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))
1662 domain_info = s4_passdb.domain_info()
1663 if domain_info["dom_sid"] != domainsid:
1664 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))
1666 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1667 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()))
1672 os.chown(sysvol, -1, gid)
1678 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1679 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs,
1680 skip_invalid_chown=True, passdb=s4_passdb,
1681 service=SYSVOL_SERVICE)
1682 for root, dirs, files in os.walk(sysvol, topdown=False):
1684 if use_ntvfs and canchown:
1685 os.chown(os.path.join(root, name), -1, gid)
1686 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid),
1687 use_ntvfs=use_ntvfs, skip_invalid_chown=True,
1688 passdb=s4_passdb, service=SYSVOL_SERVICE)
1690 if use_ntvfs and canchown:
1691 os.chown(os.path.join(root, name), -1, gid)
1692 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid),
1693 use_ntvfs=use_ntvfs, skip_invalid_chown=True,
1694 passdb=s4_passdb, service=SYSVOL_SERVICE)
1696 # Set acls on Policy folder and policies folders
1697 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb=s4_passdb)
1699 def acl_type(direct_db_access):
1700 if direct_db_access:
1705 def check_dir_acl(path, acl, lp, domainsid, direct_db_access):
1706 fsacl = getntacl(lp, path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1707 fsacl_sddl = fsacl.as_sddl(domainsid)
1708 if fsacl_sddl != acl:
1709 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))
1711 for root, dirs, files in os.walk(path, topdown=False):
1713 fsacl = getntacl(lp, os.path.join(root, name),
1714 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1716 raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1717 fsacl_sddl = fsacl.as_sddl(domainsid)
1718 if fsacl_sddl != acl:
1719 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))
1722 fsacl = getntacl(lp, os.path.join(root, name),
1723 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1725 raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1726 fsacl_sddl = fsacl.as_sddl(domainsid)
1727 if fsacl_sddl != acl:
1728 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))
1731 def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1733 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1736 :param sysvol: Physical path for the sysvol folder
1737 :param dnsdomain: The DNS name of the domain
1738 :param domainsid: The SID of the domain
1739 :param domaindn: The DN of the domain (ie. DC=...)
1740 :param samdb: An LDB object on the SAM db
1741 :param lp: an LP object
1744 # Set ACL for GPO root folder
1745 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1746 fsacl = getntacl(lp, root_policy_path,
1747 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1749 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access), root_policy_path))
1750 fsacl_sddl = fsacl.as_sddl(domainsid)
1751 if fsacl_sddl != POLICIES_ACL:
1752 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))
1753 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1754 attrs=["cn", "nTSecurityDescriptor"],
1755 expression="", scope=ldb.SCOPE_ONELEVEL)
1758 acl = ndr_unpack(security.descriptor,
1759 str(policy["nTSecurityDescriptor"])).as_sddl()
1760 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1761 check_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1762 domainsid, direct_db_access)
1765 def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn,
1767 """Set the ACL for the sysvol share and the subfolders
1769 :param samdb: An LDB object on the SAM db
1770 :param netlogon: Physical path for the netlogon folder
1771 :param sysvol: Physical path for the sysvol folder
1772 :param uid: The UID of the "Administrator" user
1773 :param gid: The GID of the "Domain adminstrators" group
1774 :param domainsid: The SID of the domain
1775 :param dnsdomain: The DNS name of the domain
1776 :param domaindn: The DN of the domain (ie. DC=...)
1779 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1780 s3conf = s3param.get_context()
1781 s3conf.load(lp.configfile)
1782 # ensure we are using the right samba_dsdb passdb backend, no matter what
1783 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1784 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1785 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1787 # now ensure everything matches correctly, to avoid wierd issues
1788 if passdb.get_global_sam_sid() != domainsid:
1789 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))
1791 domain_info = s4_passdb.domain_info()
1792 if domain_info["dom_sid"] != domainsid:
1793 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))
1795 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1796 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()))
1798 # Ensure we can read this directly, and via the smbd VFS
1799 for direct_db_access in [True, False]:
1800 # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1801 for dir_path in [os.path.join(sysvol, dnsdomain), netlogon]:
1802 fsacl = getntacl(lp, dir_path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1804 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access), dir_path))
1805 fsacl_sddl = fsacl.as_sddl(domainsid)
1806 if fsacl_sddl != SYSVOL_ACL:
1807 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))
1809 # Check acls on Policy folder and policies folders
1810 check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1814 def interface_ips_v4(lp):
1815 """return only IPv4 IPs"""
1816 ips = samba.interface_ips(lp, False)
1819 if i.find(':') == -1:
1824 def interface_ips_v6(lp):
1825 """return only IPv6 IPs"""
1826 ips = samba.interface_ips(lp, False)
1829 if i.find(':') != -1:
1834 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1836 targetdir=None, samdb_fill=FILL_FULL,
1837 hostip=None, hostip6=None,
1838 next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1839 domainguid=None, policyguid=None, policyguid_dc=None,
1840 invocationid=None, machinepass=None, ntdsguid=None,
1841 dns_backend=None, dnspass=None,
1842 serverrole=None, dom_for_fun_level=None,
1843 am_rodc=False, lp=None, use_ntvfs=False,
1844 skip_sysvolacl=False, backend_store=None):
1845 # create/adapt the group policy GUIDs
1846 # Default GUID for default policy are described at
1847 # "How Core Group Policy Works"
1848 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1849 if policyguid is None:
1850 policyguid = DEFAULT_POLICY_GUID
1851 policyguid = policyguid.upper()
1852 if policyguid_dc is None:
1853 policyguid_dc = DEFAULT_DC_POLICY_GUID
1854 policyguid_dc = policyguid_dc.upper()
1856 if invocationid is None:
1857 invocationid = str(uuid.uuid4())
1859 if krbtgtpass is None:
1860 krbtgtpass = samba.generate_random_machine_password(128, 255)
1861 if machinepass is None:
1862 machinepass = samba.generate_random_machine_password(128, 255)
1864 dnspass = samba.generate_random_password(128, 255)
1866 samdb.transaction_start()
1868 samdb = fill_samdb(samdb, lp, names, logger=logger,
1870 policyguid=policyguid, policyguid_dc=policyguid_dc,
1871 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1872 invocationid=invocationid, machinepass=machinepass,
1873 dns_backend=dns_backend, dnspass=dnspass,
1874 ntdsguid=ntdsguid, serverrole=serverrole,
1875 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1876 next_rid=next_rid, dc_rid=dc_rid,
1877 backend_store=backend_store)
1879 # Set up group policies (domain policy and domain controller
1881 if serverrole == "active directory domain controller":
1882 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1885 samdb.transaction_cancel()
1888 samdb.transaction_commit()
1890 if serverrole == "active directory domain controller":
1891 # Continue setting up sysvol for GPO. This appears to require being
1892 # outside a transaction.
1893 if not skip_sysvolacl:
1894 setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.root_uid,
1895 paths.root_gid, names.domainsid, names.dnsdomain,
1896 names.domaindn, lp, use_ntvfs)
1898 logger.info("Setting acl on sysvol skipped")
1900 secretsdb_self_join(secrets_ldb, domain=names.domain,
1901 realm=names.realm, dnsdomain=names.dnsdomain,
1902 netbiosname=names.netbiosname, domainsid=names.domainsid,
1903 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1905 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1906 # In future, this might be determined from some configuration
1907 kerberos_enctypes = str(ENC_ALL_TYPES)
1910 msg = ldb.Message(ldb.Dn(samdb,
1911 samdb.searchone("distinguishedName",
1912 expression="samAccountName=%s$" % names.netbiosname,
1913 scope=ldb.SCOPE_SUBTREE)))
1914 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1915 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1916 name="msDS-SupportedEncryptionTypes")
1918 except ldb.LdbError as e:
1919 (enum, estr) = e.args
1920 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1921 # It might be that this attribute does not exist in this schema
1924 setup_ad_dns(samdb, secrets_ldb, names, paths, lp, logger,
1925 hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
1926 dnspass=dnspass, os_level=dom_for_fun_level,
1927 targetdir=targetdir, fill_level=samdb_fill,
1928 backend_store=backend_store)
1930 domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1931 attribute="objectGUID")
1932 assert isinstance(domainguid, str)
1934 lastProvisionUSNs = get_last_provision_usn(samdb)
1935 maxUSN = get_max_usn(samdb, str(names.rootdn))
1936 if lastProvisionUSNs is not None:
1937 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1939 set_provision_usn(samdb, 0, maxUSN, invocationid)
1941 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1942 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1943 { 'NTDSGUID' : names.ntdsguid })
1945 # fix any dangling GUIDs from the provision
1946 logger.info("Fixing provision GUIDs")
1947 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
1949 samdb.transaction_start()
1951 # a small number of GUIDs are missing because of ordering issues in the
1953 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1954 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1955 scope=ldb.SCOPE_BASE,
1956 attrs=['defaultObjectCategory'])
1957 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1958 scope=ldb.SCOPE_ONELEVEL,
1959 attrs=['ipsecOwnersReference',
1960 'ipsecFilterReference',
1961 'ipsecISAKMPReference',
1962 'ipsecNegotiationPolicyReference',
1963 'ipsecNFAReference'])
1964 if chk.check_database(DN=names.schemadn, scope=ldb.SCOPE_SUBTREE,
1965 attrs=['attributeId', 'governsId']) != 0:
1966 raise ProvisioningError("Duplicate attributeId or governsId in schema. Must be fixed manually!!")
1968 samdb.transaction_cancel()
1971 samdb.transaction_commit()
1975 "ROLE_STANDALONE": "standalone server",
1976 "ROLE_DOMAIN_MEMBER": "member server",
1977 "ROLE_DOMAIN_BDC": "active directory domain controller",
1978 "ROLE_DOMAIN_PDC": "active directory domain controller",
1979 "dc": "active directory domain controller",
1980 "member": "member server",
1981 "domain controller": "active directory domain controller",
1982 "active directory domain controller": "active directory domain controller",
1983 "member server": "member server",
1984 "standalone": "standalone server",
1985 "standalone server": "standalone server",
1989 def sanitize_server_role(role):
1990 """Sanitize a server role name.
1992 :param role: Server role
1993 :raise ValueError: If the role can not be interpreted
1994 :return: Sanitized server role (one of "member server",
1995 "active directory domain controller", "standalone server")
1998 return _ROLES_MAP[role]
2000 raise ValueError(role)
2003 def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain,
2005 """Create AD entries for the fake ypserver.
2007 This is needed for being able to manipulate posix attrs via ADUC.
2009 samdb.transaction_start()
2011 logger.info("Setting up fake yp server settings")
2012 setup_add_ldif(samdb, setup_path("ypServ30.ldif"), {
2013 "DOMAINDN": domaindn,
2014 "NETBIOSNAME": netbiosname,
2015 "NISDOMAIN": nisdomain,
2018 samdb.transaction_cancel()
2021 samdb.transaction_commit()
2023 def directory_create_or_exists(path, mode=0o755):
2024 if not os.path.exists(path):
2026 os.mkdir(path, mode)
2027 except OSError as e:
2028 if e.errno in [errno.EEXIST]:
2031 raise ProvisioningError("Failed to create directory %s: %s" % (path, e.strerror))
2033 def provision(logger, session_info, smbconf=None,
2034 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
2035 domaindn=None, schemadn=None, configdn=None, serverdn=None,
2036 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
2037 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None,
2038 krbtgtpass=None, domainguid=None, policyguid=None, policyguid_dc=None,
2039 dns_backend=None, dns_forwarder=None, dnspass=None,
2040 invocationid=None, machinepass=None, ntdsguid=None,
2041 root=None, nobody=None, users=None, backup=None, aci=None,
2042 serverrole=None, dom_for_fun_level=None, backend_type=None,
2043 sitename=None, ol_mmr_urls=None, ol_olc=None, slapd_path=None,
2044 useeadb=False, am_rodc=False, lp=None, use_ntvfs=False,
2045 use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True,
2046 ldap_backend_forced_uri=None, nosync=False, ldap_dryrun_mode=False,
2047 ldap_backend_extra_port=None, base_schema=None,
2048 plaintext_secrets=False, backend_store=None):
2051 :note: caution, this wipes all existing data!
2055 serverrole = sanitize_server_role(serverrole)
2057 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole)
2059 if ldapadminpass is None:
2060 # Make a new, random password between Samba and it's LDAP server
2061 ldapadminpass = samba.generate_random_password(128, 255)
2063 if backend_type is None:
2064 backend_type = "ldb"
2065 if backend_store is None:
2066 backend_store = get_default_backend_store()
2068 if domainsid is None:
2069 domainsid = security.random_sid()
2071 root_uid = findnss_uid([root or "root"])
2072 nobody_uid = findnss_uid([nobody or "nobody"])
2073 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
2074 root_gid = pwd.getpwuid(root_uid).pw_gid
2077 bind_gid = findnss_gid(["bind", "named"])
2081 if targetdir is not None:
2082 smbconf = os.path.join(targetdir, "etc", "smb.conf")
2083 elif smbconf is None:
2084 smbconf = samba.param.default_path()
2085 if not os.path.exists(os.path.dirname(smbconf)):
2086 os.makedirs(os.path.dirname(smbconf))
2088 server_services = []
2091 global_param["idmap_ldb:use rfc2307"] = ["yes"]
2093 if dns_backend != "SAMBA_INTERNAL":
2094 server_services.append("-dns")
2096 if dns_forwarder is not None:
2097 global_param["dns forwarder"] = [dns_forwarder]
2100 server_services.append("+smb")
2101 server_services.append("-s3fs")
2102 global_param["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
2104 if len(server_services) > 0:
2105 global_param["server services"] = server_services
2107 # only install a new smb.conf if there isn't one there already
2108 if os.path.exists(smbconf):
2109 # if Samba Team members can't figure out the weird errors
2110 # loading an empty smb.conf gives, then we need to be smarter.
2111 # Pretend it just didn't exist --abartlet
2112 f = open(smbconf, 'r')
2114 data = f.read().lstrip()
2117 if data is None or data == "":
2118 make_smbconf(smbconf, hostname, domain, realm,
2119 targetdir, serverrole=serverrole,
2120 eadb=useeadb, use_ntvfs=use_ntvfs,
2121 lp=lp, global_param=global_param)
2123 make_smbconf(smbconf, hostname, domain, realm, targetdir,
2124 serverrole=serverrole,
2125 eadb=useeadb, use_ntvfs=use_ntvfs, lp=lp, global_param=global_param)
2128 lp = samba.param.LoadParm()
2130 names = guess_names(lp=lp, hostname=hostname, domain=domain,
2131 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
2132 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
2133 sitename=sitename, rootdn=rootdn, domain_names_forced=(samdb_fill == FILL_DRS))
2134 paths = provision_paths_from_lp(lp, names.dnsdomain)
2136 paths.bind_gid = bind_gid
2137 paths.root_uid = root_uid;
2138 paths.root_gid = root_gid
2141 logger.info("Looking up IPv4 addresses")
2142 hostips = interface_ips_v4(lp)
2143 if len(hostips) > 0:
2145 if len(hostips) > 1:
2146 logger.warning("More than one IPv4 address found. Using %s",
2148 if hostip == "127.0.0.1":
2151 logger.warning("No IPv4 address will be assigned")
2154 logger.info("Looking up IPv6 addresses")
2155 hostips = interface_ips_v6(lp)
2157 hostip6 = hostips[0]
2158 if len(hostips) > 1:
2159 logger.warning("More than one IPv6 address found. Using %s", hostip6)
2161 logger.warning("No IPv6 address will be assigned")
2163 names.hostip = hostip
2164 names.hostip6 = hostip6
2165 names.domainguid = domainguid
2166 names.domainsid = domainsid
2167 names.forestsid = domainsid
2169 if serverrole is None:
2170 serverrole = lp.get("server role")
2172 directory_create_or_exists(paths.private_dir, 0o700)
2173 directory_create_or_exists(paths.binddns_dir, 0o770)
2174 directory_create_or_exists(os.path.join(paths.private_dir, "tls"))
2175 directory_create_or_exists(paths.state_dir)
2176 if not plaintext_secrets:
2177 setup_encrypted_secrets_key(paths.encrypted_secrets_key_path)
2179 if paths.sysvol and not os.path.exists(paths.sysvol):
2180 os.makedirs(paths.sysvol, 0o775)
2182 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
2184 schema = Schema(domainsid, invocationid=invocationid,
2185 schemadn=names.schemadn, base_schema=base_schema)
2187 if backend_type == "ldb":
2188 provision_backend = LDBBackend(backend_type, paths=paths,
2190 names=names, logger=logger)
2191 elif backend_type == "existing":
2192 # If support for this is ever added back, then the URI will need to be
2194 provision_backend = ExistingBackend(backend_type, paths=paths,
2196 names=names, logger=logger,
2197 ldap_backend_forced_uri=ldap_backend_forced_uri)
2198 elif backend_type == "fedora-ds":
2199 provision_backend = FDSBackend(backend_type, paths=paths,
2201 names=names, logger=logger, domainsid=domainsid,
2202 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2203 slapd_path=slapd_path,
2205 elif backend_type == "openldap":
2206 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
2208 names=names, logger=logger, domainsid=domainsid,
2209 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2210 slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls,
2211 ldap_backend_extra_port=ldap_backend_extra_port,
2212 ldap_dryrun_mode=ldap_dryrun_mode, nosync=nosync,
2213 ldap_backend_forced_uri=ldap_backend_forced_uri)
2215 raise ValueError("Unknown LDAP backend type selected")
2217 provision_backend.init()
2218 provision_backend.start()
2220 # only install a new shares config db if there is none
2221 if not os.path.exists(paths.shareconf):
2222 logger.info("Setting up share.ldb")
2223 share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
2224 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
2226 logger.info("Setting up secrets.ldb")
2227 secrets_ldb = setup_secretsdb(paths,
2228 session_info=session_info,
2229 backend_credentials=provision_backend.credentials, lp=lp)
2232 logger.info("Setting up the registry")
2233 setup_registry(paths.hklm, session_info, lp=lp)
2235 logger.info("Setting up the privileges database")
2236 setup_privileges(paths.privilege, session_info, lp=lp)
2238 logger.info("Setting up idmap db")
2239 idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
2241 setup_name_mappings(idmap, sid=str(domainsid),
2242 root_uid=root_uid, nobody_uid=nobody_uid,
2243 users_gid=users_gid, root_gid=root_gid)
2245 logger.info("Setting up SAM db")
2246 samdb = setup_samdb(paths.samdb, session_info,
2247 provision_backend, lp, names, logger=logger,
2248 serverrole=serverrole,
2249 schema=schema, fill=samdb_fill, am_rodc=am_rodc,
2250 plaintext_secrets=plaintext_secrets,
2251 backend_store=backend_store)
2253 if serverrole == "active directory domain controller":
2254 if paths.netlogon is None:
2255 raise MissingShareError("netlogon", paths.smbconf)
2257 if paths.sysvol is None:
2258 raise MissingShareError("sysvol", paths.smbconf)
2260 if not os.path.isdir(paths.netlogon):
2261 os.makedirs(paths.netlogon, 0o755)
2263 if adminpass is None:
2264 adminpass = samba.generate_random_password(12, 32)
2265 adminpass_generated = True
2267 adminpass = unicode(adminpass, 'utf-8')
2268 adminpass_generated = False
2270 if samdb_fill == FILL_FULL:
2271 provision_fill(samdb, secrets_ldb, logger, names, paths,
2272 schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
2273 hostip=hostip, hostip6=hostip6,
2274 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
2275 krbtgtpass=krbtgtpass,
2276 policyguid=policyguid, policyguid_dc=policyguid_dc,
2277 invocationid=invocationid, machinepass=machinepass,
2278 ntdsguid=ntdsguid, dns_backend=dns_backend,
2279 dnspass=dnspass, serverrole=serverrole,
2280 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
2281 lp=lp, use_ntvfs=use_ntvfs,
2282 skip_sysvolacl=skip_sysvolacl,
2283 backend_store=backend_store)
2285 if not is_heimdal_built():
2286 create_kdc_conf(paths.kdcconf, realm, domain, os.path.dirname(lp.get("log file")))
2287 logger.info("The Kerberos KDC configuration for Samba AD is "
2288 "located at %s", paths.kdcconf)
2290 create_krb5_conf(paths.krb5conf,
2291 dnsdomain=names.dnsdomain, hostname=names.hostname,
2293 logger.info("A Kerberos configuration suitable for Samba AD has been "
2294 "generated at %s", paths.krb5conf)
2295 logger.info("Merge the contents of this file with your system "
2296 "krb5.conf or replace it with this one. Do not create a "
2299 if serverrole == "active directory domain controller":
2300 create_dns_update_list(lp, logger, paths)
2302 backend_result = provision_backend.post_setup()
2303 provision_backend.shutdown()
2306 secrets_ldb.transaction_cancel()
2309 # Now commit the secrets.ldb to disk
2310 secrets_ldb.transaction_commit()
2312 # the commit creates the dns.keytab in the private directory
2313 private_dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
2314 bind_dns_keytab_path = os.path.join(paths.binddns_dir, paths.dns_keytab)
2316 if os.path.isfile(private_dns_keytab_path):
2317 if os.path.isfile(bind_dns_keytab_path):
2319 os.unlink(bind_dns_keytab_path)
2320 except OSError as e:
2321 logger.error("Failed to remove %s: %s" %
2322 (bind_dns_keytab_path, e.strerror))
2324 # link the dns.keytab to the bind-dns directory
2326 os.link(private_dns_keytab_path, bind_dns_keytab_path)
2327 except OSError as e:
2328 logger.error("Failed to create link %s -> %s: %s" %
2329 (private_dns_keytab_path, bind_dns_keytab_path, e.strerror))
2331 # chown the dns.keytab in the bind-dns directory
2332 if paths.bind_gid is not None:
2334 os.chmod(paths.binddns_dir, 0o770)
2335 os.chown(paths.binddns_dir, -1, paths.bind_gid)
2337 if not os.environ.has_key('SAMBA_SELFTEST'):
2338 logger.info("Failed to chown %s to bind gid %u",
2339 paths.binddns_dir, paths.bind_gid)
2342 os.chmod(bind_dns_keytab_path, 0o640)
2343 os.chown(bind_dns_keytab_path, -1, paths.bind_gid)
2345 if not os.environ.has_key('SAMBA_SELFTEST'):
2346 logger.info("Failed to chown %s to bind gid %u",
2347 bind_dns_keytab_path, paths.bind_gid)
2349 result = ProvisionResult()
2350 result.server_role = serverrole
2351 result.domaindn = domaindn
2352 result.paths = paths
2353 result.names = names
2355 result.samdb = samdb
2356 result.idmap = idmap
2357 result.domainsid = str(domainsid)
2359 if samdb_fill == FILL_FULL:
2360 result.adminpass_generated = adminpass_generated
2361 result.adminpass = adminpass
2363 result.adminpass_generated = False
2364 result.adminpass = None
2366 result.backend_result = backend_result
2369 provision_fake_ypserver(logger=logger, samdb=samdb,
2370 domaindn=names.domaindn, netbiosname=names.netbiosname,
2371 nisdomain=names.domain.lower(), maxuid=maxuid, maxgid=maxgid)
2376 def provision_become_dc(smbconf=None, targetdir=None,
2377 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
2378 serverdn=None, domain=None, hostname=None, domainsid=None,
2379 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
2380 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
2381 dns_backend=None, root=None, nobody=None, users=None,
2382 backup=None, serverrole=None, ldap_backend=None,
2383 ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False):
2385 logger = logging.getLogger("provision")
2386 samba.set_debug_level(debuglevel)
2388 res = provision(logger, system_session(),
2389 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
2390 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
2391 configdn=configdn, serverdn=serverdn, domain=domain,
2392 hostname=hostname, hostip=None, domainsid=domainsid,
2393 machinepass=machinepass,
2394 serverrole="active directory domain controller",
2395 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass,
2396 use_ntvfs=use_ntvfs)
2397 res.lp.set("debuglevel", str(debuglevel))
2401 def create_krb5_conf(path, dnsdomain, hostname, realm):
2402 """Write out a file containing a valid krb5.conf file
2404 :param path: Path of the new krb5.conf file.
2405 :param dnsdomain: DNS Domain name
2406 :param hostname: Local hostname
2407 :param realm: Realm name
2409 setup_file(setup_path("krb5.conf"), path, {
2410 "DNSDOMAIN": dnsdomain,
2411 "HOSTNAME": hostname,
2416 class ProvisioningError(Exception):
2417 """A generic provision error."""
2419 def __init__(self, value):
2423 return "ProvisioningError: " + self.value
2426 class InvalidNetbiosName(Exception):
2427 """A specified name was not a valid NetBIOS name."""
2429 def __init__(self, name):
2430 super(InvalidNetbiosName, self).__init__(
2431 "The name '%r' is not a valid NetBIOS name" % name)
2434 class MissingShareError(ProvisioningError):
2436 def __init__(self, name, smbconf):
2437 super(MissingShareError, self).__init__(
2438 "Existing smb.conf does not have a [%s] share, but you are "
2439 "configuring a DC. Please remove %s or add the share manually." %