1 # Unix SMB/CIFS implementation.
2 # backend code for provisioning a Samba AD server
4 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2012
5 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
6 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
8 # Based on the original in EJS:
9 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 3 of the License, or
14 # (at your option) any later version.
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 """Functions for setting up a Samba configuration."""
27 __docformat__ = "restructuredText"
29 from base64 import b64encode
47 from samba.auth import system_session, admin_session
49 from samba.samba3 import smbd, passdb
50 from samba.samba3 import param as s3param
51 from samba.dsdb import DS_DOMAIN_FUNCTION_2000
55 check_all_substituted,
56 is_valid_netbios_char,
63 from samba.dcerpc import security, misc
64 from samba.dcerpc.misc import (
68 from samba.dsdb import (
69 DS_DOMAIN_FUNCTION_2003,
70 DS_DOMAIN_FUNCTION_2008_R2,
73 from samba.idmap import IDmapDB
74 from samba.ms_display_specifiers import read_ms_ldif
75 from samba.ntacls import setntacl, getntacl, dsacl2fsacl
76 from samba.ndr import ndr_pack, ndr_unpack
77 from samba.provision.backend import (
83 from samba.descriptor import (
85 get_config_descriptor,
86 get_config_partitions_descriptor,
87 get_config_sites_descriptor,
88 get_config_ntds_quotas_descriptor,
89 get_config_delete_protected1_descriptor,
90 get_config_delete_protected1wd_descriptor,
91 get_config_delete_protected2_descriptor,
92 get_domain_descriptor,
93 get_domain_infrastructure_descriptor,
94 get_domain_builtin_descriptor,
95 get_domain_computers_descriptor,
96 get_domain_users_descriptor,
97 get_domain_controllers_descriptor,
98 get_domain_delete_protected1_descriptor,
99 get_domain_delete_protected2_descriptor,
100 get_dns_partition_descriptor,
101 get_dns_forest_microsoft_dns_descriptor,
102 get_dns_domain_microsoft_dns_descriptor,
103 get_managed_service_accounts_descriptor,
105 from samba.provision.common import (
114 from samba.provision.sambadns import (
117 create_dns_update_list
121 import samba.registry
122 from samba.schema import Schema
123 from samba.samdb import SamDB
124 from samba.dbchecker import dbcheck
125 from samba.provision.kerberos import create_kdc_conf
127 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
128 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04FB984F9"
129 DEFAULTSITE = "Default-First-Site-Name"
130 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
132 DEFAULT_MIN_PWD_LENGTH = 7
135 class ProvisionPaths(object):
138 self.shareconf = None
149 self.dns_keytab = None
152 self.private_dir = None
153 self.binddns_dir = None
154 self.state_dir = None
157 class ProvisionNames(object):
165 self.dnsforestdn = None
166 self.dnsdomaindn = None
167 self.ldapmanagerdn = None
168 self.dnsdomain = None
170 self.netbiosname = None
175 self.domainsid = None
176 self.forestsid = None
177 self.domainguid = None
181 def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf,
183 """Get key provision parameters (realm, domain, ...) from a given provision
185 :param samdb: An LDB object connected to the sam.ldb file
186 :param secretsdb: An LDB object connected to the secrets.ldb file
187 :param idmapdb: An LDB object connected to the idmap.ldb file
188 :param paths: A list of path to provision object
189 :param smbconf: Path to the smb.conf file
190 :param lp: A LoadParm object
191 :return: A list of key provision parameters
193 names = ProvisionNames()
194 names.adminpass = None
196 # NT domain, kerberos realm, root dn, domain dn, domain dns name
197 names.domain = string.upper(lp.get("workgroup"))
198 names.realm = lp.get("realm")
199 names.dnsdomain = names.realm.lower()
200 basedn = samba.dn_from_dns_name(names.dnsdomain)
201 names.realm = string.upper(names.realm)
203 # Get the netbiosname first (could be obtained from smb.conf in theory)
204 res = secretsdb.search(expression="(flatname=%s)" %
205 names.domain,base="CN=Primary Domains",
206 scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"])
207 names.netbiosname = str(res[0]["sAMAccountName"]).replace("$","")
209 names.smbconf = smbconf
211 # That's a bit simplistic but it's ok as long as we have only 3
213 current = samdb.search(expression="(objectClass=*)",
214 base="", scope=ldb.SCOPE_BASE,
215 attrs=["defaultNamingContext", "schemaNamingContext",
216 "configurationNamingContext","rootDomainNamingContext",
219 names.configdn = current[0]["configurationNamingContext"][0]
220 names.schemadn = current[0]["schemaNamingContext"][0]
221 if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb,
222 current[0]["defaultNamingContext"][0]))):
223 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
224 "is not the same ..." % (paths.samdb,
225 str(current[0]["defaultNamingContext"][0]),
226 paths.smbconf, basedn)))
228 names.domaindn=current[0]["defaultNamingContext"][0]
229 names.rootdn=current[0]["rootDomainNamingContext"][0]
230 names.ncs=current[0]["namingContexts"]
231 names.dnsforestdn = None
232 names.dnsdomaindn = None
234 for i in range(0, len(names.ncs)):
237 dnsforestdn = "DC=ForestDnsZones,%s" % (str(names.rootdn))
238 if nc == dnsforestdn:
239 names.dnsforestdn = dnsforestdn
242 dnsdomaindn = "DC=DomainDnsZones,%s" % (str(names.domaindn))
243 if nc == dnsdomaindn:
244 names.dnsdomaindn = dnsdomaindn
248 res3 = samdb.search(expression="(objectClass=site)",
249 base="CN=Sites," + names.configdn, scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
250 names.sitename = str(res3[0]["cn"])
252 # dns hostname and server dn
253 res4 = samdb.search(expression="(CN=%s)" % names.netbiosname,
254 base="OU=Domain Controllers,%s" % basedn,
255 scope=ldb.SCOPE_ONELEVEL, attrs=["dNSHostName"])
257 raise ProvisioningError("Unable to find DC called CN=%s under OU=Domain Controllers,%s" % (names.netbiosname, basedn))
259 names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain, "")
261 server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
262 attrs=[], base=names.configdn)
263 names.serverdn = str(server_res[0].dn)
265 # invocation id/objectguid
266 res5 = samdb.search(expression="(objectClass=*)",
267 base="CN=NTDS Settings,%s" % str(names.serverdn),
268 scope=ldb.SCOPE_BASE,
269 attrs=["invocationID", "objectGUID"])
270 names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0]))
271 names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0]))
274 res6 = samdb.search(expression="(objectClass=*)", base=basedn,
275 scope=ldb.SCOPE_BASE, attrs=["objectGUID",
276 "objectSid","msDS-Behavior-Version" ])
277 names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
278 names.domainsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
279 names.forestsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
280 if res6[0].get("msDS-Behavior-Version") is None or \
281 int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
282 names.domainlevel = DS_DOMAIN_FUNCTION_2000
284 names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
287 res7 = samdb.search(expression="(name={%s})" % DEFAULT_POLICY_GUID,
288 base="CN=Policies,CN=System," + basedn,
289 scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
290 names.policyid = str(res7[0]["cn"]).replace("{","").replace("}","")
292 res8 = samdb.search(expression="(name={%s})" % DEFAULT_DC_POLICY_GUID,
293 base="CN=Policies,CN=System," + basedn,
294 scope=ldb.SCOPE_ONELEVEL,
295 attrs=["cn","displayName"])
297 names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","")
299 names.policyid_dc = None
301 res9 = idmapdb.search(expression="(cn=%s-%s)" %
302 (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR),
303 attrs=["xidNumber", "type"])
305 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid (%s-%s" % (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR))
306 if res9[0]["type"][0] == "ID_TYPE_BOTH":
307 names.root_gid = res9[0]["xidNumber"][0]
309 names.root_gid = pwd.getpwuid(int(res9[0]["xidNumber"][0])).pw_gid
311 res10 = samdb.search(expression="(samaccountname=dns)",
312 scope=ldb.SCOPE_SUBTREE, attrs=["dn"],
313 controls=["search_options:1:2"])
315 has_legacy_dns_account = True
317 has_legacy_dns_account = False
319 res11 = samdb.search(expression="(samaccountname=dns-%s)" % names.netbiosname,
320 scope=ldb.SCOPE_SUBTREE, attrs=["dn"],
321 controls=["search_options:1:2"])
323 has_dns_account = True
325 has_dns_account = False
327 if names.dnsdomaindn is not None:
329 names.dns_backend = 'BIND9_DLZ'
331 names.dns_backend = 'SAMBA_INTERNAL'
332 elif has_dns_account or has_legacy_dns_account:
333 names.dns_backend = 'BIND9_FLATFILE'
335 names.dns_backend = 'NONE'
337 dns_admins_sid = get_dnsadmins_sid(samdb, names.domaindn)
338 names.name_map['DnsAdmins'] = str(dns_admins_sid)
343 def update_provision_usn(samdb, low, high, id, replace=False):
344 """Update the field provisionUSN in sam.ldb
346 This field is used to track range of USN modified by provision and
348 This value is used afterward by next provision to figure out if
349 the field have been modified since last provision.
351 :param samdb: An LDB object connect to sam.ldb
352 :param low: The lowest USN modified by this upgrade
353 :param high: The highest USN modified by this upgrade
354 :param id: The invocation id of the samba's dc
355 :param replace: A boolean indicating if the range should replace any
356 existing one or appended (default)
361 entry = samdb.search(base="@PROVISION",
362 scope=ldb.SCOPE_BASE,
363 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
364 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
365 if not re.search(';', e):
366 e = "%s;%s" % (e, id)
369 tab.append("%s-%s;%s" % (low, high, id))
370 delta = ldb.Message()
371 delta.dn = ldb.Dn(samdb, "@PROVISION")
372 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
373 ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE)
374 entry = samdb.search(expression='provisionnerID=*',
375 base="@PROVISION", scope=ldb.SCOPE_BASE,
376 attrs=["provisionnerID"])
377 if len(entry) == 0 or len(entry[0]) == 0:
378 delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
382 def set_provision_usn(samdb, low, high, id):
383 """Set the field provisionUSN in sam.ldb
384 This field is used to track range of USN modified by provision and
386 This value is used afterward by next provision to figure out if
387 the field have been modified since last provision.
389 :param samdb: An LDB object connect to sam.ldb
390 :param low: The lowest USN modified by this upgrade
391 :param high: The highest USN modified by this upgrade
392 :param id: The invocationId of the provision"""
395 tab.append("%s-%s;%s" % (low, high, id))
397 delta = ldb.Message()
398 delta.dn = ldb.Dn(samdb, "@PROVISION")
399 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
400 ldb.FLAG_MOD_ADD, LAST_PROVISION_USN_ATTRIBUTE)
404 def get_max_usn(samdb,basedn):
405 """ This function return the biggest USN present in the provision
407 :param samdb: A LDB object pointing to the sam.ldb
408 :param basedn: A string containing the base DN of the provision
410 :return: The biggest USN in the provision"""
412 res = samdb.search(expression="objectClass=*",base=basedn,
413 scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
414 controls=["search_options:1:2",
415 "server_sort:1:1:uSNChanged",
416 "paged_results:1:1"])
417 return res[0]["uSNChanged"]
420 def get_last_provision_usn(sam):
421 """Get USNs ranges modified by a provision or an upgradeprovision
423 :param sam: An LDB object pointing to the sam.ldb
424 :return: a dictionary which keys are invocation id and values are an array
425 of integer representing the different ranges
428 entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE,
429 base="@PROVISION", scope=ldb.SCOPE_BASE,
430 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
431 except ldb.LdbError as e1:
432 (ecode, emsg) = e1.args
433 if ecode == ldb.ERR_NO_SUCH_OBJECT:
440 if entry[0].get("provisionnerID"):
441 for e in entry[0]["provisionnerID"]:
443 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
444 tab1 = str(r).split(';')
449 if (len(myids) > 0 and id not in myids):
451 tab2 = p.split(tab1[0])
452 if range.get(id) is None:
454 range[id].append(tab2[0])
455 range[id].append(tab2[1])
461 class ProvisionResult(object):
462 """Result of a provision.
464 :ivar server_role: The server role
465 :ivar paths: ProvisionPaths instance
466 :ivar domaindn: The domain dn, as string
470 self.server_role = None
477 self.domainsid = None
478 self.adminpass_generated = None
479 self.adminpass = None
480 self.backend_result = None
482 def report_logger(self, logger):
483 """Report this provision result to a logger."""
485 "Once the above files are installed, your Samba AD server will "
487 if self.adminpass_generated:
488 logger.info("Admin password: %s", self.adminpass)
489 logger.info("Server Role: %s", self.server_role)
490 logger.info("Hostname: %s", self.names.hostname)
491 logger.info("NetBIOS Domain: %s", self.names.domain)
492 logger.info("DNS Domain: %s", self.names.dnsdomain)
493 logger.info("DOMAIN SID: %s", self.domainsid)
495 if self.backend_result:
496 self.backend_result.report_logger(logger)
499 def check_install(lp, session_info, credentials):
500 """Check whether the current install seems ok.
502 :param lp: Loadparm context
503 :param session_info: Session information
504 :param credentials: Credentials
506 if lp.get("realm") == "":
507 raise Exception("Realm empty")
508 samdb = Ldb(lp.samdb_url(), session_info=session_info,
509 credentials=credentials, lp=lp)
510 if len(samdb.search("(cn=Administrator)")) != 1:
511 raise ProvisioningError("No administrator account found")
514 def findnss(nssfn, names):
515 """Find a user or group from a list of possibilities.
517 :param nssfn: NSS Function to try (should raise KeyError if not found)
518 :param names: Names to check.
519 :return: Value return by first names list.
526 raise KeyError("Unable to find user/group in %r" % names)
529 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
530 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
533 def provision_paths_from_lp(lp, dnsdomain):
534 """Set the default paths for provisioning.
536 :param lp: Loadparm context.
537 :param dnsdomain: DNS Domain name
539 paths = ProvisionPaths()
540 paths.private_dir = lp.get("private dir")
541 paths.binddns_dir = lp.get("binddns dir")
542 paths.state_dir = lp.get("state directory")
544 # This is stored without path prefix for the "privateKeytab" attribute in
545 # "secrets_dns.ldif".
546 paths.dns_keytab = "dns.keytab"
547 paths.keytab = "secrets.keytab"
549 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
550 paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
551 paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
552 paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
553 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
554 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
555 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
556 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
557 paths.kdcconf = os.path.join(paths.private_dir, "kdc.conf")
558 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
559 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
560 paths.encrypted_secrets_key_path = os.path.join(
562 "encrypted_secrets.key")
564 paths.dns = os.path.join(paths.binddns_dir, "dns", dnsdomain + ".zone")
565 paths.namedconf = os.path.join(paths.binddns_dir, "named.conf")
566 paths.namedconf_update = os.path.join(paths.binddns_dir, "named.conf.update")
567 paths.namedtxt = os.path.join(paths.binddns_dir, "named.txt")
569 paths.hklm = "hklm.ldb"
570 paths.hkcr = "hkcr.ldb"
571 paths.hkcu = "hkcu.ldb"
572 paths.hku = "hku.ldb"
573 paths.hkpd = "hkpd.ldb"
574 paths.hkpt = "hkpt.ldb"
575 paths.sysvol = lp.get("path", "sysvol")
576 paths.netlogon = lp.get("path", "netlogon")
577 paths.smbconf = lp.configfile
581 def determine_netbios_name(hostname):
582 """Determine a netbios name from a hostname."""
583 # remove forbidden chars and force the length to be <16
584 netbiosname = "".join([x for x in hostname if is_valid_netbios_char(x)])
585 return netbiosname[:MAX_NETBIOS_NAME_LEN].upper()
588 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
589 serverrole=None, rootdn=None, domaindn=None, configdn=None,
590 schemadn=None, serverdn=None, sitename=None,
591 domain_names_forced=False):
592 """Guess configuration settings to use."""
595 hostname = socket.gethostname().split(".")[0]
597 netbiosname = lp.get("netbios name")
598 if netbiosname is None:
599 netbiosname = determine_netbios_name(hostname)
600 netbiosname = netbiosname.upper()
601 if not valid_netbios_name(netbiosname):
602 raise InvalidNetbiosName(netbiosname)
604 if dnsdomain is None:
605 dnsdomain = lp.get("realm")
606 if dnsdomain is None or dnsdomain == "":
607 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
609 dnsdomain = dnsdomain.lower()
611 if serverrole is None:
612 serverrole = lp.get("server role")
613 if serverrole is None:
614 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
616 serverrole = serverrole.lower()
618 realm = dnsdomain.upper()
620 if lp.get("realm") == "":
621 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
623 if lp.get("realm").upper() != realm:
624 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))
626 if lp.get("server role").lower() != serverrole:
627 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))
629 if serverrole == "active directory domain controller":
631 # This will, for better or worse, default to 'WORKGROUP'
632 domain = lp.get("workgroup")
633 domain = domain.upper()
635 if lp.get("workgroup").upper() != domain:
636 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))
639 domaindn = samba.dn_from_dns_name(dnsdomain)
641 if domain == netbiosname:
642 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
646 domaindn = "DC=" + netbiosname
648 if not valid_netbios_name(domain):
649 raise InvalidNetbiosName(domain)
651 if hostname.upper() == realm:
652 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
653 if netbiosname.upper() == realm:
654 raise ProvisioningError("guess_names: Realm '%s' must not be equal to NetBIOS hostname '%s'!" % (realm, netbiosname))
655 if domain == realm and not domain_names_forced:
656 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
658 if serverrole != "active directory domain controller":
660 # This is the code path for a domain member
661 # where we provision the database as if we where
662 # on a domain controller, so we should not use
663 # the same dnsdomain as the domain controllers
664 # of our primary domain.
666 # This will be important if we start doing
667 # SID/name filtering and reject the local
668 # sid and names if they come from a domain
672 dnsdomain = netbiosname.lower()
678 configdn = "CN=Configuration," + rootdn
680 schemadn = "CN=Schema," + configdn
683 sitename = DEFAULTSITE
685 names = ProvisionNames()
686 names.rootdn = rootdn
687 names.domaindn = domaindn
688 names.configdn = configdn
689 names.schemadn = schemadn
690 names.ldapmanagerdn = "CN=Manager," + rootdn
691 names.dnsdomain = dnsdomain
692 names.domain = domain
694 names.netbiosname = netbiosname
695 names.hostname = hostname
696 names.sitename = sitename
697 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
698 netbiosname, sitename, configdn)
702 def make_smbconf(smbconf, hostname, domain, realm, targetdir,
703 serverrole=None, eadb=False, use_ntvfs=False, lp=None,
705 """Create a new smb.conf file based on a couple of basic settings.
707 assert smbconf is not None
710 hostname = socket.gethostname().split(".")[0]
712 netbiosname = determine_netbios_name(hostname)
714 if serverrole is None:
715 serverrole = "standalone server"
717 assert domain is not None
718 domain = domain.upper()
720 assert realm is not None
721 realm = realm.upper()
724 "netbios name": netbiosname,
727 "server role": serverrole,
731 lp = samba.param.LoadParm()
732 #Load non-existent file
733 if os.path.exists(smbconf):
736 if global_param is not None:
737 for ent in global_param:
738 if global_param[ent] is not None:
739 global_settings[ent] = " ".join(global_param[ent])
741 if targetdir is not None:
742 global_settings["private dir"] = os.path.abspath(os.path.join(targetdir, "private"))
743 global_settings["lock dir"] = os.path.abspath(targetdir)
744 global_settings["state directory"] = os.path.abspath(os.path.join(targetdir, "state"))
745 global_settings["cache directory"] = os.path.abspath(os.path.join(targetdir, "cache"))
747 lp.set("lock dir", os.path.abspath(targetdir))
748 lp.set("state directory", global_settings["state directory"])
749 lp.set("cache directory", global_settings["cache directory"])
752 if use_ntvfs and not lp.get("posix:eadb"):
753 if targetdir is not None:
754 privdir = os.path.join(targetdir, "private")
756 privdir = lp.get("private dir")
757 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
758 elif not use_ntvfs and not lp.get("xattr_tdb:file"):
759 if targetdir is not None:
760 statedir = os.path.join(targetdir, "state")
762 statedir = lp.get("state directory")
763 lp.set("xattr_tdb:file", os.path.abspath(os.path.join(statedir, "xattr.tdb")))
766 if serverrole == "active directory domain controller":
767 shares["sysvol"] = os.path.join(lp.get("state directory"), "sysvol")
768 shares["netlogon"] = os.path.join(shares["sysvol"], realm.lower(),
771 global_settings["passdb backend"] = "samba_dsdb"
773 f = open(smbconf, 'w')
775 f.write("[globals]\n")
776 for key, val in global_settings.iteritems():
777 f.write("\t%s = %s\n" % (key, val))
780 for name, path in shares.iteritems():
781 f.write("[%s]\n" % name)
782 f.write("\tpath = %s\n" % path)
783 f.write("\tread only = no\n")
787 # reload the smb.conf
790 # and dump it without any values that are the default
791 # this ensures that any smb.conf parameters that were set
792 # on the provision/join command line are set in the resulting smb.conf
793 lp.dump(False, smbconf)
796 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
797 users_gid, root_gid):
798 """setup reasonable name mappings for sam names to unix names.
800 :param samdb: SamDB object.
801 :param idmap: IDmap db object.
802 :param sid: The domain sid.
803 :param domaindn: The domain DN.
804 :param root_uid: uid of the UNIX root user.
805 :param nobody_uid: uid of the UNIX nobody user.
806 :param users_gid: gid of the UNIX users group.
807 :param root_gid: gid of the UNIX root group.
809 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
811 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
812 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
815 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
816 provision_backend, names, serverrole,
817 erase=False, plaintext_secrets=False):
818 """Setup the partitions for the SAM database.
820 Alternatively, provision() may call this, and then populate the database.
822 :note: This will wipe the Sam Database!
824 :note: This function always removes the local SAM LDB file. The erase
825 parameter controls whether to erase the existing data, which
826 may not be stored locally but in LDAP.
829 assert session_info is not None
831 # We use options=["modules:"] to stop the modules loading - we
832 # just want to wipe and re-initialise the database, not start it up
835 os.unlink(samdb_path)
839 samdb = Ldb(url=samdb_path, session_info=session_info,
840 lp=lp, options=["modules:"])
842 ldap_backend_line = "# No LDAP backend"
843 if provision_backend.type != "ldb":
844 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
846 required_features = "# No required features"
847 if not plaintext_secrets:
848 required_features = "requiredFeatures: encryptedSecrets"
850 samdb.transaction_start()
852 logger.info("Setting up sam.ldb partitions and settings")
853 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
854 "LDAP_BACKEND_LINE": ldap_backend_line
858 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
859 "BACKEND_TYPE": provision_backend.type,
860 "SERVER_ROLE": serverrole,
861 "REQUIRED_FEATURES": required_features
864 logger.info("Setting up sam.ldb rootDSE")
865 setup_samdb_rootdse(samdb, names)
867 samdb.transaction_cancel()
870 samdb.transaction_commit()
873 def secretsdb_self_join(secretsdb, domain,
874 netbiosname, machinepass, domainsid=None,
875 realm=None, dnsdomain=None,
877 key_version_number=1,
878 secure_channel_type=SEC_CHAN_WKSTA):
879 """Add domain join-specific bits to a secrets database.
881 :param secretsdb: Ldb Handle to the secrets database
882 :param machinepass: Machine password
884 attrs = ["whenChanged",
891 if realm is not None:
892 if dnsdomain is None:
893 dnsdomain = realm.lower()
894 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
897 shortname = netbiosname.lower()
899 # We don't need to set msg["flatname"] here, because rdn_name will handle
900 # it, and it causes problems for modifies anyway
901 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
902 msg["secureChannelType"] = [str(secure_channel_type)]
903 msg["objectClass"] = ["top", "primaryDomain"]
904 if dnsname is not None:
905 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
906 msg["realm"] = [realm]
907 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
908 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
909 msg["privateKeytab"] = ["secrets.keytab"]
911 msg["secret"] = [machinepass.encode('utf-8')]
912 msg["samAccountName"] = ["%s$" % netbiosname]
913 msg["secureChannelType"] = [str(secure_channel_type)]
914 if domainsid is not None:
915 msg["objectSid"] = [ndr_pack(domainsid)]
917 # This complex expression tries to ensure that we don't have more
918 # than one record for this SID, realm or netbios domain at a time,
919 # but we don't delete the old record that we are about to modify,
920 # because that would delete the keytab and previous password.
921 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
922 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
923 scope=ldb.SCOPE_ONELEVEL)
926 secretsdb.delete(del_msg.dn)
928 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
931 msg["priorSecret"] = [res[0]["secret"][0]]
933 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
938 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
943 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
949 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
950 secretsdb.modify(msg)
951 secretsdb.rename(res[0].dn, msg.dn)
953 spn = [ 'HOST/%s' % shortname ]
954 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
955 # we are a domain controller then we add servicePrincipalName
956 # entries for the keytab code to update.
957 spn.extend([ 'HOST/%s' % dnsname ])
958 msg["servicePrincipalName"] = spn
963 def setup_secretsdb(paths, session_info, backend_credentials, lp):
964 """Setup the secrets database.
966 :note: This function does not handle exceptions and transaction on purpose,
967 it's up to the caller to do this job.
969 :param path: Path to the secrets database.
970 :param session_info: Session info.
971 :param credentials: Credentials
972 :param lp: Loadparm context
973 :return: LDB handle for the created secrets database
975 if os.path.exists(paths.secrets):
976 os.unlink(paths.secrets)
978 keytab_path = os.path.join(paths.private_dir, paths.keytab)
979 if os.path.exists(keytab_path):
980 os.unlink(keytab_path)
982 bind_dns_keytab_path = os.path.join(paths.binddns_dir, paths.dns_keytab)
983 if os.path.exists(bind_dns_keytab_path):
984 os.unlink(bind_dns_keytab_path)
986 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
987 if os.path.exists(dns_keytab_path):
988 os.unlink(dns_keytab_path)
992 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
994 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
995 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
996 secrets_ldb.transaction_start()
998 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
1000 if (backend_credentials is not None and
1001 backend_credentials.authentication_requested()):
1002 if backend_credentials.get_bind_dn() is not None:
1003 setup_add_ldif(secrets_ldb,
1004 setup_path("secrets_simple_ldap.ldif"), {
1005 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
1006 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
1009 setup_add_ldif(secrets_ldb,
1010 setup_path("secrets_sasl_ldap.ldif"), {
1011 "LDAPADMINUSER": backend_credentials.get_username(),
1012 "LDAPADMINREALM": backend_credentials.get_realm(),
1013 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
1016 secrets_ldb.transaction_cancel()
1021 def setup_privileges(path, session_info, lp):
1022 """Setup the privileges database.
1024 :param path: Path to the privileges database.
1025 :param session_info: Session info.
1026 :param credentials: Credentials
1027 :param lp: Loadparm context
1028 :return: LDB handle for the created secrets database
1030 if os.path.exists(path):
1032 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
1033 privilege_ldb.erase()
1034 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
1036 def setup_encrypted_secrets_key(path):
1037 """Setup the encrypted secrets key file.
1039 Any existing key file will be deleted and a new random key generated.
1041 :param path: Path to the secrets key file.
1044 if os.path.exists(path):
1047 flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
1048 mode = stat.S_IRUSR | stat.S_IWUSR
1050 umask_original = os.umask(0)
1052 fd = os.open(path, flags, mode)
1054 os.umask(umask_original)
1056 with os.fdopen(fd, 'w') as f:
1057 key = samba.generate_random_bytes(16)
1061 def setup_registry(path, session_info, lp):
1062 """Setup the registry.
1064 :param path: Path to the registry database
1065 :param session_info: Session information
1066 :param credentials: Credentials
1067 :param lp: Loadparm context
1069 reg = samba.registry.Registry()
1070 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
1071 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
1072 provision_reg = setup_path("provision.reg")
1073 assert os.path.exists(provision_reg)
1074 reg.diff_apply(provision_reg)
1077 def setup_idmapdb(path, session_info, lp):
1078 """Setup the idmap database.
1080 :param path: path to the idmap database
1081 :param session_info: Session information
1082 :param credentials: Credentials
1083 :param lp: Loadparm context
1085 if os.path.exists(path):
1088 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
1090 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
1094 def setup_samdb_rootdse(samdb, names):
1095 """Setup the SamDB rootdse.
1097 :param samdb: Sam Database handle
1099 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
1100 "SCHEMADN": names.schemadn,
1101 "DOMAINDN": names.domaindn,
1102 "ROOTDN" : names.rootdn,
1103 "CONFIGDN": names.configdn,
1104 "SERVERDN": names.serverdn,
1108 def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
1109 dns_backend, dnspass, domainsid, next_rid, invocationid,
1110 policyguid, policyguid_dc,
1111 domainControllerFunctionality, ntdsguid=None, dc_rid=None):
1112 """Join a host to its own domain."""
1113 assert isinstance(invocationid, str)
1114 if ntdsguid is not None:
1115 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
1122 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
1123 "CONFIGDN": names.configdn,
1124 "SCHEMADN": names.schemadn,
1125 "DOMAINDN": names.domaindn,
1126 "SERVERDN": names.serverdn,
1127 "INVOCATIONID": invocationid,
1128 "NETBIOSNAME": names.netbiosname,
1129 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1130 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1131 "DOMAINSID": str(domainsid),
1132 "DCRID": str(dc_rid),
1133 "SAMBA_VERSION_STRING": version,
1134 "NTDSGUID": ntdsguid_line,
1135 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1136 domainControllerFunctionality),
1137 "RIDALLOCATIONSTART": str(next_rid + 100),
1138 "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
1140 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1141 "POLICYGUID": policyguid,
1142 "POLICYGUID_DC": policyguid_dc,
1143 "DNSDOMAIN": names.dnsdomain,
1144 "DOMAINDN": names.domaindn})
1146 # If we are setting up a subdomain, then this has been replicated in, so we
1147 # don't need to add it
1148 if fill == FILL_FULL:
1149 setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
1150 "CONFIGDN": names.configdn,
1151 "SCHEMADN": names.schemadn,
1152 "DOMAINDN": names.domaindn,
1153 "SERVERDN": names.serverdn,
1154 "INVOCATIONID": invocationid,
1155 "NETBIOSNAME": names.netbiosname,
1156 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1157 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1158 "DOMAINSID": str(domainsid),
1159 "DCRID": str(dc_rid),
1160 "SAMBA_VERSION_STRING": version,
1161 "NTDSGUID": ntdsguid_line,
1162 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1163 domainControllerFunctionality)})
1165 # Setup fSMORoleOwner entries to point at the newly created DC entry
1166 setup_modify_ldif(samdb,
1167 setup_path("provision_self_join_modify_config.ldif"), {
1168 "CONFIGDN": names.configdn,
1169 "SCHEMADN": names.schemadn,
1170 "DEFAULTSITE": names.sitename,
1171 "NETBIOSNAME": names.netbiosname,
1172 "SERVERDN": names.serverdn,
1175 system_session_info = system_session()
1176 samdb.set_session_info(system_session_info)
1177 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1178 # modify a serverReference under cn=config when we are a subdomain, we must
1179 # be system due to ACLs
1180 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1181 "DOMAINDN": names.domaindn,
1182 "SERVERDN": names.serverdn,
1183 "NETBIOSNAME": names.netbiosname,
1186 samdb.set_session_info(admin_session_info)
1188 if dns_backend != "SAMBA_INTERNAL":
1189 # This is Samba4 specific and should be replaced by the correct
1190 # DNS AD-style setup
1191 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1192 "DNSDOMAIN": names.dnsdomain,
1193 "DOMAINDN": names.domaindn,
1194 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1195 "HOSTNAME" : names.hostname,
1196 "DNSNAME" : '%s.%s' % (
1197 names.netbiosname.lower(), names.dnsdomain.lower())
1201 def getpolicypath(sysvolpath, dnsdomain, guid):
1202 """Return the physical path of policy given its guid.
1204 :param sysvolpath: Path to the sysvol folder
1205 :param dnsdomain: DNS name of the AD domain
1206 :param guid: The GUID of the policy
1207 :return: A string with the complete path to the policy folder
1210 guid = "{%s}" % guid
1211 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1215 def create_gpo_struct(policy_path):
1216 if not os.path.exists(policy_path):
1217 os.makedirs(policy_path, 0o775)
1218 f = open(os.path.join(policy_path, "GPT.INI"), 'w')
1220 f.write("[General]\r\nVersion=0")
1223 p = os.path.join(policy_path, "MACHINE")
1224 if not os.path.exists(p):
1225 os.makedirs(p, 0o775)
1226 p = os.path.join(policy_path, "USER")
1227 if not os.path.exists(p):
1228 os.makedirs(p, 0o775)
1231 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1232 """Create the default GPO for a domain
1234 :param sysvolpath: Physical path for the sysvol folder
1235 :param dnsdomain: DNS domain name of the AD domain
1236 :param policyguid: GUID of the default domain policy
1237 :param policyguid_dc: GUID of the default domain controler policy
1239 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1240 create_gpo_struct(policy_path)
1242 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1243 create_gpo_struct(policy_path)
1246 def setup_samdb(path, session_info, provision_backend, lp, names,
1247 logger, fill, serverrole, schema, am_rodc=False,
1248 plaintext_secrets=False):
1249 """Setup a complete SAM Database.
1251 :note: This will wipe the main SAM database file!
1254 # Also wipes the database
1255 setup_samdb_partitions(path, logger=logger, lp=lp,
1256 provision_backend=provision_backend, session_info=session_info,
1257 names=names, serverrole=serverrole, plaintext_secrets=plaintext_secrets)
1259 # Load the database, but don's load the global schema and don't connect
1261 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1262 credentials=provision_backend.credentials, lp=lp,
1263 global_schema=False, am_rodc=am_rodc)
1265 logger.info("Pre-loading the Samba 4 and AD schema")
1267 # Load the schema from the one we computed earlier
1268 samdb.set_schema(schema, write_indices_and_attributes=False)
1270 # Set the NTDS settings DN manually - in order to have it already around
1271 # before the provisioned tree exists and we connect
1272 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1274 # And now we can connect to the DB - the schema won't be loaded from the
1278 except ldb.LdbError as e2:
1279 (num, string_error) = e2.args
1280 if (num == ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS):
1281 raise ProvisioningError("Permission denied connecting to %s, are you running as root?" % path)
1285 # But we have to give it one more kick to have it use the schema
1286 # during provision - it needs, now that it is connected, to write
1287 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1288 samdb.set_schema(schema, write_indices_and_attributes=True)
1293 def fill_samdb(samdb, lp, names, logger, policyguid,
1294 policyguid_dc, fill, adminpass, krbtgtpass, machinepass, dns_backend,
1295 dnspass, invocationid, ntdsguid, serverrole, am_rodc=False,
1296 dom_for_fun_level=None, schema=None, next_rid=None, dc_rid=None):
1298 if next_rid is None:
1301 # Provision does not make much sense values larger than 1000000000
1302 # as the upper range of the rIDAvailablePool is 1073741823 and
1303 # we don't want to create a domain that cannot allocate rids.
1304 if next_rid < 1000 or next_rid > 1000000000:
1305 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1306 error += "the valid range is %u-%u. The default is %u." % (
1307 1000, 1000000000, 1000)
1308 raise ProvisioningError(error)
1310 # ATTENTION: Do NOT change these default values without discussion with the
1311 # team and/or release manager. They have a big impact on the whole program!
1312 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1314 if dom_for_fun_level is None:
1315 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
1317 if dom_for_fun_level > domainControllerFunctionality:
1318 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!")
1320 domainFunctionality = dom_for_fun_level
1321 forestFunctionality = dom_for_fun_level
1323 # Set the NTDS settings DN manually - in order to have it already around
1324 # before the provisioned tree exists and we connect
1325 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1327 # Set the domain functionality levels onto the database.
1328 # Various module (the password_hash module in particular) need
1329 # to know what level of AD we are emulating.
1331 # These will be fixed into the database via the database
1332 # modifictions below, but we need them set from the start.
1333 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1334 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1335 samdb.set_opaque_integer("domainControllerFunctionality",
1336 domainControllerFunctionality)
1338 samdb.set_domain_sid(str(names.domainsid))
1339 samdb.set_invocation_id(invocationid)
1341 logger.info("Adding DomainDN: %s" % names.domaindn)
1343 # impersonate domain admin
1344 admin_session_info = admin_session(lp, str(names.domainsid))
1345 samdb.set_session_info(admin_session_info)
1346 if names.domainguid is not None:
1347 domainguid_line = "objectGUID: %s\n-" % names.domainguid
1349 domainguid_line = ""
1351 descr = b64encode(get_domain_descriptor(names.domainsid))
1352 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1353 "DOMAINDN": names.domaindn,
1354 "DOMAINSID": str(names.domainsid),
1355 "DESCRIPTOR": descr,
1356 "DOMAINGUID": domainguid_line
1359 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1360 "DOMAINDN": names.domaindn,
1361 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1362 "NEXTRID": str(next_rid),
1363 "DEFAULTSITE": names.sitename,
1364 "CONFIGDN": names.configdn,
1365 "POLICYGUID": policyguid,
1366 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1367 "SAMBA_VERSION_STRING": version,
1368 "MIN_PWD_LENGTH": str(DEFAULT_MIN_PWD_LENGTH)
1371 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1372 if fill == FILL_FULL:
1373 logger.info("Adding configuration container")
1374 descr = b64encode(get_config_descriptor(names.domainsid))
1375 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1376 "CONFIGDN": names.configdn,
1377 "DESCRIPTOR": descr,
1380 # The LDIF here was created when the Schema object was constructed
1381 ignore_checks_oid = "local_oid:%s:0" % samba.dsdb.DSDB_CONTROL_SKIP_DUPLICATES_CHECK_OID
1382 logger.info("Setting up sam.ldb schema")
1383 samdb.add_ldif(schema.schema_dn_add,
1384 controls=["relax:0", ignore_checks_oid])
1385 samdb.modify_ldif(schema.schema_dn_modify,
1386 controls=[ignore_checks_oid])
1387 samdb.write_prefixes_from_schema()
1388 samdb.add_ldif(schema.schema_data, controls=["relax:0", ignore_checks_oid])
1389 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1390 {"SCHEMADN": names.schemadn},
1391 controls=["relax:0", ignore_checks_oid])
1393 # Now register this container in the root of the forest
1394 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1395 msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1398 samdb.invocation_id = invocationid
1400 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1401 if fill == FILL_FULL:
1402 logger.info("Setting up sam.ldb configuration data")
1404 partitions_descr = b64encode(get_config_partitions_descriptor(names.domainsid))
1405 sites_descr = b64encode(get_config_sites_descriptor(names.domainsid))
1406 ntdsquotas_descr = b64encode(get_config_ntds_quotas_descriptor(names.domainsid))
1407 protected1_descr = b64encode(get_config_delete_protected1_descriptor(names.domainsid))
1408 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid))
1409 protected2_descr = b64encode(get_config_delete_protected2_descriptor(names.domainsid))
1411 if "2008" in schema.base_schema:
1412 # exclude 2012-specific changes if we're using a 2008 schema
1417 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1418 "CONFIGDN": names.configdn,
1419 "NETBIOSNAME": names.netbiosname,
1420 "DEFAULTSITE": names.sitename,
1421 "DNSDOMAIN": names.dnsdomain,
1422 "DOMAIN": names.domain,
1423 "SCHEMADN": names.schemadn,
1424 "DOMAINDN": names.domaindn,
1425 "SERVERDN": names.serverdn,
1426 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1427 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1428 "NTDSQUOTAS_DESCRIPTOR": ntdsquotas_descr,
1429 "LOSTANDFOUND_DESCRIPTOR": protected1wd_descr,
1430 "SERVICES_DESCRIPTOR": protected1_descr,
1431 "PHYSICALLOCATIONS_DESCRIPTOR": protected1wd_descr,
1432 "FORESTUPDATES_DESCRIPTOR": protected1wd_descr,
1433 "EXTENDEDRIGHTS_DESCRIPTOR": protected2_descr,
1434 "PARTITIONS_DESCRIPTOR": partitions_descr,
1435 "SITES_DESCRIPTOR": sites_descr,
1438 setup_add_ldif(samdb, setup_path("extended-rights.ldif"), {
1439 "CONFIGDN": names.configdn,
1440 "INC2012" : incl_2012,
1443 logger.info("Setting up display specifiers")
1444 display_specifiers_ldif = read_ms_ldif(
1445 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1446 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1447 {"CONFIGDN": names.configdn})
1448 check_all_substituted(display_specifiers_ldif)
1449 samdb.add_ldif(display_specifiers_ldif)
1451 logger.info("Modifying display specifiers and extended rights")
1452 setup_modify_ldif(samdb,
1453 setup_path("provision_configuration_modify.ldif"), {
1454 "CONFIGDN": names.configdn,
1455 "DISPLAYSPECIFIERS_DESCRIPTOR": protected2_descr
1458 logger.info("Adding users container")
1459 users_desc = b64encode(get_domain_users_descriptor(names.domainsid))
1460 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1461 "DOMAINDN": names.domaindn,
1462 "USERS_DESCRIPTOR": users_desc
1464 logger.info("Modifying users container")
1465 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1466 "DOMAINDN": names.domaindn})
1467 logger.info("Adding computers container")
1468 computers_desc = b64encode(get_domain_computers_descriptor(names.domainsid))
1469 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1470 "DOMAINDN": names.domaindn,
1471 "COMPUTERS_DESCRIPTOR": computers_desc
1473 logger.info("Modifying computers container")
1474 setup_modify_ldif(samdb,
1475 setup_path("provision_computers_modify.ldif"), {
1476 "DOMAINDN": names.domaindn})
1477 logger.info("Setting up sam.ldb data")
1478 infrastructure_desc = b64encode(get_domain_infrastructure_descriptor(names.domainsid))
1479 lostandfound_desc = b64encode(get_domain_delete_protected2_descriptor(names.domainsid))
1480 system_desc = b64encode(get_domain_delete_protected1_descriptor(names.domainsid))
1481 builtin_desc = b64encode(get_domain_builtin_descriptor(names.domainsid))
1482 controllers_desc = b64encode(get_domain_controllers_descriptor(names.domainsid))
1483 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1484 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1485 "DOMAINDN": names.domaindn,
1486 "NETBIOSNAME": names.netbiosname,
1487 "DEFAULTSITE": names.sitename,
1488 "CONFIGDN": names.configdn,
1489 "SERVERDN": names.serverdn,
1490 "RIDAVAILABLESTART": str(next_rid + 600),
1491 "POLICYGUID_DC": policyguid_dc,
1492 "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc,
1493 "LOSTANDFOUND_DESCRIPTOR": lostandfound_desc,
1494 "SYSTEM_DESCRIPTOR": system_desc,
1495 "BUILTIN_DESCRIPTOR": builtin_desc,
1496 "DOMAIN_CONTROLLERS_DESCRIPTOR": controllers_desc,
1499 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1500 if fill == FILL_FULL:
1501 managedservice_descr = b64encode(get_managed_service_accounts_descriptor(names.domainsid))
1502 setup_modify_ldif(samdb,
1503 setup_path("provision_configuration_references.ldif"), {
1504 "CONFIGDN": names.configdn,
1505 "SCHEMADN": names.schemadn})
1507 logger.info("Setting up well known security principals")
1508 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid))
1509 setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1510 "CONFIGDN": names.configdn,
1511 "WELLKNOWNPRINCIPALS_DESCRIPTOR": protected1wd_descr,
1514 if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1515 setup_modify_ldif(samdb,
1516 setup_path("provision_basedn_references.ldif"), {
1517 "DOMAINDN": names.domaindn,
1518 "MANAGEDSERVICE_DESCRIPTOR": managedservice_descr
1521 logger.info("Setting up sam.ldb users and groups")
1522 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1523 "DOMAINDN": names.domaindn,
1524 "DOMAINSID": str(names.domainsid),
1525 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1526 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1529 logger.info("Setting up self join")
1530 setup_self_join(samdb, admin_session_info, names=names, fill=fill,
1531 invocationid=invocationid,
1532 dns_backend=dns_backend,
1534 machinepass=machinepass,
1535 domainsid=names.domainsid,
1538 policyguid=policyguid,
1539 policyguid_dc=policyguid_dc,
1540 domainControllerFunctionality=domainControllerFunctionality,
1543 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1544 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1545 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1546 assert isinstance(names.ntdsguid, str)
1551 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1552 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)"
1553 SYSVOL_SERVICE="sysvol"
1555 def set_dir_acl(path, acl, lp, domsid, use_ntvfs, passdb, service=SYSVOL_SERVICE):
1556 setntacl(lp, path, acl, domsid, use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1557 for root, dirs, files in os.walk(path, topdown=False):
1559 setntacl(lp, os.path.join(root, name), acl, domsid,
1560 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1562 setntacl(lp, os.path.join(root, name), acl, domsid,
1563 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1566 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb):
1567 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1570 :param sysvol: Physical path for the sysvol folder
1571 :param dnsdomain: The DNS name of the domain
1572 :param domainsid: The SID of the domain
1573 :param domaindn: The DN of the domain (ie. DC=...)
1574 :param samdb: An LDB object on the SAM db
1575 :param lp: an LP object
1578 # Set ACL for GPO root folder
1579 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1580 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid),
1581 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=SYSVOL_SERVICE)
1583 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1584 attrs=["cn", "nTSecurityDescriptor"],
1585 expression="", scope=ldb.SCOPE_ONELEVEL)
1588 acl = ndr_unpack(security.descriptor,
1589 str(policy["nTSecurityDescriptor"])).as_sddl()
1590 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1591 set_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1592 str(domainsid), use_ntvfs,
1596 def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain,
1597 domaindn, lp, use_ntvfs):
1598 """Set the ACL for the sysvol share and the subfolders
1600 :param samdb: An LDB object on the SAM db
1601 :param netlogon: Physical path for the netlogon folder
1602 :param sysvol: Physical path for the sysvol folder
1603 :param uid: The UID of the "Administrator" user
1604 :param gid: The GID of the "Domain adminstrators" group
1605 :param domainsid: The SID of the domain
1606 :param dnsdomain: The DNS name of the domain
1607 :param domaindn: The DN of the domain (ie. DC=...)
1612 s3conf = s3param.get_context()
1613 s3conf.load(lp.configfile)
1615 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(sysvol))
1618 smbd.set_simple_acl(file.name, 0o755, gid)
1620 if not smbd.have_posix_acls():
1621 # This clue is only strictly correct for RPM and
1622 # Debian-like Linux systems, but hopefully other users
1623 # will get enough clue from it.
1624 raise ProvisioningError("Samba was compiled without the posix ACL support that s3fs requires. "
1625 "Try installing libacl1-dev or libacl-devel, then re-run configure and make.")
1627 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. "
1628 "Try the mounting the filesystem with the 'acl' option.")
1630 smbd.chown(file.name, uid, gid)
1632 raise ProvisioningError("Unable to chown a file on your filesystem. "
1633 "You may not be running provision as root.")
1637 # This will ensure that the smbd code we are running when setting ACLs
1638 # is initialised with the smb.conf
1639 s3conf = s3param.get_context()
1640 s3conf.load(lp.configfile)
1641 # ensure we are using the right samba_dsdb passdb backend, no matter what
1642 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1643 passdb.reload_static_pdb()
1645 # ensure that we init the samba_dsdb backend, so the domain sid is
1646 # marked in secrets.tdb
1647 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1649 # now ensure everything matches correctly, to avoid wierd issues
1650 if passdb.get_global_sam_sid() != domainsid:
1651 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))
1653 domain_info = s4_passdb.domain_info()
1654 if domain_info["dom_sid"] != domainsid:
1655 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))
1657 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1658 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()))
1663 os.chown(sysvol, -1, gid)
1669 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1670 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs,
1671 skip_invalid_chown=True, passdb=s4_passdb,
1672 service=SYSVOL_SERVICE)
1673 for root, dirs, files in os.walk(sysvol, topdown=False):
1675 if use_ntvfs and canchown:
1676 os.chown(os.path.join(root, name), -1, gid)
1677 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid),
1678 use_ntvfs=use_ntvfs, skip_invalid_chown=True,
1679 passdb=s4_passdb, service=SYSVOL_SERVICE)
1681 if use_ntvfs and canchown:
1682 os.chown(os.path.join(root, name), -1, gid)
1683 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid),
1684 use_ntvfs=use_ntvfs, skip_invalid_chown=True,
1685 passdb=s4_passdb, service=SYSVOL_SERVICE)
1687 # Set acls on Policy folder and policies folders
1688 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb=s4_passdb)
1690 def acl_type(direct_db_access):
1691 if direct_db_access:
1696 def check_dir_acl(path, acl, lp, domainsid, direct_db_access):
1697 fsacl = getntacl(lp, path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1698 fsacl_sddl = fsacl.as_sddl(domainsid)
1699 if fsacl_sddl != acl:
1700 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))
1702 for root, dirs, files in os.walk(path, topdown=False):
1704 fsacl = getntacl(lp, os.path.join(root, name),
1705 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1707 raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1708 fsacl_sddl = fsacl.as_sddl(domainsid)
1709 if fsacl_sddl != acl:
1710 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))
1713 fsacl = getntacl(lp, os.path.join(root, name),
1714 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1716 raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1717 fsacl_sddl = fsacl.as_sddl(domainsid)
1718 if fsacl_sddl != acl:
1719 raise ProvisioningError('%s ACL on GPO 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))
1722 def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1724 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1727 :param sysvol: Physical path for the sysvol folder
1728 :param dnsdomain: The DNS name of the domain
1729 :param domainsid: The SID of the domain
1730 :param domaindn: The DN of the domain (ie. DC=...)
1731 :param samdb: An LDB object on the SAM db
1732 :param lp: an LP object
1735 # Set ACL for GPO root folder
1736 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1737 fsacl = getntacl(lp, root_policy_path,
1738 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1740 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access), root_policy_path))
1741 fsacl_sddl = fsacl.as_sddl(domainsid)
1742 if fsacl_sddl != POLICIES_ACL:
1743 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))
1744 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1745 attrs=["cn", "nTSecurityDescriptor"],
1746 expression="", scope=ldb.SCOPE_ONELEVEL)
1749 acl = ndr_unpack(security.descriptor,
1750 str(policy["nTSecurityDescriptor"])).as_sddl()
1751 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1752 check_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1753 domainsid, direct_db_access)
1756 def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn,
1758 """Set the ACL for the sysvol share and the subfolders
1760 :param samdb: An LDB object on the SAM db
1761 :param netlogon: Physical path for the netlogon folder
1762 :param sysvol: Physical path for the sysvol folder
1763 :param uid: The UID of the "Administrator" user
1764 :param gid: The GID of the "Domain adminstrators" group
1765 :param domainsid: The SID of the domain
1766 :param dnsdomain: The DNS name of the domain
1767 :param domaindn: The DN of the domain (ie. DC=...)
1770 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1771 s3conf = s3param.get_context()
1772 s3conf.load(lp.configfile)
1773 # ensure we are using the right samba_dsdb passdb backend, no matter what
1774 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1775 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1776 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1778 # now ensure everything matches correctly, to avoid wierd issues
1779 if passdb.get_global_sam_sid() != domainsid:
1780 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))
1782 domain_info = s4_passdb.domain_info()
1783 if domain_info["dom_sid"] != domainsid:
1784 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))
1786 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1787 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()))
1789 # Ensure we can read this directly, and via the smbd VFS
1790 for direct_db_access in [True, False]:
1791 # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1792 for dir_path in [os.path.join(sysvol, dnsdomain), netlogon]:
1793 fsacl = getntacl(lp, dir_path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1795 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access), dir_path))
1796 fsacl_sddl = fsacl.as_sddl(domainsid)
1797 if fsacl_sddl != SYSVOL_ACL:
1798 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))
1800 # Check acls on Policy folder and policies folders
1801 check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1805 def interface_ips_v4(lp):
1806 """return only IPv4 IPs"""
1807 ips = samba.interface_ips(lp, False)
1810 if i.find(':') == -1:
1815 def interface_ips_v6(lp):
1816 """return only IPv6 IPs"""
1817 ips = samba.interface_ips(lp, False)
1820 if i.find(':') != -1:
1825 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1827 targetdir=None, samdb_fill=FILL_FULL,
1828 hostip=None, hostip6=None,
1829 next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1830 domainguid=None, policyguid=None, policyguid_dc=None,
1831 invocationid=None, machinepass=None, ntdsguid=None,
1832 dns_backend=None, dnspass=None,
1833 serverrole=None, dom_for_fun_level=None,
1834 am_rodc=False, lp=None, use_ntvfs=False, skip_sysvolacl=False):
1835 # create/adapt the group policy GUIDs
1836 # Default GUID for default policy are described at
1837 # "How Core Group Policy Works"
1838 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1839 if policyguid is None:
1840 policyguid = DEFAULT_POLICY_GUID
1841 policyguid = policyguid.upper()
1842 if policyguid_dc is None:
1843 policyguid_dc = DEFAULT_DC_POLICY_GUID
1844 policyguid_dc = policyguid_dc.upper()
1846 if invocationid is None:
1847 invocationid = str(uuid.uuid4())
1849 if krbtgtpass is None:
1850 krbtgtpass = samba.generate_random_machine_password(128, 255)
1851 if machinepass is None:
1852 machinepass = samba.generate_random_machine_password(128, 255)
1854 dnspass = samba.generate_random_password(128, 255)
1856 samdb.transaction_start()
1858 samdb = fill_samdb(samdb, lp, names, logger=logger,
1860 policyguid=policyguid, policyguid_dc=policyguid_dc,
1861 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1862 invocationid=invocationid, machinepass=machinepass,
1863 dns_backend=dns_backend, dnspass=dnspass,
1864 ntdsguid=ntdsguid, serverrole=serverrole,
1865 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1866 next_rid=next_rid, dc_rid=dc_rid)
1868 # Set up group policies (domain policy and domain controller
1870 if serverrole == "active directory domain controller":
1871 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1874 samdb.transaction_cancel()
1877 samdb.transaction_commit()
1879 if serverrole == "active directory domain controller":
1880 # Continue setting up sysvol for GPO. This appears to require being
1881 # outside a transaction.
1882 if not skip_sysvolacl:
1883 setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.root_uid,
1884 paths.root_gid, names.domainsid, names.dnsdomain,
1885 names.domaindn, lp, use_ntvfs)
1887 logger.info("Setting acl on sysvol skipped")
1889 secretsdb_self_join(secrets_ldb, domain=names.domain,
1890 realm=names.realm, dnsdomain=names.dnsdomain,
1891 netbiosname=names.netbiosname, domainsid=names.domainsid,
1892 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1894 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1895 # In future, this might be determined from some configuration
1896 kerberos_enctypes = str(ENC_ALL_TYPES)
1899 msg = ldb.Message(ldb.Dn(samdb,
1900 samdb.searchone("distinguishedName",
1901 expression="samAccountName=%s$" % names.netbiosname,
1902 scope=ldb.SCOPE_SUBTREE)))
1903 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1904 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1905 name="msDS-SupportedEncryptionTypes")
1907 except ldb.LdbError as e:
1908 (enum, estr) = e.args
1909 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1910 # It might be that this attribute does not exist in this schema
1913 setup_ad_dns(samdb, secrets_ldb, names, paths, lp, logger,
1914 hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
1915 dnspass=dnspass, os_level=dom_for_fun_level,
1916 targetdir=targetdir, fill_level=samdb_fill)
1918 domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1919 attribute="objectGUID")
1920 assert isinstance(domainguid, str)
1922 lastProvisionUSNs = get_last_provision_usn(samdb)
1923 maxUSN = get_max_usn(samdb, str(names.rootdn))
1924 if lastProvisionUSNs is not None:
1925 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1927 set_provision_usn(samdb, 0, maxUSN, invocationid)
1929 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1930 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1931 { 'NTDSGUID' : names.ntdsguid })
1933 # fix any dangling GUIDs from the provision
1934 logger.info("Fixing provision GUIDs")
1935 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
1937 samdb.transaction_start()
1939 # a small number of GUIDs are missing because of ordering issues in the
1941 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1942 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1943 scope=ldb.SCOPE_BASE,
1944 attrs=['defaultObjectCategory'])
1945 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1946 scope=ldb.SCOPE_ONELEVEL,
1947 attrs=['ipsecOwnersReference',
1948 'ipsecFilterReference',
1949 'ipsecISAKMPReference',
1950 'ipsecNegotiationPolicyReference',
1951 'ipsecNFAReference'])
1952 if chk.check_database(DN=names.schemadn, scope=ldb.SCOPE_SUBTREE,
1953 attrs=['attributeId', 'governsId']) != 0:
1954 raise ProvisioningError("Duplicate attributeId or governsId in schema. Must be fixed manually!!")
1956 samdb.transaction_cancel()
1959 samdb.transaction_commit()
1963 "ROLE_STANDALONE": "standalone server",
1964 "ROLE_DOMAIN_MEMBER": "member server",
1965 "ROLE_DOMAIN_BDC": "active directory domain controller",
1966 "ROLE_DOMAIN_PDC": "active directory domain controller",
1967 "dc": "active directory domain controller",
1968 "member": "member server",
1969 "domain controller": "active directory domain controller",
1970 "active directory domain controller": "active directory domain controller",
1971 "member server": "member server",
1972 "standalone": "standalone server",
1973 "standalone server": "standalone server",
1977 def sanitize_server_role(role):
1978 """Sanitize a server role name.
1980 :param role: Server role
1981 :raise ValueError: If the role can not be interpreted
1982 :return: Sanitized server role (one of "member server",
1983 "active directory domain controller", "standalone server")
1986 return _ROLES_MAP[role]
1988 raise ValueError(role)
1991 def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain,
1993 """Create AD entries for the fake ypserver.
1995 This is needed for being able to manipulate posix attrs via ADUC.
1997 samdb.transaction_start()
1999 logger.info("Setting up fake yp server settings")
2000 setup_add_ldif(samdb, setup_path("ypServ30.ldif"), {
2001 "DOMAINDN": domaindn,
2002 "NETBIOSNAME": netbiosname,
2003 "NISDOMAIN": nisdomain,
2006 samdb.transaction_cancel()
2009 samdb.transaction_commit()
2011 def directory_create_or_exists(path, mode=0o755):
2012 if not os.path.exists(path):
2014 os.mkdir(path, mode)
2015 except OSError as e:
2016 if e.errno in [errno.EEXIST]:
2019 raise ProvisioningError("Failed to create directory %s: %s" % (path, e.strerror))
2021 def provision(logger, session_info, smbconf=None,
2022 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
2023 domaindn=None, schemadn=None, configdn=None, serverdn=None,
2024 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
2025 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None,
2026 krbtgtpass=None, domainguid=None, policyguid=None, policyguid_dc=None,
2027 dns_backend=None, dns_forwarder=None, dnspass=None,
2028 invocationid=None, machinepass=None, ntdsguid=None,
2029 root=None, nobody=None, users=None, backup=None, aci=None,
2030 serverrole=None, dom_for_fun_level=None, backend_type=None,
2031 sitename=None, ol_mmr_urls=None, ol_olc=None, slapd_path=None,
2032 useeadb=False, am_rodc=False, lp=None, use_ntvfs=False,
2033 use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True,
2034 ldap_backend_forced_uri=None, nosync=False, ldap_dryrun_mode=False,
2035 ldap_backend_extra_port=None, base_schema=None,
2036 plaintext_secrets=False):
2039 :note: caution, this wipes all existing data!
2043 serverrole = sanitize_server_role(serverrole)
2045 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole)
2047 if ldapadminpass is None:
2048 # Make a new, random password between Samba and it's LDAP server
2049 ldapadminpass = samba.generate_random_password(128, 255)
2051 if backend_type is None:
2052 backend_type = "ldb"
2054 if domainsid is None:
2055 domainsid = security.random_sid()
2057 root_uid = findnss_uid([root or "root"])
2058 nobody_uid = findnss_uid([nobody or "nobody"])
2059 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
2060 root_gid = pwd.getpwuid(root_uid).pw_gid
2063 bind_gid = findnss_gid(["bind", "named"])
2067 if targetdir is not None:
2068 smbconf = os.path.join(targetdir, "etc", "smb.conf")
2069 elif smbconf is None:
2070 smbconf = samba.param.default_path()
2071 if not os.path.exists(os.path.dirname(smbconf)):
2072 os.makedirs(os.path.dirname(smbconf))
2074 server_services = []
2077 global_param["idmap_ldb:use rfc2307"] = ["yes"]
2079 if dns_backend != "SAMBA_INTERNAL":
2080 server_services.append("-dns")
2082 if dns_forwarder is not None:
2083 global_param["dns forwarder"] = [dns_forwarder]
2086 server_services.append("+smb")
2087 server_services.append("-s3fs")
2088 global_param["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
2090 if len(server_services) > 0:
2091 global_param["server services"] = server_services
2093 # only install a new smb.conf if there isn't one there already
2094 if os.path.exists(smbconf):
2095 # if Samba Team members can't figure out the weird errors
2096 # loading an empty smb.conf gives, then we need to be smarter.
2097 # Pretend it just didn't exist --abartlet
2098 f = open(smbconf, 'r')
2100 data = f.read().lstrip()
2103 if data is None or data == "":
2104 make_smbconf(smbconf, hostname, domain, realm,
2105 targetdir, serverrole=serverrole,
2106 eadb=useeadb, use_ntvfs=use_ntvfs,
2107 lp=lp, global_param=global_param)
2109 make_smbconf(smbconf, hostname, domain, realm, targetdir,
2110 serverrole=serverrole,
2111 eadb=useeadb, use_ntvfs=use_ntvfs, lp=lp, global_param=global_param)
2114 lp = samba.param.LoadParm()
2116 names = guess_names(lp=lp, hostname=hostname, domain=domain,
2117 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
2118 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
2119 sitename=sitename, rootdn=rootdn, domain_names_forced=(samdb_fill == FILL_DRS))
2120 paths = provision_paths_from_lp(lp, names.dnsdomain)
2122 paths.bind_gid = bind_gid
2123 paths.root_uid = root_uid;
2124 paths.root_gid = root_gid
2127 logger.info("Looking up IPv4 addresses")
2128 hostips = interface_ips_v4(lp)
2129 if len(hostips) > 0:
2131 if len(hostips) > 1:
2132 logger.warning("More than one IPv4 address found. Using %s",
2134 if hostip == "127.0.0.1":
2137 logger.warning("No IPv4 address will be assigned")
2140 logger.info("Looking up IPv6 addresses")
2141 hostips = interface_ips_v6(lp)
2143 hostip6 = hostips[0]
2144 if len(hostips) > 1:
2145 logger.warning("More than one IPv6 address found. Using %s", hostip6)
2147 logger.warning("No IPv6 address will be assigned")
2149 names.hostip = hostip
2150 names.hostip6 = hostip6
2151 names.domainguid = domainguid
2152 names.domainsid = domainsid
2153 names.forestsid = domainsid
2155 if serverrole is None:
2156 serverrole = lp.get("server role")
2158 directory_create_or_exists(paths.private_dir, 0o700)
2159 directory_create_or_exists(paths.binddns_dir, 0o770)
2160 directory_create_or_exists(os.path.join(paths.private_dir, "tls"))
2161 directory_create_or_exists(paths.state_dir)
2162 if not plaintext_secrets:
2163 setup_encrypted_secrets_key(paths.encrypted_secrets_key_path)
2165 if paths.sysvol and not os.path.exists(paths.sysvol):
2166 os.makedirs(paths.sysvol, 0o775)
2168 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
2170 schema = Schema(domainsid, invocationid=invocationid,
2171 schemadn=names.schemadn, base_schema=base_schema)
2173 if backend_type == "ldb":
2174 provision_backend = LDBBackend(backend_type, paths=paths,
2176 names=names, logger=logger)
2177 elif backend_type == "existing":
2178 # If support for this is ever added back, then the URI will need to be
2180 provision_backend = ExistingBackend(backend_type, paths=paths,
2182 names=names, logger=logger,
2183 ldap_backend_forced_uri=ldap_backend_forced_uri)
2184 elif backend_type == "fedora-ds":
2185 provision_backend = FDSBackend(backend_type, paths=paths,
2187 names=names, logger=logger, domainsid=domainsid,
2188 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2189 slapd_path=slapd_path,
2191 elif backend_type == "openldap":
2192 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
2194 names=names, logger=logger, domainsid=domainsid,
2195 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2196 slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls,
2197 ldap_backend_extra_port=ldap_backend_extra_port,
2198 ldap_dryrun_mode=ldap_dryrun_mode, nosync=nosync,
2199 ldap_backend_forced_uri=ldap_backend_forced_uri)
2201 raise ValueError("Unknown LDAP backend type selected")
2203 provision_backend.init()
2204 provision_backend.start()
2206 # only install a new shares config db if there is none
2207 if not os.path.exists(paths.shareconf):
2208 logger.info("Setting up share.ldb")
2209 share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
2210 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
2212 logger.info("Setting up secrets.ldb")
2213 secrets_ldb = setup_secretsdb(paths,
2214 session_info=session_info,
2215 backend_credentials=provision_backend.credentials, lp=lp)
2218 logger.info("Setting up the registry")
2219 setup_registry(paths.hklm, session_info, lp=lp)
2221 logger.info("Setting up the privileges database")
2222 setup_privileges(paths.privilege, session_info, lp=lp)
2224 logger.info("Setting up idmap db")
2225 idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
2227 setup_name_mappings(idmap, sid=str(domainsid),
2228 root_uid=root_uid, nobody_uid=nobody_uid,
2229 users_gid=users_gid, root_gid=root_gid)
2231 logger.info("Setting up SAM db")
2232 samdb = setup_samdb(paths.samdb, session_info,
2233 provision_backend, lp, names, logger=logger,
2234 serverrole=serverrole,
2235 schema=schema, fill=samdb_fill, am_rodc=am_rodc,
2236 plaintext_secrets=plaintext_secrets)
2238 if serverrole == "active directory domain controller":
2239 if paths.netlogon is None:
2240 raise MissingShareError("netlogon", paths.smbconf)
2242 if paths.sysvol is None:
2243 raise MissingShareError("sysvol", paths.smbconf)
2245 if not os.path.isdir(paths.netlogon):
2246 os.makedirs(paths.netlogon, 0o755)
2248 if adminpass is None:
2249 adminpass = samba.generate_random_password(12, 32)
2250 adminpass_generated = True
2252 adminpass = unicode(adminpass, 'utf-8')
2253 adminpass_generated = False
2255 if samdb_fill == FILL_FULL:
2256 provision_fill(samdb, secrets_ldb, logger, names, paths,
2257 schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
2258 hostip=hostip, hostip6=hostip6,
2259 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
2260 krbtgtpass=krbtgtpass,
2261 policyguid=policyguid, policyguid_dc=policyguid_dc,
2262 invocationid=invocationid, machinepass=machinepass,
2263 ntdsguid=ntdsguid, dns_backend=dns_backend,
2264 dnspass=dnspass, serverrole=serverrole,
2265 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
2266 lp=lp, use_ntvfs=use_ntvfs,
2267 skip_sysvolacl=skip_sysvolacl)
2269 if not is_heimdal_built():
2270 create_kdc_conf(paths.kdcconf, realm, domain, os.path.dirname(lp.get("log file")))
2271 logger.info("The Kerberos KDC configuration for Samba AD is "
2272 "located at %s", paths.kdcconf)
2274 create_krb5_conf(paths.krb5conf,
2275 dnsdomain=names.dnsdomain, hostname=names.hostname,
2277 logger.info("A Kerberos configuration suitable for Samba AD has been "
2278 "generated at %s", paths.krb5conf)
2279 logger.info("Merge the contents of this file with your system "
2280 "krb5.conf or replace it with this one. Do not create a "
2283 if serverrole == "active directory domain controller":
2284 create_dns_update_list(lp, logger, paths)
2286 backend_result = provision_backend.post_setup()
2287 provision_backend.shutdown()
2290 secrets_ldb.transaction_cancel()
2293 # Now commit the secrets.ldb to disk
2294 secrets_ldb.transaction_commit()
2296 # the commit creates the dns.keytab in the private directory
2297 private_dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
2298 bind_dns_keytab_path = os.path.join(paths.binddns_dir, paths.dns_keytab)
2300 if os.path.isfile(private_dns_keytab_path):
2301 if os.path.isfile(bind_dns_keytab_path):
2303 os.unlink(bind_dns_keytab_path)
2304 except OSError as e:
2305 logger.error("Failed to remove %s: %s" %
2306 (bind_dns_keytab_path, e.strerror))
2308 # link the dns.keytab to the bind-dns directory
2310 os.link(private_dns_keytab_path, bind_dns_keytab_path)
2311 except OSError as e:
2312 logger.error("Failed to create link %s -> %s: %s" %
2313 (private_dns_keytab_path, bind_dns_keytab_path, e.strerror))
2315 # chown the dns.keytab in the bind-dns directory
2316 if paths.bind_gid is not None:
2318 os.chmod(paths.binddns_dir, 0o770)
2319 os.chown(paths.binddns_dir, -1, paths.bind_gid)
2321 if not os.environ.has_key('SAMBA_SELFTEST'):
2322 logger.info("Failed to chown %s to bind gid %u",
2323 paths.binddns_dir, paths.bind_gid)
2326 os.chmod(bind_dns_keytab_path, 0o640)
2327 os.chown(bind_dns_keytab_path, -1, paths.bind_gid)
2329 if not os.environ.has_key('SAMBA_SELFTEST'):
2330 logger.info("Failed to chown %s to bind gid %u",
2331 bind_dns_keytab_path, paths.bind_gid)
2333 result = ProvisionResult()
2334 result.server_role = serverrole
2335 result.domaindn = domaindn
2336 result.paths = paths
2337 result.names = names
2339 result.samdb = samdb
2340 result.idmap = idmap
2341 result.domainsid = str(domainsid)
2343 if samdb_fill == FILL_FULL:
2344 result.adminpass_generated = adminpass_generated
2345 result.adminpass = adminpass
2347 result.adminpass_generated = False
2348 result.adminpass = None
2350 result.backend_result = backend_result
2353 provision_fake_ypserver(logger=logger, samdb=samdb,
2354 domaindn=names.domaindn, netbiosname=names.netbiosname,
2355 nisdomain=names.domain.lower(), maxuid=maxuid, maxgid=maxgid)
2360 def provision_become_dc(smbconf=None, targetdir=None,
2361 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
2362 serverdn=None, domain=None, hostname=None, domainsid=None,
2363 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
2364 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
2365 dns_backend=None, root=None, nobody=None, users=None,
2366 backup=None, serverrole=None, ldap_backend=None,
2367 ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False):
2369 logger = logging.getLogger("provision")
2370 samba.set_debug_level(debuglevel)
2372 res = provision(logger, system_session(),
2373 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
2374 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
2375 configdn=configdn, serverdn=serverdn, domain=domain,
2376 hostname=hostname, hostip=None, domainsid=domainsid,
2377 machinepass=machinepass,
2378 serverrole="active directory domain controller",
2379 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass,
2380 use_ntvfs=use_ntvfs)
2381 res.lp.set("debuglevel", str(debuglevel))
2385 def create_krb5_conf(path, dnsdomain, hostname, realm):
2386 """Write out a file containing a valid krb5.conf file
2388 :param path: Path of the new krb5.conf file.
2389 :param dnsdomain: DNS Domain name
2390 :param hostname: Local hostname
2391 :param realm: Realm name
2393 setup_file(setup_path("krb5.conf"), path, {
2394 "DNSDOMAIN": dnsdomain,
2395 "HOSTNAME": hostname,
2400 class ProvisioningError(Exception):
2401 """A generic provision error."""
2403 def __init__(self, value):
2407 return "ProvisioningError: " + self.value
2410 class InvalidNetbiosName(Exception):
2411 """A specified name was not a valid NetBIOS name."""
2413 def __init__(self, name):
2414 super(InvalidNetbiosName, self).__init__(
2415 "The name '%r' is not a valid NetBIOS name" % name)
2418 class MissingShareError(ProvisioningError):
2420 def __init__(self, name, smbconf):
2421 super(MissingShareError, self).__init__(
2422 "Existing smb.conf does not have a [%s] share, but you are "
2423 "configuring a DC. Please remove %s or add the share manually." %