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.provision.descriptor import (
81 get_config_descriptor,
82 get_config_partitions_descriptor,
83 get_config_sites_descriptor,
84 get_domain_descriptor,
85 get_domain_infrastructure_descriptor,
86 get_domain_builtin_descriptor,
88 from samba.provision.common import (
93 from samba.provision.sambadns import (
95 create_dns_update_list
100 from samba.schema import Schema
101 from samba.samdb import SamDB
102 from samba.dbchecker import dbcheck
105 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
106 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04fB984F9"
107 DEFAULTSITE = "Default-First-Site-Name"
108 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
111 class ProvisionPaths(object):
114 self.shareconf = None
125 self.dns_keytab = None
128 self.private_dir = None
129 self.state_dir = None
132 class ProvisionNames(object):
139 self.ldapmanagerdn = None
140 self.dnsdomain = None
142 self.netbiosname = None
149 def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf,
151 """Get key provision parameters (realm, domain, ...) from a given provision
153 :param samdb: An LDB object connected to the sam.ldb file
154 :param secretsdb: An LDB object connected to the secrets.ldb file
155 :param idmapdb: An LDB object connected to the idmap.ldb file
156 :param paths: A list of path to provision object
157 :param smbconf: Path to the smb.conf file
158 :param lp: A LoadParm object
159 :return: A list of key provision parameters
161 names = ProvisionNames()
162 names.adminpass = None
164 # NT domain, kerberos realm, root dn, domain dn, domain dns name
165 names.domain = string.upper(lp.get("workgroup"))
166 names.realm = lp.get("realm")
167 names.dnsdomain = names.realm.lower()
168 basedn = samba.dn_from_dns_name(names.dnsdomain)
169 names.realm = string.upper(names.realm)
171 # Get the netbiosname first (could be obtained from smb.conf in theory)
172 res = secretsdb.search(expression="(flatname=%s)" %
173 names.domain,base="CN=Primary Domains",
174 scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"])
175 names.netbiosname = str(res[0]["sAMAccountName"]).replace("$","")
177 names.smbconf = smbconf
179 # That's a bit simplistic but it's ok as long as we have only 3
181 current = samdb.search(expression="(objectClass=*)",
182 base="", scope=ldb.SCOPE_BASE,
183 attrs=["defaultNamingContext", "schemaNamingContext",
184 "configurationNamingContext","rootDomainNamingContext"])
186 names.configdn = current[0]["configurationNamingContext"]
187 configdn = str(names.configdn)
188 names.schemadn = current[0]["schemaNamingContext"]
189 if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb,
190 current[0]["defaultNamingContext"][0]))):
191 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
192 "is not the same ..." % (paths.samdb,
193 str(current[0]["defaultNamingContext"][0]),
194 paths.smbconf, basedn)))
196 names.domaindn=current[0]["defaultNamingContext"]
197 names.rootdn=current[0]["rootDomainNamingContext"]
199 res3 = samdb.search(expression="(objectClass=site)",
200 base="CN=Sites," + configdn, scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
201 names.sitename = str(res3[0]["cn"])
203 # dns hostname and server dn
204 res4 = samdb.search(expression="(CN=%s)" % names.netbiosname,
205 base="OU=Domain Controllers,%s" % basedn,
206 scope=ldb.SCOPE_ONELEVEL, attrs=["dNSHostName"])
207 names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain, "")
209 server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
210 attrs=[], base=configdn)
211 names.serverdn = server_res[0].dn
213 # invocation id/objectguid
214 res5 = samdb.search(expression="(objectClass=*)",
215 base="CN=NTDS Settings,%s" % str(names.serverdn),
216 scope=ldb.SCOPE_BASE,
217 attrs=["invocationID", "objectGUID"])
218 names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0]))
219 names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0]))
222 res6 = samdb.search(expression="(objectClass=*)", base=basedn,
223 scope=ldb.SCOPE_BASE, attrs=["objectGUID",
224 "objectSid","msDS-Behavior-Version" ])
225 names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
226 names.domainsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
227 if res6[0].get("msDS-Behavior-Version") is None or \
228 int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
229 names.domainlevel = DS_DOMAIN_FUNCTION_2000
231 names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
234 res7 = samdb.search(expression="(displayName=Default Domain Policy)",
235 base="CN=Policies,CN=System," + basedn,
236 scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
237 names.policyid = str(res7[0]["cn"]).replace("{","").replace("}","")
239 res8 = samdb.search(expression="(displayName=Default Domain Controllers"
241 base="CN=Policies,CN=System," + basedn,
242 scope=ldb.SCOPE_ONELEVEL,
243 attrs=["cn","displayName"])
245 names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","")
247 names.policyid_dc = None
249 res9 = idmapdb.search(expression="(cn=%s-%s)" %
250 (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR),
251 attrs=["xidNumber", "type"])
253 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid (%s-%s" % (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR))
254 if res9[0]["type"][0] == "ID_TYPE_BOTH":
255 names.root_gid = res9[0]["xidNumber"][0]
257 names.root_gid = pwd.getpwuid(int(res9[0]["xidNumber"][0])).pw_gid
261 def update_provision_usn(samdb, low, high, id, replace=False):
262 """Update the field provisionUSN in sam.ldb
264 This field is used to track range of USN modified by provision and
266 This value is used afterward by next provision to figure out if
267 the field have been modified since last provision.
269 :param samdb: An LDB object connect to sam.ldb
270 :param low: The lowest USN modified by this upgrade
271 :param high: The highest USN modified by this upgrade
272 :param id: The invocation id of the samba's dc
273 :param replace: A boolean indicating if the range should replace any
274 existing one or appended (default)
279 entry = samdb.search(base="@PROVISION",
280 scope=ldb.SCOPE_BASE,
281 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
282 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
283 if not re.search(';', e):
284 e = "%s;%s" % (e, id)
287 tab.append("%s-%s;%s" % (low, high, id))
288 delta = ldb.Message()
289 delta.dn = ldb.Dn(samdb, "@PROVISION")
290 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
291 ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE)
292 entry = samdb.search(expression='provisionnerID=*',
293 base="@PROVISION", scope=ldb.SCOPE_BASE,
294 attrs=["provisionnerID"])
295 if len(entry) == 0 or len(entry[0]) == 0:
296 delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
300 def set_provision_usn(samdb, low, high, id):
301 """Set the field provisionUSN in sam.ldb
302 This field is used to track range of USN modified by provision and
304 This value is used afterward by next provision to figure out if
305 the field have been modified since last provision.
307 :param samdb: An LDB object connect to sam.ldb
308 :param low: The lowest USN modified by this upgrade
309 :param high: The highest USN modified by this upgrade
310 :param id: The invocationId of the provision"""
313 tab.append("%s-%s;%s" % (low, high, id))
315 delta = ldb.Message()
316 delta.dn = ldb.Dn(samdb, "@PROVISION")
317 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
318 ldb.FLAG_MOD_ADD, LAST_PROVISION_USN_ATTRIBUTE)
322 def get_max_usn(samdb,basedn):
323 """ This function return the biggest USN present in the provision
325 :param samdb: A LDB object pointing to the sam.ldb
326 :param basedn: A string containing the base DN of the provision
328 :return: The biggest USN in the provision"""
330 res = samdb.search(expression="objectClass=*",base=basedn,
331 scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
332 controls=["search_options:1:2",
333 "server_sort:1:1:uSNChanged",
334 "paged_results:1:1"])
335 return res[0]["uSNChanged"]
338 def get_last_provision_usn(sam):
339 """Get USNs ranges modified by a provision or an upgradeprovision
341 :param sam: An LDB object pointing to the sam.ldb
342 :return: a dictionary which keys are invocation id and values are an array
343 of integer representing the different ranges
346 entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE,
347 base="@PROVISION", scope=ldb.SCOPE_BASE,
348 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
349 except ldb.LdbError, (ecode, emsg):
350 if ecode == ldb.ERR_NO_SUCH_OBJECT:
357 if entry[0].get("provisionnerID"):
358 for e in entry[0]["provisionnerID"]:
360 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
361 tab1 = str(r).split(';')
366 if (len(myids) > 0 and id not in myids):
368 tab2 = p.split(tab1[0])
369 if range.get(id) is None:
371 range[id].append(tab2[0])
372 range[id].append(tab2[1])
378 class ProvisionResult(object):
379 """Result of a provision.
381 :ivar server_role: The server role
382 :ivar paths: ProvisionPaths instance
383 :ivar domaindn: The domain dn, as string
387 self.server_role = None
394 self.domainsid = None
395 self.adminpass_generated = None
396 self.adminpass = None
397 self.backend_result = None
399 def report_logger(self, logger):
400 """Report this provision result to a logger."""
402 "Once the above files are installed, your Samba4 server will "
404 if self.adminpass_generated:
405 logger.info("Admin password: %s", self.adminpass)
406 logger.info("Server Role: %s", self.server_role)
407 logger.info("Hostname: %s", self.names.hostname)
408 logger.info("NetBIOS Domain: %s", self.names.domain)
409 logger.info("DNS Domain: %s", self.names.dnsdomain)
410 logger.info("DOMAIN SID: %s", self.domainsid)
412 if self.backend_result:
413 self.backend_result.report_logger(logger)
416 def check_install(lp, session_info, credentials):
417 """Check whether the current install seems ok.
419 :param lp: Loadparm context
420 :param session_info: Session information
421 :param credentials: Credentials
423 if lp.get("realm") == "":
424 raise Exception("Realm empty")
425 samdb = Ldb(lp.samdb_url(), session_info=session_info,
426 credentials=credentials, lp=lp)
427 if len(samdb.search("(cn=Administrator)")) != 1:
428 raise ProvisioningError("No administrator account found")
431 def findnss(nssfn, names):
432 """Find a user or group from a list of possibilities.
434 :param nssfn: NSS Function to try (should raise KeyError if not found)
435 :param names: Names to check.
436 :return: Value return by first names list.
443 raise KeyError("Unable to find user/group in %r" % names)
446 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
447 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
450 def provision_paths_from_lp(lp, dnsdomain):
451 """Set the default paths for provisioning.
453 :param lp: Loadparm context.
454 :param dnsdomain: DNS Domain name
456 paths = ProvisionPaths()
457 paths.private_dir = lp.get("private dir")
458 paths.state_dir = lp.get("state directory")
460 # This is stored without path prefix for the "privateKeytab" attribute in
461 # "secrets_dns.ldif".
462 paths.dns_keytab = "dns.keytab"
463 paths.keytab = "secrets.keytab"
465 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
466 paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
467 paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
468 paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
469 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
470 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
471 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
472 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
473 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
474 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
475 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
476 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
477 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
478 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
479 paths.hklm = "hklm.ldb"
480 paths.hkcr = "hkcr.ldb"
481 paths.hkcu = "hkcu.ldb"
482 paths.hku = "hku.ldb"
483 paths.hkpd = "hkpd.ldb"
484 paths.hkpt = "hkpt.ldb"
485 paths.sysvol = lp.get("path", "sysvol")
486 paths.netlogon = lp.get("path", "netlogon")
487 paths.smbconf = lp.configfile
491 def determine_netbios_name(hostname):
492 """Determine a netbios name from a hostname."""
493 # remove forbidden chars and force the length to be <16
494 netbiosname = "".join([x for x in hostname if is_valid_netbios_char(x)])
495 return netbiosname[:MAX_NETBIOS_NAME_LEN].upper()
498 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
499 serverrole=None, rootdn=None, domaindn=None, configdn=None,
500 schemadn=None, serverdn=None, sitename=None):
501 """Guess configuration settings to use."""
504 hostname = socket.gethostname().split(".")[0]
506 netbiosname = lp.get("netbios name")
507 if netbiosname is None:
508 netbiosname = determine_netbios_name(hostname)
509 netbiosname = netbiosname.upper()
510 if not valid_netbios_name(netbiosname):
511 raise InvalidNetbiosName(netbiosname)
513 if dnsdomain is None:
514 dnsdomain = lp.get("realm")
515 if dnsdomain is None or dnsdomain == "":
516 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
518 dnsdomain = dnsdomain.lower()
520 if serverrole is None:
521 serverrole = lp.get("server role")
522 if serverrole is None:
523 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
525 serverrole = serverrole.lower()
527 realm = dnsdomain.upper()
529 if lp.get("realm") == "":
530 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
532 if lp.get("realm").upper() != realm:
533 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(), realm, lp.configfile))
535 if lp.get("server role").lower() != serverrole:
536 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))
538 if serverrole == "active directory domain controller":
540 # This will, for better or worse, default to 'WORKGROUP'
541 domain = lp.get("workgroup")
542 domain = domain.upper()
544 if lp.get("workgroup").upper() != domain:
545 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))
548 domaindn = samba.dn_from_dns_name(dnsdomain)
550 if domain == netbiosname:
551 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
555 domaindn = "DC=" + netbiosname
557 if not valid_netbios_name(domain):
558 raise InvalidNetbiosName(domain)
560 if hostname.upper() == realm:
561 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
562 if netbiosname.upper() == realm:
563 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
565 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
571 configdn = "CN=Configuration," + rootdn
573 schemadn = "CN=Schema," + configdn
576 sitename = DEFAULTSITE
578 names = ProvisionNames()
579 names.rootdn = rootdn
580 names.domaindn = domaindn
581 names.configdn = configdn
582 names.schemadn = schemadn
583 names.ldapmanagerdn = "CN=Manager," + rootdn
584 names.dnsdomain = dnsdomain
585 names.domain = domain
587 names.netbiosname = netbiosname
588 names.hostname = hostname
589 names.sitename = sitename
590 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
591 netbiosname, sitename, configdn)
596 def make_smbconf(smbconf, hostname, domain, realm, targetdir,
597 serverrole=None, eadb=False, use_ntvfs=False, lp=None,
599 """Create a new smb.conf file based on a couple of basic settings.
601 assert smbconf is not None
604 hostname = socket.gethostname().split(".")[0]
606 netbiosname = determine_netbios_name(hostname)
608 if serverrole is None:
609 serverrole = "standalone server"
611 assert domain is not None
612 domain = domain.upper()
614 assert realm is not None
615 realm = realm.upper()
618 "netbios name": netbiosname,
621 "server role": serverrole,
625 lp = samba.param.LoadParm()
626 #Load non-existent file
627 if os.path.exists(smbconf):
630 if global_param is not None:
631 for ent in global_param:
632 if global_param[ent] is not None:
633 global_settings[ent] = " ".join(global_param[ent])
635 if targetdir is not None:
636 global_settings["private dir"] = os.path.abspath(os.path.join(targetdir, "private"))
637 global_settings["lock dir"] = os.path.abspath(targetdir)
638 global_settings["state directory"] = os.path.abspath(os.path.join(targetdir, "state"))
639 global_settings["cache directory"] = os.path.abspath(os.path.join(targetdir, "cache"))
641 lp.set("lock dir", os.path.abspath(targetdir))
642 lp.set("state directory", global_settings["state directory"])
643 lp.set("cache directory", global_settings["cache directory"])
646 if use_ntvfs and not lp.get("posix:eadb"):
647 if targetdir is not None:
648 privdir = os.path.join(targetdir, "private")
650 privdir = lp.get("private dir")
651 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
652 elif not use_ntvfs and not lp.get("xattr_tdb:file"):
653 if targetdir is not None:
654 statedir = os.path.join(targetdir, "state")
656 statedir = lp.get("state directory")
657 lp.set("xattr_tdb:file", os.path.abspath(os.path.join(statedir, "xattr.tdb")))
660 if serverrole == "active directory domain controller":
661 shares["sysvol"] = os.path.join(lp.get("state directory"), "sysvol")
662 shares["netlogon"] = os.path.join(shares["sysvol"], realm.lower(),
665 global_settings["passdb backend"] = "samba_dsdb"
667 f = open(smbconf, 'w')
669 f.write("[globals]\n")
670 for key, val in global_settings.iteritems():
671 f.write("\t%s = %s\n" % (key, val))
674 for name, path in shares.iteritems():
675 f.write("[%s]\n" % name)
676 f.write("\tpath = %s\n" % path)
677 f.write("\tread only = no\n")
681 # reload the smb.conf
684 # and dump it without any values that are the default
685 # this ensures that any smb.conf parameters that were set
686 # on the provision/join command line are set in the resulting smb.conf
687 f = open(smbconf, mode='w')
694 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
695 users_gid, root_gid):
696 """setup reasonable name mappings for sam names to unix names.
698 :param samdb: SamDB object.
699 :param idmap: IDmap db object.
700 :param sid: The domain sid.
701 :param domaindn: The domain DN.
702 :param root_uid: uid of the UNIX root user.
703 :param nobody_uid: uid of the UNIX nobody user.
704 :param users_gid: gid of the UNIX users group.
705 :param root_gid: gid of the UNIX root group.
707 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
709 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
710 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
713 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
714 provision_backend, names, schema, serverrole,
716 """Setup the partitions for the SAM database.
718 Alternatively, provision() may call this, and then populate the database.
720 :note: This will wipe the Sam Database!
722 :note: This function always removes the local SAM LDB file. The erase
723 parameter controls whether to erase the existing data, which
724 may not be stored locally but in LDAP.
727 assert session_info is not None
729 # We use options=["modules:"] to stop the modules loading - we
730 # just want to wipe and re-initialise the database, not start it up
733 os.unlink(samdb_path)
737 samdb = Ldb(url=samdb_path, session_info=session_info,
738 lp=lp, options=["modules:"])
740 ldap_backend_line = "# No LDAP backend"
741 if provision_backend.type != "ldb":
742 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
744 samdb.transaction_start()
746 logger.info("Setting up sam.ldb partitions and settings")
747 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
748 "LDAP_BACKEND_LINE": ldap_backend_line
752 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
753 "BACKEND_TYPE": provision_backend.type,
754 "SERVER_ROLE": serverrole
757 logger.info("Setting up sam.ldb rootDSE")
758 setup_samdb_rootdse(samdb, names)
760 samdb.transaction_cancel()
763 samdb.transaction_commit()
766 def secretsdb_self_join(secretsdb, domain,
767 netbiosname, machinepass, domainsid=None,
768 realm=None, dnsdomain=None,
770 key_version_number=1,
771 secure_channel_type=SEC_CHAN_WKSTA):
772 """Add domain join-specific bits to a secrets database.
774 :param secretsdb: Ldb Handle to the secrets database
775 :param machinepass: Machine password
777 attrs = ["whenChanged",
784 if realm is not None:
785 if dnsdomain is None:
786 dnsdomain = realm.lower()
787 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
790 shortname = netbiosname.lower()
792 # We don't need to set msg["flatname"] here, because rdn_name will handle
793 # it, and it causes problems for modifies anyway
794 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
795 msg["secureChannelType"] = [str(secure_channel_type)]
796 msg["objectClass"] = ["top", "primaryDomain"]
797 if dnsname is not None:
798 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
799 msg["realm"] = [realm]
800 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
801 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
802 msg["privateKeytab"] = ["secrets.keytab"]
804 msg["secret"] = [machinepass]
805 msg["samAccountName"] = ["%s$" % netbiosname]
806 msg["secureChannelType"] = [str(secure_channel_type)]
807 if domainsid is not None:
808 msg["objectSid"] = [ndr_pack(domainsid)]
810 # This complex expression tries to ensure that we don't have more
811 # than one record for this SID, realm or netbios domain at a time,
812 # but we don't delete the old record that we are about to modify,
813 # because that would delete the keytab and previous password.
814 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
815 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
816 scope=ldb.SCOPE_ONELEVEL)
819 secretsdb.delete(del_msg.dn)
821 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
824 msg["priorSecret"] = [res[0]["secret"][0]]
825 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
828 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
833 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
839 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
840 secretsdb.modify(msg)
841 secretsdb.rename(res[0].dn, msg.dn)
843 spn = [ 'HOST/%s' % shortname ]
844 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
845 # we are a domain controller then we add servicePrincipalName
846 # entries for the keytab code to update.
847 spn.extend([ 'HOST/%s' % dnsname ])
848 msg["servicePrincipalName"] = spn
853 def setup_secretsdb(paths, session_info, backend_credentials, lp):
854 """Setup the secrets database.
856 :note: This function does not handle exceptions and transaction on purpose,
857 it's up to the caller to do this job.
859 :param path: Path to the secrets database.
860 :param session_info: Session info.
861 :param credentials: Credentials
862 :param lp: Loadparm context
863 :return: LDB handle for the created secrets database
865 if os.path.exists(paths.secrets):
866 os.unlink(paths.secrets)
868 keytab_path = os.path.join(paths.private_dir, paths.keytab)
869 if os.path.exists(keytab_path):
870 os.unlink(keytab_path)
872 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
873 if os.path.exists(dns_keytab_path):
874 os.unlink(dns_keytab_path)
878 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
880 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
881 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
882 secrets_ldb.transaction_start()
884 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
886 if (backend_credentials is not None and
887 backend_credentials.authentication_requested()):
888 if backend_credentials.get_bind_dn() is not None:
889 setup_add_ldif(secrets_ldb,
890 setup_path("secrets_simple_ldap.ldif"), {
891 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
892 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
895 setup_add_ldif(secrets_ldb,
896 setup_path("secrets_sasl_ldap.ldif"), {
897 "LDAPADMINUSER": backend_credentials.get_username(),
898 "LDAPADMINREALM": backend_credentials.get_realm(),
899 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
902 secrets_ldb.transaction_cancel()
907 def setup_privileges(path, session_info, lp):
908 """Setup the privileges database.
910 :param path: Path to the privileges database.
911 :param session_info: Session info.
912 :param credentials: Credentials
913 :param lp: Loadparm context
914 :return: LDB handle for the created secrets database
916 if os.path.exists(path):
918 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
919 privilege_ldb.erase()
920 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
923 def setup_registry(path, session_info, lp):
924 """Setup the registry.
926 :param path: Path to the registry database
927 :param session_info: Session information
928 :param credentials: Credentials
929 :param lp: Loadparm context
931 reg = samba.registry.Registry()
932 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
933 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
934 provision_reg = setup_path("provision.reg")
935 assert os.path.exists(provision_reg)
936 reg.diff_apply(provision_reg)
939 def setup_idmapdb(path, session_info, lp):
940 """Setup the idmap database.
942 :param path: path to the idmap database
943 :param session_info: Session information
944 :param credentials: Credentials
945 :param lp: Loadparm context
947 if os.path.exists(path):
950 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
952 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
956 def setup_samdb_rootdse(samdb, names):
957 """Setup the SamDB rootdse.
959 :param samdb: Sam Database handle
961 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
962 "SCHEMADN": names.schemadn,
963 "DOMAINDN": names.domaindn,
964 "ROOTDN" : names.rootdn,
965 "CONFIGDN": names.configdn,
966 "SERVERDN": names.serverdn,
970 def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
971 dns_backend, dnspass, domainsid, next_rid, invocationid,
972 policyguid, policyguid_dc,
973 domainControllerFunctionality, ntdsguid=None, dc_rid=None):
974 """Join a host to its own domain."""
975 assert isinstance(invocationid, str)
976 if ntdsguid is not None:
977 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
984 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
985 "CONFIGDN": names.configdn,
986 "SCHEMADN": names.schemadn,
987 "DOMAINDN": names.domaindn,
988 "SERVERDN": names.serverdn,
989 "INVOCATIONID": invocationid,
990 "NETBIOSNAME": names.netbiosname,
991 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
992 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
993 "DOMAINSID": str(domainsid),
994 "DCRID": str(dc_rid),
995 "SAMBA_VERSION_STRING": version,
996 "NTDSGUID": ntdsguid_line,
997 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
998 domainControllerFunctionality),
999 "RIDALLOCATIONSTART": str(next_rid + 100),
1000 "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
1002 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1003 "POLICYGUID": policyguid,
1004 "POLICYGUID_DC": policyguid_dc,
1005 "DNSDOMAIN": names.dnsdomain,
1006 "DOMAINDN": names.domaindn})
1008 # If we are setting up a subdomain, then this has been replicated in, so we
1009 # don't need to add it
1010 if fill == FILL_FULL:
1011 setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
1012 "CONFIGDN": names.configdn,
1013 "SCHEMADN": names.schemadn,
1014 "DOMAINDN": names.domaindn,
1015 "SERVERDN": names.serverdn,
1016 "INVOCATIONID": invocationid,
1017 "NETBIOSNAME": names.netbiosname,
1018 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1019 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1020 "DOMAINSID": str(domainsid),
1021 "DCRID": str(dc_rid),
1022 "SAMBA_VERSION_STRING": version,
1023 "NTDSGUID": ntdsguid_line,
1024 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1025 domainControllerFunctionality)})
1027 # Setup fSMORoleOwner entries to point at the newly created DC entry
1028 setup_modify_ldif(samdb,
1029 setup_path("provision_self_join_modify_config.ldif"), {
1030 "CONFIGDN": names.configdn,
1031 "SCHEMADN": names.schemadn,
1032 "DEFAULTSITE": names.sitename,
1033 "NETBIOSNAME": names.netbiosname,
1034 "SERVERDN": names.serverdn,
1037 system_session_info = system_session()
1038 samdb.set_session_info(system_session_info)
1039 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1040 # modify a serverReference under cn=config when we are a subdomain, we must
1041 # be system due to ACLs
1042 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1043 "DOMAINDN": names.domaindn,
1044 "SERVERDN": names.serverdn,
1045 "NETBIOSNAME": names.netbiosname,
1048 samdb.set_session_info(admin_session_info)
1050 if dns_backend != "SAMBA_INTERNAL":
1051 # This is Samba4 specific and should be replaced by the correct
1052 # DNS AD-style setup
1053 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1054 "DNSDOMAIN": names.dnsdomain,
1055 "DOMAINDN": names.domaindn,
1056 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1057 "HOSTNAME" : names.hostname,
1058 "DNSNAME" : '%s.%s' % (
1059 names.netbiosname.lower(), names.dnsdomain.lower())
1063 def getpolicypath(sysvolpath, dnsdomain, guid):
1064 """Return the physical path of policy given its guid.
1066 :param sysvolpath: Path to the sysvol folder
1067 :param dnsdomain: DNS name of the AD domain
1068 :param guid: The GUID of the policy
1069 :return: A string with the complete path to the policy folder
1072 guid = "{%s}" % guid
1073 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1077 def create_gpo_struct(policy_path):
1078 if not os.path.exists(policy_path):
1079 os.makedirs(policy_path, 0775)
1080 f = open(os.path.join(policy_path, "GPT.INI"), 'w')
1082 f.write("[General]\r\nVersion=0")
1085 p = os.path.join(policy_path, "MACHINE")
1086 if not os.path.exists(p):
1087 os.makedirs(p, 0775)
1088 p = os.path.join(policy_path, "USER")
1089 if not os.path.exists(p):
1090 os.makedirs(p, 0775)
1093 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1094 """Create the default GPO for a domain
1096 :param sysvolpath: Physical path for the sysvol folder
1097 :param dnsdomain: DNS domain name of the AD domain
1098 :param policyguid: GUID of the default domain policy
1099 :param policyguid_dc: GUID of the default domain controler policy
1101 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1102 create_gpo_struct(policy_path)
1104 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1105 create_gpo_struct(policy_path)
1108 def setup_samdb(path, session_info, provision_backend, lp, names,
1109 logger, fill, serverrole, schema, am_rodc=False):
1110 """Setup a complete SAM Database.
1112 :note: This will wipe the main SAM database file!
1115 # Also wipes the database
1116 setup_samdb_partitions(path, logger=logger, lp=lp,
1117 provision_backend=provision_backend, session_info=session_info,
1118 names=names, serverrole=serverrole, schema=schema)
1120 # Load the database, but don's load the global schema and don't connect
1122 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1123 credentials=provision_backend.credentials, lp=lp,
1124 global_schema=False, am_rodc=am_rodc)
1126 logger.info("Pre-loading the Samba 4 and AD schema")
1128 # Load the schema from the one we computed earlier
1129 samdb.set_schema(schema, write_indices_and_attributes=False)
1131 # Set the NTDS settings DN manually - in order to have it already around
1132 # before the provisioned tree exists and we connect
1133 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1135 # And now we can connect to the DB - the schema won't be loaded from the
1139 # But we have to give it one more kick to have it use the schema
1140 # during provision - it needs, now that it is connected, to write
1141 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1142 samdb.set_schema(schema, write_indices_and_attributes=True)
1147 def fill_samdb(samdb, lp, names, logger, domainsid, domainguid, policyguid,
1148 policyguid_dc, fill, adminpass, krbtgtpass, machinepass, dns_backend,
1149 dnspass, invocationid, ntdsguid, serverrole, am_rodc=False,
1150 dom_for_fun_level=None, schema=None, next_rid=None, dc_rid=None):
1152 if next_rid is None:
1155 # Provision does not make much sense values larger than 1000000000
1156 # as the upper range of the rIDAvailablePool is 1073741823 and
1157 # we don't want to create a domain that cannot allocate rids.
1158 if next_rid < 1000 or next_rid > 1000000000:
1159 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1160 error += "the valid range is %u-%u. The default is %u." % (
1161 1000, 1000000000, 1000)
1162 raise ProvisioningError(error)
1164 # ATTENTION: Do NOT change these default values without discussion with the
1165 # team and/or release manager. They have a big impact on the whole program!
1166 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1168 if dom_for_fun_level is None:
1169 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1171 if dom_for_fun_level > domainControllerFunctionality:
1172 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!")
1174 domainFunctionality = dom_for_fun_level
1175 forestFunctionality = dom_for_fun_level
1177 # Set the NTDS settings DN manually - in order to have it already around
1178 # before the provisioned tree exists and we connect
1179 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1181 samdb.transaction_start()
1183 # Set the domain functionality levels onto the database.
1184 # Various module (the password_hash module in particular) need
1185 # to know what level of AD we are emulating.
1187 # These will be fixed into the database via the database
1188 # modifictions below, but we need them set from the start.
1189 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1190 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1191 samdb.set_opaque_integer("domainControllerFunctionality",
1192 domainControllerFunctionality)
1194 samdb.set_domain_sid(str(domainsid))
1195 samdb.set_invocation_id(invocationid)
1197 logger.info("Adding DomainDN: %s" % names.domaindn)
1199 # impersonate domain admin
1200 admin_session_info = admin_session(lp, str(domainsid))
1201 samdb.set_session_info(admin_session_info)
1202 if domainguid is not None:
1203 domainguid_line = "objectGUID: %s\n-" % domainguid
1205 domainguid_line = ""
1207 descr = b64encode(get_domain_descriptor(domainsid))
1208 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1209 "DOMAINDN": names.domaindn,
1210 "DOMAINSID": str(domainsid),
1211 "DESCRIPTOR": descr,
1212 "DOMAINGUID": domainguid_line
1215 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1216 "DOMAINDN": names.domaindn,
1217 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1218 "NEXTRID": str(next_rid),
1219 "DEFAULTSITE": names.sitename,
1220 "CONFIGDN": names.configdn,
1221 "POLICYGUID": policyguid,
1222 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1223 "SAMBA_VERSION_STRING": version
1226 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1227 if fill == FILL_FULL:
1228 logger.info("Adding configuration container")
1229 descr = b64encode(get_config_descriptor(domainsid))
1230 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1231 "CONFIGDN": names.configdn,
1232 "DESCRIPTOR": descr,
1235 # The LDIF here was created when the Schema object was constructed
1236 logger.info("Setting up sam.ldb schema")
1237 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1238 samdb.modify_ldif(schema.schema_dn_modify)
1239 samdb.write_prefixes_from_schema()
1240 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1241 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1242 {"SCHEMADN": names.schemadn})
1244 # Now register this container in the root of the forest
1245 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1246 msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1250 samdb.transaction_cancel()
1253 samdb.transaction_commit()
1255 samdb.transaction_start()
1257 samdb.invocation_id = invocationid
1259 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1260 if fill == FILL_FULL:
1261 logger.info("Setting up sam.ldb configuration data")
1262 partitions_descr = b64encode(get_config_partitions_descriptor(domainsid))
1263 sites_descr = b64encode(get_config_sites_descriptor(domainsid))
1264 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1265 "CONFIGDN": names.configdn,
1266 "NETBIOSNAME": names.netbiosname,
1267 "DEFAULTSITE": names.sitename,
1268 "DNSDOMAIN": names.dnsdomain,
1269 "DOMAIN": names.domain,
1270 "SCHEMADN": names.schemadn,
1271 "DOMAINDN": names.domaindn,
1272 "SERVERDN": names.serverdn,
1273 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1274 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1275 "PARTITIONS_DESCRIPTOR": partitions_descr,
1276 "SITES_DESCRIPTOR": sites_descr,
1279 logger.info("Setting up display specifiers")
1280 display_specifiers_ldif = read_ms_ldif(
1281 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1282 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1283 {"CONFIGDN": names.configdn})
1284 check_all_substituted(display_specifiers_ldif)
1285 samdb.add_ldif(display_specifiers_ldif)
1287 logger.info("Adding users container")
1288 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1289 "DOMAINDN": names.domaindn})
1290 logger.info("Modifying users container")
1291 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1292 "DOMAINDN": names.domaindn})
1293 logger.info("Adding computers container")
1294 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1295 "DOMAINDN": names.domaindn})
1296 logger.info("Modifying computers container")
1297 setup_modify_ldif(samdb,
1298 setup_path("provision_computers_modify.ldif"), {
1299 "DOMAINDN": names.domaindn})
1300 logger.info("Setting up sam.ldb data")
1301 infrastructure_desc = b64encode(get_domain_infrastructure_descriptor(domainsid))
1302 builtin_desc = b64encode(get_domain_builtin_descriptor(domainsid))
1303 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1304 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1305 "DOMAINDN": names.domaindn,
1306 "NETBIOSNAME": names.netbiosname,
1307 "DEFAULTSITE": names.sitename,
1308 "CONFIGDN": names.configdn,
1309 "SERVERDN": names.serverdn,
1310 "RIDAVAILABLESTART": str(next_rid + 600),
1311 "POLICYGUID_DC": policyguid_dc,
1312 "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc,
1313 "BUILTIN_DESCRIPTOR": builtin_desc,
1316 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1317 if fill == FILL_FULL:
1318 setup_modify_ldif(samdb,
1319 setup_path("provision_configuration_references.ldif"), {
1320 "CONFIGDN": names.configdn,
1321 "SCHEMADN": names.schemadn})
1323 logger.info("Setting up well known security principals")
1324 setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1325 "CONFIGDN": names.configdn,
1328 if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1329 setup_modify_ldif(samdb,
1330 setup_path("provision_basedn_references.ldif"),
1331 {"DOMAINDN": names.domaindn})
1333 logger.info("Setting up sam.ldb users and groups")
1334 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1335 "DOMAINDN": names.domaindn,
1336 "DOMAINSID": str(domainsid),
1337 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1338 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1341 logger.info("Setting up self join")
1342 setup_self_join(samdb, admin_session_info, names=names, fill=fill,
1343 invocationid=invocationid,
1344 dns_backend=dns_backend,
1346 machinepass=machinepass,
1347 domainsid=domainsid,
1350 policyguid=policyguid,
1351 policyguid_dc=policyguid_dc,
1352 domainControllerFunctionality=domainControllerFunctionality,
1355 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1356 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1357 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1358 assert isinstance(names.ntdsguid, str)
1360 samdb.transaction_cancel()
1363 samdb.transaction_commit()
1368 FILL_SUBDOMAIN = "SUBDOMAIN"
1369 FILL_NT4SYNC = "NT4SYNC"
1371 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1372 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)"
1375 def set_dir_acl(path, acl, lp, domsid, use_ntvfs, passdb):
1376 setntacl(lp, path, acl, domsid, use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb)
1377 for root, dirs, files in os.walk(path, topdown=False):
1379 setntacl(lp, os.path.join(root, name), acl, domsid,
1380 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb)
1382 setntacl(lp, os.path.join(root, name), acl, domsid,
1383 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb)
1386 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb):
1387 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1390 :param sysvol: Physical path for the sysvol folder
1391 :param dnsdomain: The DNS name of the domain
1392 :param domainsid: The SID of the domain
1393 :param domaindn: The DN of the domain (ie. DC=...)
1394 :param samdb: An LDB object on the SAM db
1395 :param lp: an LP object
1398 # Set ACL for GPO root folder
1399 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1400 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid),
1401 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb)
1403 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1404 attrs=["cn", "nTSecurityDescriptor"],
1405 expression="", scope=ldb.SCOPE_ONELEVEL)
1408 acl = ndr_unpack(security.descriptor,
1409 str(policy["nTSecurityDescriptor"])).as_sddl()
1410 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1411 set_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1412 str(domainsid), use_ntvfs,
1416 def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain,
1417 domaindn, lp, use_ntvfs):
1418 """Set the ACL for the sysvol share and the subfolders
1420 :param samdb: An LDB object on the SAM db
1421 :param netlogon: Physical path for the netlogon folder
1422 :param sysvol: Physical path for the sysvol folder
1423 :param uid: The UID of the "Administrator" user
1424 :param gid: The GID of the "Domain adminstrators" group
1425 :param domainsid: The SID of the domain
1426 :param dnsdomain: The DNS name of the domain
1427 :param domaindn: The DN of the domain (ie. DC=...)
1432 # This will ensure that the smbd code we are running when setting ACLs
1433 # is initialised with the smb.conf
1434 s3conf = s3param.get_context()
1435 s3conf.load(lp.configfile)
1436 # ensure we are using the right samba_dsdb passdb backend, no matter what
1437 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1438 passdb.reload_static_pdb()
1440 # ensure that we init the samba_dsdb backend, so the domain sid is
1441 # marked in secrets.tdb
1442 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1444 # now ensure everything matches correctly, to avoid wierd issues
1445 if passdb.get_global_sam_sid() != domainsid:
1446 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))
1448 domain_info = s4_passdb.domain_info()
1449 if domain_info["dom_sid"] != domainsid:
1450 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))
1452 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1453 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()))
1458 os.chown(sysvol, -1, gid)
1464 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1465 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=s4_passdb)
1466 for root, dirs, files in os.walk(sysvol, topdown=False):
1468 if use_ntvfs and canchown:
1469 os.chown(os.path.join(root, name), -1, gid)
1470 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=s4_passdb)
1472 if use_ntvfs and canchown:
1473 os.chown(os.path.join(root, name), -1, gid)
1474 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=s4_passdb)
1476 # Set acls on Policy folder and policies folders
1477 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb=s4_passdb)
1479 def acl_type(direct_db_access):
1480 if direct_db_access:
1485 def check_dir_acl(path, acl, lp, domainsid, direct_db_access):
1486 fsacl = getntacl(lp, path, direct_db_access=direct_db_access)
1487 fsacl_sddl = fsacl.as_sddl(domainsid)
1488 if fsacl_sddl != acl:
1489 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))
1491 for root, dirs, files in os.walk(path, topdown=False):
1493 fsacl = getntacl(lp, os.path.join(root, name), direct_db_access=direct_db_access)
1495 raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1496 fsacl_sddl = fsacl.as_sddl(domainsid)
1497 if fsacl_sddl != acl:
1498 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))
1501 fsacl = getntacl(lp, os.path.join(root, name), direct_db_access=direct_db_access)
1503 raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1504 fsacl_sddl = fsacl.as_sddl(domainsid)
1505 if fsacl_sddl != acl:
1506 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))
1509 def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1511 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1514 :param sysvol: Physical path for the sysvol folder
1515 :param dnsdomain: The DNS name of the domain
1516 :param domainsid: The SID of the domain
1517 :param domaindn: The DN of the domain (ie. DC=...)
1518 :param samdb: An LDB object on the SAM db
1519 :param lp: an LP object
1522 # Set ACL for GPO root folder
1523 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1524 fsacl = getntacl(lp, root_policy_path, direct_db_access=direct_db_access)
1526 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access), root_policy_path))
1527 fsacl_sddl = fsacl.as_sddl(domainsid)
1528 if fsacl_sddl != POLICIES_ACL:
1529 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))
1530 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1531 attrs=["cn", "nTSecurityDescriptor"],
1532 expression="", scope=ldb.SCOPE_ONELEVEL)
1535 acl = ndr_unpack(security.descriptor,
1536 str(policy["nTSecurityDescriptor"])).as_sddl()
1537 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1538 check_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1539 domainsid, direct_db_access)
1542 def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn,
1544 """Set the ACL for the sysvol share and the subfolders
1546 :param samdb: An LDB object on the SAM db
1547 :param netlogon: Physical path for the netlogon folder
1548 :param sysvol: Physical path for the sysvol folder
1549 :param uid: The UID of the "Administrator" user
1550 :param gid: The GID of the "Domain adminstrators" group
1551 :param domainsid: The SID of the domain
1552 :param dnsdomain: The DNS name of the domain
1553 :param domaindn: The DN of the domain (ie. DC=...)
1556 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1557 s3conf = s3param.get_context()
1558 s3conf.load(lp.configfile)
1559 # ensure we are using the right samba_dsdb passdb backend, no matter what
1560 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1561 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1562 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1564 # now ensure everything matches correctly, to avoid wierd issues
1565 if passdb.get_global_sam_sid() != domainsid:
1566 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))
1568 domain_info = s4_passdb.domain_info()
1569 if domain_info["dom_sid"] != domainsid:
1570 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))
1572 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1573 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()))
1575 # Ensure we can read this directly, and via the smbd VFS
1576 for direct_db_access in [True, False]:
1577 # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1578 for dir_path in [os.path.join(sysvol, dnsdomain), netlogon]:
1579 fsacl = getntacl(lp, dir_path, direct_db_access=direct_db_access)
1581 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access), dir_path))
1582 fsacl_sddl = fsacl.as_sddl(domainsid)
1583 if fsacl_sddl != SYSVOL_ACL:
1584 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))
1586 # Check acls on Policy folder and policies folders
1587 check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1591 def interface_ips_v4(lp):
1592 """return only IPv4 IPs"""
1593 ips = samba.interface_ips(lp, False)
1596 if i.find(':') == -1:
1601 def interface_ips_v6(lp, linklocal=False):
1602 """return only IPv6 IPs"""
1603 ips = samba.interface_ips(lp, False)
1606 if i.find(':') != -1 and (linklocal or i.find('%') == -1):
1611 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1612 domainsid, schema=None,
1613 targetdir=None, samdb_fill=FILL_FULL,
1614 hostip=None, hostip6=None,
1615 next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1616 domainguid=None, policyguid=None, policyguid_dc=None,
1617 invocationid=None, machinepass=None, ntdsguid=None,
1618 dns_backend=None, dnspass=None,
1619 serverrole=None, dom_for_fun_level=None,
1620 am_rodc=False, lp=None, use_ntvfs=False, skip_sysvolacl=False):
1621 # create/adapt the group policy GUIDs
1622 # Default GUID for default policy are described at
1623 # "How Core Group Policy Works"
1624 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1625 if policyguid is None:
1626 policyguid = DEFAULT_POLICY_GUID
1627 policyguid = policyguid.upper()
1628 if policyguid_dc is None:
1629 policyguid_dc = DEFAULT_DC_POLICY_GUID
1630 policyguid_dc = policyguid_dc.upper()
1632 if invocationid is None:
1633 invocationid = str(uuid.uuid4())
1635 if krbtgtpass is None:
1636 krbtgtpass = samba.generate_random_password(128, 255)
1637 if machinepass is None:
1638 machinepass = samba.generate_random_password(128, 255)
1640 dnspass = samba.generate_random_password(128, 255)
1642 samdb = fill_samdb(samdb, lp, names, logger=logger,
1643 domainsid=domainsid, schema=schema, domainguid=domainguid,
1644 policyguid=policyguid, policyguid_dc=policyguid_dc,
1645 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1646 invocationid=invocationid, machinepass=machinepass,
1647 dns_backend=dns_backend, dnspass=dnspass,
1648 ntdsguid=ntdsguid, serverrole=serverrole,
1649 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1650 next_rid=next_rid, dc_rid=dc_rid)
1652 if serverrole == "active directory domain controller":
1654 # Set up group policies (domain policy and domain controller
1656 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1658 if not skip_sysvolacl:
1659 setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.root_uid,
1660 paths.root_gid, domainsid, names.dnsdomain,
1661 names.domaindn, lp, use_ntvfs)
1663 logger.info("Setting acl on sysvol skipped")
1665 secretsdb_self_join(secrets_ldb, domain=names.domain,
1666 realm=names.realm, dnsdomain=names.dnsdomain,
1667 netbiosname=names.netbiosname, domainsid=domainsid,
1668 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1670 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1671 # In future, this might be determined from some configuration
1672 kerberos_enctypes = str(ENC_ALL_TYPES)
1675 msg = ldb.Message(ldb.Dn(samdb,
1676 samdb.searchone("distinguishedName",
1677 expression="samAccountName=%s$" % names.netbiosname,
1678 scope=ldb.SCOPE_SUBTREE)))
1679 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1680 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1681 name="msDS-SupportedEncryptionTypes")
1683 except ldb.LdbError, (enum, estr):
1684 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1685 # It might be that this attribute does not exist in this schema
1688 setup_ad_dns(samdb, secrets_ldb, domainsid, names, paths, lp, logger,
1689 hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
1690 dnspass=dnspass, os_level=dom_for_fun_level,
1691 targetdir=targetdir, site=DEFAULTSITE)
1693 domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1694 attribute="objectGUID")
1695 assert isinstance(domainguid, str)
1697 lastProvisionUSNs = get_last_provision_usn(samdb)
1698 maxUSN = get_max_usn(samdb, str(names.rootdn))
1699 if lastProvisionUSNs is not None:
1700 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1702 set_provision_usn(samdb, 0, maxUSN, invocationid)
1704 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1705 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1706 { 'NTDSGUID' : names.ntdsguid })
1708 # fix any dangling GUIDs from the provision
1709 logger.info("Fixing provision GUIDs")
1710 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
1712 samdb.transaction_start()
1714 # a small number of GUIDs are missing because of ordering issues in the
1716 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1717 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1718 scope=ldb.SCOPE_BASE,
1719 attrs=['defaultObjectCategory'])
1720 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1721 scope=ldb.SCOPE_ONELEVEL,
1722 attrs=['ipsecOwnersReference',
1723 'ipsecFilterReference',
1724 'ipsecISAKMPReference',
1725 'ipsecNegotiationPolicyReference',
1726 'ipsecNFAReference'])
1728 samdb.transaction_cancel()
1731 samdb.transaction_commit()
1735 "ROLE_STANDALONE": "standalone server",
1736 "ROLE_DOMAIN_MEMBER": "member server",
1737 "ROLE_DOMAIN_BDC": "active directory domain controller",
1738 "ROLE_DOMAIN_PDC": "active directory domain controller",
1739 "dc": "active directory domain controller",
1740 "member": "member server",
1741 "domain controller": "active directory domain controller",
1742 "active directory domain controller": "active directory domain controller",
1743 "member server": "member server",
1744 "standalone": "standalone server",
1745 "standalone server": "standalone server",
1749 def sanitize_server_role(role):
1750 """Sanitize a server role name.
1752 :param role: Server role
1753 :raise ValueError: If the role can not be interpreted
1754 :return: Sanitized server role (one of "member server",
1755 "active directory domain controller", "standalone server")
1758 return _ROLES_MAP[role]
1760 raise ValueError(role)
1763 def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain,
1765 """Create AD entries for the fake ypserver.
1767 This is needed for being able to manipulate posix attrs via ADUC.
1769 samdb.transaction_start()
1771 logger.info("Setting up fake yp server settings")
1772 setup_add_ldif(samdb, setup_path("ypServ30.ldif"), {
1773 "DOMAINDN": domaindn,
1774 "NETBIOSNAME": netbiosname,
1775 "NISDOMAIN": nisdomain,
1778 samdb.transaction_cancel()
1781 samdb.transaction_commit()
1784 def provision(logger, session_info, credentials, smbconf=None,
1785 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1786 domaindn=None, schemadn=None, configdn=None, serverdn=None,
1787 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1788 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None,
1789 krbtgtpass=None, domainguid=None, policyguid=None, policyguid_dc=None,
1790 dns_backend=None, dns_forwarder=None, dnspass=None,
1791 invocationid=None, machinepass=None, ntdsguid=None,
1792 root=None, nobody=None, users=None, backup=None, aci=None,
1793 serverrole=None, dom_for_fun_level=None, backend_type=None,
1794 sitename=None, ol_mmr_urls=None, ol_olc=None, slapd_path="/bin/false",
1795 useeadb=False, am_rodc=False, lp=None, use_ntvfs=False,
1796 use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True):
1799 :note: caution, this wipes all existing data!
1803 serverrole = sanitize_server_role(serverrole)
1805 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole)
1807 if ldapadminpass is None:
1808 # Make a new, random password between Samba and it's LDAP server
1809 ldapadminpass = samba.generate_random_password(128, 255)
1811 if backend_type is None:
1812 backend_type = "ldb"
1814 if domainsid is None:
1815 domainsid = security.random_sid()
1817 domainsid = security.dom_sid(domainsid)
1819 root_uid = findnss_uid([root or "root"])
1820 nobody_uid = findnss_uid([nobody or "nobody"])
1821 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1822 root_gid = pwd.getpwuid(root_uid).pw_gid
1825 bind_gid = findnss_gid(["bind", "named"])
1829 if targetdir is not None:
1830 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1831 elif smbconf is None:
1832 smbconf = samba.param.default_path()
1833 if not os.path.exists(os.path.dirname(smbconf)):
1834 os.makedirs(os.path.dirname(smbconf))
1836 server_services = []
1839 global_param["idmap_ldb:use rfc2307"] = ["yes"]
1841 if dns_backend != "SAMBA_INTERNAL":
1842 server_services.append("-dns")
1844 if dns_forwarder is not None:
1845 global_param["dns forwarder"] = [dns_forwarder]
1848 server_services.append("+smb")
1849 server_services.append("-s3fs")
1850 global_param["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
1852 if len(server_services) > 0:
1853 global_param["server services"] = server_services
1855 # only install a new smb.conf if there isn't one there already
1856 if os.path.exists(smbconf):
1857 # if Samba Team members can't figure out the weird errors
1858 # loading an empty smb.conf gives, then we need to be smarter.
1859 # Pretend it just didn't exist --abartlet
1860 f = open(smbconf, 'r')
1862 data = f.read().lstrip()
1865 if data is None or data == "":
1866 make_smbconf(smbconf, hostname, domain, realm,
1867 targetdir, serverrole=serverrole,
1868 eadb=useeadb, use_ntvfs=use_ntvfs,
1869 lp=lp, global_param=global_param)
1871 make_smbconf(smbconf, hostname, domain, realm, targetdir,
1872 serverrole=serverrole,
1873 eadb=useeadb, use_ntvfs=use_ntvfs, lp=lp, global_param=global_param)
1876 lp = samba.param.LoadParm()
1878 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1879 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
1880 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
1881 sitename=sitename, rootdn=rootdn)
1882 paths = provision_paths_from_lp(lp, names.dnsdomain)
1884 paths.bind_gid = bind_gid
1885 paths.root_uid = root_uid;
1886 paths.root_gid = root_gid
1889 logger.info("Looking up IPv4 addresses")
1890 hostips = interface_ips_v4(lp)
1891 if len(hostips) > 0:
1893 if len(hostips) > 1:
1894 logger.warning("More than one IPv4 address found. Using %s",
1896 if hostip == "127.0.0.1":
1899 logger.warning("No IPv4 address will be assigned")
1902 logger.info("Looking up IPv6 addresses")
1903 hostips = interface_ips_v6(lp, linklocal=False)
1905 hostip6 = hostips[0]
1906 if len(hostips) > 1:
1907 logger.warning("More than one IPv6 address found. Using %s", hostip6)
1909 logger.warning("No IPv6 address will be assigned")
1911 names.hostip = hostip
1912 names.hostip6 = hostip6
1914 if serverrole is None:
1915 serverrole = lp.get("server role")
1917 if not os.path.exists(paths.private_dir):
1918 os.mkdir(paths.private_dir)
1919 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1920 os.mkdir(os.path.join(paths.private_dir, "tls"))
1921 if not os.path.exists(paths.state_dir):
1922 os.mkdir(paths.state_dir)
1924 if paths.sysvol and not os.path.exists(paths.sysvol):
1925 os.makedirs(paths.sysvol, 0775)
1927 if not use_ntvfs and serverrole == "active directory domain controller":
1928 s3conf = s3param.get_context()
1929 s3conf.load(lp.configfile)
1931 if paths.sysvol is None:
1932 raise MissingShareError("sysvol", paths.smbconf)
1934 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(paths.sysvol))
1937 smbd.set_simple_acl(file.name, 0755, root_gid)
1939 if not smbd.have_posix_acls():
1940 # This clue is only strictly correct for RPM and
1941 # Debian-like Linux systems, but hopefully other users
1942 # will get enough clue from it.
1943 raise ProvisioningError("Samba was compiled without the posix ACL support that s3fs requires. Try installing libacl1-dev or libacl-devel, then re-run configure and make.")
1945 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. Try the mounting the filesystem with the 'acl' option.")
1947 smbd.chown(file.name, root_uid, root_gid)
1949 raise ProvisioningError("Unable to chown a file on your filesystem. You may not be running provision as root.")
1953 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1955 schema = Schema(domainsid, invocationid=invocationid,
1956 schemadn=names.schemadn)
1958 if backend_type == "ldb":
1959 provision_backend = LDBBackend(backend_type, paths=paths,
1960 lp=lp, credentials=credentials,
1961 names=names, logger=logger)
1962 elif backend_type == "existing":
1963 # If support for this is ever added back, then the URI will need to be
1965 provision_backend = ExistingBackend(backend_type, paths=paths,
1966 lp=lp, credentials=credentials,
1967 names=names, logger=logger,
1968 ldap_backend_forced_uri=None)
1969 elif backend_type == "fedora-ds":
1970 provision_backend = FDSBackend(backend_type, paths=paths,
1971 lp=lp, credentials=credentials,
1972 names=names, logger=logger, domainsid=domainsid,
1973 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1974 slapd_path=slapd_path,
1976 elif backend_type == "openldap":
1977 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
1978 lp=lp, credentials=credentials,
1979 names=names, logger=logger, domainsid=domainsid,
1980 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1981 slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls)
1983 raise ValueError("Unknown LDAP backend type selected")
1985 provision_backend.init()
1986 provision_backend.start()
1988 # only install a new shares config db if there is none
1989 if not os.path.exists(paths.shareconf):
1990 logger.info("Setting up share.ldb")
1991 share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
1992 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1994 logger.info("Setting up secrets.ldb")
1995 secrets_ldb = setup_secretsdb(paths,
1996 session_info=session_info,
1997 backend_credentials=provision_backend.secrets_credentials, lp=lp)
2000 logger.info("Setting up the registry")
2001 setup_registry(paths.hklm, session_info, lp=lp)
2003 logger.info("Setting up the privileges database")
2004 setup_privileges(paths.privilege, session_info, lp=lp)
2006 logger.info("Setting up idmap db")
2007 idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
2009 setup_name_mappings(idmap, sid=str(domainsid),
2010 root_uid=root_uid, nobody_uid=nobody_uid,
2011 users_gid=users_gid, root_gid=root_gid)
2013 logger.info("Setting up SAM db")
2014 samdb = setup_samdb(paths.samdb, session_info,
2015 provision_backend, lp, names, logger=logger,
2016 serverrole=serverrole,
2017 schema=schema, fill=samdb_fill, am_rodc=am_rodc)
2019 if serverrole == "active directory domain controller":
2020 if paths.netlogon is None:
2021 raise MissingShareError("netlogon", paths.smbconf)
2023 if paths.sysvol is None:
2024 raise MissingShareError("sysvol", paths.smbconf)
2026 if not os.path.isdir(paths.netlogon):
2027 os.makedirs(paths.netlogon, 0755)
2029 if adminpass is None:
2030 adminpass = samba.generate_random_password(12, 32)
2031 adminpass_generated = True
2033 adminpass_generated = False
2035 if samdb_fill == FILL_FULL:
2036 provision_fill(samdb, secrets_ldb, logger, names, paths,
2037 schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
2038 hostip=hostip, hostip6=hostip6, domainsid=domainsid,
2039 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
2040 krbtgtpass=krbtgtpass, domainguid=domainguid,
2041 policyguid=policyguid, policyguid_dc=policyguid_dc,
2042 invocationid=invocationid, machinepass=machinepass,
2043 ntdsguid=ntdsguid, dns_backend=dns_backend,
2044 dnspass=dnspass, serverrole=serverrole,
2045 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
2046 lp=lp, use_ntvfs=use_ntvfs,
2047 skip_sysvolacl=skip_sysvolacl)
2049 create_krb5_conf(paths.krb5conf,
2050 dnsdomain=names.dnsdomain, hostname=names.hostname,
2052 logger.info("A Kerberos configuration suitable for Samba 4 has been "
2053 "generated at %s", paths.krb5conf)
2055 if serverrole == "active directory domain controller":
2056 create_dns_update_list(lp, logger, paths)
2058 backend_result = provision_backend.post_setup()
2059 provision_backend.shutdown()
2062 secrets_ldb.transaction_cancel()
2065 # Now commit the secrets.ldb to disk
2066 secrets_ldb.transaction_commit()
2068 # the commit creates the dns.keytab, now chown it
2069 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
2070 if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
2072 os.chmod(dns_keytab_path, 0640)
2073 os.chown(dns_keytab_path, -1, paths.bind_gid)
2075 if not os.environ.has_key('SAMBA_SELFTEST'):
2076 logger.info("Failed to chown %s to bind gid %u",
2077 dns_keytab_path, paths.bind_gid)
2079 result = ProvisionResult()
2080 result.server_role = serverrole
2081 result.domaindn = domaindn
2082 result.paths = paths
2083 result.names = names
2085 result.samdb = samdb
2086 result.idmap = idmap
2087 result.domainsid = str(domainsid)
2089 if samdb_fill == FILL_FULL:
2090 result.adminpass_generated = adminpass_generated
2091 result.adminpass = adminpass
2093 result.adminpass_generated = False
2094 result.adminpass = None
2096 result.backend_result = backend_result
2099 provision_fake_ypserver(logger=logger, samdb=samdb,
2100 domaindn=names.domaindn, netbiosname=names.netbiosname,
2101 nisdomain=names.domain.lower(), maxuid=maxuid, maxgid=maxgid)
2106 def provision_become_dc(smbconf=None, targetdir=None,
2107 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
2108 serverdn=None, domain=None, hostname=None, domainsid=None,
2109 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
2110 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
2111 dns_backend=None, root=None, nobody=None, users=None,
2112 backup=None, serverrole=None, ldap_backend=None,
2113 ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False):
2115 logger = logging.getLogger("provision")
2116 samba.set_debug_level(debuglevel)
2118 res = provision(logger, system_session(), None,
2119 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
2120 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
2121 configdn=configdn, serverdn=serverdn, domain=domain,
2122 hostname=hostname, hostip=None, domainsid=domainsid,
2123 machinepass=machinepass,
2124 serverrole="active directory domain controller",
2125 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass,
2126 use_ntvfs=use_ntvfs)
2127 res.lp.set("debuglevel", str(debuglevel))
2131 def create_krb5_conf(path, dnsdomain, hostname, realm):
2132 """Write out a file containing zone statements suitable for inclusion in a
2133 named.conf file (including GSS-TSIG configuration).
2135 :param path: Path of the new named.conf file.
2136 :param dnsdomain: DNS Domain name
2137 :param hostname: Local hostname
2138 :param realm: Realm name
2140 setup_file(setup_path("krb5.conf"), path, {
2141 "DNSDOMAIN": dnsdomain,
2142 "HOSTNAME": hostname,
2147 class ProvisioningError(Exception):
2148 """A generic provision error."""
2150 def __init__(self, value):
2154 return "ProvisioningError: " + self.value
2157 class InvalidNetbiosName(Exception):
2158 """A specified name was not a valid NetBIOS name."""
2160 def __init__(self, name):
2161 super(InvalidNetbiosName, self).__init__(
2162 "The name '%r' is not a valid NetBIOS name" % name)
2165 class MissingShareError(ProvisioningError):
2167 def __init__(self, name, smbconf):
2168 super(MissingShareError, self).__init__(
2169 "Existing smb.conf does not have a [%s] share, but you are "
2170 "configuring a DC. Please remove %s or add the share manually." %