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 = lp.get("workgroup").upper()
199 names.realm = lp.get("realm")
200 names.dnsdomain = names.realm.lower()
201 basedn = samba.dn_from_dns_name(names.dnsdomain)
202 names.realm = names.realm.upper()
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"))
747 global_settings["binddns dir"] = os.path.abspath(os.path.join(targetdir, "bind-dns"))
749 lp.set("lock dir", os.path.abspath(targetdir))
750 lp.set("state directory", global_settings["state directory"])
751 lp.set("cache directory", global_settings["cache directory"])
752 lp.set("binddns dir", global_settings["binddns dir"])
755 if use_ntvfs and not lp.get("posix:eadb"):
756 if targetdir is not None:
757 privdir = os.path.join(targetdir, "private")
759 privdir = lp.get("private dir")
760 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
761 elif not use_ntvfs and not lp.get("xattr_tdb:file"):
762 if targetdir is not None:
763 statedir = os.path.join(targetdir, "state")
765 statedir = lp.get("state directory")
766 lp.set("xattr_tdb:file", os.path.abspath(os.path.join(statedir, "xattr.tdb")))
769 if serverrole == "active directory domain controller":
770 shares["sysvol"] = os.path.join(lp.get("state directory"), "sysvol")
771 shares["netlogon"] = os.path.join(shares["sysvol"], realm.lower(),
774 global_settings["passdb backend"] = "samba_dsdb"
776 f = open(smbconf, 'w')
778 f.write("[globals]\n")
779 for key, val in global_settings.items():
780 f.write("\t%s = %s\n" % (key, val))
783 for name, path in shares.items():
784 f.write("[%s]\n" % name)
785 f.write("\tpath = %s\n" % path)
786 f.write("\tread only = no\n")
790 # reload the smb.conf
793 # and dump it without any values that are the default
794 # this ensures that any smb.conf parameters that were set
795 # on the provision/join command line are set in the resulting smb.conf
796 lp.dump(False, smbconf)
799 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
800 users_gid, root_gid):
801 """setup reasonable name mappings for sam names to unix names.
803 :param samdb: SamDB object.
804 :param idmap: IDmap db object.
805 :param sid: The domain sid.
806 :param domaindn: The domain DN.
807 :param root_uid: uid of the UNIX root user.
808 :param nobody_uid: uid of the UNIX nobody user.
809 :param users_gid: gid of the UNIX users group.
810 :param root_gid: gid of the UNIX root group.
812 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
814 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
815 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
818 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
819 provision_backend, names, serverrole,
820 erase=False, plaintext_secrets=False,
822 """Setup the partitions for the SAM database.
824 Alternatively, provision() may call this, and then populate the database.
826 :note: This will wipe the Sam Database!
828 :note: This function always removes the local SAM LDB file. The erase
829 parameter controls whether to erase the existing data, which
830 may not be stored locally but in LDAP.
833 assert session_info is not None
835 # We use options=["modules:"] to stop the modules loading - we
836 # just want to wipe and re-initialise the database, not start it up
839 os.unlink(samdb_path)
843 samdb = Ldb(url=samdb_path, session_info=session_info,
844 lp=lp, options=["modules:"])
846 ldap_backend_line = "# No LDAP backend"
847 if provision_backend.type != "ldb":
848 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
850 required_features = None
851 if not plaintext_secrets:
852 required_features = "requiredFeatures: encryptedSecrets"
854 if backend_store is None:
855 backend_store = get_default_backend_store()
856 backend_store_line = "backendStore: %s" % backend_store
858 if backend_store == "mdb":
859 if required_features is not None:
860 required_features += "\n"
862 required_features = ""
863 required_features += "requiredFeatures: lmdbLevelOne"
865 if required_features is None:
866 required_features = "# No required features"
868 samdb.transaction_start()
870 logger.info("Setting up sam.ldb partitions and settings")
871 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
872 "LDAP_BACKEND_LINE": ldap_backend_line,
873 "BACKEND_STORE": backend_store_line
877 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
878 "BACKEND_TYPE": provision_backend.type,
879 "SERVER_ROLE": serverrole,
880 "REQUIRED_FEATURES": required_features
883 logger.info("Setting up sam.ldb rootDSE")
884 setup_samdb_rootdse(samdb, names)
886 samdb.transaction_cancel()
889 samdb.transaction_commit()
892 def secretsdb_self_join(secretsdb, domain,
893 netbiosname, machinepass, domainsid=None,
894 realm=None, dnsdomain=None,
896 key_version_number=1,
897 secure_channel_type=SEC_CHAN_WKSTA):
898 """Add domain join-specific bits to a secrets database.
900 :param secretsdb: Ldb Handle to the secrets database
901 :param machinepass: Machine password
903 attrs = ["whenChanged",
910 if realm is not None:
911 if dnsdomain is None:
912 dnsdomain = realm.lower()
913 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
916 shortname = netbiosname.lower()
918 # We don't need to set msg["flatname"] here, because rdn_name will handle
919 # it, and it causes problems for modifies anyway
920 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
921 msg["secureChannelType"] = [str(secure_channel_type)]
922 msg["objectClass"] = ["top", "primaryDomain"]
923 if dnsname is not None:
924 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
925 msg["realm"] = [realm]
926 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
927 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
928 msg["privateKeytab"] = ["secrets.keytab"]
930 msg["secret"] = [machinepass.encode('utf-8')]
931 msg["samAccountName"] = ["%s$" % netbiosname]
932 msg["secureChannelType"] = [str(secure_channel_type)]
933 if domainsid is not None:
934 msg["objectSid"] = [ndr_pack(domainsid)]
936 # This complex expression tries to ensure that we don't have more
937 # than one record for this SID, realm or netbios domain at a time,
938 # but we don't delete the old record that we are about to modify,
939 # because that would delete the keytab and previous password.
940 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
941 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
942 scope=ldb.SCOPE_ONELEVEL)
945 secretsdb.delete(del_msg.dn)
947 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
950 msg["priorSecret"] = [res[0]["secret"][0]]
952 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
957 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
962 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
968 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
969 secretsdb.modify(msg)
970 secretsdb.rename(res[0].dn, msg.dn)
972 spn = [ 'HOST/%s' % shortname ]
973 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
974 # we are a domain controller then we add servicePrincipalName
975 # entries for the keytab code to update.
976 spn.extend([ 'HOST/%s' % dnsname ])
977 msg["servicePrincipalName"] = spn
982 def setup_secretsdb(paths, session_info, backend_credentials, lp):
983 """Setup the secrets database.
985 :note: This function does not handle exceptions and transaction on purpose,
986 it's up to the caller to do this job.
988 :param path: Path to the secrets database.
989 :param session_info: Session info.
990 :param credentials: Credentials
991 :param lp: Loadparm context
992 :return: LDB handle for the created secrets database
994 if os.path.exists(paths.secrets):
995 os.unlink(paths.secrets)
997 keytab_path = os.path.join(paths.private_dir, paths.keytab)
998 if os.path.exists(keytab_path):
999 os.unlink(keytab_path)
1001 bind_dns_keytab_path = os.path.join(paths.binddns_dir, paths.dns_keytab)
1002 if os.path.exists(bind_dns_keytab_path):
1003 os.unlink(bind_dns_keytab_path)
1005 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1006 if os.path.exists(dns_keytab_path):
1007 os.unlink(dns_keytab_path)
1009 path = paths.secrets
1011 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
1013 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
1014 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
1015 secrets_ldb.transaction_start()
1017 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
1019 if (backend_credentials is not None and
1020 backend_credentials.authentication_requested()):
1021 if backend_credentials.get_bind_dn() is not None:
1022 setup_add_ldif(secrets_ldb,
1023 setup_path("secrets_simple_ldap.ldif"), {
1024 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
1025 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password()).decode('utf8')
1028 setup_add_ldif(secrets_ldb,
1029 setup_path("secrets_sasl_ldap.ldif"), {
1030 "LDAPADMINUSER": backend_credentials.get_username(),
1031 "LDAPADMINREALM": backend_credentials.get_realm(),
1032 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password()).decode('utf8')
1035 secrets_ldb.transaction_cancel()
1040 def setup_privileges(path, session_info, lp):
1041 """Setup the privileges database.
1043 :param path: Path to the privileges database.
1044 :param session_info: Session info.
1045 :param credentials: Credentials
1046 :param lp: Loadparm context
1047 :return: LDB handle for the created secrets database
1049 if os.path.exists(path):
1051 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
1052 privilege_ldb.erase()
1053 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
1055 def setup_encrypted_secrets_key(path):
1056 """Setup the encrypted secrets key file.
1058 Any existing key file will be deleted and a new random key generated.
1060 :param path: Path to the secrets key file.
1063 if os.path.exists(path):
1066 flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
1067 mode = stat.S_IRUSR | stat.S_IWUSR
1069 umask_original = os.umask(0)
1071 fd = os.open(path, flags, mode)
1073 os.umask(umask_original)
1075 with os.fdopen(fd, 'w') as f:
1076 key = samba.generate_random_bytes(16)
1080 def setup_registry(path, session_info, lp):
1081 """Setup the registry.
1083 :param path: Path to the registry database
1084 :param session_info: Session information
1085 :param credentials: Credentials
1086 :param lp: Loadparm context
1088 reg = samba.registry.Registry()
1089 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
1090 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
1091 provision_reg = setup_path("provision.reg")
1092 assert os.path.exists(provision_reg)
1093 reg.diff_apply(provision_reg)
1096 def setup_idmapdb(path, session_info, lp):
1097 """Setup the idmap database.
1099 :param path: path to the idmap database
1100 :param session_info: Session information
1101 :param credentials: Credentials
1102 :param lp: Loadparm context
1104 if os.path.exists(path):
1107 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
1109 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
1113 def setup_samdb_rootdse(samdb, names):
1114 """Setup the SamDB rootdse.
1116 :param samdb: Sam Database handle
1118 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
1119 "SCHEMADN": names.schemadn,
1120 "DOMAINDN": names.domaindn,
1121 "ROOTDN" : names.rootdn,
1122 "CONFIGDN": names.configdn,
1123 "SERVERDN": names.serverdn,
1127 def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
1128 dns_backend, dnspass, domainsid, next_rid, invocationid,
1129 policyguid, policyguid_dc,
1130 domainControllerFunctionality, ntdsguid=None, dc_rid=None):
1131 """Join a host to its own domain."""
1132 assert isinstance(invocationid, str)
1133 if ntdsguid is not None:
1134 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
1141 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
1142 "CONFIGDN": names.configdn,
1143 "SCHEMADN": names.schemadn,
1144 "DOMAINDN": names.domaindn,
1145 "SERVERDN": names.serverdn,
1146 "INVOCATIONID": invocationid,
1147 "NETBIOSNAME": names.netbiosname,
1148 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1149 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')).decode('utf8'),
1150 "DOMAINSID": str(domainsid),
1151 "DCRID": str(dc_rid),
1152 "SAMBA_VERSION_STRING": version,
1153 "NTDSGUID": ntdsguid_line,
1154 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1155 domainControllerFunctionality),
1156 "RIDALLOCATIONSTART": str(next_rid + 100),
1157 "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
1159 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1160 "POLICYGUID": policyguid,
1161 "POLICYGUID_DC": policyguid_dc,
1162 "DNSDOMAIN": names.dnsdomain,
1163 "DOMAINDN": names.domaindn})
1165 # If we are setting up a subdomain, then this has been replicated in, so we
1166 # don't need to add it
1167 if fill == FILL_FULL:
1168 setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
1169 "CONFIGDN": names.configdn,
1170 "SCHEMADN": names.schemadn,
1171 "DOMAINDN": names.domaindn,
1172 "SERVERDN": names.serverdn,
1173 "INVOCATIONID": invocationid,
1174 "NETBIOSNAME": names.netbiosname,
1175 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1176 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')).decode('utf8'),
1177 "DOMAINSID": str(domainsid),
1178 "DCRID": str(dc_rid),
1179 "SAMBA_VERSION_STRING": version,
1180 "NTDSGUID": ntdsguid_line,
1181 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1182 domainControllerFunctionality)})
1184 # Setup fSMORoleOwner entries to point at the newly created DC entry
1185 setup_modify_ldif(samdb,
1186 setup_path("provision_self_join_modify_config.ldif"), {
1187 "CONFIGDN": names.configdn,
1188 "SCHEMADN": names.schemadn,
1189 "DEFAULTSITE": names.sitename,
1190 "NETBIOSNAME": names.netbiosname,
1191 "SERVERDN": names.serverdn,
1194 system_session_info = system_session()
1195 samdb.set_session_info(system_session_info)
1196 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1197 # modify a serverReference under cn=config when we are a subdomain, we must
1198 # be system due to ACLs
1199 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1200 "DOMAINDN": names.domaindn,
1201 "SERVERDN": names.serverdn,
1202 "NETBIOSNAME": names.netbiosname,
1205 samdb.set_session_info(admin_session_info)
1207 if dns_backend != "SAMBA_INTERNAL":
1208 # This is Samba4 specific and should be replaced by the correct
1209 # DNS AD-style setup
1210 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1211 "DNSDOMAIN": names.dnsdomain,
1212 "DOMAINDN": names.domaindn,
1213 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')).decode('utf8'),
1214 "HOSTNAME" : names.hostname,
1215 "DNSNAME" : '%s.%s' % (
1216 names.netbiosname.lower(), names.dnsdomain.lower())
1220 def getpolicypath(sysvolpath, dnsdomain, guid):
1221 """Return the physical path of policy given its guid.
1223 :param sysvolpath: Path to the sysvol folder
1224 :param dnsdomain: DNS name of the AD domain
1225 :param guid: The GUID of the policy
1226 :return: A string with the complete path to the policy folder
1229 guid = "{%s}" % guid
1230 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1234 def create_gpo_struct(policy_path):
1235 if not os.path.exists(policy_path):
1236 os.makedirs(policy_path, 0o775)
1237 f = open(os.path.join(policy_path, "GPT.INI"), 'w')
1239 f.write("[General]\r\nVersion=0")
1242 p = os.path.join(policy_path, "MACHINE")
1243 if not os.path.exists(p):
1244 os.makedirs(p, 0o775)
1245 p = os.path.join(policy_path, "USER")
1246 if not os.path.exists(p):
1247 os.makedirs(p, 0o775)
1250 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1251 """Create the default GPO for a domain
1253 :param sysvolpath: Physical path for the sysvol folder
1254 :param dnsdomain: DNS domain name of the AD domain
1255 :param policyguid: GUID of the default domain policy
1256 :param policyguid_dc: GUID of the default domain controler policy
1258 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1259 create_gpo_struct(policy_path)
1261 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1262 create_gpo_struct(policy_path)
1265 def setup_samdb(path, session_info, provision_backend, lp, names,
1266 logger, fill, serverrole, schema, am_rodc=False,
1267 plaintext_secrets=False, backend_store=None):
1268 """Setup a complete SAM Database.
1270 :note: This will wipe the main SAM database file!
1273 # Also wipes the database
1274 setup_samdb_partitions(path, logger=logger, lp=lp,
1275 provision_backend=provision_backend, session_info=session_info,
1276 names=names, serverrole=serverrole, plaintext_secrets=plaintext_secrets,
1277 backend_store=backend_store)
1279 # Load the database, but don's load the global schema and don't connect
1281 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1282 credentials=provision_backend.credentials, lp=lp,
1283 global_schema=False, am_rodc=am_rodc)
1285 logger.info("Pre-loading the Samba 4 and AD schema")
1287 # Load the schema from the one we computed earlier
1288 samdb.set_schema(schema, write_indices_and_attributes=False)
1290 # Set the NTDS settings DN manually - in order to have it already around
1291 # before the provisioned tree exists and we connect
1292 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1294 # And now we can connect to the DB - the schema won't be loaded from the
1298 except ldb.LdbError as e2:
1299 (num, string_error) = e2.args
1300 if (num == ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS):
1301 raise ProvisioningError("Permission denied connecting to %s, are you running as root?" % path)
1305 # But we have to give it one more kick to have it use the schema
1306 # during provision - it needs, now that it is connected, to write
1307 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1308 samdb.set_schema(schema, write_indices_and_attributes=True)
1313 def fill_samdb(samdb, lp, names, logger, policyguid,
1314 policyguid_dc, fill, adminpass, krbtgtpass, machinepass, dns_backend,
1315 dnspass, invocationid, ntdsguid, serverrole, am_rodc=False,
1316 dom_for_fun_level=None, schema=None, next_rid=None, dc_rid=None,
1317 backend_store=None):
1319 if next_rid is None:
1322 # Provision does not make much sense values larger than 1000000000
1323 # as the upper range of the rIDAvailablePool is 1073741823 and
1324 # we don't want to create a domain that cannot allocate rids.
1325 if next_rid < 1000 or next_rid > 1000000000:
1326 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1327 error += "the valid range is %u-%u. The default is %u." % (
1328 1000, 1000000000, 1000)
1329 raise ProvisioningError(error)
1331 # ATTENTION: Do NOT change these default values without discussion with the
1332 # team and/or release manager. They have a big impact on the whole program!
1333 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1335 if dom_for_fun_level is None:
1336 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
1338 if dom_for_fun_level > domainControllerFunctionality:
1339 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!")
1341 domainFunctionality = dom_for_fun_level
1342 forestFunctionality = dom_for_fun_level
1344 # Set the NTDS settings DN manually - in order to have it already around
1345 # before the provisioned tree exists and we connect
1346 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1348 # Set the domain functionality levels onto the database.
1349 # Various module (the password_hash module in particular) need
1350 # to know what level of AD we are emulating.
1352 # These will be fixed into the database via the database
1353 # modifictions below, but we need them set from the start.
1354 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1355 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1356 samdb.set_opaque_integer("domainControllerFunctionality",
1357 domainControllerFunctionality)
1359 samdb.set_domain_sid(str(names.domainsid))
1360 samdb.set_invocation_id(invocationid)
1362 logger.info("Adding DomainDN: %s" % names.domaindn)
1364 # impersonate domain admin
1365 admin_session_info = admin_session(lp, str(names.domainsid))
1366 samdb.set_session_info(admin_session_info)
1367 if names.domainguid is not None:
1368 domainguid_line = "objectGUID: %s\n-" % names.domainguid
1370 domainguid_line = ""
1372 descr = b64encode(get_domain_descriptor(names.domainsid)).decode('utf8')
1373 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1374 "DOMAINDN": names.domaindn,
1375 "DOMAINSID": str(names.domainsid),
1376 "DESCRIPTOR": descr,
1377 "DOMAINGUID": domainguid_line
1380 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1381 "DOMAINDN": names.domaindn,
1382 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1383 "NEXTRID": str(next_rid),
1384 "DEFAULTSITE": names.sitename,
1385 "CONFIGDN": names.configdn,
1386 "POLICYGUID": policyguid,
1387 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1388 "SAMBA_VERSION_STRING": version,
1389 "MIN_PWD_LENGTH": str(DEFAULT_MIN_PWD_LENGTH)
1392 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1393 if fill == FILL_FULL:
1394 logger.info("Adding configuration container")
1395 descr = b64encode(get_config_descriptor(names.domainsid)).decode('utf8')
1396 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1397 "CONFIGDN": names.configdn,
1398 "DESCRIPTOR": descr,
1401 # The LDIF here was created when the Schema object was constructed
1402 ignore_checks_oid = "local_oid:%s:0" % samba.dsdb.DSDB_CONTROL_SKIP_DUPLICATES_CHECK_OID
1403 logger.info("Setting up sam.ldb schema")
1404 samdb.add_ldif(schema.schema_dn_add,
1405 controls=["relax:0", ignore_checks_oid])
1406 samdb.modify_ldif(schema.schema_dn_modify,
1407 controls=[ignore_checks_oid])
1408 samdb.write_prefixes_from_schema()
1409 samdb.add_ldif(schema.schema_data, controls=["relax:0", ignore_checks_oid])
1410 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1411 {"SCHEMADN": names.schemadn},
1412 controls=["relax:0", ignore_checks_oid])
1414 # Now register this container in the root of the forest
1415 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1416 msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1419 samdb.invocation_id = invocationid
1421 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1422 if fill == FILL_FULL:
1423 logger.info("Setting up sam.ldb configuration data")
1425 partitions_descr = b64encode(get_config_partitions_descriptor(names.domainsid)).decode('utf8')
1426 sites_descr = b64encode(get_config_sites_descriptor(names.domainsid)).decode('utf8')
1427 ntdsquotas_descr = b64encode(get_config_ntds_quotas_descriptor(names.domainsid)).decode('utf8')
1428 protected1_descr = b64encode(get_config_delete_protected1_descriptor(names.domainsid)).decode('utf8')
1429 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid)).decode('utf8')
1430 protected2_descr = b64encode(get_config_delete_protected2_descriptor(names.domainsid)).decode('utf8')
1432 if "2008" in schema.base_schema:
1433 # exclude 2012-specific changes if we're using a 2008 schema
1438 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1439 "CONFIGDN": names.configdn,
1440 "NETBIOSNAME": names.netbiosname,
1441 "DEFAULTSITE": names.sitename,
1442 "DNSDOMAIN": names.dnsdomain,
1443 "DOMAIN": names.domain,
1444 "SCHEMADN": names.schemadn,
1445 "DOMAINDN": names.domaindn,
1446 "SERVERDN": names.serverdn,
1447 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1448 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1449 "NTDSQUOTAS_DESCRIPTOR": ntdsquotas_descr,
1450 "LOSTANDFOUND_DESCRIPTOR": protected1wd_descr,
1451 "SERVICES_DESCRIPTOR": protected1_descr,
1452 "PHYSICALLOCATIONS_DESCRIPTOR": protected1wd_descr,
1453 "FORESTUPDATES_DESCRIPTOR": protected1wd_descr,
1454 "EXTENDEDRIGHTS_DESCRIPTOR": protected2_descr,
1455 "PARTITIONS_DESCRIPTOR": partitions_descr,
1456 "SITES_DESCRIPTOR": sites_descr,
1459 setup_add_ldif(samdb, setup_path("extended-rights.ldif"), {
1460 "CONFIGDN": names.configdn,
1461 "INC2012" : incl_2012,
1464 logger.info("Setting up display specifiers")
1465 display_specifiers_ldif = read_ms_ldif(
1466 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1467 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1468 {"CONFIGDN": names.configdn})
1469 check_all_substituted(display_specifiers_ldif)
1470 samdb.add_ldif(display_specifiers_ldif)
1472 logger.info("Modifying display specifiers and extended rights")
1473 setup_modify_ldif(samdb,
1474 setup_path("provision_configuration_modify.ldif"), {
1475 "CONFIGDN": names.configdn,
1476 "DISPLAYSPECIFIERS_DESCRIPTOR": protected2_descr
1479 logger.info("Adding users container")
1480 users_desc = b64encode(get_domain_users_descriptor(names.domainsid)).decode('utf8')
1481 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1482 "DOMAINDN": names.domaindn,
1483 "USERS_DESCRIPTOR": users_desc
1485 logger.info("Modifying users container")
1486 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1487 "DOMAINDN": names.domaindn})
1488 logger.info("Adding computers container")
1489 computers_desc = b64encode(get_domain_computers_descriptor(names.domainsid)).decode('utf8')
1490 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1491 "DOMAINDN": names.domaindn,
1492 "COMPUTERS_DESCRIPTOR": computers_desc
1494 logger.info("Modifying computers container")
1495 setup_modify_ldif(samdb,
1496 setup_path("provision_computers_modify.ldif"), {
1497 "DOMAINDN": names.domaindn})
1498 logger.info("Setting up sam.ldb data")
1499 infrastructure_desc = b64encode(get_domain_infrastructure_descriptor(names.domainsid)).decode('utf8')
1500 lostandfound_desc = b64encode(get_domain_delete_protected2_descriptor(names.domainsid)).decode('utf8')
1501 system_desc = b64encode(get_domain_delete_protected1_descriptor(names.domainsid)).decode('utf8')
1502 builtin_desc = b64encode(get_domain_builtin_descriptor(names.domainsid)).decode('utf8')
1503 controllers_desc = b64encode(get_domain_controllers_descriptor(names.domainsid)).decode('utf8')
1504 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1505 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1506 "DOMAINDN": names.domaindn,
1507 "NETBIOSNAME": names.netbiosname,
1508 "DEFAULTSITE": names.sitename,
1509 "CONFIGDN": names.configdn,
1510 "SERVERDN": names.serverdn,
1511 "RIDAVAILABLESTART": str(next_rid + 600),
1512 "POLICYGUID_DC": policyguid_dc,
1513 "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc,
1514 "LOSTANDFOUND_DESCRIPTOR": lostandfound_desc,
1515 "SYSTEM_DESCRIPTOR": system_desc,
1516 "BUILTIN_DESCRIPTOR": builtin_desc,
1517 "DOMAIN_CONTROLLERS_DESCRIPTOR": controllers_desc,
1520 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1521 if fill == FILL_FULL:
1522 managedservice_descr = b64encode(get_managed_service_accounts_descriptor(names.domainsid)).decode('utf8')
1523 setup_modify_ldif(samdb,
1524 setup_path("provision_configuration_references.ldif"), {
1525 "CONFIGDN": names.configdn,
1526 "SCHEMADN": names.schemadn})
1528 logger.info("Setting up well known security principals")
1529 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid)).decode('utf8')
1530 setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1531 "CONFIGDN": names.configdn,
1532 "WELLKNOWNPRINCIPALS_DESCRIPTOR": protected1wd_descr,
1533 }, controls=["relax:0", "provision:0"])
1535 if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1536 setup_modify_ldif(samdb,
1537 setup_path("provision_basedn_references.ldif"), {
1538 "DOMAINDN": names.domaindn,
1539 "MANAGEDSERVICE_DESCRIPTOR": managedservice_descr
1542 logger.info("Setting up sam.ldb users and groups")
1543 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1544 "DOMAINDN": names.domaindn,
1545 "DOMAINSID": str(names.domainsid),
1546 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')).decode('utf8'),
1547 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le')).decode('utf8')
1548 }, controls=["relax:0", "provision:0"])
1550 logger.info("Setting up self join")
1551 setup_self_join(samdb, admin_session_info, names=names, fill=fill,
1552 invocationid=invocationid,
1553 dns_backend=dns_backend,
1555 machinepass=machinepass,
1556 domainsid=names.domainsid,
1559 policyguid=policyguid,
1560 policyguid_dc=policyguid_dc,
1561 domainControllerFunctionality=domainControllerFunctionality,
1564 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1565 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1566 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1567 assert isinstance(names.ntdsguid, str)
1572 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1573 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)"
1574 SYSVOL_SERVICE="sysvol"
1576 def set_dir_acl(path, acl, lp, domsid, use_ntvfs, passdb, service=SYSVOL_SERVICE):
1577 setntacl(lp, path, acl, domsid, use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1578 for root, dirs, files in os.walk(path, topdown=False):
1580 setntacl(lp, os.path.join(root, name), acl, domsid,
1581 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1583 setntacl(lp, os.path.join(root, name), acl, domsid,
1584 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1587 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb):
1588 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1591 :param sysvol: Physical path for the sysvol folder
1592 :param dnsdomain: The DNS name of the domain
1593 :param domainsid: The SID of the domain
1594 :param domaindn: The DN of the domain (ie. DC=...)
1595 :param samdb: An LDB object on the SAM db
1596 :param lp: an LP object
1599 # Set ACL for GPO root folder
1600 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1601 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid),
1602 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=SYSVOL_SERVICE)
1604 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1605 attrs=["cn", "nTSecurityDescriptor"],
1606 expression="", scope=ldb.SCOPE_ONELEVEL)
1609 acl = ndr_unpack(security.descriptor,
1610 str(policy["nTSecurityDescriptor"])).as_sddl()
1611 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1612 set_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1613 str(domainsid), use_ntvfs,
1617 def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain,
1618 domaindn, lp, use_ntvfs):
1619 """Set the ACL for the sysvol share and the subfolders
1621 :param samdb: An LDB object on the SAM db
1622 :param netlogon: Physical path for the netlogon folder
1623 :param sysvol: Physical path for the sysvol folder
1624 :param uid: The UID of the "Administrator" user
1625 :param gid: The GID of the "Domain adminstrators" group
1626 :param domainsid: The SID of the domain
1627 :param dnsdomain: The DNS name of the domain
1628 :param domaindn: The DN of the domain (ie. DC=...)
1633 s3conf = s3param.get_context()
1634 s3conf.load(lp.configfile)
1636 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(sysvol))
1639 smbd.set_simple_acl(file.name, 0o755, gid)
1641 if not smbd.have_posix_acls():
1642 # This clue is only strictly correct for RPM and
1643 # Debian-like Linux systems, but hopefully other users
1644 # will get enough clue from it.
1645 raise ProvisioningError("Samba was compiled without the posix ACL support that s3fs requires. "
1646 "Try installing libacl1-dev or libacl-devel, then re-run configure and make.")
1648 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. "
1649 "Try the mounting the filesystem with the 'acl' option.")
1651 smbd.chown(file.name, uid, gid)
1653 raise ProvisioningError("Unable to chown a file on your filesystem. "
1654 "You may not be running provision as root.")
1658 # This will ensure that the smbd code we are running when setting ACLs
1659 # is initialised with the smb.conf
1660 s3conf = s3param.get_context()
1661 s3conf.load(lp.configfile)
1662 # ensure we are using the right samba_dsdb passdb backend, no matter what
1663 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1664 passdb.reload_static_pdb()
1666 # ensure that we init the samba_dsdb backend, so the domain sid is
1667 # marked in secrets.tdb
1668 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1670 # now ensure everything matches correctly, to avoid wierd issues
1671 if passdb.get_global_sam_sid() != domainsid:
1672 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))
1674 domain_info = s4_passdb.domain_info()
1675 if domain_info["dom_sid"] != domainsid:
1676 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))
1678 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1679 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()))
1684 os.chown(sysvol, -1, gid)
1690 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1691 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs,
1692 skip_invalid_chown=True, passdb=s4_passdb,
1693 service=SYSVOL_SERVICE)
1694 for root, dirs, files in os.walk(sysvol, topdown=False):
1696 if use_ntvfs and canchown:
1697 os.chown(os.path.join(root, name), -1, gid)
1698 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid),
1699 use_ntvfs=use_ntvfs, skip_invalid_chown=True,
1700 passdb=s4_passdb, service=SYSVOL_SERVICE)
1702 if use_ntvfs and canchown:
1703 os.chown(os.path.join(root, name), -1, gid)
1704 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid),
1705 use_ntvfs=use_ntvfs, skip_invalid_chown=True,
1706 passdb=s4_passdb, service=SYSVOL_SERVICE)
1708 # Set acls on Policy folder and policies folders
1709 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb=s4_passdb)
1711 def acl_type(direct_db_access):
1712 if direct_db_access:
1717 def check_dir_acl(path, acl, lp, domainsid, direct_db_access):
1718 fsacl = getntacl(lp, path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1719 fsacl_sddl = fsacl.as_sddl(domainsid)
1720 if fsacl_sddl != acl:
1721 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))
1723 for root, dirs, files in os.walk(path, topdown=False):
1725 fsacl = getntacl(lp, os.path.join(root, name),
1726 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1728 raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1729 fsacl_sddl = fsacl.as_sddl(domainsid)
1730 if fsacl_sddl != acl:
1731 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))
1734 fsacl = getntacl(lp, os.path.join(root, name),
1735 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1737 raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1738 fsacl_sddl = fsacl.as_sddl(domainsid)
1739 if fsacl_sddl != acl:
1740 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))
1743 def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1745 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1748 :param sysvol: Physical path for the sysvol folder
1749 :param dnsdomain: The DNS name of the domain
1750 :param domainsid: The SID of the domain
1751 :param domaindn: The DN of the domain (ie. DC=...)
1752 :param samdb: An LDB object on the SAM db
1753 :param lp: an LP object
1756 # Set ACL for GPO root folder
1757 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1758 fsacl = getntacl(lp, root_policy_path,
1759 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1761 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access), root_policy_path))
1762 fsacl_sddl = fsacl.as_sddl(domainsid)
1763 if fsacl_sddl != POLICIES_ACL:
1764 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))
1765 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1766 attrs=["cn", "nTSecurityDescriptor"],
1767 expression="", scope=ldb.SCOPE_ONELEVEL)
1770 acl = ndr_unpack(security.descriptor,
1771 str(policy["nTSecurityDescriptor"])).as_sddl()
1772 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1773 check_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1774 domainsid, direct_db_access)
1777 def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn,
1779 """Set the ACL for the sysvol share and the subfolders
1781 :param samdb: An LDB object on the SAM db
1782 :param netlogon: Physical path for the netlogon folder
1783 :param sysvol: Physical path for the sysvol folder
1784 :param uid: The UID of the "Administrator" user
1785 :param gid: The GID of the "Domain adminstrators" group
1786 :param domainsid: The SID of the domain
1787 :param dnsdomain: The DNS name of the domain
1788 :param domaindn: The DN of the domain (ie. DC=...)
1791 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1792 s3conf = s3param.get_context()
1793 s3conf.load(lp.configfile)
1794 # ensure we are using the right samba_dsdb passdb backend, no matter what
1795 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1796 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1797 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1799 # now ensure everything matches correctly, to avoid wierd issues
1800 if passdb.get_global_sam_sid() != domainsid:
1801 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))
1803 domain_info = s4_passdb.domain_info()
1804 if domain_info["dom_sid"] != domainsid:
1805 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))
1807 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1808 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()))
1810 # Ensure we can read this directly, and via the smbd VFS
1811 for direct_db_access in [True, False]:
1812 # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1813 for dir_path in [os.path.join(sysvol, dnsdomain), netlogon]:
1814 fsacl = getntacl(lp, dir_path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1816 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access), dir_path))
1817 fsacl_sddl = fsacl.as_sddl(domainsid)
1818 if fsacl_sddl != SYSVOL_ACL:
1819 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))
1821 # Check acls on Policy folder and policies folders
1822 check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1826 def interface_ips_v4(lp):
1827 """return only IPv4 IPs"""
1828 ips = samba.interface_ips(lp, False)
1831 if i.find(':') == -1:
1836 def interface_ips_v6(lp):
1837 """return only IPv6 IPs"""
1838 ips = samba.interface_ips(lp, False)
1841 if i.find(':') != -1:
1846 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1848 targetdir=None, samdb_fill=FILL_FULL,
1849 hostip=None, hostip6=None,
1850 next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1851 domainguid=None, policyguid=None, policyguid_dc=None,
1852 invocationid=None, machinepass=None, ntdsguid=None,
1853 dns_backend=None, dnspass=None,
1854 serverrole=None, dom_for_fun_level=None,
1855 am_rodc=False, lp=None, use_ntvfs=False,
1856 skip_sysvolacl=False, backend_store=None):
1857 # create/adapt the group policy GUIDs
1858 # Default GUID for default policy are described at
1859 # "How Core Group Policy Works"
1860 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1861 if policyguid is None:
1862 policyguid = DEFAULT_POLICY_GUID
1863 policyguid = policyguid.upper()
1864 if policyguid_dc is None:
1865 policyguid_dc = DEFAULT_DC_POLICY_GUID
1866 policyguid_dc = policyguid_dc.upper()
1868 if invocationid is None:
1869 invocationid = str(uuid.uuid4())
1871 if krbtgtpass is None:
1872 krbtgtpass = samba.generate_random_machine_password(128, 255)
1873 if machinepass is None:
1874 machinepass = samba.generate_random_machine_password(128, 255)
1876 dnspass = samba.generate_random_password(128, 255)
1878 samdb.transaction_start()
1880 samdb = fill_samdb(samdb, lp, names, logger=logger,
1882 policyguid=policyguid, policyguid_dc=policyguid_dc,
1883 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1884 invocationid=invocationid, machinepass=machinepass,
1885 dns_backend=dns_backend, dnspass=dnspass,
1886 ntdsguid=ntdsguid, serverrole=serverrole,
1887 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1888 next_rid=next_rid, dc_rid=dc_rid,
1889 backend_store=backend_store)
1891 # Set up group policies (domain policy and domain controller
1893 if serverrole == "active directory domain controller":
1894 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1897 samdb.transaction_cancel()
1900 samdb.transaction_commit()
1902 if serverrole == "active directory domain controller":
1903 # Continue setting up sysvol for GPO. This appears to require being
1904 # outside a transaction.
1905 if not skip_sysvolacl:
1906 setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.root_uid,
1907 paths.root_gid, names.domainsid, names.dnsdomain,
1908 names.domaindn, lp, use_ntvfs)
1910 logger.info("Setting acl on sysvol skipped")
1912 secretsdb_self_join(secrets_ldb, domain=names.domain,
1913 realm=names.realm, dnsdomain=names.dnsdomain,
1914 netbiosname=names.netbiosname, domainsid=names.domainsid,
1915 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1917 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1918 # In future, this might be determined from some configuration
1919 kerberos_enctypes = str(ENC_ALL_TYPES)
1922 msg = ldb.Message(ldb.Dn(samdb,
1923 samdb.searchone("distinguishedName",
1924 expression="samAccountName=%s$" % names.netbiosname,
1925 scope=ldb.SCOPE_SUBTREE).decode('utf8')))
1926 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1927 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1928 name="msDS-SupportedEncryptionTypes")
1930 except ldb.LdbError as e:
1931 (enum, estr) = e.args
1932 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1933 # It might be that this attribute does not exist in this schema
1936 setup_ad_dns(samdb, secrets_ldb, names, paths, lp, logger,
1937 hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
1938 dnspass=dnspass, os_level=dom_for_fun_level,
1939 targetdir=targetdir, fill_level=samdb_fill,
1940 backend_store=backend_store)
1942 domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1943 attribute="objectGUID")
1944 assert isinstance(domainguid, str)
1946 lastProvisionUSNs = get_last_provision_usn(samdb)
1947 maxUSN = get_max_usn(samdb, str(names.rootdn))
1948 if lastProvisionUSNs is not None:
1949 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1951 set_provision_usn(samdb, 0, maxUSN, invocationid)
1953 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1954 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1955 { 'NTDSGUID' : names.ntdsguid })
1957 # fix any dangling GUIDs from the provision
1958 logger.info("Fixing provision GUIDs")
1959 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
1961 samdb.transaction_start()
1963 # a small number of GUIDs are missing because of ordering issues in the
1965 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1966 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1967 scope=ldb.SCOPE_BASE,
1968 attrs=['defaultObjectCategory'])
1969 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1970 scope=ldb.SCOPE_ONELEVEL,
1971 attrs=['ipsecOwnersReference',
1972 'ipsecFilterReference',
1973 'ipsecISAKMPReference',
1974 'ipsecNegotiationPolicyReference',
1975 'ipsecNFAReference'])
1976 if chk.check_database(DN=names.schemadn, scope=ldb.SCOPE_SUBTREE,
1977 attrs=['attributeId', 'governsId']) != 0:
1978 raise ProvisioningError("Duplicate attributeId or governsId in schema. Must be fixed manually!!")
1980 samdb.transaction_cancel()
1983 samdb.transaction_commit()
1987 "ROLE_STANDALONE": "standalone server",
1988 "ROLE_DOMAIN_MEMBER": "member server",
1989 "ROLE_DOMAIN_BDC": "active directory domain controller",
1990 "ROLE_DOMAIN_PDC": "active directory domain controller",
1991 "dc": "active directory domain controller",
1992 "member": "member server",
1993 "domain controller": "active directory domain controller",
1994 "active directory domain controller": "active directory domain controller",
1995 "member server": "member server",
1996 "standalone": "standalone server",
1997 "standalone server": "standalone server",
2001 def sanitize_server_role(role):
2002 """Sanitize a server role name.
2004 :param role: Server role
2005 :raise ValueError: If the role can not be interpreted
2006 :return: Sanitized server role (one of "member server",
2007 "active directory domain controller", "standalone server")
2010 return _ROLES_MAP[role]
2012 raise ValueError(role)
2015 def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain,
2017 """Create AD entries for the fake ypserver.
2019 This is needed for being able to manipulate posix attrs via ADUC.
2021 samdb.transaction_start()
2023 logger.info("Setting up fake yp server settings")
2024 setup_add_ldif(samdb, setup_path("ypServ30.ldif"), {
2025 "DOMAINDN": domaindn,
2026 "NETBIOSNAME": netbiosname,
2027 "NISDOMAIN": nisdomain,
2030 samdb.transaction_cancel()
2033 samdb.transaction_commit()
2035 def directory_create_or_exists(path, mode=0o755):
2036 if not os.path.exists(path):
2038 os.mkdir(path, mode)
2039 except OSError as e:
2040 if e.errno in [errno.EEXIST]:
2043 raise ProvisioningError("Failed to create directory %s: %s" % (path, e.strerror))
2045 def provision(logger, session_info, smbconf=None,
2046 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
2047 domaindn=None, schemadn=None, configdn=None, serverdn=None,
2048 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
2049 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None,
2050 krbtgtpass=None, domainguid=None, policyguid=None, policyguid_dc=None,
2051 dns_backend=None, dns_forwarder=None, dnspass=None,
2052 invocationid=None, machinepass=None, ntdsguid=None,
2053 root=None, nobody=None, users=None, backup=None, aci=None,
2054 serverrole=None, dom_for_fun_level=None, backend_type=None,
2055 sitename=None, ol_mmr_urls=None, ol_olc=None, slapd_path=None,
2056 useeadb=False, am_rodc=False, lp=None, use_ntvfs=False,
2057 use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True,
2058 ldap_backend_forced_uri=None, nosync=False, ldap_dryrun_mode=False,
2059 ldap_backend_extra_port=None, base_schema=None,
2060 plaintext_secrets=False, backend_store=None):
2063 :note: caution, this wipes all existing data!
2067 serverrole = sanitize_server_role(serverrole)
2069 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole)
2071 if ldapadminpass is None:
2072 # Make a new, random password between Samba and it's LDAP server
2073 ldapadminpass = samba.generate_random_password(128, 255)
2075 if backend_type is None:
2076 backend_type = "ldb"
2077 if backend_store is None:
2078 backend_store = get_default_backend_store()
2080 if domainsid is None:
2081 domainsid = security.random_sid()
2083 root_uid = findnss_uid([root or "root"])
2084 nobody_uid = findnss_uid([nobody or "nobody"])
2085 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
2086 root_gid = pwd.getpwuid(root_uid).pw_gid
2089 bind_gid = findnss_gid(["bind", "named"])
2093 if targetdir is not None:
2094 smbconf = os.path.join(targetdir, "etc", "smb.conf")
2095 elif smbconf is None:
2096 smbconf = samba.param.default_path()
2097 if not os.path.exists(os.path.dirname(smbconf)):
2098 os.makedirs(os.path.dirname(smbconf))
2100 server_services = []
2103 global_param["idmap_ldb:use rfc2307"] = ["yes"]
2105 if dns_backend != "SAMBA_INTERNAL":
2106 server_services.append("-dns")
2108 if dns_forwarder is not None:
2109 global_param["dns forwarder"] = [dns_forwarder]
2112 server_services.append("+smb")
2113 server_services.append("-s3fs")
2114 global_param["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
2116 if len(server_services) > 0:
2117 global_param["server services"] = server_services
2119 # only install a new smb.conf if there isn't one there already
2120 if os.path.exists(smbconf):
2121 # if Samba Team members can't figure out the weird errors
2122 # loading an empty smb.conf gives, then we need to be smarter.
2123 # Pretend it just didn't exist --abartlet
2124 f = open(smbconf, 'r')
2126 data = f.read().lstrip()
2129 if data is None or data == "":
2130 make_smbconf(smbconf, hostname, domain, realm,
2131 targetdir, serverrole=serverrole,
2132 eadb=useeadb, use_ntvfs=use_ntvfs,
2133 lp=lp, global_param=global_param)
2135 make_smbconf(smbconf, hostname, domain, realm, targetdir,
2136 serverrole=serverrole,
2137 eadb=useeadb, use_ntvfs=use_ntvfs, lp=lp, global_param=global_param)
2140 lp = samba.param.LoadParm()
2142 names = guess_names(lp=lp, hostname=hostname, domain=domain,
2143 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
2144 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
2145 sitename=sitename, rootdn=rootdn, domain_names_forced=(samdb_fill == FILL_DRS))
2146 paths = provision_paths_from_lp(lp, names.dnsdomain)
2148 paths.bind_gid = bind_gid
2149 paths.root_uid = root_uid;
2150 paths.root_gid = root_gid
2153 logger.info("Looking up IPv4 addresses")
2154 hostips = interface_ips_v4(lp)
2155 if len(hostips) > 0:
2157 if len(hostips) > 1:
2158 logger.warning("More than one IPv4 address found. Using %s",
2160 if hostip == "127.0.0.1":
2163 logger.warning("No IPv4 address will be assigned")
2166 logger.info("Looking up IPv6 addresses")
2167 hostips = interface_ips_v6(lp)
2169 hostip6 = hostips[0]
2170 if len(hostips) > 1:
2171 logger.warning("More than one IPv6 address found. Using %s", hostip6)
2173 logger.warning("No IPv6 address will be assigned")
2175 names.hostip = hostip
2176 names.hostip6 = hostip6
2177 names.domainguid = domainguid
2178 names.domainsid = domainsid
2179 names.forestsid = domainsid
2181 if serverrole is None:
2182 serverrole = lp.get("server role")
2184 directory_create_or_exists(paths.private_dir, 0o700)
2185 directory_create_or_exists(paths.binddns_dir, 0o770)
2186 directory_create_or_exists(os.path.join(paths.private_dir, "tls"))
2187 directory_create_or_exists(paths.state_dir)
2188 if not plaintext_secrets:
2189 setup_encrypted_secrets_key(paths.encrypted_secrets_key_path)
2191 if paths.sysvol and not os.path.exists(paths.sysvol):
2192 os.makedirs(paths.sysvol, 0o775)
2194 ldapi_url = "ldapi://%s" % urllib_quote(paths.s4_ldapi_path, safe="")
2196 schema = Schema(domainsid, invocationid=invocationid,
2197 schemadn=names.schemadn, base_schema=base_schema)
2199 if backend_type == "ldb":
2200 provision_backend = LDBBackend(backend_type, paths=paths,
2202 names=names, logger=logger)
2203 elif backend_type == "existing":
2204 # If support for this is ever added back, then the URI will need to be
2206 provision_backend = ExistingBackend(backend_type, paths=paths,
2208 names=names, logger=logger,
2209 ldap_backend_forced_uri=ldap_backend_forced_uri)
2210 elif backend_type == "fedora-ds":
2211 provision_backend = FDSBackend(backend_type, paths=paths,
2213 names=names, logger=logger, domainsid=domainsid,
2214 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2215 slapd_path=slapd_path,
2217 elif backend_type == "openldap":
2218 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
2220 names=names, logger=logger, domainsid=domainsid,
2221 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2222 slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls,
2223 ldap_backend_extra_port=ldap_backend_extra_port,
2224 ldap_dryrun_mode=ldap_dryrun_mode, nosync=nosync,
2225 ldap_backend_forced_uri=ldap_backend_forced_uri)
2227 raise ValueError("Unknown LDAP backend type selected")
2229 provision_backend.init()
2230 provision_backend.start()
2232 # only install a new shares config db if there is none
2233 if not os.path.exists(paths.shareconf):
2234 logger.info("Setting up share.ldb")
2235 share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
2236 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
2238 logger.info("Setting up secrets.ldb")
2239 secrets_ldb = setup_secretsdb(paths,
2240 session_info=session_info,
2241 backend_credentials=provision_backend.credentials, lp=lp)
2244 logger.info("Setting up the registry")
2245 setup_registry(paths.hklm, session_info, lp=lp)
2247 logger.info("Setting up the privileges database")
2248 setup_privileges(paths.privilege, session_info, lp=lp)
2250 logger.info("Setting up idmap db")
2251 idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
2253 setup_name_mappings(idmap, sid=str(domainsid),
2254 root_uid=root_uid, nobody_uid=nobody_uid,
2255 users_gid=users_gid, root_gid=root_gid)
2257 logger.info("Setting up SAM db")
2258 samdb = setup_samdb(paths.samdb, session_info,
2259 provision_backend, lp, names, logger=logger,
2260 serverrole=serverrole,
2261 schema=schema, fill=samdb_fill, am_rodc=am_rodc,
2262 plaintext_secrets=plaintext_secrets,
2263 backend_store=backend_store)
2265 if serverrole == "active directory domain controller":
2266 if paths.netlogon is None:
2267 raise MissingShareError("netlogon", paths.smbconf)
2269 if paths.sysvol is None:
2270 raise MissingShareError("sysvol", paths.smbconf)
2272 if not os.path.isdir(paths.netlogon):
2273 os.makedirs(paths.netlogon, 0o755)
2275 if adminpass is None:
2276 adminpass = samba.generate_random_password(12, 32)
2277 adminpass_generated = True
2279 adminpass = unicode(adminpass, 'utf-8')
2280 adminpass_generated = False
2282 if samdb_fill == FILL_FULL:
2283 provision_fill(samdb, secrets_ldb, logger, names, paths,
2284 schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
2285 hostip=hostip, hostip6=hostip6,
2286 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
2287 krbtgtpass=krbtgtpass,
2288 policyguid=policyguid, policyguid_dc=policyguid_dc,
2289 invocationid=invocationid, machinepass=machinepass,
2290 ntdsguid=ntdsguid, dns_backend=dns_backend,
2291 dnspass=dnspass, serverrole=serverrole,
2292 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
2293 lp=lp, use_ntvfs=use_ntvfs,
2294 skip_sysvolacl=skip_sysvolacl,
2295 backend_store=backend_store)
2297 if not is_heimdal_built():
2298 create_kdc_conf(paths.kdcconf, realm, domain, os.path.dirname(lp.get("log file")))
2299 logger.info("The Kerberos KDC configuration for Samba AD is "
2300 "located at %s", paths.kdcconf)
2302 create_krb5_conf(paths.krb5conf,
2303 dnsdomain=names.dnsdomain, hostname=names.hostname,
2305 logger.info("A Kerberos configuration suitable for Samba AD has been "
2306 "generated at %s", paths.krb5conf)
2307 logger.info("Merge the contents of this file with your system "
2308 "krb5.conf or replace it with this one. Do not create a "
2311 if serverrole == "active directory domain controller":
2312 create_dns_update_list(lp, logger, paths)
2314 backend_result = provision_backend.post_setup()
2315 provision_backend.shutdown()
2318 secrets_ldb.transaction_cancel()
2321 # Now commit the secrets.ldb to disk
2322 secrets_ldb.transaction_commit()
2324 # the commit creates the dns.keytab in the private directory
2325 private_dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
2326 bind_dns_keytab_path = os.path.join(paths.binddns_dir, paths.dns_keytab)
2328 if os.path.isfile(private_dns_keytab_path):
2329 if os.path.isfile(bind_dns_keytab_path):
2331 os.unlink(bind_dns_keytab_path)
2332 except OSError as e:
2333 logger.error("Failed to remove %s: %s" %
2334 (bind_dns_keytab_path, e.strerror))
2336 # link the dns.keytab to the bind-dns directory
2338 os.link(private_dns_keytab_path, bind_dns_keytab_path)
2339 except OSError as e:
2340 logger.error("Failed to create link %s -> %s: %s" %
2341 (private_dns_keytab_path, bind_dns_keytab_path, e.strerror))
2343 # chown the dns.keytab in the bind-dns directory
2344 if paths.bind_gid is not None:
2346 os.chmod(paths.binddns_dir, 0o770)
2347 os.chown(paths.binddns_dir, -1, paths.bind_gid)
2349 if not os.environ.has_key('SAMBA_SELFTEST'):
2350 logger.info("Failed to chown %s to bind gid %u",
2351 paths.binddns_dir, paths.bind_gid)
2354 os.chmod(bind_dns_keytab_path, 0o640)
2355 os.chown(bind_dns_keytab_path, -1, paths.bind_gid)
2357 if not os.environ.has_key('SAMBA_SELFTEST'):
2358 logger.info("Failed to chown %s to bind gid %u",
2359 bind_dns_keytab_path, paths.bind_gid)
2361 result = ProvisionResult()
2362 result.server_role = serverrole
2363 result.domaindn = domaindn
2364 result.paths = paths
2365 result.names = names
2367 result.samdb = samdb
2368 result.idmap = idmap
2369 result.domainsid = str(domainsid)
2371 if samdb_fill == FILL_FULL:
2372 result.adminpass_generated = adminpass_generated
2373 result.adminpass = adminpass
2375 result.adminpass_generated = False
2376 result.adminpass = None
2378 result.backend_result = backend_result
2381 provision_fake_ypserver(logger=logger, samdb=samdb,
2382 domaindn=names.domaindn, netbiosname=names.netbiosname,
2383 nisdomain=names.domain.lower(), maxuid=maxuid, maxgid=maxgid)
2388 def provision_become_dc(smbconf=None, targetdir=None,
2389 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
2390 serverdn=None, domain=None, hostname=None, domainsid=None,
2391 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
2392 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
2393 dns_backend=None, root=None, nobody=None, users=None,
2394 backup=None, serverrole=None, ldap_backend=None,
2395 ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False):
2397 logger = logging.getLogger("provision")
2398 samba.set_debug_level(debuglevel)
2400 res = provision(logger, system_session(),
2401 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
2402 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
2403 configdn=configdn, serverdn=serverdn, domain=domain,
2404 hostname=hostname, hostip=None, domainsid=domainsid,
2405 machinepass=machinepass,
2406 serverrole="active directory domain controller",
2407 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass,
2408 use_ntvfs=use_ntvfs)
2409 res.lp.set("debuglevel", str(debuglevel))
2413 def create_krb5_conf(path, dnsdomain, hostname, realm):
2414 """Write out a file containing a valid krb5.conf file
2416 :param path: Path of the new krb5.conf file.
2417 :param dnsdomain: DNS Domain name
2418 :param hostname: Local hostname
2419 :param realm: Realm name
2421 setup_file(setup_path("krb5.conf"), path, {
2422 "DNSDOMAIN": dnsdomain,
2423 "HOSTNAME": hostname,
2428 class ProvisioningError(Exception):
2429 """A generic provision error."""
2431 def __init__(self, value):
2435 return "ProvisioningError: " + self.value
2438 class InvalidNetbiosName(Exception):
2439 """A specified name was not a valid NetBIOS name."""
2441 def __init__(self, name):
2442 super(InvalidNetbiosName, self).__init__(
2443 "The name '%r' is not a valid NetBIOS name" % name)
2446 class MissingShareError(ProvisioningError):
2448 def __init__(self, name, smbconf):
2449 super(MissingShareError, self).__init__(
2450 "Existing smb.conf does not have a [%s] share, but you are "
2451 "configuring a DC. Please remove %s or add the share manually." %