1 # Unix SMB/CIFS implementation.
2 # backend code for provisioning a Samba AD server
4 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2012
5 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
6 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
8 # Based on the original in EJS:
9 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 3 of the License, or
14 # (at your option) any later version.
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 """Functions for setting up a Samba configuration."""
27 __docformat__ = "restructuredText"
29 from samba.compat import urllib_quote
30 from base64 import b64encode
47 from samba.auth import system_session, admin_session
49 from samba.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].decode('utf8')))):
224 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
225 "is not the same ..." % (paths.samdb,
226 str(current[0]["defaultNamingContext"][0].decode('utf8')),
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.items():
778 f.write("\t%s = %s\n" % (key, val))
781 for name, path in shares.items():
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 = None
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 if backend_store == "mdb":
857 if required_features is not None:
858 required_features += "\n"
860 required_features = ""
861 required_features += "requiredFeatures: lmdbLevelOne"
863 if required_features is None:
864 required_features = "# No required features"
866 samdb.transaction_start()
868 logger.info("Setting up sam.ldb partitions and settings")
869 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
870 "LDAP_BACKEND_LINE": ldap_backend_line,
871 "BACKEND_STORE": backend_store_line
875 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
876 "BACKEND_TYPE": provision_backend.type,
877 "SERVER_ROLE": serverrole,
878 "REQUIRED_FEATURES": required_features
881 logger.info("Setting up sam.ldb rootDSE")
882 setup_samdb_rootdse(samdb, names)
884 samdb.transaction_cancel()
887 samdb.transaction_commit()
890 def secretsdb_self_join(secretsdb, domain,
891 netbiosname, machinepass, domainsid=None,
892 realm=None, dnsdomain=None,
894 key_version_number=1,
895 secure_channel_type=SEC_CHAN_WKSTA):
896 """Add domain join-specific bits to a secrets database.
898 :param secretsdb: Ldb Handle to the secrets database
899 :param machinepass: Machine password
901 attrs = ["whenChanged",
908 if realm is not None:
909 if dnsdomain is None:
910 dnsdomain = realm.lower()
911 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
914 shortname = netbiosname.lower()
916 # We don't need to set msg["flatname"] here, because rdn_name will handle
917 # it, and it causes problems for modifies anyway
918 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
919 msg["secureChannelType"] = [str(secure_channel_type)]
920 msg["objectClass"] = ["top", "primaryDomain"]
921 if dnsname is not None:
922 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
923 msg["realm"] = [realm]
924 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
925 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
926 msg["privateKeytab"] = ["secrets.keytab"]
928 msg["secret"] = [machinepass.encode('utf-8')]
929 msg["samAccountName"] = ["%s$" % netbiosname]
930 msg["secureChannelType"] = [str(secure_channel_type)]
931 if domainsid is not None:
932 msg["objectSid"] = [ndr_pack(domainsid)]
934 # This complex expression tries to ensure that we don't have more
935 # than one record for this SID, realm or netbios domain at a time,
936 # but we don't delete the old record that we are about to modify,
937 # because that would delete the keytab and previous password.
938 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
939 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
940 scope=ldb.SCOPE_ONELEVEL)
943 secretsdb.delete(del_msg.dn)
945 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
948 msg["priorSecret"] = [res[0]["secret"][0]]
950 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
955 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
960 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
966 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
967 secretsdb.modify(msg)
968 secretsdb.rename(res[0].dn, msg.dn)
970 spn = [ 'HOST/%s' % shortname ]
971 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
972 # we are a domain controller then we add servicePrincipalName
973 # entries for the keytab code to update.
974 spn.extend([ 'HOST/%s' % dnsname ])
975 msg["servicePrincipalName"] = spn
980 def setup_secretsdb(paths, session_info, backend_credentials, lp):
981 """Setup the secrets database.
983 :note: This function does not handle exceptions and transaction on purpose,
984 it's up to the caller to do this job.
986 :param path: Path to the secrets 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(paths.secrets):
993 os.unlink(paths.secrets)
995 keytab_path = os.path.join(paths.private_dir, paths.keytab)
996 if os.path.exists(keytab_path):
997 os.unlink(keytab_path)
999 bind_dns_keytab_path = os.path.join(paths.binddns_dir, paths.dns_keytab)
1000 if os.path.exists(bind_dns_keytab_path):
1001 os.unlink(bind_dns_keytab_path)
1003 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1004 if os.path.exists(dns_keytab_path):
1005 os.unlink(dns_keytab_path)
1007 path = paths.secrets
1009 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
1011 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
1012 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
1013 secrets_ldb.transaction_start()
1015 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
1017 if (backend_credentials is not None and
1018 backend_credentials.authentication_requested()):
1019 if backend_credentials.get_bind_dn() is not None:
1020 setup_add_ldif(secrets_ldb,
1021 setup_path("secrets_simple_ldap.ldif"), {
1022 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
1023 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
1026 setup_add_ldif(secrets_ldb,
1027 setup_path("secrets_sasl_ldap.ldif"), {
1028 "LDAPADMINUSER": backend_credentials.get_username(),
1029 "LDAPADMINREALM": backend_credentials.get_realm(),
1030 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
1033 secrets_ldb.transaction_cancel()
1038 def setup_privileges(path, session_info, lp):
1039 """Setup the privileges database.
1041 :param path: Path to the privileges database.
1042 :param session_info: Session info.
1043 :param credentials: Credentials
1044 :param lp: Loadparm context
1045 :return: LDB handle for the created secrets database
1047 if os.path.exists(path):
1049 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
1050 privilege_ldb.erase()
1051 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
1053 def setup_encrypted_secrets_key(path):
1054 """Setup the encrypted secrets key file.
1056 Any existing key file will be deleted and a new random key generated.
1058 :param path: Path to the secrets key file.
1061 if os.path.exists(path):
1064 flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
1065 mode = stat.S_IRUSR | stat.S_IWUSR
1067 umask_original = os.umask(0)
1069 fd = os.open(path, flags, mode)
1071 os.umask(umask_original)
1073 with os.fdopen(fd, 'w') as f:
1074 key = samba.generate_random_bytes(16)
1078 def setup_registry(path, session_info, lp):
1079 """Setup the registry.
1081 :param path: Path to the registry database
1082 :param session_info: Session information
1083 :param credentials: Credentials
1084 :param lp: Loadparm context
1086 reg = samba.registry.Registry()
1087 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
1088 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
1089 provision_reg = setup_path("provision.reg")
1090 assert os.path.exists(provision_reg)
1091 reg.diff_apply(provision_reg)
1094 def setup_idmapdb(path, session_info, lp):
1095 """Setup the idmap database.
1097 :param path: path to the idmap database
1098 :param session_info: Session information
1099 :param credentials: Credentials
1100 :param lp: Loadparm context
1102 if os.path.exists(path):
1105 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
1107 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
1111 def setup_samdb_rootdse(samdb, names):
1112 """Setup the SamDB rootdse.
1114 :param samdb: Sam Database handle
1116 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
1117 "SCHEMADN": names.schemadn,
1118 "DOMAINDN": names.domaindn,
1119 "ROOTDN" : names.rootdn,
1120 "CONFIGDN": names.configdn,
1121 "SERVERDN": names.serverdn,
1125 def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
1126 dns_backend, dnspass, domainsid, next_rid, invocationid,
1127 policyguid, policyguid_dc,
1128 domainControllerFunctionality, ntdsguid=None, dc_rid=None):
1129 """Join a host to its own domain."""
1130 assert isinstance(invocationid, str)
1131 if ntdsguid is not None:
1132 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
1139 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
1140 "CONFIGDN": names.configdn,
1141 "SCHEMADN": names.schemadn,
1142 "DOMAINDN": names.domaindn,
1143 "SERVERDN": names.serverdn,
1144 "INVOCATIONID": invocationid,
1145 "NETBIOSNAME": names.netbiosname,
1146 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1147 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1148 "DOMAINSID": str(domainsid),
1149 "DCRID": str(dc_rid),
1150 "SAMBA_VERSION_STRING": version,
1151 "NTDSGUID": ntdsguid_line,
1152 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1153 domainControllerFunctionality),
1154 "RIDALLOCATIONSTART": str(next_rid + 100),
1155 "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
1157 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1158 "POLICYGUID": policyguid,
1159 "POLICYGUID_DC": policyguid_dc,
1160 "DNSDOMAIN": names.dnsdomain,
1161 "DOMAINDN": names.domaindn})
1163 # If we are setting up a subdomain, then this has been replicated in, so we
1164 # don't need to add it
1165 if fill == FILL_FULL:
1166 setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
1167 "CONFIGDN": names.configdn,
1168 "SCHEMADN": names.schemadn,
1169 "DOMAINDN": names.domaindn,
1170 "SERVERDN": names.serverdn,
1171 "INVOCATIONID": invocationid,
1172 "NETBIOSNAME": names.netbiosname,
1173 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1174 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1175 "DOMAINSID": str(domainsid),
1176 "DCRID": str(dc_rid),
1177 "SAMBA_VERSION_STRING": version,
1178 "NTDSGUID": ntdsguid_line,
1179 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1180 domainControllerFunctionality)})
1182 # Setup fSMORoleOwner entries to point at the newly created DC entry
1183 setup_modify_ldif(samdb,
1184 setup_path("provision_self_join_modify_config.ldif"), {
1185 "CONFIGDN": names.configdn,
1186 "SCHEMADN": names.schemadn,
1187 "DEFAULTSITE": names.sitename,
1188 "NETBIOSNAME": names.netbiosname,
1189 "SERVERDN": names.serverdn,
1192 system_session_info = system_session()
1193 samdb.set_session_info(system_session_info)
1194 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1195 # modify a serverReference under cn=config when we are a subdomain, we must
1196 # be system due to ACLs
1197 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1198 "DOMAINDN": names.domaindn,
1199 "SERVERDN": names.serverdn,
1200 "NETBIOSNAME": names.netbiosname,
1203 samdb.set_session_info(admin_session_info)
1205 if dns_backend != "SAMBA_INTERNAL":
1206 # This is Samba4 specific and should be replaced by the correct
1207 # DNS AD-style setup
1208 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1209 "DNSDOMAIN": names.dnsdomain,
1210 "DOMAINDN": names.domaindn,
1211 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1212 "HOSTNAME" : names.hostname,
1213 "DNSNAME" : '%s.%s' % (
1214 names.netbiosname.lower(), names.dnsdomain.lower())
1218 def getpolicypath(sysvolpath, dnsdomain, guid):
1219 """Return the physical path of policy given its guid.
1221 :param sysvolpath: Path to the sysvol folder
1222 :param dnsdomain: DNS name of the AD domain
1223 :param guid: The GUID of the policy
1224 :return: A string with the complete path to the policy folder
1227 guid = "{%s}" % guid
1228 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1232 def create_gpo_struct(policy_path):
1233 if not os.path.exists(policy_path):
1234 os.makedirs(policy_path, 0o775)
1235 f = open(os.path.join(policy_path, "GPT.INI"), 'w')
1237 f.write("[General]\r\nVersion=0")
1240 p = os.path.join(policy_path, "MACHINE")
1241 if not os.path.exists(p):
1242 os.makedirs(p, 0o775)
1243 p = os.path.join(policy_path, "USER")
1244 if not os.path.exists(p):
1245 os.makedirs(p, 0o775)
1248 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1249 """Create the default GPO for a domain
1251 :param sysvolpath: Physical path for the sysvol folder
1252 :param dnsdomain: DNS domain name of the AD domain
1253 :param policyguid: GUID of the default domain policy
1254 :param policyguid_dc: GUID of the default domain controler policy
1256 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1257 create_gpo_struct(policy_path)
1259 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1260 create_gpo_struct(policy_path)
1263 def setup_samdb(path, session_info, provision_backend, lp, names,
1264 logger, fill, serverrole, schema, am_rodc=False,
1265 plaintext_secrets=False, backend_store=None):
1266 """Setup a complete SAM Database.
1268 :note: This will wipe the main SAM database file!
1271 # Also wipes the database
1272 setup_samdb_partitions(path, logger=logger, lp=lp,
1273 provision_backend=provision_backend, session_info=session_info,
1274 names=names, serverrole=serverrole, plaintext_secrets=plaintext_secrets,
1275 backend_store=backend_store)
1277 # Load the database, but don's load the global schema and don't connect
1279 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1280 credentials=provision_backend.credentials, lp=lp,
1281 global_schema=False, am_rodc=am_rodc)
1283 logger.info("Pre-loading the Samba 4 and AD schema")
1285 # Load the schema from the one we computed earlier
1286 samdb.set_schema(schema, write_indices_and_attributes=False)
1288 # Set the NTDS settings DN manually - in order to have it already around
1289 # before the provisioned tree exists and we connect
1290 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1292 # And now we can connect to the DB - the schema won't be loaded from the
1296 except ldb.LdbError as e2:
1297 (num, string_error) = e2.args
1298 if (num == ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS):
1299 raise ProvisioningError("Permission denied connecting to %s, are you running as root?" % path)
1303 # But we have to give it one more kick to have it use the schema
1304 # during provision - it needs, now that it is connected, to write
1305 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1306 samdb.set_schema(schema, write_indices_and_attributes=True)
1311 def fill_samdb(samdb, lp, names, logger, policyguid,
1312 policyguid_dc, fill, adminpass, krbtgtpass, machinepass, dns_backend,
1313 dnspass, invocationid, ntdsguid, serverrole, am_rodc=False,
1314 dom_for_fun_level=None, schema=None, next_rid=None, dc_rid=None,
1315 backend_store=None):
1317 if next_rid is None:
1320 # Provision does not make much sense values larger than 1000000000
1321 # as the upper range of the rIDAvailablePool is 1073741823 and
1322 # we don't want to create a domain that cannot allocate rids.
1323 if next_rid < 1000 or next_rid > 1000000000:
1324 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1325 error += "the valid range is %u-%u. The default is %u." % (
1326 1000, 1000000000, 1000)
1327 raise ProvisioningError(error)
1329 # ATTENTION: Do NOT change these default values without discussion with the
1330 # team and/or release manager. They have a big impact on the whole program!
1331 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1333 if dom_for_fun_level is None:
1334 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
1336 if dom_for_fun_level > domainControllerFunctionality:
1337 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!")
1339 domainFunctionality = dom_for_fun_level
1340 forestFunctionality = dom_for_fun_level
1342 # Set the NTDS settings DN manually - in order to have it already around
1343 # before the provisioned tree exists and we connect
1344 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1346 # Set the domain functionality levels onto the database.
1347 # Various module (the password_hash module in particular) need
1348 # to know what level of AD we are emulating.
1350 # These will be fixed into the database via the database
1351 # modifictions below, but we need them set from the start.
1352 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1353 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1354 samdb.set_opaque_integer("domainControllerFunctionality",
1355 domainControllerFunctionality)
1357 samdb.set_domain_sid(str(names.domainsid))
1358 samdb.set_invocation_id(invocationid)
1360 logger.info("Adding DomainDN: %s" % names.domaindn)
1362 # impersonate domain admin
1363 admin_session_info = admin_session(lp, str(names.domainsid))
1364 samdb.set_session_info(admin_session_info)
1365 if names.domainguid is not None:
1366 domainguid_line = "objectGUID: %s\n-" % names.domainguid
1368 domainguid_line = ""
1370 descr = b64encode(get_domain_descriptor(names.domainsid))
1371 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1372 "DOMAINDN": names.domaindn,
1373 "DOMAINSID": str(names.domainsid),
1374 "DESCRIPTOR": descr,
1375 "DOMAINGUID": domainguid_line
1378 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1379 "DOMAINDN": names.domaindn,
1380 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1381 "NEXTRID": str(next_rid),
1382 "DEFAULTSITE": names.sitename,
1383 "CONFIGDN": names.configdn,
1384 "POLICYGUID": policyguid,
1385 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1386 "SAMBA_VERSION_STRING": version,
1387 "MIN_PWD_LENGTH": str(DEFAULT_MIN_PWD_LENGTH)
1390 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1391 if fill == FILL_FULL:
1392 logger.info("Adding configuration container")
1393 descr = b64encode(get_config_descriptor(names.domainsid))
1394 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1395 "CONFIGDN": names.configdn,
1396 "DESCRIPTOR": descr,
1399 # The LDIF here was created when the Schema object was constructed
1400 ignore_checks_oid = "local_oid:%s:0" % samba.dsdb.DSDB_CONTROL_SKIP_DUPLICATES_CHECK_OID
1401 logger.info("Setting up sam.ldb schema")
1402 samdb.add_ldif(schema.schema_dn_add,
1403 controls=["relax:0", ignore_checks_oid])
1404 samdb.modify_ldif(schema.schema_dn_modify,
1405 controls=[ignore_checks_oid])
1406 samdb.write_prefixes_from_schema()
1407 samdb.add_ldif(schema.schema_data, controls=["relax:0", ignore_checks_oid])
1408 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1409 {"SCHEMADN": names.schemadn},
1410 controls=["relax:0", ignore_checks_oid])
1412 # Now register this container in the root of the forest
1413 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1414 msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1417 samdb.invocation_id = invocationid
1419 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1420 if fill == FILL_FULL:
1421 logger.info("Setting up sam.ldb configuration data")
1423 partitions_descr = b64encode(get_config_partitions_descriptor(names.domainsid))
1424 sites_descr = b64encode(get_config_sites_descriptor(names.domainsid))
1425 ntdsquotas_descr = b64encode(get_config_ntds_quotas_descriptor(names.domainsid))
1426 protected1_descr = b64encode(get_config_delete_protected1_descriptor(names.domainsid))
1427 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid))
1428 protected2_descr = b64encode(get_config_delete_protected2_descriptor(names.domainsid))
1430 if "2008" in schema.base_schema:
1431 # exclude 2012-specific changes if we're using a 2008 schema
1436 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1437 "CONFIGDN": names.configdn,
1438 "NETBIOSNAME": names.netbiosname,
1439 "DEFAULTSITE": names.sitename,
1440 "DNSDOMAIN": names.dnsdomain,
1441 "DOMAIN": names.domain,
1442 "SCHEMADN": names.schemadn,
1443 "DOMAINDN": names.domaindn,
1444 "SERVERDN": names.serverdn,
1445 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1446 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1447 "NTDSQUOTAS_DESCRIPTOR": ntdsquotas_descr,
1448 "LOSTANDFOUND_DESCRIPTOR": protected1wd_descr,
1449 "SERVICES_DESCRIPTOR": protected1_descr,
1450 "PHYSICALLOCATIONS_DESCRIPTOR": protected1wd_descr,
1451 "FORESTUPDATES_DESCRIPTOR": protected1wd_descr,
1452 "EXTENDEDRIGHTS_DESCRIPTOR": protected2_descr,
1453 "PARTITIONS_DESCRIPTOR": partitions_descr,
1454 "SITES_DESCRIPTOR": sites_descr,
1457 setup_add_ldif(samdb, setup_path("extended-rights.ldif"), {
1458 "CONFIGDN": names.configdn,
1459 "INC2012" : incl_2012,
1462 logger.info("Setting up display specifiers")
1463 display_specifiers_ldif = read_ms_ldif(
1464 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1465 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1466 {"CONFIGDN": names.configdn})
1467 check_all_substituted(display_specifiers_ldif)
1468 samdb.add_ldif(display_specifiers_ldif)
1470 logger.info("Modifying display specifiers and extended rights")
1471 setup_modify_ldif(samdb,
1472 setup_path("provision_configuration_modify.ldif"), {
1473 "CONFIGDN": names.configdn,
1474 "DISPLAYSPECIFIERS_DESCRIPTOR": protected2_descr
1477 logger.info("Adding users container")
1478 users_desc = b64encode(get_domain_users_descriptor(names.domainsid))
1479 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1480 "DOMAINDN": names.domaindn,
1481 "USERS_DESCRIPTOR": users_desc
1483 logger.info("Modifying users container")
1484 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1485 "DOMAINDN": names.domaindn})
1486 logger.info("Adding computers container")
1487 computers_desc = b64encode(get_domain_computers_descriptor(names.domainsid))
1488 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1489 "DOMAINDN": names.domaindn,
1490 "COMPUTERS_DESCRIPTOR": computers_desc
1492 logger.info("Modifying computers container")
1493 setup_modify_ldif(samdb,
1494 setup_path("provision_computers_modify.ldif"), {
1495 "DOMAINDN": names.domaindn})
1496 logger.info("Setting up sam.ldb data")
1497 infrastructure_desc = b64encode(get_domain_infrastructure_descriptor(names.domainsid))
1498 lostandfound_desc = b64encode(get_domain_delete_protected2_descriptor(names.domainsid))
1499 system_desc = b64encode(get_domain_delete_protected1_descriptor(names.domainsid))
1500 builtin_desc = b64encode(get_domain_builtin_descriptor(names.domainsid))
1501 controllers_desc = b64encode(get_domain_controllers_descriptor(names.domainsid))
1502 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1503 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1504 "DOMAINDN": names.domaindn,
1505 "NETBIOSNAME": names.netbiosname,
1506 "DEFAULTSITE": names.sitename,
1507 "CONFIGDN": names.configdn,
1508 "SERVERDN": names.serverdn,
1509 "RIDAVAILABLESTART": str(next_rid + 600),
1510 "POLICYGUID_DC": policyguid_dc,
1511 "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc,
1512 "LOSTANDFOUND_DESCRIPTOR": lostandfound_desc,
1513 "SYSTEM_DESCRIPTOR": system_desc,
1514 "BUILTIN_DESCRIPTOR": builtin_desc,
1515 "DOMAIN_CONTROLLERS_DESCRIPTOR": controllers_desc,
1518 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1519 if fill == FILL_FULL:
1520 managedservice_descr = b64encode(get_managed_service_accounts_descriptor(names.domainsid))
1521 setup_modify_ldif(samdb,
1522 setup_path("provision_configuration_references.ldif"), {
1523 "CONFIGDN": names.configdn,
1524 "SCHEMADN": names.schemadn})
1526 logger.info("Setting up well known security principals")
1527 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid))
1528 setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1529 "CONFIGDN": names.configdn,
1530 "WELLKNOWNPRINCIPALS_DESCRIPTOR": protected1wd_descr,
1531 }, controls=["relax:0", "provision:0"])
1533 if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1534 setup_modify_ldif(samdb,
1535 setup_path("provision_basedn_references.ldif"), {
1536 "DOMAINDN": names.domaindn,
1537 "MANAGEDSERVICE_DESCRIPTOR": managedservice_descr
1540 logger.info("Setting up sam.ldb users and groups")
1541 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1542 "DOMAINDN": names.domaindn,
1543 "DOMAINSID": str(names.domainsid),
1544 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1545 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1546 }, controls=["relax:0", "provision:0"])
1548 logger.info("Setting up self join")
1549 setup_self_join(samdb, admin_session_info, names=names, fill=fill,
1550 invocationid=invocationid,
1551 dns_backend=dns_backend,
1553 machinepass=machinepass,
1554 domainsid=names.domainsid,
1557 policyguid=policyguid,
1558 policyguid_dc=policyguid_dc,
1559 domainControllerFunctionality=domainControllerFunctionality,
1562 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1563 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1564 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1565 assert isinstance(names.ntdsguid, str)
1570 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1571 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)"
1572 SYSVOL_SERVICE="sysvol"
1574 def set_dir_acl(path, acl, lp, domsid, use_ntvfs, passdb, service=SYSVOL_SERVICE):
1575 setntacl(lp, path, acl, domsid, use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1576 for root, dirs, files in os.walk(path, topdown=False):
1578 setntacl(lp, os.path.join(root, name), acl, domsid,
1579 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1581 setntacl(lp, os.path.join(root, name), acl, domsid,
1582 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1585 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb):
1586 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1589 :param sysvol: Physical path for the sysvol folder
1590 :param dnsdomain: The DNS name of the domain
1591 :param domainsid: The SID of the domain
1592 :param domaindn: The DN of the domain (ie. DC=...)
1593 :param samdb: An LDB object on the SAM db
1594 :param lp: an LP object
1597 # Set ACL for GPO root folder
1598 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1599 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid),
1600 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=SYSVOL_SERVICE)
1602 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1603 attrs=["cn", "nTSecurityDescriptor"],
1604 expression="", scope=ldb.SCOPE_ONELEVEL)
1607 acl = ndr_unpack(security.descriptor,
1608 str(policy["nTSecurityDescriptor"])).as_sddl()
1609 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1610 set_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1611 str(domainsid), use_ntvfs,
1615 def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain,
1616 domaindn, lp, use_ntvfs):
1617 """Set the ACL for the sysvol share and the subfolders
1619 :param samdb: An LDB object on the SAM db
1620 :param netlogon: Physical path for the netlogon folder
1621 :param sysvol: Physical path for the sysvol folder
1622 :param uid: The UID of the "Administrator" user
1623 :param gid: The GID of the "Domain adminstrators" group
1624 :param domainsid: The SID of the domain
1625 :param dnsdomain: The DNS name of the domain
1626 :param domaindn: The DN of the domain (ie. DC=...)
1631 s3conf = s3param.get_context()
1632 s3conf.load(lp.configfile)
1634 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(sysvol))
1637 smbd.set_simple_acl(file.name, 0o755, gid)
1639 if not smbd.have_posix_acls():
1640 # This clue is only strictly correct for RPM and
1641 # Debian-like Linux systems, but hopefully other users
1642 # will get enough clue from it.
1643 raise ProvisioningError("Samba was compiled without the posix ACL support that s3fs requires. "
1644 "Try installing libacl1-dev or libacl-devel, then re-run configure and make.")
1646 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. "
1647 "Try the mounting the filesystem with the 'acl' option.")
1649 smbd.chown(file.name, uid, gid)
1651 raise ProvisioningError("Unable to chown a file on your filesystem. "
1652 "You may not be running provision as root.")
1656 # This will ensure that the smbd code we are running when setting ACLs
1657 # is initialised with the smb.conf
1658 s3conf = s3param.get_context()
1659 s3conf.load(lp.configfile)
1660 # ensure we are using the right samba_dsdb passdb backend, no matter what
1661 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1662 passdb.reload_static_pdb()
1664 # ensure that we init the samba_dsdb backend, so the domain sid is
1665 # marked in secrets.tdb
1666 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1668 # now ensure everything matches correctly, to avoid wierd issues
1669 if passdb.get_global_sam_sid() != domainsid:
1670 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))
1672 domain_info = s4_passdb.domain_info()
1673 if domain_info["dom_sid"] != domainsid:
1674 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))
1676 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1677 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()))
1682 os.chown(sysvol, -1, gid)
1688 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1689 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs,
1690 skip_invalid_chown=True, passdb=s4_passdb,
1691 service=SYSVOL_SERVICE)
1692 for root, dirs, files in os.walk(sysvol, topdown=False):
1694 if use_ntvfs and canchown:
1695 os.chown(os.path.join(root, name), -1, gid)
1696 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid),
1697 use_ntvfs=use_ntvfs, skip_invalid_chown=True,
1698 passdb=s4_passdb, service=SYSVOL_SERVICE)
1700 if use_ntvfs and canchown:
1701 os.chown(os.path.join(root, name), -1, gid)
1702 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid),
1703 use_ntvfs=use_ntvfs, skip_invalid_chown=True,
1704 passdb=s4_passdb, service=SYSVOL_SERVICE)
1706 # Set acls on Policy folder and policies folders
1707 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb=s4_passdb)
1709 def acl_type(direct_db_access):
1710 if direct_db_access:
1715 def check_dir_acl(path, acl, lp, domainsid, direct_db_access):
1716 fsacl = getntacl(lp, path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1717 fsacl_sddl = fsacl.as_sddl(domainsid)
1718 if fsacl_sddl != acl:
1719 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))
1721 for root, dirs, files in os.walk(path, topdown=False):
1723 fsacl = getntacl(lp, os.path.join(root, name),
1724 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1726 raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1727 fsacl_sddl = fsacl.as_sddl(domainsid)
1728 if fsacl_sddl != acl:
1729 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))
1732 fsacl = getntacl(lp, os.path.join(root, name),
1733 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1735 raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1736 fsacl_sddl = fsacl.as_sddl(domainsid)
1737 if fsacl_sddl != acl:
1738 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))
1741 def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1743 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1746 :param sysvol: Physical path for the sysvol folder
1747 :param dnsdomain: The DNS name of the domain
1748 :param domainsid: The SID of the domain
1749 :param domaindn: The DN of the domain (ie. DC=...)
1750 :param samdb: An LDB object on the SAM db
1751 :param lp: an LP object
1754 # Set ACL for GPO root folder
1755 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1756 fsacl = getntacl(lp, root_policy_path,
1757 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1759 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access), root_policy_path))
1760 fsacl_sddl = fsacl.as_sddl(domainsid)
1761 if fsacl_sddl != POLICIES_ACL:
1762 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))
1763 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1764 attrs=["cn", "nTSecurityDescriptor"],
1765 expression="", scope=ldb.SCOPE_ONELEVEL)
1768 acl = ndr_unpack(security.descriptor,
1769 str(policy["nTSecurityDescriptor"])).as_sddl()
1770 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1771 check_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1772 domainsid, direct_db_access)
1775 def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn,
1777 """Set the ACL for the sysvol share and the subfolders
1779 :param samdb: An LDB object on the SAM db
1780 :param netlogon: Physical path for the netlogon folder
1781 :param sysvol: Physical path for the sysvol folder
1782 :param uid: The UID of the "Administrator" user
1783 :param gid: The GID of the "Domain adminstrators" group
1784 :param domainsid: The SID of the domain
1785 :param dnsdomain: The DNS name of the domain
1786 :param domaindn: The DN of the domain (ie. DC=...)
1789 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1790 s3conf = s3param.get_context()
1791 s3conf.load(lp.configfile)
1792 # ensure we are using the right samba_dsdb passdb backend, no matter what
1793 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1794 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1795 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1797 # now ensure everything matches correctly, to avoid wierd issues
1798 if passdb.get_global_sam_sid() != domainsid:
1799 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))
1801 domain_info = s4_passdb.domain_info()
1802 if domain_info["dom_sid"] != domainsid:
1803 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))
1805 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1806 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()))
1808 # Ensure we can read this directly, and via the smbd VFS
1809 for direct_db_access in [True, False]:
1810 # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1811 for dir_path in [os.path.join(sysvol, dnsdomain), netlogon]:
1812 fsacl = getntacl(lp, dir_path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1814 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access), dir_path))
1815 fsacl_sddl = fsacl.as_sddl(domainsid)
1816 if fsacl_sddl != SYSVOL_ACL:
1817 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))
1819 # Check acls on Policy folder and policies folders
1820 check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1824 def interface_ips_v4(lp):
1825 """return only IPv4 IPs"""
1826 ips = samba.interface_ips(lp, False)
1829 if i.find(':') == -1:
1834 def interface_ips_v6(lp):
1835 """return only IPv6 IPs"""
1836 ips = samba.interface_ips(lp, False)
1839 if i.find(':') != -1:
1844 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1846 targetdir=None, samdb_fill=FILL_FULL,
1847 hostip=None, hostip6=None,
1848 next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1849 domainguid=None, policyguid=None, policyguid_dc=None,
1850 invocationid=None, machinepass=None, ntdsguid=None,
1851 dns_backend=None, dnspass=None,
1852 serverrole=None, dom_for_fun_level=None,
1853 am_rodc=False, lp=None, use_ntvfs=False,
1854 skip_sysvolacl=False, backend_store=None):
1855 # create/adapt the group policy GUIDs
1856 # Default GUID for default policy are described at
1857 # "How Core Group Policy Works"
1858 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1859 if policyguid is None:
1860 policyguid = DEFAULT_POLICY_GUID
1861 policyguid = policyguid.upper()
1862 if policyguid_dc is None:
1863 policyguid_dc = DEFAULT_DC_POLICY_GUID
1864 policyguid_dc = policyguid_dc.upper()
1866 if invocationid is None:
1867 invocationid = str(uuid.uuid4())
1869 if krbtgtpass is None:
1870 krbtgtpass = samba.generate_random_machine_password(128, 255)
1871 if machinepass is None:
1872 machinepass = samba.generate_random_machine_password(128, 255)
1874 dnspass = samba.generate_random_password(128, 255)
1876 samdb.transaction_start()
1878 samdb = fill_samdb(samdb, lp, names, logger=logger,
1880 policyguid=policyguid, policyguid_dc=policyguid_dc,
1881 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1882 invocationid=invocationid, machinepass=machinepass,
1883 dns_backend=dns_backend, dnspass=dnspass,
1884 ntdsguid=ntdsguid, serverrole=serverrole,
1885 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1886 next_rid=next_rid, dc_rid=dc_rid,
1887 backend_store=backend_store)
1889 # Set up group policies (domain policy and domain controller
1891 if serverrole == "active directory domain controller":
1892 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1895 samdb.transaction_cancel()
1898 samdb.transaction_commit()
1900 if serverrole == "active directory domain controller":
1901 # Continue setting up sysvol for GPO. This appears to require being
1902 # outside a transaction.
1903 if not skip_sysvolacl:
1904 setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.root_uid,
1905 paths.root_gid, names.domainsid, names.dnsdomain,
1906 names.domaindn, lp, use_ntvfs)
1908 logger.info("Setting acl on sysvol skipped")
1910 secretsdb_self_join(secrets_ldb, domain=names.domain,
1911 realm=names.realm, dnsdomain=names.dnsdomain,
1912 netbiosname=names.netbiosname, domainsid=names.domainsid,
1913 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1915 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1916 # In future, this might be determined from some configuration
1917 kerberos_enctypes = str(ENC_ALL_TYPES)
1920 msg = ldb.Message(ldb.Dn(samdb,
1921 samdb.searchone("distinguishedName",
1922 expression="samAccountName=%s$" % names.netbiosname,
1923 scope=ldb.SCOPE_SUBTREE).decode('utf8')))
1924 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1925 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1926 name="msDS-SupportedEncryptionTypes")
1928 except ldb.LdbError as e:
1929 (enum, estr) = e.args
1930 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1931 # It might be that this attribute does not exist in this schema
1934 setup_ad_dns(samdb, secrets_ldb, names, paths, lp, logger,
1935 hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
1936 dnspass=dnspass, os_level=dom_for_fun_level,
1937 targetdir=targetdir, fill_level=samdb_fill,
1938 backend_store=backend_store)
1940 domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1941 attribute="objectGUID")
1942 assert isinstance(domainguid, str)
1944 lastProvisionUSNs = get_last_provision_usn(samdb)
1945 maxUSN = get_max_usn(samdb, str(names.rootdn))
1946 if lastProvisionUSNs is not None:
1947 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1949 set_provision_usn(samdb, 0, maxUSN, invocationid)
1951 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1952 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1953 { 'NTDSGUID' : names.ntdsguid })
1955 # fix any dangling GUIDs from the provision
1956 logger.info("Fixing provision GUIDs")
1957 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
1959 samdb.transaction_start()
1961 # a small number of GUIDs are missing because of ordering issues in the
1963 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1964 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1965 scope=ldb.SCOPE_BASE,
1966 attrs=['defaultObjectCategory'])
1967 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1968 scope=ldb.SCOPE_ONELEVEL,
1969 attrs=['ipsecOwnersReference',
1970 'ipsecFilterReference',
1971 'ipsecISAKMPReference',
1972 'ipsecNegotiationPolicyReference',
1973 'ipsecNFAReference'])
1974 if chk.check_database(DN=names.schemadn, scope=ldb.SCOPE_SUBTREE,
1975 attrs=['attributeId', 'governsId']) != 0:
1976 raise ProvisioningError("Duplicate attributeId or governsId in schema. Must be fixed manually!!")
1978 samdb.transaction_cancel()
1981 samdb.transaction_commit()
1985 "ROLE_STANDALONE": "standalone server",
1986 "ROLE_DOMAIN_MEMBER": "member server",
1987 "ROLE_DOMAIN_BDC": "active directory domain controller",
1988 "ROLE_DOMAIN_PDC": "active directory domain controller",
1989 "dc": "active directory domain controller",
1990 "member": "member server",
1991 "domain controller": "active directory domain controller",
1992 "active directory domain controller": "active directory domain controller",
1993 "member server": "member server",
1994 "standalone": "standalone server",
1995 "standalone server": "standalone server",
1999 def sanitize_server_role(role):
2000 """Sanitize a server role name.
2002 :param role: Server role
2003 :raise ValueError: If the role can not be interpreted
2004 :return: Sanitized server role (one of "member server",
2005 "active directory domain controller", "standalone server")
2008 return _ROLES_MAP[role]
2010 raise ValueError(role)
2013 def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain,
2015 """Create AD entries for the fake ypserver.
2017 This is needed for being able to manipulate posix attrs via ADUC.
2019 samdb.transaction_start()
2021 logger.info("Setting up fake yp server settings")
2022 setup_add_ldif(samdb, setup_path("ypServ30.ldif"), {
2023 "DOMAINDN": domaindn,
2024 "NETBIOSNAME": netbiosname,
2025 "NISDOMAIN": nisdomain,
2028 samdb.transaction_cancel()
2031 samdb.transaction_commit()
2033 def directory_create_or_exists(path, mode=0o755):
2034 if not os.path.exists(path):
2036 os.mkdir(path, mode)
2037 except OSError as e:
2038 if e.errno in [errno.EEXIST]:
2041 raise ProvisioningError("Failed to create directory %s: %s" % (path, e.strerror))
2043 def provision(logger, session_info, smbconf=None,
2044 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
2045 domaindn=None, schemadn=None, configdn=None, serverdn=None,
2046 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
2047 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None,
2048 krbtgtpass=None, domainguid=None, policyguid=None, policyguid_dc=None,
2049 dns_backend=None, dns_forwarder=None, dnspass=None,
2050 invocationid=None, machinepass=None, ntdsguid=None,
2051 root=None, nobody=None, users=None, backup=None, aci=None,
2052 serverrole=None, dom_for_fun_level=None, backend_type=None,
2053 sitename=None, ol_mmr_urls=None, ol_olc=None, slapd_path=None,
2054 useeadb=False, am_rodc=False, lp=None, use_ntvfs=False,
2055 use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True,
2056 ldap_backend_forced_uri=None, nosync=False, ldap_dryrun_mode=False,
2057 ldap_backend_extra_port=None, base_schema=None,
2058 plaintext_secrets=False, backend_store=None):
2061 :note: caution, this wipes all existing data!
2065 serverrole = sanitize_server_role(serverrole)
2067 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole)
2069 if ldapadminpass is None:
2070 # Make a new, random password between Samba and it's LDAP server
2071 ldapadminpass = samba.generate_random_password(128, 255)
2073 if backend_type is None:
2074 backend_type = "ldb"
2075 if backend_store is None:
2076 backend_store = get_default_backend_store()
2078 if domainsid is None:
2079 domainsid = security.random_sid()
2081 root_uid = findnss_uid([root or "root"])
2082 nobody_uid = findnss_uid([nobody or "nobody"])
2083 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
2084 root_gid = pwd.getpwuid(root_uid).pw_gid
2087 bind_gid = findnss_gid(["bind", "named"])
2091 if targetdir is not None:
2092 smbconf = os.path.join(targetdir, "etc", "smb.conf")
2093 elif smbconf is None:
2094 smbconf = samba.param.default_path()
2095 if not os.path.exists(os.path.dirname(smbconf)):
2096 os.makedirs(os.path.dirname(smbconf))
2098 server_services = []
2101 global_param["idmap_ldb:use rfc2307"] = ["yes"]
2103 if dns_backend != "SAMBA_INTERNAL":
2104 server_services.append("-dns")
2106 if dns_forwarder is not None:
2107 global_param["dns forwarder"] = [dns_forwarder]
2110 server_services.append("+smb")
2111 server_services.append("-s3fs")
2112 global_param["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
2114 if len(server_services) > 0:
2115 global_param["server services"] = server_services
2117 # only install a new smb.conf if there isn't one there already
2118 if os.path.exists(smbconf):
2119 # if Samba Team members can't figure out the weird errors
2120 # loading an empty smb.conf gives, then we need to be smarter.
2121 # Pretend it just didn't exist --abartlet
2122 f = open(smbconf, 'r')
2124 data = f.read().lstrip()
2127 if data is None or data == "":
2128 make_smbconf(smbconf, hostname, domain, realm,
2129 targetdir, serverrole=serverrole,
2130 eadb=useeadb, use_ntvfs=use_ntvfs,
2131 lp=lp, global_param=global_param)
2133 make_smbconf(smbconf, hostname, domain, realm, targetdir,
2134 serverrole=serverrole,
2135 eadb=useeadb, use_ntvfs=use_ntvfs, lp=lp, global_param=global_param)
2138 lp = samba.param.LoadParm()
2140 names = guess_names(lp=lp, hostname=hostname, domain=domain,
2141 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
2142 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
2143 sitename=sitename, rootdn=rootdn, domain_names_forced=(samdb_fill == FILL_DRS))
2144 paths = provision_paths_from_lp(lp, names.dnsdomain)
2146 paths.bind_gid = bind_gid
2147 paths.root_uid = root_uid;
2148 paths.root_gid = root_gid
2151 logger.info("Looking up IPv4 addresses")
2152 hostips = interface_ips_v4(lp)
2153 if len(hostips) > 0:
2155 if len(hostips) > 1:
2156 logger.warning("More than one IPv4 address found. Using %s",
2158 if hostip == "127.0.0.1":
2161 logger.warning("No IPv4 address will be assigned")
2164 logger.info("Looking up IPv6 addresses")
2165 hostips = interface_ips_v6(lp)
2167 hostip6 = hostips[0]
2168 if len(hostips) > 1:
2169 logger.warning("More than one IPv6 address found. Using %s", hostip6)
2171 logger.warning("No IPv6 address will be assigned")
2173 names.hostip = hostip
2174 names.hostip6 = hostip6
2175 names.domainguid = domainguid
2176 names.domainsid = domainsid
2177 names.forestsid = domainsid
2179 if serverrole is None:
2180 serverrole = lp.get("server role")
2182 directory_create_or_exists(paths.private_dir, 0o700)
2183 directory_create_or_exists(paths.binddns_dir, 0o770)
2184 directory_create_or_exists(os.path.join(paths.private_dir, "tls"))
2185 directory_create_or_exists(paths.state_dir)
2186 if not plaintext_secrets:
2187 setup_encrypted_secrets_key(paths.encrypted_secrets_key_path)
2189 if paths.sysvol and not os.path.exists(paths.sysvol):
2190 os.makedirs(paths.sysvol, 0o775)
2192 ldapi_url = "ldapi://%s" % urllib_quote(paths.s4_ldapi_path, safe="")
2194 schema = Schema(domainsid, invocationid=invocationid,
2195 schemadn=names.schemadn, base_schema=base_schema)
2197 if backend_type == "ldb":
2198 provision_backend = LDBBackend(backend_type, paths=paths,
2200 names=names, logger=logger)
2201 elif backend_type == "existing":
2202 # If support for this is ever added back, then the URI will need to be
2204 provision_backend = ExistingBackend(backend_type, paths=paths,
2206 names=names, logger=logger,
2207 ldap_backend_forced_uri=ldap_backend_forced_uri)
2208 elif backend_type == "fedora-ds":
2209 provision_backend = FDSBackend(backend_type, paths=paths,
2211 names=names, logger=logger, domainsid=domainsid,
2212 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2213 slapd_path=slapd_path,
2215 elif backend_type == "openldap":
2216 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
2218 names=names, logger=logger, domainsid=domainsid,
2219 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2220 slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls,
2221 ldap_backend_extra_port=ldap_backend_extra_port,
2222 ldap_dryrun_mode=ldap_dryrun_mode, nosync=nosync,
2223 ldap_backend_forced_uri=ldap_backend_forced_uri)
2225 raise ValueError("Unknown LDAP backend type selected")
2227 provision_backend.init()
2228 provision_backend.start()
2230 # only install a new shares config db if there is none
2231 if not os.path.exists(paths.shareconf):
2232 logger.info("Setting up share.ldb")
2233 share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
2234 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
2236 logger.info("Setting up secrets.ldb")
2237 secrets_ldb = setup_secretsdb(paths,
2238 session_info=session_info,
2239 backend_credentials=provision_backend.credentials, lp=lp)
2242 logger.info("Setting up the registry")
2243 setup_registry(paths.hklm, session_info, lp=lp)
2245 logger.info("Setting up the privileges database")
2246 setup_privileges(paths.privilege, session_info, lp=lp)
2248 logger.info("Setting up idmap db")
2249 idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
2251 setup_name_mappings(idmap, sid=str(domainsid),
2252 root_uid=root_uid, nobody_uid=nobody_uid,
2253 users_gid=users_gid, root_gid=root_gid)
2255 logger.info("Setting up SAM db")
2256 samdb = setup_samdb(paths.samdb, session_info,
2257 provision_backend, lp, names, logger=logger,
2258 serverrole=serverrole,
2259 schema=schema, fill=samdb_fill, am_rodc=am_rodc,
2260 plaintext_secrets=plaintext_secrets,
2261 backend_store=backend_store)
2263 if serverrole == "active directory domain controller":
2264 if paths.netlogon is None:
2265 raise MissingShareError("netlogon", paths.smbconf)
2267 if paths.sysvol is None:
2268 raise MissingShareError("sysvol", paths.smbconf)
2270 if not os.path.isdir(paths.netlogon):
2271 os.makedirs(paths.netlogon, 0o755)
2273 if adminpass is None:
2274 adminpass = samba.generate_random_password(12, 32)
2275 adminpass_generated = True
2277 adminpass = unicode(adminpass, 'utf-8')
2278 adminpass_generated = False
2280 if samdb_fill == FILL_FULL:
2281 provision_fill(samdb, secrets_ldb, logger, names, paths,
2282 schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
2283 hostip=hostip, hostip6=hostip6,
2284 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
2285 krbtgtpass=krbtgtpass,
2286 policyguid=policyguid, policyguid_dc=policyguid_dc,
2287 invocationid=invocationid, machinepass=machinepass,
2288 ntdsguid=ntdsguid, dns_backend=dns_backend,
2289 dnspass=dnspass, serverrole=serverrole,
2290 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
2291 lp=lp, use_ntvfs=use_ntvfs,
2292 skip_sysvolacl=skip_sysvolacl,
2293 backend_store=backend_store)
2295 if not is_heimdal_built():
2296 create_kdc_conf(paths.kdcconf, realm, domain, os.path.dirname(lp.get("log file")))
2297 logger.info("The Kerberos KDC configuration for Samba AD is "
2298 "located at %s", paths.kdcconf)
2300 create_krb5_conf(paths.krb5conf,
2301 dnsdomain=names.dnsdomain, hostname=names.hostname,
2303 logger.info("A Kerberos configuration suitable for Samba AD has been "
2304 "generated at %s", paths.krb5conf)
2305 logger.info("Merge the contents of this file with your system "
2306 "krb5.conf or replace it with this one. Do not create a "
2309 if serverrole == "active directory domain controller":
2310 create_dns_update_list(lp, logger, paths)
2312 backend_result = provision_backend.post_setup()
2313 provision_backend.shutdown()
2316 secrets_ldb.transaction_cancel()
2319 # Now commit the secrets.ldb to disk
2320 secrets_ldb.transaction_commit()
2322 # the commit creates the dns.keytab in the private directory
2323 private_dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
2324 bind_dns_keytab_path = os.path.join(paths.binddns_dir, paths.dns_keytab)
2326 if os.path.isfile(private_dns_keytab_path):
2327 if os.path.isfile(bind_dns_keytab_path):
2329 os.unlink(bind_dns_keytab_path)
2330 except OSError as e:
2331 logger.error("Failed to remove %s: %s" %
2332 (bind_dns_keytab_path, e.strerror))
2334 # link the dns.keytab to the bind-dns directory
2336 os.link(private_dns_keytab_path, bind_dns_keytab_path)
2337 except OSError as e:
2338 logger.error("Failed to create link %s -> %s: %s" %
2339 (private_dns_keytab_path, bind_dns_keytab_path, e.strerror))
2341 # chown the dns.keytab in the bind-dns directory
2342 if paths.bind_gid is not None:
2344 os.chmod(paths.binddns_dir, 0o770)
2345 os.chown(paths.binddns_dir, -1, paths.bind_gid)
2347 if not os.environ.has_key('SAMBA_SELFTEST'):
2348 logger.info("Failed to chown %s to bind gid %u",
2349 paths.binddns_dir, paths.bind_gid)
2352 os.chmod(bind_dns_keytab_path, 0o640)
2353 os.chown(bind_dns_keytab_path, -1, paths.bind_gid)
2355 if not os.environ.has_key('SAMBA_SELFTEST'):
2356 logger.info("Failed to chown %s to bind gid %u",
2357 bind_dns_keytab_path, paths.bind_gid)
2359 result = ProvisionResult()
2360 result.server_role = serverrole
2361 result.domaindn = domaindn
2362 result.paths = paths
2363 result.names = names
2365 result.samdb = samdb
2366 result.idmap = idmap
2367 result.domainsid = str(domainsid)
2369 if samdb_fill == FILL_FULL:
2370 result.adminpass_generated = adminpass_generated
2371 result.adminpass = adminpass
2373 result.adminpass_generated = False
2374 result.adminpass = None
2376 result.backend_result = backend_result
2379 provision_fake_ypserver(logger=logger, samdb=samdb,
2380 domaindn=names.domaindn, netbiosname=names.netbiosname,
2381 nisdomain=names.domain.lower(), maxuid=maxuid, maxgid=maxgid)
2386 def provision_become_dc(smbconf=None, targetdir=None,
2387 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
2388 serverdn=None, domain=None, hostname=None, domainsid=None,
2389 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
2390 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
2391 dns_backend=None, root=None, nobody=None, users=None,
2392 backup=None, serverrole=None, ldap_backend=None,
2393 ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False):
2395 logger = logging.getLogger("provision")
2396 samba.set_debug_level(debuglevel)
2398 res = provision(logger, system_session(),
2399 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
2400 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
2401 configdn=configdn, serverdn=serverdn, domain=domain,
2402 hostname=hostname, hostip=None, domainsid=domainsid,
2403 machinepass=machinepass,
2404 serverrole="active directory domain controller",
2405 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass,
2406 use_ntvfs=use_ntvfs)
2407 res.lp.set("debuglevel", str(debuglevel))
2411 def create_krb5_conf(path, dnsdomain, hostname, realm):
2412 """Write out a file containing a valid krb5.conf file
2414 :param path: Path of the new krb5.conf file.
2415 :param dnsdomain: DNS Domain name
2416 :param hostname: Local hostname
2417 :param realm: Realm name
2419 setup_file(setup_path("krb5.conf"), path, {
2420 "DNSDOMAIN": dnsdomain,
2421 "HOSTNAME": hostname,
2426 class ProvisioningError(Exception):
2427 """A generic provision error."""
2429 def __init__(self, value):
2433 return "ProvisioningError: " + self.value
2436 class InvalidNetbiosName(Exception):
2437 """A specified name was not a valid NetBIOS name."""
2439 def __init__(self, name):
2440 super(InvalidNetbiosName, self).__init__(
2441 "The name '%r' is not a valid NetBIOS name" % name)
2444 class MissingShareError(ProvisioningError):
2446 def __init__(self, name, smbconf):
2447 super(MissingShareError, self).__init__(
2448 "Existing smb.conf does not have a [%s] share, but you are "
2449 "configuring a DC. Please remove %s or add the share manually." %