2 # Unix SMB/CIFS implementation.
3 # backend code for provisioning a Samba4 server
5 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2012
6 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
7 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
9 # Based on the original in EJS:
10 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
12 # This program is free software; you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
14 # the Free Software Foundation; either version 3 of the License, or
15 # (at your option) any later version.
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
22 # You should have received a copy of the GNU General Public License
23 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 """Functions for setting up a Samba configuration."""
28 __docformat__ = "restructuredText"
30 from base64 import b64encode
44 from samba.auth import system_session, admin_session
46 from samba.dsdb import DS_DOMAIN_FUNCTION_2000
49 check_all_substituted,
55 from samba.dcerpc import security, misc
56 from samba.dcerpc.misc import (
60 from samba.dsdb import (
61 DS_DOMAIN_FUNCTION_2003,
62 DS_DOMAIN_FUNCTION_2008_R2,
65 from samba.idmap import IDmapDB
66 from samba.ms_display_specifiers import read_ms_ldif
67 from samba.ntacls import setntacl, dsacl2fsacl
68 from samba.ndr import ndr_pack, ndr_unpack
69 from samba.provision.backend import (
75 from samba.provision.descriptor import (
76 get_config_descriptor,
79 from samba.provision.common import (
84 from samba.provision.sambadns import (
86 create_dns_update_list
91 from samba.schema import Schema
92 from samba.samdb import SamDB
93 from samba.dbchecker import dbcheck
96 VALID_NETBIOS_CHARS = " !#$%&'()-.@^_{}~"
97 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
98 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04fB984F9"
99 DEFAULTSITE = "Default-First-Site-Name"
100 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
103 class ProvisionPaths(object):
106 self.shareconf = None
117 self.dns_keytab = None
120 self.private_dir = None
121 self.phpldapadminconfig = None
124 class ProvisionNames(object):
131 self.ldapmanagerdn = None
132 self.dnsdomain = None
134 self.netbiosname = None
140 def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf, lp):
141 """Get key provision parameters (realm, domain, ...) from a given provision
143 :param samdb: An LDB object connected to the sam.ldb file
144 :param secretsdb: An LDB object connected to the secrets.ldb file
145 :param idmapdb: An LDB object connected to the idmap.ldb file
146 :param paths: A list of path to provision object
147 :param smbconf: Path to the smb.conf file
148 :param lp: A LoadParm object
149 :return: A list of key provision parameters
151 names = ProvisionNames()
152 names.adminpass = None
154 # NT domain, kerberos realm, root dn, domain dn, domain dns name
155 names.domain = string.upper(lp.get("workgroup"))
156 names.realm = lp.get("realm")
157 names.dnsdomain = names.realm.lower()
158 basedn = samba.dn_from_dns_name(names.dnsdomain)
159 names.realm = string.upper(names.realm)
161 # Get the netbiosname first (could be obtained from smb.conf in theory)
162 res = secretsdb.search(expression="(flatname=%s)" %
163 names.domain,base="CN=Primary Domains",
164 scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"])
165 names.netbiosname = str(res[0]["sAMAccountName"]).replace("$","")
167 names.smbconf = smbconf
169 # That's a bit simplistic but it's ok as long as we have only 3
171 current = samdb.search(expression="(objectClass=*)",
172 base="", scope=ldb.SCOPE_BASE,
173 attrs=["defaultNamingContext", "schemaNamingContext",
174 "configurationNamingContext","rootDomainNamingContext"])
176 names.configdn = current[0]["configurationNamingContext"]
177 configdn = str(names.configdn)
178 names.schemadn = current[0]["schemaNamingContext"]
179 if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb,
180 current[0]["defaultNamingContext"][0]))):
181 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
182 "is not the same ..." % (paths.samdb,
183 str(current[0]["defaultNamingContext"][0]),
184 paths.smbconf, basedn)))
186 names.domaindn=current[0]["defaultNamingContext"]
187 names.rootdn=current[0]["rootDomainNamingContext"]
189 res3 = samdb.search(expression="(objectClass=site)",
190 base="CN=Sites," + configdn, scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
191 names.sitename = str(res3[0]["cn"])
193 # dns hostname and server dn
194 res4 = samdb.search(expression="(CN=%s)" % names.netbiosname,
195 base="OU=Domain Controllers,%s" % basedn,
196 scope=ldb.SCOPE_ONELEVEL, attrs=["dNSHostName"])
197 names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain,"")
199 server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
200 attrs=[], base=configdn)
201 names.serverdn = server_res[0].dn
203 # invocation id/objectguid
204 res5 = samdb.search(expression="(objectClass=*)",
205 base="CN=NTDS Settings,%s" % str(names.serverdn), scope=ldb.SCOPE_BASE,
206 attrs=["invocationID", "objectGUID"])
207 names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0]))
208 names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0]))
211 res6 = samdb.search(expression="(objectClass=*)", base=basedn,
212 scope=ldb.SCOPE_BASE, attrs=["objectGUID",
213 "objectSid","msDS-Behavior-Version" ])
214 names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
215 names.domainsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
216 if res6[0].get("msDS-Behavior-Version") is None or \
217 int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
218 names.domainlevel = DS_DOMAIN_FUNCTION_2000
220 names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
223 res7 = samdb.search(expression="(displayName=Default Domain Policy)",
224 base="CN=Policies,CN=System," + basedn,
225 scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
226 names.policyid = str(res7[0]["cn"]).replace("{","").replace("}","")
228 res8 = samdb.search(expression="(displayName=Default Domain Controllers"
230 base="CN=Policies,CN=System," + basedn,
231 scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
233 names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","")
235 names.policyid_dc = None
236 res9 = idmapdb.search(expression="(cn=%s)" %
237 (security.SID_BUILTIN_ADMINISTRATORS),
240 names.wheel_gid = res9[0]["xidNumber"]
242 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid")
246 def update_provision_usn(samdb, low, high, id, replace=False):
247 """Update the field provisionUSN in sam.ldb
249 This field is used to track range of USN modified by provision and
251 This value is used afterward by next provision to figure out if
252 the field have been modified since last provision.
254 :param samdb: An LDB object connect to sam.ldb
255 :param low: The lowest USN modified by this upgrade
256 :param high: The highest USN modified by this upgrade
257 :param id: The invocation id of the samba's dc
258 :param replace: A boolean indicating if the range should replace any
259 existing one or appended (default)
264 entry = samdb.search(base="@PROVISION",
265 scope=ldb.SCOPE_BASE,
266 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
267 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
268 if not re.search(';', e):
269 e = "%s;%s" % (e, id)
272 tab.append("%s-%s;%s" % (low, high, id))
273 delta = ldb.Message()
274 delta.dn = ldb.Dn(samdb, "@PROVISION")
275 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
276 ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE)
277 entry = samdb.search(expression='provisionnerID=*',
278 base="@PROVISION", scope=ldb.SCOPE_BASE,
279 attrs=["provisionnerID"])
280 if len(entry) == 0 or len(entry[0]) == 0:
281 delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
285 def set_provision_usn(samdb, low, high, id):
286 """Set the field provisionUSN in sam.ldb
287 This field is used to track range of USN modified by provision and
289 This value is used afterward by next provision to figure out if
290 the field have been modified since last provision.
292 :param samdb: An LDB object connect to sam.ldb
293 :param low: The lowest USN modified by this upgrade
294 :param high: The highest USN modified by this upgrade
295 :param id: The invocationId of the provision"""
298 tab.append("%s-%s;%s" % (low, high, id))
300 delta = ldb.Message()
301 delta.dn = ldb.Dn(samdb, "@PROVISION")
302 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
303 ldb.FLAG_MOD_ADD, LAST_PROVISION_USN_ATTRIBUTE)
307 def get_max_usn(samdb,basedn):
308 """ This function return the biggest USN present in the provision
310 :param samdb: A LDB object pointing to the sam.ldb
311 :param basedn: A string containing the base DN of the provision
313 :return: The biggest USN in the provision"""
315 res = samdb.search(expression="objectClass=*",base=basedn,
316 scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
317 controls=["search_options:1:2",
318 "server_sort:1:1:uSNChanged",
319 "paged_results:1:1"])
320 return res[0]["uSNChanged"]
323 def get_last_provision_usn(sam):
324 """Get USNs ranges modified by a provision or an upgradeprovision
326 :param sam: An LDB object pointing to the sam.ldb
327 :return: a dictionnary which keys are invocation id and values are an array
328 of integer representing the different ranges
331 entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE,
332 base="@PROVISION", scope=ldb.SCOPE_BASE,
333 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
334 except ldb.LdbError, (ecode, emsg):
335 if ecode == ldb.ERR_NO_SUCH_OBJECT:
342 if entry[0].get("provisionnerID"):
343 for e in entry[0]["provisionnerID"]:
345 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
346 tab1 = str(r).split(';')
351 if (len(myids) > 0 and id not in myids):
353 tab2 = p.split(tab1[0])
354 if range.get(id) == None:
356 range[id].append(tab2[0])
357 range[id].append(tab2[1])
363 class ProvisionResult(object):
364 """Result of a provision.
366 :ivar server_role: The server role
367 :ivar paths: ProvisionPaths instance
368 :ivar domaindn: The domain dn, as string
372 self.server_role = None
379 self.domainsid = None
380 self.adminpass_generated = None
381 self.adminpass = None
382 self.backend_result = None
384 def report_logger(self, logger):
385 """Report this provision result to a logger."""
387 "Once the above files are installed, your Samba4 server will "
389 if self.adminpass_generated:
390 logger.info("Admin password: %s", self.adminpass)
391 logger.info("Server Role: %s", self.server_role)
392 logger.info("Hostname: %s", self.names.hostname)
393 logger.info("NetBIOS Domain: %s", self.names.domain)
394 logger.info("DNS Domain: %s", self.names.dnsdomain)
395 logger.info("DOMAIN SID: %s", self.domainsid)
397 if self.paths.phpldapadminconfig is not None:
399 "A phpLDAPadmin configuration file suitable for administering "
400 "the Samba 4 LDAP server has been created in %s.",
401 self.paths.phpldapadminconfig)
403 if self.backend_result:
404 self.backend_result.report_logger(logger)
407 def check_install(lp, session_info, credentials):
408 """Check whether the current install seems ok.
410 :param lp: Loadparm context
411 :param session_info: Session information
412 :param credentials: Credentials
414 if lp.get("realm") == "":
415 raise Exception("Realm empty")
416 samdb = Ldb(lp.samdb_url(), session_info=session_info,
417 credentials=credentials, lp=lp)
418 if len(samdb.search("(cn=Administrator)")) != 1:
419 raise ProvisioningError("No administrator account found")
422 def findnss(nssfn, names):
423 """Find a user or group from a list of possibilities.
425 :param nssfn: NSS Function to try (should raise KeyError if not found)
426 :param names: Names to check.
427 :return: Value return by first names list.
434 raise KeyError("Unable to find user/group in %r" % names)
437 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
438 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
441 def provision_paths_from_lp(lp, dnsdomain):
442 """Set the default paths for provisioning.
444 :param lp: Loadparm context.
445 :param dnsdomain: DNS Domain name
447 paths = ProvisionPaths()
448 paths.private_dir = lp.get("private dir")
450 # This is stored without path prefix for the "privateKeytab" attribute in
451 # "secrets_dns.ldif".
452 paths.dns_keytab = "dns.keytab"
453 paths.keytab = "secrets.keytab"
455 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
456 paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
457 paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
458 paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
459 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
460 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
461 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
462 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
463 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
464 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
465 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
466 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
467 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
468 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
469 paths.phpldapadminconfig = os.path.join(paths.private_dir,
470 "phpldapadmin-config.php")
471 paths.hklm = "hklm.ldb"
472 paths.hkcr = "hkcr.ldb"
473 paths.hkcu = "hkcu.ldb"
474 paths.hku = "hku.ldb"
475 paths.hkpd = "hkpd.ldb"
476 paths.hkpt = "hkpt.ldb"
477 paths.sysvol = lp.get("path", "sysvol")
478 paths.netlogon = lp.get("path", "netlogon")
479 paths.smbconf = lp.configfile
483 def determine_netbios_name(hostname):
484 """Determine a netbios name from a hostname."""
485 netbiosname = hostname
486 # remove forbidden chars
488 for x in netbiosname:
489 if x.isalnum() or x in VALID_NETBIOS_CHARS:
490 newnbname = "%s%c" % (newnbname, x)
491 # force the length to be <16
492 return newnbname[0:15].upper()
495 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
496 serverrole=None, rootdn=None, domaindn=None, configdn=None,
497 schemadn=None, serverdn=None, sitename=None):
498 """Guess configuration settings to use."""
501 hostname = socket.gethostname().split(".")[0]
503 netbiosname = lp.get("netbios name")
504 if netbiosname is None:
505 netbiosname = determine_netbios_name(hostname)
506 assert netbiosname is not None
507 netbiosname = netbiosname.upper()
508 if not valid_netbios_name(netbiosname):
509 raise InvalidNetbiosName(netbiosname)
511 if dnsdomain is None:
512 dnsdomain = lp.get("realm")
513 if dnsdomain is None or dnsdomain == "":
514 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
516 dnsdomain = dnsdomain.lower()
518 if serverrole is None:
519 serverrole = lp.get("server role")
520 if serverrole is None:
521 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
523 serverrole = serverrole.lower()
525 realm = dnsdomain.upper()
527 if lp.get("realm") == "":
528 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
530 if lp.get("realm").upper() != realm:
531 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))
533 if lp.get("server role").lower() != serverrole:
534 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"), serverrole, lp.configfile))
536 if serverrole == "domain controller":
538 # This will, for better or worse, default to 'WORKGROUP'
539 domain = lp.get("workgroup")
540 domain = domain.upper()
542 if lp.get("workgroup").upper() != domain:
543 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))
546 domaindn = samba.dn_from_dns_name(dnsdomain)
548 if domain == netbiosname:
549 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
553 domaindn = "DC=" + netbiosname
555 if not valid_netbios_name(domain):
556 raise InvalidNetbiosName(domain)
558 if hostname.upper() == realm:
559 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
560 if netbiosname.upper() == realm:
561 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
563 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
569 configdn = "CN=Configuration," + rootdn
571 schemadn = "CN=Schema," + configdn
576 names = ProvisionNames()
577 names.rootdn = rootdn
578 names.domaindn = domaindn
579 names.configdn = configdn
580 names.schemadn = schemadn
581 names.ldapmanagerdn = "CN=Manager," + rootdn
582 names.dnsdomain = dnsdomain
583 names.domain = domain
585 names.netbiosname = netbiosname
586 names.hostname = hostname
587 names.sitename = sitename
588 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
589 netbiosname, sitename, configdn)
594 def make_smbconf(smbconf, hostname, domain, realm, serverrole,
595 targetdir, sid_generator="internal", eadb=False, lp=None,
596 server_services=None):
597 """Create a new smb.conf file based on a couple of basic settings.
599 assert smbconf is not None
601 hostname = socket.gethostname().split(".")[0]
602 netbiosname = hostname.upper()
603 # remove forbidden chars
605 for x in netbiosname:
606 if x.isalnum() or x in VALID_NETBIOS_CHARS:
607 newnbname = "%s%c" % (newnbname, x)
608 #force the length to be <16
609 netbiosname = newnbname[0:15]
611 netbiosname = hostname.upper()
613 if serverrole is None:
614 serverrole = "standalone"
616 assert serverrole in ("domain controller", "member server", "standalone")
617 if serverrole == "domain controller":
619 elif serverrole == "member server":
620 smbconfsuffix = "member"
621 elif serverrole == "standalone":
622 smbconfsuffix = "standalone"
624 if sid_generator is None:
625 sid_generator = "internal"
627 assert domain is not None
628 domain = domain.upper()
630 assert realm is not None
631 realm = realm.upper()
634 lp = samba.param.LoadParm()
635 #Load non-existant file
636 if os.path.exists(smbconf):
638 if eadb and not lp.get("posix:eadb"):
639 if targetdir is not None:
640 privdir = os.path.join(targetdir, "private")
642 privdir = lp.get("private dir")
643 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
645 if server_services is not None:
646 server_services_line = "server services = " + " ".join(server_services)
648 server_services_line = ""
650 if targetdir is not None:
651 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
652 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
653 statedir_line = "state directory = " + os.path.abspath(targetdir)
654 cachedir_line = "cache directory = " + os.path.abspath(targetdir)
656 lp.set("lock dir", os.path.abspath(targetdir))
657 lp.set("state directory", os.path.abspath(targetdir))
658 lp.set("cache directory", os.path.abspath(targetdir))
665 sysvol = os.path.join(lp.get("state directory"), "sysvol")
666 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
668 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
670 "NETBIOS_NAME": netbiosname,
673 "SERVERROLE": serverrole,
674 "NETLOGONPATH": netlogon,
675 "SYSVOLPATH": sysvol,
676 "PRIVATEDIR_LINE": privatedir_line,
677 "LOCKDIR_LINE": lockdir_line,
678 "STATEDIR_LINE": statedir_line,
679 "CACHEDIR_LINE": cachedir_line,
680 "SERVER_SERVICES_LINE": server_services_line
683 # reload the smb.conf
686 # and dump it without any values that are the default
687 # this ensures that any smb.conf parameters that were set
688 # on the provision/join command line are set in the resulting smb.conf
689 f = open(smbconf, mode='w')
694 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
695 users_gid, wheel_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 wheel_gid: gid of the UNIX wheel group.
707 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
708 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
710 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
711 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
714 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
715 provision_backend, names, schema, serverrole,
717 """Setup the partitions for the SAM database.
719 Alternatively, provision() may call this, and then populate the database.
721 :note: This will wipe the Sam Database!
723 :note: This function always removes the local SAM LDB file. The erase
724 parameter controls whether to erase the existing data, which
725 may not be stored locally but in LDAP.
728 assert session_info is not None
730 # We use options=["modules:"] to stop the modules loading - we
731 # just want to wipe and re-initialise the database, not start it up
734 os.unlink(samdb_path)
738 samdb = Ldb(url=samdb_path, session_info=session_info,
739 lp=lp, options=["modules:"])
741 ldap_backend_line = "# No LDAP backend"
742 if provision_backend.type != "ldb":
743 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
745 samdb.transaction_start()
747 logger.info("Setting up sam.ldb partitions and settings")
748 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
749 "LDAP_BACKEND_LINE": ldap_backend_line
753 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
754 "BACKEND_TYPE": provision_backend.type,
755 "SERVER_ROLE": serverrole
758 logger.info("Setting up sam.ldb rootDSE")
759 setup_samdb_rootdse(samdb, names)
761 samdb.transaction_cancel()
764 samdb.transaction_commit()
767 def secretsdb_self_join(secretsdb, domain,
768 netbiosname, machinepass, domainsid=None,
769 realm=None, dnsdomain=None,
771 key_version_number=1,
772 secure_channel_type=SEC_CHAN_WKSTA):
773 """Add domain join-specific bits to a secrets database.
775 :param secretsdb: Ldb Handle to the secrets database
776 :param machinepass: Machine password
778 attrs = ["whenChanged",
785 if realm is not None:
786 if dnsdomain is None:
787 dnsdomain = realm.lower()
788 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
791 shortname = netbiosname.lower()
793 # We don't need to set msg["flatname"] here, because rdn_name will handle
794 # it, and it causes problems for modifies anyway
795 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
796 msg["secureChannelType"] = [str(secure_channel_type)]
797 msg["objectClass"] = ["top", "primaryDomain"]
798 if dnsname is not None:
799 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
800 msg["realm"] = [realm]
801 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
802 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
803 msg["privateKeytab"] = ["secrets.keytab"]
805 msg["secret"] = [machinepass]
806 msg["samAccountName"] = ["%s$" % netbiosname]
807 msg["secureChannelType"] = [str(secure_channel_type)]
808 if domainsid is not None:
809 msg["objectSid"] = [ndr_pack(domainsid)]
811 # This complex expression tries to ensure that we don't have more
812 # than one record for this SID, realm or netbios domain at a time,
813 # but we don't delete the old record that we are about to modify,
814 # because that would delete the keytab and previous password.
815 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
816 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(dn=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
817 scope=ldb.SCOPE_ONELEVEL)
820 secretsdb.delete(del_msg.dn)
822 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
825 msg["priorSecret"] = [res[0]["secret"][0]]
826 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
829 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
834 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
840 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
841 secretsdb.modify(msg)
842 secretsdb.rename(res[0].dn, msg.dn)
844 spn = [ 'HOST/%s' % shortname ]
845 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
846 # we are a domain controller then we add servicePrincipalName
847 # entries for the keytab code to update.
848 spn.extend([ 'HOST/%s' % dnsname ])
849 msg["servicePrincipalName"] = spn
854 def setup_secretsdb(paths, session_info, backend_credentials, lp):
855 """Setup the secrets database.
857 :note: This function does not handle exceptions and transaction on purpose,
858 it's up to the caller to do this job.
860 :param path: Path to the secrets database.
861 :param session_info: Session info.
862 :param credentials: Credentials
863 :param lp: Loadparm context
864 :return: LDB handle for the created secrets database
866 if os.path.exists(paths.secrets):
867 os.unlink(paths.secrets)
869 keytab_path = os.path.join(paths.private_dir, paths.keytab)
870 if os.path.exists(keytab_path):
871 os.unlink(keytab_path)
873 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
874 if os.path.exists(dns_keytab_path):
875 os.unlink(dns_keytab_path)
879 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
881 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
882 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
883 secrets_ldb.transaction_start()
885 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
887 if (backend_credentials is not None and
888 backend_credentials.authentication_requested()):
889 if backend_credentials.get_bind_dn() is not None:
890 setup_add_ldif(secrets_ldb,
891 setup_path("secrets_simple_ldap.ldif"), {
892 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
893 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
896 setup_add_ldif(secrets_ldb,
897 setup_path("secrets_sasl_ldap.ldif"), {
898 "LDAPADMINUSER": backend_credentials.get_username(),
899 "LDAPADMINREALM": backend_credentials.get_realm(),
900 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
903 secrets_ldb.transaction_cancel()
908 def setup_privileges(path, session_info, lp):
909 """Setup the privileges database.
911 :param path: Path to the privileges database.
912 :param session_info: Session info.
913 :param credentials: Credentials
914 :param lp: Loadparm context
915 :return: LDB handle for the created secrets database
917 if os.path.exists(path):
919 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
920 privilege_ldb.erase()
921 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
924 def setup_registry(path, session_info, lp):
925 """Setup the registry.
927 :param path: Path to the registry database
928 :param session_info: Session information
929 :param credentials: Credentials
930 :param lp: Loadparm context
932 reg = samba.registry.Registry()
933 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
934 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
935 provision_reg = setup_path("provision.reg")
936 assert os.path.exists(provision_reg)
937 reg.diff_apply(provision_reg)
940 def setup_idmapdb(path, session_info, lp):
941 """Setup the idmap database.
943 :param path: path to the idmap database
944 :param session_info: Session information
945 :param credentials: Credentials
946 :param lp: Loadparm context
948 if os.path.exists(path):
951 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
953 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
957 def setup_samdb_rootdse(samdb, names):
958 """Setup the SamDB rootdse.
960 :param samdb: Sam Database handle
962 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
963 "SCHEMADN": names.schemadn,
964 "DOMAINDN": names.domaindn,
965 "ROOTDN" : names.rootdn,
966 "CONFIGDN": names.configdn,
967 "SERVERDN": names.serverdn,
971 def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
972 dnspass, domainsid, next_rid, invocationid, 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 # This is Samba4 specific and should be replaced by the correct
1051 # DNS AD-style setup
1052 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1053 "DNSDOMAIN": names.dnsdomain,
1054 "DOMAINDN": names.domaindn,
1055 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1056 "HOSTNAME" : names.hostname,
1057 "DNSNAME" : '%s.%s' % (
1058 names.netbiosname.lower(), names.dnsdomain.lower())
1062 def getpolicypath(sysvolpath, dnsdomain, guid):
1063 """Return the physical path of policy given its guid.
1065 :param sysvolpath: Path to the sysvol folder
1066 :param dnsdomain: DNS name of the AD domain
1067 :param guid: The GUID of the policy
1068 :return: A string with the complete path to the policy folder
1071 guid = "{%s}" % guid
1072 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1076 def create_gpo_struct(policy_path):
1077 if not os.path.exists(policy_path):
1078 os.makedirs(policy_path, 0775)
1079 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1080 "[General]\r\nVersion=0")
1081 p = os.path.join(policy_path, "MACHINE")
1082 if not os.path.exists(p):
1083 os.makedirs(p, 0775)
1084 p = os.path.join(policy_path, "USER")
1085 if not os.path.exists(p):
1086 os.makedirs(p, 0775)
1089 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1090 """Create the default GPO for a domain
1092 :param sysvolpath: Physical path for the sysvol folder
1093 :param dnsdomain: DNS domain name of the AD domain
1094 :param policyguid: GUID of the default domain policy
1095 :param policyguid_dc: GUID of the default domain controler policy
1097 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1098 create_gpo_struct(policy_path)
1100 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1101 create_gpo_struct(policy_path)
1104 def setup_samdb(path, session_info, provision_backend, lp, names,
1105 logger, fill, serverrole, schema, am_rodc=False):
1106 """Setup a complete SAM Database.
1108 :note: This will wipe the main SAM database file!
1111 # Also wipes the database
1112 setup_samdb_partitions(path, logger=logger, lp=lp,
1113 provision_backend=provision_backend, session_info=session_info,
1114 names=names, serverrole=serverrole, schema=schema)
1116 # Load the database, but don's load the global schema and don't connect
1118 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1119 credentials=provision_backend.credentials, lp=lp,
1120 global_schema=False, am_rodc=am_rodc)
1122 logger.info("Pre-loading the Samba 4 and AD schema")
1124 # Load the schema from the one we computed earlier
1125 samdb.set_schema(schema)
1127 # Set the NTDS settings DN manually - in order to have it already around
1128 # before the provisioned tree exists and we connect
1129 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1131 # And now we can connect to the DB - the schema won't be loaded from the
1138 def fill_samdb(samdb, lp, names,
1139 logger, domainsid, domainguid, policyguid, policyguid_dc, fill,
1140 adminpass, krbtgtpass, machinepass, invocationid, dnspass, ntdsguid,
1141 serverrole, am_rodc=False, dom_for_fun_level=None, schema=None,
1142 next_rid=None, dc_rid=None):
1144 if next_rid is None:
1147 # Provision does not make much sense values larger than 1000000000
1148 # as the upper range of the rIDAvailablePool is 1073741823 and
1149 # we don't want to create a domain that cannot allocate rids.
1150 if next_rid < 1000 or next_rid > 1000000000:
1151 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1152 error += "the valid range is %u-%u. The default is %u." % (
1153 1000, 1000000000, 1000)
1154 raise ProvisioningError(error)
1156 # ATTENTION: Do NOT change these default values without discussion with the
1157 # team and/or release manager. They have a big impact on the whole program!
1158 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1160 if dom_for_fun_level is None:
1161 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1163 if dom_for_fun_level > domainControllerFunctionality:
1164 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!")
1166 domainFunctionality = dom_for_fun_level
1167 forestFunctionality = dom_for_fun_level
1169 # Set the NTDS settings DN manually - in order to have it already around
1170 # before the provisioned tree exists and we connect
1171 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1173 samdb.transaction_start()
1175 # Set the domain functionality levels onto the database.
1176 # Various module (the password_hash module in particular) need
1177 # to know what level of AD we are emulating.
1179 # These will be fixed into the database via the database
1180 # modifictions below, but we need them set from the start.
1181 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1182 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1183 samdb.set_opaque_integer("domainControllerFunctionality",
1184 domainControllerFunctionality)
1186 samdb.set_domain_sid(str(domainsid))
1187 samdb.set_invocation_id(invocationid)
1189 logger.info("Adding DomainDN: %s" % names.domaindn)
1191 # impersonate domain admin
1192 admin_session_info = admin_session(lp, str(domainsid))
1193 samdb.set_session_info(admin_session_info)
1194 if domainguid is not None:
1195 domainguid_line = "objectGUID: %s\n-" % domainguid
1197 domainguid_line = ""
1199 descr = b64encode(get_domain_descriptor(domainsid))
1200 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1201 "DOMAINDN": names.domaindn,
1202 "DOMAINSID": str(domainsid),
1203 "DESCRIPTOR": descr,
1204 "DOMAINGUID": domainguid_line
1207 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1208 "DOMAINDN": names.domaindn,
1209 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1210 "NEXTRID": str(next_rid),
1211 "DEFAULTSITE": names.sitename,
1212 "CONFIGDN": names.configdn,
1213 "POLICYGUID": policyguid,
1214 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1215 "SAMBA_VERSION_STRING": version
1218 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1219 if fill == FILL_FULL:
1220 logger.info("Adding configuration container")
1221 descr = b64encode(get_config_descriptor(domainsid))
1222 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1223 "CONFIGDN": names.configdn,
1224 "DESCRIPTOR": descr,
1227 # The LDIF here was created when the Schema object was constructed
1228 logger.info("Setting up sam.ldb schema")
1229 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1230 samdb.modify_ldif(schema.schema_dn_modify)
1231 samdb.write_prefixes_from_schema()
1232 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1233 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1234 {"SCHEMADN": names.schemadn})
1236 # Now register this container in the root of the forest
1237 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1238 msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1242 samdb.transaction_cancel()
1245 samdb.transaction_commit()
1247 samdb.transaction_start()
1249 samdb.invocation_id = invocationid
1251 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1252 if fill == FILL_FULL:
1253 logger.info("Setting up sam.ldb configuration data")
1254 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1255 "CONFIGDN": names.configdn,
1256 "NETBIOSNAME": names.netbiosname,
1257 "DEFAULTSITE": names.sitename,
1258 "DNSDOMAIN": names.dnsdomain,
1259 "DOMAIN": names.domain,
1260 "SCHEMADN": names.schemadn,
1261 "DOMAINDN": names.domaindn,
1262 "SERVERDN": names.serverdn,
1263 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1264 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1267 logger.info("Setting up display specifiers")
1268 display_specifiers_ldif = read_ms_ldif(
1269 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1270 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1271 {"CONFIGDN": names.configdn})
1272 check_all_substituted(display_specifiers_ldif)
1273 samdb.add_ldif(display_specifiers_ldif)
1275 logger.info("Adding users container")
1276 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1277 "DOMAINDN": names.domaindn})
1278 logger.info("Modifying users container")
1279 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1280 "DOMAINDN": names.domaindn})
1281 logger.info("Adding computers container")
1282 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1283 "DOMAINDN": names.domaindn})
1284 logger.info("Modifying computers container")
1285 setup_modify_ldif(samdb,
1286 setup_path("provision_computers_modify.ldif"), {
1287 "DOMAINDN": names.domaindn})
1288 logger.info("Setting up sam.ldb data")
1289 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1290 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1291 "DOMAINDN": names.domaindn,
1292 "NETBIOSNAME": names.netbiosname,
1293 "DEFAULTSITE": names.sitename,
1294 "CONFIGDN": names.configdn,
1295 "SERVERDN": names.serverdn,
1296 "RIDAVAILABLESTART": str(next_rid + 600),
1297 "POLICYGUID_DC": policyguid_dc
1300 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1301 if fill == FILL_FULL:
1302 setup_modify_ldif(samdb,
1303 setup_path("provision_configuration_references.ldif"), {
1304 "CONFIGDN": names.configdn,
1305 "SCHEMADN": names.schemadn})
1307 logger.info("Setting up well known security principals")
1308 setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1309 "CONFIGDN": names.configdn,
1312 if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1313 setup_modify_ldif(samdb,
1314 setup_path("provision_basedn_references.ldif"),
1315 {"DOMAINDN": names.domaindn})
1317 logger.info("Setting up sam.ldb users and groups")
1318 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1319 "DOMAINDN": names.domaindn,
1320 "DOMAINSID": str(domainsid),
1321 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1322 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1325 logger.info("Setting up self join")
1326 setup_self_join(samdb, admin_session_info, names=names, fill=fill,
1327 invocationid=invocationid,
1329 machinepass=machinepass,
1330 domainsid=domainsid,
1333 policyguid=policyguid,
1334 policyguid_dc=policyguid_dc,
1335 domainControllerFunctionality=domainControllerFunctionality,
1338 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1339 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1340 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1341 assert isinstance(names.ntdsguid, str)
1343 samdb.transaction_cancel()
1346 samdb.transaction_commit()
1351 FILL_SUBDOMAIN = "SUBDOMAIN"
1352 FILL_NT4SYNC = "NT4SYNC"
1354 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1355 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)"
1358 def set_dir_acl(path, acl, lp, domsid):
1359 setntacl(lp, path, acl, domsid)
1360 for root, dirs, files in os.walk(path, topdown=False):
1362 setntacl(lp, os.path.join(root, name), acl, domsid)
1364 setntacl(lp, os.path.join(root, name), acl, domsid)
1367 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1368 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1371 :param sysvol: Physical path for the sysvol folder
1372 :param dnsdomain: The DNS name of the domain
1373 :param domainsid: The SID of the domain
1374 :param domaindn: The DN of the domain (ie. DC=...)
1375 :param samdb: An LDB object on the SAM db
1376 :param lp: an LP object
1379 # Set ACL for GPO root folder
1380 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1381 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid))
1383 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1384 attrs=["cn", "nTSecurityDescriptor"],
1385 expression="", scope=ldb.SCOPE_ONELEVEL)
1388 acl = ndr_unpack(security.descriptor,
1389 str(policy["nTSecurityDescriptor"])).as_sddl()
1390 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1391 set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1395 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1397 """Set the ACL for the sysvol share and the subfolders
1399 :param samdb: An LDB object on the SAM db
1400 :param netlogon: Physical path for the netlogon folder
1401 :param sysvol: Physical path for the sysvol folder
1402 :param gid: The GID of the "Domain adminstrators" group
1403 :param domainsid: The SID of the domain
1404 :param dnsdomain: The DNS name of the domain
1405 :param domaindn: The DN of the domain (ie. DC=...)
1409 os.chown(sysvol, -1, gid)
1415 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1416 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid))
1417 for root, dirs, files in os.walk(sysvol, topdown=False):
1420 os.chown(os.path.join(root, name), -1, gid)
1421 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1424 os.chown(os.path.join(root, name), -1, gid)
1425 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1427 # Set acls on Policy folder and policies folders
1428 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp)
1431 def interface_ips_v4(lp):
1432 '''return only IPv4 IPs'''
1433 ips = samba.interface_ips(lp, False)
1436 if i.find(':') == -1:
1440 def interface_ips_v6(lp, linklocal=False):
1441 '''return only IPv6 IPs'''
1442 ips = samba.interface_ips(lp, False)
1445 if i.find(':') != -1 and (linklocal or i.find('%') == -1):
1450 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1451 domainsid, schema=None,
1452 targetdir=None, samdb_fill=FILL_FULL,
1453 hostip=None, hostip6=None,
1454 next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1455 domainguid=None, policyguid=None, policyguid_dc=None,
1456 invocationid=None, machinepass=None, ntdsguid=None,
1457 dns_backend=None, dnspass=None,
1458 serverrole=None, dom_for_fun_level=None,
1459 am_rodc=False, lp=None):
1460 # create/adapt the group policy GUIDs
1461 # Default GUID for default policy are described at
1462 # "How Core Group Policy Works"
1463 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1464 if policyguid is None:
1465 policyguid = DEFAULT_POLICY_GUID
1466 policyguid = policyguid.upper()
1467 if policyguid_dc is None:
1468 policyguid_dc = DEFAULT_DC_POLICY_GUID
1469 policyguid_dc = policyguid_dc.upper()
1471 if invocationid is None:
1472 invocationid = str(uuid.uuid4())
1474 if krbtgtpass is None:
1475 krbtgtpass = samba.generate_random_password(128, 255)
1476 if machinepass is None:
1477 machinepass = samba.generate_random_password(128, 255)
1479 dnspass = samba.generate_random_password(128, 255)
1481 samdb = fill_samdb(samdb, lp, names, logger=logger,
1482 domainsid=domainsid, schema=schema, domainguid=domainguid,
1483 policyguid=policyguid, policyguid_dc=policyguid_dc,
1484 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1485 invocationid=invocationid, machinepass=machinepass,
1486 dnspass=dnspass, ntdsguid=ntdsguid, serverrole=serverrole,
1487 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1488 next_rid=next_rid, dc_rid=dc_rid)
1490 if serverrole == "domain controller":
1491 # Set up group policies (domain policy and domain controller
1493 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1495 setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.wheel_gid,
1496 domainsid, names.dnsdomain, names.domaindn, lp)
1498 secretsdb_self_join(secrets_ldb, domain=names.domain,
1499 realm=names.realm, dnsdomain=names.dnsdomain,
1500 netbiosname=names.netbiosname, domainsid=domainsid,
1501 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1503 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1504 # In future, this might be determined from some configuration
1505 kerberos_enctypes = str(ENC_ALL_TYPES)
1508 msg = ldb.Message(ldb.Dn(samdb,
1509 samdb.searchone("distinguishedName",
1510 expression="samAccountName=%s$" % names.netbiosname,
1511 scope=ldb.SCOPE_SUBTREE)))
1512 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1513 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1514 name="msDS-SupportedEncryptionTypes")
1516 except ldb.LdbError, (enum, estr):
1517 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1518 # It might be that this attribute does not exist in this schema
1521 setup_ad_dns(samdb, secrets_ldb, domainsid, names, paths, lp, logger,
1522 hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
1523 dnspass=dnspass, os_level=dom_for_fun_level,
1524 targetdir=targetdir, site=DEFAULTSITE)
1526 domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1527 attribute="objectGUID")
1528 assert isinstance(domainguid, str)
1530 lastProvisionUSNs = get_last_provision_usn(samdb)
1531 maxUSN = get_max_usn(samdb, str(names.rootdn))
1532 if lastProvisionUSNs is not None:
1533 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1535 set_provision_usn(samdb, 0, maxUSN, invocationid)
1537 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1538 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1539 { 'NTDSGUID' : names.ntdsguid })
1541 # fix any dangling GUIDs from the provision
1542 logger.info("Fixing provision GUIDs")
1543 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
1545 samdb.transaction_start()
1547 # a small number of GUIDs are missing because of ordering issues in the
1549 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1550 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1551 scope=ldb.SCOPE_BASE, attrs=['defaultObjectCategory'])
1552 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1553 scope=ldb.SCOPE_ONELEVEL,
1554 attrs=['ipsecOwnersReference',
1555 'ipsecFilterReference',
1556 'ipsecISAKMPReference',
1557 'ipsecNegotiationPolicyReference',
1558 'ipsecNFAReference'])
1560 samdb.transaction_cancel()
1563 samdb.transaction_commit()
1567 "ROLE_STANDALONE": "standalone",
1568 "ROLE_DOMAIN_MEMBER": "member server",
1569 "ROLE_DOMAIN_BDC": "domain controller",
1570 "ROLE_DOMAIN_PDC": "domain controller",
1571 "dc": "domain controller",
1572 "member": "member server",
1573 "domain controller": "domain controller",
1574 "member server": "member server",
1575 "standalone": "standalone",
1579 def sanitize_server_role(role):
1580 """Sanitize a server role name.
1582 :param role: Server role
1583 :raise ValueError: If the role can not be interpreted
1584 :return: Sanitized server role (one of "member server",
1585 "domain controller", "standalone")
1588 return _ROLES_MAP[role]
1590 raise ValueError(role)
1593 def provision(logger, session_info, credentials, smbconf=None,
1594 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1595 domaindn=None, schemadn=None, configdn=None, serverdn=None,
1596 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1597 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None, krbtgtpass=None,
1598 domainguid=None, policyguid=None, policyguid_dc=None,
1599 dns_backend=None, dnspass=None,
1600 invocationid=None, machinepass=None, ntdsguid=None,
1601 root=None, nobody=None, users=None, wheel=None, backup=None, aci=None,
1602 serverrole=None, dom_for_fun_level=None,
1603 backend_type=None, sitename=None,
1604 ol_mmr_urls=None, ol_olc=None, slapd_path=None,
1605 useeadb=False, am_rodc=False,
1609 :note: caution, this wipes all existing data!
1613 serverrole = sanitize_server_role(serverrole)
1615 raise ProvisioningError('server role (%s) should be one of "domain controller", "member server", "standalone"' % serverrole)
1617 if ldapadminpass is None:
1618 # Make a new, random password between Samba and it's LDAP server
1619 ldapadminpass = samba.generate_random_password(128, 255)
1621 if backend_type is None:
1622 backend_type = "ldb"
1624 if domainsid is None:
1625 domainsid = security.random_sid()
1627 domainsid = security.dom_sid(domainsid)
1629 sid_generator = "internal"
1630 if backend_type == "fedora-ds":
1631 sid_generator = "backend"
1633 root_uid = findnss_uid([root or "root"])
1634 nobody_uid = findnss_uid([nobody or "nobody"])
1635 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1637 wheel_gid = findnss_gid(["wheel", "adm"])
1639 wheel_gid = findnss_gid([wheel])
1641 bind_gid = findnss_gid(["bind", "named"])
1645 if targetdir is not None:
1646 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1647 elif smbconf is None:
1648 smbconf = samba.param.default_path()
1649 if not os.path.exists(os.path.dirname(smbconf)):
1650 os.makedirs(os.path.dirname(smbconf))
1652 server_services = None
1653 if dns_backend == "SAMBA_INTERNAL":
1654 server_services = [ "+dns" ]
1656 # only install a new smb.conf if there isn't one there already
1657 if os.path.exists(smbconf):
1658 # if Samba Team members can't figure out the weird errors
1659 # loading an empty smb.conf gives, then we need to be smarter.
1660 # Pretend it just didn't exist --abartlet
1661 data = open(smbconf, 'r').read()
1662 data = data.lstrip()
1663 if data is None or data == "":
1664 make_smbconf(smbconf, hostname, domain, realm,
1665 serverrole, targetdir, sid_generator, useeadb,
1666 lp=lp, server_services=server_services)
1668 make_smbconf(smbconf, hostname, domain, realm, serverrole,
1669 targetdir, sid_generator, useeadb, lp=lp,
1670 server_services=server_services)
1673 lp = samba.param.LoadParm()
1675 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1676 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
1677 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
1678 sitename=sitename, rootdn=rootdn)
1679 paths = provision_paths_from_lp(lp, names.dnsdomain)
1681 paths.bind_gid = bind_gid
1682 paths.wheel_gid = wheel_gid
1685 logger.info("Looking up IPv4 addresses")
1686 hostips = interface_ips_v4(lp)
1687 if len(hostips) > 0:
1689 if len(hostips) > 1:
1690 logger.warning("More than one IPv4 address found. Using %s",
1692 if hostip == "127.0.0.1":
1695 logger.warning("No IPv4 address will be assigned")
1698 logger.info("Looking up IPv6 addresses")
1699 hostips = interface_ips_v6(lp, linklocal=False)
1701 hostip6 = hostips[0]
1702 if len(hostips) > 1:
1703 logger.warning("More than one IPv6 address found. Using %s", hostip6)
1705 logger.warning("No IPv6 address will be assigned")
1707 names.hostip = hostip
1708 names.hostip6 = hostip6
1710 if serverrole is None:
1711 serverrole = lp.get("server role")
1713 if not os.path.exists(paths.private_dir):
1714 os.mkdir(paths.private_dir)
1715 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1716 os.mkdir(os.path.join(paths.private_dir, "tls"))
1718 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1720 schema = Schema(domainsid, invocationid=invocationid,
1721 schemadn=names.schemadn)
1723 if backend_type == "ldb":
1724 provision_backend = LDBBackend(backend_type, paths=paths,
1725 lp=lp, credentials=credentials,
1726 names=names, logger=logger)
1727 elif backend_type == "existing":
1728 # If support for this is ever added back, then the URI will need to be specified again
1729 provision_backend = ExistingBackend(backend_type, paths=paths,
1730 lp=lp, credentials=credentials,
1731 names=names, logger=logger,
1732 ldap_backend_forced_uri=None)
1733 elif backend_type == "fedora-ds":
1734 provision_backend = FDSBackend(backend_type, paths=paths,
1735 lp=lp, credentials=credentials,
1736 names=names, logger=logger, domainsid=domainsid,
1737 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1738 slapd_path=slapd_path,
1740 elif backend_type == "openldap":
1741 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
1742 lp=lp, credentials=credentials,
1743 names=names, logger=logger, domainsid=domainsid,
1744 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1745 slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls)
1747 raise ValueError("Unknown LDAP backend type selected")
1749 provision_backend.init()
1750 provision_backend.start()
1752 # only install a new shares config db if there is none
1753 if not os.path.exists(paths.shareconf):
1754 logger.info("Setting up share.ldb")
1755 share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
1756 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1758 logger.info("Setting up secrets.ldb")
1759 secrets_ldb = setup_secretsdb(paths,
1760 session_info=session_info,
1761 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1764 logger.info("Setting up the registry")
1765 setup_registry(paths.hklm, session_info, lp=lp)
1767 logger.info("Setting up the privileges database")
1768 setup_privileges(paths.privilege, session_info, lp=lp)
1770 logger.info("Setting up idmap db")
1771 idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
1773 setup_name_mappings(idmap, sid=str(domainsid),
1774 root_uid=root_uid, nobody_uid=nobody_uid,
1775 users_gid=users_gid, wheel_gid=wheel_gid)
1777 logger.info("Setting up SAM db")
1778 samdb = setup_samdb(paths.samdb, session_info,
1779 provision_backend, lp, names, logger=logger,
1780 serverrole=serverrole,
1781 schema=schema, fill=samdb_fill, am_rodc=am_rodc)
1783 if serverrole == "domain controller":
1784 if paths.netlogon is None:
1785 logger.info("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1786 logger.info("Please either remove %s or see the template at %s" %
1787 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1788 assert paths.netlogon is not None
1790 if paths.sysvol is None:
1791 logger.info("Existing smb.conf does not have a [sysvol] share, but you"
1792 " are configuring a DC.")
1793 logger.info("Please either remove %s or see the template at %s" %
1794 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1795 assert paths.sysvol is not None
1797 if not os.path.isdir(paths.netlogon):
1798 os.makedirs(paths.netlogon, 0755)
1800 if adminpass is None:
1801 adminpass = samba.generate_random_password(12, 32)
1802 adminpass_generated = True
1804 adminpass_generated = False
1806 if samdb_fill == FILL_FULL:
1807 provision_fill(samdb, secrets_ldb, logger, names, paths,
1808 schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
1809 hostip=hostip, hostip6=hostip6, domainsid=domainsid,
1810 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
1811 krbtgtpass=krbtgtpass, domainguid=domainguid,
1812 policyguid=policyguid, policyguid_dc=policyguid_dc,
1813 invocationid=invocationid, machinepass=machinepass,
1814 ntdsguid=ntdsguid, dns_backend=dns_backend,
1815 dnspass=dnspass, serverrole=serverrole,
1816 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1819 create_krb5_conf(paths.krb5conf,
1820 dnsdomain=names.dnsdomain, hostname=names.hostname,
1822 logger.info("A Kerberos configuration suitable for Samba 4 has been "
1823 "generated at %s", paths.krb5conf)
1825 if serverrole == "domain controller":
1826 create_dns_update_list(lp, logger, paths)
1828 backend_result = provision_backend.post_setup()
1829 provision_backend.shutdown()
1831 create_phpldapadmin_config(paths.phpldapadminconfig,
1834 secrets_ldb.transaction_cancel()
1837 # Now commit the secrets.ldb to disk
1838 secrets_ldb.transaction_commit()
1840 # the commit creates the dns.keytab, now chown it
1841 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1842 if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
1844 os.chmod(dns_keytab_path, 0640)
1845 os.chown(dns_keytab_path, -1, paths.bind_gid)
1847 if not os.environ.has_key('SAMBA_SELFTEST'):
1848 logger.info("Failed to chown %s to bind gid %u",
1849 dns_keytab_path, paths.bind_gid)
1851 result = ProvisionResult()
1852 result.server_role = serverrole
1853 result.domaindn = domaindn
1854 result.paths = paths
1855 result.names = names
1857 result.samdb = samdb
1858 result.idmap = idmap
1859 result.domainsid = str(domainsid)
1861 if samdb_fill == FILL_FULL:
1862 result.adminpass_generated = adminpass_generated
1863 result.adminpass = adminpass
1865 result.adminpass_generated = False
1866 result.adminpass = None
1868 result.backend_result = backend_result
1873 def provision_become_dc(smbconf=None, targetdir=None,
1874 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
1875 serverdn=None, domain=None, hostname=None, domainsid=None,
1876 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
1877 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
1878 dns_backend=None, root=None, nobody=None, users=None, wheel=None,
1879 backup=None, serverrole=None, ldap_backend=None,
1880 ldap_backend_type=None, sitename=None, debuglevel=1):
1882 logger = logging.getLogger("provision")
1883 samba.set_debug_level(debuglevel)
1885 res = provision(logger, system_session(), None,
1886 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1887 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1888 configdn=configdn, serverdn=serverdn, domain=domain,
1889 hostname=hostname, hostip=None, domainsid=domainsid,
1890 machinepass=machinepass, serverrole="domain controller",
1891 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass)
1892 res.lp.set("debuglevel", str(debuglevel))
1896 def create_phpldapadmin_config(path, ldapi_uri):
1897 """Create a PHP LDAP admin configuration file.
1899 :param path: Path to write the configuration to.
1901 setup_file(setup_path("phpldapadmin-config.php"), path,
1902 {"S4_LDAPI_URI": ldapi_uri})
1905 def create_krb5_conf(path, dnsdomain, hostname, realm):
1906 """Write out a file containing zone statements suitable for inclusion in a
1907 named.conf file (including GSS-TSIG configuration).
1909 :param path: Path of the new named.conf file.
1910 :param dnsdomain: DNS Domain name
1911 :param hostname: Local hostname
1912 :param realm: Realm name
1914 setup_file(setup_path("krb5.conf"), path, {
1915 "DNSDOMAIN": dnsdomain,
1916 "HOSTNAME": hostname,
1921 class ProvisioningError(Exception):
1922 """A generic provision error."""
1924 def __init__(self, value):
1928 return "ProvisioningError: " + self.value
1931 class InvalidNetbiosName(Exception):
1932 """A specified name was not a valid NetBIOS name."""
1934 def __init__(self, name):
1935 super(InvalidNetbiosName, self).__init__(
1936 "The name '%r' is not a valid NetBIOS name" % name)