1 # Unix SMB/CIFS implementation.
2 # backend code for provisioning a Samba4 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
44 from samba.auth import system_session, admin_session
46 from samba.samba3 import smbd, passdb
47 from samba.samba3 import param as s3param
48 from samba.dsdb import DS_DOMAIN_FUNCTION_2000
52 check_all_substituted,
53 is_valid_netbios_char,
59 from samba.dcerpc import security, misc
60 from samba.dcerpc.misc import (
64 from samba.dsdb import (
65 DS_DOMAIN_FUNCTION_2003,
66 DS_DOMAIN_FUNCTION_2008_R2,
69 from samba.idmap import IDmapDB
70 from samba.ms_display_specifiers import read_ms_ldif
71 from samba.ntacls import setntacl, getntacl, dsacl2fsacl
72 from samba.ndr import ndr_pack, ndr_unpack
73 from samba.provision.backend import (
79 from samba.descriptor import (
81 get_config_descriptor,
82 get_config_partitions_descriptor,
83 get_config_sites_descriptor,
84 get_config_ntds_quotas_descriptor,
85 get_config_delete_protected1_descriptor,
86 get_config_delete_protected1wd_descriptor,
87 get_config_delete_protected2_descriptor,
88 get_domain_descriptor,
89 get_domain_infrastructure_descriptor,
90 get_domain_builtin_descriptor,
91 get_domain_computers_descriptor,
92 get_domain_users_descriptor,
93 get_domain_controllers_descriptor,
94 get_domain_delete_protected1_descriptor,
95 get_domain_delete_protected2_descriptor,
96 get_dns_partition_descriptor,
97 get_dns_forest_microsoft_dns_descriptor,
98 get_dns_domain_microsoft_dns_descriptor,
100 from samba.provision.common import (
109 from samba.provision.sambadns import (
112 create_dns_update_list
116 import samba.registry
117 from samba.schema import Schema
118 from samba.samdb import SamDB
119 from samba.dbchecker import dbcheck
122 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
123 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04fB984F9"
124 DEFAULTSITE = "Default-First-Site-Name"
125 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
128 class ProvisionPaths(object):
131 self.shareconf = None
142 self.dns_keytab = None
145 self.private_dir = None
146 self.state_dir = None
149 class ProvisionNames(object):
157 self.dnsforestdn = None
158 self.dnsdomaindn = None
159 self.ldapmanagerdn = None
160 self.dnsdomain = None
162 self.netbiosname = None
167 self.domainsid = None
168 self.forestsid = None
169 self.domainguid = None
173 def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf,
175 """Get key provision parameters (realm, domain, ...) from a given provision
177 :param samdb: An LDB object connected to the sam.ldb file
178 :param secretsdb: An LDB object connected to the secrets.ldb file
179 :param idmapdb: An LDB object connected to the idmap.ldb file
180 :param paths: A list of path to provision object
181 :param smbconf: Path to the smb.conf file
182 :param lp: A LoadParm object
183 :return: A list of key provision parameters
185 names = ProvisionNames()
186 names.adminpass = None
188 # NT domain, kerberos realm, root dn, domain dn, domain dns name
189 names.domain = string.upper(lp.get("workgroup"))
190 names.realm = lp.get("realm")
191 names.dnsdomain = names.realm.lower()
192 basedn = samba.dn_from_dns_name(names.dnsdomain)
193 names.realm = string.upper(names.realm)
195 # Get the netbiosname first (could be obtained from smb.conf in theory)
196 res = secretsdb.search(expression="(flatname=%s)" %
197 names.domain,base="CN=Primary Domains",
198 scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"])
199 names.netbiosname = str(res[0]["sAMAccountName"]).replace("$","")
201 names.smbconf = smbconf
203 # That's a bit simplistic but it's ok as long as we have only 3
205 current = samdb.search(expression="(objectClass=*)",
206 base="", scope=ldb.SCOPE_BASE,
207 attrs=["defaultNamingContext", "schemaNamingContext",
208 "configurationNamingContext","rootDomainNamingContext",
211 names.configdn = current[0]["configurationNamingContext"][0]
212 names.schemadn = current[0]["schemaNamingContext"][0]
213 if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb,
214 current[0]["defaultNamingContext"][0]))):
215 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
216 "is not the same ..." % (paths.samdb,
217 str(current[0]["defaultNamingContext"][0]),
218 paths.smbconf, basedn)))
220 names.domaindn=current[0]["defaultNamingContext"][0]
221 names.rootdn=current[0]["rootDomainNamingContext"][0]
222 names.ncs=current[0]["namingContexts"]
223 names.dnsforestdn = None
224 names.dnsdomaindn = None
226 for i in range(0, len(names.ncs)):
229 dnsforestdn = "DC=ForestDnsZones,%s" % (str(names.rootdn))
230 if nc == dnsforestdn:
231 names.dnsforestdn = dnsforestdn
234 dnsdomaindn = "DC=DomainDnsZones,%s" % (str(names.domaindn))
235 if nc == dnsdomaindn:
236 names.dnsdomaindn = dnsdomaindn
240 res3 = samdb.search(expression="(objectClass=site)",
241 base="CN=Sites," + names.configdn, scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
242 names.sitename = str(res3[0]["cn"])
244 # dns hostname and server dn
245 res4 = samdb.search(expression="(CN=%s)" % names.netbiosname,
246 base="OU=Domain Controllers,%s" % basedn,
247 scope=ldb.SCOPE_ONELEVEL, attrs=["dNSHostName"])
249 raise ProvisioningError("Unable to find DC called CN=%s under OU=Domain Controllers,%s" % (names.netbiosname, basedn))
251 names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain, "")
253 server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
254 attrs=[], base=names.configdn)
255 names.serverdn = str(server_res[0].dn)
257 # invocation id/objectguid
258 res5 = samdb.search(expression="(objectClass=*)",
259 base="CN=NTDS Settings,%s" % str(names.serverdn),
260 scope=ldb.SCOPE_BASE,
261 attrs=["invocationID", "objectGUID"])
262 names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0]))
263 names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0]))
266 res6 = samdb.search(expression="(objectClass=*)", base=basedn,
267 scope=ldb.SCOPE_BASE, attrs=["objectGUID",
268 "objectSid","msDS-Behavior-Version" ])
269 names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
270 names.domainsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
271 names.forestsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
272 if res6[0].get("msDS-Behavior-Version") is None or \
273 int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
274 names.domainlevel = DS_DOMAIN_FUNCTION_2000
276 names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
279 res7 = samdb.search(expression="(name={%s})" % DEFAULT_POLICY_GUID,
280 base="CN=Policies,CN=System," + basedn,
281 scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
282 names.policyid = str(res7[0]["cn"]).replace("{","").replace("}","")
284 res8 = samdb.search(expression="(name={%s})" % DEFAULT_DC_POLICY_GUID,
285 base="CN=Policies,CN=System," + basedn,
286 scope=ldb.SCOPE_ONELEVEL,
287 attrs=["cn","displayName"])
289 names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","")
291 names.policyid_dc = None
293 res9 = idmapdb.search(expression="(cn=%s-%s)" %
294 (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR),
295 attrs=["xidNumber", "type"])
297 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid (%s-%s" % (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR))
298 if res9[0]["type"][0] == "ID_TYPE_BOTH":
299 names.root_gid = res9[0]["xidNumber"][0]
301 names.root_gid = pwd.getpwuid(int(res9[0]["xidNumber"][0])).pw_gid
303 res10 = samdb.search(expression="(samaccountname=dns)",
304 scope=ldb.SCOPE_SUBTREE, attrs=["dn"],
305 controls=["search_options:1:2"])
307 has_legacy_dns_account = True
309 has_legacy_dns_account = False
311 res11 = samdb.search(expression="(samaccountname=dns-%s)" % names.netbiosname,
312 scope=ldb.SCOPE_SUBTREE, attrs=["dn"],
313 controls=["search_options:1:2"])
315 has_dns_account = True
317 has_dns_account = False
319 if names.dnsdomaindn is not None:
321 names.dns_backend = 'BIND9_DLZ'
323 names.dns_backend = 'SAMBA_INTERNAL'
324 elif has_dns_account or has_legacy_dns_account:
325 names.dns_backend = 'BIND9_FLATFILE'
327 names.dns_backend = 'NONE'
329 dns_admins_sid = get_dnsadmins_sid(samdb, names.domaindn)
330 names.name_map['DnsAdmins'] = str(dns_admins_sid)
335 def update_provision_usn(samdb, low, high, id, replace=False):
336 """Update the field provisionUSN in sam.ldb
338 This field is used to track range of USN modified by provision and
340 This value is used afterward by next provision to figure out if
341 the field have been modified since last provision.
343 :param samdb: An LDB object connect to sam.ldb
344 :param low: The lowest USN modified by this upgrade
345 :param high: The highest USN modified by this upgrade
346 :param id: The invocation id of the samba's dc
347 :param replace: A boolean indicating if the range should replace any
348 existing one or appended (default)
353 entry = samdb.search(base="@PROVISION",
354 scope=ldb.SCOPE_BASE,
355 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
356 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
357 if not re.search(';', e):
358 e = "%s;%s" % (e, id)
361 tab.append("%s-%s;%s" % (low, high, id))
362 delta = ldb.Message()
363 delta.dn = ldb.Dn(samdb, "@PROVISION")
364 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
365 ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE)
366 entry = samdb.search(expression='provisionnerID=*',
367 base="@PROVISION", scope=ldb.SCOPE_BASE,
368 attrs=["provisionnerID"])
369 if len(entry) == 0 or len(entry[0]) == 0:
370 delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
374 def set_provision_usn(samdb, low, high, id):
375 """Set the field provisionUSN in sam.ldb
376 This field is used to track range of USN modified by provision and
378 This value is used afterward by next provision to figure out if
379 the field have been modified since last provision.
381 :param samdb: An LDB object connect to sam.ldb
382 :param low: The lowest USN modified by this upgrade
383 :param high: The highest USN modified by this upgrade
384 :param id: The invocationId of the provision"""
387 tab.append("%s-%s;%s" % (low, high, id))
389 delta = ldb.Message()
390 delta.dn = ldb.Dn(samdb, "@PROVISION")
391 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
392 ldb.FLAG_MOD_ADD, LAST_PROVISION_USN_ATTRIBUTE)
396 def get_max_usn(samdb,basedn):
397 """ This function return the biggest USN present in the provision
399 :param samdb: A LDB object pointing to the sam.ldb
400 :param basedn: A string containing the base DN of the provision
402 :return: The biggest USN in the provision"""
404 res = samdb.search(expression="objectClass=*",base=basedn,
405 scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
406 controls=["search_options:1:2",
407 "server_sort:1:1:uSNChanged",
408 "paged_results:1:1"])
409 return res[0]["uSNChanged"]
412 def get_last_provision_usn(sam):
413 """Get USNs ranges modified by a provision or an upgradeprovision
415 :param sam: An LDB object pointing to the sam.ldb
416 :return: a dictionary which keys are invocation id and values are an array
417 of integer representing the different ranges
420 entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE,
421 base="@PROVISION", scope=ldb.SCOPE_BASE,
422 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
423 except ldb.LdbError, (ecode, emsg):
424 if ecode == ldb.ERR_NO_SUCH_OBJECT:
431 if entry[0].get("provisionnerID"):
432 for e in entry[0]["provisionnerID"]:
434 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
435 tab1 = str(r).split(';')
440 if (len(myids) > 0 and id not in myids):
442 tab2 = p.split(tab1[0])
443 if range.get(id) is None:
445 range[id].append(tab2[0])
446 range[id].append(tab2[1])
452 class ProvisionResult(object):
453 """Result of a provision.
455 :ivar server_role: The server role
456 :ivar paths: ProvisionPaths instance
457 :ivar domaindn: The domain dn, as string
461 self.server_role = None
468 self.domainsid = None
469 self.adminpass_generated = None
470 self.adminpass = None
471 self.backend_result = None
473 def report_logger(self, logger):
474 """Report this provision result to a logger."""
476 "Once the above files are installed, your Samba4 server will "
478 if self.adminpass_generated:
479 logger.info("Admin password: %s", self.adminpass)
480 logger.info("Server Role: %s", self.server_role)
481 logger.info("Hostname: %s", self.names.hostname)
482 logger.info("NetBIOS Domain: %s", self.names.domain)
483 logger.info("DNS Domain: %s", self.names.dnsdomain)
484 logger.info("DOMAIN SID: %s", self.domainsid)
486 if self.backend_result:
487 self.backend_result.report_logger(logger)
490 def check_install(lp, session_info, credentials):
491 """Check whether the current install seems ok.
493 :param lp: Loadparm context
494 :param session_info: Session information
495 :param credentials: Credentials
497 if lp.get("realm") == "":
498 raise Exception("Realm empty")
499 samdb = Ldb(lp.samdb_url(), session_info=session_info,
500 credentials=credentials, lp=lp)
501 if len(samdb.search("(cn=Administrator)")) != 1:
502 raise ProvisioningError("No administrator account found")
505 def findnss(nssfn, names):
506 """Find a user or group from a list of possibilities.
508 :param nssfn: NSS Function to try (should raise KeyError if not found)
509 :param names: Names to check.
510 :return: Value return by first names list.
517 raise KeyError("Unable to find user/group in %r" % names)
520 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
521 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
524 def provision_paths_from_lp(lp, dnsdomain):
525 """Set the default paths for provisioning.
527 :param lp: Loadparm context.
528 :param dnsdomain: DNS Domain name
530 paths = ProvisionPaths()
531 paths.private_dir = lp.get("private dir")
532 paths.state_dir = lp.get("state directory")
534 # This is stored without path prefix for the "privateKeytab" attribute in
535 # "secrets_dns.ldif".
536 paths.dns_keytab = "dns.keytab"
537 paths.keytab = "secrets.keytab"
539 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
540 paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
541 paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
542 paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
543 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
544 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
545 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
546 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
547 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
548 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
549 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
550 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
551 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
552 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
553 paths.hklm = "hklm.ldb"
554 paths.hkcr = "hkcr.ldb"
555 paths.hkcu = "hkcu.ldb"
556 paths.hku = "hku.ldb"
557 paths.hkpd = "hkpd.ldb"
558 paths.hkpt = "hkpt.ldb"
559 paths.sysvol = lp.get("path", "sysvol")
560 paths.netlogon = lp.get("path", "netlogon")
561 paths.smbconf = lp.configfile
565 def determine_netbios_name(hostname):
566 """Determine a netbios name from a hostname."""
567 # remove forbidden chars and force the length to be <16
568 netbiosname = "".join([x for x in hostname if is_valid_netbios_char(x)])
569 return netbiosname[:MAX_NETBIOS_NAME_LEN].upper()
572 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
573 serverrole=None, rootdn=None, domaindn=None, configdn=None,
574 schemadn=None, serverdn=None, sitename=None,
575 domain_names_forced=False):
576 """Guess configuration settings to use."""
579 hostname = socket.gethostname().split(".")[0]
581 netbiosname = lp.get("netbios name")
582 if netbiosname is None:
583 netbiosname = determine_netbios_name(hostname)
584 netbiosname = netbiosname.upper()
585 if not valid_netbios_name(netbiosname):
586 raise InvalidNetbiosName(netbiosname)
588 if dnsdomain is None:
589 dnsdomain = lp.get("realm")
590 if dnsdomain is None or dnsdomain == "":
591 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
593 dnsdomain = dnsdomain.lower()
595 if serverrole is None:
596 serverrole = lp.get("server role")
597 if serverrole is None:
598 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
600 serverrole = serverrole.lower()
602 realm = dnsdomain.upper()
604 if lp.get("realm") == "":
605 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
607 if lp.get("realm").upper() != realm:
608 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))
610 if lp.get("server role").lower() != serverrole:
611 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))
613 if serverrole == "active directory domain controller":
615 # This will, for better or worse, default to 'WORKGROUP'
616 domain = lp.get("workgroup")
617 domain = domain.upper()
619 if lp.get("workgroup").upper() != domain:
620 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))
623 domaindn = samba.dn_from_dns_name(dnsdomain)
625 if domain == netbiosname:
626 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
630 domaindn = "DC=" + netbiosname
632 if not valid_netbios_name(domain):
633 raise InvalidNetbiosName(domain)
635 if hostname.upper() == realm:
636 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
637 if netbiosname.upper() == realm:
638 raise ProvisioningError("guess_names: Realm '%s' must not be equal to NetBIOS hostname '%s'!" % (realm, netbiosname))
639 if domain == realm and not domain_names_forced:
640 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
646 configdn = "CN=Configuration," + rootdn
648 schemadn = "CN=Schema," + configdn
651 sitename = DEFAULTSITE
653 names = ProvisionNames()
654 names.rootdn = rootdn
655 names.domaindn = domaindn
656 names.configdn = configdn
657 names.schemadn = schemadn
658 names.ldapmanagerdn = "CN=Manager," + rootdn
659 names.dnsdomain = dnsdomain
660 names.domain = domain
662 names.netbiosname = netbiosname
663 names.hostname = hostname
664 names.sitename = sitename
665 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
666 netbiosname, sitename, configdn)
671 def make_smbconf(smbconf, hostname, domain, realm, targetdir,
672 serverrole=None, eadb=False, use_ntvfs=False, lp=None,
674 """Create a new smb.conf file based on a couple of basic settings.
676 assert smbconf is not None
679 hostname = socket.gethostname().split(".")[0]
681 netbiosname = determine_netbios_name(hostname)
683 if serverrole is None:
684 serverrole = "standalone server"
686 assert domain is not None
687 domain = domain.upper()
689 assert realm is not None
690 realm = realm.upper()
693 "netbios name": netbiosname,
696 "server role": serverrole,
700 lp = samba.param.LoadParm()
701 #Load non-existent file
702 if os.path.exists(smbconf):
705 if global_param is not None:
706 for ent in global_param:
707 if global_param[ent] is not None:
708 global_settings[ent] = " ".join(global_param[ent])
710 if targetdir is not None:
711 global_settings["private dir"] = os.path.abspath(os.path.join(targetdir, "private"))
712 global_settings["lock dir"] = os.path.abspath(targetdir)
713 global_settings["state directory"] = os.path.abspath(os.path.join(targetdir, "state"))
714 global_settings["cache directory"] = os.path.abspath(os.path.join(targetdir, "cache"))
716 lp.set("lock dir", os.path.abspath(targetdir))
717 lp.set("state directory", global_settings["state directory"])
718 lp.set("cache directory", global_settings["cache directory"])
721 if use_ntvfs and not lp.get("posix:eadb"):
722 if targetdir is not None:
723 privdir = os.path.join(targetdir, "private")
725 privdir = lp.get("private dir")
726 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
727 elif not use_ntvfs and not lp.get("xattr_tdb:file"):
728 if targetdir is not None:
729 statedir = os.path.join(targetdir, "state")
731 statedir = lp.get("state directory")
732 lp.set("xattr_tdb:file", os.path.abspath(os.path.join(statedir, "xattr.tdb")))
735 if serverrole == "active directory domain controller":
736 shares["sysvol"] = os.path.join(lp.get("state directory"), "sysvol")
737 shares["netlogon"] = os.path.join(shares["sysvol"], realm.lower(),
740 global_settings["passdb backend"] = "samba_dsdb"
742 f = open(smbconf, 'w')
744 f.write("[globals]\n")
745 for key, val in global_settings.iteritems():
746 f.write("\t%s = %s\n" % (key, val))
749 for name, path in shares.iteritems():
750 f.write("[%s]\n" % name)
751 f.write("\tpath = %s\n" % path)
752 f.write("\tread only = no\n")
756 # reload the smb.conf
759 # and dump it without any values that are the default
760 # this ensures that any smb.conf parameters that were set
761 # on the provision/join command line are set in the resulting smb.conf
762 f = open(smbconf, mode='w')
769 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
770 users_gid, root_gid):
771 """setup reasonable name mappings for sam names to unix names.
773 :param samdb: SamDB object.
774 :param idmap: IDmap db object.
775 :param sid: The domain sid.
776 :param domaindn: The domain DN.
777 :param root_uid: uid of the UNIX root user.
778 :param nobody_uid: uid of the UNIX nobody user.
779 :param users_gid: gid of the UNIX users group.
780 :param root_gid: gid of the UNIX root group.
782 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
784 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
785 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
788 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
789 provision_backend, names, schema, serverrole,
791 """Setup the partitions for the SAM database.
793 Alternatively, provision() may call this, and then populate the database.
795 :note: This will wipe the Sam Database!
797 :note: This function always removes the local SAM LDB file. The erase
798 parameter controls whether to erase the existing data, which
799 may not be stored locally but in LDAP.
802 assert session_info is not None
804 # We use options=["modules:"] to stop the modules loading - we
805 # just want to wipe and re-initialise the database, not start it up
808 os.unlink(samdb_path)
812 samdb = Ldb(url=samdb_path, session_info=session_info,
813 lp=lp, options=["modules:"])
815 ldap_backend_line = "# No LDAP backend"
816 if provision_backend.type != "ldb":
817 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
819 samdb.transaction_start()
821 logger.info("Setting up sam.ldb partitions and settings")
822 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
823 "LDAP_BACKEND_LINE": ldap_backend_line
827 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
828 "BACKEND_TYPE": provision_backend.type,
829 "SERVER_ROLE": serverrole
832 logger.info("Setting up sam.ldb rootDSE")
833 setup_samdb_rootdse(samdb, names)
835 samdb.transaction_cancel()
838 samdb.transaction_commit()
841 def secretsdb_self_join(secretsdb, domain,
842 netbiosname, machinepass, domainsid=None,
843 realm=None, dnsdomain=None,
845 key_version_number=1,
846 secure_channel_type=SEC_CHAN_WKSTA):
847 """Add domain join-specific bits to a secrets database.
849 :param secretsdb: Ldb Handle to the secrets database
850 :param machinepass: Machine password
852 attrs = ["whenChanged",
859 if realm is not None:
860 if dnsdomain is None:
861 dnsdomain = realm.lower()
862 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
865 shortname = netbiosname.lower()
867 # We don't need to set msg["flatname"] here, because rdn_name will handle
868 # it, and it causes problems for modifies anyway
869 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
870 msg["secureChannelType"] = [str(secure_channel_type)]
871 msg["objectClass"] = ["top", "primaryDomain"]
872 if dnsname is not None:
873 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
874 msg["realm"] = [realm]
875 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
876 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
877 msg["privateKeytab"] = ["secrets.keytab"]
879 msg["secret"] = [machinepass.encode('utf-8')]
880 msg["samAccountName"] = ["%s$" % netbiosname]
881 msg["secureChannelType"] = [str(secure_channel_type)]
882 if domainsid is not None:
883 msg["objectSid"] = [ndr_pack(domainsid)]
885 # This complex expression tries to ensure that we don't have more
886 # than one record for this SID, realm or netbios domain at a time,
887 # but we don't delete the old record that we are about to modify,
888 # because that would delete the keytab and previous password.
889 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
890 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
891 scope=ldb.SCOPE_ONELEVEL)
894 secretsdb.delete(del_msg.dn)
896 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
899 msg["priorSecret"] = [res[0]["secret"][0]]
900 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
903 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
908 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
914 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
915 secretsdb.modify(msg)
916 secretsdb.rename(res[0].dn, msg.dn)
918 spn = [ 'HOST/%s' % shortname ]
919 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
920 # we are a domain controller then we add servicePrincipalName
921 # entries for the keytab code to update.
922 spn.extend([ 'HOST/%s' % dnsname ])
923 msg["servicePrincipalName"] = spn
928 def setup_secretsdb(paths, session_info, backend_credentials, lp):
929 """Setup the secrets database.
931 :note: This function does not handle exceptions and transaction on purpose,
932 it's up to the caller to do this job.
934 :param path: Path to the secrets database.
935 :param session_info: Session info.
936 :param credentials: Credentials
937 :param lp: Loadparm context
938 :return: LDB handle for the created secrets database
940 if os.path.exists(paths.secrets):
941 os.unlink(paths.secrets)
943 keytab_path = os.path.join(paths.private_dir, paths.keytab)
944 if os.path.exists(keytab_path):
945 os.unlink(keytab_path)
947 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
948 if os.path.exists(dns_keytab_path):
949 os.unlink(dns_keytab_path)
953 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
955 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
956 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
957 secrets_ldb.transaction_start()
959 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
961 if (backend_credentials is not None and
962 backend_credentials.authentication_requested()):
963 if backend_credentials.get_bind_dn() is not None:
964 setup_add_ldif(secrets_ldb,
965 setup_path("secrets_simple_ldap.ldif"), {
966 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
967 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
970 setup_add_ldif(secrets_ldb,
971 setup_path("secrets_sasl_ldap.ldif"), {
972 "LDAPADMINUSER": backend_credentials.get_username(),
973 "LDAPADMINREALM": backend_credentials.get_realm(),
974 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
977 secrets_ldb.transaction_cancel()
982 def setup_privileges(path, session_info, lp):
983 """Setup the privileges database.
985 :param path: Path to the privileges database.
986 :param session_info: Session info.
987 :param credentials: Credentials
988 :param lp: Loadparm context
989 :return: LDB handle for the created secrets database
991 if os.path.exists(path):
993 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
994 privilege_ldb.erase()
995 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
998 def setup_registry(path, session_info, lp):
999 """Setup the registry.
1001 :param path: Path to the registry database
1002 :param session_info: Session information
1003 :param credentials: Credentials
1004 :param lp: Loadparm context
1006 reg = samba.registry.Registry()
1007 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
1008 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
1009 provision_reg = setup_path("provision.reg")
1010 assert os.path.exists(provision_reg)
1011 reg.diff_apply(provision_reg)
1014 def setup_idmapdb(path, session_info, lp):
1015 """Setup the idmap database.
1017 :param path: path to the idmap database
1018 :param session_info: Session information
1019 :param credentials: Credentials
1020 :param lp: Loadparm context
1022 if os.path.exists(path):
1025 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
1027 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
1031 def setup_samdb_rootdse(samdb, names):
1032 """Setup the SamDB rootdse.
1034 :param samdb: Sam Database handle
1036 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
1037 "SCHEMADN": names.schemadn,
1038 "DOMAINDN": names.domaindn,
1039 "ROOTDN" : names.rootdn,
1040 "CONFIGDN": names.configdn,
1041 "SERVERDN": names.serverdn,
1045 def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
1046 dns_backend, dnspass, domainsid, next_rid, invocationid,
1047 policyguid, policyguid_dc,
1048 domainControllerFunctionality, ntdsguid=None, dc_rid=None):
1049 """Join a host to its own domain."""
1050 assert isinstance(invocationid, str)
1051 if ntdsguid is not None:
1052 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
1059 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
1060 "CONFIGDN": names.configdn,
1061 "SCHEMADN": names.schemadn,
1062 "DOMAINDN": names.domaindn,
1063 "SERVERDN": names.serverdn,
1064 "INVOCATIONID": invocationid,
1065 "NETBIOSNAME": names.netbiosname,
1066 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1067 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1068 "DOMAINSID": str(domainsid),
1069 "DCRID": str(dc_rid),
1070 "SAMBA_VERSION_STRING": version,
1071 "NTDSGUID": ntdsguid_line,
1072 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1073 domainControllerFunctionality),
1074 "RIDALLOCATIONSTART": str(next_rid + 100),
1075 "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
1077 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1078 "POLICYGUID": policyguid,
1079 "POLICYGUID_DC": policyguid_dc,
1080 "DNSDOMAIN": names.dnsdomain,
1081 "DOMAINDN": names.domaindn})
1083 # If we are setting up a subdomain, then this has been replicated in, so we
1084 # don't need to add it
1085 if fill == FILL_FULL:
1086 setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
1087 "CONFIGDN": names.configdn,
1088 "SCHEMADN": names.schemadn,
1089 "DOMAINDN": names.domaindn,
1090 "SERVERDN": names.serverdn,
1091 "INVOCATIONID": invocationid,
1092 "NETBIOSNAME": names.netbiosname,
1093 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1094 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1095 "DOMAINSID": str(domainsid),
1096 "DCRID": str(dc_rid),
1097 "SAMBA_VERSION_STRING": version,
1098 "NTDSGUID": ntdsguid_line,
1099 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1100 domainControllerFunctionality)})
1102 # Setup fSMORoleOwner entries to point at the newly created DC entry
1103 setup_modify_ldif(samdb,
1104 setup_path("provision_self_join_modify_config.ldif"), {
1105 "CONFIGDN": names.configdn,
1106 "SCHEMADN": names.schemadn,
1107 "DEFAULTSITE": names.sitename,
1108 "NETBIOSNAME": names.netbiosname,
1109 "SERVERDN": names.serverdn,
1112 system_session_info = system_session()
1113 samdb.set_session_info(system_session_info)
1114 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1115 # modify a serverReference under cn=config when we are a subdomain, we must
1116 # be system due to ACLs
1117 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1118 "DOMAINDN": names.domaindn,
1119 "SERVERDN": names.serverdn,
1120 "NETBIOSNAME": names.netbiosname,
1123 samdb.set_session_info(admin_session_info)
1125 if dns_backend != "SAMBA_INTERNAL":
1126 # This is Samba4 specific and should be replaced by the correct
1127 # DNS AD-style setup
1128 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1129 "DNSDOMAIN": names.dnsdomain,
1130 "DOMAINDN": names.domaindn,
1131 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1132 "HOSTNAME" : names.hostname,
1133 "DNSNAME" : '%s.%s' % (
1134 names.netbiosname.lower(), names.dnsdomain.lower())
1138 def getpolicypath(sysvolpath, dnsdomain, guid):
1139 """Return the physical path of policy given its guid.
1141 :param sysvolpath: Path to the sysvol folder
1142 :param dnsdomain: DNS name of the AD domain
1143 :param guid: The GUID of the policy
1144 :return: A string with the complete path to the policy folder
1147 guid = "{%s}" % guid
1148 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1152 def create_gpo_struct(policy_path):
1153 if not os.path.exists(policy_path):
1154 os.makedirs(policy_path, 0775)
1155 f = open(os.path.join(policy_path, "GPT.INI"), 'w')
1157 f.write("[General]\r\nVersion=0")
1160 p = os.path.join(policy_path, "MACHINE")
1161 if not os.path.exists(p):
1162 os.makedirs(p, 0775)
1163 p = os.path.join(policy_path, "USER")
1164 if not os.path.exists(p):
1165 os.makedirs(p, 0775)
1168 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1169 """Create the default GPO for a domain
1171 :param sysvolpath: Physical path for the sysvol folder
1172 :param dnsdomain: DNS domain name of the AD domain
1173 :param policyguid: GUID of the default domain policy
1174 :param policyguid_dc: GUID of the default domain controler policy
1176 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1177 create_gpo_struct(policy_path)
1179 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1180 create_gpo_struct(policy_path)
1183 def setup_samdb(path, session_info, provision_backend, lp, names,
1184 logger, fill, serverrole, schema, am_rodc=False):
1185 """Setup a complete SAM Database.
1187 :note: This will wipe the main SAM database file!
1190 # Also wipes the database
1191 setup_samdb_partitions(path, logger=logger, lp=lp,
1192 provision_backend=provision_backend, session_info=session_info,
1193 names=names, serverrole=serverrole, schema=schema)
1195 # Load the database, but don's load the global schema and don't connect
1197 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1198 credentials=provision_backend.credentials, lp=lp,
1199 global_schema=False, am_rodc=am_rodc)
1201 logger.info("Pre-loading the Samba 4 and AD schema")
1203 # Load the schema from the one we computed earlier
1204 samdb.set_schema(schema, write_indices_and_attributes=False)
1206 # Set the NTDS settings DN manually - in order to have it already around
1207 # before the provisioned tree exists and we connect
1208 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1210 # And now we can connect to the DB - the schema won't be loaded from the
1214 except ldb.LdbError, (num, string_error):
1215 if (num == ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS):
1216 raise ProvisioningError("Permission denied connecting to %s, are you running as root?" % path)
1220 # But we have to give it one more kick to have it use the schema
1221 # during provision - it needs, now that it is connected, to write
1222 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1223 samdb.set_schema(schema, write_indices_and_attributes=True)
1228 def fill_samdb(samdb, lp, names, logger, policyguid,
1229 policyguid_dc, fill, adminpass, krbtgtpass, machinepass, dns_backend,
1230 dnspass, invocationid, ntdsguid, serverrole, am_rodc=False,
1231 dom_for_fun_level=None, schema=None, next_rid=None, dc_rid=None):
1233 if next_rid is None:
1236 # Provision does not make much sense values larger than 1000000000
1237 # as the upper range of the rIDAvailablePool is 1073741823 and
1238 # we don't want to create a domain that cannot allocate rids.
1239 if next_rid < 1000 or next_rid > 1000000000:
1240 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1241 error += "the valid range is %u-%u. The default is %u." % (
1242 1000, 1000000000, 1000)
1243 raise ProvisioningError(error)
1245 # ATTENTION: Do NOT change these default values without discussion with the
1246 # team and/or release manager. They have a big impact on the whole program!
1247 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1249 if dom_for_fun_level is None:
1250 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
1252 if dom_for_fun_level > domainControllerFunctionality:
1253 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!")
1255 domainFunctionality = dom_for_fun_level
1256 forestFunctionality = dom_for_fun_level
1258 # Set the NTDS settings DN manually - in order to have it already around
1259 # before the provisioned tree exists and we connect
1260 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1262 samdb.transaction_start()
1264 # Set the domain functionality levels onto the database.
1265 # Various module (the password_hash module in particular) need
1266 # to know what level of AD we are emulating.
1268 # These will be fixed into the database via the database
1269 # modifictions below, but we need them set from the start.
1270 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1271 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1272 samdb.set_opaque_integer("domainControllerFunctionality",
1273 domainControllerFunctionality)
1275 samdb.set_domain_sid(str(names.domainsid))
1276 samdb.set_invocation_id(invocationid)
1278 logger.info("Adding DomainDN: %s" % names.domaindn)
1280 # impersonate domain admin
1281 admin_session_info = admin_session(lp, str(names.domainsid))
1282 samdb.set_session_info(admin_session_info)
1283 if names.domainguid is not None:
1284 domainguid_line = "objectGUID: %s\n-" % names.domainguid
1286 domainguid_line = ""
1288 descr = b64encode(get_domain_descriptor(names.domainsid))
1289 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1290 "DOMAINDN": names.domaindn,
1291 "DOMAINSID": str(names.domainsid),
1292 "DESCRIPTOR": descr,
1293 "DOMAINGUID": domainguid_line
1296 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1297 "DOMAINDN": names.domaindn,
1298 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1299 "NEXTRID": str(next_rid),
1300 "DEFAULTSITE": names.sitename,
1301 "CONFIGDN": names.configdn,
1302 "POLICYGUID": policyguid,
1303 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1304 "SAMBA_VERSION_STRING": version
1307 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1308 if fill == FILL_FULL:
1309 logger.info("Adding configuration container")
1310 descr = b64encode(get_config_descriptor(names.domainsid))
1311 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1312 "CONFIGDN": names.configdn,
1313 "DESCRIPTOR": descr,
1316 # The LDIF here was created when the Schema object was constructed
1317 logger.info("Setting up sam.ldb schema")
1318 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1319 samdb.modify_ldif(schema.schema_dn_modify)
1320 samdb.write_prefixes_from_schema()
1321 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1322 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1323 {"SCHEMADN": names.schemadn})
1325 # Now register this container in the root of the forest
1326 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1327 msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1331 samdb.transaction_cancel()
1334 samdb.transaction_commit()
1336 samdb.transaction_start()
1338 samdb.invocation_id = invocationid
1340 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1341 if fill == FILL_FULL:
1342 logger.info("Setting up sam.ldb configuration data")
1344 partitions_descr = b64encode(get_config_partitions_descriptor(names.domainsid))
1345 sites_descr = b64encode(get_config_sites_descriptor(names.domainsid))
1346 ntdsquotas_descr = b64encode(get_config_ntds_quotas_descriptor(names.domainsid))
1347 protected1_descr = b64encode(get_config_delete_protected1_descriptor(names.domainsid))
1348 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid))
1349 protected2_descr = b64encode(get_config_delete_protected2_descriptor(names.domainsid))
1351 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1352 "CONFIGDN": names.configdn,
1353 "NETBIOSNAME": names.netbiosname,
1354 "DEFAULTSITE": names.sitename,
1355 "DNSDOMAIN": names.dnsdomain,
1356 "DOMAIN": names.domain,
1357 "SCHEMADN": names.schemadn,
1358 "DOMAINDN": names.domaindn,
1359 "SERVERDN": names.serverdn,
1360 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1361 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1362 "NTDSQUOTAS_DESCRIPTOR": ntdsquotas_descr,
1363 "LOSTANDFOUND_DESCRIPTOR": protected1wd_descr,
1364 "SERVICES_DESCRIPTOR": protected1_descr,
1365 "PHYSICALLOCATIONS_DESCRIPTOR": protected1wd_descr,
1366 "FORESTUPDATES_DESCRIPTOR": protected1wd_descr,
1367 "EXTENDEDRIGHTS_DESCRIPTOR": protected2_descr,
1368 "PARTITIONS_DESCRIPTOR": partitions_descr,
1369 "SITES_DESCRIPTOR": sites_descr,
1372 logger.info("Setting up display specifiers")
1373 display_specifiers_ldif = read_ms_ldif(
1374 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1375 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1376 {"CONFIGDN": names.configdn})
1377 check_all_substituted(display_specifiers_ldif)
1378 samdb.add_ldif(display_specifiers_ldif)
1380 logger.info("Modifying display specifiers")
1381 setup_modify_ldif(samdb,
1382 setup_path("provision_configuration_modify.ldif"), {
1383 "CONFIGDN": names.configdn,
1384 "DISPLAYSPECIFIERS_DESCRIPTOR": protected2_descr
1387 logger.info("Adding users container")
1388 users_desc = b64encode(get_domain_users_descriptor(names.domainsid))
1389 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1390 "DOMAINDN": names.domaindn,
1391 "USERS_DESCRIPTOR": users_desc
1393 logger.info("Modifying users container")
1394 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1395 "DOMAINDN": names.domaindn})
1396 logger.info("Adding computers container")
1397 computers_desc = b64encode(get_domain_computers_descriptor(names.domainsid))
1398 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1399 "DOMAINDN": names.domaindn,
1400 "COMPUTERS_DESCRIPTOR": computers_desc
1402 logger.info("Modifying computers container")
1403 setup_modify_ldif(samdb,
1404 setup_path("provision_computers_modify.ldif"), {
1405 "DOMAINDN": names.domaindn})
1406 logger.info("Setting up sam.ldb data")
1407 infrastructure_desc = b64encode(get_domain_infrastructure_descriptor(names.domainsid))
1408 lostandfound_desc = b64encode(get_domain_delete_protected2_descriptor(names.domainsid))
1409 system_desc = b64encode(get_domain_delete_protected1_descriptor(names.domainsid))
1410 builtin_desc = b64encode(get_domain_builtin_descriptor(names.domainsid))
1411 controllers_desc = b64encode(get_domain_controllers_descriptor(names.domainsid))
1412 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1413 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1414 "DOMAINDN": names.domaindn,
1415 "NETBIOSNAME": names.netbiosname,
1416 "DEFAULTSITE": names.sitename,
1417 "CONFIGDN": names.configdn,
1418 "SERVERDN": names.serverdn,
1419 "RIDAVAILABLESTART": str(next_rid + 600),
1420 "POLICYGUID_DC": policyguid_dc,
1421 "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc,
1422 "LOSTANDFOUND_DESCRIPTOR": lostandfound_desc,
1423 "SYSTEM_DESCRIPTOR": system_desc,
1424 "BUILTIN_DESCRIPTOR": builtin_desc,
1425 "DOMAIN_CONTROLLERS_DESCRIPTOR": controllers_desc,
1428 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1429 if fill == FILL_FULL:
1430 setup_modify_ldif(samdb,
1431 setup_path("provision_configuration_references.ldif"), {
1432 "CONFIGDN": names.configdn,
1433 "SCHEMADN": names.schemadn})
1435 logger.info("Setting up well known security principals")
1436 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid))
1437 setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1438 "CONFIGDN": names.configdn,
1439 "WELLKNOWNPRINCIPALS_DESCRIPTOR": protected1wd_descr,
1442 if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1443 setup_modify_ldif(samdb,
1444 setup_path("provision_basedn_references.ldif"),
1445 {"DOMAINDN": names.domaindn})
1447 logger.info("Setting up sam.ldb users and groups")
1448 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1449 "DOMAINDN": names.domaindn,
1450 "DOMAINSID": str(names.domainsid),
1451 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1452 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1455 logger.info("Setting up self join")
1456 setup_self_join(samdb, admin_session_info, names=names, fill=fill,
1457 invocationid=invocationid,
1458 dns_backend=dns_backend,
1460 machinepass=machinepass,
1461 domainsid=names.domainsid,
1464 policyguid=policyguid,
1465 policyguid_dc=policyguid_dc,
1466 domainControllerFunctionality=domainControllerFunctionality,
1469 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1470 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1471 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1472 assert isinstance(names.ntdsguid, str)
1474 samdb.transaction_cancel()
1477 samdb.transaction_commit()
1481 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1482 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)"
1483 SYSVOL_SERVICE="sysvol"
1485 def set_dir_acl(path, acl, lp, domsid, use_ntvfs, passdb, service=SYSVOL_SERVICE):
1486 setntacl(lp, path, acl, domsid, use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1487 for root, dirs, files in os.walk(path, topdown=False):
1489 setntacl(lp, os.path.join(root, name), acl, domsid,
1490 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1492 setntacl(lp, os.path.join(root, name), acl, domsid,
1493 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1496 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb):
1497 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1500 :param sysvol: Physical path for the sysvol folder
1501 :param dnsdomain: The DNS name of the domain
1502 :param domainsid: The SID of the domain
1503 :param domaindn: The DN of the domain (ie. DC=...)
1504 :param samdb: An LDB object on the SAM db
1505 :param lp: an LP object
1508 # Set ACL for GPO root folder
1509 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1510 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid),
1511 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=SYSVOL_SERVICE)
1513 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1514 attrs=["cn", "nTSecurityDescriptor"],
1515 expression="", scope=ldb.SCOPE_ONELEVEL)
1518 acl = ndr_unpack(security.descriptor,
1519 str(policy["nTSecurityDescriptor"])).as_sddl()
1520 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1521 set_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1522 str(domainsid), use_ntvfs,
1526 def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain,
1527 domaindn, lp, use_ntvfs):
1528 """Set the ACL for the sysvol share and the subfolders
1530 :param samdb: An LDB object on the SAM db
1531 :param netlogon: Physical path for the netlogon folder
1532 :param sysvol: Physical path for the sysvol folder
1533 :param uid: The UID of the "Administrator" user
1534 :param gid: The GID of the "Domain adminstrators" group
1535 :param domainsid: The SID of the domain
1536 :param dnsdomain: The DNS name of the domain
1537 :param domaindn: The DN of the domain (ie. DC=...)
1542 s3conf = s3param.get_context()
1543 s3conf.load(lp.configfile)
1545 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(sysvol))
1548 smbd.set_simple_acl(file.name, 0755, gid)
1550 if not smbd.have_posix_acls():
1551 # This clue is only strictly correct for RPM and
1552 # Debian-like Linux systems, but hopefully other users
1553 # will get enough clue from it.
1554 raise ProvisioningError("Samba was compiled without the posix ACL support that s3fs requires. "
1555 "Try installing libacl1-dev or libacl-devel, then re-run configure and make.")
1557 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. "
1558 "Try the mounting the filesystem with the 'acl' option.")
1560 smbd.chown(file.name, uid, gid)
1562 raise ProvisioningError("Unable to chown a file on your filesystem. "
1563 "You may not be running provision as root.")
1567 # This will ensure that the smbd code we are running when setting ACLs
1568 # is initialised with the smb.conf
1569 s3conf = s3param.get_context()
1570 s3conf.load(lp.configfile)
1571 # ensure we are using the right samba_dsdb passdb backend, no matter what
1572 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1573 passdb.reload_static_pdb()
1575 # ensure that we init the samba_dsdb backend, so the domain sid is
1576 # marked in secrets.tdb
1577 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1579 # now ensure everything matches correctly, to avoid wierd issues
1580 if passdb.get_global_sam_sid() != domainsid:
1581 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))
1583 domain_info = s4_passdb.domain_info()
1584 if domain_info["dom_sid"] != domainsid:
1585 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))
1587 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1588 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()))
1593 os.chown(sysvol, -1, gid)
1599 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1600 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs,
1601 skip_invalid_chown=True, passdb=s4_passdb,
1602 service=SYSVOL_SERVICE)
1603 for root, dirs, files in os.walk(sysvol, topdown=False):
1605 if use_ntvfs and canchown:
1606 os.chown(os.path.join(root, name), -1, gid)
1607 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid),
1608 use_ntvfs=use_ntvfs, skip_invalid_chown=True,
1609 passdb=s4_passdb, service=SYSVOL_SERVICE)
1611 if use_ntvfs and canchown:
1612 os.chown(os.path.join(root, name), -1, gid)
1613 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid),
1614 use_ntvfs=use_ntvfs, skip_invalid_chown=True,
1615 passdb=s4_passdb, service=SYSVOL_SERVICE)
1617 # Set acls on Policy folder and policies folders
1618 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb=s4_passdb)
1620 def acl_type(direct_db_access):
1621 if direct_db_access:
1626 def check_dir_acl(path, acl, lp, domainsid, direct_db_access):
1627 fsacl = getntacl(lp, path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1628 fsacl_sddl = fsacl.as_sddl(domainsid)
1629 if fsacl_sddl != acl:
1630 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))
1632 for root, dirs, files in os.walk(path, topdown=False):
1634 fsacl = getntacl(lp, os.path.join(root, name),
1635 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1637 raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1638 fsacl_sddl = fsacl.as_sddl(domainsid)
1639 if fsacl_sddl != acl:
1640 raise ProvisioningError('%s ACL on GPO 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))
1643 fsacl = getntacl(lp, os.path.join(root, name),
1644 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1646 raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1647 fsacl_sddl = fsacl.as_sddl(domainsid)
1648 if fsacl_sddl != acl:
1649 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))
1652 def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1654 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1657 :param sysvol: Physical path for the sysvol folder
1658 :param dnsdomain: The DNS name of the domain
1659 :param domainsid: The SID of the domain
1660 :param domaindn: The DN of the domain (ie. DC=...)
1661 :param samdb: An LDB object on the SAM db
1662 :param lp: an LP object
1665 # Set ACL for GPO root folder
1666 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1667 fsacl = getntacl(lp, root_policy_path,
1668 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1670 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access), root_policy_path))
1671 fsacl_sddl = fsacl.as_sddl(domainsid)
1672 if fsacl_sddl != POLICIES_ACL:
1673 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))
1674 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1675 attrs=["cn", "nTSecurityDescriptor"],
1676 expression="", scope=ldb.SCOPE_ONELEVEL)
1679 acl = ndr_unpack(security.descriptor,
1680 str(policy["nTSecurityDescriptor"])).as_sddl()
1681 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1682 check_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1683 domainsid, direct_db_access)
1686 def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn,
1688 """Set the ACL for the sysvol share and the subfolders
1690 :param samdb: An LDB object on the SAM db
1691 :param netlogon: Physical path for the netlogon folder
1692 :param sysvol: Physical path for the sysvol folder
1693 :param uid: The UID of the "Administrator" user
1694 :param gid: The GID of the "Domain adminstrators" group
1695 :param domainsid: The SID of the domain
1696 :param dnsdomain: The DNS name of the domain
1697 :param domaindn: The DN of the domain (ie. DC=...)
1700 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1701 s3conf = s3param.get_context()
1702 s3conf.load(lp.configfile)
1703 # ensure we are using the right samba_dsdb passdb backend, no matter what
1704 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1705 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1706 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1708 # now ensure everything matches correctly, to avoid wierd issues
1709 if passdb.get_global_sam_sid() != domainsid:
1710 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))
1712 domain_info = s4_passdb.domain_info()
1713 if domain_info["dom_sid"] != domainsid:
1714 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))
1716 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1717 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()))
1719 # Ensure we can read this directly, and via the smbd VFS
1720 for direct_db_access in [True, False]:
1721 # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1722 for dir_path in [os.path.join(sysvol, dnsdomain), netlogon]:
1723 fsacl = getntacl(lp, dir_path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1725 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access), dir_path))
1726 fsacl_sddl = fsacl.as_sddl(domainsid)
1727 if fsacl_sddl != SYSVOL_ACL:
1728 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))
1730 # Check acls on Policy folder and policies folders
1731 check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1735 def interface_ips_v4(lp):
1736 """return only IPv4 IPs"""
1737 ips = samba.interface_ips(lp, False)
1740 if i.find(':') == -1:
1745 def interface_ips_v6(lp):
1746 """return only IPv6 IPs"""
1747 ips = samba.interface_ips(lp, False)
1750 if i.find(':') != -1:
1755 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1757 targetdir=None, samdb_fill=FILL_FULL,
1758 hostip=None, hostip6=None,
1759 next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1760 domainguid=None, policyguid=None, policyguid_dc=None,
1761 invocationid=None, machinepass=None, ntdsguid=None,
1762 dns_backend=None, dnspass=None,
1763 serverrole=None, dom_for_fun_level=None,
1764 am_rodc=False, lp=None, use_ntvfs=False, skip_sysvolacl=False):
1765 # create/adapt the group policy GUIDs
1766 # Default GUID for default policy are described at
1767 # "How Core Group Policy Works"
1768 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1769 if policyguid is None:
1770 policyguid = DEFAULT_POLICY_GUID
1771 policyguid = policyguid.upper()
1772 if policyguid_dc is None:
1773 policyguid_dc = DEFAULT_DC_POLICY_GUID
1774 policyguid_dc = policyguid_dc.upper()
1776 if invocationid is None:
1777 invocationid = str(uuid.uuid4())
1779 if krbtgtpass is None:
1780 krbtgtpass = samba.generate_random_password(128, 255)
1782 krbtgtpass = unicode(krbtgtpass, 'utf-8')
1783 if machinepass is None:
1784 machinepass = samba.generate_random_password(128, 255)
1786 machinepass = unicode(machinepass, 'utf-8')
1788 dnspass = samba.generate_random_password(128, 255)
1790 dnspass = unicode(dnspass, 'utf-8')
1792 samdb = fill_samdb(samdb, lp, names, logger=logger,
1794 policyguid=policyguid, policyguid_dc=policyguid_dc,
1795 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1796 invocationid=invocationid, machinepass=machinepass,
1797 dns_backend=dns_backend, dnspass=dnspass,
1798 ntdsguid=ntdsguid, serverrole=serverrole,
1799 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1800 next_rid=next_rid, dc_rid=dc_rid)
1802 if serverrole == "active directory domain controller":
1804 # Set up group policies (domain policy and domain controller
1806 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1808 if not skip_sysvolacl:
1809 setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.root_uid,
1810 paths.root_gid, names.domainsid, names.dnsdomain,
1811 names.domaindn, lp, use_ntvfs)
1813 logger.info("Setting acl on sysvol skipped")
1815 secretsdb_self_join(secrets_ldb, domain=names.domain,
1816 realm=names.realm, dnsdomain=names.dnsdomain,
1817 netbiosname=names.netbiosname, domainsid=names.domainsid,
1818 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1820 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1821 # In future, this might be determined from some configuration
1822 kerberos_enctypes = str(ENC_ALL_TYPES)
1825 msg = ldb.Message(ldb.Dn(samdb,
1826 samdb.searchone("distinguishedName",
1827 expression="samAccountName=%s$" % names.netbiosname,
1828 scope=ldb.SCOPE_SUBTREE)))
1829 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1830 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1831 name="msDS-SupportedEncryptionTypes")
1833 except ldb.LdbError, (enum, estr):
1834 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1835 # It might be that this attribute does not exist in this schema
1838 setup_ad_dns(samdb, secrets_ldb, names, paths, lp, logger,
1839 hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
1840 dnspass=dnspass, os_level=dom_for_fun_level,
1841 targetdir=targetdir, fill_level=samdb_fill)
1843 domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1844 attribute="objectGUID")
1845 assert isinstance(domainguid, str)
1847 lastProvisionUSNs = get_last_provision_usn(samdb)
1848 maxUSN = get_max_usn(samdb, str(names.rootdn))
1849 if lastProvisionUSNs is not None:
1850 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1852 set_provision_usn(samdb, 0, maxUSN, invocationid)
1854 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1855 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1856 { 'NTDSGUID' : names.ntdsguid })
1858 # fix any dangling GUIDs from the provision
1859 logger.info("Fixing provision GUIDs")
1860 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
1862 samdb.transaction_start()
1864 # a small number of GUIDs are missing because of ordering issues in the
1866 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1867 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1868 scope=ldb.SCOPE_BASE,
1869 attrs=['defaultObjectCategory'])
1870 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1871 scope=ldb.SCOPE_ONELEVEL,
1872 attrs=['ipsecOwnersReference',
1873 'ipsecFilterReference',
1874 'ipsecISAKMPReference',
1875 'ipsecNegotiationPolicyReference',
1876 'ipsecNFAReference'])
1878 samdb.transaction_cancel()
1881 samdb.transaction_commit()
1885 "ROLE_STANDALONE": "standalone server",
1886 "ROLE_DOMAIN_MEMBER": "member server",
1887 "ROLE_DOMAIN_BDC": "active directory domain controller",
1888 "ROLE_DOMAIN_PDC": "active directory domain controller",
1889 "dc": "active directory domain controller",
1890 "member": "member server",
1891 "domain controller": "active directory domain controller",
1892 "active directory domain controller": "active directory domain controller",
1893 "member server": "member server",
1894 "standalone": "standalone server",
1895 "standalone server": "standalone server",
1899 def sanitize_server_role(role):
1900 """Sanitize a server role name.
1902 :param role: Server role
1903 :raise ValueError: If the role can not be interpreted
1904 :return: Sanitized server role (one of "member server",
1905 "active directory domain controller", "standalone server")
1908 return _ROLES_MAP[role]
1910 raise ValueError(role)
1913 def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain,
1915 """Create AD entries for the fake ypserver.
1917 This is needed for being able to manipulate posix attrs via ADUC.
1919 samdb.transaction_start()
1921 logger.info("Setting up fake yp server settings")
1922 setup_add_ldif(samdb, setup_path("ypServ30.ldif"), {
1923 "DOMAINDN": domaindn,
1924 "NETBIOSNAME": netbiosname,
1925 "NISDOMAIN": nisdomain,
1928 samdb.transaction_cancel()
1931 samdb.transaction_commit()
1934 def provision(logger, session_info, smbconf=None,
1935 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1936 domaindn=None, schemadn=None, configdn=None, serverdn=None,
1937 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1938 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None,
1939 krbtgtpass=None, domainguid=None, policyguid=None, policyguid_dc=None,
1940 dns_backend=None, dns_forwarder=None, dnspass=None,
1941 invocationid=None, machinepass=None, ntdsguid=None,
1942 root=None, nobody=None, users=None, backup=None, aci=None,
1943 serverrole=None, dom_for_fun_level=None, backend_type=None,
1944 sitename=None, ol_mmr_urls=None, ol_olc=None, slapd_path=None,
1945 useeadb=False, am_rodc=False, lp=None, use_ntvfs=False,
1946 use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True,
1947 ldap_backend_forced_uri=None, nosync=False, ldap_dryrun_mode=False, ldap_backend_extra_port=None):
1950 :note: caution, this wipes all existing data!
1954 serverrole = sanitize_server_role(serverrole)
1956 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole)
1958 if ldapadminpass is None:
1959 # Make a new, random password between Samba and it's LDAP server
1960 ldapadminpass = samba.generate_random_password(128, 255)
1962 if backend_type is None:
1963 backend_type = "ldb"
1965 if domainsid is None:
1966 domainsid = security.random_sid()
1968 root_uid = findnss_uid([root or "root"])
1969 nobody_uid = findnss_uid([nobody or "nobody"])
1970 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1971 root_gid = pwd.getpwuid(root_uid).pw_gid
1974 bind_gid = findnss_gid(["bind", "named"])
1978 if targetdir is not None:
1979 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1980 elif smbconf is None:
1981 smbconf = samba.param.default_path()
1982 if not os.path.exists(os.path.dirname(smbconf)):
1983 os.makedirs(os.path.dirname(smbconf))
1985 server_services = []
1988 global_param["idmap_ldb:use rfc2307"] = ["yes"]
1990 if dns_backend != "SAMBA_INTERNAL":
1991 server_services.append("-dns")
1993 if dns_forwarder is not None:
1994 global_param["dns forwarder"] = [dns_forwarder]
1997 server_services.append("+smb")
1998 server_services.append("-s3fs")
1999 global_param["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
2001 if len(server_services) > 0:
2002 global_param["server services"] = server_services
2004 # only install a new smb.conf if there isn't one there already
2005 if os.path.exists(smbconf):
2006 # if Samba Team members can't figure out the weird errors
2007 # loading an empty smb.conf gives, then we need to be smarter.
2008 # Pretend it just didn't exist --abartlet
2009 f = open(smbconf, 'r')
2011 data = f.read().lstrip()
2014 if data is None or data == "":
2015 make_smbconf(smbconf, hostname, domain, realm,
2016 targetdir, serverrole=serverrole,
2017 eadb=useeadb, use_ntvfs=use_ntvfs,
2018 lp=lp, global_param=global_param)
2020 make_smbconf(smbconf, hostname, domain, realm, targetdir,
2021 serverrole=serverrole,
2022 eadb=useeadb, use_ntvfs=use_ntvfs, lp=lp, global_param=global_param)
2025 lp = samba.param.LoadParm()
2027 names = guess_names(lp=lp, hostname=hostname, domain=domain,
2028 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
2029 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
2030 sitename=sitename, rootdn=rootdn, domain_names_forced=(samdb_fill == FILL_DRS))
2031 paths = provision_paths_from_lp(lp, names.dnsdomain)
2033 paths.bind_gid = bind_gid
2034 paths.root_uid = root_uid;
2035 paths.root_gid = root_gid
2038 logger.info("Looking up IPv4 addresses")
2039 hostips = interface_ips_v4(lp)
2040 if len(hostips) > 0:
2042 if len(hostips) > 1:
2043 logger.warning("More than one IPv4 address found. Using %s",
2045 if hostip == "127.0.0.1":
2048 logger.warning("No IPv4 address will be assigned")
2051 logger.info("Looking up IPv6 addresses")
2052 hostips = interface_ips_v6(lp)
2054 hostip6 = hostips[0]
2055 if len(hostips) > 1:
2056 logger.warning("More than one IPv6 address found. Using %s", hostip6)
2058 logger.warning("No IPv6 address will be assigned")
2060 names.hostip = hostip
2061 names.hostip6 = hostip6
2062 names.domainguid = domainguid
2063 names.domainsid = domainsid
2064 names.forestsid = domainsid
2066 if serverrole is None:
2067 serverrole = lp.get("server role")
2069 if not os.path.exists(paths.private_dir):
2070 os.mkdir(paths.private_dir)
2071 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
2072 os.makedirs(os.path.join(paths.private_dir, "tls"), 0700)
2073 if not os.path.exists(paths.state_dir):
2074 os.mkdir(paths.state_dir)
2076 if paths.sysvol and not os.path.exists(paths.sysvol):
2077 os.makedirs(paths.sysvol, 0775)
2079 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
2081 schema = Schema(domainsid, invocationid=invocationid,
2082 schemadn=names.schemadn)
2084 if backend_type == "ldb":
2085 provision_backend = LDBBackend(backend_type, paths=paths,
2087 names=names, logger=logger)
2088 elif backend_type == "existing":
2089 # If support for this is ever added back, then the URI will need to be
2091 provision_backend = ExistingBackend(backend_type, paths=paths,
2093 names=names, logger=logger,
2094 ldap_backend_forced_uri=ldap_backend_forced_uri)
2095 elif backend_type == "fedora-ds":
2096 provision_backend = FDSBackend(backend_type, paths=paths,
2098 names=names, logger=logger, domainsid=domainsid,
2099 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2100 slapd_path=slapd_path,
2102 elif backend_type == "openldap":
2103 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
2105 names=names, logger=logger, domainsid=domainsid,
2106 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2107 slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls,
2108 ldap_backend_extra_port=ldap_backend_extra_port,
2109 ldap_dryrun_mode=ldap_dryrun_mode, nosync=nosync,
2110 ldap_backend_forced_uri=ldap_backend_forced_uri)
2112 raise ValueError("Unknown LDAP backend type selected")
2114 provision_backend.init()
2115 provision_backend.start()
2117 # only install a new shares config db if there is none
2118 if not os.path.exists(paths.shareconf):
2119 logger.info("Setting up share.ldb")
2120 share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
2121 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
2123 logger.info("Setting up secrets.ldb")
2124 secrets_ldb = setup_secretsdb(paths,
2125 session_info=session_info,
2126 backend_credentials=provision_backend.credentials, lp=lp)
2129 logger.info("Setting up the registry")
2130 setup_registry(paths.hklm, session_info, lp=lp)
2132 logger.info("Setting up the privileges database")
2133 setup_privileges(paths.privilege, session_info, lp=lp)
2135 logger.info("Setting up idmap db")
2136 idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
2138 setup_name_mappings(idmap, sid=str(domainsid),
2139 root_uid=root_uid, nobody_uid=nobody_uid,
2140 users_gid=users_gid, root_gid=root_gid)
2142 logger.info("Setting up SAM db")
2143 samdb = setup_samdb(paths.samdb, session_info,
2144 provision_backend, lp, names, logger=logger,
2145 serverrole=serverrole,
2146 schema=schema, fill=samdb_fill, am_rodc=am_rodc)
2148 if serverrole == "active directory domain controller":
2149 if paths.netlogon is None:
2150 raise MissingShareError("netlogon", paths.smbconf)
2152 if paths.sysvol is None:
2153 raise MissingShareError("sysvol", paths.smbconf)
2155 if not os.path.isdir(paths.netlogon):
2156 os.makedirs(paths.netlogon, 0755)
2158 if adminpass is None:
2159 adminpass = samba.generate_random_password(12, 32)
2160 adminpass_generated = True
2162 adminpass = unicode(adminpass, 'utf-8')
2163 adminpass_generated = False
2165 if samdb_fill == FILL_FULL:
2166 provision_fill(samdb, secrets_ldb, logger, names, paths,
2167 schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
2168 hostip=hostip, hostip6=hostip6,
2169 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
2170 krbtgtpass=krbtgtpass,
2171 policyguid=policyguid, policyguid_dc=policyguid_dc,
2172 invocationid=invocationid, machinepass=machinepass,
2173 ntdsguid=ntdsguid, dns_backend=dns_backend,
2174 dnspass=dnspass, serverrole=serverrole,
2175 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
2176 lp=lp, use_ntvfs=use_ntvfs,
2177 skip_sysvolacl=skip_sysvolacl)
2179 create_krb5_conf(paths.krb5conf,
2180 dnsdomain=names.dnsdomain, hostname=names.hostname,
2182 logger.info("A Kerberos configuration suitable for Samba 4 has been "
2183 "generated at %s", paths.krb5conf)
2185 if serverrole == "active directory domain controller":
2186 create_dns_update_list(lp, logger, paths)
2188 backend_result = provision_backend.post_setup()
2189 provision_backend.shutdown()
2192 secrets_ldb.transaction_cancel()
2195 # Now commit the secrets.ldb to disk
2196 secrets_ldb.transaction_commit()
2198 # the commit creates the dns.keytab, now chown it
2199 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
2200 if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
2202 os.chmod(dns_keytab_path, 0640)
2203 os.chown(dns_keytab_path, -1, paths.bind_gid)
2205 if not os.environ.has_key('SAMBA_SELFTEST'):
2206 logger.info("Failed to chown %s to bind gid %u",
2207 dns_keytab_path, paths.bind_gid)
2209 result = ProvisionResult()
2210 result.server_role = serverrole
2211 result.domaindn = domaindn
2212 result.paths = paths
2213 result.names = names
2215 result.samdb = samdb
2216 result.idmap = idmap
2217 result.domainsid = str(domainsid)
2219 if samdb_fill == FILL_FULL:
2220 result.adminpass_generated = adminpass_generated
2221 result.adminpass = adminpass
2223 result.adminpass_generated = False
2224 result.adminpass = None
2226 result.backend_result = backend_result
2229 provision_fake_ypserver(logger=logger, samdb=samdb,
2230 domaindn=names.domaindn, netbiosname=names.netbiosname,
2231 nisdomain=names.domain.lower(), maxuid=maxuid, maxgid=maxgid)
2236 def provision_become_dc(smbconf=None, targetdir=None,
2237 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
2238 serverdn=None, domain=None, hostname=None, domainsid=None,
2239 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
2240 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
2241 dns_backend=None, root=None, nobody=None, users=None,
2242 backup=None, serverrole=None, ldap_backend=None,
2243 ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False):
2245 logger = logging.getLogger("provision")
2246 samba.set_debug_level(debuglevel)
2248 res = provision(logger, system_session(),
2249 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
2250 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
2251 configdn=configdn, serverdn=serverdn, domain=domain,
2252 hostname=hostname, hostip=None, domainsid=domainsid,
2253 machinepass=machinepass,
2254 serverrole="active directory domain controller",
2255 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass,
2256 use_ntvfs=use_ntvfs)
2257 res.lp.set("debuglevel", str(debuglevel))
2261 def create_krb5_conf(path, dnsdomain, hostname, realm):
2262 """Write out a file containing a valid krb5.conf file
2264 :param path: Path of the new krb5.conf file.
2265 :param dnsdomain: DNS Domain name
2266 :param hostname: Local hostname
2267 :param realm: Realm name
2269 setup_file(setup_path("krb5.conf"), path, {
2270 "DNSDOMAIN": dnsdomain,
2271 "HOSTNAME": hostname,
2276 class ProvisioningError(Exception):
2277 """A generic provision error."""
2279 def __init__(self, value):
2283 return "ProvisioningError: " + self.value
2286 class InvalidNetbiosName(Exception):
2287 """A specified name was not a valid NetBIOS name."""
2289 def __init__(self, name):
2290 super(InvalidNetbiosName, self).__init__(
2291 "The name '%r' is not a valid NetBIOS name" % name)
2294 class MissingShareError(ProvisioningError):
2296 def __init__(self, name, smbconf):
2297 super(MissingShareError, self).__init__(
2298 "Existing smb.conf does not have a [%s] share, but you are "
2299 "configuring a DC. Please remove %s or add the share manually." %