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
50 check_all_substituted,
51 is_valid_netbios_char,
57 from samba.dcerpc import security, misc
58 from samba.dcerpc.misc import (
62 from samba.dsdb import (
63 DS_DOMAIN_FUNCTION_2003,
64 DS_DOMAIN_FUNCTION_2008_R2,
67 from samba.idmap import IDmapDB
68 from samba.ms_display_specifiers import read_ms_ldif
69 from samba.ntacls import setntacl, dsacl2fsacl
70 from samba.ndr import ndr_pack, ndr_unpack
71 from samba.provision.backend import (
77 from samba.provision.descriptor import (
78 get_config_descriptor,
81 from samba.provision.common import (
86 from samba.provision.sambadns import (
88 create_dns_update_list
93 from samba.schema import Schema
94 from samba.samdb import SamDB
95 from samba.dbchecker import dbcheck
98 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
99 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04fB984F9"
100 DEFAULTSITE = "Default-First-Site-Name"
101 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
104 class ProvisionPaths(object):
107 self.shareconf = None
118 self.dns_keytab = None
121 self.private_dir = None
122 self.phpldapadminconfig = None
125 class ProvisionNames(object):
132 self.ldapmanagerdn = None
133 self.dnsdomain = None
135 self.netbiosname = None
141 def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf, lp):
142 """Get key provision parameters (realm, domain, ...) from a given provision
144 :param samdb: An LDB object connected to the sam.ldb file
145 :param secretsdb: An LDB object connected to the secrets.ldb file
146 :param idmapdb: An LDB object connected to the idmap.ldb file
147 :param paths: A list of path to provision object
148 :param smbconf: Path to the smb.conf file
149 :param lp: A LoadParm object
150 :return: A list of key provision parameters
152 names = ProvisionNames()
153 names.adminpass = None
155 # NT domain, kerberos realm, root dn, domain dn, domain dns name
156 names.domain = string.upper(lp.get("workgroup"))
157 names.realm = lp.get("realm")
158 names.dnsdomain = names.realm.lower()
159 basedn = samba.dn_from_dns_name(names.dnsdomain)
160 names.realm = string.upper(names.realm)
162 # Get the netbiosname first (could be obtained from smb.conf in theory)
163 res = secretsdb.search(expression="(flatname=%s)" %
164 names.domain,base="CN=Primary Domains",
165 scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"])
166 names.netbiosname = str(res[0]["sAMAccountName"]).replace("$","")
168 names.smbconf = smbconf
170 # That's a bit simplistic but it's ok as long as we have only 3
172 current = samdb.search(expression="(objectClass=*)",
173 base="", scope=ldb.SCOPE_BASE,
174 attrs=["defaultNamingContext", "schemaNamingContext",
175 "configurationNamingContext","rootDomainNamingContext"])
177 names.configdn = current[0]["configurationNamingContext"]
178 configdn = str(names.configdn)
179 names.schemadn = current[0]["schemaNamingContext"]
180 if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb,
181 current[0]["defaultNamingContext"][0]))):
182 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
183 "is not the same ..." % (paths.samdb,
184 str(current[0]["defaultNamingContext"][0]),
185 paths.smbconf, basedn)))
187 names.domaindn=current[0]["defaultNamingContext"]
188 names.rootdn=current[0]["rootDomainNamingContext"]
190 res3 = samdb.search(expression="(objectClass=site)",
191 base="CN=Sites," + configdn, scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
192 names.sitename = str(res3[0]["cn"])
194 # dns hostname and server dn
195 res4 = samdb.search(expression="(CN=%s)" % names.netbiosname,
196 base="OU=Domain Controllers,%s" % basedn,
197 scope=ldb.SCOPE_ONELEVEL, attrs=["dNSHostName"])
198 names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain,"")
200 server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
201 attrs=[], base=configdn)
202 names.serverdn = server_res[0].dn
204 # invocation id/objectguid
205 res5 = samdb.search(expression="(objectClass=*)",
206 base="CN=NTDS Settings,%s" % str(names.serverdn), scope=ldb.SCOPE_BASE,
207 attrs=["invocationID", "objectGUID"])
208 names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0]))
209 names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0]))
212 res6 = samdb.search(expression="(objectClass=*)", base=basedn,
213 scope=ldb.SCOPE_BASE, attrs=["objectGUID",
214 "objectSid","msDS-Behavior-Version" ])
215 names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
216 names.domainsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
217 if res6[0].get("msDS-Behavior-Version") is None or \
218 int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
219 names.domainlevel = DS_DOMAIN_FUNCTION_2000
221 names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
224 res7 = samdb.search(expression="(displayName=Default Domain Policy)",
225 base="CN=Policies,CN=System," + basedn,
226 scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
227 names.policyid = str(res7[0]["cn"]).replace("{","").replace("}","")
229 res8 = samdb.search(expression="(displayName=Default Domain Controllers"
231 base="CN=Policies,CN=System," + basedn,
232 scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
234 names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","")
236 names.policyid_dc = None
237 res9 = idmapdb.search(expression="(cn=%s)" %
238 (security.SID_BUILTIN_ADMINISTRATORS),
241 names.wheel_gid = res9[0]["xidNumber"]
243 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid")
247 def update_provision_usn(samdb, low, high, id, replace=False):
248 """Update the field provisionUSN in sam.ldb
250 This field is used to track range of USN modified by provision and
252 This value is used afterward by next provision to figure out if
253 the field have been modified since last provision.
255 :param samdb: An LDB object connect to sam.ldb
256 :param low: The lowest USN modified by this upgrade
257 :param high: The highest USN modified by this upgrade
258 :param id: The invocation id of the samba's dc
259 :param replace: A boolean indicating if the range should replace any
260 existing one or appended (default)
265 entry = samdb.search(base="@PROVISION",
266 scope=ldb.SCOPE_BASE,
267 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
268 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
269 if not re.search(';', e):
270 e = "%s;%s" % (e, id)
273 tab.append("%s-%s;%s" % (low, high, id))
274 delta = ldb.Message()
275 delta.dn = ldb.Dn(samdb, "@PROVISION")
276 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
277 ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE)
278 entry = samdb.search(expression='provisionnerID=*',
279 base="@PROVISION", scope=ldb.SCOPE_BASE,
280 attrs=["provisionnerID"])
281 if len(entry) == 0 or len(entry[0]) == 0:
282 delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
286 def set_provision_usn(samdb, low, high, id):
287 """Set the field provisionUSN in sam.ldb
288 This field is used to track range of USN modified by provision and
290 This value is used afterward by next provision to figure out if
291 the field have been modified since last provision.
293 :param samdb: An LDB object connect to sam.ldb
294 :param low: The lowest USN modified by this upgrade
295 :param high: The highest USN modified by this upgrade
296 :param id: The invocationId of the provision"""
299 tab.append("%s-%s;%s" % (low, high, id))
301 delta = ldb.Message()
302 delta.dn = ldb.Dn(samdb, "@PROVISION")
303 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
304 ldb.FLAG_MOD_ADD, LAST_PROVISION_USN_ATTRIBUTE)
308 def get_max_usn(samdb,basedn):
309 """ This function return the biggest USN present in the provision
311 :param samdb: A LDB object pointing to the sam.ldb
312 :param basedn: A string containing the base DN of the provision
314 :return: The biggest USN in the provision"""
316 res = samdb.search(expression="objectClass=*",base=basedn,
317 scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
318 controls=["search_options:1:2",
319 "server_sort:1:1:uSNChanged",
320 "paged_results:1:1"])
321 return res[0]["uSNChanged"]
324 def get_last_provision_usn(sam):
325 """Get USNs ranges modified by a provision or an upgradeprovision
327 :param sam: An LDB object pointing to the sam.ldb
328 :return: a dictionnary which keys are invocation id and values are an array
329 of integer representing the different ranges
332 entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE,
333 base="@PROVISION", scope=ldb.SCOPE_BASE,
334 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
335 except ldb.LdbError, (ecode, emsg):
336 if ecode == ldb.ERR_NO_SUCH_OBJECT:
343 if entry[0].get("provisionnerID"):
344 for e in entry[0]["provisionnerID"]:
346 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
347 tab1 = str(r).split(';')
352 if (len(myids) > 0 and id not in myids):
354 tab2 = p.split(tab1[0])
355 if range.get(id) == None:
357 range[id].append(tab2[0])
358 range[id].append(tab2[1])
364 class ProvisionResult(object):
365 """Result of a provision.
367 :ivar server_role: The server role
368 :ivar paths: ProvisionPaths instance
369 :ivar domaindn: The domain dn, as string
373 self.server_role = None
380 self.domainsid = None
381 self.adminpass_generated = None
382 self.adminpass = None
383 self.backend_result = None
385 def report_logger(self, logger):
386 """Report this provision result to a logger."""
388 "Once the above files are installed, your Samba4 server will "
390 if self.adminpass_generated:
391 logger.info("Admin password: %s", self.adminpass)
392 logger.info("Server Role: %s", self.server_role)
393 logger.info("Hostname: %s", self.names.hostname)
394 logger.info("NetBIOS Domain: %s", self.names.domain)
395 logger.info("DNS Domain: %s", self.names.dnsdomain)
396 logger.info("DOMAIN SID: %s", self.domainsid)
398 if self.paths.phpldapadminconfig is not None:
400 "A phpLDAPadmin configuration file suitable for administering "
401 "the Samba 4 LDAP server has been created in %s.",
402 self.paths.phpldapadminconfig)
404 if self.backend_result:
405 self.backend_result.report_logger(logger)
408 def check_install(lp, session_info, credentials):
409 """Check whether the current install seems ok.
411 :param lp: Loadparm context
412 :param session_info: Session information
413 :param credentials: Credentials
415 if lp.get("realm") == "":
416 raise Exception("Realm empty")
417 samdb = Ldb(lp.samdb_url(), session_info=session_info,
418 credentials=credentials, lp=lp)
419 if len(samdb.search("(cn=Administrator)")) != 1:
420 raise ProvisioningError("No administrator account found")
423 def findnss(nssfn, names):
424 """Find a user or group from a list of possibilities.
426 :param nssfn: NSS Function to try (should raise KeyError if not found)
427 :param names: Names to check.
428 :return: Value return by first names list.
435 raise KeyError("Unable to find user/group in %r" % names)
438 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
439 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
442 def provision_paths_from_lp(lp, dnsdomain):
443 """Set the default paths for provisioning.
445 :param lp: Loadparm context.
446 :param dnsdomain: DNS Domain name
448 paths = ProvisionPaths()
449 paths.private_dir = lp.get("private dir")
451 # This is stored without path prefix for the "privateKeytab" attribute in
452 # "secrets_dns.ldif".
453 paths.dns_keytab = "dns.keytab"
454 paths.keytab = "secrets.keytab"
456 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
457 paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
458 paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
459 paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
460 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
461 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
462 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
463 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
464 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
465 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
466 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
467 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
468 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
469 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
470 paths.phpldapadminconfig = os.path.join(paths.private_dir,
471 "phpldapadmin-config.php")
472 paths.hklm = "hklm.ldb"
473 paths.hkcr = "hkcr.ldb"
474 paths.hkcu = "hkcu.ldb"
475 paths.hku = "hku.ldb"
476 paths.hkpd = "hkpd.ldb"
477 paths.hkpt = "hkpt.ldb"
478 paths.sysvol = lp.get("path", "sysvol")
479 paths.netlogon = lp.get("path", "netlogon")
480 paths.smbconf = lp.configfile
484 def determine_netbios_name(hostname):
485 """Determine a netbios name from a hostname."""
486 # remove forbidden chars and force the length to be <16
487 netbiosname = "".join([x for x in hostname if is_valid_netbios_char(x)])
488 return netbiosname[:MAX_NETBIOS_NAME_LEN].upper()
491 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
492 serverrole=None, rootdn=None, domaindn=None, configdn=None,
493 schemadn=None, serverdn=None, sitename=None):
494 """Guess configuration settings to use."""
497 hostname = socket.gethostname().split(".")[0]
499 netbiosname = lp.get("netbios name")
500 if netbiosname is None:
501 netbiosname = determine_netbios_name(hostname)
502 assert netbiosname is not None
503 netbiosname = netbiosname.upper()
504 if not valid_netbios_name(netbiosname):
505 raise InvalidNetbiosName(netbiosname)
507 if dnsdomain is None:
508 dnsdomain = lp.get("realm")
509 if dnsdomain is None or dnsdomain == "":
510 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
512 dnsdomain = dnsdomain.lower()
514 if serverrole is None:
515 serverrole = lp.get("server role")
516 if serverrole is None:
517 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
519 serverrole = serverrole.lower()
521 realm = dnsdomain.upper()
523 if lp.get("realm") == "":
524 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
526 if lp.get("realm").upper() != realm:
527 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))
529 if lp.get("server role").lower() != serverrole:
530 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))
532 if serverrole == "domain controller":
534 # This will, for better or worse, default to 'WORKGROUP'
535 domain = lp.get("workgroup")
536 domain = domain.upper()
538 if lp.get("workgroup").upper() != domain:
539 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))
542 domaindn = samba.dn_from_dns_name(dnsdomain)
544 if domain == netbiosname:
545 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
549 domaindn = "DC=" + netbiosname
551 if not valid_netbios_name(domain):
552 raise InvalidNetbiosName(domain)
554 if hostname.upper() == realm:
555 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
556 if netbiosname.upper() == realm:
557 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
559 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
565 configdn = "CN=Configuration," + rootdn
567 schemadn = "CN=Schema," + configdn
572 names = ProvisionNames()
573 names.rootdn = rootdn
574 names.domaindn = domaindn
575 names.configdn = configdn
576 names.schemadn = schemadn
577 names.ldapmanagerdn = "CN=Manager," + rootdn
578 names.dnsdomain = dnsdomain
579 names.domain = domain
581 names.netbiosname = netbiosname
582 names.hostname = hostname
583 names.sitename = sitename
584 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
585 netbiosname, sitename, configdn)
590 def make_smbconf(smbconf, hostname, domain, realm, serverrole,
591 targetdir, sid_generator="internal", eadb=False, lp=None,
592 server_services=None):
593 """Create a new smb.conf file based on a couple of basic settings.
595 assert smbconf is not None
597 hostname = socket.gethostname().split(".")[0]
598 netbiosname = determine_netbios_name(hostname)
600 netbiosname = hostname.upper()
602 if serverrole is None:
603 serverrole = "standalone"
605 assert serverrole in ("domain controller", "member server", "standalone")
606 if serverrole == "domain controller":
608 elif serverrole == "member server":
609 smbconfsuffix = "member"
610 elif serverrole == "standalone":
611 smbconfsuffix = "standalone"
613 if sid_generator is None:
614 sid_generator = "internal"
616 assert domain is not None
617 domain = domain.upper()
619 assert realm is not None
620 realm = realm.upper()
623 lp = samba.param.LoadParm()
624 #Load non-existant file
625 if os.path.exists(smbconf):
627 if eadb and not lp.get("posix:eadb"):
628 if targetdir is not None:
629 privdir = os.path.join(targetdir, "private")
631 privdir = lp.get("private dir")
632 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
634 if server_services is not None:
635 server_services_line = "server services = " + " ".join(server_services)
637 server_services_line = ""
639 if targetdir is not None:
640 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
641 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
642 statedir_line = "state directory = " + os.path.abspath(targetdir)
643 cachedir_line = "cache directory = " + os.path.abspath(targetdir)
645 lp.set("lock dir", os.path.abspath(targetdir))
646 lp.set("state directory", os.path.abspath(targetdir))
647 lp.set("cache directory", os.path.abspath(targetdir))
654 sysvol = os.path.join(lp.get("state directory"), "sysvol")
655 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
657 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
659 "NETBIOS_NAME": netbiosname,
662 "SERVERROLE": serverrole,
663 "NETLOGONPATH": netlogon,
664 "SYSVOLPATH": sysvol,
665 "PRIVATEDIR_LINE": privatedir_line,
666 "LOCKDIR_LINE": lockdir_line,
667 "STATEDIR_LINE": statedir_line,
668 "CACHEDIR_LINE": cachedir_line,
669 "SERVER_SERVICES_LINE": server_services_line
672 # reload the smb.conf
675 # and dump it without any values that are the default
676 # this ensures that any smb.conf parameters that were set
677 # on the provision/join command line are set in the resulting smb.conf
678 f = open(smbconf, mode='w')
685 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
686 users_gid, wheel_gid):
687 """setup reasonable name mappings for sam names to unix names.
689 :param samdb: SamDB object.
690 :param idmap: IDmap db object.
691 :param sid: The domain sid.
692 :param domaindn: The domain DN.
693 :param root_uid: uid of the UNIX root user.
694 :param nobody_uid: uid of the UNIX nobody user.
695 :param users_gid: gid of the UNIX users group.
696 :param wheel_gid: gid of the UNIX wheel group.
698 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
699 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
701 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
702 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
705 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
706 provision_backend, names, schema, serverrole,
708 """Setup the partitions for the SAM database.
710 Alternatively, provision() may call this, and then populate the database.
712 :note: This will wipe the Sam Database!
714 :note: This function always removes the local SAM LDB file. The erase
715 parameter controls whether to erase the existing data, which
716 may not be stored locally but in LDAP.
719 assert session_info is not None
721 # We use options=["modules:"] to stop the modules loading - we
722 # just want to wipe and re-initialise the database, not start it up
725 os.unlink(samdb_path)
729 samdb = Ldb(url=samdb_path, session_info=session_info,
730 lp=lp, options=["modules:"])
732 ldap_backend_line = "# No LDAP backend"
733 if provision_backend.type != "ldb":
734 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
736 samdb.transaction_start()
738 logger.info("Setting up sam.ldb partitions and settings")
739 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
740 "LDAP_BACKEND_LINE": ldap_backend_line
744 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
745 "BACKEND_TYPE": provision_backend.type,
746 "SERVER_ROLE": serverrole
749 logger.info("Setting up sam.ldb rootDSE")
750 setup_samdb_rootdse(samdb, names)
752 samdb.transaction_cancel()
755 samdb.transaction_commit()
758 def secretsdb_self_join(secretsdb, domain,
759 netbiosname, machinepass, domainsid=None,
760 realm=None, dnsdomain=None,
762 key_version_number=1,
763 secure_channel_type=SEC_CHAN_WKSTA):
764 """Add domain join-specific bits to a secrets database.
766 :param secretsdb: Ldb Handle to the secrets database
767 :param machinepass: Machine password
769 attrs = ["whenChanged",
776 if realm is not None:
777 if dnsdomain is None:
778 dnsdomain = realm.lower()
779 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
782 shortname = netbiosname.lower()
784 # We don't need to set msg["flatname"] here, because rdn_name will handle
785 # it, and it causes problems for modifies anyway
786 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
787 msg["secureChannelType"] = [str(secure_channel_type)]
788 msg["objectClass"] = ["top", "primaryDomain"]
789 if dnsname is not None:
790 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
791 msg["realm"] = [realm]
792 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
793 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
794 msg["privateKeytab"] = ["secrets.keytab"]
796 msg["secret"] = [machinepass]
797 msg["samAccountName"] = ["%s$" % netbiosname]
798 msg["secureChannelType"] = [str(secure_channel_type)]
799 if domainsid is not None:
800 msg["objectSid"] = [ndr_pack(domainsid)]
802 # This complex expression tries to ensure that we don't have more
803 # than one record for this SID, realm or netbios domain at a time,
804 # but we don't delete the old record that we are about to modify,
805 # because that would delete the keytab and previous password.
806 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
807 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(dn=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
808 scope=ldb.SCOPE_ONELEVEL)
811 secretsdb.delete(del_msg.dn)
813 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
816 msg["priorSecret"] = [res[0]["secret"][0]]
817 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
820 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
825 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
831 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
832 secretsdb.modify(msg)
833 secretsdb.rename(res[0].dn, msg.dn)
835 spn = [ 'HOST/%s' % shortname ]
836 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
837 # we are a domain controller then we add servicePrincipalName
838 # entries for the keytab code to update.
839 spn.extend([ 'HOST/%s' % dnsname ])
840 msg["servicePrincipalName"] = spn
845 def setup_secretsdb(paths, session_info, backend_credentials, lp):
846 """Setup the secrets database.
848 :note: This function does not handle exceptions and transaction on purpose,
849 it's up to the caller to do this job.
851 :param path: Path to the secrets database.
852 :param session_info: Session info.
853 :param credentials: Credentials
854 :param lp: Loadparm context
855 :return: LDB handle for the created secrets database
857 if os.path.exists(paths.secrets):
858 os.unlink(paths.secrets)
860 keytab_path = os.path.join(paths.private_dir, paths.keytab)
861 if os.path.exists(keytab_path):
862 os.unlink(keytab_path)
864 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
865 if os.path.exists(dns_keytab_path):
866 os.unlink(dns_keytab_path)
870 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
872 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
873 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
874 secrets_ldb.transaction_start()
876 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
878 if (backend_credentials is not None and
879 backend_credentials.authentication_requested()):
880 if backend_credentials.get_bind_dn() is not None:
881 setup_add_ldif(secrets_ldb,
882 setup_path("secrets_simple_ldap.ldif"), {
883 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
884 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
887 setup_add_ldif(secrets_ldb,
888 setup_path("secrets_sasl_ldap.ldif"), {
889 "LDAPADMINUSER": backend_credentials.get_username(),
890 "LDAPADMINREALM": backend_credentials.get_realm(),
891 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
894 secrets_ldb.transaction_cancel()
899 def setup_privileges(path, session_info, lp):
900 """Setup the privileges database.
902 :param path: Path to the privileges database.
903 :param session_info: Session info.
904 :param credentials: Credentials
905 :param lp: Loadparm context
906 :return: LDB handle for the created secrets database
908 if os.path.exists(path):
910 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
911 privilege_ldb.erase()
912 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
915 def setup_registry(path, session_info, lp):
916 """Setup the registry.
918 :param path: Path to the registry database
919 :param session_info: Session information
920 :param credentials: Credentials
921 :param lp: Loadparm context
923 reg = samba.registry.Registry()
924 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
925 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
926 provision_reg = setup_path("provision.reg")
927 assert os.path.exists(provision_reg)
928 reg.diff_apply(provision_reg)
931 def setup_idmapdb(path, session_info, lp):
932 """Setup the idmap database.
934 :param path: path to the idmap database
935 :param session_info: Session information
936 :param credentials: Credentials
937 :param lp: Loadparm context
939 if os.path.exists(path):
942 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
944 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
948 def setup_samdb_rootdse(samdb, names):
949 """Setup the SamDB rootdse.
951 :param samdb: Sam Database handle
953 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
954 "SCHEMADN": names.schemadn,
955 "DOMAINDN": names.domaindn,
956 "ROOTDN" : names.rootdn,
957 "CONFIGDN": names.configdn,
958 "SERVERDN": names.serverdn,
962 def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
963 dnspass, domainsid, next_rid, invocationid, policyguid, policyguid_dc,
964 domainControllerFunctionality, ntdsguid=None, dc_rid=None):
965 """Join a host to its own domain."""
966 assert isinstance(invocationid, str)
967 if ntdsguid is not None:
968 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
975 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
976 "CONFIGDN": names.configdn,
977 "SCHEMADN": names.schemadn,
978 "DOMAINDN": names.domaindn,
979 "SERVERDN": names.serverdn,
980 "INVOCATIONID": invocationid,
981 "NETBIOSNAME": names.netbiosname,
982 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
983 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
984 "DOMAINSID": str(domainsid),
985 "DCRID": str(dc_rid),
986 "SAMBA_VERSION_STRING": version,
987 "NTDSGUID": ntdsguid_line,
988 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
989 domainControllerFunctionality),
990 "RIDALLOCATIONSTART": str(next_rid + 100),
991 "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
993 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
994 "POLICYGUID": policyguid,
995 "POLICYGUID_DC": policyguid_dc,
996 "DNSDOMAIN": names.dnsdomain,
997 "DOMAINDN": names.domaindn})
999 # If we are setting up a subdomain, then this has been replicated in, so we
1000 # don't need to add it
1001 if fill == FILL_FULL:
1002 setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
1003 "CONFIGDN": names.configdn,
1004 "SCHEMADN": names.schemadn,
1005 "DOMAINDN": names.domaindn,
1006 "SERVERDN": names.serverdn,
1007 "INVOCATIONID": invocationid,
1008 "NETBIOSNAME": names.netbiosname,
1009 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1010 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1011 "DOMAINSID": str(domainsid),
1012 "DCRID": str(dc_rid),
1013 "SAMBA_VERSION_STRING": version,
1014 "NTDSGUID": ntdsguid_line,
1015 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1016 domainControllerFunctionality)})
1018 # Setup fSMORoleOwner entries to point at the newly created DC entry
1019 setup_modify_ldif(samdb,
1020 setup_path("provision_self_join_modify_config.ldif"), {
1021 "CONFIGDN": names.configdn,
1022 "SCHEMADN": names.schemadn,
1023 "DEFAULTSITE": names.sitename,
1024 "NETBIOSNAME": names.netbiosname,
1025 "SERVERDN": names.serverdn,
1028 system_session_info = system_session()
1029 samdb.set_session_info(system_session_info)
1030 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1031 # modify a serverReference under cn=config when we are a subdomain, we must
1032 # be system due to ACLs
1033 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1034 "DOMAINDN": names.domaindn,
1035 "SERVERDN": names.serverdn,
1036 "NETBIOSNAME": names.netbiosname,
1039 samdb.set_session_info(admin_session_info)
1041 # This is Samba4 specific and should be replaced by the correct
1042 # DNS AD-style setup
1043 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1044 "DNSDOMAIN": names.dnsdomain,
1045 "DOMAINDN": names.domaindn,
1046 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1047 "HOSTNAME" : names.hostname,
1048 "DNSNAME" : '%s.%s' % (
1049 names.netbiosname.lower(), names.dnsdomain.lower())
1053 def getpolicypath(sysvolpath, dnsdomain, guid):
1054 """Return the physical path of policy given its guid.
1056 :param sysvolpath: Path to the sysvol folder
1057 :param dnsdomain: DNS name of the AD domain
1058 :param guid: The GUID of the policy
1059 :return: A string with the complete path to the policy folder
1062 guid = "{%s}" % guid
1063 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1067 def create_gpo_struct(policy_path):
1068 if not os.path.exists(policy_path):
1069 os.makedirs(policy_path, 0775)
1070 f = open(os.path.join(policy_path, "GPT.INI"), 'w')
1072 f.write("[General]\r\nVersion=0")
1075 p = os.path.join(policy_path, "MACHINE")
1076 if not os.path.exists(p):
1077 os.makedirs(p, 0775)
1078 p = os.path.join(policy_path, "USER")
1079 if not os.path.exists(p):
1080 os.makedirs(p, 0775)
1083 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1084 """Create the default GPO for a domain
1086 :param sysvolpath: Physical path for the sysvol folder
1087 :param dnsdomain: DNS domain name of the AD domain
1088 :param policyguid: GUID of the default domain policy
1089 :param policyguid_dc: GUID of the default domain controler policy
1091 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1092 create_gpo_struct(policy_path)
1094 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1095 create_gpo_struct(policy_path)
1098 def setup_samdb(path, session_info, provision_backend, lp, names,
1099 logger, fill, serverrole, schema, am_rodc=False):
1100 """Setup a complete SAM Database.
1102 :note: This will wipe the main SAM database file!
1105 # Also wipes the database
1106 setup_samdb_partitions(path, logger=logger, lp=lp,
1107 provision_backend=provision_backend, session_info=session_info,
1108 names=names, serverrole=serverrole, schema=schema)
1110 # Load the database, but don's load the global schema and don't connect
1112 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1113 credentials=provision_backend.credentials, lp=lp,
1114 global_schema=False, am_rodc=am_rodc)
1116 logger.info("Pre-loading the Samba 4 and AD schema")
1118 # Load the schema from the one we computed earlier
1119 samdb.set_schema(schema)
1121 # Set the NTDS settings DN manually - in order to have it already around
1122 # before the provisioned tree exists and we connect
1123 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1125 # And now we can connect to the DB - the schema won't be loaded from the
1132 def fill_samdb(samdb, lp, names,
1133 logger, domainsid, domainguid, policyguid, policyguid_dc, fill,
1134 adminpass, krbtgtpass, machinepass, invocationid, dnspass, ntdsguid,
1135 serverrole, am_rodc=False, dom_for_fun_level=None, schema=None,
1136 next_rid=None, dc_rid=None):
1138 if next_rid is None:
1141 # Provision does not make much sense values larger than 1000000000
1142 # as the upper range of the rIDAvailablePool is 1073741823 and
1143 # we don't want to create a domain that cannot allocate rids.
1144 if next_rid < 1000 or next_rid > 1000000000:
1145 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1146 error += "the valid range is %u-%u. The default is %u." % (
1147 1000, 1000000000, 1000)
1148 raise ProvisioningError(error)
1150 # ATTENTION: Do NOT change these default values without discussion with the
1151 # team and/or release manager. They have a big impact on the whole program!
1152 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1154 if dom_for_fun_level is None:
1155 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1157 if dom_for_fun_level > domainControllerFunctionality:
1158 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!")
1160 domainFunctionality = dom_for_fun_level
1161 forestFunctionality = dom_for_fun_level
1163 # Set the NTDS settings DN manually - in order to have it already around
1164 # before the provisioned tree exists and we connect
1165 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1167 samdb.transaction_start()
1169 # Set the domain functionality levels onto the database.
1170 # Various module (the password_hash module in particular) need
1171 # to know what level of AD we are emulating.
1173 # These will be fixed into the database via the database
1174 # modifictions below, but we need them set from the start.
1175 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1176 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1177 samdb.set_opaque_integer("domainControllerFunctionality",
1178 domainControllerFunctionality)
1180 samdb.set_domain_sid(str(domainsid))
1181 samdb.set_invocation_id(invocationid)
1183 logger.info("Adding DomainDN: %s" % names.domaindn)
1185 # impersonate domain admin
1186 admin_session_info = admin_session(lp, str(domainsid))
1187 samdb.set_session_info(admin_session_info)
1188 if domainguid is not None:
1189 domainguid_line = "objectGUID: %s\n-" % domainguid
1191 domainguid_line = ""
1193 descr = b64encode(get_domain_descriptor(domainsid))
1194 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1195 "DOMAINDN": names.domaindn,
1196 "DOMAINSID": str(domainsid),
1197 "DESCRIPTOR": descr,
1198 "DOMAINGUID": domainguid_line
1201 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1202 "DOMAINDN": names.domaindn,
1203 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1204 "NEXTRID": str(next_rid),
1205 "DEFAULTSITE": names.sitename,
1206 "CONFIGDN": names.configdn,
1207 "POLICYGUID": policyguid,
1208 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1209 "SAMBA_VERSION_STRING": version
1212 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1213 if fill == FILL_FULL:
1214 logger.info("Adding configuration container")
1215 descr = b64encode(get_config_descriptor(domainsid))
1216 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1217 "CONFIGDN": names.configdn,
1218 "DESCRIPTOR": descr,
1221 # The LDIF here was created when the Schema object was constructed
1222 logger.info("Setting up sam.ldb schema")
1223 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1224 samdb.modify_ldif(schema.schema_dn_modify)
1225 samdb.write_prefixes_from_schema()
1226 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1227 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1228 {"SCHEMADN": names.schemadn})
1230 # Now register this container in the root of the forest
1231 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1232 msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1236 samdb.transaction_cancel()
1239 samdb.transaction_commit()
1241 samdb.transaction_start()
1243 samdb.invocation_id = invocationid
1245 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1246 if fill == FILL_FULL:
1247 logger.info("Setting up sam.ldb configuration data")
1248 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1249 "CONFIGDN": names.configdn,
1250 "NETBIOSNAME": names.netbiosname,
1251 "DEFAULTSITE": names.sitename,
1252 "DNSDOMAIN": names.dnsdomain,
1253 "DOMAIN": names.domain,
1254 "SCHEMADN": names.schemadn,
1255 "DOMAINDN": names.domaindn,
1256 "SERVERDN": names.serverdn,
1257 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1258 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1261 logger.info("Setting up display specifiers")
1262 display_specifiers_ldif = read_ms_ldif(
1263 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1264 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1265 {"CONFIGDN": names.configdn})
1266 check_all_substituted(display_specifiers_ldif)
1267 samdb.add_ldif(display_specifiers_ldif)
1269 logger.info("Adding users container")
1270 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1271 "DOMAINDN": names.domaindn})
1272 logger.info("Modifying users container")
1273 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1274 "DOMAINDN": names.domaindn})
1275 logger.info("Adding computers container")
1276 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1277 "DOMAINDN": names.domaindn})
1278 logger.info("Modifying computers container")
1279 setup_modify_ldif(samdb,
1280 setup_path("provision_computers_modify.ldif"), {
1281 "DOMAINDN": names.domaindn})
1282 logger.info("Setting up sam.ldb data")
1283 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1284 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1285 "DOMAINDN": names.domaindn,
1286 "NETBIOSNAME": names.netbiosname,
1287 "DEFAULTSITE": names.sitename,
1288 "CONFIGDN": names.configdn,
1289 "SERVERDN": names.serverdn,
1290 "RIDAVAILABLESTART": str(next_rid + 600),
1291 "POLICYGUID_DC": policyguid_dc
1294 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1295 if fill == FILL_FULL:
1296 setup_modify_ldif(samdb,
1297 setup_path("provision_configuration_references.ldif"), {
1298 "CONFIGDN": names.configdn,
1299 "SCHEMADN": names.schemadn})
1301 logger.info("Setting up well known security principals")
1302 setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1303 "CONFIGDN": names.configdn,
1306 if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1307 setup_modify_ldif(samdb,
1308 setup_path("provision_basedn_references.ldif"),
1309 {"DOMAINDN": names.domaindn})
1311 logger.info("Setting up sam.ldb users and groups")
1312 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1313 "DOMAINDN": names.domaindn,
1314 "DOMAINSID": str(domainsid),
1315 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1316 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1319 logger.info("Setting up self join")
1320 setup_self_join(samdb, admin_session_info, names=names, fill=fill,
1321 invocationid=invocationid,
1323 machinepass=machinepass,
1324 domainsid=domainsid,
1327 policyguid=policyguid,
1328 policyguid_dc=policyguid_dc,
1329 domainControllerFunctionality=domainControllerFunctionality,
1332 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1333 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1334 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1335 assert isinstance(names.ntdsguid, str)
1337 samdb.transaction_cancel()
1340 samdb.transaction_commit()
1345 FILL_SUBDOMAIN = "SUBDOMAIN"
1346 FILL_NT4SYNC = "NT4SYNC"
1348 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1349 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)"
1352 def set_dir_acl(path, acl, lp, domsid):
1353 setntacl(lp, path, acl, domsid)
1354 for root, dirs, files in os.walk(path, topdown=False):
1356 setntacl(lp, os.path.join(root, name), acl, domsid)
1358 setntacl(lp, os.path.join(root, name), acl, domsid)
1361 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1362 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1365 :param sysvol: Physical path for the sysvol folder
1366 :param dnsdomain: The DNS name of the domain
1367 :param domainsid: The SID of the domain
1368 :param domaindn: The DN of the domain (ie. DC=...)
1369 :param samdb: An LDB object on the SAM db
1370 :param lp: an LP object
1373 # Set ACL for GPO root folder
1374 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1375 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid))
1377 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1378 attrs=["cn", "nTSecurityDescriptor"],
1379 expression="", scope=ldb.SCOPE_ONELEVEL)
1382 acl = ndr_unpack(security.descriptor,
1383 str(policy["nTSecurityDescriptor"])).as_sddl()
1384 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1385 set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1389 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1391 """Set the ACL for the sysvol share and the subfolders
1393 :param samdb: An LDB object on the SAM db
1394 :param netlogon: Physical path for the netlogon folder
1395 :param sysvol: Physical path for the sysvol folder
1396 :param gid: The GID of the "Domain adminstrators" group
1397 :param domainsid: The SID of the domain
1398 :param dnsdomain: The DNS name of the domain
1399 :param domaindn: The DN of the domain (ie. DC=...)
1403 os.chown(sysvol, -1, gid)
1409 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1410 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid))
1411 for root, dirs, files in os.walk(sysvol, topdown=False):
1414 os.chown(os.path.join(root, name), -1, gid)
1415 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1418 os.chown(os.path.join(root, name), -1, gid)
1419 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1421 # Set acls on Policy folder and policies folders
1422 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp)
1425 def interface_ips_v4(lp):
1426 '''return only IPv4 IPs'''
1427 ips = samba.interface_ips(lp, False)
1430 if i.find(':') == -1:
1434 def interface_ips_v6(lp, linklocal=False):
1435 '''return only IPv6 IPs'''
1436 ips = samba.interface_ips(lp, False)
1439 if i.find(':') != -1 and (linklocal or i.find('%') == -1):
1444 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1445 domainsid, schema=None,
1446 targetdir=None, samdb_fill=FILL_FULL,
1447 hostip=None, hostip6=None,
1448 next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1449 domainguid=None, policyguid=None, policyguid_dc=None,
1450 invocationid=None, machinepass=None, ntdsguid=None,
1451 dns_backend=None, dnspass=None,
1452 serverrole=None, dom_for_fun_level=None,
1453 am_rodc=False, lp=None):
1454 # create/adapt the group policy GUIDs
1455 # Default GUID for default policy are described at
1456 # "How Core Group Policy Works"
1457 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1458 if policyguid is None:
1459 policyguid = DEFAULT_POLICY_GUID
1460 policyguid = policyguid.upper()
1461 if policyguid_dc is None:
1462 policyguid_dc = DEFAULT_DC_POLICY_GUID
1463 policyguid_dc = policyguid_dc.upper()
1465 if invocationid is None:
1466 invocationid = str(uuid.uuid4())
1468 if krbtgtpass is None:
1469 krbtgtpass = samba.generate_random_password(128, 255)
1470 if machinepass is None:
1471 machinepass = samba.generate_random_password(128, 255)
1473 dnspass = samba.generate_random_password(128, 255)
1475 samdb = fill_samdb(samdb, lp, names, logger=logger,
1476 domainsid=domainsid, schema=schema, domainguid=domainguid,
1477 policyguid=policyguid, policyguid_dc=policyguid_dc,
1478 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1479 invocationid=invocationid, machinepass=machinepass,
1480 dnspass=dnspass, ntdsguid=ntdsguid, serverrole=serverrole,
1481 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1482 next_rid=next_rid, dc_rid=dc_rid)
1484 if serverrole == "domain controller":
1485 # Set up group policies (domain policy and domain controller
1487 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1489 setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.wheel_gid,
1490 domainsid, names.dnsdomain, names.domaindn, lp)
1492 secretsdb_self_join(secrets_ldb, domain=names.domain,
1493 realm=names.realm, dnsdomain=names.dnsdomain,
1494 netbiosname=names.netbiosname, domainsid=domainsid,
1495 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1497 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1498 # In future, this might be determined from some configuration
1499 kerberos_enctypes = str(ENC_ALL_TYPES)
1502 msg = ldb.Message(ldb.Dn(samdb,
1503 samdb.searchone("distinguishedName",
1504 expression="samAccountName=%s$" % names.netbiosname,
1505 scope=ldb.SCOPE_SUBTREE)))
1506 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1507 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1508 name="msDS-SupportedEncryptionTypes")
1510 except ldb.LdbError, (enum, estr):
1511 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1512 # It might be that this attribute does not exist in this schema
1515 setup_ad_dns(samdb, secrets_ldb, domainsid, names, paths, lp, logger,
1516 hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
1517 dnspass=dnspass, os_level=dom_for_fun_level,
1518 targetdir=targetdir, site=DEFAULTSITE)
1520 domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1521 attribute="objectGUID")
1522 assert isinstance(domainguid, str)
1524 lastProvisionUSNs = get_last_provision_usn(samdb)
1525 maxUSN = get_max_usn(samdb, str(names.rootdn))
1526 if lastProvisionUSNs is not None:
1527 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1529 set_provision_usn(samdb, 0, maxUSN, invocationid)
1531 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1532 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1533 { 'NTDSGUID' : names.ntdsguid })
1535 # fix any dangling GUIDs from the provision
1536 logger.info("Fixing provision GUIDs")
1537 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
1539 samdb.transaction_start()
1541 # a small number of GUIDs are missing because of ordering issues in the
1543 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1544 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1545 scope=ldb.SCOPE_BASE, attrs=['defaultObjectCategory'])
1546 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1547 scope=ldb.SCOPE_ONELEVEL,
1548 attrs=['ipsecOwnersReference',
1549 'ipsecFilterReference',
1550 'ipsecISAKMPReference',
1551 'ipsecNegotiationPolicyReference',
1552 'ipsecNFAReference'])
1554 samdb.transaction_cancel()
1557 samdb.transaction_commit()
1561 "ROLE_STANDALONE": "standalone",
1562 "ROLE_DOMAIN_MEMBER": "member server",
1563 "ROLE_DOMAIN_BDC": "domain controller",
1564 "ROLE_DOMAIN_PDC": "domain controller",
1565 "dc": "domain controller",
1566 "member": "member server",
1567 "domain controller": "domain controller",
1568 "member server": "member server",
1569 "standalone": "standalone",
1573 def sanitize_server_role(role):
1574 """Sanitize a server role name.
1576 :param role: Server role
1577 :raise ValueError: If the role can not be interpreted
1578 :return: Sanitized server role (one of "member server",
1579 "domain controller", "standalone")
1582 return _ROLES_MAP[role]
1584 raise ValueError(role)
1587 def provision(logger, session_info, credentials, smbconf=None,
1588 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1589 domaindn=None, schemadn=None, configdn=None, serverdn=None,
1590 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1591 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None, krbtgtpass=None,
1592 domainguid=None, policyguid=None, policyguid_dc=None,
1593 dns_backend=None, dnspass=None,
1594 invocationid=None, machinepass=None, ntdsguid=None,
1595 root=None, nobody=None, users=None, wheel=None, backup=None, aci=None,
1596 serverrole=None, dom_for_fun_level=None,
1597 backend_type=None, sitename=None,
1598 ol_mmr_urls=None, ol_olc=None, slapd_path=None,
1599 useeadb=False, am_rodc=False,
1603 :note: caution, this wipes all existing data!
1607 serverrole = sanitize_server_role(serverrole)
1609 raise ProvisioningError('server role (%s) should be one of "domain controller", "member server", "standalone"' % serverrole)
1611 if ldapadminpass is None:
1612 # Make a new, random password between Samba and it's LDAP server
1613 ldapadminpass = samba.generate_random_password(128, 255)
1615 if backend_type is None:
1616 backend_type = "ldb"
1618 if domainsid is None:
1619 domainsid = security.random_sid()
1621 domainsid = security.dom_sid(domainsid)
1623 sid_generator = "internal"
1624 if backend_type == "fedora-ds":
1625 sid_generator = "backend"
1627 root_uid = findnss_uid([root or "root"])
1628 nobody_uid = findnss_uid([nobody or "nobody"])
1629 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1631 wheel_gid = findnss_gid(["wheel", "adm"])
1633 wheel_gid = findnss_gid([wheel])
1635 bind_gid = findnss_gid(["bind", "named"])
1639 if targetdir is not None:
1640 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1641 elif smbconf is None:
1642 smbconf = samba.param.default_path()
1643 if not os.path.exists(os.path.dirname(smbconf)):
1644 os.makedirs(os.path.dirname(smbconf))
1646 server_services = None
1647 if dns_backend == "SAMBA_INTERNAL":
1648 server_services = [ "+dns" ]
1650 # only install a new smb.conf if there isn't one there already
1651 if os.path.exists(smbconf):
1652 # if Samba Team members can't figure out the weird errors
1653 # loading an empty smb.conf gives, then we need to be smarter.
1654 # Pretend it just didn't exist --abartlet
1655 f = open(smbconf, 'r')
1657 data = f.read().lstrip()
1660 if data is None or data == "":
1661 make_smbconf(smbconf, hostname, domain, realm,
1662 serverrole, targetdir, sid_generator, useeadb,
1663 lp=lp, server_services=server_services)
1665 make_smbconf(smbconf, hostname, domain, realm, serverrole,
1666 targetdir, sid_generator, useeadb, lp=lp,
1667 server_services=server_services)
1670 lp = samba.param.LoadParm()
1672 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1673 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
1674 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
1675 sitename=sitename, rootdn=rootdn)
1676 paths = provision_paths_from_lp(lp, names.dnsdomain)
1678 paths.bind_gid = bind_gid
1679 paths.wheel_gid = wheel_gid
1682 logger.info("Looking up IPv4 addresses")
1683 hostips = interface_ips_v4(lp)
1684 if len(hostips) > 0:
1686 if len(hostips) > 1:
1687 logger.warning("More than one IPv4 address found. Using %s",
1689 if hostip == "127.0.0.1":
1692 logger.warning("No IPv4 address will be assigned")
1695 logger.info("Looking up IPv6 addresses")
1696 hostips = interface_ips_v6(lp, linklocal=False)
1698 hostip6 = hostips[0]
1699 if len(hostips) > 1:
1700 logger.warning("More than one IPv6 address found. Using %s", hostip6)
1702 logger.warning("No IPv6 address will be assigned")
1704 names.hostip = hostip
1705 names.hostip6 = hostip6
1707 if serverrole is None:
1708 serverrole = lp.get("server role")
1710 if not os.path.exists(paths.private_dir):
1711 os.mkdir(paths.private_dir)
1712 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1713 os.mkdir(os.path.join(paths.private_dir, "tls"))
1715 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1717 schema = Schema(domainsid, invocationid=invocationid,
1718 schemadn=names.schemadn)
1720 if backend_type == "ldb":
1721 provision_backend = LDBBackend(backend_type, paths=paths,
1722 lp=lp, credentials=credentials,
1723 names=names, logger=logger)
1724 elif backend_type == "existing":
1725 # If support for this is ever added back, then the URI will need to be specified again
1726 provision_backend = ExistingBackend(backend_type, paths=paths,
1727 lp=lp, credentials=credentials,
1728 names=names, logger=logger,
1729 ldap_backend_forced_uri=None)
1730 elif backend_type == "fedora-ds":
1731 provision_backend = FDSBackend(backend_type, paths=paths,
1732 lp=lp, credentials=credentials,
1733 names=names, logger=logger, domainsid=domainsid,
1734 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1735 slapd_path=slapd_path,
1737 elif backend_type == "openldap":
1738 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
1739 lp=lp, credentials=credentials,
1740 names=names, logger=logger, domainsid=domainsid,
1741 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1742 slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls)
1744 raise ValueError("Unknown LDAP backend type selected")
1746 provision_backend.init()
1747 provision_backend.start()
1749 # only install a new shares config db if there is none
1750 if not os.path.exists(paths.shareconf):
1751 logger.info("Setting up share.ldb")
1752 share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
1753 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1755 logger.info("Setting up secrets.ldb")
1756 secrets_ldb = setup_secretsdb(paths,
1757 session_info=session_info,
1758 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1761 logger.info("Setting up the registry")
1762 setup_registry(paths.hklm, session_info, lp=lp)
1764 logger.info("Setting up the privileges database")
1765 setup_privileges(paths.privilege, session_info, lp=lp)
1767 logger.info("Setting up idmap db")
1768 idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
1770 setup_name_mappings(idmap, sid=str(domainsid),
1771 root_uid=root_uid, nobody_uid=nobody_uid,
1772 users_gid=users_gid, wheel_gid=wheel_gid)
1774 logger.info("Setting up SAM db")
1775 samdb = setup_samdb(paths.samdb, session_info,
1776 provision_backend, lp, names, logger=logger,
1777 serverrole=serverrole,
1778 schema=schema, fill=samdb_fill, am_rodc=am_rodc)
1780 if serverrole == "domain controller":
1781 if paths.netlogon is None:
1782 logger.info("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1783 logger.info("Please either remove %s or see the template at %s" %
1784 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1785 assert paths.netlogon is not None
1787 if paths.sysvol is None:
1788 logger.info("Existing smb.conf does not have a [sysvol] share, but you"
1789 " are configuring a DC.")
1790 logger.info("Please either remove %s or see the template at %s" %
1791 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1792 assert paths.sysvol is not None
1794 if not os.path.isdir(paths.netlogon):
1795 os.makedirs(paths.netlogon, 0755)
1797 if adminpass is None:
1798 adminpass = samba.generate_random_password(12, 32)
1799 adminpass_generated = True
1801 adminpass_generated = False
1803 if samdb_fill == FILL_FULL:
1804 provision_fill(samdb, secrets_ldb, logger, names, paths,
1805 schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
1806 hostip=hostip, hostip6=hostip6, domainsid=domainsid,
1807 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
1808 krbtgtpass=krbtgtpass, domainguid=domainguid,
1809 policyguid=policyguid, policyguid_dc=policyguid_dc,
1810 invocationid=invocationid, machinepass=machinepass,
1811 ntdsguid=ntdsguid, dns_backend=dns_backend,
1812 dnspass=dnspass, serverrole=serverrole,
1813 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1816 create_krb5_conf(paths.krb5conf,
1817 dnsdomain=names.dnsdomain, hostname=names.hostname,
1819 logger.info("A Kerberos configuration suitable for Samba 4 has been "
1820 "generated at %s", paths.krb5conf)
1822 if serverrole == "domain controller":
1823 create_dns_update_list(lp, logger, paths)
1825 backend_result = provision_backend.post_setup()
1826 provision_backend.shutdown()
1828 create_phpldapadmin_config(paths.phpldapadminconfig,
1831 secrets_ldb.transaction_cancel()
1834 # Now commit the secrets.ldb to disk
1835 secrets_ldb.transaction_commit()
1837 # the commit creates the dns.keytab, now chown it
1838 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1839 if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
1841 os.chmod(dns_keytab_path, 0640)
1842 os.chown(dns_keytab_path, -1, paths.bind_gid)
1844 if not os.environ.has_key('SAMBA_SELFTEST'):
1845 logger.info("Failed to chown %s to bind gid %u",
1846 dns_keytab_path, paths.bind_gid)
1848 result = ProvisionResult()
1849 result.server_role = serverrole
1850 result.domaindn = domaindn
1851 result.paths = paths
1852 result.names = names
1854 result.samdb = samdb
1855 result.idmap = idmap
1856 result.domainsid = str(domainsid)
1858 if samdb_fill == FILL_FULL:
1859 result.adminpass_generated = adminpass_generated
1860 result.adminpass = adminpass
1862 result.adminpass_generated = False
1863 result.adminpass = None
1865 result.backend_result = backend_result
1870 def provision_become_dc(smbconf=None, targetdir=None,
1871 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
1872 serverdn=None, domain=None, hostname=None, domainsid=None,
1873 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
1874 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
1875 dns_backend=None, root=None, nobody=None, users=None, wheel=None,
1876 backup=None, serverrole=None, ldap_backend=None,
1877 ldap_backend_type=None, sitename=None, debuglevel=1):
1879 logger = logging.getLogger("provision")
1880 samba.set_debug_level(debuglevel)
1882 res = provision(logger, system_session(), None,
1883 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1884 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1885 configdn=configdn, serverdn=serverdn, domain=domain,
1886 hostname=hostname, hostip=None, domainsid=domainsid,
1887 machinepass=machinepass, serverrole="domain controller",
1888 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass)
1889 res.lp.set("debuglevel", str(debuglevel))
1893 def create_phpldapadmin_config(path, ldapi_uri):
1894 """Create a PHP LDAP admin configuration file.
1896 :param path: Path to write the configuration to.
1898 setup_file(setup_path("phpldapadmin-config.php"), path,
1899 {"S4_LDAPI_URI": ldapi_uri})
1902 def create_krb5_conf(path, dnsdomain, hostname, realm):
1903 """Write out a file containing zone statements suitable for inclusion in a
1904 named.conf file (including GSS-TSIG configuration).
1906 :param path: Path of the new named.conf file.
1907 :param dnsdomain: DNS Domain name
1908 :param hostname: Local hostname
1909 :param realm: Realm name
1911 setup_file(setup_path("krb5.conf"), path, {
1912 "DNSDOMAIN": dnsdomain,
1913 "HOSTNAME": hostname,
1918 class ProvisioningError(Exception):
1919 """A generic provision error."""
1921 def __init__(self, value):
1925 return "ProvisioningError: " + self.value
1928 class InvalidNetbiosName(Exception):
1929 """A specified name was not a valid NetBIOS name."""
1931 def __init__(self, name):
1932 super(InvalidNetbiosName, self).__init__(
1933 "The name '%r' is not a valid NetBIOS name" % name)